1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c6c19332SMike Rapoport /*
3c6c19332SMike Rapoport * Core driver for TI TPS6586x PMIC family
4c6c19332SMike Rapoport *
5c6c19332SMike Rapoport * Copyright (c) 2010 CompuLab Ltd.
6c6c19332SMike Rapoport * Mike Rapoport <mike@compulab.co.il>
7c6c19332SMike Rapoport *
8c6c19332SMike Rapoport * Based on da903x.c.
9c6c19332SMike Rapoport * Copyright (C) 2008 Compulab, Ltd.
10c6c19332SMike Rapoport * Mike Rapoport <mike@compulab.co.il>
11c6c19332SMike Rapoport * Copyright (C) 2006-2008 Marvell International Ltd.
12c6c19332SMike Rapoport * Eric Miao <eric.miao@marvell.com>
13c6c19332SMike Rapoport */
14c6c19332SMike Rapoport
15c26448c4SGary King #include <linux/interrupt.h>
16c26448c4SGary King #include <linux/irq.h>
17605511a8SLaxman Dewangan #include <linux/irqdomain.h>
18c6c19332SMike Rapoport #include <linux/kernel.h>
19c6c19332SMike Rapoport #include <linux/module.h>
20c6c19332SMike Rapoport #include <linux/mutex.h>
21c6c19332SMike Rapoport #include <linux/slab.h>
221176b5beSLaxman Dewangan #include <linux/err.h>
23c6c19332SMike Rapoport #include <linux/i2c.h>
24605511a8SLaxman Dewangan #include <linux/platform_device.h>
251176b5beSLaxman Dewangan #include <linux/regmap.h>
2606bf3c2fSSachin Kamat #include <linux/of.h>
27c6c19332SMike Rapoport
28c6c19332SMike Rapoport #include <linux/mfd/core.h>
29c6c19332SMike Rapoport #include <linux/mfd/tps6586x.h>
30c6c19332SMike Rapoport
31004c15a6SBill Huang #define TPS6586X_SUPPLYENE 0x14
32004c15a6SBill Huang #define EXITSLREQ_BIT BIT(1)
33004c15a6SBill Huang #define SLEEP_MODE_BIT BIT(3)
34004c15a6SBill Huang
35c26448c4SGary King /* interrupt control registers */
36c26448c4SGary King #define TPS6586X_INT_ACK1 0xb5
37c26448c4SGary King #define TPS6586X_INT_ACK2 0xb6
38c26448c4SGary King #define TPS6586X_INT_ACK3 0xb7
39c26448c4SGary King #define TPS6586X_INT_ACK4 0xb8
40c26448c4SGary King
41c26448c4SGary King /* interrupt mask registers */
42c26448c4SGary King #define TPS6586X_INT_MASK1 0xb0
43c26448c4SGary King #define TPS6586X_INT_MASK2 0xb1
44c26448c4SGary King #define TPS6586X_INT_MASK3 0xb2
45c26448c4SGary King #define TPS6586X_INT_MASK4 0xb3
46c26448c4SGary King #define TPS6586X_INT_MASK5 0xb4
47c26448c4SGary King
48c6c19332SMike Rapoport /* device id */
49c6c19332SMike Rapoport #define TPS6586X_VERSIONCRC 0xcd
50c6c19332SMike Rapoport
511176b5beSLaxman Dewangan /* Maximum register */
52088d862cSAxel Lin #define TPS6586X_MAX_REGISTER TPS6586X_VERSIONCRC
531176b5beSLaxman Dewangan
54c26448c4SGary King struct tps6586x_irq_data {
55c26448c4SGary King u8 mask_reg;
56c26448c4SGary King u8 mask_mask;
57c26448c4SGary King };
58c26448c4SGary King
59c26448c4SGary King #define TPS6586X_IRQ(_reg, _mask) \
60c26448c4SGary King { \
61c26448c4SGary King .mask_reg = (_reg) - TPS6586X_INT_MASK1, \
62c26448c4SGary King .mask_mask = (_mask), \
63c26448c4SGary King }
64c26448c4SGary King
65c26448c4SGary King static const struct tps6586x_irq_data tps6586x_irqs[] = {
66c26448c4SGary King [TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0),
67c26448c4SGary King [TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1),
68c26448c4SGary King [TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2),
69c26448c4SGary King [TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3),
70c26448c4SGary King [TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4),
71c26448c4SGary King [TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5),
72c26448c4SGary King [TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6),
73c26448c4SGary King [TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7),
74c26448c4SGary King [TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0),
75c26448c4SGary King [TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1),
76c26448c4SGary King [TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2),
77c26448c4SGary King [TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3),
78c26448c4SGary King [TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4),
79c26448c4SGary King [TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5),
80c26448c4SGary King [TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6),
81c26448c4SGary King [TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7),
82c26448c4SGary King [TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4),
83c26448c4SGary King [TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03),
84c26448c4SGary King [TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2),
85c26448c4SGary King [TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3),
86c26448c4SGary King [TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0),
87c26448c4SGary King [TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc),
88c26448c4SGary King [TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06),
89c26448c4SGary King [TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0),
90c26448c4SGary King [TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5),
91c26448c4SGary King [TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6),
92c26448c4SGary King [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
93c26448c4SGary King };
94c26448c4SGary King
95c4a164f4SRikard Falkeborn static const struct resource tps6586x_rtc_resources[] = {
965b8b1fe2SLaxman Dewangan {
975b8b1fe2SLaxman Dewangan .start = TPS6586X_INT_RTC_ALM1,
985b8b1fe2SLaxman Dewangan .end = TPS6586X_INT_RTC_ALM1,
995b8b1fe2SLaxman Dewangan .flags = IORESOURCE_IRQ,
1005b8b1fe2SLaxman Dewangan },
1015b8b1fe2SLaxman Dewangan };
1025b8b1fe2SLaxman Dewangan
10330fe2b5bSGeert Uytterhoeven static const struct mfd_cell tps6586x_cell[] = {
1047a7487cbSLaxman Dewangan {
1057a7487cbSLaxman Dewangan .name = "tps6586x-gpio",
1067a7487cbSLaxman Dewangan },
1077a7487cbSLaxman Dewangan {
108ec8da805SMarc Dietrich .name = "tps6586x-regulator",
10964e48160SLaxman Dewangan },
11064e48160SLaxman Dewangan {
1117a7487cbSLaxman Dewangan .name = "tps6586x-rtc",
1125b8b1fe2SLaxman Dewangan .num_resources = ARRAY_SIZE(tps6586x_rtc_resources),
1135b8b1fe2SLaxman Dewangan .resources = &tps6586x_rtc_resources[0],
1147a7487cbSLaxman Dewangan },
1157a7487cbSLaxman Dewangan {
1167a7487cbSLaxman Dewangan .name = "tps6586x-onkey",
1177a7487cbSLaxman Dewangan },
1187a7487cbSLaxman Dewangan };
1197a7487cbSLaxman Dewangan
120c6c19332SMike Rapoport struct tps6586x {
121c6c19332SMike Rapoport struct device *dev;
122c6c19332SMike Rapoport struct i2c_client *client;
1231176b5beSLaxman Dewangan struct regmap *regmap;
124e0a3da80SStefan Agner int version;
125c6c19332SMike Rapoport
126234506adSStephen Warren int irq;
127c26448c4SGary King struct irq_chip irq_chip;
128c26448c4SGary King struct mutex irq_lock;
129c26448c4SGary King int irq_base;
130c26448c4SGary King u32 irq_en;
131c26448c4SGary King u8 mask_reg[5];
132605511a8SLaxman Dewangan struct irq_domain *irq_domain;
133c6c19332SMike Rapoport };
134c6c19332SMike Rapoport
dev_to_tps6586x(struct device * dev)1351176b5beSLaxman Dewangan static inline struct tps6586x *dev_to_tps6586x(struct device *dev)
136c6c19332SMike Rapoport {
1371176b5beSLaxman Dewangan return i2c_get_clientdata(to_i2c_client(dev));
138c6c19332SMike Rapoport }
139c6c19332SMike Rapoport
tps6586x_write(struct device * dev,int reg,uint8_t val)140c6c19332SMike Rapoport int tps6586x_write(struct device *dev, int reg, uint8_t val)
141c6c19332SMike Rapoport {
1421176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
1431176b5beSLaxman Dewangan
1441176b5beSLaxman Dewangan return regmap_write(tps6586x->regmap, reg, val);
145c6c19332SMike Rapoport }
146c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_write);
147c6c19332SMike Rapoport
tps6586x_writes(struct device * dev,int reg,int len,uint8_t * val)148c6c19332SMike Rapoport int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
149c6c19332SMike Rapoport {
1501176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
1511176b5beSLaxman Dewangan
1521176b5beSLaxman Dewangan return regmap_bulk_write(tps6586x->regmap, reg, val, len);
153c6c19332SMike Rapoport }
154c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_writes);
155c6c19332SMike Rapoport
tps6586x_read(struct device * dev,int reg,uint8_t * val)156c6c19332SMike Rapoport int tps6586x_read(struct device *dev, int reg, uint8_t *val)
157c6c19332SMike Rapoport {
1581176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
1591176b5beSLaxman Dewangan unsigned int rval;
1601176b5beSLaxman Dewangan int ret;
1611176b5beSLaxman Dewangan
1621176b5beSLaxman Dewangan ret = regmap_read(tps6586x->regmap, reg, &rval);
1631176b5beSLaxman Dewangan if (!ret)
1641176b5beSLaxman Dewangan *val = rval;
1651176b5beSLaxman Dewangan return ret;
166c6c19332SMike Rapoport }
167c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_read);
168c6c19332SMike Rapoport
tps6586x_reads(struct device * dev,int reg,int len,uint8_t * val)169c6c19332SMike Rapoport int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
170c6c19332SMike Rapoport {
1711176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
1721176b5beSLaxman Dewangan
1731176b5beSLaxman Dewangan return regmap_bulk_read(tps6586x->regmap, reg, val, len);
174c6c19332SMike Rapoport }
175c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_reads);
176c6c19332SMike Rapoport
tps6586x_set_bits(struct device * dev,int reg,uint8_t bit_mask)177c6c19332SMike Rapoport int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
178c6c19332SMike Rapoport {
1791176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
180c6c19332SMike Rapoport
1811176b5beSLaxman Dewangan return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask);
182c6c19332SMike Rapoport }
183c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_set_bits);
184c6c19332SMike Rapoport
tps6586x_clr_bits(struct device * dev,int reg,uint8_t bit_mask)185c6c19332SMike Rapoport int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
186c6c19332SMike Rapoport {
1871176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
188c6c19332SMike Rapoport
1891176b5beSLaxman Dewangan return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0);
190c6c19332SMike Rapoport }
191c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
192c6c19332SMike Rapoport
tps6586x_update(struct device * dev,int reg,uint8_t val,uint8_t mask)193c6c19332SMike Rapoport int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
194c6c19332SMike Rapoport {
1951176b5beSLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
196c6c19332SMike Rapoport
1971176b5beSLaxman Dewangan return regmap_update_bits(tps6586x->regmap, reg, mask, val);
198c6c19332SMike Rapoport }
199c6c19332SMike Rapoport EXPORT_SYMBOL_GPL(tps6586x_update);
200c6c19332SMike Rapoport
tps6586x_irq_get_virq(struct device * dev,int irq)201605511a8SLaxman Dewangan int tps6586x_irq_get_virq(struct device *dev, int irq)
202605511a8SLaxman Dewangan {
203605511a8SLaxman Dewangan struct tps6586x *tps6586x = dev_to_tps6586x(dev);
204605511a8SLaxman Dewangan
205605511a8SLaxman Dewangan return irq_create_mapping(tps6586x->irq_domain, irq);
206605511a8SLaxman Dewangan }
207605511a8SLaxman Dewangan EXPORT_SYMBOL_GPL(tps6586x_irq_get_virq);
208605511a8SLaxman Dewangan
tps6586x_get_version(struct device * dev)209e0a3da80SStefan Agner int tps6586x_get_version(struct device *dev)
210e0a3da80SStefan Agner {
211e0a3da80SStefan Agner struct tps6586x *tps6586x = dev_get_drvdata(dev);
212e0a3da80SStefan Agner
213e0a3da80SStefan Agner return tps6586x->version;
214e0a3da80SStefan Agner }
215e0a3da80SStefan Agner EXPORT_SYMBOL_GPL(tps6586x_get_version);
216e0a3da80SStefan Agner
__remove_subdev(struct device * dev,void * unused)217c6c19332SMike Rapoport static int __remove_subdev(struct device *dev, void *unused)
218c6c19332SMike Rapoport {
219c6c19332SMike Rapoport platform_device_unregister(to_platform_device(dev));
220c6c19332SMike Rapoport return 0;
221c6c19332SMike Rapoport }
222c6c19332SMike Rapoport
tps6586x_remove_subdevs(struct tps6586x * tps6586x)223c6c19332SMike Rapoport static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
224c6c19332SMike Rapoport {
225c6c19332SMike Rapoport return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
226c6c19332SMike Rapoport }
227c6c19332SMike Rapoport
tps6586x_irq_lock(struct irq_data * data)22896e824bdSMark Brown static void tps6586x_irq_lock(struct irq_data *data)
229c26448c4SGary King {
23096e824bdSMark Brown struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data);
231c26448c4SGary King
232c26448c4SGary King mutex_lock(&tps6586x->irq_lock);
233c26448c4SGary King }
234c26448c4SGary King
tps6586x_irq_enable(struct irq_data * irq_data)23596e824bdSMark Brown static void tps6586x_irq_enable(struct irq_data *irq_data)
236c26448c4SGary King {
23796e824bdSMark Brown struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
238605511a8SLaxman Dewangan unsigned int __irq = irq_data->hwirq;
239c26448c4SGary King const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
240c26448c4SGary King
241c26448c4SGary King tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask;
242c26448c4SGary King tps6586x->irq_en |= (1 << __irq);
243c26448c4SGary King }
244c26448c4SGary King
tps6586x_irq_disable(struct irq_data * irq_data)24596e824bdSMark Brown static void tps6586x_irq_disable(struct irq_data *irq_data)
246c26448c4SGary King {
24796e824bdSMark Brown struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
248c26448c4SGary King
249605511a8SLaxman Dewangan unsigned int __irq = irq_data->hwirq;
250c26448c4SGary King const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
251c26448c4SGary King
252c26448c4SGary King tps6586x->mask_reg[data->mask_reg] |= data->mask_mask;
253c26448c4SGary King tps6586x->irq_en &= ~(1 << __irq);
254c26448c4SGary King }
255c26448c4SGary King
tps6586x_irq_sync_unlock(struct irq_data * data)25696e824bdSMark Brown static void tps6586x_irq_sync_unlock(struct irq_data *data)
257c26448c4SGary King {
25896e824bdSMark Brown struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data);
259c26448c4SGary King int i;
260c26448c4SGary King
261c26448c4SGary King for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
26275edd5afSLaxman Dewangan int ret;
26375edd5afSLaxman Dewangan ret = tps6586x_write(tps6586x->dev,
264c26448c4SGary King TPS6586X_INT_MASK1 + i,
26575edd5afSLaxman Dewangan tps6586x->mask_reg[i]);
26675edd5afSLaxman Dewangan WARN_ON(ret);
267c26448c4SGary King }
268c26448c4SGary King
269c26448c4SGary King mutex_unlock(&tps6586x->irq_lock);
270c26448c4SGary King }
271c26448c4SGary King
tps6586x_irq_set_wake(struct irq_data * irq_data,unsigned int on)272234506adSStephen Warren static int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on)
273234506adSStephen Warren {
274234506adSStephen Warren struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
275234506adSStephen Warren return irq_set_irq_wake(tps6586x->irq, on);
276234506adSStephen Warren }
277234506adSStephen Warren
278605511a8SLaxman Dewangan static struct irq_chip tps6586x_irq_chip = {
279605511a8SLaxman Dewangan .name = "tps6586x",
280605511a8SLaxman Dewangan .irq_bus_lock = tps6586x_irq_lock,
281605511a8SLaxman Dewangan .irq_bus_sync_unlock = tps6586x_irq_sync_unlock,
282605511a8SLaxman Dewangan .irq_disable = tps6586x_irq_disable,
283605511a8SLaxman Dewangan .irq_enable = tps6586x_irq_enable,
284*d115e88cSPaul Cercueil .irq_set_wake = pm_sleep_ptr(tps6586x_irq_set_wake),
285605511a8SLaxman Dewangan };
286605511a8SLaxman Dewangan
tps6586x_irq_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)287605511a8SLaxman Dewangan static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
288605511a8SLaxman Dewangan irq_hw_number_t hw)
289605511a8SLaxman Dewangan {
290605511a8SLaxman Dewangan struct tps6586x *tps6586x = h->host_data;
291605511a8SLaxman Dewangan
292605511a8SLaxman Dewangan irq_set_chip_data(virq, tps6586x);
293605511a8SLaxman Dewangan irq_set_chip_and_handler(virq, &tps6586x_irq_chip, handle_simple_irq);
294605511a8SLaxman Dewangan irq_set_nested_thread(virq, 1);
295605511a8SLaxman Dewangan irq_set_noprobe(virq);
296605511a8SLaxman Dewangan
297605511a8SLaxman Dewangan return 0;
298605511a8SLaxman Dewangan }
299605511a8SLaxman Dewangan
3007ce7b26fSKrzysztof Kozlowski static const struct irq_domain_ops tps6586x_domain_ops = {
301605511a8SLaxman Dewangan .map = tps6586x_irq_map,
302605511a8SLaxman Dewangan .xlate = irq_domain_xlate_twocell,
303605511a8SLaxman Dewangan };
304605511a8SLaxman Dewangan
tps6586x_irq(int irq,void * data)305c26448c4SGary King static irqreturn_t tps6586x_irq(int irq, void *data)
306c26448c4SGary King {
307c26448c4SGary King struct tps6586x *tps6586x = data;
308c504a248SLee Jones uint32_t acks;
309c504a248SLee Jones __le32 val;
310c26448c4SGary King int ret = 0;
311c26448c4SGary King
312c26448c4SGary King ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1,
313c504a248SLee Jones sizeof(acks), (uint8_t *)&val);
314c26448c4SGary King
315c26448c4SGary King if (ret < 0) {
316c26448c4SGary King dev_err(tps6586x->dev, "failed to read interrupt status\n");
317c26448c4SGary King return IRQ_NONE;
318c26448c4SGary King }
319c26448c4SGary King
320c504a248SLee Jones acks = le32_to_cpu(val);
321c26448c4SGary King
322c26448c4SGary King while (acks) {
323c26448c4SGary King int i = __ffs(acks);
324c26448c4SGary King
325c26448c4SGary King if (tps6586x->irq_en & (1 << i))
326605511a8SLaxman Dewangan handle_nested_irq(
327605511a8SLaxman Dewangan irq_find_mapping(tps6586x->irq_domain, i));
328c26448c4SGary King
329c26448c4SGary King acks &= ~(1 << i);
330c26448c4SGary King }
331c26448c4SGary King
332c26448c4SGary King return IRQ_HANDLED;
333c26448c4SGary King }
334c26448c4SGary King
tps6586x_irq_init(struct tps6586x * tps6586x,int irq,int irq_base)335f791be49SBill Pemberton static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
336c26448c4SGary King int irq_base)
337c26448c4SGary King {
338c26448c4SGary King int i, ret;
339c26448c4SGary King u8 tmp[4];
340605511a8SLaxman Dewangan int new_irq_base;
341605511a8SLaxman Dewangan int irq_num = ARRAY_SIZE(tps6586x_irqs);
342c26448c4SGary King
343234506adSStephen Warren tps6586x->irq = irq;
344234506adSStephen Warren
345c26448c4SGary King mutex_init(&tps6586x->irq_lock);
346c26448c4SGary King for (i = 0; i < 5; i++) {
347c26448c4SGary King tps6586x->mask_reg[i] = 0xff;
348c26448c4SGary King tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
349c26448c4SGary King }
350c26448c4SGary King
351c26448c4SGary King tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp);
352c26448c4SGary King
353605511a8SLaxman Dewangan if (irq_base > 0) {
354605511a8SLaxman Dewangan new_irq_base = irq_alloc_descs(irq_base, 0, irq_num, -1);
355605511a8SLaxman Dewangan if (new_irq_base < 0) {
356605511a8SLaxman Dewangan dev_err(tps6586x->dev,
357605511a8SLaxman Dewangan "Failed to alloc IRQs: %d\n", new_irq_base);
358605511a8SLaxman Dewangan return new_irq_base;
359605511a8SLaxman Dewangan }
360605511a8SLaxman Dewangan } else {
361605511a8SLaxman Dewangan new_irq_base = 0;
362c26448c4SGary King }
363c26448c4SGary King
364605511a8SLaxman Dewangan tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node,
365605511a8SLaxman Dewangan irq_num, new_irq_base, &tps6586x_domain_ops,
366605511a8SLaxman Dewangan tps6586x);
367605511a8SLaxman Dewangan if (!tps6586x->irq_domain) {
368605511a8SLaxman Dewangan dev_err(tps6586x->dev, "Failed to create IRQ domain\n");
369605511a8SLaxman Dewangan return -ENOMEM;
370605511a8SLaxman Dewangan }
371c26448c4SGary King ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
372c26448c4SGary King "tps6586x", tps6586x);
373c26448c4SGary King
374234506adSStephen Warren if (!ret)
375c26448c4SGary King device_init_wakeup(tps6586x->dev, 1);
376c26448c4SGary King
377c26448c4SGary King return ret;
378c26448c4SGary King }
379c26448c4SGary King
tps6586x_add_subdevs(struct tps6586x * tps6586x,struct tps6586x_platform_data * pdata)380f791be49SBill Pemberton static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
381c6c19332SMike Rapoport struct tps6586x_platform_data *pdata)
382c6c19332SMike Rapoport {
383c6c19332SMike Rapoport struct tps6586x_subdev_info *subdev;
384c6c19332SMike Rapoport struct platform_device *pdev;
385c6c19332SMike Rapoport int i, ret = 0;
386c6c19332SMike Rapoport
387c6c19332SMike Rapoport for (i = 0; i < pdata->num_subdevs; i++) {
388c6c19332SMike Rapoport subdev = &pdata->subdevs[i];
389c6c19332SMike Rapoport
390c6c19332SMike Rapoport pdev = platform_device_alloc(subdev->name, subdev->id);
391929980abSAxel Lin if (!pdev) {
392929980abSAxel Lin ret = -ENOMEM;
393929980abSAxel Lin goto failed;
394929980abSAxel Lin }
395c6c19332SMike Rapoport
396c6c19332SMike Rapoport pdev->dev.parent = tps6586x->dev;
397c6c19332SMike Rapoport pdev->dev.platform_data = subdev->platform_data;
39862f6b087SThierry Reding pdev->dev.of_node = subdev->of_node;
399c6c19332SMike Rapoport
400c6c19332SMike Rapoport ret = platform_device_add(pdev);
401929980abSAxel Lin if (ret) {
402929980abSAxel Lin platform_device_put(pdev);
403c6c19332SMike Rapoport goto failed;
404c6c19332SMike Rapoport }
405929980abSAxel Lin }
406c6c19332SMike Rapoport return 0;
407c6c19332SMike Rapoport
408c6c19332SMike Rapoport failed:
409c6c19332SMike Rapoport tps6586x_remove_subdevs(tps6586x);
410c6c19332SMike Rapoport return ret;
411c6c19332SMike Rapoport }
412c6c19332SMike Rapoport
41362f6b087SThierry Reding #ifdef CONFIG_OF
tps6586x_parse_dt(struct i2c_client * client)41462f6b087SThierry Reding static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client)
41562f6b087SThierry Reding {
41662f6b087SThierry Reding struct device_node *np = client->dev.of_node;
41762f6b087SThierry Reding struct tps6586x_platform_data *pdata;
41862f6b087SThierry Reding
41962f6b087SThierry Reding pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
420feafdffaSMarkus Elfring if (!pdata)
42162f6b087SThierry Reding return NULL;
42262f6b087SThierry Reding
42364e48160SLaxman Dewangan pdata->num_subdevs = 0;
42464e48160SLaxman Dewangan pdata->subdevs = NULL;
42562f6b087SThierry Reding pdata->gpio_base = -1;
42662f6b087SThierry Reding pdata->irq_base = -1;
427004c15a6SBill Huang pdata->pm_off = of_property_read_bool(np, "ti,system-power-controller");
42862f6b087SThierry Reding
42962f6b087SThierry Reding return pdata;
43062f6b087SThierry Reding }
43162f6b087SThierry Reding
432a58cc84cSJingoo Han static const struct of_device_id tps6586x_of_match[] = {
43362f6b087SThierry Reding { .compatible = "ti,tps6586x", },
43462f6b087SThierry Reding { },
43562f6b087SThierry Reding };
43662f6b087SThierry Reding #else
tps6586x_parse_dt(struct i2c_client * client)43762f6b087SThierry Reding static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client)
43862f6b087SThierry Reding {
43962f6b087SThierry Reding return NULL;
44062f6b087SThierry Reding }
44162f6b087SThierry Reding #endif
44262f6b087SThierry Reding
is_volatile_reg(struct device * dev,unsigned int reg)44375edd5afSLaxman Dewangan static bool is_volatile_reg(struct device *dev, unsigned int reg)
44475edd5afSLaxman Dewangan {
44575edd5afSLaxman Dewangan /* Cache all interrupt mask register */
44675edd5afSLaxman Dewangan if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5))
44775edd5afSLaxman Dewangan return false;
44875edd5afSLaxman Dewangan
44975edd5afSLaxman Dewangan return true;
45075edd5afSLaxman Dewangan }
45175edd5afSLaxman Dewangan
4521176b5beSLaxman Dewangan static const struct regmap_config tps6586x_regmap_config = {
4531176b5beSLaxman Dewangan .reg_bits = 8,
4541176b5beSLaxman Dewangan .val_bits = 8,
455088d862cSAxel Lin .max_register = TPS6586X_MAX_REGISTER,
45675edd5afSLaxman Dewangan .volatile_reg = is_volatile_reg,
45775edd5afSLaxman Dewangan .cache_type = REGCACHE_RBTREE,
4581176b5beSLaxman Dewangan };
4591176b5beSLaxman Dewangan
460004c15a6SBill Huang static struct device *tps6586x_dev;
tps6586x_power_off(void)461004c15a6SBill Huang static void tps6586x_power_off(void)
462004c15a6SBill Huang {
463004c15a6SBill Huang if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT))
464004c15a6SBill Huang return;
465004c15a6SBill Huang
466004c15a6SBill Huang tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
467004c15a6SBill Huang }
468004c15a6SBill Huang
tps6586x_print_version(struct i2c_client * client,int version)469e0a3da80SStefan Agner static void tps6586x_print_version(struct i2c_client *client, int version)
470e0a3da80SStefan Agner {
471e0a3da80SStefan Agner const char *name;
472e0a3da80SStefan Agner
473e0a3da80SStefan Agner switch (version) {
474e0a3da80SStefan Agner case TPS658621A:
475e0a3da80SStefan Agner name = "TPS658621A";
476e0a3da80SStefan Agner break;
477e0a3da80SStefan Agner case TPS658621CD:
478e0a3da80SStefan Agner name = "TPS658621C/D";
479e0a3da80SStefan Agner break;
480e0a3da80SStefan Agner case TPS658623:
481e0a3da80SStefan Agner name = "TPS658623";
482e0a3da80SStefan Agner break;
4836c46ccc8SAlban Bedel case TPS658640:
4846c46ccc8SAlban Bedel case TPS658640v2:
4856c46ccc8SAlban Bedel name = "TPS658640";
4866c46ccc8SAlban Bedel break;
487e0a3da80SStefan Agner case TPS658643:
488e0a3da80SStefan Agner name = "TPS658643";
489e0a3da80SStefan Agner break;
490e0a3da80SStefan Agner default:
491e0a3da80SStefan Agner name = "TPS6586X";
492e0a3da80SStefan Agner break;
493e0a3da80SStefan Agner }
494e0a3da80SStefan Agner
495e0a3da80SStefan Agner dev_info(&client->dev, "Found %s, VERSIONCRC is %02x\n", name, version);
496e0a3da80SStefan Agner }
497e0a3da80SStefan Agner
tps6586x_i2c_probe(struct i2c_client * client)498ba801bd5SUwe Kleine-König static int tps6586x_i2c_probe(struct i2c_client *client)
499c6c19332SMike Rapoport {
500334a41ceSJingoo Han struct tps6586x_platform_data *pdata = dev_get_platdata(&client->dev);
501c6c19332SMike Rapoport struct tps6586x *tps6586x;
502c6c19332SMike Rapoport int ret;
503e0a3da80SStefan Agner int version;
504c6c19332SMike Rapoport
50562f6b087SThierry Reding if (!pdata && client->dev.of_node)
50662f6b087SThierry Reding pdata = tps6586x_parse_dt(client);
50762f6b087SThierry Reding
508c6c19332SMike Rapoport if (!pdata) {
509c6c19332SMike Rapoport dev_err(&client->dev, "tps6586x requires platform data\n");
510c6c19332SMike Rapoport return -ENOTSUPP;
511c6c19332SMike Rapoport }
512c6c19332SMike Rapoport
513e0a3da80SStefan Agner version = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
514e0a3da80SStefan Agner if (version < 0) {
515e0a3da80SStefan Agner dev_err(&client->dev, "Chip ID read failed: %d\n", version);
516c6c19332SMike Rapoport return -EIO;
517c6c19332SMike Rapoport }
518c6c19332SMike Rapoport
519b6719412SLaxman Dewangan tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL);
520e0a3da80SStefan Agner if (!tps6586x)
521c6c19332SMike Rapoport return -ENOMEM;
522e0a3da80SStefan Agner
523e0a3da80SStefan Agner tps6586x->version = version;
524e0a3da80SStefan Agner tps6586x_print_version(client, tps6586x->version);
525c6c19332SMike Rapoport
526c6c19332SMike Rapoport tps6586x->client = client;
527c6c19332SMike Rapoport tps6586x->dev = &client->dev;
528c6c19332SMike Rapoport i2c_set_clientdata(client, tps6586x);
529c6c19332SMike Rapoport
5301176b5beSLaxman Dewangan tps6586x->regmap = devm_regmap_init_i2c(client,
5311176b5beSLaxman Dewangan &tps6586x_regmap_config);
5321176b5beSLaxman Dewangan if (IS_ERR(tps6586x->regmap)) {
5331176b5beSLaxman Dewangan ret = PTR_ERR(tps6586x->regmap);
5341176b5beSLaxman Dewangan dev_err(&client->dev, "regmap init failed: %d\n", ret);
5351176b5beSLaxman Dewangan return ret;
5361176b5beSLaxman Dewangan }
5371176b5beSLaxman Dewangan
538c6c19332SMike Rapoport
539c26448c4SGary King if (client->irq) {
540c26448c4SGary King ret = tps6586x_irq_init(tps6586x, client->irq,
541c26448c4SGary King pdata->irq_base);
542c26448c4SGary King if (ret) {
543c26448c4SGary King dev_err(&client->dev, "IRQ init failed: %d\n", ret);
544b6719412SLaxman Dewangan return ret;
545c26448c4SGary King }
546c26448c4SGary King }
547c26448c4SGary King
5487a7487cbSLaxman Dewangan ret = mfd_add_devices(tps6586x->dev, -1,
5490848c94fSMark Brown tps6586x_cell, ARRAY_SIZE(tps6586x_cell),
5505b8b1fe2SLaxman Dewangan NULL, 0, tps6586x->irq_domain);
5517a7487cbSLaxman Dewangan if (ret < 0) {
5527a7487cbSLaxman Dewangan dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
5537a7487cbSLaxman Dewangan goto err_mfd_add;
5546f9f13bfSVincent Palatin }
5556f9f13bfSVincent Palatin
556c6c19332SMike Rapoport ret = tps6586x_add_subdevs(tps6586x, pdata);
557c6c19332SMike Rapoport if (ret) {
558c6c19332SMike Rapoport dev_err(&client->dev, "add devices failed: %d\n", ret);
559c6c19332SMike Rapoport goto err_add_devs;
560c6c19332SMike Rapoport }
561c6c19332SMike Rapoport
562004c15a6SBill Huang if (pdata->pm_off && !pm_power_off) {
563004c15a6SBill Huang tps6586x_dev = &client->dev;
564004c15a6SBill Huang pm_power_off = tps6586x_power_off;
565004c15a6SBill Huang }
566004c15a6SBill Huang
567c6c19332SMike Rapoport return 0;
568c6c19332SMike Rapoport
569c6c19332SMike Rapoport err_add_devs:
5707a7487cbSLaxman Dewangan mfd_remove_devices(tps6586x->dev);
5717a7487cbSLaxman Dewangan err_mfd_add:
572c26448c4SGary King if (client->irq)
573c26448c4SGary King free_irq(client->irq, tps6586x);
574c6c19332SMike Rapoport return ret;
575c6c19332SMike Rapoport }
576c6c19332SMike Rapoport
tps6586x_i2c_remove(struct i2c_client * client)577ed5c2f5fSUwe Kleine-König static void tps6586x_i2c_remove(struct i2c_client *client)
578c6c19332SMike Rapoport {
5794b751cf5SAxel Lin struct tps6586x *tps6586x = i2c_get_clientdata(client);
5804b751cf5SAxel Lin
5814b751cf5SAxel Lin tps6586x_remove_subdevs(tps6586x);
5827a7487cbSLaxman Dewangan mfd_remove_devices(tps6586x->dev);
5837a7487cbSLaxman Dewangan if (client->irq)
5847a7487cbSLaxman Dewangan free_irq(client->irq, tps6586x);
585c6c19332SMike Rapoport }
586c6c19332SMike Rapoport
tps6586x_i2c_suspend(struct device * dev)587ac4ca4b9SJonathan Hunter static int __maybe_unused tps6586x_i2c_suspend(struct device *dev)
588ac4ca4b9SJonathan Hunter {
589ac4ca4b9SJonathan Hunter struct tps6586x *tps6586x = dev_get_drvdata(dev);
590ac4ca4b9SJonathan Hunter
591ac4ca4b9SJonathan Hunter if (tps6586x->client->irq)
592ac4ca4b9SJonathan Hunter disable_irq(tps6586x->client->irq);
593ac4ca4b9SJonathan Hunter
594ac4ca4b9SJonathan Hunter return 0;
595ac4ca4b9SJonathan Hunter }
596ac4ca4b9SJonathan Hunter
tps6586x_i2c_resume(struct device * dev)597ac4ca4b9SJonathan Hunter static int __maybe_unused tps6586x_i2c_resume(struct device *dev)
598ac4ca4b9SJonathan Hunter {
599ac4ca4b9SJonathan Hunter struct tps6586x *tps6586x = dev_get_drvdata(dev);
600ac4ca4b9SJonathan Hunter
601ac4ca4b9SJonathan Hunter if (tps6586x->client->irq)
602ac4ca4b9SJonathan Hunter enable_irq(tps6586x->client->irq);
603ac4ca4b9SJonathan Hunter
604ac4ca4b9SJonathan Hunter return 0;
605ac4ca4b9SJonathan Hunter }
606ac4ca4b9SJonathan Hunter
607ac4ca4b9SJonathan Hunter static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend,
608ac4ca4b9SJonathan Hunter tps6586x_i2c_resume);
609ac4ca4b9SJonathan Hunter
610c6c19332SMike Rapoport static const struct i2c_device_id tps6586x_id_table[] = {
611c6c19332SMike Rapoport { "tps6586x", 0 },
612c6c19332SMike Rapoport { },
613c6c19332SMike Rapoport };
614c6c19332SMike Rapoport MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
615c6c19332SMike Rapoport
616c6c19332SMike Rapoport static struct i2c_driver tps6586x_driver = {
617c6c19332SMike Rapoport .driver = {
618c6c19332SMike Rapoport .name = "tps6586x",
61962f6b087SThierry Reding .of_match_table = of_match_ptr(tps6586x_of_match),
620ac4ca4b9SJonathan Hunter .pm = &tps6586x_pm_ops,
621c6c19332SMike Rapoport },
622ba801bd5SUwe Kleine-König .probe = tps6586x_i2c_probe,
62384449216SBill Pemberton .remove = tps6586x_i2c_remove,
624c6c19332SMike Rapoport .id_table = tps6586x_id_table,
625c6c19332SMike Rapoport };
626c6c19332SMike Rapoport
tps6586x_init(void)627c6c19332SMike Rapoport static int __init tps6586x_init(void)
628c6c19332SMike Rapoport {
629c6c19332SMike Rapoport return i2c_add_driver(&tps6586x_driver);
630c6c19332SMike Rapoport }
631c6c19332SMike Rapoport subsys_initcall(tps6586x_init);
632c6c19332SMike Rapoport
tps6586x_exit(void)633c6c19332SMike Rapoport static void __exit tps6586x_exit(void)
634c6c19332SMike Rapoport {
635c6c19332SMike Rapoport i2c_del_driver(&tps6586x_driver);
636c6c19332SMike Rapoport }
637c6c19332SMike Rapoport module_exit(tps6586x_exit);
638c6c19332SMike Rapoport
639c6c19332SMike Rapoport MODULE_DESCRIPTION("TPS6586X core driver");
640c6c19332SMike Rapoport MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
641