mchp23k256.c (5917B)
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 22struct mchp23_caps { 23 u8 addr_width; 24 unsigned int size; 25}; 26 27struct 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 41static 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 55static int mchp23k256_cmdsz(struct mchp23k256_flash *flash) 56{ 57 return 1 + flash->caps->addr_width; 58} 59 60static 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 99static 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 */ 143static 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 161static const struct mchp23_caps mchp23k256_caps = { 162 .size = SZ_32K, 163 .addr_width = 2, 164}; 165 166static const struct mchp23_caps mchp23lcv1024_caps = { 167 .size = SZ_128K, 168 .addr_width = 3, 169}; 170 171static 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 212static void mchp23k256_remove(struct spi_device *spi) 213{ 214 struct mchp23k256_flash *flash = spi_get_drvdata(spi); 215 216 WARN_ON(mtd_device_unregister(&flash->mtd)); 217} 218 219static 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}; 230MODULE_DEVICE_TABLE(of, mchp23k256_of_table); 231 232static const struct spi_device_id mchp23k256_spi_ids[] = { 233 { 234 .name = "mchp23k256", 235 .driver_data = (kernel_ulong_t)&mchp23k256_caps, 236 }, 237 { 238 .name = "mchp23lcv1024", 239 .driver_data = (kernel_ulong_t)&mchp23lcv1024_caps, 240 }, 241 {} 242}; 243MODULE_DEVICE_TABLE(spi, mchp23k256_spi_ids); 244 245static struct spi_driver mchp23k256_driver = { 246 .driver = { 247 .name = "mchp23k256", 248 .of_match_table = mchp23k256_of_table, 249 }, 250 .probe = mchp23k256_probe, 251 .remove = mchp23k256_remove, 252 .id_table = mchp23k256_spi_ids, 253}; 254 255module_spi_driver(mchp23k256_driver); 256 257MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips"); 258MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>"); 259MODULE_LICENSE("GPL v2"); 260MODULE_ALIAS("spi:mchp23k256");