xref: /openbmc/linux/drivers/mfd/88pm800.c (revision 9816d859)
170c6cce0SQiao Zhou /*
270c6cce0SQiao Zhou  * Base driver for Marvell 88PM800
370c6cce0SQiao Zhou  *
470c6cce0SQiao Zhou  * Copyright (C) 2012 Marvell International Ltd.
570c6cce0SQiao Zhou  * Haojian Zhuang <haojian.zhuang@marvell.com>
670c6cce0SQiao Zhou  * Joseph(Yossi) Hanin <yhanin@marvell.com>
770c6cce0SQiao Zhou  * Qiao Zhou <zhouqiao@marvell.com>
870c6cce0SQiao Zhou  *
970c6cce0SQiao Zhou  * This file is subject to the terms and conditions of the GNU General
1070c6cce0SQiao Zhou  * Public License. See the file "COPYING" in the main directory of this
1170c6cce0SQiao Zhou  * archive for more details.
1270c6cce0SQiao Zhou  *
1370c6cce0SQiao Zhou  * This program is distributed in the hope that it will be useful,
1470c6cce0SQiao Zhou  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1570c6cce0SQiao Zhou  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1670c6cce0SQiao Zhou  * GNU General Public License for more details.
1770c6cce0SQiao Zhou  *
1870c6cce0SQiao Zhou  * You should have received a copy of the GNU General Public License
1970c6cce0SQiao Zhou  * along with this program; if not, write to the Free Software
2070c6cce0SQiao Zhou  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2170c6cce0SQiao Zhou  */
2270c6cce0SQiao Zhou 
2370c6cce0SQiao Zhou #include <linux/kernel.h>
2470c6cce0SQiao Zhou #include <linux/module.h>
2552705344SChao Xie #include <linux/err.h>
2670c6cce0SQiao Zhou #include <linux/i2c.h>
2770c6cce0SQiao Zhou #include <linux/mfd/core.h>
2870c6cce0SQiao Zhou #include <linux/mfd/88pm80x.h>
2970c6cce0SQiao Zhou #include <linux/slab.h>
3070c6cce0SQiao Zhou 
3170c6cce0SQiao Zhou /* Interrupt Registers */
3270c6cce0SQiao Zhou #define PM800_INT_STATUS1		(0x05)
3370c6cce0SQiao Zhou #define PM800_ONKEY_INT_STS1		(1 << 0)
3470c6cce0SQiao Zhou #define PM800_EXTON_INT_STS1		(1 << 1)
3570c6cce0SQiao Zhou #define PM800_CHG_INT_STS1			(1 << 2)
3670c6cce0SQiao Zhou #define PM800_BAT_INT_STS1			(1 << 3)
3770c6cce0SQiao Zhou #define PM800_RTC_INT_STS1			(1 << 4)
3870c6cce0SQiao Zhou #define PM800_CLASSD_OC_INT_STS1	(1 << 5)
3970c6cce0SQiao Zhou 
4070c6cce0SQiao Zhou #define PM800_INT_STATUS2		(0x06)
4170c6cce0SQiao Zhou #define PM800_VBAT_INT_STS2		(1 << 0)
4270c6cce0SQiao Zhou #define PM800_VSYS_INT_STS2		(1 << 1)
4370c6cce0SQiao Zhou #define PM800_VCHG_INT_STS2		(1 << 2)
4470c6cce0SQiao Zhou #define PM800_TINT_INT_STS2		(1 << 3)
4570c6cce0SQiao Zhou #define PM800_GPADC0_INT_STS2	(1 << 4)
4670c6cce0SQiao Zhou #define PM800_TBAT_INT_STS2		(1 << 5)
4770c6cce0SQiao Zhou #define PM800_GPADC2_INT_STS2	(1 << 6)
4870c6cce0SQiao Zhou #define PM800_GPADC3_INT_STS2	(1 << 7)
4970c6cce0SQiao Zhou 
5070c6cce0SQiao Zhou #define PM800_INT_STATUS3		(0x07)
5170c6cce0SQiao Zhou 
5270c6cce0SQiao Zhou #define PM800_INT_STATUS4		(0x08)
5370c6cce0SQiao Zhou #define PM800_GPIO0_INT_STS4		(1 << 0)
5470c6cce0SQiao Zhou #define PM800_GPIO1_INT_STS4		(1 << 1)
5570c6cce0SQiao Zhou #define PM800_GPIO2_INT_STS4		(1 << 2)
5670c6cce0SQiao Zhou #define PM800_GPIO3_INT_STS4		(1 << 3)
5770c6cce0SQiao Zhou #define PM800_GPIO4_INT_STS4		(1 << 4)
5870c6cce0SQiao Zhou 
5970c6cce0SQiao Zhou #define PM800_INT_ENA_1		(0x09)
6070c6cce0SQiao Zhou #define PM800_ONKEY_INT_ENA1		(1 << 0)
6170c6cce0SQiao Zhou #define PM800_EXTON_INT_ENA1		(1 << 1)
6270c6cce0SQiao Zhou #define PM800_CHG_INT_ENA1			(1 << 2)
6370c6cce0SQiao Zhou #define PM800_BAT_INT_ENA1			(1 << 3)
6470c6cce0SQiao Zhou #define PM800_RTC_INT_ENA1			(1 << 4)
6570c6cce0SQiao Zhou #define PM800_CLASSD_OC_INT_ENA1	(1 << 5)
6670c6cce0SQiao Zhou 
6770c6cce0SQiao Zhou #define PM800_INT_ENA_2		(0x0A)
6870c6cce0SQiao Zhou #define PM800_VBAT_INT_ENA2		(1 << 0)
6970c6cce0SQiao Zhou #define PM800_VSYS_INT_ENA2		(1 << 1)
7070c6cce0SQiao Zhou #define PM800_VCHG_INT_ENA2		(1 << 2)
7170c6cce0SQiao Zhou #define PM800_TINT_INT_ENA2		(1 << 3)
7270c6cce0SQiao Zhou 
7370c6cce0SQiao Zhou #define PM800_INT_ENA_3		(0x0B)
7470c6cce0SQiao Zhou #define PM800_GPADC0_INT_ENA3		(1 << 0)
7570c6cce0SQiao Zhou #define PM800_GPADC1_INT_ENA3		(1 << 1)
7670c6cce0SQiao Zhou #define PM800_GPADC2_INT_ENA3		(1 << 2)
7770c6cce0SQiao Zhou #define PM800_GPADC3_INT_ENA3		(1 << 3)
7870c6cce0SQiao Zhou #define PM800_GPADC4_INT_ENA3		(1 << 4)
7970c6cce0SQiao Zhou 
8070c6cce0SQiao Zhou #define PM800_INT_ENA_4		(0x0C)
8170c6cce0SQiao Zhou #define PM800_GPIO0_INT_ENA4		(1 << 0)
8270c6cce0SQiao Zhou #define PM800_GPIO1_INT_ENA4		(1 << 1)
8370c6cce0SQiao Zhou #define PM800_GPIO2_INT_ENA4		(1 << 2)
8470c6cce0SQiao Zhou #define PM800_GPIO3_INT_ENA4		(1 << 3)
8570c6cce0SQiao Zhou #define PM800_GPIO4_INT_ENA4		(1 << 4)
8670c6cce0SQiao Zhou 
8770c6cce0SQiao Zhou /* number of INT_ENA & INT_STATUS regs */
8870c6cce0SQiao Zhou #define PM800_INT_REG_NUM			(4)
8970c6cce0SQiao Zhou 
9070c6cce0SQiao Zhou /* Interrupt Number in 88PM800 */
9170c6cce0SQiao Zhou enum {
9270c6cce0SQiao Zhou 	PM800_IRQ_ONKEY,	/*EN1b0 *//*0 */
9370c6cce0SQiao Zhou 	PM800_IRQ_EXTON,	/*EN1b1 */
9470c6cce0SQiao Zhou 	PM800_IRQ_CHG,		/*EN1b2 */
9570c6cce0SQiao Zhou 	PM800_IRQ_BAT,		/*EN1b3 */
9670c6cce0SQiao Zhou 	PM800_IRQ_RTC,		/*EN1b4 */
9770c6cce0SQiao Zhou 	PM800_IRQ_CLASSD,	/*EN1b5 *//*5 */
9870c6cce0SQiao Zhou 	PM800_IRQ_VBAT,		/*EN2b0 */
9970c6cce0SQiao Zhou 	PM800_IRQ_VSYS,		/*EN2b1 */
10070c6cce0SQiao Zhou 	PM800_IRQ_VCHG,		/*EN2b2 */
10170c6cce0SQiao Zhou 	PM800_IRQ_TINT,		/*EN2b3 */
10270c6cce0SQiao Zhou 	PM800_IRQ_GPADC0,	/*EN3b0 *//*10 */
10370c6cce0SQiao Zhou 	PM800_IRQ_GPADC1,	/*EN3b1 */
10470c6cce0SQiao Zhou 	PM800_IRQ_GPADC2,	/*EN3b2 */
10570c6cce0SQiao Zhou 	PM800_IRQ_GPADC3,	/*EN3b3 */
10670c6cce0SQiao Zhou 	PM800_IRQ_GPADC4,	/*EN3b4 */
10770c6cce0SQiao Zhou 	PM800_IRQ_GPIO0,	/*EN4b0 *//*15 */
10870c6cce0SQiao Zhou 	PM800_IRQ_GPIO1,	/*EN4b1 */
10970c6cce0SQiao Zhou 	PM800_IRQ_GPIO2,	/*EN4b2 */
11070c6cce0SQiao Zhou 	PM800_IRQ_GPIO3,	/*EN4b3 */
11170c6cce0SQiao Zhou 	PM800_IRQ_GPIO4,	/*EN4b4 *//*19 */
11270c6cce0SQiao Zhou 	PM800_MAX_IRQ,
11370c6cce0SQiao Zhou };
11470c6cce0SQiao Zhou 
11503dcc544SChao Xie /* PM800: generation identification number */
11603dcc544SChao Xie #define PM800_CHIP_GEN_ID_NUM	0x3
11770c6cce0SQiao Zhou 
11870c6cce0SQiao Zhou static const struct i2c_device_id pm80x_id_table[] = {
11903dcc544SChao Xie 	{"88PM800", 0},
12031b3ffbdSSamuel Ortiz 	{} /* NULL terminated */
12170c6cce0SQiao Zhou };
12270c6cce0SQiao Zhou MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
12370c6cce0SQiao Zhou 
124c4a164f4SRikard Falkeborn static const struct resource rtc_resources[] = {
1255c058e92SZhen Lei 	DEFINE_RES_IRQ_NAMED(PM800_IRQ_RTC, "88pm80x-rtc"),
12670c6cce0SQiao Zhou };
12770c6cce0SQiao Zhou 
12870c6cce0SQiao Zhou static struct mfd_cell rtc_devs[] = {
12970c6cce0SQiao Zhou 	{
13070c6cce0SQiao Zhou 	 .name = "88pm80x-rtc",
13170c6cce0SQiao Zhou 	 .num_resources = ARRAY_SIZE(rtc_resources),
13270c6cce0SQiao Zhou 	 .resources = &rtc_resources[0],
13370c6cce0SQiao Zhou 	 .id = -1,
13470c6cce0SQiao Zhou 	 },
13570c6cce0SQiao Zhou };
13670c6cce0SQiao Zhou 
13770c6cce0SQiao Zhou static struct resource onkey_resources[] = {
1385c058e92SZhen Lei 	DEFINE_RES_IRQ_NAMED(PM800_IRQ_ONKEY, "88pm80x-onkey"),
13970c6cce0SQiao Zhou };
14070c6cce0SQiao Zhou 
14104e02417SGeert Uytterhoeven static const struct mfd_cell onkey_devs[] = {
14270c6cce0SQiao Zhou 	{
14370c6cce0SQiao Zhou 	 .name = "88pm80x-onkey",
14470c6cce0SQiao Zhou 	 .num_resources = 1,
14570c6cce0SQiao Zhou 	 .resources = &onkey_resources[0],
14670c6cce0SQiao Zhou 	 .id = -1,
14770c6cce0SQiao Zhou 	 },
14870c6cce0SQiao Zhou };
14970c6cce0SQiao Zhou 
15004e02417SGeert Uytterhoeven static const struct mfd_cell regulator_devs[] = {
1512d3aa056SChao Xie 	{
1522d3aa056SChao Xie 	 .name = "88pm80x-regulator",
1532d3aa056SChao Xie 	 .id = -1,
1542d3aa056SChao Xie 	},
1552d3aa056SChao Xie };
1562d3aa056SChao Xie 
15770c6cce0SQiao Zhou static const struct regmap_irq pm800_irqs[] = {
15870c6cce0SQiao Zhou 	/* INT0 */
15970c6cce0SQiao Zhou 	[PM800_IRQ_ONKEY] = {
16070c6cce0SQiao Zhou 		.mask = PM800_ONKEY_INT_ENA1,
16170c6cce0SQiao Zhou 	},
16270c6cce0SQiao Zhou 	[PM800_IRQ_EXTON] = {
16370c6cce0SQiao Zhou 		.mask = PM800_EXTON_INT_ENA1,
16470c6cce0SQiao Zhou 	},
16570c6cce0SQiao Zhou 	[PM800_IRQ_CHG] = {
16670c6cce0SQiao Zhou 		.mask = PM800_CHG_INT_ENA1,
16770c6cce0SQiao Zhou 	},
16870c6cce0SQiao Zhou 	[PM800_IRQ_BAT] = {
16970c6cce0SQiao Zhou 		.mask = PM800_BAT_INT_ENA1,
17070c6cce0SQiao Zhou 	},
17170c6cce0SQiao Zhou 	[PM800_IRQ_RTC] = {
17270c6cce0SQiao Zhou 		.mask = PM800_RTC_INT_ENA1,
17370c6cce0SQiao Zhou 	},
17470c6cce0SQiao Zhou 	[PM800_IRQ_CLASSD] = {
17570c6cce0SQiao Zhou 		.mask = PM800_CLASSD_OC_INT_ENA1,
17670c6cce0SQiao Zhou 	},
17770c6cce0SQiao Zhou 	/* INT1 */
17870c6cce0SQiao Zhou 	[PM800_IRQ_VBAT] = {
17970c6cce0SQiao Zhou 		.reg_offset = 1,
18070c6cce0SQiao Zhou 		.mask = PM800_VBAT_INT_ENA2,
18170c6cce0SQiao Zhou 	},
18270c6cce0SQiao Zhou 	[PM800_IRQ_VSYS] = {
18370c6cce0SQiao Zhou 		.reg_offset = 1,
18470c6cce0SQiao Zhou 		.mask = PM800_VSYS_INT_ENA2,
18570c6cce0SQiao Zhou 	},
18670c6cce0SQiao Zhou 	[PM800_IRQ_VCHG] = {
18770c6cce0SQiao Zhou 		.reg_offset = 1,
18870c6cce0SQiao Zhou 		.mask = PM800_VCHG_INT_ENA2,
18970c6cce0SQiao Zhou 	},
19070c6cce0SQiao Zhou 	[PM800_IRQ_TINT] = {
19170c6cce0SQiao Zhou 		.reg_offset = 1,
19270c6cce0SQiao Zhou 		.mask = PM800_TINT_INT_ENA2,
19370c6cce0SQiao Zhou 	},
19470c6cce0SQiao Zhou 	/* INT2 */
19570c6cce0SQiao Zhou 	[PM800_IRQ_GPADC0] = {
19670c6cce0SQiao Zhou 		.reg_offset = 2,
19770c6cce0SQiao Zhou 		.mask = PM800_GPADC0_INT_ENA3,
19870c6cce0SQiao Zhou 	},
19970c6cce0SQiao Zhou 	[PM800_IRQ_GPADC1] = {
20070c6cce0SQiao Zhou 		.reg_offset = 2,
20170c6cce0SQiao Zhou 		.mask = PM800_GPADC1_INT_ENA3,
20270c6cce0SQiao Zhou 	},
20370c6cce0SQiao Zhou 	[PM800_IRQ_GPADC2] = {
20470c6cce0SQiao Zhou 		.reg_offset = 2,
20570c6cce0SQiao Zhou 		.mask = PM800_GPADC2_INT_ENA3,
20670c6cce0SQiao Zhou 	},
20770c6cce0SQiao Zhou 	[PM800_IRQ_GPADC3] = {
20870c6cce0SQiao Zhou 		.reg_offset = 2,
20970c6cce0SQiao Zhou 		.mask = PM800_GPADC3_INT_ENA3,
21070c6cce0SQiao Zhou 	},
21170c6cce0SQiao Zhou 	[PM800_IRQ_GPADC4] = {
21270c6cce0SQiao Zhou 		.reg_offset = 2,
21370c6cce0SQiao Zhou 		.mask = PM800_GPADC4_INT_ENA3,
21470c6cce0SQiao Zhou 	},
21570c6cce0SQiao Zhou 	/* INT3 */
21670c6cce0SQiao Zhou 	[PM800_IRQ_GPIO0] = {
21770c6cce0SQiao Zhou 		.reg_offset = 3,
21870c6cce0SQiao Zhou 		.mask = PM800_GPIO0_INT_ENA4,
21970c6cce0SQiao Zhou 	},
22070c6cce0SQiao Zhou 	[PM800_IRQ_GPIO1] = {
22170c6cce0SQiao Zhou 		.reg_offset = 3,
22270c6cce0SQiao Zhou 		.mask = PM800_GPIO1_INT_ENA4,
22370c6cce0SQiao Zhou 	},
22470c6cce0SQiao Zhou 	[PM800_IRQ_GPIO2] = {
22570c6cce0SQiao Zhou 		.reg_offset = 3,
22670c6cce0SQiao Zhou 		.mask = PM800_GPIO2_INT_ENA4,
22770c6cce0SQiao Zhou 	},
22870c6cce0SQiao Zhou 	[PM800_IRQ_GPIO3] = {
22970c6cce0SQiao Zhou 		.reg_offset = 3,
23070c6cce0SQiao Zhou 		.mask = PM800_GPIO3_INT_ENA4,
23170c6cce0SQiao Zhou 	},
23270c6cce0SQiao Zhou 	[PM800_IRQ_GPIO4] = {
23370c6cce0SQiao Zhou 		.reg_offset = 3,
23470c6cce0SQiao Zhou 		.mask = PM800_GPIO4_INT_ENA4,
23570c6cce0SQiao Zhou 	},
23670c6cce0SQiao Zhou };
23770c6cce0SQiao Zhou 
device_gpadc_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)238f791be49SBill Pemberton static int device_gpadc_init(struct pm80x_chip *chip,
23970c6cce0SQiao Zhou 				       struct pm80x_platform_data *pdata)
24070c6cce0SQiao Zhou {
24170c6cce0SQiao Zhou 	struct pm80x_subchip *subchip = chip->subchip;
24270c6cce0SQiao Zhou 	struct regmap *map = subchip->regmap_gpadc;
24370c6cce0SQiao Zhou 	int data = 0, mask = 0, ret = 0;
24470c6cce0SQiao Zhou 
24570c6cce0SQiao Zhou 	if (!map) {
24670c6cce0SQiao Zhou 		dev_warn(chip->dev,
24770c6cce0SQiao Zhou 			 "Warning: gpadc regmap is not available!\n");
24870c6cce0SQiao Zhou 		return -EINVAL;
24970c6cce0SQiao Zhou 	}
25070c6cce0SQiao Zhou 	/*
25170c6cce0SQiao Zhou 	 * initialize GPADC without activating it turn on GPADC
25270c6cce0SQiao Zhou 	 * measurments
25370c6cce0SQiao Zhou 	 */
25470c6cce0SQiao Zhou 	ret = regmap_update_bits(map,
25570c6cce0SQiao Zhou 				 PM800_GPADC_MISC_CONFIG2,
25670c6cce0SQiao Zhou 				 PM800_GPADC_MISC_GPFSM_EN,
25770c6cce0SQiao Zhou 				 PM800_GPADC_MISC_GPFSM_EN);
25870c6cce0SQiao Zhou 	if (ret < 0)
25970c6cce0SQiao Zhou 		goto out;
26070c6cce0SQiao Zhou 	/*
26170c6cce0SQiao Zhou 	 * This function configures the ADC as requires for
26270c6cce0SQiao Zhou 	 * CP implementation.CP does not "own" the ADC configuration
26370c6cce0SQiao Zhou 	 * registers and relies on AP.
26470c6cce0SQiao Zhou 	 * Reason: enable automatic ADC measurements needed
26570c6cce0SQiao Zhou 	 * for CP to get VBAT and RF temperature readings.
26670c6cce0SQiao Zhou 	 */
26770c6cce0SQiao Zhou 	ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1,
26870c6cce0SQiao Zhou 				 PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT);
26970c6cce0SQiao Zhou 	if (ret < 0)
27070c6cce0SQiao Zhou 		goto out;
27170c6cce0SQiao Zhou 	ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2,
27270c6cce0SQiao Zhou 				 (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN),
27370c6cce0SQiao Zhou 				 (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN));
27470c6cce0SQiao Zhou 	if (ret < 0)
27570c6cce0SQiao Zhou 		goto out;
27670c6cce0SQiao Zhou 
27770c6cce0SQiao Zhou 	/*
27870c6cce0SQiao Zhou 	 * the defult of PM800 is GPADC operates at 100Ks/s rate
27970c6cce0SQiao Zhou 	 * and Number of GPADC slots with active current bias prior
28070c6cce0SQiao Zhou 	 * to GPADC sampling = 1 slot for all GPADCs set for
28170c6cce0SQiao Zhou 	 * Temprature mesurmants
28270c6cce0SQiao Zhou 	 */
28370c6cce0SQiao Zhou 	mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
28470c6cce0SQiao Zhou 		PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
28570c6cce0SQiao Zhou 
28670c6cce0SQiao Zhou 	if (pdata && (pdata->batt_det == 0))
28770c6cce0SQiao Zhou 		data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
28870c6cce0SQiao Zhou 			PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
28970c6cce0SQiao Zhou 	else
29070c6cce0SQiao Zhou 		data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 |
29170c6cce0SQiao Zhou 			PM800_GPADC_GP_BIAS_EN3);
29270c6cce0SQiao Zhou 
29370c6cce0SQiao Zhou 	ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data);
29470c6cce0SQiao Zhou 	if (ret < 0)
29570c6cce0SQiao Zhou 		goto out;
29670c6cce0SQiao Zhou 
29770c6cce0SQiao Zhou 	dev_info(chip->dev, "pm800 device_gpadc_init: Done\n");
29870c6cce0SQiao Zhou 	return 0;
29970c6cce0SQiao Zhou 
30070c6cce0SQiao Zhou out:
30170c6cce0SQiao Zhou 	dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n");
30270c6cce0SQiao Zhou 	return ret;
30370c6cce0SQiao Zhou }
30470c6cce0SQiao Zhou 
device_onkey_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)3053a3ece54SChao Xie static int device_onkey_init(struct pm80x_chip *chip,
3063a3ece54SChao Xie 				struct pm80x_platform_data *pdata)
3073a3ece54SChao Xie {
3083a3ece54SChao Xie 	int ret;
3093a3ece54SChao Xie 
3103a3ece54SChao Xie 	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
3113a3ece54SChao Xie 			      ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
3123a3ece54SChao Xie 			      NULL);
3133a3ece54SChao Xie 	if (ret) {
3143a3ece54SChao Xie 		dev_err(chip->dev, "Failed to add onkey subdev\n");
3153a3ece54SChao Xie 		return ret;
3163a3ece54SChao Xie 	}
3173a3ece54SChao Xie 
3183a3ece54SChao Xie 	return 0;
3193a3ece54SChao Xie }
3203a3ece54SChao Xie 
device_rtc_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)3213a3ece54SChao Xie static int device_rtc_init(struct pm80x_chip *chip,
3223a3ece54SChao Xie 				struct pm80x_platform_data *pdata)
3233a3ece54SChao Xie {
3243a3ece54SChao Xie 	int ret;
3253a3ece54SChao Xie 
326b432fc25SChao Xie 	if (pdata) {
3273a3ece54SChao Xie 		rtc_devs[0].platform_data = pdata->rtc;
3283a3ece54SChao Xie 		rtc_devs[0].pdata_size =
3293a3ece54SChao Xie 				pdata->rtc ? sizeof(struct pm80x_rtc_pdata) : 0;
330b432fc25SChao Xie 	}
3313a3ece54SChao Xie 	ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
3323a3ece54SChao Xie 			      ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
3333a3ece54SChao Xie 	if (ret) {
3343a3ece54SChao Xie 		dev_err(chip->dev, "Failed to add rtc subdev\n");
3353a3ece54SChao Xie 		return ret;
3363a3ece54SChao Xie 	}
3373a3ece54SChao Xie 
3383a3ece54SChao Xie 	return 0;
3393a3ece54SChao Xie }
3403a3ece54SChao Xie 
device_regulator_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)3412d3aa056SChao Xie static int device_regulator_init(struct pm80x_chip *chip,
3422d3aa056SChao Xie 					   struct pm80x_platform_data *pdata)
3432d3aa056SChao Xie {
3442d3aa056SChao Xie 	int ret;
3452d3aa056SChao Xie 
3462d3aa056SChao Xie 	ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
3472d3aa056SChao Xie 			      ARRAY_SIZE(regulator_devs), NULL, 0, NULL);
3482d3aa056SChao Xie 	if (ret) {
3492d3aa056SChao Xie 		dev_err(chip->dev, "Failed to add regulator subdev\n");
3502d3aa056SChao Xie 		return ret;
3512d3aa056SChao Xie 	}
3522d3aa056SChao Xie 
3532d3aa056SChao Xie 	return 0;
3542d3aa056SChao Xie }
3552d3aa056SChao Xie 
device_irq_init_800(struct pm80x_chip * chip)356f791be49SBill Pemberton static int device_irq_init_800(struct pm80x_chip *chip)
35770c6cce0SQiao Zhou {
35870c6cce0SQiao Zhou 	struct regmap *map = chip->regmap;
3591ef5677eSYi Zhang 	unsigned long flags = IRQF_ONESHOT;
36070c6cce0SQiao Zhou 	int data, mask, ret = -EINVAL;
36170c6cce0SQiao Zhou 
36270c6cce0SQiao Zhou 	if (!map || !chip->irq) {
36370c6cce0SQiao Zhou 		dev_err(chip->dev, "incorrect parameters\n");
36470c6cce0SQiao Zhou 		return -EINVAL;
36570c6cce0SQiao Zhou 	}
36670c6cce0SQiao Zhou 
36770c6cce0SQiao Zhou 	/*
36870c6cce0SQiao Zhou 	 * irq_mode defines the way of clearing interrupt. it's read-clear by
36970c6cce0SQiao Zhou 	 * default.
37070c6cce0SQiao Zhou 	 */
37170c6cce0SQiao Zhou 	mask =
37270c6cce0SQiao Zhou 	    PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR |
37370c6cce0SQiao Zhou 	    PM800_WAKEUP2_INT_MASK;
37470c6cce0SQiao Zhou 
37570c6cce0SQiao Zhou 	data = PM800_WAKEUP2_INT_CLEAR;
37670c6cce0SQiao Zhou 	ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data);
37770c6cce0SQiao Zhou 
37870c6cce0SQiao Zhou 	if (ret < 0)
37970c6cce0SQiao Zhou 		goto out;
38070c6cce0SQiao Zhou 
38170c6cce0SQiao Zhou 	ret =
38270c6cce0SQiao Zhou 	    regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
38370c6cce0SQiao Zhou 				chip->regmap_irq_chip, &chip->irq_data);
38470c6cce0SQiao Zhou 
38570c6cce0SQiao Zhou out:
38670c6cce0SQiao Zhou 	return ret;
38770c6cce0SQiao Zhou }
38870c6cce0SQiao Zhou 
device_irq_exit_800(struct pm80x_chip * chip)38970c6cce0SQiao Zhou static void device_irq_exit_800(struct pm80x_chip *chip)
39070c6cce0SQiao Zhou {
39170c6cce0SQiao Zhou 	regmap_del_irq_chip(chip->irq, chip->irq_data);
39270c6cce0SQiao Zhou }
39370c6cce0SQiao Zhou 
39470c6cce0SQiao Zhou static struct regmap_irq_chip pm800_irq_chip = {
39570c6cce0SQiao Zhou 	.name = "88pm800",
39670c6cce0SQiao Zhou 	.irqs = pm800_irqs,
39770c6cce0SQiao Zhou 	.num_irqs = ARRAY_SIZE(pm800_irqs),
39870c6cce0SQiao Zhou 
39970c6cce0SQiao Zhou 	.num_regs = 4,
40070c6cce0SQiao Zhou 	.status_base = PM800_INT_STATUS1,
4010eeb2ddcSAidan MacDonald 	.unmask_base = PM800_INT_ENA_1,
40270c6cce0SQiao Zhou 	.ack_base = PM800_INT_STATUS1,
40370c6cce0SQiao Zhou };
40470c6cce0SQiao Zhou 
pm800_pages_init(struct pm80x_chip * chip)40570c6cce0SQiao Zhou static int pm800_pages_init(struct pm80x_chip *chip)
40670c6cce0SQiao Zhou {
40770c6cce0SQiao Zhou 	struct pm80x_subchip *subchip;
40870c6cce0SQiao Zhou 	struct i2c_client *client = chip->client;
40970c6cce0SQiao Zhou 
41052705344SChao Xie 	int ret = 0;
41170c6cce0SQiao Zhou 
41252705344SChao Xie 	subchip = chip->subchip;
41352705344SChao Xie 	if (!subchip || !subchip->power_page_addr || !subchip->gpadc_page_addr)
41452705344SChao Xie 		return -ENODEV;
41552705344SChao Xie 
41652705344SChao Xie 	/* PM800 block power page */
41783215897SWolfram Sang 	subchip->power_page = i2c_new_dummy_device(client->adapter,
41852705344SChao Xie 					    subchip->power_page_addr);
41983215897SWolfram Sang 	if (IS_ERR(subchip->power_page)) {
42083215897SWolfram Sang 		ret = PTR_ERR(subchip->power_page);
42152705344SChao Xie 		goto out;
42252705344SChao Xie 	}
42352705344SChao Xie 
42452705344SChao Xie 	subchip->regmap_power = devm_regmap_init_i2c(subchip->power_page,
42552705344SChao Xie 						     &pm80x_regmap_config);
42652705344SChao Xie 	if (IS_ERR(subchip->regmap_power)) {
42752705344SChao Xie 		ret = PTR_ERR(subchip->regmap_power);
42852705344SChao Xie 		dev_err(chip->dev,
42952705344SChao Xie 			"Failed to allocate regmap_power: %d\n", ret);
43052705344SChao Xie 		goto out;
43152705344SChao Xie 	}
43252705344SChao Xie 
43352705344SChao Xie 	i2c_set_clientdata(subchip->power_page, chip);
43452705344SChao Xie 
43552705344SChao Xie 	/* PM800 block GPADC */
43683215897SWolfram Sang 	subchip->gpadc_page = i2c_new_dummy_device(client->adapter,
43770c6cce0SQiao Zhou 					    subchip->gpadc_page_addr);
43883215897SWolfram Sang 	if (IS_ERR(subchip->gpadc_page)) {
43983215897SWolfram Sang 		ret = PTR_ERR(subchip->gpadc_page);
44052705344SChao Xie 		goto out;
44152705344SChao Xie 	}
44270c6cce0SQiao Zhou 
44352705344SChao Xie 	subchip->regmap_gpadc = devm_regmap_init_i2c(subchip->gpadc_page,
44452705344SChao Xie 						     &pm80x_regmap_config);
44552705344SChao Xie 	if (IS_ERR(subchip->regmap_gpadc)) {
44652705344SChao Xie 		ret = PTR_ERR(subchip->regmap_gpadc);
44752705344SChao Xie 		dev_err(chip->dev,
44852705344SChao Xie 			"Failed to allocate regmap_gpadc: %d\n", ret);
44952705344SChao Xie 		goto out;
45052705344SChao Xie 	}
45152705344SChao Xie 	i2c_set_clientdata(subchip->gpadc_page, chip);
45252705344SChao Xie 
45352705344SChao Xie out:
45452705344SChao Xie 	return ret;
45570c6cce0SQiao Zhou }
45670c6cce0SQiao Zhou 
pm800_pages_exit(struct pm80x_chip * chip)45770c6cce0SQiao Zhou static void pm800_pages_exit(struct pm80x_chip *chip)
45870c6cce0SQiao Zhou {
45970c6cce0SQiao Zhou 	struct pm80x_subchip *subchip;
46070c6cce0SQiao Zhou 
46170c6cce0SQiao Zhou 	subchip = chip->subchip;
46252705344SChao Xie 
46352705344SChao Xie 	if (subchip && subchip->power_page)
46470c6cce0SQiao Zhou 		i2c_unregister_device(subchip->power_page);
46552705344SChao Xie 
46652705344SChao Xie 	if (subchip && subchip->gpadc_page)
46770c6cce0SQiao Zhou 		i2c_unregister_device(subchip->gpadc_page);
46870c6cce0SQiao Zhou }
46970c6cce0SQiao Zhou 
device_800_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)470f791be49SBill Pemberton static int device_800_init(struct pm80x_chip *chip,
47170c6cce0SQiao Zhou 				     struct pm80x_platform_data *pdata)
47270c6cce0SQiao Zhou {
47303dcc544SChao Xie 	int ret;
47446b65a8fSAxel Lin 	unsigned int val;
47570c6cce0SQiao Zhou 
47670c6cce0SQiao Zhou 	/*
47770c6cce0SQiao Zhou 	 * alarm wake up bit will be clear in device_irq_init(),
47870c6cce0SQiao Zhou 	 * read before that
47970c6cce0SQiao Zhou 	 */
48046b65a8fSAxel Lin 	ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val);
48170c6cce0SQiao Zhou 	if (ret < 0) {
48270c6cce0SQiao Zhou 		dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
48370c6cce0SQiao Zhou 		goto out;
48470c6cce0SQiao Zhou 	}
48546b65a8fSAxel Lin 	if (val & PM800_ALARM_WAKEUP) {
48670c6cce0SQiao Zhou 		if (pdata && pdata->rtc)
48770c6cce0SQiao Zhou 			pdata->rtc->rtc_wakeup = 1;
48870c6cce0SQiao Zhou 	}
48970c6cce0SQiao Zhou 
49070c6cce0SQiao Zhou 	ret = device_gpadc_init(chip, pdata);
49170c6cce0SQiao Zhou 	if (ret < 0) {
49270c6cce0SQiao Zhou 		dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__);
49370c6cce0SQiao Zhou 		goto out;
49470c6cce0SQiao Zhou 	}
49570c6cce0SQiao Zhou 
49670c6cce0SQiao Zhou 	chip->regmap_irq_chip = &pm800_irq_chip;
49770c6cce0SQiao Zhou 
49870c6cce0SQiao Zhou 	ret = device_irq_init_800(chip);
49970c6cce0SQiao Zhou 	if (ret < 0) {
50070c6cce0SQiao Zhou 		dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__);
50170c6cce0SQiao Zhou 		goto out;
50270c6cce0SQiao Zhou 	}
50370c6cce0SQiao Zhou 
5043a3ece54SChao Xie 	ret = device_onkey_init(chip, pdata);
5053a3ece54SChao Xie 	if (ret) {
50670c6cce0SQiao Zhou 		dev_err(chip->dev, "Failed to add onkey subdev\n");
50770c6cce0SQiao Zhou 		goto out_dev;
5083a3ece54SChao Xie 	}
50970c6cce0SQiao Zhou 
5103a3ece54SChao Xie 	ret = device_rtc_init(chip, pdata);
5113a3ece54SChao Xie 	if (ret) {
51270c6cce0SQiao Zhou 		dev_err(chip->dev, "Failed to add rtc subdev\n");
5133a3ece54SChao Xie 		goto out;
51470c6cce0SQiao Zhou 	}
51570c6cce0SQiao Zhou 
5162d3aa056SChao Xie 	ret = device_regulator_init(chip, pdata);
5172d3aa056SChao Xie 	if (ret) {
5182d3aa056SChao Xie 		dev_err(chip->dev, "Failed to add regulators subdev\n");
5192d3aa056SChao Xie 		goto out;
5202d3aa056SChao Xie 	}
5212d3aa056SChao Xie 
52270c6cce0SQiao Zhou 	return 0;
52370c6cce0SQiao Zhou out_dev:
52470c6cce0SQiao Zhou 	mfd_remove_devices(chip->dev);
52570c6cce0SQiao Zhou 	device_irq_exit_800(chip);
52670c6cce0SQiao Zhou out:
52770c6cce0SQiao Zhou 	return ret;
52870c6cce0SQiao Zhou }
52970c6cce0SQiao Zhou 
pm800_probe(struct i2c_client * client)530b9420da8SUwe Kleine-König static int pm800_probe(struct i2c_client *client)
53170c6cce0SQiao Zhou {
53270c6cce0SQiao Zhou 	int ret = 0;
53370c6cce0SQiao Zhou 	struct pm80x_chip *chip;
534334a41ceSJingoo Han 	struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev);
53570c6cce0SQiao Zhou 	struct pm80x_subchip *subchip;
53670c6cce0SQiao Zhou 
53703dcc544SChao Xie 	ret = pm80x_init(client);
53870c6cce0SQiao Zhou 	if (ret) {
53970c6cce0SQiao Zhou 		dev_err(&client->dev, "pm800_init fail\n");
54070c6cce0SQiao Zhou 		goto out_init;
54170c6cce0SQiao Zhou 	}
54270c6cce0SQiao Zhou 
54370c6cce0SQiao Zhou 	chip = i2c_get_clientdata(client);
54470c6cce0SQiao Zhou 
54570c6cce0SQiao Zhou 	/* init subchip for PM800 */
54670c6cce0SQiao Zhou 	subchip =
54770c6cce0SQiao Zhou 	    devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip),
54870c6cce0SQiao Zhou 			 GFP_KERNEL);
54970c6cce0SQiao Zhou 	if (!subchip) {
55070c6cce0SQiao Zhou 		ret = -ENOMEM;
55170c6cce0SQiao Zhou 		goto err_subchip_alloc;
55270c6cce0SQiao Zhou 	}
55370c6cce0SQiao Zhou 
554c750d8e0SChao Xie 	/* pm800 has 2 addtional pages to support power and gpadc. */
555c750d8e0SChao Xie 	subchip->power_page_addr = client->addr + 1;
556c750d8e0SChao Xie 	subchip->gpadc_page_addr = client->addr + 2;
55770c6cce0SQiao Zhou 	chip->subchip = subchip;
55870c6cce0SQiao Zhou 
55970c6cce0SQiao Zhou 	ret = pm800_pages_init(chip);
56070c6cce0SQiao Zhou 	if (ret) {
56170c6cce0SQiao Zhou 		dev_err(&client->dev, "pm800_pages_init failed!\n");
562141050cfSKrzysztof Kozlowski 		goto err_device_init;
56370c6cce0SQiao Zhou 	}
56470c6cce0SQiao Zhou 
565618fa575SYi Zhang 	ret = device_800_init(chip, pdata);
566618fa575SYi Zhang 	if (ret) {
56703dcc544SChao Xie 		dev_err(chip->dev, "Failed to initialize 88pm800 devices\n");
568618fa575SYi Zhang 		goto err_device_init;
569618fa575SYi Zhang 	}
570618fa575SYi Zhang 
571b432fc25SChao Xie 	if (pdata && pdata->plat_config)
57270c6cce0SQiao Zhou 		pdata->plat_config(chip, pdata);
57370c6cce0SQiao Zhou 
574618fa575SYi Zhang 	return 0;
575618fa575SYi Zhang 
576618fa575SYi Zhang err_device_init:
577618fa575SYi Zhang 	pm800_pages_exit(chip);
57870c6cce0SQiao Zhou err_subchip_alloc:
579306df798SYi Zhang 	pm80x_deinit();
58070c6cce0SQiao Zhou out_init:
58170c6cce0SQiao Zhou 	return ret;
58270c6cce0SQiao Zhou }
58370c6cce0SQiao Zhou 
pm800_remove(struct i2c_client * client)584ed5c2f5fSUwe Kleine-König static void pm800_remove(struct i2c_client *client)
58570c6cce0SQiao Zhou {
58670c6cce0SQiao Zhou 	struct pm80x_chip *chip = i2c_get_clientdata(client);
58770c6cce0SQiao Zhou 
58870c6cce0SQiao Zhou 	mfd_remove_devices(chip->dev);
58970c6cce0SQiao Zhou 	device_irq_exit_800(chip);
59070c6cce0SQiao Zhou 
59170c6cce0SQiao Zhou 	pm800_pages_exit(chip);
592306df798SYi Zhang 	pm80x_deinit();
59370c6cce0SQiao Zhou }
59470c6cce0SQiao Zhou 
59570c6cce0SQiao Zhou static struct i2c_driver pm800_driver = {
59670c6cce0SQiao Zhou 	.driver = {
59746223a19SChao Xie 		.name = "88PM800",
59819755a0aSPaul Cercueil 		.pm = pm_sleep_ptr(&pm80x_pm_ops),
59970c6cce0SQiao Zhou 		},
600*9816d859SUwe Kleine-König 	.probe = pm800_probe,
60184449216SBill Pemberton 	.remove = pm800_remove,
60270c6cce0SQiao Zhou 	.id_table = pm80x_id_table,
60370c6cce0SQiao Zhou };
60470c6cce0SQiao Zhou 
pm800_i2c_init(void)60570c6cce0SQiao Zhou static int __init pm800_i2c_init(void)
60670c6cce0SQiao Zhou {
60770c6cce0SQiao Zhou 	return i2c_add_driver(&pm800_driver);
60870c6cce0SQiao Zhou }
60970c6cce0SQiao Zhou subsys_initcall(pm800_i2c_init);
61070c6cce0SQiao Zhou 
pm800_i2c_exit(void)61170c6cce0SQiao Zhou static void __exit pm800_i2c_exit(void)
61270c6cce0SQiao Zhou {
61370c6cce0SQiao Zhou 	i2c_del_driver(&pm800_driver);
61470c6cce0SQiao Zhou }
61570c6cce0SQiao Zhou module_exit(pm800_i2c_exit);
61670c6cce0SQiao Zhou 
61770c6cce0SQiao Zhou MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800");
61870c6cce0SQiao Zhou MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
61970c6cce0SQiao Zhou MODULE_LICENSE("GPL");
620