blob: 85e65393f997cd3291622f32b4259488f16b6b78 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13/*
14 * Qualcomm PMIC8901 MPP driver
15 *
16 */
17
18#include <linux/platform_device.h>
19#include <linux/gpio.h>
20#include <linux/mfd/pmic8901.h>
21#include <mach/mpp.h>
22#include <linux/seq_file.h>
23
24/* MPP Control Registers */
25#define SSBI_MPP_CNTRL_BASE 0x27
26#define SSBI_MPP_CNTRL(n) (SSBI_MPP_CNTRL_BASE + (n))
27
28/* MPP Type */
29#define PM8901_MPP_TYPE_MASK 0xE0
30#define PM8901_MPP_TYPE_SHIFT 5
31
32/* MPP Config Level */
33#define PM8901_MPP_CONFIG_LVL_MASK 0x1C
34#define PM8901_MPP_CONFIG_LVL_SHIFT 2
35
36/* MPP Config Control */
37#define PM8901_MPP_CONFIG_CTL_MASK 0x03
38
39struct pm8901_mpp_chip {
40 struct gpio_chip chip;
41 struct pm8901_chip *pm_chip;
42 u8 ctrl[PM8901_MPPS];
43};
44
45static int pm8901_mpp_write(struct pm8901_chip *chip, u16 addr, u8 val,
46 u8 mask, u8 *bak)
47{
48 u8 reg = (*bak & ~mask) | (val & mask);
49 int rc = pm8901_write(chip, addr, &reg, 1);
50 if (!rc)
51 *bak = reg;
52 return rc;
53}
54
55static int pm8901_mpp_to_irq(struct gpio_chip *chip, unsigned offset)
56{
57 struct pm8901_gpio_platform_data *pdata;
58 pdata = chip->dev->platform_data;
59 return pdata->irq_base + offset;
60}
61
62static int pm8901_mpp_get(struct gpio_chip *chip, unsigned offset)
63{
64 struct pm8901_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
65 int ret;
66
67 if ((mpp_chip->ctrl[offset] & PM8901_MPP_TYPE_MASK) >>
68 PM8901_MPP_TYPE_SHIFT == PM_MPP_TYPE_D_OUTPUT)
69 ret = mpp_chip->ctrl[offset] & PM8901_MPP_CONFIG_CTL_MASK;
70 else
71 ret = pm8901_irq_get_rt_status(mpp_chip->pm_chip,
72 pm8901_mpp_to_irq(chip, offset));
73 return ret;
74}
75
76static void pm8901_mpp_set(struct gpio_chip *chip, unsigned offset, int val)
77{
78 struct pm8901_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
79 u8 reg = val ? PM_MPP_DOUT_CTL_HIGH : PM_MPP_DOUT_CTL_LOW;
80 int rc;
81
82 rc = pm8901_mpp_write(mpp_chip->pm_chip, SSBI_MPP_CNTRL(offset),
83 reg, PM8901_MPP_CONFIG_CTL_MASK,
84 &mpp_chip->ctrl[offset]);
85 if (rc)
86 pr_err("%s: pm8901_mpp_write(): rc=%d\n", __func__, rc);
87}
88
89static int pm8901_mpp_dir_input(struct gpio_chip *chip, unsigned offset)
90{
91 struct pm8901_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
92 int rc = pm8901_mpp_write(mpp_chip->pm_chip,
93 SSBI_MPP_CNTRL(offset),
94 PM_MPP_TYPE_D_INPUT << PM8901_MPP_TYPE_SHIFT,
95 PM8901_MPP_TYPE_MASK, &mpp_chip->ctrl[offset]);
96 if (rc)
97 pr_err("%s: pm8901_mpp_write(): rc=%d\n", __func__, rc);
98 return rc;
99}
100
101static int pm8901_mpp_dir_output(struct gpio_chip *chip,
102 unsigned offset, int val)
103{
104 struct pm8901_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
105 u8 reg = (PM_MPP_TYPE_D_OUTPUT << PM8901_MPP_TYPE_SHIFT) |
106 (val & PM8901_MPP_CONFIG_CTL_MASK);
107 u8 mask = PM8901_MPP_TYPE_MASK | PM8901_MPP_CONFIG_CTL_MASK;
108 int rc = pm8901_mpp_write(mpp_chip->pm_chip,
109 SSBI_MPP_CNTRL(offset), reg, mask,
110 &mpp_chip->ctrl[offset]);
111 if (rc)
112 pr_err("%s: pm8901_mpp_write(): rc=%d\n", __func__, rc);
113 return rc;
114}
115
116static void pm8901_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
117{
118 static const char *ctype[] = { "d_in", "d_out", "bi_dir", "a_in",
119 "a_out", "sink", "dtest_sink", "dtest_out" };
120 struct pm8901_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
121 u8 type, state;
122 const char *label;
123 int i;
124
125 for (i = 0; i < PM8901_MPPS; i++) {
126 label = gpiochip_is_requested(chip, i);
127 type = (mpp_chip->ctrl[i] & PM8901_MPP_TYPE_MASK) >>
128 PM8901_MPP_TYPE_SHIFT;
129 state = pm8901_mpp_get(chip, i);
130 seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
131 " %s 0x%02x\n",
132 chip->base + i,
133 label ? label : "--",
134 ctype[type],
135 state ? "hi" : "lo",
136 mpp_chip->ctrl[i]);
137 }
138}
139
140static struct pm8901_mpp_chip pm8901_mpp_chip = {
141 .chip = {
142 .label = "pm8901-mpp",
143 .to_irq = pm8901_mpp_to_irq,
144 .get = pm8901_mpp_get,
145 .set = pm8901_mpp_set,
146 .direction_input = pm8901_mpp_dir_input,
147 .direction_output = pm8901_mpp_dir_output,
148 .dbg_show = pm8901_mpp_dbg_show,
149 .ngpio = PM8901_MPPS,
150 },
151};
152
153int pm8901_mpp_config(unsigned mpp, unsigned type, unsigned level,
154 unsigned control)
155{
156 u8 config, mask;
157 int rc;
158
159 if (mpp >= PM8901_MPPS)
160 return -EINVAL;
161
162 mask = PM8901_MPP_TYPE_MASK | PM8901_MPP_CONFIG_LVL_MASK |
163 PM8901_MPP_CONFIG_CTL_MASK;
164 config = (type << PM8901_MPP_TYPE_SHIFT) & PM8901_MPP_TYPE_MASK;
165 config |= (level << PM8901_MPP_CONFIG_LVL_SHIFT) &
166 PM8901_MPP_CONFIG_LVL_MASK;
167 config |= control & PM8901_MPP_CONFIG_CTL_MASK;
168
169 rc = pm8901_mpp_write(pm8901_mpp_chip.pm_chip, SSBI_MPP_CNTRL(mpp),
170 config, mask, &pm8901_mpp_chip.ctrl[mpp]);
171 if (rc)
172 pr_err("%s: pm8901_mpp_write(): rc=%d\n", __func__, rc);
173
174 return rc;
175}
176EXPORT_SYMBOL(pm8901_mpp_config);
177
178static int __devinit pm8901_mpp_probe(struct platform_device *pdev)
179{
180 int ret, i;
181 struct pm8901_gpio_platform_data *pdata = pdev->dev.platform_data;
182
183 pm8901_mpp_chip.pm_chip = dev_get_drvdata(pdev->dev.parent);
184 for (i = 0; i < PM8901_MPPS; i++) {
185 ret = pm8901_read(pm8901_mpp_chip.pm_chip,
186 SSBI_MPP_CNTRL(i), &pm8901_mpp_chip.ctrl[i], 1);
187 if (ret)
188 goto bail;
189
190 }
191 platform_set_drvdata(pdev, &pm8901_mpp_chip);
192 pm8901_mpp_chip.chip.dev = &pdev->dev;
193 pm8901_mpp_chip.chip.base = pdata->gpio_base;
194 ret = gpiochip_add(&pm8901_mpp_chip.chip);
195
196bail:
197 pr_info("%s: gpiochip_add(): rc=%d\n", __func__, ret);
198 return ret;
199}
200
201static int __devexit pm8901_mpp_remove(struct platform_device *pdev)
202{
203 return gpiochip_remove(&pm8901_mpp_chip.chip);
204}
205
206static struct platform_driver pm8901_mpp_driver = {
207 .probe = pm8901_mpp_probe,
208 .remove = __devexit_p(pm8901_mpp_remove),
209 .driver = {
210 .name = "pm8901-mpp",
211 .owner = THIS_MODULE,
212 },
213};
214
215static int __init pm8901_mpp_init(void)
216{
217 return platform_driver_register(&pm8901_mpp_driver);
218}
219
220static void __exit pm8901_mpp_exit(void)
221{
222 platform_driver_unregister(&pm8901_mpp_driver);
223}
224
225subsys_initcall(pm8901_mpp_init);
226module_exit(pm8901_mpp_exit);
227
228MODULE_LICENSE("GPL v2");
229MODULE_DESCRIPTION("PMIC8901 MPP driver");
230MODULE_VERSION("1.0");
231MODULE_ALIAS("platform:pm8901-mpp");