xref: /OK3568_Linux_fs/kernel/drivers/mfd/lp8788.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * TI LP8788 MFD - core interface
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright 2012 Texas Instruments
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/err.h>
11*4882a593Smuzhiyun #include <linux/i2c.h>
12*4882a593Smuzhiyun #include <linux/mfd/core.h>
13*4882a593Smuzhiyun #include <linux/mfd/lp8788.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #define MAX_LP8788_REGISTERS		0xA2
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define MFD_DEV_SIMPLE(_name)					\
20*4882a593Smuzhiyun {								\
21*4882a593Smuzhiyun 	.name = LP8788_DEV_##_name,				\
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define MFD_DEV_WITH_ID(_name, _id)				\
25*4882a593Smuzhiyun {								\
26*4882a593Smuzhiyun 	.name = LP8788_DEV_##_name,				\
27*4882a593Smuzhiyun 	.id = _id,						\
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define MFD_DEV_WITH_RESOURCE(_name, _resource, num_resource)	\
31*4882a593Smuzhiyun {								\
32*4882a593Smuzhiyun 	.name = LP8788_DEV_##_name,				\
33*4882a593Smuzhiyun 	.resources = _resource,					\
34*4882a593Smuzhiyun 	.num_resources = num_resource,				\
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun static struct resource chg_irqs[] = {
38*4882a593Smuzhiyun 	/* Charger Interrupts */
39*4882a593Smuzhiyun 	{
40*4882a593Smuzhiyun 		.start = LP8788_INT_CHG_INPUT_STATE,
41*4882a593Smuzhiyun 		.end   = LP8788_INT_PRECHG_TIMEOUT,
42*4882a593Smuzhiyun 		.name  = LP8788_CHG_IRQ,
43*4882a593Smuzhiyun 		.flags = IORESOURCE_IRQ,
44*4882a593Smuzhiyun 	},
45*4882a593Smuzhiyun 	/* Power Routing Switch Interrupts */
46*4882a593Smuzhiyun 	{
47*4882a593Smuzhiyun 		.start = LP8788_INT_ENTER_SYS_SUPPORT,
48*4882a593Smuzhiyun 		.end   = LP8788_INT_EXIT_SYS_SUPPORT,
49*4882a593Smuzhiyun 		.name  = LP8788_PRSW_IRQ,
50*4882a593Smuzhiyun 		.flags = IORESOURCE_IRQ,
51*4882a593Smuzhiyun 	},
52*4882a593Smuzhiyun 	/* Battery Interrupts */
53*4882a593Smuzhiyun 	{
54*4882a593Smuzhiyun 		.start = LP8788_INT_BATT_LOW,
55*4882a593Smuzhiyun 		.end   = LP8788_INT_NO_BATT,
56*4882a593Smuzhiyun 		.name  = LP8788_BATT_IRQ,
57*4882a593Smuzhiyun 		.flags = IORESOURCE_IRQ,
58*4882a593Smuzhiyun 	},
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun static struct resource rtc_irqs[] = {
62*4882a593Smuzhiyun 	{
63*4882a593Smuzhiyun 		.start = LP8788_INT_RTC_ALARM1,
64*4882a593Smuzhiyun 		.end   = LP8788_INT_RTC_ALARM2,
65*4882a593Smuzhiyun 		.name  = LP8788_ALM_IRQ,
66*4882a593Smuzhiyun 		.flags = IORESOURCE_IRQ,
67*4882a593Smuzhiyun 	},
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun static const struct mfd_cell lp8788_devs[] = {
71*4882a593Smuzhiyun 	/* 4 bucks */
72*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(BUCK, 1),
73*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(BUCK, 2),
74*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(BUCK, 3),
75*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(BUCK, 4),
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	/* 12 digital ldos */
78*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 1),
79*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 2),
80*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 3),
81*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 4),
82*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 5),
83*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 6),
84*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 7),
85*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 8),
86*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 9),
87*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 10),
88*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 11),
89*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(DLDO, 12),
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	/* 10 analog ldos */
92*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 1),
93*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 2),
94*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 3),
95*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 4),
96*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 5),
97*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 6),
98*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 7),
99*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 8),
100*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 9),
101*4882a593Smuzhiyun 	MFD_DEV_WITH_ID(ALDO, 10),
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	/* ADC */
104*4882a593Smuzhiyun 	MFD_DEV_SIMPLE(ADC),
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	/* battery charger */
107*4882a593Smuzhiyun 	MFD_DEV_WITH_RESOURCE(CHARGER, chg_irqs, ARRAY_SIZE(chg_irqs)),
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	/* rtc */
110*4882a593Smuzhiyun 	MFD_DEV_WITH_RESOURCE(RTC, rtc_irqs, ARRAY_SIZE(rtc_irqs)),
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	/* backlight */
113*4882a593Smuzhiyun 	MFD_DEV_SIMPLE(BACKLIGHT),
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	/* current sink for vibrator */
116*4882a593Smuzhiyun 	MFD_DEV_SIMPLE(VIBRATOR),
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	/* current sink for keypad LED */
119*4882a593Smuzhiyun 	MFD_DEV_SIMPLE(KEYLED),
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun 
lp8788_read_byte(struct lp8788 * lp,u8 reg,u8 * data)122*4882a593Smuzhiyun int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	int ret;
125*4882a593Smuzhiyun 	unsigned int val;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	ret = regmap_read(lp->regmap, reg, &val);
128*4882a593Smuzhiyun 	if (ret < 0) {
129*4882a593Smuzhiyun 		dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
130*4882a593Smuzhiyun 		return ret;
131*4882a593Smuzhiyun 	}
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	*data = (u8)val;
134*4882a593Smuzhiyun 	return 0;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lp8788_read_byte);
137*4882a593Smuzhiyun 
lp8788_read_multi_bytes(struct lp8788 * lp,u8 reg,u8 * data,size_t count)138*4882a593Smuzhiyun int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	return regmap_bulk_read(lp->regmap, reg, data, count);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes);
143*4882a593Smuzhiyun 
lp8788_write_byte(struct lp8788 * lp,u8 reg,u8 data)144*4882a593Smuzhiyun int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	return regmap_write(lp->regmap, reg, data);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lp8788_write_byte);
149*4882a593Smuzhiyun 
lp8788_update_bits(struct lp8788 * lp,u8 reg,u8 mask,u8 data)150*4882a593Smuzhiyun int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	return regmap_update_bits(lp->regmap, reg, mask, data);
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lp8788_update_bits);
155*4882a593Smuzhiyun 
lp8788_platform_init(struct lp8788 * lp)156*4882a593Smuzhiyun static int lp8788_platform_init(struct lp8788 *lp)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	struct lp8788_platform_data *pdata = lp->pdata;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	return (pdata && pdata->init_func) ? pdata->init_func(lp) : 0;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun static const struct regmap_config lp8788_regmap_config = {
164*4882a593Smuzhiyun 	.reg_bits = 8,
165*4882a593Smuzhiyun 	.val_bits = 8,
166*4882a593Smuzhiyun 	.max_register = MAX_LP8788_REGISTERS,
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun 
lp8788_probe(struct i2c_client * cl,const struct i2c_device_id * id)169*4882a593Smuzhiyun static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	struct lp8788 *lp;
172*4882a593Smuzhiyun 	struct lp8788_platform_data *pdata = dev_get_platdata(&cl->dev);
173*4882a593Smuzhiyun 	int ret;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL);
176*4882a593Smuzhiyun 	if (!lp)
177*4882a593Smuzhiyun 		return -ENOMEM;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	lp->regmap = devm_regmap_init_i2c(cl, &lp8788_regmap_config);
180*4882a593Smuzhiyun 	if (IS_ERR(lp->regmap)) {
181*4882a593Smuzhiyun 		ret = PTR_ERR(lp->regmap);
182*4882a593Smuzhiyun 		dev_err(&cl->dev, "regmap init i2c err: %d\n", ret);
183*4882a593Smuzhiyun 		return ret;
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	lp->pdata = pdata;
187*4882a593Smuzhiyun 	lp->dev = &cl->dev;
188*4882a593Smuzhiyun 	i2c_set_clientdata(cl, lp);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	ret = lp8788_platform_init(lp);
191*4882a593Smuzhiyun 	if (ret)
192*4882a593Smuzhiyun 		return ret;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	ret = lp8788_irq_init(lp, cl->irq);
195*4882a593Smuzhiyun 	if (ret)
196*4882a593Smuzhiyun 		return ret;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	ret = mfd_add_devices(lp->dev, -1, lp8788_devs,
199*4882a593Smuzhiyun 			      ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
200*4882a593Smuzhiyun 	if (ret)
201*4882a593Smuzhiyun 		goto err_exit_irq;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	return 0;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun err_exit_irq:
206*4882a593Smuzhiyun 	lp8788_irq_exit(lp);
207*4882a593Smuzhiyun 	return ret;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
lp8788_remove(struct i2c_client * cl)210*4882a593Smuzhiyun static int lp8788_remove(struct i2c_client *cl)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	struct lp8788 *lp = i2c_get_clientdata(cl);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	mfd_remove_devices(lp->dev);
215*4882a593Smuzhiyun 	lp8788_irq_exit(lp);
216*4882a593Smuzhiyun 	return 0;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun static const struct i2c_device_id lp8788_ids[] = {
220*4882a593Smuzhiyun 	{"lp8788", 0},
221*4882a593Smuzhiyun 	{ }
222*4882a593Smuzhiyun };
223*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, lp8788_ids);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun static struct i2c_driver lp8788_driver = {
226*4882a593Smuzhiyun 	.driver = {
227*4882a593Smuzhiyun 		.name = "lp8788",
228*4882a593Smuzhiyun 	},
229*4882a593Smuzhiyun 	.probe = lp8788_probe,
230*4882a593Smuzhiyun 	.remove = lp8788_remove,
231*4882a593Smuzhiyun 	.id_table = lp8788_ids,
232*4882a593Smuzhiyun };
233*4882a593Smuzhiyun 
lp8788_init(void)234*4882a593Smuzhiyun static int __init lp8788_init(void)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	return i2c_add_driver(&lp8788_driver);
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun subsys_initcall(lp8788_init);
239*4882a593Smuzhiyun 
lp8788_exit(void)240*4882a593Smuzhiyun static void __exit lp8788_exit(void)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	i2c_del_driver(&lp8788_driver);
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun module_exit(lp8788_exit);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun MODULE_DESCRIPTION("TI LP8788 MFD Driver");
247*4882a593Smuzhiyun MODULE_AUTHOR("Milo Kim");
248*4882a593Smuzhiyun MODULE_LICENSE("GPL");
249