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 72 spi_message_init(&message); 73 74 command[0] = MCHP23K256_CMD_WRITE; 75 mchp23k256_addr2cmd(flash, to, command); 76 77 transfer[0].tx_buf = command; 78 transfer[0].len = mchp23k256_cmdsz(flash); 79 spi_message_add_tail(&transfer[0], &message); 80 81 transfer[1].tx_buf = buf; 82 transfer[1].len = len; 83 spi_message_add_tail(&transfer[1], &message); 84 85 mutex_lock(&flash->lock); 86 87 spi_sync(flash->spi, &message); 88 89 if (retlen && message.actual_length > sizeof(command)) 90 *retlen += message.actual_length - sizeof(command); 91 92 mutex_unlock(&flash->lock); 93 return 0; 94 } 95 96 static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, 97 size_t *retlen, unsigned char *buf) 98 { 99 struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd); 100 struct spi_transfer transfer[2] = {}; 101 struct spi_message message; 102 unsigned char command[MAX_CMD_SIZE]; 103 104 spi_message_init(&message); 105 106 memset(&transfer, 0, sizeof(transfer)); 107 command[0] = MCHP23K256_CMD_READ; 108 mchp23k256_addr2cmd(flash, from, command); 109 110 transfer[0].tx_buf = command; 111 transfer[0].len = mchp23k256_cmdsz(flash); 112 spi_message_add_tail(&transfer[0], &message); 113 114 transfer[1].rx_buf = buf; 115 transfer[1].len = len; 116 spi_message_add_tail(&transfer[1], &message); 117 118 mutex_lock(&flash->lock); 119 120 spi_sync(flash->spi, &message); 121 122 if (retlen && message.actual_length > sizeof(command)) 123 *retlen += message.actual_length - sizeof(command); 124 125 mutex_unlock(&flash->lock); 126 return 0; 127 } 128 129 /* 130 * Set the device into sequential mode. This allows read/writes to the 131 * entire SRAM in a single operation 132 */ 133 static int mchp23k256_set_mode(struct spi_device *spi) 134 { 135 struct spi_transfer transfer = {}; 136 struct spi_message message; 137 unsigned char command[2]; 138 139 spi_message_init(&message); 140 141 command[0] = MCHP23K256_CMD_WRITE_STATUS; 142 command[1] = MCHP23K256_MODE_SEQ; 143 144 transfer.tx_buf = command; 145 transfer.len = sizeof(command); 146 spi_message_add_tail(&transfer, &message); 147 148 return spi_sync(spi, &message); 149 } 150 151 static const struct mchp23_caps mchp23k256_caps = { 152 .size = SZ_32K, 153 .addr_width = 2, 154 }; 155 156 static const struct mchp23_caps mchp23lcv1024_caps = { 157 .size = SZ_128K, 158 .addr_width = 3, 159 }; 160 161 static int mchp23k256_probe(struct spi_device *spi) 162 { 163 struct mchp23k256_flash *flash; 164 struct flash_platform_data *data; 165 int err; 166 167 flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); 168 if (!flash) 169 return -ENOMEM; 170 171 flash->spi = spi; 172 mutex_init(&flash->lock); 173 spi_set_drvdata(spi, flash); 174 175 err = mchp23k256_set_mode(spi); 176 if (err) 177 return err; 178 179 data = dev_get_platdata(&spi->dev); 180 181 flash->caps = of_device_get_match_data(&spi->dev); 182 if (!flash->caps) 183 flash->caps = &mchp23k256_caps; 184 185 mtd_set_of_node(&flash->mtd, spi->dev.of_node); 186 flash->mtd.dev.parent = &spi->dev; 187 flash->mtd.type = MTD_RAM; 188 flash->mtd.flags = MTD_CAP_RAM; 189 flash->mtd.writesize = 1; 190 flash->mtd.size = flash->caps->size; 191 flash->mtd._read = mchp23k256_read; 192 flash->mtd._write = mchp23k256_write; 193 194 err = mtd_device_register(&flash->mtd, data ? data->parts : NULL, 195 data ? data->nr_parts : 0); 196 if (err) 197 return err; 198 199 return 0; 200 } 201 202 static int mchp23k256_remove(struct spi_device *spi) 203 { 204 struct mchp23k256_flash *flash = spi_get_drvdata(spi); 205 206 return mtd_device_unregister(&flash->mtd); 207 } 208 209 static const struct of_device_id mchp23k256_of_table[] = { 210 { 211 .compatible = "microchip,mchp23k256", 212 .data = &mchp23k256_caps, 213 }, 214 { 215 .compatible = "microchip,mchp23lcv1024", 216 .data = &mchp23lcv1024_caps, 217 }, 218 {} 219 }; 220 MODULE_DEVICE_TABLE(of, mchp23k256_of_table); 221 222 static struct spi_driver mchp23k256_driver = { 223 .driver = { 224 .name = "mchp23k256", 225 .of_match_table = of_match_ptr(mchp23k256_of_table), 226 }, 227 .probe = mchp23k256_probe, 228 .remove = mchp23k256_remove, 229 }; 230 231 module_spi_driver(mchp23k256_driver); 232 233 MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips"); 234 MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>"); 235 MODULE_LICENSE("GPL v2"); 236 MODULE_ALIAS("spi:mchp23k256"); 237