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