1 /* 2 * mchp23k256.c 3 * 4 * Driver for Microchip 23k256 SPI RAM chips 5 * 6 * Copyright © 2016 Andrew Lunn <andrew@lunn.ch> 7 * 8 * This code is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 #include <linux/device.h> 14 #include <linux/module.h> 15 #include <linux/mtd/mtd.h> 16 #include <linux/mtd/partitions.h> 17 #include <linux/mutex.h> 18 #include <linux/sched.h> 19 #include <linux/sizes.h> 20 #include <linux/spi/flash.h> 21 #include <linux/spi/spi.h> 22 #include <linux/of_device.h> 23 24 #define MAX_CMD_SIZE 4 25 26 struct mchp23_caps { 27 u8 addr_width; 28 unsigned int size; 29 }; 30 31 struct mchp23k256_flash { 32 struct spi_device *spi; 33 struct mutex lock; 34 struct mtd_info mtd; 35 const struct mchp23_caps *caps; 36 }; 37 38 #define MCHP23K256_CMD_WRITE_STATUS 0x01 39 #define MCHP23K256_CMD_WRITE 0x02 40 #define MCHP23K256_CMD_READ 0x03 41 #define MCHP23K256_MODE_SEQ BIT(6) 42 43 #define to_mchp23k256_flash(x) container_of(x, struct mchp23k256_flash, mtd) 44 45 static void mchp23k256_addr2cmd(struct mchp23k256_flash *flash, 46 unsigned int addr, u8 *cmd) 47 { 48 int i; 49 50 /* 51 * Address is sent in big endian (MSB first) and we skip 52 * the first entry of the cmd array which contains the cmd 53 * opcode. 54 */ 55 for (i = flash->caps->addr_width; i > 0; i--, addr >>= 8) 56 cmd[i] = addr; 57 } 58 59 static int mchp23k256_cmdsz(struct mchp23k256_flash *flash) 60 { 61 return 1 + flash->caps->addr_width; 62 } 63 64 static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len, 65 size_t *retlen, const unsigned char *buf) 66 { 67 struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd); 68 struct spi_transfer transfer[2] = {}; 69 struct spi_message message; 70 unsigned char command[MAX_CMD_SIZE]; 71 int ret; 72 73 spi_message_init(&message); 74 75 command[0] = MCHP23K256_CMD_WRITE; 76 mchp23k256_addr2cmd(flash, to, command); 77 78 transfer[0].tx_buf = command; 79 transfer[0].len = mchp23k256_cmdsz(flash); 80 spi_message_add_tail(&transfer[0], &message); 81 82 transfer[1].tx_buf = buf; 83 transfer[1].len = len; 84 spi_message_add_tail(&transfer[1], &message); 85 86 mutex_lock(&flash->lock); 87 88 ret = spi_sync(flash->spi, &message); 89 90 mutex_unlock(&flash->lock); 91 92 if (ret) 93 return ret; 94 95 if (retlen && message.actual_length > sizeof(command)) 96 *retlen += message.actual_length - sizeof(command); 97 98 return 0; 99 } 100 101 static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, 102 size_t *retlen, unsigned char *buf) 103 { 104 struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd); 105 struct spi_transfer transfer[2] = {}; 106 struct spi_message message; 107 unsigned char command[MAX_CMD_SIZE]; 108 int ret; 109 110 spi_message_init(&message); 111 112 memset(&transfer, 0, sizeof(transfer)); 113 command[0] = MCHP23K256_CMD_READ; 114 mchp23k256_addr2cmd(flash, from, command); 115 116 transfer[0].tx_buf = command; 117 transfer[0].len = mchp23k256_cmdsz(flash); 118 spi_message_add_tail(&transfer[0], &message); 119 120 transfer[1].rx_buf = buf; 121 transfer[1].len = len; 122 spi_message_add_tail(&transfer[1], &message); 123 124 mutex_lock(&flash->lock); 125 126 ret = spi_sync(flash->spi, &message); 127 128 mutex_unlock(&flash->lock); 129 130 if (ret) 131 return ret; 132 133 if (retlen && message.actual_length > sizeof(command)) 134 *retlen += message.actual_length - sizeof(command); 135 136 return 0; 137 } 138 139 /* 140 * Set the device into sequential mode. This allows read/writes to the 141 * entire SRAM in a single operation 142 */ 143 static int mchp23k256_set_mode(struct spi_device *spi) 144 { 145 struct spi_transfer transfer = {}; 146 struct spi_message message; 147 unsigned char command[2]; 148 149 spi_message_init(&message); 150 151 command[0] = MCHP23K256_CMD_WRITE_STATUS; 152 command[1] = MCHP23K256_MODE_SEQ; 153 154 transfer.tx_buf = command; 155 transfer.len = sizeof(command); 156 spi_message_add_tail(&transfer, &message); 157 158 return spi_sync(spi, &message); 159 } 160 161 static const struct mchp23_caps mchp23k256_caps = { 162 .size = SZ_32K, 163 .addr_width = 2, 164 }; 165 166 static const struct mchp23_caps mchp23lcv1024_caps = { 167 .size = SZ_128K, 168 .addr_width = 3, 169 }; 170 171 static int mchp23k256_probe(struct spi_device *spi) 172 { 173 struct mchp23k256_flash *flash; 174 struct flash_platform_data *data; 175 int err; 176 177 flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); 178 if (!flash) 179 return -ENOMEM; 180 181 flash->spi = spi; 182 mutex_init(&flash->lock); 183 spi_set_drvdata(spi, flash); 184 185 err = mchp23k256_set_mode(spi); 186 if (err) 187 return err; 188 189 data = dev_get_platdata(&spi->dev); 190 191 flash->caps = of_device_get_match_data(&spi->dev); 192 if (!flash->caps) 193 flash->caps = &mchp23k256_caps; 194 195 mtd_set_of_node(&flash->mtd, spi->dev.of_node); 196 flash->mtd.dev.parent = &spi->dev; 197 flash->mtd.type = MTD_RAM; 198 flash->mtd.flags = MTD_CAP_RAM; 199 flash->mtd.writesize = 1; 200 flash->mtd.size = flash->caps->size; 201 flash->mtd._read = mchp23k256_read; 202 flash->mtd._write = mchp23k256_write; 203 204 err = mtd_device_register(&flash->mtd, data ? data->parts : NULL, 205 data ? data->nr_parts : 0); 206 if (err) 207 return err; 208 209 return 0; 210 } 211 212 static int mchp23k256_remove(struct spi_device *spi) 213 { 214 struct mchp23k256_flash *flash = spi_get_drvdata(spi); 215 216 return mtd_device_unregister(&flash->mtd); 217 } 218 219 static const struct of_device_id mchp23k256_of_table[] = { 220 { 221 .compatible = "microchip,mchp23k256", 222 .data = &mchp23k256_caps, 223 }, 224 { 225 .compatible = "microchip,mchp23lcv1024", 226 .data = &mchp23lcv1024_caps, 227 }, 228 {} 229 }; 230 MODULE_DEVICE_TABLE(of, mchp23k256_of_table); 231 232 static struct spi_driver mchp23k256_driver = { 233 .driver = { 234 .name = "mchp23k256", 235 .of_match_table = of_match_ptr(mchp23k256_of_table), 236 }, 237 .probe = mchp23k256_probe, 238 .remove = mchp23k256_remove, 239 }; 240 241 module_spi_driver(mchp23k256_driver); 242 243 MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips"); 244 MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>"); 245 MODULE_LICENSE("GPL v2"); 246 MODULE_ALIAS("spi:mchp23k256"); 247