2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-04-06 01:40:52 +04:00
/*
* Copyright ( c ) 2011 , Code Aurora Forum . All rights reserved .
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/kernel.h>
2014-02-26 22:59:19 +04:00
# include <linux/interrupt.h>
2014-02-26 22:59:20 +04:00
# include <linux/irqchip/chained_irq.h>
2014-02-26 22:59:19 +04:00
# include <linux/irq.h>
2014-02-26 22:59:21 +04:00
# include <linux/irqdomain.h>
2013-08-20 11:01:26 +04:00
# include <linux/module.h>
2011-04-06 01:40:52 +04:00
# include <linux/platform_device.h>
# include <linux/slab.h>
2011-04-06 01:40:53 +04:00
# include <linux/err.h>
2013-03-12 22:41:54 +04:00
# include <linux/ssbi.h>
2014-02-26 22:59:23 +04:00
# include <linux/regmap.h>
2014-02-26 22:59:21 +04:00
# include <linux/of_platform.h>
2011-04-06 01:40:52 +04:00
# include <linux/mfd/core.h>
2014-02-26 22:59:19 +04:00
# define SSBI_REG_ADDR_IRQ_BASE 0x1BB
# define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
# define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
# define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
# define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
# define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
# define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
# define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
# define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
# define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
2016-11-15 15:01:51 +03:00
# define PM8821_SSBI_REG_ADDR_IRQ_BASE 0x100
# define PM8821_SSBI_REG_ADDR_IRQ_MASTER0 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0x30)
# define PM8821_SSBI_REG_ADDR_IRQ_MASTER1 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0xb0)
# define PM8821_SSBI_REG(m, b, offset) \
( ( m = = 0 ) ? \
( PM8821_SSBI_REG_ADDR_IRQ_MASTER0 + b + offset ) : \
( PM8821_SSBI_REG_ADDR_IRQ_MASTER1 + b + offset ) )
# define PM8821_SSBI_ADDR_IRQ_ROOT(m, b) PM8821_SSBI_REG(m, b, 0x0)
# define PM8821_SSBI_ADDR_IRQ_CLEAR(m, b) PM8821_SSBI_REG(m, b, 0x01)
# define PM8821_SSBI_ADDR_IRQ_MASK(m, b) PM8821_SSBI_REG(m, b, 0x08)
# define PM8821_SSBI_ADDR_IRQ_RT_STATUS(m, b) PM8821_SSBI_REG(m, b, 0x0f)
# define PM8821_BLOCKS_PER_MASTER 7
2014-02-26 22:59:19 +04:00
# define PM_IRQF_LVL_SEL 0x01 /* level select */
# define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
# define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
# define PM_IRQF_CLR 0x08 /* clear interrupt */
# define PM_IRQF_BITS_MASK 0x70
# define PM_IRQF_BITS_SHIFT 4
# define PM_IRQF_WRITE 0x80
# define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
PM_IRQF_MASK_RE )
2011-04-06 01:40:52 +04:00
# define REG_HWREV 0x002 /* PMIC4 revision */
# define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
2016-11-19 18:15:18 +03:00
# define PM8XXX_NR_IRQS 256
2016-11-15 15:01:51 +03:00
# define PM8821_NR_IRQS 112
2014-02-26 22:59:21 +04:00
2019-02-08 05:16:24 +03:00
struct pm_irq_data {
int num_irqs ;
struct irq_chip * irq_chip ;
void ( * irq_handler ) ( struct irq_desc * desc ) ;
} ;
2014-02-26 22:59:19 +04:00
struct pm_irq_chip {
2014-02-26 22:59:23 +04:00
struct regmap * regmap ;
2014-02-26 22:59:19 +04:00
spinlock_t pm_irq_lock ;
2014-02-26 22:59:21 +04:00
struct irq_domain * irqdomain ;
2014-02-26 22:59:19 +04:00
unsigned int num_blocks ;
unsigned int num_masters ;
2019-02-08 05:16:24 +03:00
const struct pm_irq_data * pm_irq_data ;
/* MUST BE AT THE END OF THIS STRUCT */
2020-02-13 02:59:11 +03:00
u8 config [ ] ;
2014-02-26 22:59:19 +04:00
} ;
2014-02-26 22:59:23 +04:00
static int pm8xxx_read_block_irq ( struct pm_irq_chip * chip , unsigned int bp ,
unsigned int * ip )
2014-02-26 22:59:19 +04:00
{
int rc ;
spin_lock ( & chip - > pm_irq_lock ) ;
2014-02-26 22:59:23 +04:00
rc = regmap_write ( chip - > regmap , SSBI_REG_ADDR_IRQ_BLK_SEL , bp ) ;
2014-02-26 22:59:19 +04:00
if ( rc ) {
pr_err ( " Failed Selecting Block %d rc=%d \n " , bp , rc ) ;
goto bail ;
}
2014-02-26 22:59:23 +04:00
rc = regmap_read ( chip - > regmap , SSBI_REG_ADDR_IRQ_IT_STATUS , ip ) ;
2014-02-26 22:59:19 +04:00
if ( rc )
pr_err ( " Failed Reading Status rc=%d \n " , rc ) ;
bail :
spin_unlock ( & chip - > pm_irq_lock ) ;
return rc ;
}
2014-02-26 22:59:23 +04:00
static int
pm8xxx_config_irq ( struct pm_irq_chip * chip , unsigned int bp , unsigned int cp )
2014-02-26 22:59:19 +04:00
{
int rc ;
spin_lock ( & chip - > pm_irq_lock ) ;
2014-02-26 22:59:23 +04:00
rc = regmap_write ( chip - > regmap , SSBI_REG_ADDR_IRQ_BLK_SEL , bp ) ;
2014-02-26 22:59:19 +04:00
if ( rc ) {
pr_err ( " Failed Selecting Block %d rc=%d \n " , bp , rc ) ;
goto bail ;
}
cp | = PM_IRQF_WRITE ;
2014-02-26 22:59:23 +04:00
rc = regmap_write ( chip - > regmap , SSBI_REG_ADDR_IRQ_CONFIG , cp ) ;
2014-02-26 22:59:19 +04:00
if ( rc )
pr_err ( " Failed Configuring IRQ rc=%d \n " , rc ) ;
bail :
spin_unlock ( & chip - > pm_irq_lock ) ;
return rc ;
}
static int pm8xxx_irq_block_handler ( struct pm_irq_chip * chip , int block )
{
int pmirq , irq , i , ret = 0 ;
2014-02-26 22:59:23 +04:00
unsigned int bits ;
2014-02-26 22:59:19 +04:00
ret = pm8xxx_read_block_irq ( chip , block , & bits ) ;
if ( ret ) {
pr_err ( " Failed reading %d block ret=%d " , block , ret ) ;
return ret ;
}
if ( ! bits ) {
pr_err ( " block bit set in master but no irqs: %d " , block ) ;
return 0 ;
}
/* Check IRQ bits */
for ( i = 0 ; i < 8 ; i + + ) {
if ( bits & ( 1 < < i ) ) {
pmirq = block * 8 + i ;
2014-02-26 22:59:21 +04:00
irq = irq_find_mapping ( chip - > irqdomain , pmirq ) ;
2014-02-26 22:59:19 +04:00
generic_handle_irq ( irq ) ;
}
}
return 0 ;
}
static int pm8xxx_irq_master_handler ( struct pm_irq_chip * chip , int master )
{
2014-02-26 22:59:23 +04:00
unsigned int blockbits ;
2014-02-26 22:59:19 +04:00
int block_number , i , ret = 0 ;
2014-02-26 22:59:23 +04:00
ret = regmap_read ( chip - > regmap , SSBI_REG_ADDR_IRQ_M_STATUS1 + master ,
& blockbits ) ;
2014-02-26 22:59:19 +04:00
if ( ret ) {
pr_err ( " Failed to read master %d ret=%d \n " , master , ret ) ;
return ret ;
}
if ( ! blockbits ) {
pr_err ( " master bit set in root but no blocks: %d " , master ) ;
return 0 ;
}
for ( i = 0 ; i < 8 ; i + + )
if ( blockbits & ( 1 < < i ) ) {
block_number = master * 8 + i ; /* block # */
ret | = pm8xxx_irq_block_handler ( chip , block_number ) ;
}
return ret ;
}
2015-09-14 11:42:37 +03:00
static void pm8xxx_irq_handler ( struct irq_desc * desc )
2014-02-26 22:59:19 +04:00
{
struct pm_irq_chip * chip = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * irq_chip = irq_desc_get_chip ( desc ) ;
2014-02-26 22:59:23 +04:00
unsigned int root ;
2014-02-26 22:59:19 +04:00
int i , ret , masters = 0 ;
2014-02-26 22:59:20 +04:00
chained_irq_enter ( irq_chip , desc ) ;
2014-02-26 22:59:23 +04:00
ret = regmap_read ( chip - > regmap , SSBI_REG_ADDR_IRQ_ROOT , & root ) ;
2014-02-26 22:59:19 +04:00
if ( ret ) {
pr_err ( " Can't read root status ret=%d \n " , ret ) ;
return ;
}
/* on pm8xxx series masters start from bit 1 of the root */
masters = root > > 1 ;
/* Read allowed masters for blocks. */
for ( i = 0 ; i < chip - > num_masters ; i + + )
if ( masters & ( 1 < < i ) )
pm8xxx_irq_master_handler ( chip , i ) ;
2014-02-26 22:59:20 +04:00
chained_irq_exit ( irq_chip , desc ) ;
2014-02-26 22:59:19 +04:00
}
2016-11-15 15:01:51 +03:00
static void pm8821_irq_block_handler ( struct pm_irq_chip * chip ,
int master , int block )
{
int pmirq , irq , i , ret ;
unsigned int bits ;
ret = regmap_read ( chip - > regmap ,
PM8821_SSBI_ADDR_IRQ_ROOT ( master , block ) , & bits ) ;
if ( ret ) {
pr_err ( " Reading block %d failed ret=%d " , block , ret ) ;
return ;
}
/* Convert block offset to global block number */
block + = ( master * PM8821_BLOCKS_PER_MASTER ) - 1 ;
/* Check IRQ bits */
for ( i = 0 ; i < 8 ; i + + ) {
if ( bits & BIT ( i ) ) {
pmirq = block * 8 + i ;
irq = irq_find_mapping ( chip - > irqdomain , pmirq ) ;
generic_handle_irq ( irq ) ;
}
}
}
static inline void pm8821_irq_master_handler ( struct pm_irq_chip * chip ,
int master , u8 master_val )
{
int block ;
for ( block = 1 ; block < 8 ; block + + )
if ( master_val & BIT ( block ) )
pm8821_irq_block_handler ( chip , master , block ) ;
}
static void pm8821_irq_handler ( struct irq_desc * desc )
{
struct pm_irq_chip * chip = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * irq_chip = irq_desc_get_chip ( desc ) ;
unsigned int master ;
int ret ;
chained_irq_enter ( irq_chip , desc ) ;
ret = regmap_read ( chip - > regmap ,
PM8821_SSBI_REG_ADDR_IRQ_MASTER0 , & master ) ;
if ( ret ) {
pr_err ( " Failed to read master 0 ret=%d \n " , ret ) ;
goto done ;
}
/* bits 1 through 7 marks the first 7 blocks in master 0 */
if ( master & GENMASK ( 7 , 1 ) )
pm8821_irq_master_handler ( chip , 0 , master ) ;
/* bit 0 marks if master 1 contains any bits */
if ( ! ( master & BIT ( 0 ) ) )
goto done ;
ret = regmap_read ( chip - > regmap ,
PM8821_SSBI_REG_ADDR_IRQ_MASTER1 , & master ) ;
if ( ret ) {
pr_err ( " Failed to read master 1 ret=%d \n " , ret ) ;
goto done ;
}
pm8821_irq_master_handler ( chip , 1 , master ) ;
done :
chained_irq_exit ( irq_chip , desc ) ;
}
2014-02-26 22:59:19 +04:00
static void pm8xxx_irq_mask_ack ( struct irq_data * d )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
2014-02-26 22:59:21 +04:00
unsigned int pmirq = irqd_to_hwirq ( d ) ;
2014-02-26 22:59:19 +04:00
u8 block , config ;
block = pmirq / 8 ;
config = chip - > config [ pmirq ] | PM_IRQF_MASK_ALL | PM_IRQF_CLR ;
pm8xxx_config_irq ( chip , block , config ) ;
}
static void pm8xxx_irq_unmask ( struct irq_data * d )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
2014-02-26 22:59:21 +04:00
unsigned int pmirq = irqd_to_hwirq ( d ) ;
2014-02-26 22:59:19 +04:00
u8 block , config ;
block = pmirq / 8 ;
config = chip - > config [ pmirq ] ;
pm8xxx_config_irq ( chip , block , config ) ;
}
static int pm8xxx_irq_set_type ( struct irq_data * d , unsigned int flow_type )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
2014-02-26 22:59:21 +04:00
unsigned int pmirq = irqd_to_hwirq ( d ) ;
int irq_bit ;
2014-02-26 22:59:19 +04:00
u8 block , config ;
block = pmirq / 8 ;
irq_bit = pmirq % 8 ;
chip - > config [ pmirq ] = ( irq_bit < < PM_IRQF_BITS_SHIFT )
| PM_IRQF_MASK_ALL ;
if ( flow_type & ( IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ) ) {
if ( flow_type & IRQF_TRIGGER_RISING )
chip - > config [ pmirq ] & = ~ PM_IRQF_MASK_RE ;
if ( flow_type & IRQF_TRIGGER_FALLING )
chip - > config [ pmirq ] & = ~ PM_IRQF_MASK_FE ;
} else {
chip - > config [ pmirq ] | = PM_IRQF_LVL_SEL ;
if ( flow_type & IRQF_TRIGGER_HIGH )
chip - > config [ pmirq ] & = ~ PM_IRQF_MASK_RE ;
else
chip - > config [ pmirq ] & = ~ PM_IRQF_MASK_FE ;
}
config = chip - > config [ pmirq ] | PM_IRQF_CLR ;
return pm8xxx_config_irq ( chip , block , config ) ;
}
2015-07-15 09:40:34 +03:00
static int pm8xxx_irq_get_irqchip_state ( struct irq_data * d ,
enum irqchip_irq_state which ,
bool * state )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
unsigned int pmirq = irqd_to_hwirq ( d ) ;
unsigned int bits ;
int irq_bit ;
u8 block ;
int rc ;
if ( which ! = IRQCHIP_STATE_LINE_LEVEL )
return - EINVAL ;
block = pmirq / 8 ;
irq_bit = pmirq % 8 ;
spin_lock ( & chip - > pm_irq_lock ) ;
rc = regmap_write ( chip - > regmap , SSBI_REG_ADDR_IRQ_BLK_SEL , block ) ;
if ( rc ) {
pr_err ( " Failed Selecting Block %d rc=%d \n " , block , rc ) ;
goto bail ;
}
rc = regmap_read ( chip - > regmap , SSBI_REG_ADDR_IRQ_RT_STATUS , & bits ) ;
if ( rc ) {
pr_err ( " Failed Reading Status rc=%d \n " , rc ) ;
goto bail ;
}
* state = ! ! ( bits & BIT ( irq_bit ) ) ;
bail :
spin_unlock ( & chip - > pm_irq_lock ) ;
return rc ;
}
2014-02-26 22:59:19 +04:00
static struct irq_chip pm8xxx_irq_chip = {
. name = " pm8xxx " ,
. irq_mask_ack = pm8xxx_irq_mask_ack ,
. irq_unmask = pm8xxx_irq_unmask ,
. irq_set_type = pm8xxx_irq_set_type ,
2015-07-15 09:40:34 +03:00
. irq_get_irqchip_state = pm8xxx_irq_get_irqchip_state ,
2014-03-04 22:48:52 +04:00
. flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE ,
2014-02-26 22:59:19 +04:00
} ;
2019-02-08 05:16:24 +03:00
static void pm8xxx_irq_domain_map ( struct pm_irq_chip * chip ,
struct irq_domain * domain , unsigned int irq ,
irq_hw_number_t hwirq , unsigned int type )
2014-02-26 22:59:21 +04:00
{
2019-02-08 05:16:24 +03:00
irq_domain_set_info ( domain , irq , hwirq , chip - > pm_irq_data - > irq_chip ,
chip , handle_level_irq , NULL , NULL ) ;
2014-02-26 22:59:21 +04:00
irq_set_noprobe ( irq ) ;
2019-02-08 05:16:24 +03:00
}
static int pm8xxx_irq_domain_alloc ( struct irq_domain * domain , unsigned int virq ,
unsigned int nr_irqs , void * data )
{
struct pm_irq_chip * chip = domain - > host_data ;
struct irq_fwspec * fwspec = data ;
irq_hw_number_t hwirq ;
unsigned int type ;
int ret , i ;
ret = irq_domain_translate_twocell ( domain , fwspec , & hwirq , & type ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < nr_irqs ; i + + )
pm8xxx_irq_domain_map ( chip , domain , virq + i , hwirq + i , type ) ;
2015-07-27 23:55:20 +03:00
2014-02-26 22:59:19 +04:00
return 0 ;
}
2014-02-26 22:59:21 +04:00
static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
2019-02-08 05:16:24 +03:00
. alloc = pm8xxx_irq_domain_alloc ,
. free = irq_domain_free_irqs_common ,
. translate = irq_domain_translate_twocell ,
2014-02-26 22:59:21 +04:00
} ;
2016-11-15 15:01:51 +03:00
static void pm8821_irq_mask_ack ( struct irq_data * d )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
unsigned int pmirq = irqd_to_hwirq ( d ) ;
u8 block , master ;
int irq_bit , rc ;
block = pmirq / 8 ;
master = block / PM8821_BLOCKS_PER_MASTER ;
irq_bit = pmirq % 8 ;
block % = PM8821_BLOCKS_PER_MASTER ;
rc = regmap_update_bits ( chip - > regmap ,
PM8821_SSBI_ADDR_IRQ_MASK ( master , block ) ,
BIT ( irq_bit ) , BIT ( irq_bit ) ) ;
if ( rc ) {
pr_err ( " Failed to mask IRQ:%d rc=%d \n " , pmirq , rc ) ;
return ;
}
rc = regmap_update_bits ( chip - > regmap ,
PM8821_SSBI_ADDR_IRQ_CLEAR ( master , block ) ,
BIT ( irq_bit ) , BIT ( irq_bit ) ) ;
if ( rc )
pr_err ( " Failed to CLEAR IRQ:%d rc=%d \n " , pmirq , rc ) ;
}
static void pm8821_irq_unmask ( struct irq_data * d )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
unsigned int pmirq = irqd_to_hwirq ( d ) ;
int irq_bit , rc ;
u8 block , master ;
block = pmirq / 8 ;
master = block / PM8821_BLOCKS_PER_MASTER ;
irq_bit = pmirq % 8 ;
block % = PM8821_BLOCKS_PER_MASTER ;
rc = regmap_update_bits ( chip - > regmap ,
PM8821_SSBI_ADDR_IRQ_MASK ( master , block ) ,
BIT ( irq_bit ) , ~ BIT ( irq_bit ) ) ;
if ( rc )
pr_err ( " Failed to read/write unmask IRQ:%d rc=%d \n " , pmirq , rc ) ;
}
static int pm8821_irq_get_irqchip_state ( struct irq_data * d ,
enum irqchip_irq_state which ,
bool * state )
{
struct pm_irq_chip * chip = irq_data_get_irq_chip_data ( d ) ;
int rc , pmirq = irqd_to_hwirq ( d ) ;
u8 block , irq_bit , master ;
unsigned int bits ;
block = pmirq / 8 ;
master = block / PM8821_BLOCKS_PER_MASTER ;
irq_bit = pmirq % 8 ;
block % = PM8821_BLOCKS_PER_MASTER ;
rc = regmap_read ( chip - > regmap ,
PM8821_SSBI_ADDR_IRQ_RT_STATUS ( master , block ) , & bits ) ;
if ( rc ) {
pr_err ( " Reading Status of IRQ %d failed rc=%d \n " , pmirq , rc ) ;
return rc ;
}
* state = ! ! ( bits & BIT ( irq_bit ) ) ;
return rc ;
}
static struct irq_chip pm8821_irq_chip = {
. name = " pm8821 " ,
. irq_mask_ack = pm8821_irq_mask_ack ,
. irq_unmask = pm8821_irq_unmask ,
. irq_get_irqchip_state = pm8821_irq_get_irqchip_state ,
. flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE ,
} ;
2014-02-26 22:59:23 +04:00
static const struct regmap_config ssbi_regmap_config = {
. reg_bits = 16 ,
. val_bits = 8 ,
. max_register = 0x3ff ,
. fast_io = true ,
. reg_read = ssbi_reg_read ,
. reg_write = ssbi_reg_write
} ;
2016-11-15 15:01:51 +03:00
static const struct pm_irq_data pm8xxx_data = {
. num_irqs = PM8XXX_NR_IRQS ,
2019-02-08 05:16:24 +03:00
. irq_chip = & pm8xxx_irq_chip ,
2016-11-15 15:01:51 +03:00
. irq_handler = pm8xxx_irq_handler ,
} ;
static const struct pm_irq_data pm8821_data = {
. num_irqs = PM8821_NR_IRQS ,
2019-02-08 05:16:24 +03:00
. irq_chip = & pm8821_irq_chip ,
2016-11-15 15:01:51 +03:00
. irq_handler = pm8821_irq_handler ,
} ;
2016-11-19 18:15:18 +03:00
static const struct of_device_id pm8xxx_id_table [ ] = {
2016-11-15 15:01:51 +03:00
{ . compatible = " qcom,pm8018 " , . data = & pm8xxx_data } ,
{ . compatible = " qcom,pm8058 " , . data = & pm8xxx_data } ,
{ . compatible = " qcom,pm8821 " , . data = & pm8821_data } ,
{ . compatible = " qcom,pm8921 " , . data = & pm8xxx_data } ,
2014-02-26 22:59:24 +04:00
{ }
} ;
2016-11-19 18:15:18 +03:00
MODULE_DEVICE_TABLE ( of , pm8xxx_id_table ) ;
2014-02-26 22:59:24 +04:00
2016-11-19 18:15:18 +03:00
static int pm8xxx_probe ( struct platform_device * pdev )
2011-04-06 01:40:52 +04:00
{
2016-11-15 15:01:51 +03:00
const struct pm_irq_data * data ;
2014-02-26 22:59:23 +04:00
struct regmap * regmap ;
2014-03-05 23:34:25 +04:00
int irq , rc ;
2014-02-26 22:59:23 +04:00
unsigned int val ;
2011-04-06 01:40:53 +04:00
u32 rev ;
2014-02-26 22:59:21 +04:00
struct pm_irq_chip * chip ;
2016-11-15 15:01:51 +03:00
data = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! data ) {
dev_err ( & pdev - > dev , " No matching driver data found \n " ) ;
return - EINVAL ;
}
2011-04-06 01:40:52 +04:00
2014-02-26 22:59:21 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
2011-04-06 01:40:52 +04:00
2014-02-26 22:59:23 +04:00
regmap = devm_regmap_init ( & pdev - > dev , NULL , pdev - > dev . parent ,
& ssbi_regmap_config ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
2011-04-06 01:40:52 +04:00
/* Read PMIC chip revision */
2014-02-26 22:59:23 +04:00
rc = regmap_read ( regmap , REG_HWREV , & val ) ;
2011-04-06 01:40:52 +04:00
if ( rc ) {
pr_err ( " Failed to read hw rev reg %d:rc=%d \n " , REG_HWREV , rc ) ;
2013-08-20 11:02:26 +04:00
return rc ;
2011-04-06 01:40:52 +04:00
}
pr_info ( " PMIC revision 1: %02X \n " , val ) ;
2011-04-06 01:40:53 +04:00
rev = val ;
2011-04-06 01:40:52 +04:00
/* Read PMIC chip revision 2 */
2014-02-26 22:59:23 +04:00
rc = regmap_read ( regmap , REG_HWREV_2 , & val ) ;
2011-04-06 01:40:52 +04:00
if ( rc ) {
pr_err ( " Failed to read hw rev 2 reg %d:rc=%d \n " ,
REG_HWREV_2 , rc ) ;
2013-08-20 11:02:26 +04:00
return rc ;
2011-04-06 01:40:52 +04:00
}
pr_info ( " PMIC revision 2: %02X \n " , val ) ;
2011-04-06 01:40:53 +04:00
rev | = val < < BITS_PER_BYTE ;
2011-04-06 01:40:52 +04:00
treewide: Use struct_size() for devm_kmalloc() and friends
Replaces open-coded struct size calculations with struct_size() for
devm_*, f2fs_*, and sock_* allocations. Automatically generated (and
manually adjusted) from the following Coccinelle script:
// Direct reference to struct field.
@@
identifier alloc =~ "devm_kmalloc|devm_kzalloc|sock_kmalloc|f2fs_kmalloc|f2fs_kzalloc";
expression HANDLE;
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(HANDLE, sizeof(*VAR) + COUNT * sizeof(*VAR->ELEMENT), GFP)
+ alloc(HANDLE, struct_size(VAR, ELEMENT, COUNT), GFP)
// mr = kzalloc(sizeof(*mr) + m * sizeof(mr->map[0]), GFP_KERNEL);
@@
identifier alloc =~ "devm_kmalloc|devm_kzalloc|sock_kmalloc|f2fs_kmalloc|f2fs_kzalloc";
expression HANDLE;
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(HANDLE, sizeof(*VAR) + COUNT * sizeof(VAR->ELEMENT[0]), GFP)
+ alloc(HANDLE, struct_size(VAR, ELEMENT, COUNT), GFP)
// Same pattern, but can't trivially locate the trailing element name,
// or variable name.
@@
identifier alloc =~ "devm_kmalloc|devm_kzalloc|sock_kmalloc|f2fs_kmalloc|f2fs_kzalloc";
expression HANDLE;
expression GFP;
expression SOMETHING, COUNT, ELEMENT;
@@
- alloc(HANDLE, sizeof(SOMETHING) + COUNT * sizeof(ELEMENT), GFP)
+ alloc(HANDLE, CHECKME_struct_size(&SOMETHING, ELEMENT, COUNT), GFP)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-05-09 02:08:53 +03:00
chip = devm_kzalloc ( & pdev - > dev ,
struct_size ( chip , config , data - > num_irqs ) ,
2016-11-15 15:01:51 +03:00
GFP_KERNEL ) ;
2014-02-26 22:59:21 +04:00
if ( ! chip )
return - ENOMEM ;
2014-04-09 04:14:15 +04:00
platform_set_drvdata ( pdev , chip ) ;
2014-02-26 22:59:23 +04:00
chip - > regmap = regmap ;
2019-02-08 05:16:24 +03:00
chip - > num_blocks = DIV_ROUND_UP ( data - > num_irqs , 8 ) ;
2014-02-26 22:59:21 +04:00
chip - > num_masters = DIV_ROUND_UP ( chip - > num_blocks , 8 ) ;
2019-02-08 05:16:24 +03:00
chip - > pm_irq_data = data ;
2014-02-26 22:59:21 +04:00
spin_lock_init ( & chip - > pm_irq_lock ) ;
2016-11-15 15:01:51 +03:00
chip - > irqdomain = irq_domain_add_linear ( pdev - > dev . of_node ,
data - > num_irqs ,
2019-02-08 05:16:24 +03:00
& pm8xxx_irq_domain_ops ,
2014-02-26 22:59:21 +04:00
chip ) ;
if ( ! chip - > irqdomain )
return - ENODEV ;
2016-11-15 15:01:51 +03:00
irq_set_chained_handler_and_data ( irq , data - > irq_handler , chip ) ;
2014-02-26 22:59:21 +04:00
irq_set_irq_wake ( irq , 1 ) ;
rc = of_platform_populate ( pdev - > dev . of_node , NULL , NULL , & pdev - > dev ) ;
2011-04-06 01:40:53 +04:00
if ( rc ) {
2015-07-13 23:44:51 +03:00
irq_set_chained_handler_and_data ( irq , NULL , NULL ) ;
2014-02-26 22:59:21 +04:00
irq_domain_remove ( chip - > irqdomain ) ;
2011-04-06 01:40:53 +04:00
}
2014-02-26 22:59:21 +04:00
return rc ;
}
2011-04-06 01:40:53 +04:00
2016-11-19 18:15:18 +03:00
static int pm8xxx_remove_child ( struct device * dev , void * unused )
2014-02-26 22:59:21 +04:00
{
platform_device_unregister ( to_platform_device ( dev ) ) ;
2011-04-06 01:40:52 +04:00
return 0 ;
}
2016-11-19 18:15:18 +03:00
static int pm8xxx_remove ( struct platform_device * pdev )
2011-04-06 01:40:52 +04:00
{
2014-02-26 22:59:21 +04:00
int irq = platform_get_irq ( pdev , 0 ) ;
2014-04-09 04:14:15 +04:00
struct pm_irq_chip * chip = platform_get_drvdata ( pdev ) ;
2014-02-26 22:59:21 +04:00
2016-11-19 18:15:18 +03:00
device_for_each_child ( & pdev - > dev , NULL , pm8xxx_remove_child ) ;
2015-07-13 23:44:51 +03:00
irq_set_chained_handler_and_data ( irq , NULL , NULL ) ;
2014-02-26 22:59:21 +04:00
irq_domain_remove ( chip - > irqdomain ) ;
2011-04-06 01:40:52 +04:00
return 0 ;
}
2016-11-19 18:15:18 +03:00
static struct platform_driver pm8xxx_driver = {
. probe = pm8xxx_probe ,
. remove = pm8xxx_remove ,
2011-04-06 01:40:52 +04:00
. driver = {
2016-11-19 18:15:18 +03:00
. name = " pm8xxx-core " ,
. of_match_table = pm8xxx_id_table ,
2011-04-06 01:40:52 +04:00
} ,
} ;
2016-11-19 18:15:18 +03:00
static int __init pm8xxx_init ( void )
2011-04-06 01:40:52 +04:00
{
2016-11-19 18:15:18 +03:00
return platform_driver_register ( & pm8xxx_driver ) ;
2011-04-06 01:40:52 +04:00
}
2016-11-19 18:15:18 +03:00
subsys_initcall ( pm8xxx_init ) ;
2011-04-06 01:40:52 +04:00
2016-11-19 18:15:18 +03:00
static void __exit pm8xxx_exit ( void )
2011-04-06 01:40:52 +04:00
{
2016-11-19 18:15:18 +03:00
platform_driver_unregister ( & pm8xxx_driver ) ;
2011-04-06 01:40:52 +04:00
}
2016-11-19 18:15:18 +03:00
module_exit ( pm8xxx_exit ) ;
2011-04-06 01:40:52 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2016-11-19 18:15:18 +03:00
MODULE_DESCRIPTION ( " PMIC 8xxx core driver " ) ;
2011-04-06 01:40:52 +04:00
MODULE_VERSION ( " 1.0 " ) ;
2016-11-19 18:15:18 +03:00
MODULE_ALIAS ( " platform:pm8xxx-core " ) ;