blob: 9b2ac74e827b9586bd0960d6b6216d8d95bb17ed [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Qualcomm PM8XXX Multi-Purpose Pin (MPP) driver
3 *
Duy Truong790f06d2013-02-13 16:38:12 -08004 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 and
8 * only version 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#define pr_fmt(fmt) "%s: " fmt, __func__
17
Steve Mucklef132c6c2012-06-06 18:30:57 -070018#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include <linux/platform_device.h>
20#include <linux/gpio.h>
21#include <linux/seq_file.h>
22#include <linux/slab.h>
23#include <linux/spinlock.h>
24#include <linux/mfd/pm8xxx/core.h>
25#include <linux/mfd/pm8xxx/mpp.h>
26
27/* MPP Type */
28#define PM8XXX_MPP_TYPE_MASK 0xE0
29#define PM8XXX_MPP_TYPE_SHIFT 5
30
31/* MPP Config Level */
32#define PM8XXX_MPP_CONFIG_LVL_MASK 0x1C
33#define PM8XXX_MPP_CONFIG_LVL_SHIFT 2
34
35/* MPP Config Control */
36#define PM8XXX_MPP_CONFIG_CTRL_MASK 0x03
37#define PM8XXX_MPP_CONFIG_CTRL_SHIFT 0
38
39struct pm8xxx_mpp_chip {
40 struct list_head link;
41 struct gpio_chip gpio_chip;
42 spinlock_t pm_lock;
43 u8 *ctrl_reg;
44 int mpp_base;
45 int irq_base;
46 int nmpps;
47 u16 base_addr;
48};
49
50static LIST_HEAD(pm8xxx_mpp_chips);
51static DEFINE_MUTEX(pm8xxx_mpp_chips_lock);
52
53static int pm8xxx_mpp_write(struct pm8xxx_mpp_chip *mpp_chip, u16 offset,
54 u8 val, u8 mask)
55{
56 u8 reg;
57 int rc;
58 unsigned long flags;
59
60 spin_lock_irqsave(&mpp_chip->pm_lock, flags);
61
62 reg = (mpp_chip->ctrl_reg[offset] & ~mask) | (val & mask);
63 rc = pm8xxx_writeb(mpp_chip->gpio_chip.dev->parent,
64 mpp_chip->base_addr + offset, reg);
65 if (!rc)
66 mpp_chip->ctrl_reg[offset] = reg;
67
68 spin_unlock_irqrestore(&mpp_chip->pm_lock, flags);
69
70 return rc;
71}
72
73static int pm8xxx_mpp_to_irq(struct gpio_chip *chip, unsigned offset)
74{
75 struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
76
77 return mpp_chip->irq_base + offset;
78}
79
80static int pm8xxx_mpp_get(struct gpio_chip *chip, unsigned offset)
81{
82 struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
83 int rc;
84
85 if ((mpp_chip->ctrl_reg[offset] & PM8XXX_MPP_TYPE_MASK) >>
86 PM8XXX_MPP_TYPE_SHIFT == PM8XXX_MPP_TYPE_D_OUTPUT)
87 rc = mpp_chip->ctrl_reg[offset] & PM8XXX_MPP_CONFIG_CTRL_MASK;
88 else
89 rc = pm8xxx_read_irq_stat(mpp_chip->gpio_chip.dev->parent,
90 mpp_chip->irq_base + offset);
91
92 return rc;
93}
94
95static void pm8xxx_mpp_set(struct gpio_chip *chip, unsigned offset, int val)
96{
97 struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
98 u8 reg = val ? PM8XXX_MPP_DOUT_CTRL_HIGH : PM8XXX_MPP_DOUT_CTRL_LOW;
99 int rc;
100
101 rc = pm8xxx_mpp_write(mpp_chip, offset, reg,
102 PM8XXX_MPP_CONFIG_CTRL_MASK);
103 if (rc)
104 pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
105}
106
107static int pm8xxx_mpp_dir_input(struct gpio_chip *chip, unsigned offset)
108{
109 struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
110 int rc = pm8xxx_mpp_write(mpp_chip, offset,
111 PM8XXX_MPP_TYPE_D_INPUT << PM8XXX_MPP_TYPE_SHIFT,
112 PM8XXX_MPP_TYPE_MASK);
113
114 if (rc)
115 pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
116 return rc;
117}
118
119static int pm8xxx_mpp_dir_output(struct gpio_chip *chip,
120 unsigned offset, int val)
121{
122 struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
123 u8 reg = (PM8XXX_MPP_TYPE_D_OUTPUT << PM8XXX_MPP_TYPE_SHIFT) |
124 (val & PM8XXX_MPP_CONFIG_CTRL_MASK);
125 u8 mask = PM8XXX_MPP_TYPE_MASK | PM8XXX_MPP_CONFIG_CTRL_MASK;
126 int rc = pm8xxx_mpp_write(mpp_chip, offset, reg, mask);
127
128 if (rc)
129 pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
130 return rc;
131}
132
133static void pm8xxx_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
134{
135 static const char * const ctype[] = { "d_in", "d_out", "bi_dir",
136 "a_in", "a_out", "sink",
137 "dtest_sink", "dtest_out"
138 };
139 struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
140 u8 type, state;
141 const char *label;
142 int i;
143
144 for (i = 0; i < mpp_chip->nmpps; i++) {
145 label = gpiochip_is_requested(chip, i);
146 type = (mpp_chip->ctrl_reg[i] & PM8XXX_MPP_TYPE_MASK) >>
147 PM8XXX_MPP_TYPE_SHIFT;
148 state = pm8xxx_mpp_get(chip, i);
149 seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
150 " %s 0x%02x\n",
151 chip->base + i,
152 label ? label : "--",
153 ctype[type],
154 state ? "hi" : "lo",
155 mpp_chip->ctrl_reg[i]);
156 }
157}
158
159int pm8xxx_mpp_config(unsigned mpp, struct pm8xxx_mpp_config_data *config)
160{
161 struct pm8xxx_mpp_chip *mpp_chip;
162 int rc, found = 0;
163 u8 config_reg, mask;
164
165 if (!config) {
166 pr_err("config not specified for MPP %d\n", mpp);
167 return -EINVAL;
168 }
169
170 mutex_lock(&pm8xxx_mpp_chips_lock);
171 list_for_each_entry(mpp_chip, &pm8xxx_mpp_chips, link) {
172 if (mpp >= mpp_chip->mpp_base
173 && mpp < mpp_chip->mpp_base + mpp_chip->nmpps) {
174 found = 1;
175 break;
176 }
177 }
178 mutex_unlock(&pm8xxx_mpp_chips_lock);
179 if (!found) {
180 pr_err("called on mpp %d not handled by any pmic\n", mpp);
181 return -EINVAL;
182 }
183
184 mask = PM8XXX_MPP_TYPE_MASK | PM8XXX_MPP_CONFIG_LVL_MASK |
185 PM8XXX_MPP_CONFIG_CTRL_MASK;
186 config_reg = (config->type << PM8XXX_MPP_TYPE_SHIFT)
187 & PM8XXX_MPP_TYPE_MASK;
188 config_reg |= (config->level << PM8XXX_MPP_CONFIG_LVL_SHIFT)
189 & PM8XXX_MPP_CONFIG_LVL_MASK;
190 config_reg |= config->control & PM8XXX_MPP_CONFIG_CTRL_MASK;
191
192 rc = pm8xxx_mpp_write(mpp_chip, mpp - mpp_chip->mpp_base, config_reg,
193 mask);
194
195 if (rc)
196 pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
197
198 return rc;
199}
200EXPORT_SYMBOL_GPL(pm8xxx_mpp_config);
201
202static int __devinit pm8xxx_mpp_reg_init(struct pm8xxx_mpp_chip *mpp_chip)
203{
204 int rc, i;
205
206 for (i = 0; i < mpp_chip->nmpps; i++) {
207 rc = pm8xxx_readb(mpp_chip->gpio_chip.dev->parent,
208 mpp_chip->base_addr + i,
209 &mpp_chip->ctrl_reg[i]);
210 if (rc) {
211 pr_err("failed to read register 0x%x rc=%d\n",
212 mpp_chip->base_addr + i, rc);
213 return rc;
214 }
215 }
216 return 0;
217}
218
219static int __devinit pm8xxx_mpp_probe(struct platform_device *pdev)
220{
221 int rc;
222 const struct pm8xxx_mpp_platform_data *pdata = pdev->dev.platform_data;
223 struct pm8xxx_mpp_chip *mpp_chip;
224
225 if (!pdata) {
226 pr_err("missing platform data\n");
227 return -EINVAL;
228 }
229
230 mpp_chip = kzalloc(sizeof(struct pm8xxx_mpp_chip), GFP_KERNEL);
231 if (!mpp_chip) {
232 pr_err("Cannot allocate %d bytes\n",
233 sizeof(struct pm8xxx_mpp_chip));
234 return -ENOMEM;
235 }
236
237 mpp_chip->ctrl_reg = kzalloc(pdata->core_data.nmpps, GFP_KERNEL);
238 if (!mpp_chip->ctrl_reg) {
239 pr_err("Cannot allocate %d bytes\n", pdata->core_data.nmpps);
240 rc = -ENOMEM;
241 goto free_mpp_chip;
242 }
243
244 spin_lock_init(&mpp_chip->pm_lock);
245
246 mpp_chip->gpio_chip.label = PM8XXX_MPP_DEV_NAME;
247 mpp_chip->gpio_chip.direction_input = pm8xxx_mpp_dir_input;
248 mpp_chip->gpio_chip.direction_output = pm8xxx_mpp_dir_output;
249 mpp_chip->gpio_chip.to_irq = pm8xxx_mpp_to_irq;
250 mpp_chip->gpio_chip.get = pm8xxx_mpp_get;
251 mpp_chip->gpio_chip.set = pm8xxx_mpp_set;
252 mpp_chip->gpio_chip.dbg_show = pm8xxx_mpp_dbg_show;
253 mpp_chip->gpio_chip.ngpio = pdata->core_data.nmpps;
David Collinsc010fa52011-12-14 14:10:49 -0800254 mpp_chip->gpio_chip.can_sleep = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 mpp_chip->gpio_chip.dev = &pdev->dev;
256 mpp_chip->gpio_chip.base = pdata->mpp_base;
257 mpp_chip->irq_base = platform_get_irq(pdev, 0);
258 mpp_chip->mpp_base = pdata->mpp_base;
259 mpp_chip->base_addr = pdata->core_data.base_addr;
260 mpp_chip->nmpps = pdata->core_data.nmpps;
261
262 mutex_lock(&pm8xxx_mpp_chips_lock);
263 list_add(&mpp_chip->link, &pm8xxx_mpp_chips);
264 mutex_unlock(&pm8xxx_mpp_chips_lock);
265
266 platform_set_drvdata(pdev, mpp_chip);
267
268 rc = gpiochip_add(&mpp_chip->gpio_chip);
269 if (rc) {
270 pr_err("gpiochip_add failed, rc=%d\n", rc);
271 goto reset_drvdata;
272 }
273
274 rc = pm8xxx_mpp_reg_init(mpp_chip);
275 if (rc) {
276 pr_err("failed to read MPP ctrl registers, rc=%d\n", rc);
277 goto remove_chip;
278 }
279
280 pr_info("OK: base=%d, ngpio=%d\n", mpp_chip->gpio_chip.base,
281 mpp_chip->gpio_chip.ngpio);
282
283 return 0;
284
285remove_chip:
286 if (gpiochip_remove(&mpp_chip->gpio_chip))
287 pr_err("failed to remove gpio chip\n");
288reset_drvdata:
289 platform_set_drvdata(pdev, NULL);
290free_mpp_chip:
291 kfree(mpp_chip);
292 return rc;
293}
294
295static int __devexit pm8xxx_mpp_remove(struct platform_device *pdev)
296{
297 struct pm8xxx_mpp_chip *mpp_chip = platform_get_drvdata(pdev);
298
299 mutex_lock(&pm8xxx_mpp_chips_lock);
300 list_del(&mpp_chip->link);
301 mutex_unlock(&pm8xxx_mpp_chips_lock);
302 platform_set_drvdata(pdev, NULL);
303 if (gpiochip_remove(&mpp_chip->gpio_chip))
304 pr_err("failed to remove gpio chip\n");
305 kfree(mpp_chip->ctrl_reg);
306 kfree(mpp_chip);
307
308 return 0;
309}
310
311static struct platform_driver pm8xxx_mpp_driver = {
312 .probe = pm8xxx_mpp_probe,
313 .remove = __devexit_p(pm8xxx_mpp_remove),
314 .driver = {
315 .name = PM8XXX_MPP_DEV_NAME,
316 .owner = THIS_MODULE,
317 },
318};
319
320static int __init pm8xxx_mpp_init(void)
321{
322 return platform_driver_register(&pm8xxx_mpp_driver);
323}
324postcore_initcall(pm8xxx_mpp_init);
325
326static void __exit pm8xxx_mpp_exit(void)
327{
328 platform_driver_unregister(&pm8xxx_mpp_driver);
329}
330module_exit(pm8xxx_mpp_exit);
331
332MODULE_LICENSE("GPL v2");
333MODULE_DESCRIPTION("PM8XXX MPP driver");
334MODULE_VERSION("1.0");
335MODULE_ALIAS("platform:" PM8XXX_MPP_DEV_NAME);