2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-02-22 15:15:29 +03:00
/*
* Copyright ( c ) 2014 MediaTek Inc .
* Author : Flora Fu , MediaTek
*/
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/regmap.h>
# include <linux/mfd/core.h>
# include <linux/mfd/mt6397/core.h>
2016-01-27 14:47:38 +03:00
# include <linux/mfd/mt6323/core.h>
2015-02-22 15:15:29 +03:00
# include <linux/mfd/mt6397/registers.h>
2016-01-27 14:47:38 +03:00
# include <linux/mfd/mt6323/registers.h>
2015-02-22 15:15:29 +03:00
2015-05-06 10:23:40 +03:00
# define MT6397_RTC_BASE 0xe000
# define MT6397_RTC_SIZE 0x3e
2016-01-27 14:47:38 +03:00
# define MT6323_CID_CODE 0x23
2016-01-27 14:47:37 +03:00
# define MT6391_CID_CODE 0x91
# define MT6397_CID_CODE 0x97
2015-05-06 10:23:40 +03:00
static const struct resource mt6397_rtc_resources [ ] = {
{
. start = MT6397_RTC_BASE ,
. end = MT6397_RTC_BASE + MT6397_RTC_SIZE ,
. flags = IORESOURCE_MEM ,
} ,
{
. start = MT6397_IRQ_RTC ,
. end = MT6397_IRQ_RTC ,
. flags = IORESOURCE_IRQ ,
} ,
} ;
2017-10-25 16:16:04 +03:00
static const struct resource mt6323_keys_resources [ ] = {
DEFINE_RES_IRQ ( MT6323_IRQ_STATUS_PWRKEY ) ,
DEFINE_RES_IRQ ( MT6323_IRQ_STATUS_FCHRKEY ) ,
} ;
static const struct resource mt6397_keys_resources [ ] = {
DEFINE_RES_IRQ ( MT6397_IRQ_PWRKEY ) ,
DEFINE_RES_IRQ ( MT6397_IRQ_HOMEKEY ) ,
} ;
2016-01-27 14:47:38 +03:00
static const struct mfd_cell mt6323_devs [ ] = {
{
. name = " mt6323-regulator " ,
. of_compatible = " mediatek,mt6323-regulator "
2017-03-20 09:47:27 +03:00
} , {
2017-01-23 06:54:45 +03:00
. name = " mt6323-led " ,
. of_compatible = " mediatek,mt6323-led "
2017-10-25 16:16:04 +03:00
} , {
. name = " mtk-pmic-keys " ,
. num_resources = ARRAY_SIZE ( mt6323_keys_resources ) ,
. resources = mt6323_keys_resources ,
. of_compatible = " mediatek,mt6323-keys "
2017-01-23 06:54:45 +03:00
} ,
2016-01-27 14:47:38 +03:00
} ;
2015-02-22 15:15:29 +03:00
static const struct mfd_cell mt6397_devs [ ] = {
{
. name = " mt6397-rtc " ,
2015-05-06 10:23:40 +03:00
. num_resources = ARRAY_SIZE ( mt6397_rtc_resources ) ,
. resources = mt6397_rtc_resources ,
2015-02-22 15:15:29 +03:00
. of_compatible = " mediatek,mt6397-rtc " ,
} , {
. name = " mt6397-regulator " ,
. of_compatible = " mediatek,mt6397-regulator " ,
} , {
. name = " mt6397-codec " ,
. of_compatible = " mediatek,mt6397-codec " ,
} , {
. name = " mt6397-clk " ,
. of_compatible = " mediatek,mt6397-clk " ,
2015-05-27 12:10:35 +03:00
} , {
. name = " mt6397-pinctrl " ,
. of_compatible = " mediatek,mt6397-pinctrl " ,
2017-10-25 16:16:04 +03:00
} , {
. name = " mtk-pmic-keys " ,
. num_resources = ARRAY_SIZE ( mt6397_keys_resources ) ,
. resources = mt6397_keys_resources ,
. of_compatible = " mediatek,mt6397-keys "
}
2015-02-22 15:15:29 +03:00
} ;
static void mt6397_irq_lock ( struct irq_data * data )
{
2015-07-13 23:44:56 +03:00
struct mt6397_chip * mt6397 = irq_data_get_irq_chip_data ( data ) ;
2015-02-22 15:15:29 +03:00
mutex_lock ( & mt6397 - > irqlock ) ;
}
static void mt6397_irq_sync_unlock ( struct irq_data * data )
{
2015-07-13 23:44:56 +03:00
struct mt6397_chip * mt6397 = irq_data_get_irq_chip_data ( data ) ;
2015-02-22 15:15:29 +03:00
2016-01-27 14:47:36 +03:00
regmap_write ( mt6397 - > regmap , mt6397 - > int_con [ 0 ] ,
mt6397 - > irq_masks_cur [ 0 ] ) ;
regmap_write ( mt6397 - > regmap , mt6397 - > int_con [ 1 ] ,
mt6397 - > irq_masks_cur [ 1 ] ) ;
2015-02-22 15:15:29 +03:00
mutex_unlock ( & mt6397 - > irqlock ) ;
}
static void mt6397_irq_disable ( struct irq_data * data )
{
2015-07-13 23:44:56 +03:00
struct mt6397_chip * mt6397 = irq_data_get_irq_chip_data ( data ) ;
2015-02-22 15:15:29 +03:00
int shift = data - > hwirq & 0xf ;
int reg = data - > hwirq > > 4 ;
mt6397 - > irq_masks_cur [ reg ] & = ~ BIT ( shift ) ;
}
static void mt6397_irq_enable ( struct irq_data * data )
{
2015-07-13 23:44:56 +03:00
struct mt6397_chip * mt6397 = irq_data_get_irq_chip_data ( data ) ;
2015-02-22 15:15:29 +03:00
int shift = data - > hwirq & 0xf ;
int reg = data - > hwirq > > 4 ;
mt6397 - > irq_masks_cur [ reg ] | = BIT ( shift ) ;
}
2015-08-10 16:10:45 +03:00
# ifdef CONFIG_PM_SLEEP
static int mt6397_irq_set_wake ( struct irq_data * irq_data , unsigned int on )
{
struct mt6397_chip * mt6397 = irq_data_get_irq_chip_data ( irq_data ) ;
int shift = irq_data - > hwirq & 0xf ;
int reg = irq_data - > hwirq > > 4 ;
if ( on )
mt6397 - > wake_mask [ reg ] | = BIT ( shift ) ;
else
mt6397 - > wake_mask [ reg ] & = ~ BIT ( shift ) ;
return 0 ;
}
# else
# define mt6397_irq_set_wake NULL
# endif
2015-02-22 15:15:29 +03:00
static struct irq_chip mt6397_irq_chip = {
. name = " mt6397-irq " ,
. irq_bus_lock = mt6397_irq_lock ,
. irq_bus_sync_unlock = mt6397_irq_sync_unlock ,
. irq_enable = mt6397_irq_enable ,
. irq_disable = mt6397_irq_disable ,
2015-08-10 16:10:45 +03:00
. irq_set_wake = mt6397_irq_set_wake ,
2015-02-22 15:15:29 +03:00
} ;
static void mt6397_irq_handle_reg ( struct mt6397_chip * mt6397 , int reg ,
int irqbase )
{
unsigned int status ;
int i , irq , ret ;
ret = regmap_read ( mt6397 - > regmap , reg , & status ) ;
if ( ret ) {
dev_err ( mt6397 - > dev , " Failed to read irq status: %d \n " , ret ) ;
return ;
}
for ( i = 0 ; i < 16 ; i + + ) {
if ( status & BIT ( i ) ) {
irq = irq_find_mapping ( mt6397 - > irq_domain , irqbase + i ) ;
if ( irq )
handle_nested_irq ( irq ) ;
}
}
regmap_write ( mt6397 - > regmap , reg , status ) ;
}
static irqreturn_t mt6397_irq_thread ( int irq , void * data )
{
struct mt6397_chip * mt6397 = data ;
2016-01-27 14:47:36 +03:00
mt6397_irq_handle_reg ( mt6397 , mt6397 - > int_status [ 0 ] , 0 ) ;
mt6397_irq_handle_reg ( mt6397 , mt6397 - > int_status [ 1 ] , 16 ) ;
2015-02-22 15:15:29 +03:00
return IRQ_HANDLED ;
}
static int mt6397_irq_domain_map ( struct irq_domain * d , unsigned int irq ,
irq_hw_number_t hw )
{
struct mt6397_chip * mt6397 = d - > host_data ;
irq_set_chip_data ( irq , mt6397 ) ;
irq_set_chip_and_handler ( irq , & mt6397_irq_chip , handle_level_irq ) ;
irq_set_nested_thread ( irq , 1 ) ;
irq_set_noprobe ( irq ) ;
return 0 ;
}
2015-04-27 15:54:13 +03:00
static const struct irq_domain_ops mt6397_irq_domain_ops = {
2015-02-22 15:15:29 +03:00
. map = mt6397_irq_domain_map ,
} ;
static int mt6397_irq_init ( struct mt6397_chip * mt6397 )
{
int ret ;
mutex_init ( & mt6397 - > irqlock ) ;
/* Mask all interrupt sources */
2016-01-27 14:47:36 +03:00
regmap_write ( mt6397 - > regmap , mt6397 - > int_con [ 0 ] , 0x0 ) ;
regmap_write ( mt6397 - > regmap , mt6397 - > int_con [ 1 ] , 0x0 ) ;
2015-02-22 15:15:29 +03:00
mt6397 - > irq_domain = irq_domain_add_linear ( mt6397 - > dev - > of_node ,
MT6397_IRQ_NR , & mt6397_irq_domain_ops , mt6397 ) ;
if ( ! mt6397 - > irq_domain ) {
dev_err ( mt6397 - > dev , " could not create irq domain \n " ) ;
return - ENOMEM ;
}
ret = devm_request_threaded_irq ( mt6397 - > dev , mt6397 - > irq , NULL ,
mt6397_irq_thread , IRQF_ONESHOT , " mt6397-pmic " , mt6397 ) ;
if ( ret ) {
dev_err ( mt6397 - > dev , " failed to register irq=%d; err: %d \n " ,
mt6397 - > irq , ret ) ;
return ret ;
}
return 0 ;
}
2015-08-10 16:10:45 +03:00
# ifdef CONFIG_PM_SLEEP
static int mt6397_irq_suspend ( struct device * dev )
{
struct mt6397_chip * chip = dev_get_drvdata ( dev ) ;
2016-01-27 14:47:36 +03:00
regmap_write ( chip - > regmap , chip - > int_con [ 0 ] , chip - > wake_mask [ 0 ] ) ;
regmap_write ( chip - > regmap , chip - > int_con [ 1 ] , chip - > wake_mask [ 1 ] ) ;
2015-08-10 16:10:45 +03:00
enable_irq_wake ( chip - > irq ) ;
return 0 ;
}
static int mt6397_irq_resume ( struct device * dev )
{
struct mt6397_chip * chip = dev_get_drvdata ( dev ) ;
2016-01-27 14:47:36 +03:00
regmap_write ( chip - > regmap , chip - > int_con [ 0 ] , chip - > irq_masks_cur [ 0 ] ) ;
regmap_write ( chip - > regmap , chip - > int_con [ 1 ] , chip - > irq_masks_cur [ 1 ] ) ;
2015-08-10 16:10:45 +03:00
disable_irq_wake ( chip - > irq ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( mt6397_pm_ops , mt6397_irq_suspend ,
mt6397_irq_resume ) ;
2015-02-22 15:15:29 +03:00
static int mt6397_probe ( struct platform_device * pdev )
{
int ret ;
2016-01-27 14:47:37 +03:00
unsigned int id ;
struct mt6397_chip * pmic ;
2015-02-22 15:15:29 +03:00
2016-01-27 14:47:37 +03:00
pmic = devm_kzalloc ( & pdev - > dev , sizeof ( * pmic ) , GFP_KERNEL ) ;
if ( ! pmic )
2015-02-22 15:15:29 +03:00
return - ENOMEM ;
2016-01-27 14:47:37 +03:00
pmic - > dev = & pdev - > dev ;
2016-01-27 14:47:36 +03:00
2015-02-22 15:15:29 +03:00
/*
* mt6397 MFD is child device of soc pmic wrapper .
* Regmap is set from its parent .
*/
2016-01-27 14:47:37 +03:00
pmic - > regmap = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! pmic - > regmap )
2015-02-22 15:15:29 +03:00
return - ENODEV ;
2016-01-27 14:47:37 +03:00
platform_set_drvdata ( pdev , pmic ) ;
ret = regmap_read ( pmic - > regmap , MT6397_CID , & id ) ;
if ( ret ) {
dev_err ( pmic - > dev , " Failed to read chip id: %d \n " , ret ) ;
2016-04-15 11:30:29 +03:00
return ret ;
2016-01-27 14:47:37 +03:00
}
2016-04-15 11:30:29 +03:00
pmic - > irq = platform_get_irq ( pdev , 0 ) ;
if ( pmic - > irq < = 0 )
return pmic - > irq ;
2016-01-27 14:47:37 +03:00
switch ( id & 0xff ) {
2016-01-27 14:47:38 +03:00
case MT6323_CID_CODE :
pmic - > int_con [ 0 ] = MT6323_INT_CON0 ;
pmic - > int_con [ 1 ] = MT6323_INT_CON1 ;
pmic - > int_status [ 0 ] = MT6323_INT_STATUS0 ;
pmic - > int_status [ 1 ] = MT6323_INT_STATUS1 ;
2016-04-15 11:30:29 +03:00
ret = mt6397_irq_init ( pmic ) ;
if ( ret )
return ret ;
2016-04-07 21:43:04 +03:00
ret = devm_mfd_add_devices ( & pdev - > dev , - 1 , mt6323_devs ,
ARRAY_SIZE ( mt6323_devs ) , NULL ,
2017-10-25 16:15:59 +03:00
0 , pmic - > irq_domain ) ;
2016-01-27 14:47:38 +03:00
break ;
2016-01-27 14:47:37 +03:00
case MT6397_CID_CODE :
case MT6391_CID_CODE :
pmic - > int_con [ 0 ] = MT6397_INT_CON0 ;
pmic - > int_con [ 1 ] = MT6397_INT_CON1 ;
pmic - > int_status [ 0 ] = MT6397_INT_STATUS0 ;
pmic - > int_status [ 1 ] = MT6397_INT_STATUS1 ;
2016-04-15 11:30:29 +03:00
ret = mt6397_irq_init ( pmic ) ;
if ( ret )
return ret ;
2016-04-07 21:43:04 +03:00
ret = devm_mfd_add_devices ( & pdev - > dev , - 1 , mt6397_devs ,
ARRAY_SIZE ( mt6397_devs ) , NULL ,
2017-10-25 16:15:59 +03:00
0 , pmic - > irq_domain ) ;
2016-01-27 14:47:37 +03:00
break ;
default :
dev_err ( & pdev - > dev , " unsupported chip: %d \n " , id ) ;
2018-10-22 05:55:06 +03:00
return - ENODEV ;
2016-01-27 14:47:37 +03:00
}
2015-02-22 15:15:29 +03:00
2016-01-27 14:47:37 +03:00
if ( ret ) {
irq_domain_remove ( pmic - > irq_domain ) ;
2015-02-22 15:15:29 +03:00
dev_err ( & pdev - > dev , " failed to add child devices: %d \n " , ret ) ;
2016-01-27 14:47:37 +03:00
}
2015-02-22 15:15:29 +03:00
return ret ;
}
static const struct of_device_id mt6397_of_match [ ] = {
{ . compatible = " mediatek,mt6397 " } ,
2016-01-27 14:47:38 +03:00
{ . compatible = " mediatek,mt6323 " } ,
2015-02-22 15:15:29 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , mt6397_of_match ) ;
2016-02-10 19:50:18 +03:00
static const struct platform_device_id mt6397_id [ ] = {
{ " mt6397 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , mt6397_id ) ;
2015-02-22 15:15:29 +03:00
static struct platform_driver mt6397_driver = {
. probe = mt6397_probe ,
. driver = {
. name = " mt6397 " ,
. of_match_table = of_match_ptr ( mt6397_of_match ) ,
2015-08-10 16:10:45 +03:00
. pm = & mt6397_pm_ops ,
2015-02-22 15:15:29 +03:00
} ,
2016-02-10 19:50:18 +03:00
. id_table = mt6397_id ,
2015-02-22 15:15:29 +03:00
} ;
module_platform_driver ( mt6397_driver ) ;
MODULE_AUTHOR ( " Flora Fu, MediaTek " ) ;
MODULE_DESCRIPTION ( " Driver for MediaTek MT6397 PMIC " ) ;
MODULE_LICENSE ( " GPL " ) ;