xref: /OK3568_Linux_fs/kernel/drivers/mtd/devices/sst25l.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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