1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * JZ4780 EFUSE Memory Support driver |
4 | * |
5 | * Copyright (c) 2017 PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com> |
6 | * Copyright (c) 2020 H. Nikolaus Schaller <hns@goldelico.com> |
7 | */ |
8 | |
9 | /* |
10 | * Currently supports JZ4780 efuse which has 8K programmable bit. |
11 | * Efuse is separated into seven segments as below: |
12 | * |
13 | * ----------------------------------------------------------------------- |
14 | * | 64 bit | 128 bit | 128 bit | 3520 bit | 8 bit | 2296 bit | 2048 bit | |
15 | * ----------------------------------------------------------------------- |
16 | * |
17 | * The rom itself is accessed using a 9 bit address line and an 8 word wide bus |
18 | * which reads/writes based on strobes. The strobe is configured in the config |
19 | * register and is based on number of cycles of the bus clock. |
20 | * |
21 | * Driver supports read only as the writes are done in the Factory. |
22 | */ |
23 | |
24 | #include <linux/bitops.h> |
25 | #include <linux/clk.h> |
26 | #include <linux/module.h> |
27 | #include <linux/nvmem-provider.h> |
28 | #include <linux/of.h> |
29 | #include <linux/platform_device.h> |
30 | #include <linux/regmap.h> |
31 | #include <linux/timer.h> |
32 | |
33 | #define JZ_EFUCTRL (0x0) /* Control Register */ |
34 | #define JZ_EFUCFG (0x4) /* Configure Register*/ |
35 | #define JZ_EFUSTATE (0x8) /* Status Register */ |
36 | #define JZ_EFUDATA(n) (0xC + (n) * 4) |
37 | |
38 | /* We read 32 byte chunks to avoid complexity in the driver. */ |
39 | #define JZ_EFU_READ_SIZE 32 |
40 | |
41 | #define EFUCTRL_ADDR_MASK 0x3FF |
42 | #define EFUCTRL_ADDR_SHIFT 21 |
43 | #define EFUCTRL_LEN_MASK 0x1F |
44 | #define EFUCTRL_LEN_SHIFT 16 |
45 | #define EFUCTRL_PG_EN BIT(15) |
46 | #define EFUCTRL_WR_EN BIT(1) |
47 | #define EFUCTRL_RD_EN BIT(0) |
48 | |
49 | #define EFUCFG_INT_EN BIT(31) |
50 | #define EFUCFG_RD_ADJ_MASK 0xF |
51 | #define EFUCFG_RD_ADJ_SHIFT 20 |
52 | #define EFUCFG_RD_STR_MASK 0xF |
53 | #define EFUCFG_RD_STR_SHIFT 16 |
54 | #define EFUCFG_WR_ADJ_MASK 0xF |
55 | #define EFUCFG_WR_ADJ_SHIFT 12 |
56 | #define EFUCFG_WR_STR_MASK 0xFFF |
57 | #define EFUCFG_WR_STR_SHIFT 0 |
58 | |
59 | #define EFUSTATE_WR_DONE BIT(1) |
60 | #define EFUSTATE_RD_DONE BIT(0) |
61 | |
62 | struct jz4780_efuse { |
63 | struct device *dev; |
64 | struct regmap *map; |
65 | struct clk *clk; |
66 | }; |
67 | |
68 | /* main entry point */ |
69 | static int jz4780_efuse_read(void *context, unsigned int offset, |
70 | void *val, size_t bytes) |
71 | { |
72 | struct jz4780_efuse *efuse = context; |
73 | |
74 | while (bytes > 0) { |
75 | size_t start = offset & ~(JZ_EFU_READ_SIZE - 1); |
76 | size_t chunk = min(bytes, (start + JZ_EFU_READ_SIZE) |
77 | - offset); |
78 | char buf[JZ_EFU_READ_SIZE]; |
79 | unsigned int tmp; |
80 | u32 ctrl; |
81 | int ret; |
82 | |
83 | ctrl = (start << EFUCTRL_ADDR_SHIFT) |
84 | | ((JZ_EFU_READ_SIZE - 1) << EFUCTRL_LEN_SHIFT) |
85 | | EFUCTRL_RD_EN; |
86 | |
87 | regmap_update_bits(map: efuse->map, JZ_EFUCTRL, |
88 | mask: (EFUCTRL_ADDR_MASK << EFUCTRL_ADDR_SHIFT) | |
89 | (EFUCTRL_LEN_MASK << EFUCTRL_LEN_SHIFT) | |
90 | EFUCTRL_PG_EN | EFUCTRL_WR_EN | |
91 | EFUCTRL_RD_EN, |
92 | val: ctrl); |
93 | |
94 | ret = regmap_read_poll_timeout(efuse->map, JZ_EFUSTATE, |
95 | tmp, tmp & EFUSTATE_RD_DONE, |
96 | 1 * MSEC_PER_SEC, |
97 | 50 * MSEC_PER_SEC); |
98 | if (ret < 0) { |
99 | dev_err(efuse->dev, "Time out while reading efuse data" ); |
100 | return ret; |
101 | } |
102 | |
103 | ret = regmap_bulk_read(map: efuse->map, JZ_EFUDATA(0), |
104 | val: buf, JZ_EFU_READ_SIZE / sizeof(u32)); |
105 | if (ret < 0) |
106 | return ret; |
107 | |
108 | memcpy(val, &buf[offset - start], chunk); |
109 | |
110 | val += chunk; |
111 | offset += chunk; |
112 | bytes -= chunk; |
113 | } |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | static struct nvmem_config jz4780_efuse_nvmem_config = { |
119 | .name = "jz4780-efuse" , |
120 | .size = 1024, |
121 | .word_size = 1, |
122 | .stride = 1, |
123 | .owner = THIS_MODULE, |
124 | .reg_read = jz4780_efuse_read, |
125 | }; |
126 | |
127 | static const struct regmap_config jz4780_efuse_regmap_config = { |
128 | .reg_bits = 32, |
129 | .val_bits = 32, |
130 | .reg_stride = 4, |
131 | .max_register = JZ_EFUDATA(7), |
132 | }; |
133 | |
134 | static void clk_disable_unprepare_helper(void *clock) |
135 | { |
136 | clk_disable_unprepare(clk: clock); |
137 | } |
138 | |
139 | static int jz4780_efuse_probe(struct platform_device *pdev) |
140 | { |
141 | struct nvmem_device *nvmem; |
142 | struct jz4780_efuse *efuse; |
143 | struct nvmem_config cfg; |
144 | unsigned long clk_rate; |
145 | unsigned long rd_adj; |
146 | unsigned long rd_strobe; |
147 | struct device *dev = &pdev->dev; |
148 | void __iomem *regs; |
149 | int ret; |
150 | |
151 | efuse = devm_kzalloc(dev, size: sizeof(*efuse), GFP_KERNEL); |
152 | if (!efuse) |
153 | return -ENOMEM; |
154 | |
155 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
156 | if (IS_ERR(ptr: regs)) |
157 | return PTR_ERR(ptr: regs); |
158 | |
159 | efuse->map = devm_regmap_init_mmio(dev, regs, |
160 | &jz4780_efuse_regmap_config); |
161 | if (IS_ERR(ptr: efuse->map)) |
162 | return PTR_ERR(ptr: efuse->map); |
163 | |
164 | efuse->clk = devm_clk_get(dev: &pdev->dev, NULL); |
165 | if (IS_ERR(ptr: efuse->clk)) |
166 | return PTR_ERR(ptr: efuse->clk); |
167 | |
168 | ret = clk_prepare_enable(clk: efuse->clk); |
169 | if (ret < 0) |
170 | return ret; |
171 | |
172 | ret = devm_add_action_or_reset(&pdev->dev, |
173 | clk_disable_unprepare_helper, |
174 | efuse->clk); |
175 | if (ret < 0) |
176 | return ret; |
177 | |
178 | clk_rate = clk_get_rate(clk: efuse->clk); |
179 | |
180 | efuse->dev = dev; |
181 | |
182 | /* |
183 | * rd_adj and rd_strobe are 4 bit values |
184 | * conditions: |
185 | * bus clk_period * (rd_adj + 1) > 6.5ns |
186 | * bus clk_period * (rd_adj + 5 + rd_strobe) > 35ns |
187 | * i.e. rd_adj >= 6.5ns / clk_period |
188 | * i.e. rd_strobe >= 35 ns / clk_period - 5 - rd_adj + 1 |
189 | * constants: |
190 | * 1 / 6.5ns == 153846154 Hz |
191 | * 1 / 35ns == 28571429 Hz |
192 | */ |
193 | |
194 | rd_adj = clk_rate / 153846154; |
195 | rd_strobe = clk_rate / 28571429 - 5 - rd_adj + 1; |
196 | |
197 | if (rd_adj > EFUCFG_RD_ADJ_MASK || |
198 | rd_strobe > EFUCFG_RD_STR_MASK) { |
199 | dev_err(&pdev->dev, "Cannot set clock configuration\n" ); |
200 | return -EINVAL; |
201 | } |
202 | |
203 | regmap_update_bits(map: efuse->map, JZ_EFUCFG, |
204 | mask: (EFUCFG_RD_ADJ_MASK << EFUCFG_RD_ADJ_SHIFT) | |
205 | (EFUCFG_RD_STR_MASK << EFUCFG_RD_STR_SHIFT), |
206 | val: (rd_adj << EFUCFG_RD_ADJ_SHIFT) | |
207 | (rd_strobe << EFUCFG_RD_STR_SHIFT)); |
208 | |
209 | cfg = jz4780_efuse_nvmem_config; |
210 | cfg.dev = &pdev->dev; |
211 | cfg.priv = efuse; |
212 | |
213 | nvmem = devm_nvmem_register(dev, cfg: &cfg); |
214 | |
215 | return PTR_ERR_OR_ZERO(ptr: nvmem); |
216 | } |
217 | |
218 | static const struct of_device_id jz4780_efuse_match[] = { |
219 | { .compatible = "ingenic,jz4780-efuse" }, |
220 | { /* sentinel */ }, |
221 | }; |
222 | MODULE_DEVICE_TABLE(of, jz4780_efuse_match); |
223 | |
224 | static struct platform_driver jz4780_efuse_driver = { |
225 | .probe = jz4780_efuse_probe, |
226 | .driver = { |
227 | .name = "jz4780-efuse" , |
228 | .of_match_table = jz4780_efuse_match, |
229 | }, |
230 | }; |
231 | module_platform_driver(jz4780_efuse_driver); |
232 | |
233 | MODULE_AUTHOR("PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>" ); |
234 | MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>" ); |
235 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" ); |
236 | MODULE_DESCRIPTION("Ingenic JZ4780 efuse driver" ); |
237 | MODULE_LICENSE("GPL v2" ); |
238 | |