1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Driver for Microchip 48L640 64 Kb SPI Serial EERAM |
4 | * |
5 | * Copyright Heiko Schocher <hs@denx.de> |
6 | * |
7 | * datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20006055B.pdf |
8 | * |
9 | * we set continuous mode but reading/writing more bytes than |
10 | * pagesize seems to bring chip into state where readden values |
11 | * are wrong ... no idea why. |
12 | * |
13 | */ |
14 | #include <linux/delay.h> |
15 | #include <linux/device.h> |
16 | #include <linux/jiffies.h> |
17 | #include <linux/module.h> |
18 | #include <linux/mtd/mtd.h> |
19 | #include <linux/mtd/partitions.h> |
20 | #include <linux/mutex.h> |
21 | #include <linux/sched.h> |
22 | #include <linux/sizes.h> |
23 | #include <linux/spi/flash.h> |
24 | #include <linux/spi/spi.h> |
25 | #include <linux/of.h> |
26 | |
27 | struct mchp48_caps { |
28 | unsigned int size; |
29 | unsigned int page_size; |
30 | }; |
31 | |
32 | struct mchp48l640_flash { |
33 | struct spi_device *spi; |
34 | struct mutex lock; |
35 | struct mtd_info mtd; |
36 | const struct mchp48_caps *caps; |
37 | }; |
38 | |
39 | #define MCHP48L640_CMD_WREN 0x06 |
40 | #define MCHP48L640_CMD_WRDI 0x04 |
41 | #define MCHP48L640_CMD_WRITE 0x02 |
42 | #define MCHP48L640_CMD_READ 0x03 |
43 | #define MCHP48L640_CMD_WRSR 0x01 |
44 | #define MCHP48L640_CMD_RDSR 0x05 |
45 | |
46 | #define MCHP48L640_STATUS_RDY 0x01 |
47 | #define MCHP48L640_STATUS_WEL 0x02 |
48 | #define MCHP48L640_STATUS_BP0 0x04 |
49 | #define MCHP48L640_STATUS_BP1 0x08 |
50 | #define MCHP48L640_STATUS_SWM 0x10 |
51 | #define MCHP48L640_STATUS_PRO 0x20 |
52 | #define MCHP48L640_STATUS_ASE 0x40 |
53 | |
54 | #define MCHP48L640_TIMEOUT 100 |
55 | |
56 | #define MAX_CMD_SIZE 0x10 |
57 | |
58 | #define to_mchp48l640_flash(x) container_of(x, struct mchp48l640_flash, mtd) |
59 | |
60 | static int mchp48l640_mkcmd(struct mchp48l640_flash *flash, u8 cmd, loff_t addr, char *buf) |
61 | { |
62 | buf[0] = cmd; |
63 | buf[1] = addr >> 8; |
64 | buf[2] = addr; |
65 | |
66 | return 3; |
67 | } |
68 | |
69 | static int mchp48l640_read_status(struct mchp48l640_flash *flash, int *status) |
70 | { |
71 | unsigned char cmd[2]; |
72 | int ret; |
73 | |
74 | cmd[0] = MCHP48L640_CMD_RDSR; |
75 | cmd[1] = 0x00; |
76 | mutex_lock(&flash->lock); |
77 | ret = spi_write_then_read(spi: flash->spi, txbuf: &cmd[0], n_tx: 1, rxbuf: &cmd[1], n_rx: 1); |
78 | mutex_unlock(lock: &flash->lock); |
79 | if (!ret) |
80 | *status = cmd[1]; |
81 | dev_dbg(&flash->spi->dev, "read status ret: %d status: %x" , ret, *status); |
82 | |
83 | return ret; |
84 | } |
85 | |
86 | static int mchp48l640_waitforbit(struct mchp48l640_flash *flash, int bit, bool set) |
87 | { |
88 | int ret, status; |
89 | unsigned long deadline; |
90 | |
91 | deadline = jiffies + msecs_to_jiffies(MCHP48L640_TIMEOUT); |
92 | do { |
93 | ret = mchp48l640_read_status(flash, status: &status); |
94 | dev_dbg(&flash->spi->dev, "read status ret: %d bit: %x %sset status: %x" , |
95 | ret, bit, (set ? "" : "not" ), status); |
96 | if (ret) |
97 | return ret; |
98 | |
99 | if (set) { |
100 | if ((status & bit) == bit) |
101 | return 0; |
102 | } else { |
103 | if ((status & bit) == 0) |
104 | return 0; |
105 | } |
106 | |
107 | usleep_range(min: 1000, max: 2000); |
108 | } while (!time_after_eq(jiffies, deadline)); |
109 | |
110 | dev_err(&flash->spi->dev, "Timeout waiting for bit %x %s set in status register." , |
111 | bit, (set ? "" : "not" )); |
112 | return -ETIMEDOUT; |
113 | } |
114 | |
115 | static int mchp48l640_write_prepare(struct mchp48l640_flash *flash, bool enable) |
116 | { |
117 | unsigned char cmd[2]; |
118 | int ret; |
119 | |
120 | if (enable) |
121 | cmd[0] = MCHP48L640_CMD_WREN; |
122 | else |
123 | cmd[0] = MCHP48L640_CMD_WRDI; |
124 | |
125 | mutex_lock(&flash->lock); |
126 | ret = spi_write(spi: flash->spi, buf: cmd, len: 1); |
127 | mutex_unlock(lock: &flash->lock); |
128 | |
129 | if (ret) |
130 | dev_err(&flash->spi->dev, "write %sable failed ret: %d" , |
131 | (enable ? "en" : "dis" ), ret); |
132 | |
133 | dev_dbg(&flash->spi->dev, "write %sable success ret: %d" , |
134 | (enable ? "en" : "dis" ), ret); |
135 | if (enable) |
136 | return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_WEL, set: true); |
137 | |
138 | return ret; |
139 | } |
140 | |
141 | static int mchp48l640_set_mode(struct mchp48l640_flash *flash) |
142 | { |
143 | unsigned char cmd[2]; |
144 | int ret; |
145 | |
146 | ret = mchp48l640_write_prepare(flash, enable: true); |
147 | if (ret) |
148 | return ret; |
149 | |
150 | cmd[0] = MCHP48L640_CMD_WRSR; |
151 | cmd[1] = MCHP48L640_STATUS_PRO; |
152 | |
153 | mutex_lock(&flash->lock); |
154 | ret = spi_write(spi: flash->spi, buf: cmd, len: 2); |
155 | mutex_unlock(lock: &flash->lock); |
156 | if (ret) |
157 | dev_err(&flash->spi->dev, "Could not set continuous mode ret: %d" , ret); |
158 | |
159 | return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_PRO, set: true); |
160 | } |
161 | |
162 | static int mchp48l640_wait_rdy(struct mchp48l640_flash *flash) |
163 | { |
164 | return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_RDY, set: false); |
165 | }; |
166 | |
167 | static int mchp48l640_write_page(struct mtd_info *mtd, loff_t to, size_t len, |
168 | size_t *retlen, const unsigned char *buf) |
169 | { |
170 | struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd); |
171 | unsigned char *cmd; |
172 | int ret; |
173 | int cmdlen; |
174 | |
175 | cmd = kmalloc(size: (3 + len), GFP_KERNEL | GFP_DMA); |
176 | if (!cmd) |
177 | return -ENOMEM; |
178 | |
179 | ret = mchp48l640_wait_rdy(flash); |
180 | if (ret) |
181 | goto fail; |
182 | |
183 | ret = mchp48l640_write_prepare(flash, enable: true); |
184 | if (ret) |
185 | goto fail; |
186 | |
187 | mutex_lock(&flash->lock); |
188 | cmdlen = mchp48l640_mkcmd(flash, MCHP48L640_CMD_WRITE, addr: to, buf: cmd); |
189 | memcpy(&cmd[cmdlen], buf, len); |
190 | ret = spi_write(spi: flash->spi, buf: cmd, len: cmdlen + len); |
191 | mutex_unlock(lock: &flash->lock); |
192 | if (!ret) |
193 | *retlen += len; |
194 | else |
195 | goto fail; |
196 | |
197 | ret = mchp48l640_waitforbit(flash, MCHP48L640_STATUS_WEL, set: false); |
198 | if (ret) |
199 | goto fail; |
200 | |
201 | kfree(objp: cmd); |
202 | return 0; |
203 | fail: |
204 | kfree(objp: cmd); |
205 | dev_err(&flash->spi->dev, "write fail with: %d" , ret); |
206 | return ret; |
207 | }; |
208 | |
209 | static int mchp48l640_write(struct mtd_info *mtd, loff_t to, size_t len, |
210 | size_t *retlen, const unsigned char *buf) |
211 | { |
212 | struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd); |
213 | int ret; |
214 | size_t wlen = 0; |
215 | loff_t woff = to; |
216 | size_t ws; |
217 | size_t page_sz = flash->caps->page_size; |
218 | |
219 | /* |
220 | * we set PRO bit (page rollover), but writing length > page size |
221 | * does result in total chaos, so write in 32 byte chunks. |
222 | */ |
223 | while (wlen < len) { |
224 | ws = min((len - wlen), page_sz); |
225 | ret = mchp48l640_write_page(mtd, to: woff, len: ws, retlen, buf: &buf[wlen]); |
226 | if (ret) |
227 | return ret; |
228 | wlen += ws; |
229 | woff += ws; |
230 | } |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static int mchp48l640_read_page(struct mtd_info *mtd, loff_t from, size_t len, |
236 | size_t *retlen, unsigned char *buf) |
237 | { |
238 | struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd); |
239 | unsigned char *cmd; |
240 | int ret; |
241 | int cmdlen; |
242 | |
243 | cmd = kmalloc(size: (3 + len), GFP_KERNEL | GFP_DMA); |
244 | if (!cmd) |
245 | return -ENOMEM; |
246 | |
247 | ret = mchp48l640_wait_rdy(flash); |
248 | if (ret) |
249 | goto fail; |
250 | |
251 | mutex_lock(&flash->lock); |
252 | cmdlen = mchp48l640_mkcmd(flash, MCHP48L640_CMD_READ, addr: from, buf: cmd); |
253 | ret = spi_write_then_read(spi: flash->spi, txbuf: cmd, n_tx: cmdlen, rxbuf: buf, n_rx: len); |
254 | mutex_unlock(lock: &flash->lock); |
255 | if (!ret) |
256 | *retlen += len; |
257 | |
258 | kfree(objp: cmd); |
259 | return ret; |
260 | |
261 | fail: |
262 | kfree(objp: cmd); |
263 | dev_err(&flash->spi->dev, "read fail with: %d" , ret); |
264 | return ret; |
265 | } |
266 | |
267 | static int mchp48l640_read(struct mtd_info *mtd, loff_t from, size_t len, |
268 | size_t *retlen, unsigned char *buf) |
269 | { |
270 | struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd); |
271 | int ret; |
272 | size_t wlen = 0; |
273 | loff_t woff = from; |
274 | size_t ws; |
275 | size_t page_sz = flash->caps->page_size; |
276 | |
277 | /* |
278 | * we set PRO bit (page rollover), but if read length > page size |
279 | * does result in total chaos in result ... |
280 | */ |
281 | while (wlen < len) { |
282 | ws = min((len - wlen), page_sz); |
283 | ret = mchp48l640_read_page(mtd, from: woff, len: ws, retlen, buf: &buf[wlen]); |
284 | if (ret) |
285 | return ret; |
286 | wlen += ws; |
287 | woff += ws; |
288 | } |
289 | |
290 | return 0; |
291 | }; |
292 | |
293 | static const struct mchp48_caps mchp48l640_caps = { |
294 | .size = SZ_8K, |
295 | .page_size = 32, |
296 | }; |
297 | |
298 | static int mchp48l640_probe(struct spi_device *spi) |
299 | { |
300 | struct mchp48l640_flash *flash; |
301 | struct flash_platform_data *data; |
302 | int err; |
303 | int status; |
304 | |
305 | flash = devm_kzalloc(dev: &spi->dev, size: sizeof(*flash), GFP_KERNEL); |
306 | if (!flash) |
307 | return -ENOMEM; |
308 | |
309 | flash->spi = spi; |
310 | mutex_init(&flash->lock); |
311 | spi_set_drvdata(spi, data: flash); |
312 | |
313 | err = mchp48l640_read_status(flash, status: &status); |
314 | if (err) |
315 | return err; |
316 | |
317 | err = mchp48l640_set_mode(flash); |
318 | if (err) |
319 | return err; |
320 | |
321 | data = dev_get_platdata(dev: &spi->dev); |
322 | |
323 | flash->caps = of_device_get_match_data(dev: &spi->dev); |
324 | if (!flash->caps) |
325 | flash->caps = &mchp48l640_caps; |
326 | |
327 | mtd_set_of_node(mtd: &flash->mtd, np: spi->dev.of_node); |
328 | flash->mtd.dev.parent = &spi->dev; |
329 | flash->mtd.type = MTD_RAM; |
330 | flash->mtd.flags = MTD_CAP_RAM; |
331 | flash->mtd.writesize = flash->caps->page_size; |
332 | flash->mtd.size = flash->caps->size; |
333 | flash->mtd._read = mchp48l640_read; |
334 | flash->mtd._write = mchp48l640_write; |
335 | |
336 | err = mtd_device_register(&flash->mtd, data ? data->parts : NULL, |
337 | data ? data->nr_parts : 0); |
338 | if (err) |
339 | return err; |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | static void mchp48l640_remove(struct spi_device *spi) |
345 | { |
346 | struct mchp48l640_flash *flash = spi_get_drvdata(spi); |
347 | |
348 | WARN_ON(mtd_device_unregister(&flash->mtd)); |
349 | } |
350 | |
351 | static const struct of_device_id mchp48l640_of_table[] = { |
352 | { |
353 | .compatible = "microchip,48l640" , |
354 | .data = &mchp48l640_caps, |
355 | }, |
356 | {} |
357 | }; |
358 | MODULE_DEVICE_TABLE(of, mchp48l640_of_table); |
359 | |
360 | static const struct spi_device_id mchp48l640_spi_ids[] = { |
361 | { |
362 | .name = "48l640" , |
363 | .driver_data = (kernel_ulong_t)&mchp48l640_caps, |
364 | }, |
365 | {} |
366 | }; |
367 | MODULE_DEVICE_TABLE(spi, mchp48l640_spi_ids); |
368 | |
369 | static struct spi_driver mchp48l640_driver = { |
370 | .driver = { |
371 | .name = "mchp48l640" , |
372 | .of_match_table = mchp48l640_of_table, |
373 | }, |
374 | .probe = mchp48l640_probe, |
375 | .remove = mchp48l640_remove, |
376 | .id_table = mchp48l640_spi_ids, |
377 | }; |
378 | |
379 | module_spi_driver(mchp48l640_driver); |
380 | |
381 | MODULE_DESCRIPTION("MTD SPI driver for Microchip 48l640 EERAM chips" ); |
382 | MODULE_AUTHOR("Heiko Schocher <hs@denx.de>" ); |
383 | MODULE_LICENSE("GPL v2" ); |
384 | MODULE_ALIAS("spi:mchp48l640" ); |
385 | |