--- linux-2.6.22.14/drivers/mtd/nand/rb400_nand.c 1970-01-01 03:00:00.000000000 +0300 +++ linux/drivers/mtd/nand/rb400_nand.c 2008-01-22 12:22:15.000000000 +0200 @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#include +#include +#include + +#define RB400_DEBUG 0 +#define REG_PLL_CONFIG KSEG1ADDR(0x18050000) + +static struct mtd_partition partition_info[] = { + { + name: "RouterBoard NAND Boot", + offset: 256 * 1024, + size: 4 * 1024 * 1024 - 256 * 1024, + }, + { + name: "RouterBoard NAND Main", + offset: MTDPART_OFS_NXTBLK, + size: MTDPART_SIZ_FULL, + }, + { + name: "RouterBoard NAND Booter", + offset: 0, + size: 256 * 1024, + } +}; + +static struct mtd_info rmtd; +static struct nand_chip rnand; + +static unsigned init_ok = 0; + +unsigned get_rbnand_block_size(void) { + if (init_ok) return rmtd.writesize; else return 0; +} + +EXPORT_SYMBOL(get_rbnand_block_size); + +static int rbmips_probe(void) { + memset(&rmtd, 0, sizeof(rmtd)); + + rnand.ecc.mode = NAND_ECC_SOFT; + rnand.chip_delay = 25; + rnand.options |= NAND_NO_AUTOINCR; + rmtd.priv = &rnand; + + if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) + && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) { + printk("RBxxx nand device not found\n"); + return -ENXIO; + } + + add_mtd_partitions(&rmtd, partition_info, 3); + init_ok = 1; + return 0; +} + +/* RB400 NAND through SPI support */ +#define USE_FAST_READ 1 +#define USE_FAST_WRITE 1 + +#define SPI_BASE 0x1F000000 +#define SPI_FUNC 0x00000000 +#define SBIT_FUNC_GPIO 0x00000001 +#define SPI_CTRL 0x00000004 +#define SPI_CTRL_FASTEST 0x40 +#define SPI_CTRL_SAFE 0x43 // 25 MHz for AHB 200 MHz +#define SPI_FLASH_HZ 33333334 +#define SPI_NAND_HZ 33333334 +#define SPI_IO_CTRL 0x00000008 +#define SBIT_IOC_BASE 0x00020000 +#define SBIT_DO2_SHIFT 18 +#define SBIT_DO2 (1u << SBIT_DO2_SHIFT) +#define SBIT_CS_0 0x00010000 +#define SBIT_CLK 0x00000100 +#define SBIT_DO_SHIFT 0 +#define SBIT_DO (1u << SBIT_DO_SHIFT) +#define SPI_RDATA 0x0000000C + +#if USE_FAST_READ +#define SPI_NDATA_BASE 0x00800000 +static unsigned spi_ctrl_fread = SPI_CTRL_SAFE; +static unsigned spi_ctrl_flash = SPI_CTRL_SAFE; +extern unsigned mips_hpt_frequency; +#endif + +#define SPI_CMD_WRITE_MULT 0x08 /* send cmd, n x send data, read data */ +#define SPI_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */ +#define SPI_CMD_READ_MULT 0x0a /* send cmd, send idle, n x read data */ +//#define SPI_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */ + +#define CFG_BIT_nCE 0x80 +#define CFG_BIT_CLE 0x40 +#define CFG_BIT_ALE 0x20 +#define CFG_BIT_FAN 0x10 +#define CFG_BIT_nLED4 0x08 +#define CFG_BIT_nLED3 0x04 +#define CFG_BIT_nLED2 0x02 +#define CFG_BIT_nLED1 0x01 + +#define GPIO_BASE 0x18040000 +#define GPIO_OE_REG 0x0000 +#define GPIO_IN_REG 0x0004 +#define GPIO_NAND_RDY (1 << 5) +#define GPIO_FUNC_REG 0x0028 +#define FUNC_GPIO1_SPI (1 << 13) + +#if RB400_DEBUG +#define CLK_HALF_DELAY() ndelay(20000) +#else +#define CLK_HALF_DELAY() while(0) {} +#endif + +#define SPI_REG(x) (*(volatile unsigned *)(KSEG1ADDR(SPI_BASE) + (x))) +#define GPIO_REG(x) (*(volatile unsigned *)(KSEG1ADDR(GPIO_BASE) + (x))) + +static void inline do_spi_clk(int bit) { + unsigned bval = SBIT_IOC_BASE | (bit & 1); + CLK_HALF_DELAY(); + SPI_REG(SPI_IO_CTRL) = bval; + CLK_HALF_DELAY(); + SPI_REG(SPI_IO_CTRL) = bval | SBIT_CLK; +} + +static void do_spi_byte(uint8_t byte) { + do_spi_clk(byte >> 7); + do_spi_clk(byte >> 6); + do_spi_clk(byte >> 5); + do_spi_clk(byte >> 4); + do_spi_clk(byte >> 3); + do_spi_clk(byte >> 2); + do_spi_clk(byte >> 1); + do_spi_clk(byte); +#if RB400_DEBUG + printk("spi_byte sent 0x%x got 0x%x\n", + (unsigned)byte, + SPI_REG(SPI_RDATA)); +#endif +} + +#if USE_FAST_WRITE +static void inline do_spi_clk_fast(int bit1, int bit2) { + unsigned bval = (SBIT_IOC_BASE | + ((bit1 << SBIT_DO_SHIFT) & SBIT_DO) | + ((bit2 << SBIT_DO2_SHIFT) & SBIT_DO2)); + CLK_HALF_DELAY(); + SPI_REG(SPI_IO_CTRL) = bval; + CLK_HALF_DELAY(); + SPI_REG(SPI_IO_CTRL) = bval | SBIT_CLK; +} + +static void do_spi_byte_fast(uint8_t byte) { + do_spi_clk_fast(byte >> 7, byte >> 6); + do_spi_clk_fast(byte >> 5, byte >> 4); + do_spi_clk_fast(byte >> 3, byte >> 2); + do_spi_clk_fast(byte >> 1, byte >> 0); +#if RB400_DEBUG + printk("spi_byte_fast sent 0x%x got 0x%x\n", + (unsigned)byte, + SPI_REG(SPI_RDATA)); +#endif +} +#else +static void inline do_spi_byte_fast(uint8_t byte) { do_spi_byte(byte); } +#endif + +static int do_spi_cmd(unsigned cmd, unsigned sendCnt, const uint8_t *sendData, + unsigned recvCnt, uint8_t *recvData, + const uint8_t *verifyData, int fastWrite) { + unsigned i; +#if RB400_DEBUG + printk("SPI cmd 0x%x send %u recv %u\n", cmd, sendCnt, recvCnt); +#endif + + SPI_REG(SPI_FUNC) = SBIT_FUNC_GPIO; + SPI_REG(SPI_CTRL) = SPI_CTRL_FASTEST; + + do_spi_byte(cmd); +#if 0 + if (cmd == SPI_CMD_READ_FAST) { + do_spi_byte(0x80); + do_spi_byte(0); + do_spi_byte(0); + } +#endif + for (i = 0; i < sendCnt; ++i) { + if (fastWrite) + do_spi_byte_fast(sendData[i]); + else + do_spi_byte(sendData[i]); + } + + for (i = 0; i < recvCnt; ++i) { + if (fastWrite) + do_spi_byte_fast(0); + else + do_spi_byte(0); + if (recvData) { + recvData[i] = SPI_REG(SPI_RDATA) & 0xff; + } + else if (verifyData) { + if (verifyData[i] != (SPI_REG(SPI_RDATA) & 0xff)) break; + } + } + + CLK_HALF_DELAY(); + SPI_REG(SPI_IO_CTRL) = SBIT_IOC_BASE | SBIT_CS_0; + SPI_REG(SPI_CTRL) = spi_ctrl_flash; + SPI_REG(SPI_FUNC) = 0; + return i == recvCnt; +} + +static int got_write = 1; + +static void rb400_write_data(const uint8_t *byte, unsigned cnt) { + do_spi_cmd(SPI_CMD_WRITE_MULT, cnt, byte, 1, NULL, NULL, 1); + got_write = 1; +} + +static void rb400_write_byte(uint8_t byte) { + rb400_write_data(&byte, 1); +} + +#if USE_FAST_READ +static uint8_t *rb400_read_getaddr(unsigned cnt) { + static unsigned nboffset = 0x100000; + unsigned addr; + + if (got_write) { + nboffset = (nboffset + 31) & ~31; + if (nboffset >= 0x100000) { // 1 MB + nboffset = 0; + } + got_write = 0; + SPI_REG(SPI_FUNC) = SBIT_FUNC_GPIO; + SPI_REG(SPI_CTRL) = spi_ctrl_fread; + SPI_REG(SPI_FUNC) = 0; + } + addr = KSEG1ADDR(SPI_BASE + SPI_NDATA_BASE) + nboffset; +#if RB400_DEBUG + printk("rb400_read_getaddr 0x%x cnt 0x%x\n", addr, cnt); +#endif + + nboffset += cnt; + return (uint8_t *)addr; +} +#endif + +static void rb400_read_data(uint8_t *buf, unsigned cnt) { +#if USE_FAST_READ + unsigned size32 = cnt & ~31; + unsigned sizeLeft = cnt & 31; + + if (size32) { + uint8_t *addr = rb400_read_getaddr(size32); + memcpy(buf, (void *)addr, size32); + } + + if (sizeLeft) { + do_spi_cmd(SPI_CMD_READ_MULT, 1, buf, sizeLeft, + buf + size32, NULL, 0); + } +#else + do_spi_cmd(SPI_CMD_READ_MULT, 1, buf, cnt, buf, NULL, 0); +#endif +} + +static int rb400_verify_data(const uint8_t *buf, unsigned cnt) { +#if USE_FAST_READ + unsigned size32 = cnt & ~31; + unsigned sizeLeft = cnt & 31; + + if (size32) { + uint8_t *addr = rb400_read_getaddr(size32); + if (memcmp(buf, (void *)addr, size32) != 0) return 0; + } + + if (sizeLeft) { + return do_spi_cmd(SPI_CMD_READ_MULT, 1, buf, sizeLeft, + NULL, buf + size32, 0); + } + return 1; +#else + return do_spi_cmd(SPI_CMD_READ_MULT, 1, buf, cnt, NULL, buf, 0); +#endif +} + +static void rb400_write_cfg(uint8_t byte) { + do_spi_cmd(SPI_CMD_WRITE_CFG, 1, &byte, 0, NULL, NULL, 0); + got_write = 1; +} + +static int rb400_dev_ready(struct mtd_info *mtd) { + return GPIO_REG(GPIO_IN_REG) & GPIO_NAND_RDY; +} + +static void rb400_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { + if (ctrl & NAND_CTRL_CHANGE) { + uint8_t cfg = (CFG_BIT_nLED1 | CFG_BIT_nLED2 | + CFG_BIT_nLED3 | CFG_BIT_nLED4); + + if (ctrl & NAND_CLE) { + cfg |= CFG_BIT_CLE; + } + if (ctrl & NAND_ALE) { + cfg |= CFG_BIT_ALE; + } + if (!(ctrl & NAND_NCE)) { + cfg |= CFG_BIT_nCE; + } + rb400_write_cfg(cfg); + } + + if (cmd != NAND_CMD_NONE) rb400_write_byte(cmd); +} + +static uint8_t rb400_read_byte(struct mtd_info *mtd) +{ + uint8_t byte = 0; + rb400_read_data(&byte, 1); + return byte; +} + +static void rb400_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + rb400_write_data(buf, len); +} + +static void rb400_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + rb400_read_data(buf, len); +} + +static int rb400_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + if (!rb400_verify_data(buf, len)) + return -EFAULT; + + return 0; +} + +static unsigned get_spi_ctrl(unsigned hz_max, const char *name) { + unsigned ahb_div = ((rb400_readl(REG_PLL_CONFIG) >> 20) & 0x7) + 1; + unsigned ahb_clock = (mips_hpt_frequency + ahb_div - 1) / ahb_div; + unsigned div = (ahb_clock - 1) / (2 * hz_max); + if (div == 0) { + // CPU has a bug at (div == 0) - first bit read is random + ++div; + } + if (name) { + unsigned ahb_khz = (ahb_clock + 500) / 1000; + unsigned div_real = 2 * (div + 1); + printk(KERN_INFO "%s SPI clock %u kHz (AHB %u kHz / %u)\n", + name, + ahb_khz / div_real, + ahb_khz, div_real); + } + return SPI_CTRL_FASTEST + div; +} + +static int rb400_nand_probe(struct platform_device *pdev) +{ +// unsigned id; + printk("RB400 nand\n"); + memset(&rnand, 0, sizeof(rnand)); + +#if USE_FAST_READ + spi_ctrl_fread = get_spi_ctrl(SPI_NAND_HZ, "NAND"); +#endif + spi_ctrl_flash = get_spi_ctrl(SPI_FLASH_HZ, "FLASH"); + +#if 0 + rb400_write_cfg(CFG_BIT_CLE); + rb400_write_byte(0x90); + rb400_write_cfg(CFG_BIT_ALE); + rb400_write_byte(0x00); + rb400_write_cfg(0); +#if 0 + rb400_read_data((void *)&id, 4); +#else + SPI_REG(SPI_FUNC) = 1; + SPI_REG(SPI_CTRL) = 0x42; + SPI_REG(SPI_FUNC) = 0; + id = SPI_REG(SPI_NDATA_BASE); +#endif + printk("NAND ID 0x%x\n", id); + rb400_write_cfg(CFG_BIT_nCE); + return -1; +#else + GPIO_REG(GPIO_OE_REG) &= ~GPIO_NAND_RDY; + GPIO_REG(GPIO_FUNC_REG) |= FUNC_GPIO1_SPI; + + rnand.cmd_ctrl = rb400_hwcontrol; + rnand.dev_ready = rb400_dev_ready; + rnand.read_byte = rb400_read_byte; + rnand.write_buf = rb400_write_buf; + rnand.read_buf = rb400_read_buf; + rnand.verify_buf = rb400_verify_buf; + + return rbmips_probe(); +#endif +} + +static struct platform_driver rb400_nand_driver = { + .probe = rb400_nand_probe, + .driver = { + .name = "rb400-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init rb400_nand_init(void) +{ + platform_driver_register(&rb400_nand_driver); + return 0; +} + +static void __exit rb400_nand_exit(void) +{ + platform_driver_unregister(&rb400_nand_driver); +} + +module_init(rb400_nand_init); +module_exit(rb400_nand_exit);