regulator: regulator for Palmas Kconfig
[linux-2.6.git] / drivers / regulator / aat2870-regulator.c
1 /*
2  * linux/drivers/regulator/aat2870-regulator.c
3  *
4  * Copyright (c) 2011, NVIDIA Corporation.
5  * Author: Jin Park <jinyoungp@nvidia.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA
20  */
21
22 #include <linux/kernel.h>
23 #include <linux/init.h>
24 #include <linux/err.h>
25 #include <linux/module.h>
26 #include <linux/slab.h>
27 #include <linux/delay.h>
28 #include <linux/platform_device.h>
29 #include <linux/regulator/driver.h>
30 #include <linux/regulator/machine.h>
31 #include <linux/mfd/core.h>
32 #include <linux/mfd/aat2870.h>
33
34 struct aat2870_regulator {
35         struct aat2870_data *aat2870;
36         struct regulator_desc desc;
37
38         const int *voltages; /* uV */
39
40         int min_uV;
41         int max_uV;
42
43         u8 enable_addr;
44         u8 enable_shift;
45         u8 enable_mask;
46
47         u8 voltage_addr;
48         u8 voltage_shift;
49         u8 voltage_mask;
50 };
51
52 static int aat2870_ldo_list_voltage(struct regulator_dev *rdev,
53                                     unsigned selector)
54 {
55         struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
56
57         return ri->voltages[selector];
58 }
59
60 static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev,
61                                        unsigned selector)
62 {
63         struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
64         struct aat2870_data *aat2870 = ri->aat2870;
65
66         return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask,
67                                selector << ri->voltage_shift);
68 }
69
70 static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev)
71 {
72         struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
73         struct aat2870_data *aat2870 = ri->aat2870;
74         u8 val;
75         int ret;
76
77         ret = aat2870->read(aat2870, ri->voltage_addr, &val);
78         if (ret)
79                 return ret;
80
81         return (val & ri->voltage_mask) >> ri->voltage_shift;
82 }
83
84 static int aat2870_ldo_enable(struct regulator_dev *rdev)
85 {
86         struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
87         struct aat2870_data *aat2870 = ri->aat2870;
88
89         return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask,
90                                ri->enable_mask);
91 }
92
93 static int aat2870_ldo_disable(struct regulator_dev *rdev)
94 {
95         struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
96         struct aat2870_data *aat2870 = ri->aat2870;
97
98         return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0);
99 }
100
101 static int aat2870_ldo_is_enabled(struct regulator_dev *rdev)
102 {
103         struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
104         struct aat2870_data *aat2870 = ri->aat2870;
105         u8 val;
106         int ret;
107
108         ret = aat2870->read(aat2870, ri->enable_addr, &val);
109         if (ret)
110                 return ret;
111
112         return val & ri->enable_mask ? 1 : 0;
113 }
114
115 static struct regulator_ops aat2870_ldo_ops = {
116         .list_voltage = aat2870_ldo_list_voltage,
117         .set_voltage_sel = aat2870_ldo_set_voltage_sel,
118         .get_voltage_sel = aat2870_ldo_get_voltage_sel,
119         .enable = aat2870_ldo_enable,
120         .disable = aat2870_ldo_disable,
121         .is_enabled = aat2870_ldo_is_enabled,
122 };
123
124 static const int aat2870_ldo_voltages[] = {
125         1200000, 1300000, 1500000, 1600000,
126         1800000, 2000000, 2200000, 2500000,
127         2600000, 2700000, 2800000, 2900000,
128         3000000, 3100000, 3200000, 3300000,
129 };
130
131 #define AAT2870_LDO(ids)                                \
132         {                                               \
133                 .desc = {                               \
134                         .name = #ids,                   \
135                         .id = AAT2870_ID_##ids,         \
136                         .n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \
137                         .ops = &aat2870_ldo_ops,        \
138                         .type = REGULATOR_VOLTAGE,      \
139                         .owner = THIS_MODULE,           \
140                 },                                      \
141                 .voltages = aat2870_ldo_voltages,       \
142                 .min_uV = 1200000,                      \
143                 .max_uV = 3300000,                      \
144         }
145
146 static struct aat2870_regulator aat2870_regulators[] = {
147         AAT2870_LDO(LDOA),
148         AAT2870_LDO(LDOB),
149         AAT2870_LDO(LDOC),
150         AAT2870_LDO(LDOD),
151 };
152
153 static struct aat2870_regulator *aat2870_get_regulator(int id)
154 {
155         struct aat2870_regulator *ri = NULL;
156         int i;
157
158         for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) {
159                 ri = &aat2870_regulators[i];
160                 if (ri->desc.id == id)
161                         break;
162         }
163
164         if (i == ARRAY_SIZE(aat2870_regulators))
165                 return NULL;
166
167         ri->enable_addr = AAT2870_LDO_EN;
168         ri->enable_shift = id - AAT2870_ID_LDOA;
169         ri->enable_mask = 0x1 << ri->enable_shift;
170
171         ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ?
172                            AAT2870_LDO_CD : AAT2870_LDO_AB;
173         ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4;
174         ri->voltage_mask = 0xF << ri->voltage_shift;
175
176         return ri;
177 }
178
179 static int aat2870_regulator_probe(struct platform_device *pdev)
180 {
181         struct aat2870_regulator *ri;
182         struct regulator_dev *rdev;
183
184         ri = aat2870_get_regulator(pdev->id);
185         if (!ri) {
186                 dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
187                 return -EINVAL;
188         }
189         ri->aat2870 = dev_get_drvdata(pdev->dev.parent);
190
191         rdev = regulator_register(&ri->desc, &pdev->dev,
192                                   mfd_get_data(pdev), ri, NULL);
193         if (IS_ERR(rdev)) {
194                 dev_err(&pdev->dev, "Failed to register regulator %s\n",
195                         ri->desc.name);
196                 return PTR_ERR(rdev);
197         }
198         platform_set_drvdata(pdev, rdev);
199
200         return 0;
201 }
202
203 static int __devexit aat2870_regulator_remove(struct platform_device *pdev)
204 {
205         struct regulator_dev *rdev = platform_get_drvdata(pdev);
206
207         regulator_unregister(rdev);
208         return 0;
209 }
210
211 static struct platform_driver aat2870_regulator_driver = {
212         .driver = {
213                 .name   = "aat2870-regulator",
214                 .owner  = THIS_MODULE,
215         },
216         .probe  = aat2870_regulator_probe,
217         .remove = __devexit_p(aat2870_regulator_remove),
218 };
219
220 static int __init aat2870_regulator_init(void)
221 {
222         return platform_driver_register(&aat2870_regulator_driver);
223 }
224 subsys_initcall(aat2870_regulator_init);
225
226 static void __exit aat2870_regulator_exit(void)
227 {
228         platform_driver_unregister(&aat2870_regulator_driver);
229 }
230 module_exit(aat2870_regulator_exit);
231
232 MODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator");
233 MODULE_LICENSE("GPL");
234 MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");