1ec77e21bSRyan Mallon /* 2ec77e21bSRyan Mallon * sst25l.c 3ec77e21bSRyan Mallon * 4ec77e21bSRyan Mallon * Driver for SST25L SPI Flash chips 5ec77e21bSRyan Mallon * 6ec77e21bSRyan Mallon * Copyright © 2009 Bluewater Systems Ltd 7ec77e21bSRyan Mallon * Author: Andre Renaud <andre@bluewatersys.com> 8ec77e21bSRyan Mallon * Author: Ryan Mallon <ryan@bluewatersys.com> 9ec77e21bSRyan Mallon * 10ec77e21bSRyan Mallon * Based on m25p80.c 11ec77e21bSRyan Mallon * 12ec77e21bSRyan Mallon * This code is free software; you can redistribute it and/or modify 13ec77e21bSRyan Mallon * it under the terms of the GNU General Public License version 2 as 14ec77e21bSRyan Mallon * published by the Free Software Foundation. 15ec77e21bSRyan Mallon * 16ec77e21bSRyan Mallon */ 17ec77e21bSRyan Mallon 18ec77e21bSRyan Mallon #include <linux/init.h> 19ec77e21bSRyan Mallon #include <linux/module.h> 20ec77e21bSRyan Mallon #include <linux/device.h> 21ec77e21bSRyan Mallon #include <linux/mutex.h> 22ec77e21bSRyan Mallon #include <linux/interrupt.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 24d43c36dcSAlexey Dobriyan #include <linux/sched.h> 25ec77e21bSRyan Mallon 26ec77e21bSRyan Mallon #include <linux/mtd/mtd.h> 27ec77e21bSRyan Mallon #include <linux/mtd/partitions.h> 28ec77e21bSRyan Mallon 29ec77e21bSRyan Mallon #include <linux/spi/spi.h> 30ec77e21bSRyan Mallon #include <linux/spi/flash.h> 31ec77e21bSRyan Mallon 32ec77e21bSRyan Mallon /* Erases can take up to 3 seconds! */ 33ec77e21bSRyan Mallon #define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000) 34ec77e21bSRyan Mallon 35ec77e21bSRyan Mallon #define SST25L_CMD_WRSR 0x01 /* Write status register */ 36ec77e21bSRyan Mallon #define SST25L_CMD_WRDI 0x04 /* Write disable */ 37ec77e21bSRyan Mallon #define SST25L_CMD_RDSR 0x05 /* Read status register */ 38ec77e21bSRyan Mallon #define SST25L_CMD_WREN 0x06 /* Write enable */ 39ec77e21bSRyan Mallon #define SST25L_CMD_READ 0x03 /* High speed read */ 40ec77e21bSRyan Mallon 41ec77e21bSRyan Mallon #define SST25L_CMD_EWSR 0x50 /* Enable write status register */ 42ec77e21bSRyan Mallon #define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */ 43ec77e21bSRyan Mallon #define SST25L_CMD_READ_ID 0x90 /* Read device ID */ 44ec77e21bSRyan Mallon #define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */ 45ec77e21bSRyan Mallon 46ec77e21bSRyan Mallon #define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */ 47ec77e21bSRyan Mallon #define SST25L_STATUS_WREN (1 << 1) /* Write enabled */ 48ec77e21bSRyan Mallon #define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */ 49ec77e21bSRyan Mallon #define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */ 50ec77e21bSRyan Mallon 51ec77e21bSRyan Mallon struct sst25l_flash { 52ec77e21bSRyan Mallon struct spi_device *spi; 53ec77e21bSRyan Mallon struct mutex lock; 54ec77e21bSRyan Mallon struct mtd_info mtd; 55ec77e21bSRyan Mallon 56ec77e21bSRyan Mallon int partitioned; 57ec77e21bSRyan Mallon }; 58ec77e21bSRyan Mallon 59ec77e21bSRyan Mallon struct flash_info { 60ec77e21bSRyan Mallon const char *name; 61ec77e21bSRyan Mallon uint16_t device_id; 62ec77e21bSRyan Mallon unsigned page_size; 63ec77e21bSRyan Mallon unsigned nr_pages; 64ec77e21bSRyan Mallon unsigned erase_size; 65ec77e21bSRyan Mallon }; 66ec77e21bSRyan Mallon 67ec77e21bSRyan Mallon #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) 68ec77e21bSRyan Mallon 69ec77e21bSRyan Mallon static struct flash_info __initdata sst25l_flash_info[] = { 70ec77e21bSRyan Mallon {"sst25lf020a", 0xbf43, 256, 1024, 4096}, 71ec77e21bSRyan Mallon {"sst25lf040a", 0xbf44, 256, 2048, 4096}, 72ec77e21bSRyan Mallon }; 73ec77e21bSRyan Mallon 74ec77e21bSRyan Mallon static int sst25l_status(struct sst25l_flash *flash, int *status) 75ec77e21bSRyan Mallon { 760ffe0ce3SH Hartley Sweeten struct spi_message m; 770ffe0ce3SH Hartley Sweeten struct spi_transfer t; 780ffe0ce3SH Hartley Sweeten unsigned char cmd_resp[2]; 79ec77e21bSRyan Mallon int err; 80ec77e21bSRyan Mallon 810ffe0ce3SH Hartley Sweeten spi_message_init(&m); 820ffe0ce3SH Hartley Sweeten memset(&t, 0, sizeof(struct spi_transfer)); 830ffe0ce3SH Hartley Sweeten 840ffe0ce3SH Hartley Sweeten cmd_resp[0] = SST25L_CMD_RDSR; 850ffe0ce3SH Hartley Sweeten cmd_resp[1] = 0xff; 860ffe0ce3SH Hartley Sweeten t.tx_buf = cmd_resp; 870ffe0ce3SH Hartley Sweeten t.rx_buf = cmd_resp; 880ffe0ce3SH Hartley Sweeten t.len = sizeof(cmd_resp); 890ffe0ce3SH Hartley Sweeten spi_message_add_tail(&t, &m); 900ffe0ce3SH Hartley Sweeten err = spi_sync(flash->spi, &m); 91ec77e21bSRyan Mallon if (err < 0) 92ec77e21bSRyan Mallon return err; 93ec77e21bSRyan Mallon 940ffe0ce3SH Hartley Sweeten *status = cmd_resp[1]; 95ec77e21bSRyan Mallon return 0; 96ec77e21bSRyan Mallon } 97ec77e21bSRyan Mallon 98ec77e21bSRyan Mallon static int sst25l_write_enable(struct sst25l_flash *flash, int enable) 99ec77e21bSRyan Mallon { 100ec77e21bSRyan Mallon unsigned char command[2]; 101ec77e21bSRyan Mallon int status, err; 102ec77e21bSRyan Mallon 103ec77e21bSRyan Mallon command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI; 104ec77e21bSRyan Mallon err = spi_write(flash->spi, command, 1); 105ec77e21bSRyan Mallon if (err) 106ec77e21bSRyan Mallon return err; 107ec77e21bSRyan Mallon 108ec77e21bSRyan Mallon command[0] = SST25L_CMD_EWSR; 109ec77e21bSRyan Mallon err = spi_write(flash->spi, command, 1); 110ec77e21bSRyan Mallon if (err) 111ec77e21bSRyan Mallon return err; 112ec77e21bSRyan Mallon 113ec77e21bSRyan Mallon command[0] = SST25L_CMD_WRSR; 114ec77e21bSRyan Mallon command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1; 115ec77e21bSRyan Mallon err = spi_write(flash->spi, command, 2); 116ec77e21bSRyan Mallon if (err) 117ec77e21bSRyan Mallon return err; 118ec77e21bSRyan Mallon 119ec77e21bSRyan Mallon if (enable) { 120ec77e21bSRyan Mallon err = sst25l_status(flash, &status); 121ec77e21bSRyan Mallon if (err) 122ec77e21bSRyan Mallon return err; 123ec77e21bSRyan Mallon if (!(status & SST25L_STATUS_WREN)) 124ec77e21bSRyan Mallon return -EROFS; 125ec77e21bSRyan Mallon } 126ec77e21bSRyan Mallon 127ec77e21bSRyan Mallon return 0; 128ec77e21bSRyan Mallon } 129ec77e21bSRyan Mallon 130ec77e21bSRyan Mallon static int sst25l_wait_till_ready(struct sst25l_flash *flash) 131ec77e21bSRyan Mallon { 132ec77e21bSRyan Mallon unsigned long deadline; 133ec77e21bSRyan Mallon int status, err; 134ec77e21bSRyan Mallon 135ec77e21bSRyan Mallon deadline = jiffies + MAX_READY_WAIT_JIFFIES; 136ec77e21bSRyan Mallon do { 137ec77e21bSRyan Mallon err = sst25l_status(flash, &status); 138ec77e21bSRyan Mallon if (err) 139ec77e21bSRyan Mallon return err; 140ec77e21bSRyan Mallon if (!(status & SST25L_STATUS_BUSY)) 141ec77e21bSRyan Mallon return 0; 142ec77e21bSRyan Mallon 143ec77e21bSRyan Mallon cond_resched(); 144ec77e21bSRyan Mallon } while (!time_after_eq(jiffies, deadline)); 145ec77e21bSRyan Mallon 146ec77e21bSRyan Mallon return -ETIMEDOUT; 147ec77e21bSRyan Mallon } 148ec77e21bSRyan Mallon 149ec77e21bSRyan Mallon static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset) 150ec77e21bSRyan Mallon { 151ec77e21bSRyan Mallon unsigned char command[4]; 152ec77e21bSRyan Mallon int err; 153ec77e21bSRyan Mallon 154ec77e21bSRyan Mallon err = sst25l_write_enable(flash, 1); 155ec77e21bSRyan Mallon if (err) 156ec77e21bSRyan Mallon return err; 157ec77e21bSRyan Mallon 158ec77e21bSRyan Mallon command[0] = SST25L_CMD_SECTOR_ERASE; 159ec77e21bSRyan Mallon command[1] = offset >> 16; 160ec77e21bSRyan Mallon command[2] = offset >> 8; 161ec77e21bSRyan Mallon command[3] = offset; 162ec77e21bSRyan Mallon err = spi_write(flash->spi, command, 4); 163ec77e21bSRyan Mallon if (err) 164ec77e21bSRyan Mallon return err; 165ec77e21bSRyan Mallon 166ec77e21bSRyan Mallon err = sst25l_wait_till_ready(flash); 167ec77e21bSRyan Mallon if (err) 168ec77e21bSRyan Mallon return err; 169ec77e21bSRyan Mallon 170ec77e21bSRyan Mallon return sst25l_write_enable(flash, 0); 171ec77e21bSRyan Mallon } 172ec77e21bSRyan Mallon 173ec77e21bSRyan Mallon static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) 174ec77e21bSRyan Mallon { 175ec77e21bSRyan Mallon struct sst25l_flash *flash = to_sst25l_flash(mtd); 176ec77e21bSRyan Mallon uint32_t addr, end; 177ec77e21bSRyan Mallon int err; 178ec77e21bSRyan Mallon 179ec77e21bSRyan Mallon /* Sanity checks */ 180ec77e21bSRyan Mallon if (instr->addr + instr->len > flash->mtd.size) 181ec77e21bSRyan Mallon return -EINVAL; 182ec77e21bSRyan Mallon 183ec77e21bSRyan Mallon if ((uint32_t)instr->len % mtd->erasesize) 184ec77e21bSRyan Mallon return -EINVAL; 185ec77e21bSRyan Mallon 186ec77e21bSRyan Mallon if ((uint32_t)instr->addr % mtd->erasesize) 187ec77e21bSRyan Mallon return -EINVAL; 188ec77e21bSRyan Mallon 189ec77e21bSRyan Mallon addr = instr->addr; 190ec77e21bSRyan Mallon end = addr + instr->len; 191ec77e21bSRyan Mallon 192ec77e21bSRyan Mallon mutex_lock(&flash->lock); 193ec77e21bSRyan Mallon 194ec77e21bSRyan Mallon err = sst25l_wait_till_ready(flash); 1952eaaa5ffSJiri Slaby if (err) { 1962eaaa5ffSJiri Slaby mutex_unlock(&flash->lock); 197ec77e21bSRyan Mallon return err; 1982eaaa5ffSJiri Slaby } 199ec77e21bSRyan Mallon 200ec77e21bSRyan Mallon while (addr < end) { 201ec77e21bSRyan Mallon err = sst25l_erase_sector(flash, addr); 202ec77e21bSRyan Mallon if (err) { 203ec77e21bSRyan Mallon mutex_unlock(&flash->lock); 204ec77e21bSRyan Mallon instr->state = MTD_ERASE_FAILED; 205ec77e21bSRyan Mallon dev_err(&flash->spi->dev, "Erase failed\n"); 206ec77e21bSRyan Mallon return err; 207ec77e21bSRyan Mallon } 208ec77e21bSRyan Mallon 209ec77e21bSRyan Mallon addr += mtd->erasesize; 210ec77e21bSRyan Mallon } 211ec77e21bSRyan Mallon 212ec77e21bSRyan Mallon mutex_unlock(&flash->lock); 213ec77e21bSRyan Mallon 214ec77e21bSRyan Mallon instr->state = MTD_ERASE_DONE; 215ec77e21bSRyan Mallon mtd_erase_callback(instr); 216ec77e21bSRyan Mallon return 0; 217ec77e21bSRyan Mallon } 218ec77e21bSRyan Mallon 219ec77e21bSRyan Mallon static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len, 220ec77e21bSRyan Mallon size_t *retlen, unsigned char *buf) 221ec77e21bSRyan Mallon { 222ec77e21bSRyan Mallon struct sst25l_flash *flash = to_sst25l_flash(mtd); 223ec77e21bSRyan Mallon struct spi_transfer transfer[2]; 224ec77e21bSRyan Mallon struct spi_message message; 225ec77e21bSRyan Mallon unsigned char command[4]; 226ec77e21bSRyan Mallon int ret; 227ec77e21bSRyan Mallon 228ec77e21bSRyan Mallon /* Sanity checking */ 229ec77e21bSRyan Mallon if (len == 0) 230ec77e21bSRyan Mallon return 0; 231ec77e21bSRyan Mallon 232ec77e21bSRyan Mallon if (from + len > flash->mtd.size) 233ec77e21bSRyan Mallon return -EINVAL; 234ec77e21bSRyan Mallon 235ec77e21bSRyan Mallon if (retlen) 236ec77e21bSRyan Mallon *retlen = 0; 237ec77e21bSRyan Mallon 238ec77e21bSRyan Mallon spi_message_init(&message); 239ec77e21bSRyan Mallon memset(&transfer, 0, sizeof(transfer)); 240ec77e21bSRyan Mallon 241ec77e21bSRyan Mallon command[0] = SST25L_CMD_READ; 242ec77e21bSRyan Mallon command[1] = from >> 16; 243ec77e21bSRyan Mallon command[2] = from >> 8; 244ec77e21bSRyan Mallon command[3] = from; 245ec77e21bSRyan Mallon 246ec77e21bSRyan Mallon transfer[0].tx_buf = command; 247ec77e21bSRyan Mallon transfer[0].len = sizeof(command); 248ec77e21bSRyan Mallon spi_message_add_tail(&transfer[0], &message); 249ec77e21bSRyan Mallon 250ec77e21bSRyan Mallon transfer[1].rx_buf = buf; 251ec77e21bSRyan Mallon transfer[1].len = len; 252ec77e21bSRyan Mallon spi_message_add_tail(&transfer[1], &message); 253ec77e21bSRyan Mallon 254ec77e21bSRyan Mallon mutex_lock(&flash->lock); 255ec77e21bSRyan Mallon 256ec77e21bSRyan Mallon /* Wait for previous write/erase to complete */ 257ec77e21bSRyan Mallon ret = sst25l_wait_till_ready(flash); 258ec77e21bSRyan Mallon if (ret) { 259ec77e21bSRyan Mallon mutex_unlock(&flash->lock); 260ec77e21bSRyan Mallon return ret; 261ec77e21bSRyan Mallon } 262ec77e21bSRyan Mallon 263ec77e21bSRyan Mallon spi_sync(flash->spi, &message); 264ec77e21bSRyan Mallon 265ec77e21bSRyan Mallon if (retlen && message.actual_length > sizeof(command)) 266ec77e21bSRyan Mallon *retlen += message.actual_length - sizeof(command); 267ec77e21bSRyan Mallon 268ec77e21bSRyan Mallon mutex_unlock(&flash->lock); 269ec77e21bSRyan Mallon return 0; 270ec77e21bSRyan Mallon } 271ec77e21bSRyan Mallon 272ec77e21bSRyan Mallon static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len, 273ec77e21bSRyan Mallon size_t *retlen, const unsigned char *buf) 274ec77e21bSRyan Mallon { 275ec77e21bSRyan Mallon struct sst25l_flash *flash = to_sst25l_flash(mtd); 276ec77e21bSRyan Mallon int i, j, ret, bytes, copied = 0; 277ec77e21bSRyan Mallon unsigned char command[5]; 278ec77e21bSRyan Mallon 279ec77e21bSRyan Mallon /* Sanity checks */ 280ec77e21bSRyan Mallon if (!len) 281ec77e21bSRyan Mallon return 0; 282ec77e21bSRyan Mallon 283ec77e21bSRyan Mallon if (to + len > flash->mtd.size) 284ec77e21bSRyan Mallon return -EINVAL; 285ec77e21bSRyan Mallon 286ec77e21bSRyan Mallon if ((uint32_t)to % mtd->writesize) 287ec77e21bSRyan Mallon return -EINVAL; 288ec77e21bSRyan Mallon 289ec77e21bSRyan Mallon mutex_lock(&flash->lock); 290ec77e21bSRyan Mallon 291ec77e21bSRyan Mallon ret = sst25l_write_enable(flash, 1); 292ec77e21bSRyan Mallon if (ret) 293ec77e21bSRyan Mallon goto out; 294ec77e21bSRyan Mallon 295ec77e21bSRyan Mallon for (i = 0; i < len; i += mtd->writesize) { 296ec77e21bSRyan Mallon ret = sst25l_wait_till_ready(flash); 297ec77e21bSRyan Mallon if (ret) 298ec77e21bSRyan Mallon goto out; 299ec77e21bSRyan Mallon 300ec77e21bSRyan Mallon /* Write the first byte of the page */ 301ec77e21bSRyan Mallon command[0] = SST25L_CMD_AAI_PROGRAM; 302ec77e21bSRyan Mallon command[1] = (to + i) >> 16; 303ec77e21bSRyan Mallon command[2] = (to + i) >> 8; 304ec77e21bSRyan Mallon command[3] = (to + i); 305ec77e21bSRyan Mallon command[4] = buf[i]; 306ec77e21bSRyan Mallon ret = spi_write(flash->spi, command, 5); 307ec77e21bSRyan Mallon if (ret < 0) 308ec77e21bSRyan Mallon goto out; 309ec77e21bSRyan Mallon copied++; 310ec77e21bSRyan Mallon 311ec77e21bSRyan Mallon /* 312ec77e21bSRyan Mallon * Write the remaining bytes using auto address 313ec77e21bSRyan Mallon * increment mode 314ec77e21bSRyan Mallon */ 315ec77e21bSRyan Mallon bytes = min_t(uint32_t, mtd->writesize, len - i); 316ec77e21bSRyan Mallon for (j = 1; j < bytes; j++, copied++) { 317ec77e21bSRyan Mallon ret = sst25l_wait_till_ready(flash); 318ec77e21bSRyan Mallon if (ret) 319ec77e21bSRyan Mallon goto out; 320ec77e21bSRyan Mallon 321ec77e21bSRyan Mallon command[1] = buf[i + j]; 322ec77e21bSRyan Mallon ret = spi_write(flash->spi, command, 2); 323ec77e21bSRyan Mallon if (ret) 324ec77e21bSRyan Mallon goto out; 325ec77e21bSRyan Mallon } 326ec77e21bSRyan Mallon } 327ec77e21bSRyan Mallon 328ec77e21bSRyan Mallon out: 329ec77e21bSRyan Mallon ret = sst25l_write_enable(flash, 0); 330ec77e21bSRyan Mallon 331ec77e21bSRyan Mallon if (retlen) 332ec77e21bSRyan Mallon *retlen = copied; 333ec77e21bSRyan Mallon 334ec77e21bSRyan Mallon mutex_unlock(&flash->lock); 335ec77e21bSRyan Mallon return ret; 336ec77e21bSRyan Mallon } 337ec77e21bSRyan Mallon 3386b2995b6SFabio Estevam static struct flash_info *__devinit sst25l_match_device(struct spi_device *spi) 339ec77e21bSRyan Mallon { 340ec77e21bSRyan Mallon struct flash_info *flash_info = NULL; 3410ffe0ce3SH Hartley Sweeten struct spi_message m; 3420ffe0ce3SH Hartley Sweeten struct spi_transfer t; 3430ffe0ce3SH Hartley Sweeten unsigned char cmd_resp[6]; 344ec77e21bSRyan Mallon int i, err; 345ec77e21bSRyan Mallon uint16_t id; 346ec77e21bSRyan Mallon 3470ffe0ce3SH Hartley Sweeten spi_message_init(&m); 3480ffe0ce3SH Hartley Sweeten memset(&t, 0, sizeof(struct spi_transfer)); 3490ffe0ce3SH Hartley Sweeten 3500ffe0ce3SH Hartley Sweeten cmd_resp[0] = SST25L_CMD_READ_ID; 3510ffe0ce3SH Hartley Sweeten cmd_resp[1] = 0; 3520ffe0ce3SH Hartley Sweeten cmd_resp[2] = 0; 3530ffe0ce3SH Hartley Sweeten cmd_resp[3] = 0; 3540ffe0ce3SH Hartley Sweeten cmd_resp[4] = 0xff; 3550ffe0ce3SH Hartley Sweeten cmd_resp[5] = 0xff; 3560ffe0ce3SH Hartley Sweeten t.tx_buf = cmd_resp; 3570ffe0ce3SH Hartley Sweeten t.rx_buf = cmd_resp; 3580ffe0ce3SH Hartley Sweeten t.len = sizeof(cmd_resp); 3590ffe0ce3SH Hartley Sweeten spi_message_add_tail(&t, &m); 3600ffe0ce3SH Hartley Sweeten err = spi_sync(spi, &m); 361ec77e21bSRyan Mallon if (err < 0) { 3620ffe0ce3SH Hartley Sweeten dev_err(&spi->dev, "error reading device id\n"); 363ec77e21bSRyan Mallon return NULL; 364ec77e21bSRyan Mallon } 365ec77e21bSRyan Mallon 3660ffe0ce3SH Hartley Sweeten id = (cmd_resp[4] << 8) | cmd_resp[5]; 367ec77e21bSRyan Mallon 368ec77e21bSRyan Mallon for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) 369ec77e21bSRyan Mallon if (sst25l_flash_info[i].device_id == id) 370ec77e21bSRyan Mallon flash_info = &sst25l_flash_info[i]; 371ec77e21bSRyan Mallon 372ec77e21bSRyan Mallon if (!flash_info) 373ec77e21bSRyan Mallon dev_err(&spi->dev, "unknown id %.4x\n", id); 374ec77e21bSRyan Mallon 375ec77e21bSRyan Mallon return flash_info; 376ec77e21bSRyan Mallon } 377ec77e21bSRyan Mallon 3786b2995b6SFabio Estevam static int __devinit sst25l_probe(struct spi_device *spi) 379ec77e21bSRyan Mallon { 380ec77e21bSRyan Mallon struct flash_info *flash_info; 381ec77e21bSRyan Mallon struct sst25l_flash *flash; 382ec77e21bSRyan Mallon struct flash_platform_data *data; 383ec77e21bSRyan Mallon int ret, i; 384ec77e21bSRyan Mallon 385ec77e21bSRyan Mallon flash_info = sst25l_match_device(spi); 386ec77e21bSRyan Mallon if (!flash_info) 387ec77e21bSRyan Mallon return -ENODEV; 388ec77e21bSRyan Mallon 389ec77e21bSRyan Mallon flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); 390ec77e21bSRyan Mallon if (!flash) 391ec77e21bSRyan Mallon return -ENOMEM; 392ec77e21bSRyan Mallon 393ec77e21bSRyan Mallon flash->spi = spi; 394ec77e21bSRyan Mallon mutex_init(&flash->lock); 395ec77e21bSRyan Mallon dev_set_drvdata(&spi->dev, flash); 396ec77e21bSRyan Mallon 397ec77e21bSRyan Mallon data = spi->dev.platform_data; 398ec77e21bSRyan Mallon if (data && data->name) 399ec77e21bSRyan Mallon flash->mtd.name = data->name; 400ec77e21bSRyan Mallon else 401ec77e21bSRyan Mallon flash->mtd.name = dev_name(&spi->dev); 402ec77e21bSRyan Mallon 403ec77e21bSRyan Mallon flash->mtd.type = MTD_NORFLASH; 404ec77e21bSRyan Mallon flash->mtd.flags = MTD_CAP_NORFLASH; 405ec77e21bSRyan Mallon flash->mtd.erasesize = flash_info->erase_size; 406ec77e21bSRyan Mallon flash->mtd.writesize = flash_info->page_size; 407ec77e21bSRyan Mallon flash->mtd.size = flash_info->page_size * flash_info->nr_pages; 408ec77e21bSRyan Mallon flash->mtd.erase = sst25l_erase; 409ec77e21bSRyan Mallon flash->mtd.read = sst25l_read; 410ec77e21bSRyan Mallon flash->mtd.write = sst25l_write; 411ec77e21bSRyan Mallon 412ec77e21bSRyan Mallon dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name, 413ec77e21bSRyan Mallon (long long)flash->mtd.size >> 10); 414ec77e21bSRyan Mallon 415ec77e21bSRyan Mallon DEBUG(MTD_DEBUG_LEVEL2, 416ec77e21bSRyan Mallon "mtd .name = %s, .size = 0x%llx (%lldMiB) " 417ec77e21bSRyan Mallon ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", 418ec77e21bSRyan Mallon flash->mtd.name, 419ec77e21bSRyan Mallon (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), 420ec77e21bSRyan Mallon flash->mtd.erasesize, flash->mtd.erasesize / 1024, 421ec77e21bSRyan Mallon flash->mtd.numeraseregions); 422ec77e21bSRyan Mallon 423ec77e21bSRyan Mallon if (mtd_has_partitions()) { 424ec77e21bSRyan Mallon struct mtd_partition *parts = NULL; 425ec77e21bSRyan Mallon int nr_parts = 0; 426ec77e21bSRyan Mallon 427ec77e21bSRyan Mallon if (mtd_has_cmdlinepart()) { 428ec77e21bSRyan Mallon static const char *part_probes[] = 429ec77e21bSRyan Mallon {"cmdlinepart", NULL}; 430ec77e21bSRyan Mallon 431ec77e21bSRyan Mallon nr_parts = parse_mtd_partitions(&flash->mtd, 432ec77e21bSRyan Mallon part_probes, 433ec77e21bSRyan Mallon &parts, 0); 434ec77e21bSRyan Mallon } 435ec77e21bSRyan Mallon 436ec77e21bSRyan Mallon if (nr_parts <= 0 && data && data->parts) { 437ec77e21bSRyan Mallon parts = data->parts; 438ec77e21bSRyan Mallon nr_parts = data->nr_parts; 439ec77e21bSRyan Mallon } 440ec77e21bSRyan Mallon 441ec77e21bSRyan Mallon if (nr_parts > 0) { 442ec77e21bSRyan Mallon for (i = 0; i < nr_parts; i++) { 443ec77e21bSRyan Mallon DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " 444ec77e21bSRyan Mallon "{.name = %s, .offset = 0x%llx, " 445ec77e21bSRyan Mallon ".size = 0x%llx (%lldKiB) }\n", 446ec77e21bSRyan Mallon i, parts[i].name, 447ec77e21bSRyan Mallon (long long)parts[i].offset, 448ec77e21bSRyan Mallon (long long)parts[i].size, 449ec77e21bSRyan Mallon (long long)(parts[i].size >> 10)); 450ec77e21bSRyan Mallon } 451ec77e21bSRyan Mallon 452ec77e21bSRyan Mallon flash->partitioned = 1; 453ec77e21bSRyan Mallon return add_mtd_partitions(&flash->mtd, 454ec77e21bSRyan Mallon parts, nr_parts); 455ec77e21bSRyan Mallon } 456ec77e21bSRyan Mallon 457a4b81ca5SDan Carpenter } else if (data && data->nr_parts) { 458ec77e21bSRyan Mallon dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", 459ec77e21bSRyan Mallon data->nr_parts, data->name); 460ec77e21bSRyan Mallon } 461ec77e21bSRyan Mallon 462ec77e21bSRyan Mallon ret = add_mtd_device(&flash->mtd); 463ec77e21bSRyan Mallon if (ret == 1) { 464ec77e21bSRyan Mallon kfree(flash); 465ec77e21bSRyan Mallon dev_set_drvdata(&spi->dev, NULL); 466ec77e21bSRyan Mallon return -ENODEV; 467ec77e21bSRyan Mallon } 468ec77e21bSRyan Mallon 469ec77e21bSRyan Mallon return 0; 470ec77e21bSRyan Mallon } 471ec77e21bSRyan Mallon 472ec77e21bSRyan Mallon static int __exit sst25l_remove(struct spi_device *spi) 473ec77e21bSRyan Mallon { 474ec77e21bSRyan Mallon struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); 475ec77e21bSRyan Mallon int ret; 476ec77e21bSRyan Mallon 477ec77e21bSRyan Mallon if (mtd_has_partitions() && flash->partitioned) 478ec77e21bSRyan Mallon ret = del_mtd_partitions(&flash->mtd); 479ec77e21bSRyan Mallon else 480ec77e21bSRyan Mallon ret = del_mtd_device(&flash->mtd); 481ec77e21bSRyan Mallon if (ret == 0) 482ec77e21bSRyan Mallon kfree(flash); 483ec77e21bSRyan Mallon return ret; 484ec77e21bSRyan Mallon } 485ec77e21bSRyan Mallon 486ec77e21bSRyan Mallon static struct spi_driver sst25l_driver = { 487ec77e21bSRyan Mallon .driver = { 488ec77e21bSRyan Mallon .name = "sst25l", 489ec77e21bSRyan Mallon .bus = &spi_bus_type, 490ec77e21bSRyan Mallon .owner = THIS_MODULE, 491ec77e21bSRyan Mallon }, 492ec77e21bSRyan Mallon .probe = sst25l_probe, 493ec77e21bSRyan Mallon .remove = __exit_p(sst25l_remove), 494ec77e21bSRyan Mallon }; 495ec77e21bSRyan Mallon 496ec77e21bSRyan Mallon static int __init sst25l_init(void) 497ec77e21bSRyan Mallon { 498ec77e21bSRyan Mallon return spi_register_driver(&sst25l_driver); 499ec77e21bSRyan Mallon } 500ec77e21bSRyan Mallon 501ec77e21bSRyan Mallon static void __exit sst25l_exit(void) 502ec77e21bSRyan Mallon { 503ec77e21bSRyan Mallon spi_unregister_driver(&sst25l_driver); 504ec77e21bSRyan Mallon } 505ec77e21bSRyan Mallon 506ec77e21bSRyan Mallon module_init(sst25l_init); 507ec77e21bSRyan Mallon module_exit(sst25l_exit); 508ec77e21bSRyan Mallon 509ec77e21bSRyan Mallon MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips"); 510ec77e21bSRyan Mallon MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, " 511ec77e21bSRyan Mallon "Ryan Mallon <ryan@bluewatersys.com>"); 512ec77e21bSRyan Mallon MODULE_LICENSE("GPL"); 513