1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * sst25l.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Driver for SST25L SPI Flash chips
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Copyright © 2009 Bluewater Systems Ltd
8*4882a593Smuzhiyun * Author: Andre Renaud <andre@bluewatersys.com>
9*4882a593Smuzhiyun * Author: Ryan Mallon
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * Based on m25p80.c
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/mutex.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/sched.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #include <linux/mtd/mtd.h>
22*4882a593Smuzhiyun #include <linux/mtd/partitions.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include <linux/spi/spi.h>
25*4882a593Smuzhiyun #include <linux/spi/flash.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun /* Erases can take up to 3 seconds! */
28*4882a593Smuzhiyun #define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000)
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define SST25L_CMD_WRSR 0x01 /* Write status register */
31*4882a593Smuzhiyun #define SST25L_CMD_WRDI 0x04 /* Write disable */
32*4882a593Smuzhiyun #define SST25L_CMD_RDSR 0x05 /* Read status register */
33*4882a593Smuzhiyun #define SST25L_CMD_WREN 0x06 /* Write enable */
34*4882a593Smuzhiyun #define SST25L_CMD_READ 0x03 /* High speed read */
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define SST25L_CMD_EWSR 0x50 /* Enable write status register */
37*4882a593Smuzhiyun #define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */
38*4882a593Smuzhiyun #define SST25L_CMD_READ_ID 0x90 /* Read device ID */
39*4882a593Smuzhiyun #define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */
42*4882a593Smuzhiyun #define SST25L_STATUS_WREN (1 << 1) /* Write enabled */
43*4882a593Smuzhiyun #define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */
44*4882a593Smuzhiyun #define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun struct sst25l_flash {
47*4882a593Smuzhiyun struct spi_device *spi;
48*4882a593Smuzhiyun struct mutex lock;
49*4882a593Smuzhiyun struct mtd_info mtd;
50*4882a593Smuzhiyun };
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun struct flash_info {
53*4882a593Smuzhiyun const char *name;
54*4882a593Smuzhiyun uint16_t device_id;
55*4882a593Smuzhiyun unsigned page_size;
56*4882a593Smuzhiyun unsigned nr_pages;
57*4882a593Smuzhiyun unsigned erase_size;
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd)
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun static struct flash_info sst25l_flash_info[] = {
63*4882a593Smuzhiyun {"sst25lf020a", 0xbf43, 256, 1024, 4096},
64*4882a593Smuzhiyun {"sst25lf040a", 0xbf44, 256, 2048, 4096},
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun
sst25l_status(struct sst25l_flash * flash,int * status)67*4882a593Smuzhiyun static int sst25l_status(struct sst25l_flash *flash, int *status)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct spi_message m;
70*4882a593Smuzhiyun struct spi_transfer t;
71*4882a593Smuzhiyun unsigned char cmd_resp[2];
72*4882a593Smuzhiyun int err;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun spi_message_init(&m);
75*4882a593Smuzhiyun memset(&t, 0, sizeof(struct spi_transfer));
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun cmd_resp[0] = SST25L_CMD_RDSR;
78*4882a593Smuzhiyun cmd_resp[1] = 0xff;
79*4882a593Smuzhiyun t.tx_buf = cmd_resp;
80*4882a593Smuzhiyun t.rx_buf = cmd_resp;
81*4882a593Smuzhiyun t.len = sizeof(cmd_resp);
82*4882a593Smuzhiyun spi_message_add_tail(&t, &m);
83*4882a593Smuzhiyun err = spi_sync(flash->spi, &m);
84*4882a593Smuzhiyun if (err < 0)
85*4882a593Smuzhiyun return err;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun *status = cmd_resp[1];
88*4882a593Smuzhiyun return 0;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
sst25l_write_enable(struct sst25l_flash * flash,int enable)91*4882a593Smuzhiyun static int sst25l_write_enable(struct sst25l_flash *flash, int enable)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun unsigned char command[2];
94*4882a593Smuzhiyun int status, err;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI;
97*4882a593Smuzhiyun err = spi_write(flash->spi, command, 1);
98*4882a593Smuzhiyun if (err)
99*4882a593Smuzhiyun return err;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun command[0] = SST25L_CMD_EWSR;
102*4882a593Smuzhiyun err = spi_write(flash->spi, command, 1);
103*4882a593Smuzhiyun if (err)
104*4882a593Smuzhiyun return err;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun command[0] = SST25L_CMD_WRSR;
107*4882a593Smuzhiyun command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1;
108*4882a593Smuzhiyun err = spi_write(flash->spi, command, 2);
109*4882a593Smuzhiyun if (err)
110*4882a593Smuzhiyun return err;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun if (enable) {
113*4882a593Smuzhiyun err = sst25l_status(flash, &status);
114*4882a593Smuzhiyun if (err)
115*4882a593Smuzhiyun return err;
116*4882a593Smuzhiyun if (!(status & SST25L_STATUS_WREN))
117*4882a593Smuzhiyun return -EROFS;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return 0;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
sst25l_wait_till_ready(struct sst25l_flash * flash)123*4882a593Smuzhiyun static int sst25l_wait_till_ready(struct sst25l_flash *flash)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun unsigned long deadline;
126*4882a593Smuzhiyun int status, err;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun deadline = jiffies + MAX_READY_WAIT_JIFFIES;
129*4882a593Smuzhiyun do {
130*4882a593Smuzhiyun err = sst25l_status(flash, &status);
131*4882a593Smuzhiyun if (err)
132*4882a593Smuzhiyun return err;
133*4882a593Smuzhiyun if (!(status & SST25L_STATUS_BUSY))
134*4882a593Smuzhiyun return 0;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun cond_resched();
137*4882a593Smuzhiyun } while (!time_after_eq(jiffies, deadline));
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun return -ETIMEDOUT;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
sst25l_erase_sector(struct sst25l_flash * flash,uint32_t offset)142*4882a593Smuzhiyun static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun unsigned char command[4];
145*4882a593Smuzhiyun int err;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun err = sst25l_write_enable(flash, 1);
148*4882a593Smuzhiyun if (err)
149*4882a593Smuzhiyun return err;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun command[0] = SST25L_CMD_SECTOR_ERASE;
152*4882a593Smuzhiyun command[1] = offset >> 16;
153*4882a593Smuzhiyun command[2] = offset >> 8;
154*4882a593Smuzhiyun command[3] = offset;
155*4882a593Smuzhiyun err = spi_write(flash->spi, command, 4);
156*4882a593Smuzhiyun if (err)
157*4882a593Smuzhiyun return err;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun err = sst25l_wait_till_ready(flash);
160*4882a593Smuzhiyun if (err)
161*4882a593Smuzhiyun return err;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun return sst25l_write_enable(flash, 0);
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
sst25l_erase(struct mtd_info * mtd,struct erase_info * instr)166*4882a593Smuzhiyun static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun struct sst25l_flash *flash = to_sst25l_flash(mtd);
169*4882a593Smuzhiyun uint32_t addr, end;
170*4882a593Smuzhiyun int err;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun /* Sanity checks */
173*4882a593Smuzhiyun if ((uint32_t)instr->len % mtd->erasesize)
174*4882a593Smuzhiyun return -EINVAL;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if ((uint32_t)instr->addr % mtd->erasesize)
177*4882a593Smuzhiyun return -EINVAL;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun addr = instr->addr;
180*4882a593Smuzhiyun end = addr + instr->len;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun mutex_lock(&flash->lock);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun err = sst25l_wait_till_ready(flash);
185*4882a593Smuzhiyun if (err) {
186*4882a593Smuzhiyun mutex_unlock(&flash->lock);
187*4882a593Smuzhiyun return err;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun while (addr < end) {
191*4882a593Smuzhiyun err = sst25l_erase_sector(flash, addr);
192*4882a593Smuzhiyun if (err) {
193*4882a593Smuzhiyun mutex_unlock(&flash->lock);
194*4882a593Smuzhiyun dev_err(&flash->spi->dev, "Erase failed\n");
195*4882a593Smuzhiyun return err;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun addr += mtd->erasesize;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun mutex_unlock(&flash->lock);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun return 0;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
sst25l_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,unsigned char * buf)206*4882a593Smuzhiyun static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
207*4882a593Smuzhiyun size_t *retlen, unsigned char *buf)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun struct sst25l_flash *flash = to_sst25l_flash(mtd);
210*4882a593Smuzhiyun struct spi_transfer transfer[2];
211*4882a593Smuzhiyun struct spi_message message;
212*4882a593Smuzhiyun unsigned char command[4];
213*4882a593Smuzhiyun int ret;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun spi_message_init(&message);
216*4882a593Smuzhiyun memset(&transfer, 0, sizeof(transfer));
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun command[0] = SST25L_CMD_READ;
219*4882a593Smuzhiyun command[1] = from >> 16;
220*4882a593Smuzhiyun command[2] = from >> 8;
221*4882a593Smuzhiyun command[3] = from;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun transfer[0].tx_buf = command;
224*4882a593Smuzhiyun transfer[0].len = sizeof(command);
225*4882a593Smuzhiyun spi_message_add_tail(&transfer[0], &message);
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun transfer[1].rx_buf = buf;
228*4882a593Smuzhiyun transfer[1].len = len;
229*4882a593Smuzhiyun spi_message_add_tail(&transfer[1], &message);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun mutex_lock(&flash->lock);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun /* Wait for previous write/erase to complete */
234*4882a593Smuzhiyun ret = sst25l_wait_till_ready(flash);
235*4882a593Smuzhiyun if (ret) {
236*4882a593Smuzhiyun mutex_unlock(&flash->lock);
237*4882a593Smuzhiyun return ret;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun spi_sync(flash->spi, &message);
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun if (retlen && message.actual_length > sizeof(command))
243*4882a593Smuzhiyun *retlen += message.actual_length - sizeof(command);
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun mutex_unlock(&flash->lock);
246*4882a593Smuzhiyun return 0;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
sst25l_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const unsigned char * buf)249*4882a593Smuzhiyun static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
250*4882a593Smuzhiyun size_t *retlen, const unsigned char *buf)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun struct sst25l_flash *flash = to_sst25l_flash(mtd);
253*4882a593Smuzhiyun int i, j, ret, bytes, copied = 0;
254*4882a593Smuzhiyun unsigned char command[5];
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun if ((uint32_t)to % mtd->writesize)
257*4882a593Smuzhiyun return -EINVAL;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun mutex_lock(&flash->lock);
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun ret = sst25l_write_enable(flash, 1);
262*4882a593Smuzhiyun if (ret)
263*4882a593Smuzhiyun goto out;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun for (i = 0; i < len; i += mtd->writesize) {
266*4882a593Smuzhiyun ret = sst25l_wait_till_ready(flash);
267*4882a593Smuzhiyun if (ret)
268*4882a593Smuzhiyun goto out;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun /* Write the first byte of the page */
271*4882a593Smuzhiyun command[0] = SST25L_CMD_AAI_PROGRAM;
272*4882a593Smuzhiyun command[1] = (to + i) >> 16;
273*4882a593Smuzhiyun command[2] = (to + i) >> 8;
274*4882a593Smuzhiyun command[3] = (to + i);
275*4882a593Smuzhiyun command[4] = buf[i];
276*4882a593Smuzhiyun ret = spi_write(flash->spi, command, 5);
277*4882a593Smuzhiyun if (ret < 0)
278*4882a593Smuzhiyun goto out;
279*4882a593Smuzhiyun copied++;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun /*
282*4882a593Smuzhiyun * Write the remaining bytes using auto address
283*4882a593Smuzhiyun * increment mode
284*4882a593Smuzhiyun */
285*4882a593Smuzhiyun bytes = min_t(uint32_t, mtd->writesize, len - i);
286*4882a593Smuzhiyun for (j = 1; j < bytes; j++, copied++) {
287*4882a593Smuzhiyun ret = sst25l_wait_till_ready(flash);
288*4882a593Smuzhiyun if (ret)
289*4882a593Smuzhiyun goto out;
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun command[1] = buf[i + j];
292*4882a593Smuzhiyun ret = spi_write(flash->spi, command, 2);
293*4882a593Smuzhiyun if (ret)
294*4882a593Smuzhiyun goto out;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun out:
299*4882a593Smuzhiyun ret = sst25l_write_enable(flash, 0);
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun if (retlen)
302*4882a593Smuzhiyun *retlen = copied;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun mutex_unlock(&flash->lock);
305*4882a593Smuzhiyun return ret;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
sst25l_match_device(struct spi_device * spi)308*4882a593Smuzhiyun static struct flash_info *sst25l_match_device(struct spi_device *spi)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun struct flash_info *flash_info = NULL;
311*4882a593Smuzhiyun struct spi_message m;
312*4882a593Smuzhiyun struct spi_transfer t;
313*4882a593Smuzhiyun unsigned char cmd_resp[6];
314*4882a593Smuzhiyun int i, err;
315*4882a593Smuzhiyun uint16_t id;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun spi_message_init(&m);
318*4882a593Smuzhiyun memset(&t, 0, sizeof(struct spi_transfer));
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun cmd_resp[0] = SST25L_CMD_READ_ID;
321*4882a593Smuzhiyun cmd_resp[1] = 0;
322*4882a593Smuzhiyun cmd_resp[2] = 0;
323*4882a593Smuzhiyun cmd_resp[3] = 0;
324*4882a593Smuzhiyun cmd_resp[4] = 0xff;
325*4882a593Smuzhiyun cmd_resp[5] = 0xff;
326*4882a593Smuzhiyun t.tx_buf = cmd_resp;
327*4882a593Smuzhiyun t.rx_buf = cmd_resp;
328*4882a593Smuzhiyun t.len = sizeof(cmd_resp);
329*4882a593Smuzhiyun spi_message_add_tail(&t, &m);
330*4882a593Smuzhiyun err = spi_sync(spi, &m);
331*4882a593Smuzhiyun if (err < 0) {
332*4882a593Smuzhiyun dev_err(&spi->dev, "error reading device id\n");
333*4882a593Smuzhiyun return NULL;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun id = (cmd_resp[4] << 8) | cmd_resp[5];
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++)
339*4882a593Smuzhiyun if (sst25l_flash_info[i].device_id == id)
340*4882a593Smuzhiyun flash_info = &sst25l_flash_info[i];
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun if (!flash_info)
343*4882a593Smuzhiyun dev_err(&spi->dev, "unknown id %.4x\n", id);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun return flash_info;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
sst25l_probe(struct spi_device * spi)348*4882a593Smuzhiyun static int sst25l_probe(struct spi_device *spi)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun struct flash_info *flash_info;
351*4882a593Smuzhiyun struct sst25l_flash *flash;
352*4882a593Smuzhiyun struct flash_platform_data *data;
353*4882a593Smuzhiyun int ret;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun flash_info = sst25l_match_device(spi);
356*4882a593Smuzhiyun if (!flash_info)
357*4882a593Smuzhiyun return -ENODEV;
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
360*4882a593Smuzhiyun if (!flash)
361*4882a593Smuzhiyun return -ENOMEM;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun flash->spi = spi;
364*4882a593Smuzhiyun mutex_init(&flash->lock);
365*4882a593Smuzhiyun spi_set_drvdata(spi, flash);
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun data = dev_get_platdata(&spi->dev);
368*4882a593Smuzhiyun if (data && data->name)
369*4882a593Smuzhiyun flash->mtd.name = data->name;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun flash->mtd.dev.parent = &spi->dev;
372*4882a593Smuzhiyun flash->mtd.type = MTD_NORFLASH;
373*4882a593Smuzhiyun flash->mtd.flags = MTD_CAP_NORFLASH;
374*4882a593Smuzhiyun flash->mtd.erasesize = flash_info->erase_size;
375*4882a593Smuzhiyun flash->mtd.writesize = flash_info->page_size;
376*4882a593Smuzhiyun flash->mtd.writebufsize = flash_info->page_size;
377*4882a593Smuzhiyun flash->mtd.size = flash_info->page_size * flash_info->nr_pages;
378*4882a593Smuzhiyun flash->mtd._erase = sst25l_erase;
379*4882a593Smuzhiyun flash->mtd._read = sst25l_read;
380*4882a593Smuzhiyun flash->mtd._write = sst25l_write;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
383*4882a593Smuzhiyun (long long)flash->mtd.size >> 10);
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) "
386*4882a593Smuzhiyun ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
387*4882a593Smuzhiyun flash->mtd.name,
388*4882a593Smuzhiyun (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
389*4882a593Smuzhiyun flash->mtd.erasesize, flash->mtd.erasesize / 1024,
390*4882a593Smuzhiyun flash->mtd.numeraseregions);
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun ret = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
394*4882a593Smuzhiyun data ? data->nr_parts : 0);
395*4882a593Smuzhiyun if (ret)
396*4882a593Smuzhiyun return -ENODEV;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun return 0;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun
sst25l_remove(struct spi_device * spi)401*4882a593Smuzhiyun static int sst25l_remove(struct spi_device *spi)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun struct sst25l_flash *flash = spi_get_drvdata(spi);
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun return mtd_device_unregister(&flash->mtd);
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun static struct spi_driver sst25l_driver = {
409*4882a593Smuzhiyun .driver = {
410*4882a593Smuzhiyun .name = "sst25l",
411*4882a593Smuzhiyun },
412*4882a593Smuzhiyun .probe = sst25l_probe,
413*4882a593Smuzhiyun .remove = sst25l_remove,
414*4882a593Smuzhiyun };
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun module_spi_driver(sst25l_driver);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
419*4882a593Smuzhiyun MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
420*4882a593Smuzhiyun "Ryan Mallon");
421*4882a593Smuzhiyun MODULE_LICENSE("GPL");
422