brintos

brintos / linux-shallow public Read only

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