File Index
Files and directories
Sign in
FILE

mchp48l640.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/devices/mchp48l640.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Driver for Microchip 48L640 64 Kb SPI Serial EERAM
  4 *
  5 * Copyright Heiko Schocher <hs@denx.de>
  6 *
  7 * datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20006055B.pdf
  8 *
  9 * we set continuous mode but reading/writing more bytes than
 10 * pagesize seems to bring chip into state where readden values
 11 * are wrong ... no idea why.
 12 *
 13 */
 14#include <linux/delay.h>
 15#include <linux/device.h>
 16#include <linux/jiffies.h>
 17#include <linux/module.h>
 18#include <linux/mtd/mtd.h>
 19#include <linux/mtd/partitions.h>
 20#include <linux/mutex.h>
 21#include <linux/sched.h>
 22#include <linux/sizes.h>
 23#include <linux/spi/flash.h>
 24#include <linux/spi/spi.h>
 25#include <linux/of.h>
 26#include <linux/string_choices.h>
 27
 28struct mchp48_caps {
 29	unsigned int size;
 30	unsigned int page_size;
 31	bool auto_disable_wel;
 32};
 33
 34struct mchp48l640_flash {
 35	struct spi_device	*spi;
 36	struct mutex		lock;
 37	struct mtd_info		mtd;
 38	const struct mchp48_caps	*caps;
 39};
 40
 41#define MCHP48L640_CMD_WREN		0x06
 42#define MCHP48L640_CMD_WRDI		0x04
 43#define MCHP48L640_CMD_WRITE		0x02
 44#define MCHP48L640_CMD_READ		0x03
 45#define MCHP48L640_CMD_WRSR		0x01
 46#define MCHP48L640_CMD_RDSR		0x05
 47
 48#define MCHP48L640_STATUS_RDY		0x01
 49#define MCHP48L640_STATUS_WEL		0x02
 50#define MCHP48L640_STATUS_BP0		0x04
 51#define MCHP48L640_STATUS_BP1		0x08
 52#define MCHP48L640_STATUS_SWM		0x10
 53#define MCHP48L640_STATUS_PRO		0x20
 54#define MCHP48L640_STATUS_ASE		0x40
 55
 56#define MCHP48L640_TIMEOUT		100
 57
 58#define MAX_CMD_SIZE			0x10
 59
 60#define to_mchp48l640_flash(x) container_of(x, struct mchp48l640_flash, mtd)
 61
 62static int mchp48l640_mkcmd(struct mchp48l640_flash *flash, u8 cmd, loff_t addr, char *buf)
 63{
 64	buf[0] = cmd;
 65	buf[1] = addr >> 8;
 66	buf[2] = addr;
 67
 68	return 3;
 69}
 70
 71static int mchp48l640_read_status(struct mchp48l640_flash *flash, int *status)
 72{
 73	unsigned char cmd[2];
 74	int ret;
 75
 76	cmd[0] = MCHP48L640_CMD_RDSR;
 77	cmd[1] = 0x00;
 78	mutex_lock(&flash->lock);
 79	ret = spi_write_then_read(flash->spi, &cmd[0], 1, &cmd[1], 1);
 80	mutex_unlock(&flash->lock);
 81	if (!ret)
 82		*status = cmd[1];
 83	dev_dbg(&flash->spi->dev, "read status ret: %d status: %x", ret, *status);
 84
 85	return ret;
 86}
 87
 88static int mchp48l640_waitforbit(struct mchp48l640_flash *flash, int bit, bool set)
 89{
 90	int ret, status;
 91	unsigned long deadline;
 92
 93	deadline = jiffies + msecs_to_jiffies(MCHP48L640_TIMEOUT);
 94	do {
 95		ret = mchp48l640_read_status(flash, &status);
 96		dev_dbg(&flash->spi->dev, "read status ret: %d bit: %x %sset status: %x",
 97			ret, bit, (set ? "" : "not"), status);
 98		if (ret)
 99			return ret;
100
101		if (set) {
102			if ((status & bit) == bit)
103				return 0;
104		} else {
105			if ((status & bit) == 0)
106				return 0;
107		}
108
109		usleep_range(1000, 2000);
110	} while (!time_after_eq(jiffies, deadline));
111
112	dev_err(&flash->spi->dev, "Timeout waiting for bit %x %s set in status register.",
113		bit, (set ? "" : "not"));
114	return -ETIMEDOUT;
115}
116
117static int mchp48l640_write_prepare(struct mchp48l640_flash *flash, bool enable)
118{
119	unsigned char cmd[2];
120	int ret;
121
122	if (enable)
123		cmd[0] = MCHP48L640_CMD_WREN;
124	else
125		cmd[0] = MCHP48L640_CMD_WRDI;
126
127	mutex_lock(&flash->lock);
128	ret = spi_write(flash->spi, cmd, 1);
129	mutex_unlock(&flash->lock);
130
131	if (ret)
132		dev_err(&flash->spi->dev, "write %s failed ret: %d",
133			str_enable_disable(enable), ret);
134
135	dev_dbg(&flash->spi->dev, "write %s success ret: %d",
136		str_enable_disable(enable), ret);
137	if (enable)
138		return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_WEL, true);
139
140	return ret;
141}
142
143static int mchp48l640_set_mode(struct mchp48l640_flash *flash)
144{
145	unsigned char cmd[2];
146	int ret;
147
148	ret = mchp48l640_write_prepare(flash, true);
149	if (ret)
150		return ret;
151
152	cmd[0] = MCHP48L640_CMD_WRSR;
153	cmd[1] = MCHP48L640_STATUS_PRO;
154
155	mutex_lock(&flash->lock);
156	ret = spi_write(flash->spi, cmd, 2);
157	mutex_unlock(&flash->lock);
158	if (ret)
159		dev_err(&flash->spi->dev, "Could not set continuous mode ret: %d", ret);
160
161	return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_PRO, true);
162}
163
164static int mchp48l640_wait_rdy(struct mchp48l640_flash *flash)
165{
166	return mchp48l640_waitforbit(flash, MCHP48L640_STATUS_RDY, false);
167};
168
169static int mchp48l640_write_page(struct mtd_info *mtd, loff_t to, size_t len,
170			    size_t *retlen, const unsigned char *buf)
171{
172	struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd);
173	unsigned char *cmd;
174	int ret;
175	int cmdlen;
176
177	cmd = kmalloc((3 + len), GFP_KERNEL | GFP_DMA);
178	if (!cmd)
179		return -ENOMEM;
180
181	ret = mchp48l640_wait_rdy(flash);
182	if (ret)
183		goto fail;
184
185	ret = mchp48l640_write_prepare(flash, true);
186	if (ret)
187		goto fail;
188
189	mutex_lock(&flash->lock);
190	cmdlen = mchp48l640_mkcmd(flash, MCHP48L640_CMD_WRITE, to, cmd);
191	memcpy(&cmd[cmdlen], buf, len);
192	ret = spi_write(flash->spi, cmd, cmdlen + len);
193	mutex_unlock(&flash->lock);
194	if (!ret)
195		*retlen += len;
196	else
197		goto fail;
198
199	if (flash->caps->auto_disable_wel) {
200		ret = mchp48l640_waitforbit(flash, MCHP48L640_STATUS_WEL, false);
201		if (ret)
202			goto fail;
203	} else {
204		ret = mchp48l640_write_prepare(flash, false);
205		if (ret)
206			goto fail;
207	}
208
209	kfree(cmd);
210	return 0;
211fail:
212	kfree(cmd);
213	dev_err(&flash->spi->dev, "write fail with: %d", ret);
214	return ret;
215};
216
217static int mchp48l640_write(struct mtd_info *mtd, loff_t to, size_t len,
218			    size_t *retlen, const unsigned char *buf)
219{
220	struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd);
221	int ret;
222	size_t wlen = 0;
223	loff_t woff = to;
224	size_t ws;
225	size_t page_sz = flash->caps->page_size;
226
227	/*
228	 * we set PRO bit (page rollover), but writing length > page size
229	 * does result in total chaos, so write in 32 byte chunks.
230	 */
231	while (wlen < len) {
232		ws = min((len - wlen), page_sz);
233		ret = mchp48l640_write_page(mtd, woff, ws, retlen, &buf[wlen]);
234		if (ret)
235			return ret;
236		wlen += ws;
237		woff += ws;
238	}
239
240	return 0;
241}
242
243static int mchp48l640_read_page(struct mtd_info *mtd, loff_t from, size_t len,
244			   size_t *retlen, unsigned char *buf)
245{
246	struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd);
247	unsigned char *cmd;
248	int ret;
249	int cmdlen;
250
251	cmd = kmalloc((3 + len), GFP_KERNEL | GFP_DMA);
252	if (!cmd)
253		return -ENOMEM;
254
255	ret = mchp48l640_wait_rdy(flash);
256	if (ret)
257		goto fail;
258
259	mutex_lock(&flash->lock);
260	cmdlen = mchp48l640_mkcmd(flash, MCHP48L640_CMD_READ, from, cmd);
261	ret = spi_write_then_read(flash->spi, cmd, cmdlen, buf, len);
262	mutex_unlock(&flash->lock);
263	if (!ret)
264		*retlen += len;
265
266	kfree(cmd);
267	return ret;
268
269fail:
270	kfree(cmd);
271	dev_err(&flash->spi->dev, "read fail with: %d", ret);
272	return ret;
273}
274
275static int mchp48l640_read(struct mtd_info *mtd, loff_t from, size_t len,
276			   size_t *retlen, unsigned char *buf)
277{
278	struct mchp48l640_flash *flash = to_mchp48l640_flash(mtd);
279	int ret;
280	size_t wlen = 0;
281	loff_t woff = from;
282	size_t ws;
283	size_t page_sz = flash->caps->page_size;
284
285	/*
286	 * we set PRO bit (page rollover), but if read length > page size
287	 * does result in total chaos in result ...
288	 */
289	while (wlen < len) {
290		ws = min((len - wlen), page_sz);
291		ret = mchp48l640_read_page(mtd, woff, ws, retlen, &buf[wlen]);
292		if (ret)
293			return ret;
294		wlen += ws;
295		woff += ws;
296	}
297
298	return 0;
299};
300
301static const struct mchp48_caps mchp48l640_caps = {
302	.size = SZ_8K,
303	.page_size = 32,
304	.auto_disable_wel = true,
305};
306
307static const struct mchp48_caps mb85rs128ty_caps = {
308	.size = SZ_16K,
309	.page_size = 256,
310	.auto_disable_wel = false,
311};
312
313static int mchp48l640_probe(struct spi_device *spi)
314{
315	struct mchp48l640_flash *flash;
316	struct flash_platform_data *data;
317	int err;
318	int status;
319
320	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
321	if (!flash)
322		return -ENOMEM;
323
324	flash->spi = spi;
325	mutex_init(&flash->lock);
326	spi_set_drvdata(spi, flash);
327
328	err = mchp48l640_read_status(flash, &status);
329	if (err)
330		return err;
331
332	err = mchp48l640_set_mode(flash);
333	if (err)
334		return err;
335
336	data = dev_get_platdata(&spi->dev);
337
338	flash->caps = of_device_get_match_data(&spi->dev);
339	if (!flash->caps)
340		flash->caps = &mchp48l640_caps;
341
342	mtd_set_of_node(&flash->mtd, spi->dev.of_node);
343	flash->mtd.dev.parent	= &spi->dev;
344	flash->mtd.type		= MTD_RAM;
345	flash->mtd.flags	= MTD_CAP_RAM;
346	flash->mtd.writesize	= flash->caps->page_size;
347	flash->mtd.size		= flash->caps->size;
348	flash->mtd._read	= mchp48l640_read;
349	flash->mtd._write	= mchp48l640_write;
350
351	err = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
352				  data ? data->nr_parts : 0);
353	if (err)
354		return err;
355
356	return 0;
357}
358
359static void mchp48l640_remove(struct spi_device *spi)
360{
361	struct mchp48l640_flash *flash = spi_get_drvdata(spi);
362
363	WARN_ON(mtd_device_unregister(&flash->mtd));
364}
365
366static const struct of_device_id mchp48l640_of_table[] = {
367	{
368		.compatible = "microchip,48l640",
369		.data = &mchp48l640_caps,
370	},
371	{
372		.compatible = "fujitsu,mb85rs128ty",
373		.data = &mb85rs128ty_caps,
374	},
375	{}
376};
377MODULE_DEVICE_TABLE(of, mchp48l640_of_table);
378
379static const struct spi_device_id mchp48l640_spi_ids[] = {
380	{
381		.name = "48l640",
382		.driver_data = (kernel_ulong_t)&mchp48l640_caps,
383	},
384	{
385		.name = "mb85rs128ty",
386		.driver_data = (kernel_ulong_t)&mb85rs128ty_caps,
387	},
388	{}
389};
390MODULE_DEVICE_TABLE(spi, mchp48l640_spi_ids);
391
392static struct spi_driver mchp48l640_driver = {
393	.driver = {
394		.name	= "mchp48l640",
395		.of_match_table = mchp48l640_of_table,
396	},
397	.probe		= mchp48l640_probe,
398	.remove		= mchp48l640_remove,
399	.id_table	= mchp48l640_spi_ids,
400};
401
402module_spi_driver(mchp48l640_driver);
403
404MODULE_DESCRIPTION("MTD SPI driver for Microchip 48l640 EERAM chips");
405MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
406MODULE_LICENSE("GPL v2");
407MODULE_ALIAS("spi:mchp48l640");
~/ Read-only file preview.