Commit | Line | Data |
---|---|---|
452534e5 VB |
1 | /* |
2 | * Regulator driver for tps65090 power management chip. | |
3 | * | |
4 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
5 | ||
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | ||
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/> | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
452534e5 VB |
20 | #include <linux/init.h> |
21 | #include <linux/slab.h> | |
22 | #include <linux/err.h> | |
23 | #include <linux/platform_device.h> | |
24 | #include <linux/regulator/driver.h> | |
25 | #include <linux/regulator/machine.h> | |
26 | #include <linux/mfd/tps65090.h> | |
452534e5 VB |
27 | |
28 | struct tps65090_regulator { | |
452534e5 | 29 | struct device *dev; |
24282a1c LD |
30 | struct regulator_desc *desc; |
31 | struct regulator_dev *rdev; | |
452534e5 VB |
32 | }; |
33 | ||
452534e5 | 34 | static struct regulator_ops tps65090_ops = { |
06c4998b AL |
35 | .enable = regulator_enable_regmap, |
36 | .disable = regulator_disable_regmap, | |
37 | .is_enabled = regulator_is_enabled_regmap, | |
452534e5 VB |
38 | }; |
39 | ||
24282a1c | 40 | #define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \ |
452534e5 | 41 | { \ |
24282a1c LD |
42 | .name = "TPS65090_RAILS"#_id, \ |
43 | .supply_name = _sname, \ | |
44 | .id = TPS65090_ID_##_id, \ | |
45 | .ops = &_ops, \ | |
46 | .enable_reg = _en_reg, \ | |
47 | .enable_mask = BIT(0), \ | |
48 | .type = REGULATOR_VOLTAGE, \ | |
49 | .owner = THIS_MODULE, \ | |
452534e5 VB |
50 | } |
51 | ||
24282a1c LD |
52 | static struct regulator_desc tps65090_regulator_desc[] = { |
53 | tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_ops), | |
54 | tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_ops), | |
55 | tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_ops), | |
56 | tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_ops), | |
57 | tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_ops), | |
58 | tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_ops), | |
59 | tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_ops), | |
60 | tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_ops), | |
61 | tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_ops), | |
62 | tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_ops), | |
452534e5 VB |
63 | }; |
64 | ||
24282a1c | 65 | static inline bool is_dcdc(int id) |
452534e5 | 66 | { |
24282a1c LD |
67 | switch (id) { |
68 | case TPS65090_ID_DCDC1: | |
69 | case TPS65090_ID_DCDC2: | |
70 | case TPS65090_ID_DCDC3: | |
71 | return true; | |
72 | default: | |
73 | return false; | |
74 | } | |
75 | } | |
452534e5 | 76 | |
24282a1c LD |
77 | static int __devinit tps65090_config_ext_control( |
78 | struct tps65090_regulator *ri, bool enable) | |
79 | { | |
80 | int ret; | |
81 | struct device *parent = ri->dev->parent; | |
82 | unsigned int reg_en_reg = ri->desc->enable_reg; | |
83 | ||
84 | if (enable) | |
85 | ret = tps65090_set_bits(parent, reg_en_reg, 1); | |
86 | else | |
87 | ret = tps65090_clr_bits(parent, reg_en_reg, 1); | |
88 | if (ret < 0) | |
89 | dev_err(ri->dev, "Error in updating reg 0x%x\n", reg_en_reg); | |
90 | return ret; | |
91 | } | |
92 | ||
93 | static int __devinit tps65090_regulator_disable_ext_control( | |
94 | struct tps65090_regulator *ri, | |
95 | struct tps65090_regulator_plat_data *tps_pdata) | |
96 | { | |
97 | int ret = 0; | |
98 | struct device *parent = ri->dev->parent; | |
99 | unsigned int reg_en_reg = ri->desc->enable_reg; | |
100 | ||
101 | /* | |
102 | * First enable output for internal control if require. | |
103 | * And then disable external control. | |
104 | */ | |
105 | if (tps_pdata->reg_init_data->constraints.always_on || | |
106 | tps_pdata->reg_init_data->constraints.boot_on) { | |
107 | ret = tps65090_set_bits(parent, reg_en_reg, 0); | |
108 | if (ret < 0) { | |
109 | dev_err(ri->dev, "Error in set reg 0x%x\n", reg_en_reg); | |
110 | return ret; | |
111 | } | |
452534e5 | 112 | } |
24282a1c | 113 | return tps65090_config_ext_control(ri, false); |
452534e5 VB |
114 | } |
115 | ||
116 | static int __devinit tps65090_regulator_probe(struct platform_device *pdev) | |
117 | { | |
06c4998b | 118 | struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); |
452534e5 | 119 | struct tps65090_regulator *ri = NULL; |
c172708d | 120 | struct regulator_config config = { }; |
452534e5 | 121 | struct regulator_dev *rdev; |
24282a1c LD |
122 | struct tps65090_regulator_plat_data *tps_pdata; |
123 | struct tps65090_regulator *pmic; | |
124 | struct tps65090_platform_data *tps65090_pdata; | |
125 | int num; | |
126 | int ret; | |
452534e5 | 127 | |
24282a1c | 128 | dev_dbg(&pdev->dev, "Probing regulator\n"); |
452534e5 | 129 | |
24282a1c LD |
130 | tps65090_pdata = dev_get_platdata(pdev->dev.parent); |
131 | if (!tps65090_pdata) { | |
132 | dev_err(&pdev->dev, "Platform data missing\n"); | |
452534e5 VB |
133 | return -EINVAL; |
134 | } | |
24282a1c LD |
135 | |
136 | pmic = devm_kzalloc(&pdev->dev, TPS65090_ID_MAX * sizeof(*pmic), | |
137 | GFP_KERNEL); | |
138 | if (!pmic) { | |
139 | dev_err(&pdev->dev, "mem alloc for pmic failed\n"); | |
140 | return -ENOMEM; | |
141 | } | |
142 | ||
143 | for (num = 0; num < TPS65090_ID_MAX; num++) { | |
144 | tps_pdata = tps65090_pdata->reg_pdata[num]; | |
145 | ||
146 | ri = &pmic[num]; | |
147 | ri->dev = &pdev->dev; | |
148 | ri->desc = &tps65090_regulator_desc[num]; | |
149 | ||
150 | /* | |
151 | * TPS5090 DCDC support the control from external digital input. | |
152 | * It may be possible that during boot, the external control is | |
153 | * enabled. Disabling external control for DCDC. | |
154 | */ | |
155 | if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) { | |
156 | ret = tps65090_regulator_disable_ext_control( | |
157 | ri, tps_pdata); | |
158 | if (ret < 0) { | |
159 | dev_err(&pdev->dev, | |
160 | "failed disable ext control\n"); | |
161 | goto scrub; | |
162 | } | |
163 | } | |
164 | config.dev = &pdev->dev; | |
165 | config.driver_data = ri; | |
166 | config.regmap = tps65090_mfd->rmap; | |
167 | if (tps_pdata) | |
168 | config.init_data = tps_pdata->reg_init_data; | |
169 | else | |
170 | config.init_data = NULL; | |
171 | ||
172 | rdev = regulator_register(ri->desc, &config); | |
173 | if (IS_ERR(rdev)) { | |
174 | dev_err(&pdev->dev, "failed to register regulator %s\n", | |
175 | ri->desc->name); | |
176 | ret = PTR_ERR(rdev); | |
177 | goto scrub; | |
178 | } | |
179 | ri->rdev = rdev; | |
452534e5 VB |
180 | } |
181 | ||
24282a1c | 182 | platform_set_drvdata(pdev, pmic); |
452534e5 | 183 | return 0; |
24282a1c LD |
184 | |
185 | scrub: | |
186 | while (--num >= 0) { | |
187 | ri = &pmic[num]; | |
188 | regulator_unregister(ri->rdev); | |
189 | } | |
190 | return ret; | |
452534e5 VB |
191 | } |
192 | ||
193 | static int __devexit tps65090_regulator_remove(struct platform_device *pdev) | |
194 | { | |
24282a1c LD |
195 | struct tps65090_regulator *pmic = platform_get_drvdata(pdev); |
196 | struct tps65090_regulator *ri; | |
197 | int num; | |
452534e5 | 198 | |
24282a1c LD |
199 | for (num = 0; num < TPS65090_ID_MAX; ++num) { |
200 | ri = &pmic[num]; | |
201 | regulator_unregister(ri->rdev); | |
202 | } | |
452534e5 VB |
203 | return 0; |
204 | } | |
205 | ||
206 | static struct platform_driver tps65090_regulator_driver = { | |
207 | .driver = { | |
208 | .name = "tps65090-regulator", | |
209 | .owner = THIS_MODULE, | |
210 | }, | |
211 | .probe = tps65090_regulator_probe, | |
212 | .remove = __devexit_p(tps65090_regulator_remove), | |
213 | }; | |
214 | ||
215 | static int __init tps65090_regulator_init(void) | |
216 | { | |
217 | return platform_driver_register(&tps65090_regulator_driver); | |
218 | } | |
219 | subsys_initcall(tps65090_regulator_init); | |
220 | ||
221 | static void __exit tps65090_regulator_exit(void) | |
222 | { | |
223 | platform_driver_unregister(&tps65090_regulator_driver); | |
224 | } | |
225 | module_exit(tps65090_regulator_exit); | |
226 | ||
227 | MODULE_DESCRIPTION("tps65090 regulator driver"); | |
228 | MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); | |
229 | MODULE_LICENSE("GPL v2"); |