aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/stw481x.c250
-rw-r--r--include/linux/mfd/stw481x.h56
4 files changed, 317 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");
diff --git a/include/linux/mfd/stw481x.h b/include/linux/mfd/stw481x.h
new file mode 100644
index 000000000000..eda121556e5d
--- /dev/null
+++ b/include/linux/mfd/stw481x.h
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2011 ST-Ericsson SA
3 * Written on behalf of Linaro for ST-Ericsson
4 *
5 * Author: Linus Walleij <linus.walleij@linaro.org>
6 *
7 * License terms: GNU General Public License (GPL) version 2
8 */
9#ifndef MFD_STW481X_H
10#define MFD_STW481X_H
11
12#include <linux/i2c.h>
13#include <linux/regulator/machine.h>
14#include <linux/regmap.h>
15#include <linux/bitops.h>
16
17/* These registers are accessed from more than one driver */
18#define STW_CONF1 0x11U
19#define STW_CONF1_PDN_VMMC 0x01U
20#define STW_CONF1_VMMC_MASK 0x0eU
21#define STW_CONF1_VMMC_1_8V 0x02U
22#define STW_CONF1_VMMC_2_85V 0x04U
23#define STW_CONF1_VMMC_3V 0x06U
24#define STW_CONF1_VMMC_1_85V 0x08U
25#define STW_CONF1_VMMC_2_6V 0x0aU
26#define STW_CONF1_VMMC_2_7V 0x0cU
27#define STW_CONF1_VMMC_3_3V 0x0eU
28#define STW_CONF1_MMC_LS_STATUS 0x10U
29#define STW_PCTL_REG_LO 0x1eU
30#define STW_PCTL_REG_HI 0x1fU
31#define STW_CONF1_V_MONITORING 0x20U
32#define STW_CONF1_IT_WARN 0x40U
33#define STW_CONF1_PDN_VAUX 0x80U
34#define STW_CONF2 0x20U
35#define STW_CONF2_MASK_TWARN 0x01U
36#define STW_CONF2_VMMC_EXT 0x02U
37#define STW_CONF2_MASK_IT_WAKE_UP 0x04U
38#define STW_CONF2_GPO1 0x08U
39#define STW_CONF2_GPO2 0x10U
40#define STW_VCORE_SLEEP 0x21U
41
42/**
43 * struct stw481x - state holder for the Stw481x drivers
44 * @mutex: mutex to serialize I2C accesses
45 * @i2c_client: corresponding I2C client
46 * @regulator: regulator device for regulator children
47 * @map: regmap handle to access device registers
48 */
49struct stw481x {
50 struct mutex lock;
51 struct i2c_client *client;
52 struct regulator_dev *vmmc_regulator;
53 struct regmap *map;
54};
55
56#endif