blob: 2542f5b8b63fc4d8157d66692e8e4c1a66747f2e [file] [log] [blame]
Andrew Lunn5dc17fa2017-04-12 17:34:31 +02001/*
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>
Chris Packham4db4d35e2017-05-25 11:49:12 +120022#include <linux/of_device.h>
Andrew Lunn5dc17fa2017-04-12 17:34:31 +020023
24struct mchp23k256_flash {
25 struct spi_device *spi;
26 struct mutex lock;
27 struct mtd_info mtd;
28};
29
30#define MCHP23K256_CMD_WRITE_STATUS 0x01
31#define MCHP23K256_CMD_WRITE 0x02
32#define MCHP23K256_CMD_READ 0x03
33#define MCHP23K256_MODE_SEQ BIT(6)
34
35#define to_mchp23k256_flash(x) container_of(x, struct mchp23k256_flash, mtd)
36
37static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len,
38 size_t *retlen, const unsigned char *buf)
39{
40 struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
41 struct spi_transfer transfer[2] = {};
42 struct spi_message message;
43 unsigned char command[3];
44
45 spi_message_init(&message);
46
47 command[0] = MCHP23K256_CMD_WRITE;
48 command[1] = to >> 8;
49 command[2] = to;
50
51 transfer[0].tx_buf = command;
52 transfer[0].len = sizeof(command);
53 spi_message_add_tail(&transfer[0], &message);
54
55 transfer[1].tx_buf = buf;
56 transfer[1].len = len;
57 spi_message_add_tail(&transfer[1], &message);
58
59 mutex_lock(&flash->lock);
60
61 spi_sync(flash->spi, &message);
62
63 if (retlen && message.actual_length > sizeof(command))
64 *retlen += message.actual_length - sizeof(command);
65
66 mutex_unlock(&flash->lock);
67 return 0;
68}
69
70static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len,
71 size_t *retlen, unsigned char *buf)
72{
73 struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
74 struct spi_transfer transfer[2] = {};
75 struct spi_message message;
76 unsigned char command[3];
77
78 spi_message_init(&message);
79
80 memset(&transfer, 0, sizeof(transfer));
81 command[0] = MCHP23K256_CMD_READ;
82 command[1] = from >> 8;
83 command[2] = from;
84
85 transfer[0].tx_buf = command;
86 transfer[0].len = sizeof(command);
87 spi_message_add_tail(&transfer[0], &message);
88
89 transfer[1].rx_buf = buf;
90 transfer[1].len = len;
91 spi_message_add_tail(&transfer[1], &message);
92
93 mutex_lock(&flash->lock);
94
95 spi_sync(flash->spi, &message);
96
97 if (retlen && message.actual_length > sizeof(command))
98 *retlen += message.actual_length - sizeof(command);
99
100 mutex_unlock(&flash->lock);
101 return 0;
102}
103
104/*
105 * Set the device into sequential mode. This allows read/writes to the
106 * entire SRAM in a single operation
107 */
108static int mchp23k256_set_mode(struct spi_device *spi)
109{
110 struct spi_transfer transfer = {};
111 struct spi_message message;
112 unsigned char command[2];
113
114 spi_message_init(&message);
115
116 command[0] = MCHP23K256_CMD_WRITE_STATUS;
117 command[1] = MCHP23K256_MODE_SEQ;
118
119 transfer.tx_buf = command;
120 transfer.len = sizeof(command);
121 spi_message_add_tail(&transfer, &message);
122
123 return spi_sync(spi, &message);
124}
125
126static int mchp23k256_probe(struct spi_device *spi)
127{
128 struct mchp23k256_flash *flash;
129 struct flash_platform_data *data;
130 int err;
131
132 flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
133 if (!flash)
134 return -ENOMEM;
135
136 flash->spi = spi;
137 mutex_init(&flash->lock);
138 spi_set_drvdata(spi, flash);
139
140 err = mchp23k256_set_mode(spi);
141 if (err)
142 return err;
143
144 data = dev_get_platdata(&spi->dev);
145
146 flash->mtd.dev.parent = &spi->dev;
147 flash->mtd.type = MTD_RAM;
148 flash->mtd.flags = MTD_CAP_RAM;
149 flash->mtd.writesize = 1;
150 flash->mtd.size = SZ_32K;
151 flash->mtd._read = mchp23k256_read;
152 flash->mtd._write = mchp23k256_write;
153
Chris Packham44225c92017-05-25 11:49:13 +1200154 err = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
155 data ? data->nr_parts : 0);
Andrew Lunn5dc17fa2017-04-12 17:34:31 +0200156 if (err)
157 return err;
158
159 return 0;
160}
161
162static int mchp23k256_remove(struct spi_device *spi)
163{
164 struct mchp23k256_flash *flash = spi_get_drvdata(spi);
165
166 return mtd_device_unregister(&flash->mtd);
167}
168
Chris Packham4db4d35e2017-05-25 11:49:12 +1200169static const struct of_device_id mchp23k256_of_table[] = {
170 { .compatible = "microchip,mchp23k256" },
171 {}
172};
173MODULE_DEVICE_TABLE(of, mchp23k256_of_table);
174
Andrew Lunn5dc17fa2017-04-12 17:34:31 +0200175static struct spi_driver mchp23k256_driver = {
176 .driver = {
177 .name = "mchp23k256",
Chris Packham4db4d35e2017-05-25 11:49:12 +1200178 .of_match_table = of_match_ptr(mchp23k256_of_table),
Andrew Lunn5dc17fa2017-04-12 17:34:31 +0200179 },
180 .probe = mchp23k256_probe,
181 .remove = mchp23k256_remove,
182};
183
184module_spi_driver(mchp23k256_driver);
185
186MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips");
187MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>");
188MODULE_LICENSE("GPL v2");
189MODULE_ALIAS("spi:mchp23k256");