Blame view

drivers/mfd/tps6586x.c 15.8 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
2
3
4
5
6
7
8
9
10
11
12
  /*
   * Core driver for TI TPS6586x PMIC family
   *
   * Copyright (c) 2010 CompuLab Ltd.
   * Mike Rapoport <mike@compulab.co.il>
   *
   * Based on da903x.c.
   * Copyright (C) 2008 Compulab, Ltd.
   * Mike Rapoport <mike@compulab.co.il>
   * Copyright (C) 2006-2008 Marvell International Ltd.
   * Eric Miao <eric.miao@marvell.com>
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
13
   */
c26448c48   Gary King   mfd: Add basic tp...
14
15
  #include <linux/interrupt.h>
  #include <linux/irq.h>
605511a84   Laxman Dewangan   mfd: Convert tps6...
16
  #include <linux/irqdomain.h>
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
17
18
19
20
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/slab.h>
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
21
  #include <linux/err.h>
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
22
  #include <linux/i2c.h>
605511a84   Laxman Dewangan   mfd: Convert tps6...
23
  #include <linux/platform_device.h>
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
24
  #include <linux/regmap.h>
06bf3c2f1   Sachin Kamat   mfd: tps6586x: In...
25
  #include <linux/of.h>
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
26
27
28
  
  #include <linux/mfd/core.h>
  #include <linux/mfd/tps6586x.h>
004c15a68   Bill Huang   mfd: dt: tps6586x...
29
30
31
  #define TPS6586X_SUPPLYENE	0x14
  #define EXITSLREQ_BIT		BIT(1)
  #define SLEEP_MODE_BIT		BIT(3)
c26448c48   Gary King   mfd: Add basic tp...
32
33
34
35
36
37
38
39
40
41
42
43
  /* interrupt control registers */
  #define TPS6586X_INT_ACK1	0xb5
  #define TPS6586X_INT_ACK2	0xb6
  #define TPS6586X_INT_ACK3	0xb7
  #define TPS6586X_INT_ACK4	0xb8
  
  /* interrupt mask registers */
  #define TPS6586X_INT_MASK1	0xb0
  #define TPS6586X_INT_MASK2	0xb1
  #define TPS6586X_INT_MASK3	0xb2
  #define TPS6586X_INT_MASK4	0xb3
  #define TPS6586X_INT_MASK5	0xb4
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
44
45
  /* device id */
  #define TPS6586X_VERSIONCRC	0xcd
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
46

1176b5be6   Laxman Dewangan   mfd: Use regmap f...
47
  /* Maximum register */
088d862c5   Axel Lin   mfd: tps6586x: Fi...
48
  #define TPS6586X_MAX_REGISTER	TPS6586X_VERSIONCRC
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
49

c26448c48   Gary King   mfd: Add basic tp...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  struct tps6586x_irq_data {
  	u8	mask_reg;
  	u8	mask_mask;
  };
  
  #define TPS6586X_IRQ(_reg, _mask)				\
  	{							\
  		.mask_reg = (_reg) - TPS6586X_INT_MASK1,	\
  		.mask_mask = (_mask),				\
  	}
  
  static const struct tps6586x_irq_data tps6586x_irqs[] = {
  	[TPS6586X_INT_PLDO_0]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0),
  	[TPS6586X_INT_PLDO_1]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1),
  	[TPS6586X_INT_PLDO_2]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2),
  	[TPS6586X_INT_PLDO_3]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3),
  	[TPS6586X_INT_PLDO_4]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4),
  	[TPS6586X_INT_PLDO_5]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5),
  	[TPS6586X_INT_PLDO_6]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6),
  	[TPS6586X_INT_PLDO_7]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7),
  	[TPS6586X_INT_COMP_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0),
  	[TPS6586X_INT_ADC]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1),
  	[TPS6586X_INT_PLDO_8]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2),
  	[TPS6586X_INT_PLDO_9]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3),
  	[TPS6586X_INT_PSM_0]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4),
  	[TPS6586X_INT_PSM_1]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5),
  	[TPS6586X_INT_PSM_2]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6),
  	[TPS6586X_INT_PSM_3]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7),
  	[TPS6586X_INT_RTC_ALM1]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4),
  	[TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03),
  	[TPS6586X_INT_USB_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2),
  	[TPS6586X_INT_AC_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3),
  	[TPS6586X_INT_BAT_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0),
  	[TPS6586X_INT_CHG_STAT]	= TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc),
  	[TPS6586X_INT_CHG_TEMP]	= TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06),
  	[TPS6586X_INT_PP]	= TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0),
  	[TPS6586X_INT_RESUME]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5),
  	[TPS6586X_INT_LOW_SYS]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6),
  	[TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
  };
5b8b1fe2d   Laxman Dewangan   mfd: Add irq io-r...
90
91
92
93
94
95
96
  static struct resource tps6586x_rtc_resources[] = {
  	{
  		.start  = TPS6586X_INT_RTC_ALM1,
  		.end	= TPS6586X_INT_RTC_ALM1,
  		.flags	= IORESOURCE_IRQ,
  	},
  };
30fe2b5bd   Geert Uytterhoeven   mfd: ti: Constify...
97
  static const struct mfd_cell tps6586x_cell[] = {
7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
98
99
100
101
  	{
  		.name = "tps6586x-gpio",
  	},
  	{
ec8da805c   Marc Dietrich   mfd: tps6586x: co...
102
  		.name = "tps6586x-regulator",
64e481603   Laxman Dewangan   mfd: tps6586x: mo...
103
104
  	},
  	{
7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
105
  		.name = "tps6586x-rtc",
5b8b1fe2d   Laxman Dewangan   mfd: Add irq io-r...
106
107
  		.num_resources = ARRAY_SIZE(tps6586x_rtc_resources),
  		.resources = &tps6586x_rtc_resources[0],
7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
108
109
110
111
112
  	},
  	{
  		.name = "tps6586x-onkey",
  	},
  };
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
113
  struct tps6586x {
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
114
115
  	struct device		*dev;
  	struct i2c_client	*client;
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
116
  	struct regmap		*regmap;
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
117
  	int			version;
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
118

234506ad3   Stephen Warren   mfd: tps6586x: Im...
119
  	int			irq;
c26448c48   Gary King   mfd: Add basic tp...
120
121
122
123
  	struct irq_chip		irq_chip;
  	struct mutex		irq_lock;
  	int			irq_base;
  	u32			irq_en;
c26448c48   Gary King   mfd: Add basic tp...
124
  	u8			mask_reg[5];
605511a84   Laxman Dewangan   mfd: Convert tps6...
125
  	struct irq_domain	*irq_domain;
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
126
  };
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
127
  static inline struct tps6586x *dev_to_tps6586x(struct device *dev)
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
128
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
129
  	return i2c_get_clientdata(to_i2c_client(dev));
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
130
131
132
133
  }
  
  int tps6586x_write(struct device *dev, int reg, uint8_t val)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
134
135
136
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
  
  	return regmap_write(tps6586x->regmap, reg, val);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
137
138
139
140
141
  }
  EXPORT_SYMBOL_GPL(tps6586x_write);
  
  int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
142
143
144
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
  
  	return regmap_bulk_write(tps6586x->regmap, reg, val, len);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
145
146
147
148
149
  }
  EXPORT_SYMBOL_GPL(tps6586x_writes);
  
  int tps6586x_read(struct device *dev, int reg, uint8_t *val)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
150
151
152
153
154
155
156
157
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
  	unsigned int rval;
  	int ret;
  
  	ret = regmap_read(tps6586x->regmap, reg, &rval);
  	if (!ret)
  		*val = rval;
  	return ret;
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
158
159
160
161
162
  }
  EXPORT_SYMBOL_GPL(tps6586x_read);
  
  int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
163
164
165
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
  
  	return regmap_bulk_read(tps6586x->regmap, reg, val, len);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
166
167
168
169
170
  }
  EXPORT_SYMBOL_GPL(tps6586x_reads);
  
  int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
171
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
172

1176b5be6   Laxman Dewangan   mfd: Use regmap f...
173
  	return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
174
175
176
177
178
  }
  EXPORT_SYMBOL_GPL(tps6586x_set_bits);
  
  int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
179
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
180

1176b5be6   Laxman Dewangan   mfd: Use regmap f...
181
  	return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
182
183
184
185
186
  }
  EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
  
  int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
  {
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
187
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
188

1176b5be6   Laxman Dewangan   mfd: Use regmap f...
189
  	return regmap_update_bits(tps6586x->regmap, reg, mask, val);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
190
191
  }
  EXPORT_SYMBOL_GPL(tps6586x_update);
605511a84   Laxman Dewangan   mfd: Convert tps6...
192
193
194
195
196
197
198
  int tps6586x_irq_get_virq(struct device *dev, int irq)
  {
  	struct tps6586x *tps6586x = dev_to_tps6586x(dev);
  
  	return irq_create_mapping(tps6586x->irq_domain, irq);
  }
  EXPORT_SYMBOL_GPL(tps6586x_irq_get_virq);
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
199
200
201
202
203
204
205
  int tps6586x_get_version(struct device *dev)
  {
  	struct tps6586x *tps6586x = dev_get_drvdata(dev);
  
  	return tps6586x->version;
  }
  EXPORT_SYMBOL_GPL(tps6586x_get_version);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
206
207
208
209
210
211
212
213
214
215
  static int __remove_subdev(struct device *dev, void *unused)
  {
  	platform_device_unregister(to_platform_device(dev));
  	return 0;
  }
  
  static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
  {
  	return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
  }
96e824bdf   Mark Brown   mfd: Convert tps6...
216
  static void tps6586x_irq_lock(struct irq_data *data)
c26448c48   Gary King   mfd: Add basic tp...
217
  {
96e824bdf   Mark Brown   mfd: Convert tps6...
218
  	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data);
c26448c48   Gary King   mfd: Add basic tp...
219
220
221
  
  	mutex_lock(&tps6586x->irq_lock);
  }
96e824bdf   Mark Brown   mfd: Convert tps6...
222
  static void tps6586x_irq_enable(struct irq_data *irq_data)
c26448c48   Gary King   mfd: Add basic tp...
223
  {
96e824bdf   Mark Brown   mfd: Convert tps6...
224
  	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
605511a84   Laxman Dewangan   mfd: Convert tps6...
225
  	unsigned int __irq = irq_data->hwirq;
c26448c48   Gary King   mfd: Add basic tp...
226
227
228
229
230
  	const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
  
  	tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask;
  	tps6586x->irq_en |= (1 << __irq);
  }
96e824bdf   Mark Brown   mfd: Convert tps6...
231
  static void tps6586x_irq_disable(struct irq_data *irq_data)
c26448c48   Gary King   mfd: Add basic tp...
232
  {
96e824bdf   Mark Brown   mfd: Convert tps6...
233
  	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
c26448c48   Gary King   mfd: Add basic tp...
234

605511a84   Laxman Dewangan   mfd: Convert tps6...
235
  	unsigned int __irq = irq_data->hwirq;
c26448c48   Gary King   mfd: Add basic tp...
236
237
238
239
240
  	const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
  
  	tps6586x->mask_reg[data->mask_reg] |= data->mask_mask;
  	tps6586x->irq_en &= ~(1 << __irq);
  }
96e824bdf   Mark Brown   mfd: Convert tps6...
241
  static void tps6586x_irq_sync_unlock(struct irq_data *data)
c26448c48   Gary King   mfd: Add basic tp...
242
  {
96e824bdf   Mark Brown   mfd: Convert tps6...
243
  	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data);
c26448c48   Gary King   mfd: Add basic tp...
244
245
246
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
75edd5af6   Laxman Dewangan   mfd: Cache tps658...
247
248
249
250
251
  		int ret;
  		ret = tps6586x_write(tps6586x->dev,
  					    TPS6586X_INT_MASK1 + i,
  					    tps6586x->mask_reg[i]);
  		WARN_ON(ret);
c26448c48   Gary King   mfd: Add basic tp...
252
253
254
255
  	}
  
  	mutex_unlock(&tps6586x->irq_lock);
  }
234506ad3   Stephen Warren   mfd: tps6586x: Im...
256
257
258
259
260
261
262
263
264
  #ifdef CONFIG_PM_SLEEP
  static int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on)
  {
  	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
  	return irq_set_irq_wake(tps6586x->irq, on);
  }
  #else
  #define tps6586x_irq_set_wake NULL
  #endif
605511a84   Laxman Dewangan   mfd: Convert tps6...
265
266
267
268
269
270
  static struct irq_chip tps6586x_irq_chip = {
  	.name = "tps6586x",
  	.irq_bus_lock = tps6586x_irq_lock,
  	.irq_bus_sync_unlock = tps6586x_irq_sync_unlock,
  	.irq_disable = tps6586x_irq_disable,
  	.irq_enable = tps6586x_irq_enable,
234506ad3   Stephen Warren   mfd: tps6586x: Im...
271
  	.irq_set_wake = tps6586x_irq_set_wake,
605511a84   Laxman Dewangan   mfd: Convert tps6...
272
273
274
275
276
277
278
279
280
281
  };
  
  static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
  				irq_hw_number_t hw)
  {
  	struct tps6586x *tps6586x = h->host_data;
  
  	irq_set_chip_data(virq, tps6586x);
  	irq_set_chip_and_handler(virq, &tps6586x_irq_chip, handle_simple_irq);
  	irq_set_nested_thread(virq, 1);
605511a84   Laxman Dewangan   mfd: Convert tps6...
282
  	irq_set_noprobe(virq);
605511a84   Laxman Dewangan   mfd: Convert tps6...
283
284
285
  
  	return 0;
  }
7ce7b26f8   Krzysztof Kozlowski   mfd: Constify reg...
286
  static const struct irq_domain_ops tps6586x_domain_ops = {
605511a84   Laxman Dewangan   mfd: Convert tps6...
287
288
289
  	.map    = tps6586x_irq_map,
  	.xlate  = irq_domain_xlate_twocell,
  };
c26448c48   Gary King   mfd: Add basic tp...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  static irqreturn_t tps6586x_irq(int irq, void *data)
  {
  	struct tps6586x *tps6586x = data;
  	u32 acks;
  	int ret = 0;
  
  	ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1,
  			     sizeof(acks), (uint8_t *)&acks);
  
  	if (ret < 0) {
  		dev_err(tps6586x->dev, "failed to read interrupt status
  ");
  		return IRQ_NONE;
  	}
  
  	acks = le32_to_cpu(acks);
  
  	while (acks) {
  		int i = __ffs(acks);
  
  		if (tps6586x->irq_en & (1 << i))
605511a84   Laxman Dewangan   mfd: Convert tps6...
311
312
  			handle_nested_irq(
  				irq_find_mapping(tps6586x->irq_domain, i));
c26448c48   Gary King   mfd: Add basic tp...
313
314
315
316
317
318
  
  		acks &= ~(1 << i);
  	}
  
  	return IRQ_HANDLED;
  }
f791be492   Bill Pemberton   mfd: remove use o...
319
  static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
c26448c48   Gary King   mfd: Add basic tp...
320
321
322
323
  				       int irq_base)
  {
  	int i, ret;
  	u8 tmp[4];
605511a84   Laxman Dewangan   mfd: Convert tps6...
324
325
  	int new_irq_base;
  	int irq_num = ARRAY_SIZE(tps6586x_irqs);
c26448c48   Gary King   mfd: Add basic tp...
326

234506ad3   Stephen Warren   mfd: tps6586x: Im...
327
  	tps6586x->irq = irq;
c26448c48   Gary King   mfd: Add basic tp...
328
329
  	mutex_init(&tps6586x->irq_lock);
  	for (i = 0; i < 5; i++) {
c26448c48   Gary King   mfd: Add basic tp...
330
331
332
333
334
  		tps6586x->mask_reg[i] = 0xff;
  		tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
  	}
  
  	tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp);
605511a84   Laxman Dewangan   mfd: Convert tps6...
335
336
337
338
339
340
341
342
343
344
  	if  (irq_base > 0) {
  		new_irq_base = irq_alloc_descs(irq_base, 0, irq_num, -1);
  		if (new_irq_base < 0) {
  			dev_err(tps6586x->dev,
  				"Failed to alloc IRQs: %d
  ", new_irq_base);
  			return new_irq_base;
  		}
  	} else {
  		new_irq_base = 0;
c26448c48   Gary King   mfd: Add basic tp...
345
  	}
605511a84   Laxman Dewangan   mfd: Convert tps6...
346
347
348
349
350
351
352
353
  	tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node,
  				irq_num, new_irq_base, &tps6586x_domain_ops,
  				tps6586x);
  	if (!tps6586x->irq_domain) {
  		dev_err(tps6586x->dev, "Failed to create IRQ domain
  ");
  		return -ENOMEM;
  	}
c26448c48   Gary King   mfd: Add basic tp...
354
355
  	ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
  				   "tps6586x", tps6586x);
234506ad3   Stephen Warren   mfd: tps6586x: Im...
356
  	if (!ret)
c26448c48   Gary King   mfd: Add basic tp...
357
  		device_init_wakeup(tps6586x->dev, 1);
c26448c48   Gary King   mfd: Add basic tp...
358
359
360
  
  	return ret;
  }
f791be492   Bill Pemberton   mfd: remove use o...
361
  static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
362
363
364
365
366
367
368
369
370
371
  					  struct tps6586x_platform_data *pdata)
  {
  	struct tps6586x_subdev_info *subdev;
  	struct platform_device *pdev;
  	int i, ret = 0;
  
  	for (i = 0; i < pdata->num_subdevs; i++) {
  		subdev = &pdata->subdevs[i];
  
  		pdev = platform_device_alloc(subdev->name, subdev->id);
929980ab1   Axel Lin   mfd: Fix tps6586x...
372
373
374
375
  		if (!pdev) {
  			ret = -ENOMEM;
  			goto failed;
  		}
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
376
377
378
  
  		pdev->dev.parent = tps6586x->dev;
  		pdev->dev.platform_data = subdev->platform_data;
62f6b0879   Thierry Reding   tps6586x: Add dev...
379
  		pdev->dev.of_node = subdev->of_node;
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
380
381
  
  		ret = platform_device_add(pdev);
929980ab1   Axel Lin   mfd: Fix tps6586x...
382
383
  		if (ret) {
  			platform_device_put(pdev);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
384
  			goto failed;
929980ab1   Axel Lin   mfd: Fix tps6586x...
385
  		}
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
386
387
388
389
390
391
392
  	}
  	return 0;
  
  failed:
  	tps6586x_remove_subdevs(tps6586x);
  	return ret;
  }
62f6b0879   Thierry Reding   tps6586x: Add dev...
393
  #ifdef CONFIG_OF
62f6b0879   Thierry Reding   tps6586x: Add dev...
394
395
  static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client)
  {
62f6b0879   Thierry Reding   tps6586x: Add dev...
396
397
  	struct device_node *np = client->dev.of_node;
  	struct tps6586x_platform_data *pdata;
62f6b0879   Thierry Reding   tps6586x: Add dev...
398
399
  
  	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
feafdffa4   Markus Elfring   mfd: tps6586x: De...
400
  	if (!pdata)
62f6b0879   Thierry Reding   tps6586x: Add dev...
401
  		return NULL;
64e481603   Laxman Dewangan   mfd: tps6586x: mo...
402
403
  	pdata->num_subdevs = 0;
  	pdata->subdevs = NULL;
62f6b0879   Thierry Reding   tps6586x: Add dev...
404
405
  	pdata->gpio_base = -1;
  	pdata->irq_base = -1;
004c15a68   Bill Huang   mfd: dt: tps6586x...
406
  	pdata->pm_off = of_property_read_bool(np, "ti,system-power-controller");
62f6b0879   Thierry Reding   tps6586x: Add dev...
407
408
409
  
  	return pdata;
  }
a58cc84ca   Jingoo Han   mfd: tps6586x: Ma...
410
  static const struct of_device_id tps6586x_of_match[] = {
62f6b0879   Thierry Reding   tps6586x: Add dev...
411
412
413
414
415
416
417
418
419
  	{ .compatible = "ti,tps6586x", },
  	{ },
  };
  #else
  static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client)
  {
  	return NULL;
  }
  #endif
75edd5af6   Laxman Dewangan   mfd: Cache tps658...
420
421
422
423
424
425
426
427
  static bool is_volatile_reg(struct device *dev, unsigned int reg)
  {
  	/* Cache all interrupt mask register */
  	if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5))
  		return false;
  
  	return true;
  }
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
428
429
430
  static const struct regmap_config tps6586x_regmap_config = {
  	.reg_bits = 8,
  	.val_bits = 8,
088d862c5   Axel Lin   mfd: tps6586x: Fi...
431
  	.max_register = TPS6586X_MAX_REGISTER,
75edd5af6   Laxman Dewangan   mfd: Cache tps658...
432
433
  	.volatile_reg = is_volatile_reg,
  	.cache_type = REGCACHE_RBTREE,
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
434
  };
004c15a68   Bill Huang   mfd: dt: tps6586x...
435
436
437
438
439
440
441
442
  static struct device *tps6586x_dev;
  static void tps6586x_power_off(void)
  {
  	if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT))
  		return;
  
  	tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
  }
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  static void tps6586x_print_version(struct i2c_client *client, int version)
  {
  	const char *name;
  
  	switch (version) {
  	case TPS658621A:
  		name = "TPS658621A";
  		break;
  	case TPS658621CD:
  		name = "TPS658621C/D";
  		break;
  	case TPS658623:
  		name = "TPS658623";
  		break;
6c46ccc8b   Alban Bedel   regulator: tps658...
457
458
459
460
  	case TPS658640:
  	case TPS658640v2:
  		name = "TPS658640";
  		break;
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
461
462
463
464
465
466
467
468
469
470
471
  	case TPS658643:
  		name = "TPS658643";
  		break;
  	default:
  		name = "TPS6586X";
  		break;
  	}
  
  	dev_info(&client->dev, "Found %s, VERSIONCRC is %02x
  ", name, version);
  }
f791be492   Bill Pemberton   mfd: remove use o...
472
  static int tps6586x_i2c_probe(struct i2c_client *client,
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
473
474
  					const struct i2c_device_id *id)
  {
334a41ce9   Jingoo Han   mfd: Use dev_get_...
475
  	struct tps6586x_platform_data *pdata = dev_get_platdata(&client->dev);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
476
477
  	struct tps6586x *tps6586x;
  	int ret;
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
478
  	int version;
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
479

62f6b0879   Thierry Reding   tps6586x: Add dev...
480
481
  	if (!pdata && client->dev.of_node)
  		pdata = tps6586x_parse_dt(client);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
482
483
484
485
486
  	if (!pdata) {
  		dev_err(&client->dev, "tps6586x requires platform data
  ");
  		return -ENOTSUPP;
  	}
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
487
488
489
490
  	version = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
  	if (version < 0) {
  		dev_err(&client->dev, "Chip ID read failed: %d
  ", version);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
491
492
  		return -EIO;
  	}
b6719412d   Laxman Dewangan   mfd: Use devm man...
493
  	tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL);
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
494
  	if (!tps6586x)
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
495
  		return -ENOMEM;
e0a3da80c   Stefan Agner   mfd: tps6586x: Ad...
496
497
498
  
  	tps6586x->version = version;
  	tps6586x_print_version(client, tps6586x->version);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
499
500
501
502
  
  	tps6586x->client = client;
  	tps6586x->dev = &client->dev;
  	i2c_set_clientdata(client, tps6586x);
1176b5be6   Laxman Dewangan   mfd: Use regmap f...
503
504
505
506
507
508
509
510
  	tps6586x->regmap = devm_regmap_init_i2c(client,
  					&tps6586x_regmap_config);
  	if (IS_ERR(tps6586x->regmap)) {
  		ret = PTR_ERR(tps6586x->regmap);
  		dev_err(&client->dev, "regmap init failed: %d
  ", ret);
  		return ret;
  	}
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
511

c26448c48   Gary King   mfd: Add basic tp...
512
513
514
515
516
517
  	if (client->irq) {
  		ret = tps6586x_irq_init(tps6586x, client->irq,
  					pdata->irq_base);
  		if (ret) {
  			dev_err(&client->dev, "IRQ init failed: %d
  ", ret);
b6719412d   Laxman Dewangan   mfd: Use devm man...
518
  			return ret;
c26448c48   Gary King   mfd: Add basic tp...
519
520
  		}
  	}
7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
521
  	ret = mfd_add_devices(tps6586x->dev, -1,
0848c94fb   Mark Brown   mfd: core: Push i...
522
  			      tps6586x_cell, ARRAY_SIZE(tps6586x_cell),
5b8b1fe2d   Laxman Dewangan   mfd: Add irq io-r...
523
  			      NULL, 0, tps6586x->irq_domain);
7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
524
525
526
527
  	if (ret < 0) {
  		dev_err(&client->dev, "mfd_add_devices failed: %d
  ", ret);
  		goto err_mfd_add;
6f9f13bf9   Vincent Palatin   mfd: Invert tps65...
528
  	}
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
529
530
531
532
533
534
  	ret = tps6586x_add_subdevs(tps6586x, pdata);
  	if (ret) {
  		dev_err(&client->dev, "add devices failed: %d
  ", ret);
  		goto err_add_devs;
  	}
004c15a68   Bill Huang   mfd: dt: tps6586x...
535
536
537
538
  	if (pdata->pm_off && !pm_power_off) {
  		tps6586x_dev = &client->dev;
  		pm_power_off = tps6586x_power_off;
  	}
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
539
540
541
  	return 0;
  
  err_add_devs:
7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
542
543
  	mfd_remove_devices(tps6586x->dev);
  err_mfd_add:
c26448c48   Gary King   mfd: Add basic tp...
544
545
  	if (client->irq)
  		free_irq(client->irq, tps6586x);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
546
547
  	return ret;
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
548
  static int tps6586x_i2c_remove(struct i2c_client *client)
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
549
  {
4b751cf5d   Axel Lin   mfd: Free tps6586...
550
  	struct tps6586x *tps6586x = i2c_get_clientdata(client);
4b751cf5d   Axel Lin   mfd: Free tps6586...
551

7a7487cb5   Laxman Dewangan   mfd: Remove gpio ...
552
553
  	tps6586x_remove_subdevs(tps6586x);
  	mfd_remove_devices(tps6586x->dev);
c26448c48   Gary King   mfd: Add basic tp...
554
555
  	if (client->irq)
  		free_irq(client->irq, tps6586x);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
556
557
  	return 0;
  }
ac4ca4b9f   Jonathan Hunter   mfd: tps6586x: Ha...
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  static int __maybe_unused tps6586x_i2c_suspend(struct device *dev)
  {
  	struct tps6586x *tps6586x = dev_get_drvdata(dev);
  
  	if (tps6586x->client->irq)
  		disable_irq(tps6586x->client->irq);
  
  	return 0;
  }
  
  static int __maybe_unused tps6586x_i2c_resume(struct device *dev)
  {
  	struct tps6586x *tps6586x = dev_get_drvdata(dev);
  
  	if (tps6586x->client->irq)
  		enable_irq(tps6586x->client->irq);
  
  	return 0;
  }
  
  static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend,
  			 tps6586x_i2c_resume);
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
580
581
582
583
584
585
586
587
588
  static const struct i2c_device_id tps6586x_id_table[] = {
  	{ "tps6586x", 0 },
  	{ },
  };
  MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
  
  static struct i2c_driver tps6586x_driver = {
  	.driver	= {
  		.name	= "tps6586x",
62f6b0879   Thierry Reding   tps6586x: Add dev...
589
  		.of_match_table = of_match_ptr(tps6586x_of_match),
ac4ca4b9f   Jonathan Hunter   mfd: tps6586x: Ha...
590
  		.pm	= &tps6586x_pm_ops,
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
591
592
  	},
  	.probe		= tps6586x_i2c_probe,
84449216b   Bill Pemberton   mfd: remove use o...
593
  	.remove		= tps6586x_i2c_remove,
c6c193326   Mike Rapoport   mfd: Add TPS6586x...
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
  	.id_table	= tps6586x_id_table,
  };
  
  static int __init tps6586x_init(void)
  {
  	return i2c_add_driver(&tps6586x_driver);
  }
  subsys_initcall(tps6586x_init);
  
  static void __exit tps6586x_exit(void)
  {
  	i2c_del_driver(&tps6586x_driver);
  }
  module_exit(tps6586x_exit);
  
  MODULE_DESCRIPTION("TPS6586X core driver");
  MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
  MODULE_LICENSE("GPL");