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