xref: /openbmc/linux/drivers/mfd/tps6586x.c (revision 9816d859)
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,
284d115e88cSPaul 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 	},
622*9816d859SUwe 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