aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/max14577.c
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2013-11-22 10:51:05 -0500
committerLee Jones <lee.jones@linaro.org>2014-01-21 03:13:36 -0500
commit3008ddbe061b0f1d5c8ffbb599f105b67cf06637 (patch)
treeccad503aca0fd33432bba8bc6998151d99214674 /drivers/mfd/max14577.c
parentf6d6daaf9b5260f1769ed040caca53e1c087ca8b (diff)
mfd: max14577: Add max14577 MFD driver core
This patch adds max14577 core/irq driver to support MUIC(Micro USB IC) device and charger device and support irq domain method to control internal interrupt of max14577 device. Also, this patch supports DT binding with max14577_i2c_parse_dt(). The MAXIM 14577 chip contains Micro-USB Interface Circuit and Li+ Battery Charger. It contains accessory and USB charger detection logic. It supports USB 2.0 Hi-Speed, UART and stereo audio signals over Micro-USB connector. The battery charger is compliant with the USB Battery Charging Specification Revision 1.1. It has also SFOUT LDO output for powering USB devices. Reviewed-by: Mark Brown <broonie@linaro.org> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd/max14577.c')
-rw-r--r--drivers/mfd/max14577.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
new file mode 100644
index 000000000000..94b766d8cb15
--- /dev/null
+++ b/drivers/mfd/max14577.c
@@ -0,0 +1,243 @@
1/*
2 * max14577.c - mfd core driver for the Maxim 14577
3 *
4 * Copyright (C) 2013 Samsung Electrnoics
5 * Chanwoo Choi <cw00.choi@samsung.com>
6 * Krzysztof Kozlowski <k.kozlowski@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * This driver is based on max8997.c
19 */
20
21#include <linux/module.h>
22#include <linux/interrupt.h>
23#include <linux/mfd/core.h>
24#include <linux/mfd/max14577.h>
25#include <linux/mfd/max14577-private.h>
26
27static struct mfd_cell max14577_devs[] = {
28 { .name = "max14577-muic", },
29 { .name = "max14577-regulator", },
30 { .name = "max14577-charger", },
31};
32
33static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
34{
35 switch (reg) {
36 case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
37 return true;
38 default:
39 break;
40 }
41 return false;
42}
43
44static const struct regmap_config max14577_regmap_config = {
45 .reg_bits = 8,
46 .val_bits = 8,
47 .volatile_reg = max14577_volatile_reg,
48 .max_register = MAX14577_REG_END,
49};
50
51static const struct regmap_irq max14577_irqs[] = {
52 /* INT1 interrupts */
53 { .reg_offset = 0, .mask = INT1_ADC_MASK, },
54 { .reg_offset = 0, .mask = INT1_ADCLOW_MASK, },
55 { .reg_offset = 0, .mask = INT1_ADCERR_MASK, },
56 /* INT2 interrupts */
57 { .reg_offset = 1, .mask = INT2_CHGTYP_MASK, },
58 { .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, },
59 { .reg_offset = 1, .mask = INT2_DCDTMR_MASK, },
60 { .reg_offset = 1, .mask = INT2_DBCHG_MASK, },
61 { .reg_offset = 1, .mask = INT2_VBVOLT_MASK, },
62 /* INT3 interrupts */
63 { .reg_offset = 2, .mask = INT3_EOC_MASK, },
64 { .reg_offset = 2, .mask = INT3_CGMBC_MASK, },
65 { .reg_offset = 2, .mask = INT3_OVP_MASK, },
66 { .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, },
67};
68
69static const struct regmap_irq_chip max14577_irq_chip = {
70 .name = "max14577",
71 .status_base = MAX14577_REG_INT1,
72 .mask_base = MAX14577_REG_INTMASK1,
73 .mask_invert = 1,
74 .num_regs = 3,
75 .irqs = max14577_irqs,
76 .num_irqs = ARRAY_SIZE(max14577_irqs),
77};
78
79static int max14577_i2c_probe(struct i2c_client *i2c,
80 const struct i2c_device_id *id)
81{
82 struct max14577 *max14577;
83 struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
84 struct device_node *np = i2c->dev.of_node;
85 u8 reg_data;
86 int ret = 0;
87
88 if (np) {
89 pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
90 if (!pdata)
91 return -ENOMEM;
92 i2c->dev.platform_data = pdata;
93 }
94
95 if (!pdata) {
96 dev_err(&i2c->dev, "No platform data found: %ld\n",
97 PTR_ERR(pdata));
98 return -EINVAL;
99 }
100
101 max14577 = devm_kzalloc(&i2c->dev, sizeof(*max14577), GFP_KERNEL);
102 if (!max14577)
103 return -ENOMEM;
104
105 i2c_set_clientdata(i2c, max14577);
106 max14577->dev = &i2c->dev;
107 max14577->i2c = i2c;
108 max14577->irq = i2c->irq;
109
110 max14577->regmap = devm_regmap_init_i2c(i2c, &max14577_regmap_config);
111 if (IS_ERR(max14577->regmap)) {
112 ret = PTR_ERR(max14577->regmap);
113 dev_err(max14577->dev, "Failed to allocate register map: %d\n",
114 ret);
115 return ret;
116 }
117
118 ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
119 &reg_data);
120 if (ret) {
121 dev_err(max14577->dev, "Device not found on this channel: %d\n",
122 ret);
123 return ret;
124 }
125 max14577->vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
126 DEVID_VENDORID_SHIFT);
127 max14577->device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
128 DEVID_DEVICEID_SHIFT);
129 dev_info(max14577->dev, "Device ID: 0x%x, vendor: 0x%x\n",
130 max14577->device_id, max14577->vendor_id);
131
132 ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
133 IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
134 &max14577_irq_chip,
135 &max14577->irq_data);
136 if (ret != 0) {
137 dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
138 max14577->irq, ret);
139 return ret;
140 }
141
142 ret = mfd_add_devices(max14577->dev, -1, max14577_devs,
143 ARRAY_SIZE(max14577_devs), NULL, 0,
144 regmap_irq_get_domain(max14577->irq_data));
145 if (ret < 0)
146 goto err_mfd;
147
148 device_init_wakeup(max14577->dev, 1);
149
150 return 0;
151
152err_mfd:
153 regmap_del_irq_chip(max14577->irq, max14577->irq_data);
154
155 return ret;
156}
157
158static int max14577_i2c_remove(struct i2c_client *i2c)
159{
160 struct max14577 *max14577 = i2c_get_clientdata(i2c);
161
162 mfd_remove_devices(max14577->dev);
163 regmap_del_irq_chip(max14577->irq, max14577->irq_data);
164
165 return 0;
166}
167
168static const struct i2c_device_id max14577_i2c_id[] = {
169 { "max14577", 0 },
170 { }
171};
172MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
173
174static int max14577_suspend(struct device *dev)
175{
176 struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
177 struct max14577 *max14577 = i2c_get_clientdata(i2c);
178
179 if (device_may_wakeup(dev)) {
180 enable_irq_wake(max14577->irq);
181 /*
182 * MUIC IRQ must be disabled during suspend if this is
183 * a wake up source because it will be handled before
184 * resuming I2C.
185 *
186 * When device is woken up from suspend (e.g. by ADC change),
187 * an interrupt occurs before resuming I2C bus controller.
188 * Interrupt handler tries to read registers but this read
189 * will fail because I2C is still suspended.
190 */
191 disable_irq(max14577->irq);
192 }
193
194 return 0;
195}
196
197static int max14577_resume(struct device *dev)
198{
199 struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
200 struct max14577 *max14577 = i2c_get_clientdata(i2c);
201
202 if (device_may_wakeup(dev)) {
203 disable_irq_wake(max14577->irq);
204 enable_irq(max14577->irq);
205 }
206
207 return 0;
208}
209
210static struct of_device_id max14577_dt_match[] = {
211 { .compatible = "maxim,max14577", },
212 {},
213};
214
215static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
216
217static struct i2c_driver max14577_i2c_driver = {
218 .driver = {
219 .name = "max14577",
220 .owner = THIS_MODULE,
221 .pm = &max14577_pm,
222 .of_match_table = of_match_ptr(max14577_dt_match),
223 },
224 .probe = max14577_i2c_probe,
225 .remove = max14577_i2c_remove,
226 .id_table = max14577_i2c_id,
227};
228
229static int __init max14577_i2c_init(void)
230{
231 return i2c_add_driver(&max14577_i2c_driver);
232}
233subsys_initcall(max14577_i2c_init);
234
235static void __exit max14577_i2c_exit(void)
236{
237 i2c_del_driver(&max14577_i2c_driver);
238}
239module_exit(max14577_i2c_exit);
240
241MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
242MODULE_DESCRIPTION("MAXIM 14577 multi-function core driver");
243MODULE_LICENSE("GPL");