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