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