summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFreeman Liu <freeman.liu@spreadtrum.com>2018-07-11 06:20:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-07-15 08:03:05 -0400
commit19c54468f222d61f07ec83d13e46a4c78d326c80 (patch)
tree6c8587d80a1200b3105c90f9a53c1b52f2fe85de
parent3eb93e0825abf76ddb7960506be71e6f1a63ad17 (diff)
nvmem: Add Spreadtrum SC27XX efuse support
This patch add the efuse driver which is embeded in Spreadtrum SC27XX series PMICs. The sc27xx efuse contains 32 blocks and each block's data width is 16 bits. Signed-off-by: Freeman Liu <freeman.liu@spreadtrum.com> Signed-off-by: Baolin Wang <baolin.wang@linaro.org> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/nvmem/Kconfig11
-rw-r--r--drivers/nvmem/Makefile3
-rw-r--r--drivers/nvmem/sc27xx-efuse.c264
3 files changed, 277 insertions, 1 deletions
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 54a3c298247b..0a7a470ee859 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -181,4 +181,15 @@ config RAVE_SP_EEPROM
181 help 181 help
182 Say y here to enable Rave SP EEPROM support. 182 Say y here to enable Rave SP EEPROM support.
183 183
184config SC27XX_EFUSE
185 tristate "Spreadtrum SC27XX eFuse Support"
186 depends on MFD_SC27XX_PMIC || COMPILE_TEST
187 depends on HAS_IOMEM
188 help
189 This is a simple driver to dump specified values of Spreadtrum
190 SC27XX PMICs from eFuse.
191
192 This driver can also be built as a module. If so, the module
193 will be called nvmem-sc27xx-efuse.
194
184endif 195endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index 27e96a8efd1c..4e8c61628f1a 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -39,4 +39,5 @@ obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
39nvmem_snvs_lpgpr-y := snvs_lpgpr.o 39nvmem_snvs_lpgpr-y := snvs_lpgpr.o
40obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o 40obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
41nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o 41nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
42 42obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o
43nvmem-sc27xx-efuse-y := sc27xx-efuse.o
diff --git a/drivers/nvmem/sc27xx-efuse.c b/drivers/nvmem/sc27xx-efuse.c
new file mode 100644
index 000000000000..33185d8d82cf
--- /dev/null
+++ b/drivers/nvmem/sc27xx-efuse.c
@@ -0,0 +1,264 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2018 Spreadtrum Communications Inc.
3
4#include <linux/hwspinlock.h>
5#include <linux/module.h>
6#include <linux/of.h>
7#include <linux/platform_device.h>
8#include <linux/regmap.h>
9#include <linux/nvmem-provider.h>
10
11/* PMIC global registers definition */
12#define SC27XX_MODULE_EN 0xc08
13#define SC27XX_EFUSE_EN BIT(6)
14
15/* Efuse controller registers definition */
16#define SC27XX_EFUSE_GLB_CTRL 0x0
17#define SC27XX_EFUSE_DATA_RD 0x4
18#define SC27XX_EFUSE_DATA_WR 0x8
19#define SC27XX_EFUSE_BLOCK_INDEX 0xc
20#define SC27XX_EFUSE_MODE_CTRL 0x10
21#define SC27XX_EFUSE_STATUS 0x14
22#define SC27XX_EFUSE_WR_TIMING_CTRL 0x20
23#define SC27XX_EFUSE_RD_TIMING_CTRL 0x24
24#define SC27XX_EFUSE_EFUSE_DEB_CTRL 0x28
25
26/* Mask definition for SC27XX_EFUSE_BLOCK_INDEX register */
27#define SC27XX_EFUSE_BLOCK_MASK GENMASK(4, 0)
28
29/* Bits definitions for SC27XX_EFUSE_MODE_CTRL register */
30#define SC27XX_EFUSE_PG_START BIT(0)
31#define SC27XX_EFUSE_RD_START BIT(1)
32#define SC27XX_EFUSE_CLR_RDDONE BIT(2)
33
34/* Bits definitions for SC27XX_EFUSE_STATUS register */
35#define SC27XX_EFUSE_PGM_BUSY BIT(0)
36#define SC27XX_EFUSE_READ_BUSY BIT(1)
37#define SC27XX_EFUSE_STANDBY BIT(2)
38#define SC27XX_EFUSE_GLOBAL_PROT BIT(3)
39#define SC27XX_EFUSE_RD_DONE BIT(4)
40
41/* Block number and block width (bytes) definitions */
42#define SC27XX_EFUSE_BLOCK_MAX 32
43#define SC27XX_EFUSE_BLOCK_WIDTH 2
44
45/* Timeout (ms) for the trylock of hardware spinlocks */
46#define SC27XX_EFUSE_HWLOCK_TIMEOUT 5000
47
48/* Timeout (us) of polling the status */
49#define SC27XX_EFUSE_POLL_TIMEOUT 3000000
50#define SC27XX_EFUSE_POLL_DELAY_US 10000
51
52struct sc27xx_efuse {
53 struct device *dev;
54 struct regmap *regmap;
55 struct hwspinlock *hwlock;
56 struct mutex mutex;
57 u32 base;
58};
59
60/*
61 * On Spreadtrum platform, we have multi-subsystems will access the unique
62 * efuse controller, so we need one hardware spinlock to synchronize between
63 * the multiple subsystems.
64 */
65static int sc27xx_efuse_lock(struct sc27xx_efuse *efuse)
66{
67 int ret;
68
69 mutex_lock(&efuse->mutex);
70
71 ret = hwspin_lock_timeout_raw(efuse->hwlock,
72 SC27XX_EFUSE_HWLOCK_TIMEOUT);
73 if (ret) {
74 dev_err(efuse->dev, "timeout to get the hwspinlock\n");
75 mutex_unlock(&efuse->mutex);
76 return ret;
77 }
78
79 return 0;
80}
81
82static void sc27xx_efuse_unlock(struct sc27xx_efuse *efuse)
83{
84 hwspin_unlock_raw(efuse->hwlock);
85 mutex_unlock(&efuse->mutex);
86}
87
88static int sc27xx_efuse_poll_status(struct sc27xx_efuse *efuse, u32 bits)
89{
90 int ret;
91 u32 val;
92
93 ret = regmap_read_poll_timeout(efuse->regmap,
94 efuse->base + SC27XX_EFUSE_STATUS,
95 val, (val & bits),
96 SC27XX_EFUSE_POLL_DELAY_US,
97 SC27XX_EFUSE_POLL_TIMEOUT);
98 if (ret) {
99 dev_err(efuse->dev, "timeout to update the efuse status\n");
100 return ret;
101 }
102
103 return 0;
104}
105
106static int sc27xx_efuse_read(void *context, u32 offset, void *val, size_t bytes)
107{
108 struct sc27xx_efuse *efuse = context;
109 u32 buf;
110 int ret;
111
112 if (offset > SC27XX_EFUSE_BLOCK_MAX || bytes > SC27XX_EFUSE_BLOCK_WIDTH)
113 return -EINVAL;
114
115 ret = sc27xx_efuse_lock(efuse);
116 if (ret)
117 return ret;
118
119 /* Enable the efuse controller. */
120 ret = regmap_update_bits(efuse->regmap, SC27XX_MODULE_EN,
121 SC27XX_EFUSE_EN, SC27XX_EFUSE_EN);
122 if (ret)
123 goto unlock_efuse;
124
125 /*
126 * Before reading, we should ensure the efuse controller is in
127 * standby state.
128 */
129 ret = sc27xx_efuse_poll_status(efuse, SC27XX_EFUSE_STANDBY);
130 if (ret)
131 goto disable_efuse;
132
133 /* Set the block address to be read. */
134 ret = regmap_write(efuse->regmap,
135 efuse->base + SC27XX_EFUSE_BLOCK_INDEX,
136 offset & SC27XX_EFUSE_BLOCK_MASK);
137 if (ret)
138 goto disable_efuse;
139
140 /* Start reading process from efuse memory. */
141 ret = regmap_update_bits(efuse->regmap,
142 efuse->base + SC27XX_EFUSE_MODE_CTRL,
143 SC27XX_EFUSE_RD_START,
144 SC27XX_EFUSE_RD_START);
145 if (ret)
146 goto disable_efuse;
147
148 /*
149 * Polling the read done status to make sure the reading process
150 * is completed, that means the data can be read out now.
151 */
152 ret = sc27xx_efuse_poll_status(efuse, SC27XX_EFUSE_RD_DONE);
153 if (ret)
154 goto disable_efuse;
155
156 /* Read data from efuse memory. */
157 ret = regmap_read(efuse->regmap, efuse->base + SC27XX_EFUSE_DATA_RD,
158 &buf);
159 if (ret)
160 goto disable_efuse;
161
162 /* Clear the read done flag. */
163 ret = regmap_update_bits(efuse->regmap,
164 efuse->base + SC27XX_EFUSE_MODE_CTRL,
165 SC27XX_EFUSE_CLR_RDDONE,
166 SC27XX_EFUSE_CLR_RDDONE);
167
168disable_efuse:
169 /* Disable the efuse controller after reading. */
170 regmap_update_bits(efuse->regmap, SC27XX_MODULE_EN, SC27XX_EFUSE_EN, 0);
171unlock_efuse:
172 sc27xx_efuse_unlock(efuse);
173
174 if (!ret)
175 memcpy(val, &buf, bytes);
176
177 return ret;
178}
179
180static int sc27xx_efuse_probe(struct platform_device *pdev)
181{
182 struct device_node *np = pdev->dev.of_node;
183 struct nvmem_config econfig = { };
184 struct nvmem_device *nvmem;
185 struct sc27xx_efuse *efuse;
186 int ret;
187
188 efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
189 if (!efuse)
190 return -ENOMEM;
191
192 efuse->regmap = dev_get_regmap(pdev->dev.parent, NULL);
193 if (!efuse->regmap) {
194 dev_err(&pdev->dev, "failed to get efuse regmap\n");
195 return -ENODEV;
196 }
197
198 ret = of_property_read_u32(np, "reg", &efuse->base);
199 if (ret) {
200 dev_err(&pdev->dev, "failed to get efuse base address\n");
201 return ret;
202 }
203
204 ret = of_hwspin_lock_get_id(np, 0);
205 if (ret < 0) {
206 dev_err(&pdev->dev, "failed to get hwspinlock id\n");
207 return ret;
208 }
209
210 efuse->hwlock = hwspin_lock_request_specific(ret);
211 if (!efuse->hwlock) {
212 dev_err(&pdev->dev, "failed to request hwspinlock\n");
213 return -ENXIO;
214 }
215
216 mutex_init(&efuse->mutex);
217 efuse->dev = &pdev->dev;
218 platform_set_drvdata(pdev, efuse);
219
220 econfig.stride = 1;
221 econfig.word_size = 1;
222 econfig.read_only = true;
223 econfig.name = "sc27xx-efuse";
224 econfig.size = SC27XX_EFUSE_BLOCK_MAX * SC27XX_EFUSE_BLOCK_WIDTH;
225 econfig.reg_read = sc27xx_efuse_read;
226 econfig.priv = efuse;
227 econfig.dev = &pdev->dev;
228 nvmem = devm_nvmem_register(&pdev->dev, &econfig);
229 if (IS_ERR(nvmem)) {
230 dev_err(&pdev->dev, "failed to register nvmem config\n");
231 hwspin_lock_free(efuse->hwlock);
232 return PTR_ERR(nvmem);
233 }
234
235 return 0;
236}
237
238static int sc27xx_efuse_remove(struct platform_device *pdev)
239{
240 struct sc27xx_efuse *efuse = platform_get_drvdata(pdev);
241
242 hwspin_lock_free(efuse->hwlock);
243 return 0;
244}
245
246static const struct of_device_id sc27xx_efuse_of_match[] = {
247 { .compatible = "sprd,sc2731-efuse" },
248 { }
249};
250
251static struct platform_driver sc27xx_efuse_driver = {
252 .probe = sc27xx_efuse_probe,
253 .remove = sc27xx_efuse_remove,
254 .driver = {
255 .name = "sc27xx-efuse",
256 .of_match_table = sc27xx_efuse_of_match,
257 },
258};
259
260module_platform_driver(sc27xx_efuse_driver);
261
262MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>");
263MODULE_DESCRIPTION("Spreadtrum SC27xx efuse driver");
264MODULE_LICENSE("GPL v2");