Commit | Line | Data |
---|---|---|
8d36fe1e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5dc17fa6 AL |
2 | /* |
3 | * mchp23k256.c | |
4 | * | |
5 | * Driver for Microchip 23k256 SPI RAM chips | |
6 | * | |
7 | * Copyright © 2016 Andrew Lunn <andrew@lunn.ch> | |
5dc17fa6 AL |
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> | |
c2fc6b69 | 18 | #include <linux/of.h> |
5dc17fa6 | 19 | |
4379075a CP |
20 | #define MAX_CMD_SIZE 4 |
21 | ||
22 | struct mchp23_caps { | |
23 | u8 addr_width; | |
24 | unsigned int size; | |
25 | }; | |
26 | ||
5dc17fa6 AL |
27 | struct mchp23k256_flash { |
28 | struct spi_device *spi; | |
29 | struct mutex lock; | |
30 | struct mtd_info mtd; | |
4379075a | 31 | const struct mchp23_caps *caps; |
5dc17fa6 AL |
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 | ||
4379075a CP |
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 | ||
5dc17fa6 AL |
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; | |
4379075a | 66 | unsigned char command[MAX_CMD_SIZE]; |
14f89e08 | 67 | int ret, cmd_len; |
5dc17fa6 AL |
68 | |
69 | spi_message_init(&message); | |
70 | ||
14f89e08 AD |
71 | cmd_len = mchp23k256_cmdsz(flash); |
72 | ||
5dc17fa6 | 73 | command[0] = MCHP23K256_CMD_WRITE; |
4379075a | 74 | mchp23k256_addr2cmd(flash, to, command); |
5dc17fa6 AL |
75 | |
76 | transfer[0].tx_buf = command; | |
14f89e08 | 77 | transfer[0].len = cmd_len; |
5dc17fa6 AL |
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 | ||
db601f3a AB |
86 | ret = spi_sync(flash->spi, &message); |
87 | ||
88 | mutex_unlock(&flash->lock); | |
89 | ||
90 | if (ret) | |
91 | return ret; | |
5dc17fa6 | 92 | |
14f89e08 AD |
93 | if (retlen && message.actual_length > cmd_len) |
94 | *retlen += message.actual_length - cmd_len; | |
5dc17fa6 | 95 | |
5dc17fa6 AL |
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; | |
4379075a | 105 | unsigned char command[MAX_CMD_SIZE]; |
14f89e08 | 106 | int ret, cmd_len; |
5dc17fa6 AL |
107 | |
108 | spi_message_init(&message); | |
109 | ||
14f89e08 AD |
110 | cmd_len = mchp23k256_cmdsz(flash); |
111 | ||
5dc17fa6 AL |
112 | memset(&transfer, 0, sizeof(transfer)); |
113 | command[0] = MCHP23K256_CMD_READ; | |
4379075a | 114 | mchp23k256_addr2cmd(flash, from, command); |
5dc17fa6 AL |
115 | |
116 | transfer[0].tx_buf = command; | |
14f89e08 | 117 | transfer[0].len = cmd_len; |
5dc17fa6 AL |
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 | ||
db601f3a AB |
126 | ret = spi_sync(flash->spi, &message); |
127 | ||
128 | mutex_unlock(&flash->lock); | |
129 | ||
130 | if (ret) | |
131 | return ret; | |
5dc17fa6 | 132 | |
14f89e08 AD |
133 | if (retlen && message.actual_length > cmd_len) |
134 | *retlen += message.actual_length - cmd_len; | |
5dc17fa6 | 135 | |
5dc17fa6 AL |
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 | ||
4379075a CP |
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 | ||
5dc17fa6 AL |
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 | ||
4379075a CP |
191 | flash->caps = of_device_get_match_data(&spi->dev); |
192 | if (!flash->caps) | |
193 | flash->caps = &mchp23k256_caps; | |
194 | ||
2f071ff1 | 195 | mtd_set_of_node(&flash->mtd, spi->dev.of_node); |
5dc17fa6 AL |
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; | |
4379075a | 200 | flash->mtd.size = flash->caps->size; |
5dc17fa6 AL |
201 | flash->mtd._read = mchp23k256_read; |
202 | flash->mtd._write = mchp23k256_write; | |
203 | ||
44225c9c CP |
204 | err = mtd_device_register(&flash->mtd, data ? data->parts : NULL, |
205 | data ? data->nr_parts : 0); | |
5dc17fa6 AL |
206 | if (err) |
207 | return err; | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
a0386bba | 212 | static void mchp23k256_remove(struct spi_device *spi) |
5dc17fa6 AL |
213 | { |
214 | struct mchp23k256_flash *flash = spi_get_drvdata(spi); | |
215 | ||
367cefba | 216 | WARN_ON(mtd_device_unregister(&flash->mtd)); |
5dc17fa6 AL |
217 | } |
218 | ||
4db4d35e | 219 | static const struct of_device_id mchp23k256_of_table[] = { |
4379075a CP |
220 | { |
221 | .compatible = "microchip,mchp23k256", | |
222 | .data = &mchp23k256_caps, | |
223 | }, | |
224 | { | |
225 | .compatible = "microchip,mchp23lcv1024", | |
226 | .data = &mchp23lcv1024_caps, | |
227 | }, | |
4db4d35e CP |
228 | {} |
229 | }; | |
230 | MODULE_DEVICE_TABLE(of, mchp23k256_of_table); | |
231 | ||
bc7ee2e3 MB |
232 | static 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 | }; | |
243 | MODULE_DEVICE_TABLE(spi, mchp23k256_spi_ids); | |
244 | ||
5dc17fa6 AL |
245 | static struct spi_driver mchp23k256_driver = { |
246 | .driver = { | |
247 | .name = "mchp23k256", | |
3f26d1bf | 248 | .of_match_table = mchp23k256_of_table, |
5dc17fa6 AL |
249 | }, |
250 | .probe = mchp23k256_probe, | |
251 | .remove = mchp23k256_remove, | |
bc7ee2e3 | 252 | .id_table = mchp23k256_spi_ids, |
5dc17fa6 AL |
253 | }; |
254 | ||
255 | module_spi_driver(mchp23k256_driver); | |
256 | ||
257 | MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips"); | |
258 | MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>"); | |
259 | MODULE_LICENSE("GPL v2"); |