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, cmd_len; 68 69 spi_message_init(&message); 70 71 cmd_len = mchp23k256_cmdsz(flash); 72 73 command[0] = MCHP23K256_CMD_WRITE; 74 mchp23k256_addr2cmd(flash, to, command); 75 76 transfer[0].tx_buf = command; 77 transfer[0].len = cmd_len; 78 spi_message_add_tail(&transfer[0], &message); 79 80 transfer[1].tx_buf = buf; 81 transfer[1].len = len; 82 spi_message_add_tail(&transfer[1], &message); 83 84 mutex_lock(&flash->lock); 85 86 ret = spi_sync(flash->spi, &message); 87 88 mutex_unlock(&flash->lock); 89 90 if (ret) 91 return ret; 92 93 if (retlen && message.actual_length > cmd_len) 94 *retlen += message.actual_length - cmd_len; 95 96 return 0; 97 } 98 99 static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, 100 size_t *retlen, unsigned char *buf) 101 { 102 struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd); 103 struct spi_transfer transfer[2] = {}; 104 struct spi_message message; 105 unsigned char command[MAX_CMD_SIZE]; 106 int ret, cmd_len; 107 108 spi_message_init(&message); 109 110 cmd_len = mchp23k256_cmdsz(flash); 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 = cmd_len; 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 > cmd_len) 134 *retlen += message.actual_length - cmd_len; 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