385 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for Microchip 48L640 64 Kb SPI Serial EERAM4 *5 * Copyright Heiko Schocher <hs@denx.de>6 *7 * datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20006055B.pdf8 *9 * we set continuous mode but reading/writing more bytes than10 * pagesize seems to bring chip into state where readden values11 * 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 27struct mchp48_caps {28 unsigned int size;29 unsigned int page_size;30};31 32struct 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 0x0640#define MCHP48L640_CMD_WRDI 0x0441#define MCHP48L640_CMD_WRITE 0x0242#define MCHP48L640_CMD_READ 0x0343#define MCHP48L640_CMD_WRSR 0x0144#define MCHP48L640_CMD_RDSR 0x0545 46#define MCHP48L640_STATUS_RDY 0x0147#define MCHP48L640_STATUS_WEL 0x0248#define MCHP48L640_STATUS_BP0 0x0449#define MCHP48L640_STATUS_BP1 0x0850#define MCHP48L640_STATUS_SWM 0x1051#define MCHP48L640_STATUS_PRO 0x2052#define MCHP48L640_STATUS_ASE 0x4053 54#define MCHP48L640_TIMEOUT 10055 56#define MAX_CMD_SIZE 0x1057 58#define to_mchp48l640_flash(x) container_of(x, struct mchp48l640_flash, mtd)59 60static 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 69static 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(flash->spi, &cmd[0], 1, &cmd[1], 1);78 mutex_unlock(&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 86static 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);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(1000, 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 115static 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 else123 cmd[0] = MCHP48L640_CMD_WRDI;124 125 mutex_lock(&flash->lock);126 ret = spi_write(flash->spi, cmd, 1);127 mutex_unlock(&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, true);137 138 return ret;139}140 141static int mchp48l640_set_mode(struct mchp48l640_flash *flash)142{143 unsigned char cmd[2];144 int ret;145 146 ret = mchp48l640_write_prepare(flash, 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(flash->spi, cmd, 2);155 mutex_unlock(&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, true);160}161 162static int mchp48l640_wait_rdy(struct mchp48l640_flash *flash)163{164 return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_RDY, false);165};166 167static 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((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, true);184 if (ret)185 goto fail;186 187 mutex_lock(&flash->lock);188 cmdlen = mchp48l640_mkcmd(flash, MCHP48L640_CMD_WRITE, to, cmd);189 memcpy(&cmd[cmdlen], buf, len);190 ret = spi_write(flash->spi, cmd, cmdlen + len);191 mutex_unlock(&flash->lock);192 if (!ret)193 *retlen += len;194 else195 goto fail;196 197 ret = mchp48l640_waitforbit(flash, MCHP48L640_STATUS_WEL, false);198 if (ret)199 goto fail;200 201 kfree(cmd);202 return 0;203fail:204 kfree(cmd);205 dev_err(&flash->spi->dev, "write fail with: %d", ret);206 return ret;207};208 209static 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 size221 * 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, woff, ws, retlen, &buf[wlen]);226 if (ret)227 return ret;228 wlen += ws;229 woff += ws;230 }231 232 return 0;233}234 235static 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((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, from, cmd);253 ret = spi_write_then_read(flash->spi, cmd, cmdlen, buf, len);254 mutex_unlock(&flash->lock);255 if (!ret)256 *retlen += len;257 258 kfree(cmd);259 return ret;260 261fail:262 kfree(cmd);263 dev_err(&flash->spi->dev, "read fail with: %d", ret);264 return ret;265}266 267static 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 size279 * 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, woff, ws, retlen, &buf[wlen]);284 if (ret)285 return ret;286 wlen += ws;287 woff += ws;288 }289 290 return 0;291};292 293static const struct mchp48_caps mchp48l640_caps = {294 .size = SZ_8K,295 .page_size = 32,296};297 298static 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(&spi->dev, 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, flash);312 313 err = mchp48l640_read_status(flash, &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(&spi->dev);322 323 flash->caps = of_device_get_match_data(&spi->dev);324 if (!flash->caps)325 flash->caps = &mchp48l640_caps;326 327 mtd_set_of_node(&flash->mtd, 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 344static 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 351static const struct of_device_id mchp48l640_of_table[] = {352 {353 .compatible = "microchip,48l640",354 .data = &mchp48l640_caps,355 },356 {}357};358MODULE_DEVICE_TABLE(of, mchp48l640_of_table);359 360static const struct spi_device_id mchp48l640_spi_ids[] = {361 {362 .name = "48l640",363 .driver_data = (kernel_ulong_t)&mchp48l640_caps,364 },365 {}366};367MODULE_DEVICE_TABLE(spi, mchp48l640_spi_ids);368 369static 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 379module_spi_driver(mchp48l640_driver);380 381MODULE_DESCRIPTION("MTD SPI driver for Microchip 48l640 EERAM chips");382MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");383MODULE_LICENSE("GPL v2");384MODULE_ALIAS("spi:mchp48l640");385