Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * mchp23k256.c
0004  *
0005  * Driver for Microchip 23k256 SPI RAM chips
0006  *
0007  * Copyright © 2016 Andrew Lunn <andrew@lunn.ch>
0008  */
0009 #include <linux/device.h>
0010 #include <linux/module.h>
0011 #include <linux/mtd/mtd.h>
0012 #include <linux/mtd/partitions.h>
0013 #include <linux/mutex.h>
0014 #include <linux/sched.h>
0015 #include <linux/sizes.h>
0016 #include <linux/spi/flash.h>
0017 #include <linux/spi/spi.h>
0018 #include <linux/of_device.h>
0019 
0020 #define MAX_CMD_SIZE        4
0021 
0022 struct mchp23_caps {
0023     u8 addr_width;
0024     unsigned int size;
0025 };
0026 
0027 struct mchp23k256_flash {
0028     struct spi_device   *spi;
0029     struct mutex        lock;
0030     struct mtd_info     mtd;
0031     const struct mchp23_caps    *caps;
0032 };
0033 
0034 #define MCHP23K256_CMD_WRITE_STATUS 0x01
0035 #define MCHP23K256_CMD_WRITE        0x02
0036 #define MCHP23K256_CMD_READ     0x03
0037 #define MCHP23K256_MODE_SEQ     BIT(6)
0038 
0039 #define to_mchp23k256_flash(x) container_of(x, struct mchp23k256_flash, mtd)
0040 
0041 static void mchp23k256_addr2cmd(struct mchp23k256_flash *flash,
0042                 unsigned int addr, u8 *cmd)
0043 {
0044     int i;
0045 
0046     /*
0047      * Address is sent in big endian (MSB first) and we skip
0048      * the first entry of the cmd array which contains the cmd
0049      * opcode.
0050      */
0051     for (i = flash->caps->addr_width; i > 0; i--, addr >>= 8)
0052         cmd[i] = addr;
0053 }
0054 
0055 static int mchp23k256_cmdsz(struct mchp23k256_flash *flash)
0056 {
0057     return 1 + flash->caps->addr_width;
0058 }
0059 
0060 static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len,
0061                 size_t *retlen, const unsigned char *buf)
0062 {
0063     struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
0064     struct spi_transfer transfer[2] = {};
0065     struct spi_message message;
0066     unsigned char command[MAX_CMD_SIZE];
0067     int ret, cmd_len;
0068 
0069     spi_message_init(&message);
0070 
0071     cmd_len = mchp23k256_cmdsz(flash);
0072 
0073     command[0] = MCHP23K256_CMD_WRITE;
0074     mchp23k256_addr2cmd(flash, to, command);
0075 
0076     transfer[0].tx_buf = command;
0077     transfer[0].len = cmd_len;
0078     spi_message_add_tail(&transfer[0], &message);
0079 
0080     transfer[1].tx_buf = buf;
0081     transfer[1].len = len;
0082     spi_message_add_tail(&transfer[1], &message);
0083 
0084     mutex_lock(&flash->lock);
0085 
0086     ret = spi_sync(flash->spi, &message);
0087 
0088     mutex_unlock(&flash->lock);
0089 
0090     if (ret)
0091         return ret;
0092 
0093     if (retlen && message.actual_length > cmd_len)
0094         *retlen += message.actual_length - cmd_len;
0095 
0096     return 0;
0097 }
0098 
0099 static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len,
0100                size_t *retlen, unsigned char *buf)
0101 {
0102     struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
0103     struct spi_transfer transfer[2] = {};
0104     struct spi_message message;
0105     unsigned char command[MAX_CMD_SIZE];
0106     int ret, cmd_len;
0107 
0108     spi_message_init(&message);
0109 
0110     cmd_len = mchp23k256_cmdsz(flash);
0111 
0112     memset(&transfer, 0, sizeof(transfer));
0113     command[0] = MCHP23K256_CMD_READ;
0114     mchp23k256_addr2cmd(flash, from, command);
0115 
0116     transfer[0].tx_buf = command;
0117     transfer[0].len = cmd_len;
0118     spi_message_add_tail(&transfer[0], &message);
0119 
0120     transfer[1].rx_buf = buf;
0121     transfer[1].len = len;
0122     spi_message_add_tail(&transfer[1], &message);
0123 
0124     mutex_lock(&flash->lock);
0125 
0126     ret = spi_sync(flash->spi, &message);
0127 
0128     mutex_unlock(&flash->lock);
0129 
0130     if (ret)
0131         return ret;
0132 
0133     if (retlen && message.actual_length > cmd_len)
0134         *retlen += message.actual_length - cmd_len;
0135 
0136     return 0;
0137 }
0138 
0139 /*
0140  * Set the device into sequential mode. This allows read/writes to the
0141  * entire SRAM in a single operation
0142  */
0143 static int mchp23k256_set_mode(struct spi_device *spi)
0144 {
0145     struct spi_transfer transfer = {};
0146     struct spi_message message;
0147     unsigned char command[2];
0148 
0149     spi_message_init(&message);
0150 
0151     command[0] = MCHP23K256_CMD_WRITE_STATUS;
0152     command[1] = MCHP23K256_MODE_SEQ;
0153 
0154     transfer.tx_buf = command;
0155     transfer.len = sizeof(command);
0156     spi_message_add_tail(&transfer, &message);
0157 
0158     return spi_sync(spi, &message);
0159 }
0160 
0161 static const struct mchp23_caps mchp23k256_caps = {
0162     .size = SZ_32K,
0163     .addr_width = 2,
0164 };
0165 
0166 static const struct mchp23_caps mchp23lcv1024_caps = {
0167     .size = SZ_128K,
0168     .addr_width = 3,
0169 };
0170 
0171 static int mchp23k256_probe(struct spi_device *spi)
0172 {
0173     struct mchp23k256_flash *flash;
0174     struct flash_platform_data *data;
0175     int err;
0176 
0177     flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
0178     if (!flash)
0179         return -ENOMEM;
0180 
0181     flash->spi = spi;
0182     mutex_init(&flash->lock);
0183     spi_set_drvdata(spi, flash);
0184 
0185     err = mchp23k256_set_mode(spi);
0186     if (err)
0187         return err;
0188 
0189     data = dev_get_platdata(&spi->dev);
0190 
0191     flash->caps = of_device_get_match_data(&spi->dev);
0192     if (!flash->caps)
0193         flash->caps = &mchp23k256_caps;
0194 
0195     mtd_set_of_node(&flash->mtd, spi->dev.of_node);
0196     flash->mtd.dev.parent   = &spi->dev;
0197     flash->mtd.type     = MTD_RAM;
0198     flash->mtd.flags    = MTD_CAP_RAM;
0199     flash->mtd.writesize    = 1;
0200     flash->mtd.size     = flash->caps->size;
0201     flash->mtd._read    = mchp23k256_read;
0202     flash->mtd._write   = mchp23k256_write;
0203 
0204     err = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
0205                   data ? data->nr_parts : 0);
0206     if (err)
0207         return err;
0208 
0209     return 0;
0210 }
0211 
0212 static void mchp23k256_remove(struct spi_device *spi)
0213 {
0214     struct mchp23k256_flash *flash = spi_get_drvdata(spi);
0215 
0216     WARN_ON(mtd_device_unregister(&flash->mtd));
0217 }
0218 
0219 static const struct of_device_id mchp23k256_of_table[] = {
0220     {
0221         .compatible = "microchip,mchp23k256",
0222         .data = &mchp23k256_caps,
0223     },
0224     {
0225         .compatible = "microchip,mchp23lcv1024",
0226         .data = &mchp23lcv1024_caps,
0227     },
0228     {}
0229 };
0230 MODULE_DEVICE_TABLE(of, mchp23k256_of_table);
0231 
0232 static const struct spi_device_id mchp23k256_spi_ids[] = {
0233     {
0234         .name = "mchp23k256",
0235         .driver_data = (kernel_ulong_t)&mchp23k256_caps,
0236     },
0237     {
0238         .name = "mchp23lcv1024",
0239         .driver_data = (kernel_ulong_t)&mchp23lcv1024_caps,
0240     },
0241     {}
0242 };
0243 MODULE_DEVICE_TABLE(spi, mchp23k256_spi_ids);
0244 
0245 static struct spi_driver mchp23k256_driver = {
0246     .driver = {
0247         .name   = "mchp23k256",
0248         .of_match_table = mchp23k256_of_table,
0249     },
0250     .probe      = mchp23k256_probe,
0251     .remove     = mchp23k256_remove,
0252     .id_table   = mchp23k256_spi_ids,
0253 };
0254 
0255 module_spi_driver(mchp23k256_driver);
0256 
0257 MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips");
0258 MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>");
0259 MODULE_LICENSE("GPL v2");
0260 MODULE_ALIAS("spi:mchp23k256");