Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/*
  2 * drivers/mtd/nand/socrates_nand.c
  3 *
  4 *  Copyright © 2008 Ilya Yanok, Emcraft Systems
  5 *
  6 *
  7 * This program is free software; you can redistribute it and/or modify
  8 * it under the terms of the GNU General Public License version 2 as
  9 * published by the Free Software Foundation.
 10 *
 11 */
 12
 13#include <linux/slab.h>
 14#include <linux/module.h>
 15#include <linux/mtd/mtd.h>
 16#include <linux/mtd/nand.h>
 17#include <linux/mtd/partitions.h>
 18#include <linux/of_address.h>
 19#include <linux/of_platform.h>
 20#include <linux/io.h>
 21
 22#define FPGA_NAND_CMD_MASK		(0x7 << 28)
 23#define FPGA_NAND_CMD_COMMAND		(0x0 << 28)
 24#define FPGA_NAND_CMD_ADDR		(0x1 << 28)
 25#define FPGA_NAND_CMD_READ		(0x2 << 28)
 26#define FPGA_NAND_CMD_WRITE		(0x3 << 28)
 27#define FPGA_NAND_BUSY			(0x1 << 15)
 28#define FPGA_NAND_ENABLE		(0x1 << 31)
 29#define FPGA_NAND_DATA_SHIFT		16
 30
 31struct socrates_nand_host {
 32	struct nand_chip	nand_chip;
 33	void __iomem		*io_base;
 34	struct device		*dev;
 35};
 36
 37/**
 38 * socrates_nand_write_buf -  write buffer to chip
 39 * @mtd:	MTD device structure
 40 * @buf:	data buffer
 41 * @len:	number of bytes to write
 42 */
 43static void socrates_nand_write_buf(struct mtd_info *mtd,
 44		const uint8_t *buf, int len)
 45{
 46	int i;
 47	struct nand_chip *this = mtd_to_nand(mtd);
 48	struct socrates_nand_host *host = nand_get_controller_data(this);
 49
 50	for (i = 0; i < len; i++) {
 51		out_be32(host->io_base, FPGA_NAND_ENABLE |
 52				FPGA_NAND_CMD_WRITE |
 53				(buf[i] << FPGA_NAND_DATA_SHIFT));
 54	}
 55}
 56
 57/**
 58 * socrates_nand_read_buf -  read chip data into buffer
 59 * @mtd:	MTD device structure
 60 * @buf:	buffer to store date
 61 * @len:	number of bytes to read
 62 */
 63static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 64{
 65	int i;
 66	struct nand_chip *this = mtd_to_nand(mtd);
 67	struct socrates_nand_host *host = nand_get_controller_data(this);
 68	uint32_t val;
 69
 70	val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ;
 71
 72	out_be32(host->io_base, val);
 73	for (i = 0; i < len; i++) {
 74		buf[i] = (in_be32(host->io_base) >>
 75				FPGA_NAND_DATA_SHIFT) & 0xff;
 76	}
 77}
 78
 79/**
 80 * socrates_nand_read_byte -  read one byte from the chip
 81 * @mtd:	MTD device structure
 82 */
 83static uint8_t socrates_nand_read_byte(struct mtd_info *mtd)
 84{
 85	uint8_t byte;
 86	socrates_nand_read_buf(mtd, &byte, sizeof(byte));
 87	return byte;
 88}
 89
 90/**
 91 * socrates_nand_read_word -  read one word from the chip
 92 * @mtd:	MTD device structure
 93 */
 94static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
 95{
 96	uint16_t word;
 97	socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word));
 98	return word;
 99}
100
101/*
102 * Hardware specific access to control-lines
103 */
104static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
105		unsigned int ctrl)
106{
107	struct nand_chip *nand_chip = mtd_to_nand(mtd);
108	struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
109	uint32_t val;
110
111	if (cmd == NAND_CMD_NONE)
112		return;
113
114	if (ctrl & NAND_CLE)
115		val = FPGA_NAND_CMD_COMMAND;
116	else
117		val = FPGA_NAND_CMD_ADDR;
118
119	if (ctrl & NAND_NCE)
120		val |= FPGA_NAND_ENABLE;
121
122	val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT;
123
124	out_be32(host->io_base, val);
125}
126
127/*
128 * Read the Device Ready pin.
129 */
130static int socrates_nand_device_ready(struct mtd_info *mtd)
131{
132	struct nand_chip *nand_chip = mtd_to_nand(mtd);
133	struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
134
135	if (in_be32(host->io_base) & FPGA_NAND_BUSY)
136		return 0; /* busy */
137	return 1;
138}
139
140/*
141 * Probe for the NAND device.
142 */
143static int socrates_nand_probe(struct platform_device *ofdev)
144{
145	struct socrates_nand_host *host;
146	struct mtd_info *mtd;
147	struct nand_chip *nand_chip;
148	int res;
149
150	/* Allocate memory for the device structure (and zero it) */
151	host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL);
152	if (!host)
153		return -ENOMEM;
154
155	host->io_base = of_iomap(ofdev->dev.of_node, 0);
156	if (host->io_base == NULL) {
157		dev_err(&ofdev->dev, "ioremap failed\n");
158		return -EIO;
159	}
160
161	nand_chip = &host->nand_chip;
162	mtd = nand_to_mtd(nand_chip);
163	host->dev = &ofdev->dev;
164
165	/* link the private data structures */
166	nand_set_controller_data(nand_chip, host);
167	nand_set_flash_node(nand_chip, ofdev->dev.of_node);
168	mtd->name = "socrates_nand";
169	mtd->dev.parent = &ofdev->dev;
170
171	/*should never be accessed directly */
172	nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
173	nand_chip->IO_ADDR_W = (void *)0xdeadbeef;
174
175	nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl;
176	nand_chip->read_byte = socrates_nand_read_byte;
177	nand_chip->read_word = socrates_nand_read_word;
178	nand_chip->write_buf = socrates_nand_write_buf;
179	nand_chip->read_buf = socrates_nand_read_buf;
180	nand_chip->dev_ready = socrates_nand_device_ready;
181
182	nand_chip->ecc.mode = NAND_ECC_SOFT;	/* enable ECC */
183	nand_chip->ecc.algo = NAND_ECC_HAMMING;
184
185	/* TODO: I have no idea what real delay is. */
186	nand_chip->chip_delay = 20;		/* 20us command delay time */
187
188	dev_set_drvdata(&ofdev->dev, host);
189
190	res = nand_scan(mtd, 1);
191	if (res)
192		goto out;
193
194	res = mtd_device_register(mtd, NULL, 0);
195	if (!res)
196		return res;
197
198	nand_release(mtd);
199
200out:
201	iounmap(host->io_base);
202	return res;
203}
204
205/*
206 * Remove a NAND device.
207 */
208static int socrates_nand_remove(struct platform_device *ofdev)
209{
210	struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
211	struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
212
213	nand_release(mtd);
214
215	iounmap(host->io_base);
216
217	return 0;
218}
219
220static const struct of_device_id socrates_nand_match[] =
221{
222	{
223		.compatible   = "abb,socrates-nand",
224	},
225	{},
226};
227
228MODULE_DEVICE_TABLE(of, socrates_nand_match);
229
230static struct platform_driver socrates_nand_driver = {
231	.driver = {
232		.name = "socrates_nand",
233		.of_match_table = socrates_nand_match,
234	},
235	.probe		= socrates_nand_probe,
236	.remove		= socrates_nand_remove,
237};
238
239module_platform_driver(socrates_nand_driver);
240
241MODULE_LICENSE("GPL");
242MODULE_AUTHOR("Ilya Yanok");
243MODULE_DESCRIPTION("NAND driver for Socrates board");