2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-06 03:44:39 +03:00
/*
* Motorola CPCAP PMIC core driver
*
* Copyright ( C ) 2016 Tony Lindgren < tony @ atomide . com >
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/regmap.h>
# include <linux/sysfs.h>
2018-08-29 12:31:04 +03:00
# include <linux/mfd/core.h>
2017-01-06 03:44:39 +03:00
# include <linux/mfd/motorola-cpcap.h>
# include <linux/spi/spi.h>
# define CPCAP_NR_IRQ_REG_BANKS 6
# define CPCAP_NR_IRQ_CHIPS 3
2017-03-29 15:18:20 +03:00
# define CPCAP_REGISTER_SIZE 4
# define CPCAP_REGISTER_BITS 16
2017-01-06 03:44:39 +03:00
struct cpcap_ddata {
struct spi_device * spi ;
struct regmap_irq * irqs ;
struct regmap_irq_chip_data * irqdata [ CPCAP_NR_IRQ_CHIPS ] ;
const struct regmap_config * regmap_conf ;
struct regmap * regmap ;
} ;
2017-03-29 15:18:20 +03:00
static int cpcap_sense_irq ( struct regmap * regmap , int irq )
{
int regnum = irq / CPCAP_REGISTER_BITS ;
int mask = BIT ( irq % CPCAP_REGISTER_BITS ) ;
int reg = CPCAP_REG_INTS1 + ( regnum * CPCAP_REGISTER_SIZE ) ;
int err , val ;
if ( reg < CPCAP_REG_INTS1 | | reg > CPCAP_REG_INTS4 )
return - EINVAL ;
err = regmap_read ( regmap , reg , & val ) ;
if ( err )
return err ;
return ! ! ( val & mask ) ;
}
int cpcap_sense_virq ( struct regmap * regmap , int virq )
{
struct regmap_irq_chip_data * d = irq_get_chip_data ( virq ) ;
int irq_base = regmap_irq_chip_get_base ( d ) ;
return cpcap_sense_irq ( regmap , virq - irq_base ) ;
}
EXPORT_SYMBOL_GPL ( cpcap_sense_virq ) ;
2017-01-06 03:44:39 +03:00
static int cpcap_check_revision ( struct cpcap_ddata * cpcap )
{
u16 vendor , rev ;
int ret ;
ret = cpcap_get_vendor ( & cpcap - > spi - > dev , cpcap - > regmap , & vendor ) ;
if ( ret )
return ret ;
ret = cpcap_get_revision ( & cpcap - > spi - > dev , cpcap - > regmap , & rev ) ;
if ( ret )
return ret ;
dev_info ( & cpcap - > spi - > dev , " CPCAP vendor: %s rev: %i.%i (%x) \n " ,
vendor = = CPCAP_VENDOR_ST ? " ST " : " TI " ,
CPCAP_REVISION_MAJOR ( rev ) , CPCAP_REVISION_MINOR ( rev ) ,
rev ) ;
if ( rev < CPCAP_REVISION_2_1 ) {
dev_info ( & cpcap - > spi - > dev ,
" Please add old CPCAP revision support as needed \n " ) ;
return - ENODEV ;
}
return 0 ;
}
/*
* First two irq chips are the two private macro interrupt chips , the third
* irq chip is for register banks 1 - 4 and is available for drivers to use .
*/
static struct regmap_irq_chip cpcap_irq_chip [ CPCAP_NR_IRQ_CHIPS ] = {
{
. name = " cpcap-m2 " ,
. num_regs = 1 ,
. status_base = CPCAP_REG_MI1 ,
. ack_base = CPCAP_REG_MI1 ,
. mask_base = CPCAP_REG_MIM1 ,
. use_ack = true ,
2017-04-04 06:15:55 +03:00
. ack_invert = true ,
2017-01-06 03:44:39 +03:00
} ,
{
. name = " cpcap-m2 " ,
. num_regs = 1 ,
. status_base = CPCAP_REG_MI2 ,
. ack_base = CPCAP_REG_MI2 ,
. mask_base = CPCAP_REG_MIM2 ,
. use_ack = true ,
2017-04-04 06:15:55 +03:00
. ack_invert = true ,
2017-01-06 03:44:39 +03:00
} ,
{
. name = " cpcap1-4 " ,
. num_regs = 4 ,
. status_base = CPCAP_REG_INT1 ,
. ack_base = CPCAP_REG_INT1 ,
. mask_base = CPCAP_REG_INTM1 ,
. use_ack = true ,
2017-04-04 06:15:55 +03:00
. ack_invert = true ,
2017-01-06 03:44:39 +03:00
} ,
} ;
static void cpcap_init_one_regmap_irq ( struct cpcap_ddata * cpcap ,
struct regmap_irq * rirq ,
int irq_base , int irq )
{
unsigned int reg_offset ;
unsigned int bit , mask ;
reg_offset = irq - irq_base ;
reg_offset / = cpcap - > regmap_conf - > val_bits ;
reg_offset * = cpcap - > regmap_conf - > reg_stride ;
bit = irq % cpcap - > regmap_conf - > val_bits ;
mask = ( 1 < < bit ) ;
rirq - > reg_offset = reg_offset ;
rirq - > mask = mask ;
}
static int cpcap_init_irq_chip ( struct cpcap_ddata * cpcap , int irq_chip ,
int irq_start , int nr_irqs )
{
struct regmap_irq_chip * chip = & cpcap_irq_chip [ irq_chip ] ;
int i , ret ;
for ( i = irq_start ; i < irq_start + nr_irqs ; i + + ) {
struct regmap_irq * rirq = & cpcap - > irqs [ i ] ;
cpcap_init_one_regmap_irq ( cpcap , rirq , irq_start , i ) ;
}
chip - > irqs = & cpcap - > irqs [ irq_start ] ;
chip - > num_irqs = nr_irqs ;
chip - > irq_drv_data = cpcap ;
ret = devm_regmap_add_irq_chip ( & cpcap - > spi - > dev , cpcap - > regmap ,
cpcap - > spi - > irq ,
2017-04-04 06:15:54 +03:00
irq_get_trigger_type ( cpcap - > spi - > irq ) |
2017-01-06 03:44:39 +03:00
IRQF_SHARED , - 1 ,
chip , & cpcap - > irqdata [ irq_chip ] ) ;
if ( ret ) {
dev_err ( & cpcap - > spi - > dev , " could not add irq chip %i: %i \n " ,
irq_chip , ret ) ;
return ret ;
}
return 0 ;
}
static int cpcap_init_irq ( struct cpcap_ddata * cpcap )
{
int ret ;
cpcap - > irqs = devm_kzalloc ( & cpcap - > spi - > dev ,
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
array3_size ( sizeof ( * cpcap - > irqs ) ,
CPCAP_NR_IRQ_REG_BANKS ,
cpcap - > regmap_conf - > val_bits ) ,
2017-01-06 03:44:39 +03:00
GFP_KERNEL ) ;
if ( ! cpcap - > irqs )
return - ENOMEM ;
ret = cpcap_init_irq_chip ( cpcap , 0 , 0 , 16 ) ;
if ( ret )
return ret ;
ret = cpcap_init_irq_chip ( cpcap , 1 , 16 , 16 ) ;
if ( ret )
return ret ;
ret = cpcap_init_irq_chip ( cpcap , 2 , 32 , 64 ) ;
if ( ret )
return ret ;
enable_irq_wake ( cpcap - > spi - > irq ) ;
return 0 ;
}
static const struct of_device_id cpcap_of_match [ ] = {
{ . compatible = " motorola,cpcap " , } ,
{ . compatible = " st,6556002 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , cpcap_of_match ) ;
static const struct regmap_config cpcap_regmap_config = {
. reg_bits = 16 ,
. reg_stride = 4 ,
. pad_bits = 0 ,
. val_bits = 16 ,
. write_flag_mask = 0x8000 ,
. max_register = CPCAP_REG_ST_TEST2 ,
. cache_type = REGCACHE_NONE ,
. reg_format_endian = REGMAP_ENDIAN_LITTLE ,
. val_format_endian = REGMAP_ENDIAN_LITTLE ,
} ;
2020-07-06 21:39:34 +03:00
# ifdef CONFIG_PM_SLEEP
static int cpcap_suspend ( struct device * dev )
{
struct spi_device * spi = to_spi_device ( dev ) ;
disable_irq ( spi - > irq ) ;
return 0 ;
}
static int cpcap_resume ( struct device * dev )
{
struct spi_device * spi = to_spi_device ( dev ) ;
enable_irq ( spi - > irq ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( cpcap_pm , cpcap_suspend , cpcap_resume ) ;
2018-08-29 12:31:04 +03:00
static const struct mfd_cell cpcap_mfd_devices [ ] = {
{
. name = " cpcap_adc " ,
. of_compatible = " motorola,mapphone-cpcap-adc " ,
} , {
. name = " cpcap_battery " ,
. of_compatible = " motorola,cpcap-battery " ,
} , {
. name = " cpcap-charger " ,
. of_compatible = " motorola,mapphone-cpcap-charger " ,
} , {
. name = " cpcap-regulator " ,
. of_compatible = " motorola,mapphone-cpcap-regulator " ,
} , {
. name = " cpcap-rtc " ,
. of_compatible = " motorola,cpcap-rtc " ,
} , {
. name = " cpcap-pwrbutton " ,
. of_compatible = " motorola,cpcap-pwrbutton " ,
} , {
. name = " cpcap-usb-phy " ,
. of_compatible = " motorola,mapphone-cpcap-usb-phy " ,
} , {
. name = " cpcap-led " ,
. id = 0 ,
. of_compatible = " motorola,cpcap-led-red " ,
} , {
. name = " cpcap-led " ,
. id = 1 ,
. of_compatible = " motorola,cpcap-led-green " ,
} , {
. name = " cpcap-led " ,
. id = 2 ,
. of_compatible = " motorola,cpcap-led-blue " ,
} , {
. name = " cpcap-led " ,
. id = 3 ,
. of_compatible = " motorola,cpcap-led-adl " ,
} , {
. name = " cpcap-led " ,
. id = 4 ,
. of_compatible = " motorola,cpcap-led-cp " ,
} , {
. name = " cpcap-codec " ,
}
} ;
2017-01-06 03:44:39 +03:00
static int cpcap_probe ( struct spi_device * spi )
{
const struct of_device_id * match ;
struct cpcap_ddata * cpcap ;
int ret ;
match = of_match_device ( of_match_ptr ( cpcap_of_match ) , & spi - > dev ) ;
if ( ! match )
return - ENODEV ;
cpcap = devm_kzalloc ( & spi - > dev , sizeof ( * cpcap ) , GFP_KERNEL ) ;
if ( ! cpcap )
return - ENOMEM ;
cpcap - > spi = spi ;
spi_set_drvdata ( spi , cpcap ) ;
spi - > bits_per_word = 16 ;
spi - > mode = SPI_MODE_0 | SPI_CS_HIGH ;
ret = spi_setup ( spi ) ;
if ( ret )
return ret ;
cpcap - > regmap_conf = & cpcap_regmap_config ;
cpcap - > regmap = devm_regmap_init_spi ( spi , & cpcap_regmap_config ) ;
if ( IS_ERR ( cpcap - > regmap ) ) {
ret = PTR_ERR ( cpcap - > regmap ) ;
dev_err ( & cpcap - > spi - > dev , " Failed to initialize regmap: %d \n " ,
ret ) ;
return ret ;
}
ret = cpcap_check_revision ( cpcap ) ;
if ( ret ) {
dev_err ( & cpcap - > spi - > dev , " Failed to detect CPCAP: %i \n " , ret ) ;
return ret ;
}
ret = cpcap_init_irq ( cpcap ) ;
if ( ret )
return ret ;
2018-08-29 12:31:04 +03:00
return devm_mfd_add_devices ( & spi - > dev , 0 , cpcap_mfd_devices ,
ARRAY_SIZE ( cpcap_mfd_devices ) , NULL , 0 , NULL ) ;
2017-01-06 03:44:39 +03:00
}
static struct spi_driver cpcap_driver = {
. driver = {
. name = " cpcap-core " ,
. of_match_table = cpcap_of_match ,
2020-07-06 21:39:34 +03:00
. pm = & cpcap_pm ,
2017-01-06 03:44:39 +03:00
} ,
. probe = cpcap_probe ,
} ;
module_spi_driver ( cpcap_driver ) ;
MODULE_ALIAS ( " platform:cpcap " ) ;
MODULE_DESCRIPTION ( " CPCAP driver " ) ;
MODULE_AUTHOR ( " Tony Lindgren <tony@atomide.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;