aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2013-09-22 15:49:18 -0400
committerLee Jones <lee.jones@linaro.org>2013-10-23 11:21:12 -0400
commit60013b94d9530346db963474f7fde8aecabaff25 (patch)
treeb3e78336a3e946b923ce0bf6a2d5e381dac4b3e6 /drivers/mfd
parentca13ce3701900c5b64c2c477a9cfea396c6e79c3 (diff)
mfd: Add STw481x driver
This adds a driver for the STw481x PMICs found in the Nomadik family of platforms. This one uses pure device tree probing. Print some of the OTP registers on boot and register a regulator MFD child. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/stw481x.c250
3 files changed, 261 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 914c3d142f78..d078fe4a82ef 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1151,6 +1151,16 @@ config MFD_WM8994
1151 core support for the WM8994, in order to use the actual 1151 core support for the WM8994, in order to use the actual
1152 functionaltiy of the device other drivers must be enabled. 1152 functionaltiy of the device other drivers must be enabled.
1153 1153
1154config MFD_STW481X
1155 bool "Support for ST Microelectronics STw481x"
1156 depends on I2C && ARCH_NOMADIK
1157 select REGMAP_I2C
1158 select MFD_CORE
1159 help
1160 Select this option to enable the STw481x chip driver used
1161 in various ST Microelectronics and ST-Ericsson embedded
1162 Nomadik series.
1163
1154endmenu 1164endmenu
1155endif 1165endif
1156 1166
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 15b905c6553c..656cec90a1e5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -162,3 +162,4 @@ obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
162obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o 162obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
163obj-$(CONFIG_MFD_RETU) += retu-mfd.o 163obj-$(CONFIG_MFD_RETU) += retu-mfd.o
164obj-$(CONFIG_MFD_AS3711) += as3711.o 164obj-$(CONFIG_MFD_AS3711) += as3711.o
165obj-$(CONFIG_MFD_STW481X) += stw481x.o
diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c
new file mode 100644
index 000000000000..1243d5c6a448
--- /dev/null
+++ b/drivers/mfd/stw481x.c
@@ -0,0 +1,250 @@
1/*
2 * Core driver for STw4810/STw4811
3 *
4 * Copyright (C) 2013 ST-Ericsson SA
5 * Written on behalf of Linaro for ST-Ericsson
6 *
7 * Author: Linus Walleij <linus.walleij@linaro.org>
8 *
9 * License terms: GNU General Public License (GPL) version 2
10 */
11
12#include <linux/err.h>
13#include <linux/i2c.h>
14#include <linux/init.h>
15#include <linux/mfd/core.h>
16#include <linux/mfd/stw481x.h>
17#include <linux/module.h>
18#include <linux/regmap.h>
19#include <linux/spinlock.h>
20#include <linux/slab.h>
21
22/*
23 * This driver can only access the non-USB portions of STw4811, the register
24 * range 0x00-0x10 dealing with USB is bound to the two special I2C pins used
25 * for USB control.
26 */
27
28/* Registers inside the power control address space */
29#define STW_PC_VCORE_SEL 0x05U
30#define STW_PC_VAUX_SEL 0x06U
31#define STW_PC_VPLL_SEL 0x07U
32
33/**
34 * stw481x_get_pctl_reg() - get a power control register
35 * @stw481x: handle to the stw481x chip
36 * @reg: power control register to fetch
37 *
38 * The power control registers is a set of one-time-programmable registers
39 * in its own register space, accessed by writing addess bits to these
40 * two registers: bits 7,6,5 of PCTL_REG_LO corresponds to the 3 LSBs of
41 * the address and bits 8,9 of PCTL_REG_HI corresponds to the 2 MSBs of
42 * the address, forming an address space of 5 bits, i.e. 32 registers
43 * 0x00 ... 0x1f can be obtained.
44 */
45static int stw481x_get_pctl_reg(struct stw481x *stw481x, u8 reg)
46{
47 u8 msb = (reg >> 3) & 0x03;
48 u8 lsb = (reg << 5) & 0xe0;
49 unsigned int val;
50 u8 vrfy;
51 int ret;
52
53 ret = regmap_write(stw481x->map, STW_PCTL_REG_HI, msb);
54 if (ret)
55 return ret;
56 ret = regmap_write(stw481x->map, STW_PCTL_REG_LO, lsb);
57 if (ret)
58 return ret;
59 ret = regmap_read(stw481x->map, STW_PCTL_REG_HI, &val);
60 if (ret)
61 return ret;
62 vrfy = (val & 0x03) << 3;
63 ret = regmap_read(stw481x->map, STW_PCTL_REG_LO, &val);
64 if (ret)
65 return ret;
66 vrfy |= ((val >> 5) & 0x07);
67 if (vrfy != reg)
68 return -EIO;
69 return (val >> 1) & 0x0f;
70}
71
72static int stw481x_startup(struct stw481x *stw481x)
73{
74 /* Voltages multiplied by 100 */
75 u8 vcore_val[] = { 100, 105, 110, 115, 120, 122, 124, 126, 128,
76 130, 132, 134, 136, 138, 140, 145 };
77 u8 vpll_val[] = { 105, 120, 130, 180 };
78 u8 vaux_val[] = { 15, 18, 25, 28 };
79 u8 vcore;
80 u8 vcore_slp;
81 u8 vpll;
82 u8 vaux;
83 bool vaux_en;
84 bool it_warn;
85 int ret;
86 unsigned int val;
87
88 ret = regmap_read(stw481x->map, STW_CONF1, &val);
89 if (ret)
90 return ret;
91 vaux_en = !!(val & STW_CONF1_PDN_VAUX);
92 it_warn = !!(val & STW_CONF1_IT_WARN);
93
94 dev_info(&stw481x->client->dev, "voltages %s\n",
95 (val & STW_CONF1_V_MONITORING) ? "OK" : "LOW");
96 dev_info(&stw481x->client->dev, "MMC level shifter %s\n",
97 (val & STW_CONF1_MMC_LS_STATUS) ? "high impedance" : "ON");
98 dev_info(&stw481x->client->dev, "VMMC: %s\n",
99 (val & STW_CONF1_PDN_VMMC) ? "ON" : "disabled");
100
101 dev_info(&stw481x->client->dev, "STw481x power control registers:\n");
102
103 ret = stw481x_get_pctl_reg(stw481x, STW_PC_VCORE_SEL);
104 if (ret < 0)
105 return ret;
106 vcore = ret & 0x0f;
107
108 ret = stw481x_get_pctl_reg(stw481x, STW_PC_VAUX_SEL);
109 if (ret < 0)
110 return ret;
111 vaux = (ret >> 2) & 3;
112 vpll = (ret >> 4) & 1; /* Save bit 4 */
113
114 ret = stw481x_get_pctl_reg(stw481x, STW_PC_VPLL_SEL);
115 if (ret < 0)
116 return ret;
117 vpll |= (ret >> 1) & 2;
118
119 dev_info(&stw481x->client->dev, "VCORE: %u.%uV %s\n",
120 vcore_val[vcore] / 100, vcore_val[vcore] % 100,
121 (ret & 4) ? "ON" : "OFF");
122
123 dev_info(&stw481x->client->dev, "VPLL: %u.%uV %s\n",
124 vpll_val[vpll] / 100, vpll_val[vpll] % 100,
125 (ret & 0x10) ? "ON" : "OFF");
126
127 dev_info(&stw481x->client->dev, "VAUX: %u.%uV %s\n",
128 vaux_val[vaux] / 10, vaux_val[vaux] % 10,
129 vaux_en ? "ON" : "OFF");
130
131 ret = regmap_read(stw481x->map, STW_CONF2, &val);
132 if (ret)
133 return ret;
134
135 dev_info(&stw481x->client->dev, "TWARN: %s threshold, %s\n",
136 it_warn ? "below" : "above",
137 (val & STW_CONF2_MASK_TWARN) ?
138 "enabled" : "mask through VDDOK");
139 dev_info(&stw481x->client->dev, "VMMC: %s\n",
140 (val & STW_CONF2_VMMC_EXT) ? "internal" : "external");
141 dev_info(&stw481x->client->dev, "IT WAKE UP: %s\n",
142 (val & STW_CONF2_MASK_IT_WAKE_UP) ? "enabled" : "masked");
143 dev_info(&stw481x->client->dev, "GPO1: %s\n",
144 (val & STW_CONF2_GPO1) ? "low" : "high impedance");
145 dev_info(&stw481x->client->dev, "GPO2: %s\n",
146 (val & STW_CONF2_GPO2) ? "low" : "high impedance");
147
148 ret = regmap_read(stw481x->map, STW_VCORE_SLEEP, &val);
149 if (ret)
150 return ret;
151 vcore_slp = val & 0x0f;
152 dev_info(&stw481x->client->dev, "VCORE SLEEP: %u.%uV\n",
153 vcore_val[vcore_slp] / 100, vcore_val[vcore_slp] % 100);
154
155 return 0;
156}
157
158/*
159 * MFD cells - we have one cell which is selected operation
160 * mode, and we always have a GPIO cell.
161 */
162static struct mfd_cell stw481x_cells[] = {
163 {
164 .of_compatible = "st,stw481x-vmmc",
165 .name = "stw481x-vmmc-regulator",
166 .id = -1,
167 },
168};
169
170const struct regmap_config stw481x_regmap_config = {
171 .reg_bits = 8,
172 .val_bits = 8,
173};
174
175static int stw481x_probe(struct i2c_client *client,
176 const struct i2c_device_id *id)
177{
178 struct stw481x *stw481x;
179 int ret;
180 int i;
181
182 stw481x = devm_kzalloc(&client->dev, sizeof(*stw481x), GFP_KERNEL);
183 if (!stw481x)
184 return -ENOMEM;
185
186 i2c_set_clientdata(client, stw481x);
187 stw481x->client = client;
188 stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config);
189
190 ret = stw481x_startup(stw481x);
191 if (ret) {
192 dev_err(&client->dev, "chip initialization failed\n");
193 return ret;
194 }
195
196 /* Set up and register the platform devices. */
197 for (i = 0; i < ARRAY_SIZE(stw481x_cells); i++) {
198 /* One state holder for all drivers, this is simple */
199 stw481x_cells[i].platform_data = stw481x;
200 stw481x_cells[i].pdata_size = sizeof(*stw481x);
201 }
202
203 ret = mfd_add_devices(&client->dev, 0, stw481x_cells,
204 ARRAY_SIZE(stw481x_cells), NULL, 0, NULL);
205 if (ret)
206 return ret;
207
208 dev_info(&client->dev, "initialized STw481x device\n");
209
210 return ret;
211}
212
213static int stw481x_remove(struct i2c_client *client)
214{
215 mfd_remove_devices(&client->dev);
216 return 0;
217}
218
219/*
220 * This ID table is completely unused, as this is a pure
221 * device-tree probed driver, but it has to be here due to
222 * the structure of the I2C core.
223 */
224static const struct i2c_device_id stw481x_id[] = {
225 { "stw481x", 0 },
226 { },
227};
228
229static const struct of_device_id stw481x_match[] = {
230 { .compatible = "st,stw4810", },
231 { .compatible = "st,stw4811", },
232 { },
233};
234MODULE_DEVICE_TABLE(of, stw481x_match);
235
236static struct i2c_driver stw481x_driver = {
237 .driver = {
238 .name = "stw481x",
239 .of_match_table = stw481x_match,
240 },
241 .probe = stw481x_probe,
242 .remove = stw481x_remove,
243 .id_table = stw481x_id,
244};
245
246module_i2c_driver(stw481x_driver);
247
248MODULE_AUTHOR("Linus Walleij");
249MODULE_DESCRIPTION("STw481x PMIC driver");
250MODULE_LICENSE("GPL v2");