2 * DA9150 Core MFD Driver
4 * Copyright (c) 2014 Dialog Semiconductor
6 * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/i2c.h>
18 #include <linux/regmap.h>
19 #include <linux/slab.h>
20 #include <linux/irq.h>
21 #include <linux/interrupt.h>
22 #include <linux/mfd/core.h>
23 #include <linux/mfd/da9150/core.h>
24 #include <linux/mfd/da9150/registers.h>
26 static bool da9150_volatile_reg(struct device *dev, unsigned int reg)
43 case DA9150_FAULT_LOG_A:
44 case DA9150_FAULT_LOG_B:
49 case DA9150_CONTROL_B:
50 case DA9150_CONTROL_C:
51 case DA9150_GPADC_MAN:
52 case DA9150_GPADC_RES_A:
53 case DA9150_GPADC_RES_B:
54 case DA9150_ADETVB_CFG_C:
55 case DA9150_ADETD_STAT:
56 case DA9150_ADET_CMPSTAT:
57 case DA9150_ADET_CTRL_A:
58 case DA9150_PPR_TCTR_B:
59 case DA9150_COREBTLD_STAT_A:
60 case DA9150_CORE_DATA_A:
61 case DA9150_CORE_DATA_B:
62 case DA9150_CORE_DATA_C:
63 case DA9150_CORE_DATA_D:
64 case DA9150_CORE2WIRE_STAT_A:
65 case DA9150_FW_CTRL_C:
66 case DA9150_FG_CTRL_B:
67 case DA9150_FW_CTRL_B:
68 case DA9150_GPADC_CMAN:
69 case DA9150_GPADC_CRES_A:
70 case DA9150_GPADC_CRES_B:
71 case DA9150_CC_ICHG_RES_A:
72 case DA9150_CC_ICHG_RES_B:
73 case DA9150_CC_IAVG_RES_A:
74 case DA9150_CC_IAVG_RES_B:
75 case DA9150_TAUX_CTRL_A:
76 case DA9150_TAUX_VALUE_H:
77 case DA9150_TAUX_VALUE_L:
78 case DA9150_TBAT_RES_A:
79 case DA9150_TBAT_RES_B:
86 static const struct regmap_range_cfg da9150_range_cfg[] = {
88 .range_min = DA9150_PAGE_CON,
89 .range_max = DA9150_TBAT_RES_B,
90 .selector_reg = DA9150_PAGE_CON,
91 .selector_mask = DA9150_I2C_PAGE_MASK,
92 .selector_shift = DA9150_I2C_PAGE_SHIFT,
98 static const struct regmap_config da9150_regmap_config = {
101 .ranges = da9150_range_cfg,
102 .num_ranges = ARRAY_SIZE(da9150_range_cfg),
103 .max_register = DA9150_TBAT_RES_B,
105 .cache_type = REGCACHE_RBTREE,
107 .volatile_reg = da9150_volatile_reg,
110 u8 da9150_reg_read(struct da9150 *da9150, u16 reg)
114 ret = regmap_read(da9150->regmap, reg, &val);
116 dev_err(da9150->dev, "Failed to read from reg 0x%x: %d\n",
121 EXPORT_SYMBOL_GPL(da9150_reg_read);
123 void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val)
127 ret = regmap_write(da9150->regmap, reg, val);
129 dev_err(da9150->dev, "Failed to write to reg 0x%x: %d\n",
132 EXPORT_SYMBOL_GPL(da9150_reg_write);
134 void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val)
138 ret = regmap_update_bits(da9150->regmap, reg, mask, val);
140 dev_err(da9150->dev, "Failed to set bits in reg 0x%x: %d\n",
143 EXPORT_SYMBOL_GPL(da9150_set_bits);
145 void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf)
149 ret = regmap_bulk_read(da9150->regmap, reg, buf, count);
151 dev_err(da9150->dev, "Failed to bulk read from reg 0x%x: %d\n",
154 EXPORT_SYMBOL_GPL(da9150_bulk_read);
156 void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
160 ret = regmap_raw_write(da9150->regmap, reg, buf, count);
162 dev_err(da9150->dev, "Failed to bulk write to reg 0x%x %d\n",
165 EXPORT_SYMBOL_GPL(da9150_bulk_write);
167 static const struct regmap_irq da9150_irqs[] = {
168 [DA9150_IRQ_VBUS] = {
170 .mask = DA9150_E_VBUS_MASK,
174 .mask = DA9150_E_CHG_MASK,
176 [DA9150_IRQ_TCLASS] = {
178 .mask = DA9150_E_TCLASS_MASK,
180 [DA9150_IRQ_TJUNC] = {
182 .mask = DA9150_E_TJUNC_MASK,
184 [DA9150_IRQ_VFAULT] = {
186 .mask = DA9150_E_VFAULT_MASK,
188 [DA9150_IRQ_CONF] = {
190 .mask = DA9150_E_CONF_MASK,
194 .mask = DA9150_E_DAT_MASK,
196 [DA9150_IRQ_DTYPE] = {
198 .mask = DA9150_E_DTYPE_MASK,
202 .mask = DA9150_E_ID_MASK,
206 .mask = DA9150_E_ADP_MASK,
208 [DA9150_IRQ_SESS_END] = {
210 .mask = DA9150_E_SESS_END_MASK,
212 [DA9150_IRQ_SESS_VLD] = {
214 .mask = DA9150_E_SESS_VLD_MASK,
218 .mask = DA9150_E_FG_MASK,
222 .mask = DA9150_E_GP_MASK,
224 [DA9150_IRQ_TBAT] = {
226 .mask = DA9150_E_TBAT_MASK,
228 [DA9150_IRQ_GPIOA] = {
230 .mask = DA9150_E_GPIOA_MASK,
232 [DA9150_IRQ_GPIOB] = {
234 .mask = DA9150_E_GPIOB_MASK,
236 [DA9150_IRQ_GPIOC] = {
238 .mask = DA9150_E_GPIOC_MASK,
240 [DA9150_IRQ_GPIOD] = {
242 .mask = DA9150_E_GPIOD_MASK,
244 [DA9150_IRQ_GPADC] = {
246 .mask = DA9150_E_GPADC_MASK,
248 [DA9150_IRQ_WKUP] = {
250 .mask = DA9150_E_WKUP_MASK,
254 static const struct regmap_irq_chip da9150_regmap_irq_chip = {
255 .name = "da9150_irq",
256 .status_base = DA9150_EVENT_E,
257 .mask_base = DA9150_IRQ_MASK_E,
258 .ack_base = DA9150_EVENT_E,
259 .num_regs = DA9150_NUM_IRQ_REGS,
261 .num_irqs = ARRAY_SIZE(da9150_irqs),
264 static struct resource da9150_gpadc_resources[] = {
267 .start = DA9150_IRQ_GPADC,
268 .end = DA9150_IRQ_GPADC,
269 .flags = IORESOURCE_IRQ,
273 static struct resource da9150_charger_resources[] = {
275 .name = "CHG_STATUS",
276 .start = DA9150_IRQ_CHG,
277 .end = DA9150_IRQ_CHG,
278 .flags = IORESOURCE_IRQ,
282 .start = DA9150_IRQ_TJUNC,
283 .end = DA9150_IRQ_TJUNC,
284 .flags = IORESOURCE_IRQ,
287 .name = "CHG_VFAULT",
288 .start = DA9150_IRQ_VFAULT,
289 .end = DA9150_IRQ_VFAULT,
290 .flags = IORESOURCE_IRQ,
294 .start = DA9150_IRQ_VBUS,
295 .end = DA9150_IRQ_VBUS,
296 .flags = IORESOURCE_IRQ,
300 static struct mfd_cell da9150_devs[] = {
302 .name = "da9150-gpadc",
303 .of_compatible = "dlg,da9150-gpadc",
304 .resources = da9150_gpadc_resources,
305 .num_resources = ARRAY_SIZE(da9150_gpadc_resources),
308 .name = "da9150-charger",
309 .of_compatible = "dlg,da9150-charger",
310 .resources = da9150_charger_resources,
311 .num_resources = ARRAY_SIZE(da9150_charger_resources),
315 static int da9150_probe(struct i2c_client *client,
316 const struct i2c_device_id *id)
318 struct da9150 *da9150;
319 struct da9150_pdata *pdata = dev_get_platdata(&client->dev);
322 da9150 = devm_kzalloc(&client->dev, sizeof(*da9150), GFP_KERNEL);
326 da9150->dev = &client->dev;
327 da9150->irq = client->irq;
328 i2c_set_clientdata(client, da9150);
330 da9150->regmap = devm_regmap_init_i2c(client, &da9150_regmap_config);
331 if (IS_ERR(da9150->regmap)) {
332 ret = PTR_ERR(da9150->regmap);
333 dev_err(da9150->dev, "Failed to allocate register map: %d\n",
338 da9150->irq_base = pdata ? pdata->irq_base : -1;
340 ret = regmap_add_irq_chip(da9150->regmap, da9150->irq,
341 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
342 da9150->irq_base, &da9150_regmap_irq_chip,
343 &da9150->regmap_irq_data);
347 da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data);
348 enable_irq_wake(da9150->irq);
350 ret = mfd_add_devices(da9150->dev, -1, da9150_devs,
351 ARRAY_SIZE(da9150_devs), NULL,
352 da9150->irq_base, NULL);
354 dev_err(da9150->dev, "Failed to add child devices: %d\n", ret);
355 regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
362 static int da9150_remove(struct i2c_client *client)
364 struct da9150 *da9150 = i2c_get_clientdata(client);
366 regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
367 mfd_remove_devices(da9150->dev);
372 static void da9150_shutdown(struct i2c_client *client)
374 struct da9150 *da9150 = i2c_get_clientdata(client);
376 /* Make sure we have a wakup source for the device */
377 da9150_set_bits(da9150, DA9150_CONFIG_D,
378 DA9150_WKUP_PM_EN_MASK,
379 DA9150_WKUP_PM_EN_MASK);
381 /* Set device to DISABLED mode */
382 da9150_set_bits(da9150, DA9150_CONTROL_C,
383 DA9150_DISABLE_MASK, DA9150_DISABLE_MASK);
386 static const struct i2c_device_id da9150_i2c_id[] = {
390 MODULE_DEVICE_TABLE(i2c, da9150_i2c_id);
392 static const struct of_device_id da9150_of_match[] = {
393 { .compatible = "dlg,da9150", },
396 MODULE_DEVICE_TABLE(of, da9150_of_match);
398 static struct i2c_driver da9150_driver = {
401 .of_match_table = of_match_ptr(da9150_of_match),
403 .probe = da9150_probe,
404 .remove = da9150_remove,
405 .shutdown = da9150_shutdown,
406 .id_table = da9150_i2c_id,
409 module_i2c_driver(da9150_driver);
411 MODULE_DESCRIPTION("MFD Core Driver for DA9150");
412 MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
413 MODULE_LICENSE("GPL");