15467fb02SDavid Woodhouse /* 2fbad5696SDavid Woodhouse * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 35467fb02SDavid Woodhouse * 4514fca43SDavid Woodhouse * The data sheet for this device can be found at: 5631dd1a8SJustin P. Mattock * http://wiki.laptop.org/go/Datasheets 6514fca43SDavid Woodhouse * 75467fb02SDavid Woodhouse * Copyright © 2006 Red Hat, Inc. 85467fb02SDavid Woodhouse * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> 95467fb02SDavid Woodhouse */ 105467fb02SDavid Woodhouse 118dd851deSDavid Woodhouse #define DEBUG 125467fb02SDavid Woodhouse 135467fb02SDavid Woodhouse #include <linux/device.h> 145467fb02SDavid Woodhouse #undef DEBUG 155467fb02SDavid Woodhouse #include <linux/mtd/mtd.h> 16d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h> 179c37f332SDavid Woodhouse #include <linux/mtd/partitions.h> 188c61b7a7SSegher Boessenkool #include <linux/rslib.h> 195467fb02SDavid Woodhouse #include <linux/pci.h> 205467fb02SDavid Woodhouse #include <linux/delay.h> 215467fb02SDavid Woodhouse #include <linux/interrupt.h> 22a1274302SAl Viro #include <linux/dma-mapping.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 24a0e5cc58SPaul Gortmaker #include <linux/module.h> 255467fb02SDavid Woodhouse #include <asm/io.h> 265467fb02SDavid Woodhouse 275467fb02SDavid Woodhouse #define CAFE_NAND_CTRL1 0x00 285467fb02SDavid Woodhouse #define CAFE_NAND_CTRL2 0x04 295467fb02SDavid Woodhouse #define CAFE_NAND_CTRL3 0x08 305467fb02SDavid Woodhouse #define CAFE_NAND_STATUS 0x0c 315467fb02SDavid Woodhouse #define CAFE_NAND_IRQ 0x10 325467fb02SDavid Woodhouse #define CAFE_NAND_IRQ_MASK 0x14 335467fb02SDavid Woodhouse #define CAFE_NAND_DATA_LEN 0x18 345467fb02SDavid Woodhouse #define CAFE_NAND_ADDR1 0x1c 355467fb02SDavid Woodhouse #define CAFE_NAND_ADDR2 0x20 365467fb02SDavid Woodhouse #define CAFE_NAND_TIMING1 0x24 375467fb02SDavid Woodhouse #define CAFE_NAND_TIMING2 0x28 385467fb02SDavid Woodhouse #define CAFE_NAND_TIMING3 0x2c 395467fb02SDavid Woodhouse #define CAFE_NAND_NONMEM 0x30 4004459d7cSDavid Woodhouse #define CAFE_NAND_ECC_RESULT 0x3C 41fbad5696SDavid Woodhouse #define CAFE_NAND_DMA_CTRL 0x40 42fbad5696SDavid Woodhouse #define CAFE_NAND_DMA_ADDR0 0x44 43fbad5696SDavid Woodhouse #define CAFE_NAND_DMA_ADDR1 0x48 4404459d7cSDavid Woodhouse #define CAFE_NAND_ECC_SYN01 0x50 4504459d7cSDavid Woodhouse #define CAFE_NAND_ECC_SYN23 0x54 4604459d7cSDavid Woodhouse #define CAFE_NAND_ECC_SYN45 0x58 4704459d7cSDavid Woodhouse #define CAFE_NAND_ECC_SYN67 0x5c 485467fb02SDavid Woodhouse #define CAFE_NAND_READ_DATA 0x1000 495467fb02SDavid Woodhouse #define CAFE_NAND_WRITE_DATA 0x2000 505467fb02SDavid Woodhouse 51195a253bSDavid Woodhouse #define CAFE_GLOBAL_CTRL 0x3004 52195a253bSDavid Woodhouse #define CAFE_GLOBAL_IRQ 0x3008 53195a253bSDavid Woodhouse #define CAFE_GLOBAL_IRQ_MASK 0x300c 54195a253bSDavid Woodhouse #define CAFE_NAND_RESET 0x3034 55195a253bSDavid Woodhouse 56048c37b4SDavid Woodhouse /* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */ 57048c37b4SDavid Woodhouse #define CTRL1_CHIPSELECT (1<<19) 58048c37b4SDavid Woodhouse 595467fb02SDavid Woodhouse struct cafe_priv { 605467fb02SDavid Woodhouse struct nand_chip nand; 615467fb02SDavid Woodhouse struct pci_dev *pdev; 625467fb02SDavid Woodhouse void __iomem *mmio; 638c61b7a7SSegher Boessenkool struct rs_control *rs; 645467fb02SDavid Woodhouse uint32_t ctl1; 655467fb02SDavid Woodhouse uint32_t ctl2; 665467fb02SDavid Woodhouse int datalen; 675467fb02SDavid Woodhouse int nr_data; 685467fb02SDavid Woodhouse int data_pos; 695467fb02SDavid Woodhouse int page_addr; 7073a27db8SMiquel Raynal bool usedma; 715467fb02SDavid Woodhouse dma_addr_t dmaaddr; 725467fb02SDavid Woodhouse unsigned char *dmabuf; 735467fb02SDavid Woodhouse }; 745467fb02SDavid Woodhouse 75b478c775SDavid Woodhouse static int usedma = 1; 765467fb02SDavid Woodhouse module_param(usedma, int, 0644); 775467fb02SDavid Woodhouse 788dd851deSDavid Woodhouse static int skipbbt = 0; 798dd851deSDavid Woodhouse module_param(skipbbt, int, 0644); 808dd851deSDavid Woodhouse 818dd851deSDavid Woodhouse static int debug = 0; 828dd851deSDavid Woodhouse module_param(debug, int, 0644); 838dd851deSDavid Woodhouse 84be8444bdSDavid Woodhouse static int regdebug = 0; 85be8444bdSDavid Woodhouse module_param(regdebug, int, 0644); 86be8444bdSDavid Woodhouse 87b478c775SDavid Woodhouse static int checkecc = 1; 88470b0a90SDavid Woodhouse module_param(checkecc, int, 0644); 89470b0a90SDavid Woodhouse 9064a6f950SAl Viro static unsigned int numtimings; 91527a4f45SDavid Woodhouse static int timing[3]; 92527a4f45SDavid Woodhouse module_param_array(timing, int, &numtimings, 0644); 93b478c775SDavid Woodhouse 9468874414SPhilip Rakity static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; 959c37f332SDavid Woodhouse 9604459d7cSDavid Woodhouse /* Hrm. Why isn't this already conditional on something in the struct device? */ 978dd851deSDavid Woodhouse #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) 988dd851deSDavid Woodhouse 99195a253bSDavid Woodhouse /* Make it easier to switch to PIO if we need to */ 100195a253bSDavid Woodhouse #define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) 101195a253bSDavid Woodhouse #define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) 1028dd851deSDavid Woodhouse 1035467fb02SDavid Woodhouse static int cafe_device_ready(struct mtd_info *mtd) 1045467fb02SDavid Woodhouse { 1054bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 106d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 10748f8b641SDan Carpenter int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000); 108195a253bSDavid Woodhouse uint32_t irqs = cafe_readl(cafe, NAND_IRQ); 109fbad5696SDavid Woodhouse 110195a253bSDavid Woodhouse cafe_writel(cafe, irqs, NAND_IRQ); 111fbad5696SDavid Woodhouse 1128dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", 113195a253bSDavid Woodhouse result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), 114195a253bSDavid Woodhouse cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); 115fbad5696SDavid Woodhouse 1165467fb02SDavid Woodhouse return result; 1175467fb02SDavid Woodhouse } 1185467fb02SDavid Woodhouse 1195467fb02SDavid Woodhouse 1205467fb02SDavid Woodhouse static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 1215467fb02SDavid Woodhouse { 1224bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 123d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 1245467fb02SDavid Woodhouse 12573a27db8SMiquel Raynal if (cafe->usedma) 1265467fb02SDavid Woodhouse memcpy(cafe->dmabuf + cafe->datalen, buf, len); 1275467fb02SDavid Woodhouse else 1285467fb02SDavid Woodhouse memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); 129fbad5696SDavid Woodhouse 1305467fb02SDavid Woodhouse cafe->datalen += len; 1315467fb02SDavid Woodhouse 1328dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", 1335467fb02SDavid Woodhouse len, cafe->datalen); 1345467fb02SDavid Woodhouse } 1355467fb02SDavid Woodhouse 1365467fb02SDavid Woodhouse static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) 1375467fb02SDavid Woodhouse { 1384bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 139d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 1405467fb02SDavid Woodhouse 14173a27db8SMiquel Raynal if (cafe->usedma) 1425467fb02SDavid Woodhouse memcpy(buf, cafe->dmabuf + cafe->datalen, len); 1435467fb02SDavid Woodhouse else 1445467fb02SDavid Woodhouse memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); 1455467fb02SDavid Woodhouse 1468dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", 1475467fb02SDavid Woodhouse len, cafe->datalen); 1485467fb02SDavid Woodhouse cafe->datalen += len; 1495467fb02SDavid Woodhouse } 1505467fb02SDavid Woodhouse 1515467fb02SDavid Woodhouse static uint8_t cafe_read_byte(struct mtd_info *mtd) 1525467fb02SDavid Woodhouse { 1534bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 154d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 1555467fb02SDavid Woodhouse uint8_t d; 1565467fb02SDavid Woodhouse 1575467fb02SDavid Woodhouse cafe_read_buf(mtd, &d, 1); 1588dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); 1595467fb02SDavid Woodhouse 1605467fb02SDavid Woodhouse return d; 1615467fb02SDavid Woodhouse } 1625467fb02SDavid Woodhouse 1635467fb02SDavid Woodhouse static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, 1645467fb02SDavid Woodhouse int column, int page_addr) 1655467fb02SDavid Woodhouse { 1664bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 167d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 1685467fb02SDavid Woodhouse int adrbytes = 0; 1695467fb02SDavid Woodhouse uint32_t ctl1; 1705467fb02SDavid Woodhouse uint32_t doneint = 0x80000000; 1715467fb02SDavid Woodhouse 1728dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", 1735467fb02SDavid Woodhouse command, column, page_addr); 1745467fb02SDavid Woodhouse 1755467fb02SDavid Woodhouse if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { 1765467fb02SDavid Woodhouse /* Second half of a command we already calculated */ 177195a253bSDavid Woodhouse cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); 1785467fb02SDavid Woodhouse ctl1 = cafe->ctl1; 179cad40654SDavid Woodhouse cafe->ctl2 &= ~(1<<30); 1808dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", 1815467fb02SDavid Woodhouse cafe->ctl1, cafe->nr_data); 1825467fb02SDavid Woodhouse goto do_command; 1835467fb02SDavid Woodhouse } 1845467fb02SDavid Woodhouse /* Reset ECC engine */ 185195a253bSDavid Woodhouse cafe_writel(cafe, 0, NAND_CTRL2); 1865467fb02SDavid Woodhouse 1875467fb02SDavid Woodhouse /* Emulate NAND_CMD_READOOB on large-page chips */ 1885467fb02SDavid Woodhouse if (mtd->writesize > 512 && 1895467fb02SDavid Woodhouse command == NAND_CMD_READOOB) { 1905467fb02SDavid Woodhouse column += mtd->writesize; 1915467fb02SDavid Woodhouse command = NAND_CMD_READ0; 1925467fb02SDavid Woodhouse } 1935467fb02SDavid Woodhouse 1945467fb02SDavid Woodhouse /* FIXME: Do we need to send read command before sending data 1955467fb02SDavid Woodhouse for small-page chips, to position the buffer correctly? */ 1965467fb02SDavid Woodhouse 1975467fb02SDavid Woodhouse if (column != -1) { 198195a253bSDavid Woodhouse cafe_writel(cafe, column, NAND_ADDR1); 1995467fb02SDavid Woodhouse adrbytes = 2; 2005467fb02SDavid Woodhouse if (page_addr != -1) 2015467fb02SDavid Woodhouse goto write_adr2; 2025467fb02SDavid Woodhouse } else if (page_addr != -1) { 203195a253bSDavid Woodhouse cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); 2045467fb02SDavid Woodhouse page_addr >>= 16; 2055467fb02SDavid Woodhouse write_adr2: 206195a253bSDavid Woodhouse cafe_writel(cafe, page_addr, NAND_ADDR2); 2075467fb02SDavid Woodhouse adrbytes += 2; 2085467fb02SDavid Woodhouse if (mtd->size > mtd->writesize << 16) 2095467fb02SDavid Woodhouse adrbytes++; 2105467fb02SDavid Woodhouse } 2115467fb02SDavid Woodhouse 2125467fb02SDavid Woodhouse cafe->data_pos = cafe->datalen = 0; 2135467fb02SDavid Woodhouse 214048c37b4SDavid Woodhouse /* Set command valid bit, mask in the chip select bit */ 215048c37b4SDavid Woodhouse ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT); 2165467fb02SDavid Woodhouse 2175467fb02SDavid Woodhouse /* Set RD or WR bits as appropriate */ 2185467fb02SDavid Woodhouse if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { 2195467fb02SDavid Woodhouse ctl1 |= (1<<26); /* rd */ 2205467fb02SDavid Woodhouse /* Always 5 bytes, for now */ 2218dd851deSDavid Woodhouse cafe->datalen = 4; 2225467fb02SDavid Woodhouse /* And one address cycle -- even for STATUS, since the controller doesn't work without */ 2235467fb02SDavid Woodhouse adrbytes = 1; 2245467fb02SDavid Woodhouse } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || 2255467fb02SDavid Woodhouse command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { 2265467fb02SDavid Woodhouse ctl1 |= 1<<26; /* rd */ 2275467fb02SDavid Woodhouse /* For now, assume just read to end of page */ 2285467fb02SDavid Woodhouse cafe->datalen = mtd->writesize + mtd->oobsize - column; 2295467fb02SDavid Woodhouse } else if (command == NAND_CMD_SEQIN) 2305467fb02SDavid Woodhouse ctl1 |= 1<<25; /* wr */ 2315467fb02SDavid Woodhouse 2325467fb02SDavid Woodhouse /* Set number of address bytes */ 2335467fb02SDavid Woodhouse if (adrbytes) 2345467fb02SDavid Woodhouse ctl1 |= ((adrbytes-1)|8) << 27; 2355467fb02SDavid Woodhouse 2365467fb02SDavid Woodhouse if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { 2375467fb02SDavid Woodhouse /* Ignore the first command of a pair; the hardware 2385467fb02SDavid Woodhouse deals with them both at once, later */ 2395467fb02SDavid Woodhouse cafe->ctl1 = ctl1; 2408dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", 2415467fb02SDavid Woodhouse cafe->ctl1, cafe->datalen); 2425467fb02SDavid Woodhouse return; 2435467fb02SDavid Woodhouse } 2445467fb02SDavid Woodhouse /* RNDOUT and READ0 commands need a following byte */ 2455467fb02SDavid Woodhouse if (command == NAND_CMD_RNDOUT) 246195a253bSDavid Woodhouse cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); 2475467fb02SDavid Woodhouse else if (command == NAND_CMD_READ0 && mtd->writesize > 512) 248195a253bSDavid Woodhouse cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); 2495467fb02SDavid Woodhouse 2505467fb02SDavid Woodhouse do_command: 2518dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", 252195a253bSDavid Woodhouse cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); 253fbad5696SDavid Woodhouse 2545467fb02SDavid Woodhouse /* NB: The datasheet lies -- we really should be subtracting 1 here */ 255195a253bSDavid Woodhouse cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); 256195a253bSDavid Woodhouse cafe_writel(cafe, 0x90000000, NAND_IRQ); 25773a27db8SMiquel Raynal if (cafe->usedma && (ctl1 & (3<<25))) { 2585467fb02SDavid Woodhouse uint32_t dmactl = 0xc0000000 + cafe->datalen; 2595467fb02SDavid Woodhouse /* If WR or RD bits set, set up DMA */ 2605467fb02SDavid Woodhouse if (ctl1 & (1<<26)) { 2615467fb02SDavid Woodhouse /* It's a read */ 2625467fb02SDavid Woodhouse dmactl |= (1<<29); 2635467fb02SDavid Woodhouse /* ... so it's done when the DMA is done, not just 2645467fb02SDavid Woodhouse the command. */ 2655467fb02SDavid Woodhouse doneint = 0x10000000; 2665467fb02SDavid Woodhouse } 267195a253bSDavid Woodhouse cafe_writel(cafe, dmactl, NAND_DMA_CTRL); 2685467fb02SDavid Woodhouse } 2695467fb02SDavid Woodhouse cafe->datalen = 0; 2705467fb02SDavid Woodhouse 271be8444bdSDavid Woodhouse if (unlikely(regdebug)) { 272be8444bdSDavid Woodhouse int i; 273be8444bdSDavid Woodhouse printk("About to write command %08x to register 0\n", ctl1); 274be8444bdSDavid Woodhouse for (i=4; i< 0x5c; i+=4) 2755467fb02SDavid Woodhouse printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); 276fbad5696SDavid Woodhouse } 277be8444bdSDavid Woodhouse 278195a253bSDavid Woodhouse cafe_writel(cafe, ctl1, NAND_CTRL1); 2795467fb02SDavid Woodhouse /* Apply this short delay always to ensure that we do wait tWB in 2805467fb02SDavid Woodhouse * any case on any machine. */ 2815467fb02SDavid Woodhouse ndelay(100); 2825467fb02SDavid Woodhouse 2835467fb02SDavid Woodhouse if (1) { 2842a7295b2SAndrew Morton int c; 2855467fb02SDavid Woodhouse uint32_t irqs; 2865467fb02SDavid Woodhouse 2872a7295b2SAndrew Morton for (c = 500000; c != 0; c--) { 288195a253bSDavid Woodhouse irqs = cafe_readl(cafe, NAND_IRQ); 2895467fb02SDavid Woodhouse if (irqs & doneint) 2905467fb02SDavid Woodhouse break; 2915467fb02SDavid Woodhouse udelay(1); 2928dd851deSDavid Woodhouse if (!(c % 100000)) 2938dd851deSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); 2945467fb02SDavid Woodhouse cpu_relax(); 2955467fb02SDavid Woodhouse } 296195a253bSDavid Woodhouse cafe_writel(cafe, doneint, NAND_IRQ); 297a020727bSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", 298195a253bSDavid Woodhouse command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); 2995467fb02SDavid Woodhouse } 3005467fb02SDavid Woodhouse 301cad40654SDavid Woodhouse WARN_ON(cafe->ctl2 & (1<<30)); 3025467fb02SDavid Woodhouse 3035467fb02SDavid Woodhouse switch (command) { 3045467fb02SDavid Woodhouse 3055467fb02SDavid Woodhouse case NAND_CMD_CACHEDPROG: 3065467fb02SDavid Woodhouse case NAND_CMD_PAGEPROG: 3075467fb02SDavid Woodhouse case NAND_CMD_ERASE1: 3085467fb02SDavid Woodhouse case NAND_CMD_ERASE2: 3095467fb02SDavid Woodhouse case NAND_CMD_SEQIN: 3105467fb02SDavid Woodhouse case NAND_CMD_RNDIN: 3115467fb02SDavid Woodhouse case NAND_CMD_STATUS: 3125467fb02SDavid Woodhouse case NAND_CMD_RNDOUT: 313195a253bSDavid Woodhouse cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); 3145467fb02SDavid Woodhouse return; 3155467fb02SDavid Woodhouse } 3165467fb02SDavid Woodhouse nand_wait_ready(mtd); 317195a253bSDavid Woodhouse cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); 3185467fb02SDavid Woodhouse } 3195467fb02SDavid Woodhouse 3205467fb02SDavid Woodhouse static void cafe_select_chip(struct mtd_info *mtd, int chipnr) 3215467fb02SDavid Woodhouse { 3224bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 323d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 324048c37b4SDavid Woodhouse 325048c37b4SDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); 326048c37b4SDavid Woodhouse 327048c37b4SDavid Woodhouse /* Mask the appropriate bit into the stored value of ctl1 328048c37b4SDavid Woodhouse which will be used by cafe_nand_cmdfunc() */ 329048c37b4SDavid Woodhouse if (chipnr) 330048c37b4SDavid Woodhouse cafe->ctl1 |= CTRL1_CHIPSELECT; 331048c37b4SDavid Woodhouse else 332048c37b4SDavid Woodhouse cafe->ctl1 &= ~CTRL1_CHIPSELECT; 3335467fb02SDavid Woodhouse } 334fbad5696SDavid Woodhouse 33567cd724fSAlan Cox static irqreturn_t cafe_nand_interrupt(int irq, void *id) 3365467fb02SDavid Woodhouse { 3375467fb02SDavid Woodhouse struct mtd_info *mtd = id; 3384bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 339d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 340195a253bSDavid Woodhouse uint32_t irqs = cafe_readl(cafe, NAND_IRQ); 341195a253bSDavid Woodhouse cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); 3425467fb02SDavid Woodhouse if (!irqs) 3435467fb02SDavid Woodhouse return IRQ_NONE; 3445467fb02SDavid Woodhouse 345195a253bSDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); 3465467fb02SDavid Woodhouse return IRQ_HANDLED; 3475467fb02SDavid Woodhouse } 3485467fb02SDavid Woodhouse 3495467fb02SDavid Woodhouse static int cafe_nand_write_oob(struct mtd_info *mtd, 3505467fb02SDavid Woodhouse struct nand_chip *chip, int page) 3515467fb02SDavid Woodhouse { 35297d90da8SBoris Brezillon return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, 35397d90da8SBoris Brezillon mtd->oobsize); 3545467fb02SDavid Woodhouse } 3555467fb02SDavid Woodhouse 3565467fb02SDavid Woodhouse /* Don't use -- use nand_read_oob_std for now */ 3575467fb02SDavid Woodhouse static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, 3585c2ffb11SShmulik Ladkani int page) 3595467fb02SDavid Woodhouse { 36097d90da8SBoris Brezillon return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); 3615467fb02SDavid Woodhouse } 3625467fb02SDavid Woodhouse /** 3637854d3f7SBrian Norris * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read 3645467fb02SDavid Woodhouse * @mtd: mtd info structure 3655467fb02SDavid Woodhouse * @chip: nand chip info structure 3665467fb02SDavid Woodhouse * @buf: buffer to store read data 3671fbb938dSBrian Norris * @oob_required: caller expects OOB data read to chip->oob_poi 3685467fb02SDavid Woodhouse * 369b9bc815cSBrian Norris * The hw generator calculates the error syndrome automatically. Therefore 3705467fb02SDavid Woodhouse * we need a special oob layout and handling. 3715467fb02SDavid Woodhouse */ 3725467fb02SDavid Woodhouse static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, 3731fbb938dSBrian Norris uint8_t *buf, int oob_required, int page) 3745467fb02SDavid Woodhouse { 375d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 3763f91e94fSMike Dunn unsigned int max_bitflips = 0; 3775467fb02SDavid Woodhouse 378fbad5696SDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", 379195a253bSDavid Woodhouse cafe_readl(cafe, NAND_ECC_RESULT), 380195a253bSDavid Woodhouse cafe_readl(cafe, NAND_ECC_SYN01)); 3815467fb02SDavid Woodhouse 38225f815f6SBoris Brezillon nand_read_page_op(chip, page, 0, buf, mtd->writesize); 3835467fb02SDavid Woodhouse chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); 3845467fb02SDavid Woodhouse 385195a253bSDavid Woodhouse if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { 3868c61b7a7SSegher Boessenkool unsigned short syn[8], pat[4]; 3878c61b7a7SSegher Boessenkool int pos[4]; 3888c61b7a7SSegher Boessenkool u8 *oob = chip->oob_poi; 3898c61b7a7SSegher Boessenkool int i, n; 39004459d7cSDavid Woodhouse 39104459d7cSDavid Woodhouse for (i=0; i<8; i+=2) { 392195a253bSDavid Woodhouse uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); 39321633981SThomas Gleixner 39421633981SThomas Gleixner syn[i] = cafe->rs->codec->index_of[tmp & 0xfff]; 39521633981SThomas Gleixner syn[i+1] = cafe->rs->codec->index_of[(tmp >> 16) & 0xfff]; 39604459d7cSDavid Woodhouse } 39704459d7cSDavid Woodhouse 3988c61b7a7SSegher Boessenkool n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, 3998c61b7a7SSegher Boessenkool pat); 4008c61b7a7SSegher Boessenkool 4018c61b7a7SSegher Boessenkool for (i = 0; i < n; i++) { 4028c61b7a7SSegher Boessenkool int p = pos[i]; 4038c61b7a7SSegher Boessenkool 4048c61b7a7SSegher Boessenkool /* The 12-bit symbols are mapped to bytes here */ 4058c61b7a7SSegher Boessenkool 4068c61b7a7SSegher Boessenkool if (p > 1374) { 4078c61b7a7SSegher Boessenkool /* out of range */ 4088c61b7a7SSegher Boessenkool n = -1374; 4098c61b7a7SSegher Boessenkool } else if (p == 0) { 4108c61b7a7SSegher Boessenkool /* high four bits do not correspond to data */ 4118c61b7a7SSegher Boessenkool if (pat[i] > 0xff) 4128c61b7a7SSegher Boessenkool n = -2048; 4138c61b7a7SSegher Boessenkool else 4148c61b7a7SSegher Boessenkool buf[0] ^= pat[i]; 4158c61b7a7SSegher Boessenkool } else if (p == 1365) { 4168c61b7a7SSegher Boessenkool buf[2047] ^= pat[i] >> 4; 4178c61b7a7SSegher Boessenkool oob[0] ^= pat[i] << 4; 4188c61b7a7SSegher Boessenkool } else if (p > 1365) { 4198c61b7a7SSegher Boessenkool if ((p & 1) == 1) { 4208c61b7a7SSegher Boessenkool oob[3*p/2 - 2048] ^= pat[i] >> 4; 4218c61b7a7SSegher Boessenkool oob[3*p/2 - 2047] ^= pat[i] << 4; 4228c61b7a7SSegher Boessenkool } else { 4238c61b7a7SSegher Boessenkool oob[3*p/2 - 2049] ^= pat[i] >> 8; 4248c61b7a7SSegher Boessenkool oob[3*p/2 - 2048] ^= pat[i]; 4258c61b7a7SSegher Boessenkool } 4268c61b7a7SSegher Boessenkool } else if ((p & 1) == 1) { 4278c61b7a7SSegher Boessenkool buf[3*p/2] ^= pat[i] >> 4; 4288c61b7a7SSegher Boessenkool buf[3*p/2 + 1] ^= pat[i] << 4; 4298c61b7a7SSegher Boessenkool } else { 4308c61b7a7SSegher Boessenkool buf[3*p/2 - 1] ^= pat[i] >> 8; 4318c61b7a7SSegher Boessenkool buf[3*p/2] ^= pat[i]; 4328c61b7a7SSegher Boessenkool } 4338c61b7a7SSegher Boessenkool } 4348c61b7a7SSegher Boessenkool 4358c61b7a7SSegher Boessenkool if (n < 0) { 436be8444bdSDavid Woodhouse dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", 437be8444bdSDavid Woodhouse cafe_readl(cafe, NAND_ADDR2) * 2048); 438be8444bdSDavid Woodhouse for (i = 0; i < 0x5c; i += 4) 439be8444bdSDavid Woodhouse printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); 44004459d7cSDavid Woodhouse mtd->ecc_stats.failed++; 44104459d7cSDavid Woodhouse } else { 4428c61b7a7SSegher Boessenkool dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); 4438c61b7a7SSegher Boessenkool mtd->ecc_stats.corrected += n; 4443f91e94fSMike Dunn max_bitflips = max_t(unsigned int, max_bitflips, n); 44504459d7cSDavid Woodhouse } 44604459d7cSDavid Woodhouse } 44704459d7cSDavid Woodhouse 4483f91e94fSMike Dunn return max_bitflips; 4495467fb02SDavid Woodhouse } 4505467fb02SDavid Woodhouse 451a8ed6e66SBoris Brezillon static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section, 452a8ed6e66SBoris Brezillon struct mtd_oob_region *oobregion) 453a8ed6e66SBoris Brezillon { 454a8ed6e66SBoris Brezillon struct nand_chip *chip = mtd_to_nand(mtd); 455a8ed6e66SBoris Brezillon 456a8ed6e66SBoris Brezillon if (section) 457a8ed6e66SBoris Brezillon return -ERANGE; 458a8ed6e66SBoris Brezillon 459a8ed6e66SBoris Brezillon oobregion->offset = 0; 460a8ed6e66SBoris Brezillon oobregion->length = chip->ecc.total; 461a8ed6e66SBoris Brezillon 462a8ed6e66SBoris Brezillon return 0; 463a8ed6e66SBoris Brezillon } 464a8ed6e66SBoris Brezillon 465a8ed6e66SBoris Brezillon static int cafe_ooblayout_free(struct mtd_info *mtd, int section, 466a8ed6e66SBoris Brezillon struct mtd_oob_region *oobregion) 467a8ed6e66SBoris Brezillon { 468a8ed6e66SBoris Brezillon struct nand_chip *chip = mtd_to_nand(mtd); 469a8ed6e66SBoris Brezillon 470a8ed6e66SBoris Brezillon if (section) 471a8ed6e66SBoris Brezillon return -ERANGE; 472a8ed6e66SBoris Brezillon 473a8ed6e66SBoris Brezillon oobregion->offset = chip->ecc.total; 474a8ed6e66SBoris Brezillon oobregion->length = mtd->oobsize - chip->ecc.total; 475a8ed6e66SBoris Brezillon 476a8ed6e66SBoris Brezillon return 0; 477a8ed6e66SBoris Brezillon } 478a8ed6e66SBoris Brezillon 479a8ed6e66SBoris Brezillon static const struct mtd_ooblayout_ops cafe_ooblayout_ops = { 480a8ed6e66SBoris Brezillon .ecc = cafe_ooblayout_ecc, 481a8ed6e66SBoris Brezillon .free = cafe_ooblayout_free, 4828dd851deSDavid Woodhouse }; 4838dd851deSDavid Woodhouse 4848dd851deSDavid Woodhouse /* Ick. The BBT code really ought to be able to work this bit out 485fbad5696SDavid Woodhouse for itself from the above, at least for the 2KiB case */ 486fbad5696SDavid Woodhouse static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; 487fbad5696SDavid Woodhouse static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; 488fbad5696SDavid Woodhouse 489fbad5696SDavid Woodhouse static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; 490fbad5696SDavid Woodhouse static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; 491fbad5696SDavid Woodhouse 4928dd851deSDavid Woodhouse 4938dd851deSDavid Woodhouse static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { 4948dd851deSDavid Woodhouse .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 495048c37b4SDavid Woodhouse | NAND_BBT_2BIT | NAND_BBT_VERSION, 4968dd851deSDavid Woodhouse .offs = 14, 4978dd851deSDavid Woodhouse .len = 4, 4988dd851deSDavid Woodhouse .veroffs = 18, 4998dd851deSDavid Woodhouse .maxblocks = 4, 500fbad5696SDavid Woodhouse .pattern = cafe_bbt_pattern_2048 5018dd851deSDavid Woodhouse }; 5028dd851deSDavid Woodhouse 5038dd851deSDavid Woodhouse static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { 5048dd851deSDavid Woodhouse .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 505048c37b4SDavid Woodhouse | NAND_BBT_2BIT | NAND_BBT_VERSION, 5068dd851deSDavid Woodhouse .offs = 14, 5078dd851deSDavid Woodhouse .len = 4, 5088dd851deSDavid Woodhouse .veroffs = 18, 5098dd851deSDavid Woodhouse .maxblocks = 4, 510fbad5696SDavid Woodhouse .pattern = cafe_mirror_pattern_2048 5118dd851deSDavid Woodhouse }; 5128dd851deSDavid Woodhouse 513fbad5696SDavid Woodhouse static struct nand_bbt_descr cafe_bbt_main_descr_512 = { 514fbad5696SDavid Woodhouse .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 515048c37b4SDavid Woodhouse | NAND_BBT_2BIT | NAND_BBT_VERSION, 516fbad5696SDavid Woodhouse .offs = 14, 517fbad5696SDavid Woodhouse .len = 1, 518fbad5696SDavid Woodhouse .veroffs = 15, 519fbad5696SDavid Woodhouse .maxblocks = 4, 520fbad5696SDavid Woodhouse .pattern = cafe_bbt_pattern_512 521fbad5696SDavid Woodhouse }; 522fbad5696SDavid Woodhouse 523fbad5696SDavid Woodhouse static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { 524fbad5696SDavid Woodhouse .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 525048c37b4SDavid Woodhouse | NAND_BBT_2BIT | NAND_BBT_VERSION, 526fbad5696SDavid Woodhouse .offs = 14, 527fbad5696SDavid Woodhouse .len = 1, 528fbad5696SDavid Woodhouse .veroffs = 15, 529fbad5696SDavid Woodhouse .maxblocks = 4, 530fbad5696SDavid Woodhouse .pattern = cafe_mirror_pattern_512 531fbad5696SDavid Woodhouse }; 532fbad5696SDavid Woodhouse 533fbad5696SDavid Woodhouse 534fdbad98dSJosh Wu static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, 5351fbb938dSBrian Norris struct nand_chip *chip, 53645aaeff9SBoris BREZILLON const uint8_t *buf, int oob_required, 53745aaeff9SBoris BREZILLON int page) 5385467fb02SDavid Woodhouse { 539d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 5405467fb02SDavid Woodhouse 54125f815f6SBoris Brezillon nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); 5428dd851deSDavid Woodhouse chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); 5435467fb02SDavid Woodhouse 5445467fb02SDavid Woodhouse /* Set up ECC autogeneration */ 545cad40654SDavid Woodhouse cafe->ctl2 |= (1<<30); 546fdbad98dSJosh Wu 54725f815f6SBoris Brezillon return nand_prog_page_end_op(chip); 5485467fb02SDavid Woodhouse } 5495467fb02SDavid Woodhouse 5509f3e0429SArchit Taneja static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs) 5518dd851deSDavid Woodhouse { 5528dd851deSDavid Woodhouse return 0; 5538dd851deSDavid Woodhouse } 5545467fb02SDavid Woodhouse 5558c61b7a7SSegher Boessenkool /* F_2[X]/(X**6+X+1) */ 55606f25510SBill Pemberton static unsigned short gf64_mul(u8 a, u8 b) 5578c61b7a7SSegher Boessenkool { 5588c61b7a7SSegher Boessenkool u8 c; 5598c61b7a7SSegher Boessenkool unsigned int i; 5608c61b7a7SSegher Boessenkool 5618c61b7a7SSegher Boessenkool c = 0; 5628c61b7a7SSegher Boessenkool for (i = 0; i < 6; i++) { 5638c61b7a7SSegher Boessenkool if (a & 1) 5648c61b7a7SSegher Boessenkool c ^= b; 5658c61b7a7SSegher Boessenkool a >>= 1; 5668c61b7a7SSegher Boessenkool b <<= 1; 5678c61b7a7SSegher Boessenkool if ((b & 0x40) != 0) 5688c61b7a7SSegher Boessenkool b ^= 0x43; 5698c61b7a7SSegher Boessenkool } 5708c61b7a7SSegher Boessenkool 5718c61b7a7SSegher Boessenkool return c; 5728c61b7a7SSegher Boessenkool } 5738c61b7a7SSegher Boessenkool 5748c61b7a7SSegher Boessenkool /* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ 57506f25510SBill Pemberton static u16 gf4096_mul(u16 a, u16 b) 5768c61b7a7SSegher Boessenkool { 5778c61b7a7SSegher Boessenkool u8 ah, al, bh, bl, ch, cl; 5788c61b7a7SSegher Boessenkool 5798c61b7a7SSegher Boessenkool ah = a >> 6; 5808c61b7a7SSegher Boessenkool al = a & 0x3f; 5818c61b7a7SSegher Boessenkool bh = b >> 6; 5828c61b7a7SSegher Boessenkool bl = b & 0x3f; 5838c61b7a7SSegher Boessenkool 5848c61b7a7SSegher Boessenkool ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); 5858c61b7a7SSegher Boessenkool cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); 5868c61b7a7SSegher Boessenkool 5878c61b7a7SSegher Boessenkool return (ch << 6) ^ cl; 5888c61b7a7SSegher Boessenkool } 5898c61b7a7SSegher Boessenkool 59006f25510SBill Pemberton static int cafe_mul(int x) 5918c61b7a7SSegher Boessenkool { 5928c61b7a7SSegher Boessenkool if (x == 0) 5938c61b7a7SSegher Boessenkool return 1; 5948c61b7a7SSegher Boessenkool return gf4096_mul(x, 0xe01); 5958c61b7a7SSegher Boessenkool } 5968c61b7a7SSegher Boessenkool 59773a27db8SMiquel Raynal static int cafe_nand_attach_chip(struct nand_chip *chip) 59873a27db8SMiquel Raynal { 59973a27db8SMiquel Raynal struct mtd_info *mtd = nand_to_mtd(chip); 60073a27db8SMiquel Raynal struct cafe_priv *cafe = nand_get_controller_data(chip); 60173a27db8SMiquel Raynal int err = 0; 60273a27db8SMiquel Raynal 60373a27db8SMiquel Raynal cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112, 60473a27db8SMiquel Raynal &cafe->dmaaddr, GFP_KERNEL); 60573a27db8SMiquel Raynal if (!cafe->dmabuf) 60673a27db8SMiquel Raynal return -ENOMEM; 60773a27db8SMiquel Raynal 60873a27db8SMiquel Raynal /* Set up DMA address */ 60973a27db8SMiquel Raynal cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0); 61073a27db8SMiquel Raynal cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1); 61173a27db8SMiquel Raynal 61273a27db8SMiquel Raynal cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", 61373a27db8SMiquel Raynal cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); 61473a27db8SMiquel Raynal 61573a27db8SMiquel Raynal /* Restore the DMA flag */ 61673a27db8SMiquel Raynal cafe->usedma = usedma; 61773a27db8SMiquel Raynal 61873a27db8SMiquel Raynal cafe->ctl2 = BIT(27); /* Reed-Solomon ECC */ 61973a27db8SMiquel Raynal if (mtd->writesize == 2048) 62073a27db8SMiquel Raynal cafe->ctl2 |= BIT(29); /* 2KiB page size */ 62173a27db8SMiquel Raynal 62273a27db8SMiquel Raynal /* Set up ECC according to the type of chip we found */ 62373a27db8SMiquel Raynal mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); 62473a27db8SMiquel Raynal if (mtd->writesize == 2048) { 62573a27db8SMiquel Raynal cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; 62673a27db8SMiquel Raynal cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; 62773a27db8SMiquel Raynal } else if (mtd->writesize == 512) { 62873a27db8SMiquel Raynal cafe->nand.bbt_td = &cafe_bbt_main_descr_512; 62973a27db8SMiquel Raynal cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; 63073a27db8SMiquel Raynal } else { 63173a27db8SMiquel Raynal dev_warn(&cafe->pdev->dev, 63273a27db8SMiquel Raynal "Unexpected NAND flash writesize %d. Aborting\n", 63373a27db8SMiquel Raynal mtd->writesize); 63473a27db8SMiquel Raynal err = -ENOTSUPP; 63573a27db8SMiquel Raynal goto out_free_dma; 63673a27db8SMiquel Raynal } 63773a27db8SMiquel Raynal 63873a27db8SMiquel Raynal cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; 63973a27db8SMiquel Raynal cafe->nand.ecc.size = mtd->writesize; 64073a27db8SMiquel Raynal cafe->nand.ecc.bytes = 14; 64173a27db8SMiquel Raynal cafe->nand.ecc.strength = 4; 64273a27db8SMiquel Raynal cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; 64373a27db8SMiquel Raynal cafe->nand.ecc.write_oob = cafe_nand_write_oob; 64473a27db8SMiquel Raynal cafe->nand.ecc.read_page = cafe_nand_read_page; 64573a27db8SMiquel Raynal cafe->nand.ecc.read_oob = cafe_nand_read_oob; 64673a27db8SMiquel Raynal 64773a27db8SMiquel Raynal return 0; 64873a27db8SMiquel Raynal 64973a27db8SMiquel Raynal out_free_dma: 65073a27db8SMiquel Raynal dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); 65173a27db8SMiquel Raynal 65273a27db8SMiquel Raynal return err; 65373a27db8SMiquel Raynal } 65473a27db8SMiquel Raynal 65573a27db8SMiquel Raynal static void cafe_nand_detach_chip(struct nand_chip *chip) 65673a27db8SMiquel Raynal { 65773a27db8SMiquel Raynal struct cafe_priv *cafe = nand_get_controller_data(chip); 65873a27db8SMiquel Raynal 65973a27db8SMiquel Raynal dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); 66073a27db8SMiquel Raynal } 66173a27db8SMiquel Raynal 66273a27db8SMiquel Raynal static const struct nand_controller_ops cafe_nand_controller_ops = { 66373a27db8SMiquel Raynal .attach_chip = cafe_nand_attach_chip, 66473a27db8SMiquel Raynal .detach_chip = cafe_nand_detach_chip, 66573a27db8SMiquel Raynal }; 66673a27db8SMiquel Raynal 66706f25510SBill Pemberton static int cafe_nand_probe(struct pci_dev *pdev, 6685467fb02SDavid Woodhouse const struct pci_device_id *ent) 6695467fb02SDavid Woodhouse { 6705467fb02SDavid Woodhouse struct mtd_info *mtd; 6715467fb02SDavid Woodhouse struct cafe_priv *cafe; 6725467fb02SDavid Woodhouse uint32_t ctrl; 6735467fb02SDavid Woodhouse int err = 0; 6745467fb02SDavid Woodhouse 67506ed24e5SDavid Woodhouse /* Very old versions shared the same PCI ident for all three 67606ed24e5SDavid Woodhouse functions on the chip. Verify the class too... */ 67706ed24e5SDavid Woodhouse if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) 67806ed24e5SDavid Woodhouse return -ENODEV; 67906ed24e5SDavid Woodhouse 6805467fb02SDavid Woodhouse err = pci_enable_device(pdev); 6815467fb02SDavid Woodhouse if (err) 6825467fb02SDavid Woodhouse return err; 6835467fb02SDavid Woodhouse 6845467fb02SDavid Woodhouse pci_set_master(pdev); 6855467fb02SDavid Woodhouse 686e787dfd1SBoris BREZILLON cafe = kzalloc(sizeof(*cafe), GFP_KERNEL); 687e787dfd1SBoris BREZILLON if (!cafe) 6885467fb02SDavid Woodhouse return -ENOMEM; 6895467fb02SDavid Woodhouse 690e787dfd1SBoris BREZILLON mtd = nand_to_mtd(&cafe->nand); 691c451c7c4SDavid Woodhouse mtd->dev.parent = &pdev->dev; 692d699ed25SBoris BREZILLON nand_set_controller_data(&cafe->nand, cafe); 6935467fb02SDavid Woodhouse 6945467fb02SDavid Woodhouse cafe->pdev = pdev; 6955467fb02SDavid Woodhouse cafe->mmio = pci_iomap(pdev, 0, 0); 6965467fb02SDavid Woodhouse if (!cafe->mmio) { 6975467fb02SDavid Woodhouse dev_warn(&pdev->dev, "failed to iomap\n"); 6985467fb02SDavid Woodhouse err = -ENOMEM; 6995467fb02SDavid Woodhouse goto out_free_mtd; 7005467fb02SDavid Woodhouse } 7015467fb02SDavid Woodhouse 7028c61b7a7SSegher Boessenkool cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); 7038c61b7a7SSegher Boessenkool if (!cafe->rs) { 7048c61b7a7SSegher Boessenkool err = -ENOMEM; 7058c61b7a7SSegher Boessenkool goto out_ior; 7068c61b7a7SSegher Boessenkool } 7078c61b7a7SSegher Boessenkool 7085467fb02SDavid Woodhouse cafe->nand.cmdfunc = cafe_nand_cmdfunc; 7095467fb02SDavid Woodhouse cafe->nand.dev_ready = cafe_device_ready; 7105467fb02SDavid Woodhouse cafe->nand.read_byte = cafe_read_byte; 7115467fb02SDavid Woodhouse cafe->nand.read_buf = cafe_read_buf; 7125467fb02SDavid Woodhouse cafe->nand.write_buf = cafe_write_buf; 7135467fb02SDavid Woodhouse cafe->nand.select_chip = cafe_select_chip; 714b958758eSMiquel Raynal cafe->nand.set_features = nand_get_set_features_notsupp; 715b958758eSMiquel Raynal cafe->nand.get_features = nand_get_set_features_notsupp; 7165467fb02SDavid Woodhouse 7175467fb02SDavid Woodhouse cafe->nand.chip_delay = 0; 7185467fb02SDavid Woodhouse 7195467fb02SDavid Woodhouse /* Enable the following for a flash based bad block table */ 720bb9ebd4eSBrian Norris cafe->nand.bbt_options = NAND_BBT_USE_FLASH; 7215467fb02SDavid Woodhouse 7228dd851deSDavid Woodhouse if (skipbbt) { 7238dd851deSDavid Woodhouse cafe->nand.options |= NAND_SKIP_BBTSCAN; 7248dd851deSDavid Woodhouse cafe->nand.block_bad = cafe_nand_block_bad; 7258dd851deSDavid Woodhouse } 7268dd851deSDavid Woodhouse 727527a4f45SDavid Woodhouse if (numtimings && numtimings != 3) { 728527a4f45SDavid Woodhouse dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); 729527a4f45SDavid Woodhouse } 730527a4f45SDavid Woodhouse 731527a4f45SDavid Woodhouse if (numtimings == 3) { 732527a4f45SDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", 7338e5368a1SDavid Woodhouse timing[0], timing[1], timing[2]); 734527a4f45SDavid Woodhouse } else { 7358e5368a1SDavid Woodhouse timing[0] = cafe_readl(cafe, NAND_TIMING1); 7368e5368a1SDavid Woodhouse timing[1] = cafe_readl(cafe, NAND_TIMING2); 7378e5368a1SDavid Woodhouse timing[2] = cafe_readl(cafe, NAND_TIMING3); 738527a4f45SDavid Woodhouse 7398e5368a1SDavid Woodhouse if (timing[0] | timing[1] | timing[2]) { 7408e5368a1SDavid Woodhouse cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", 7418e5368a1SDavid Woodhouse timing[0], timing[1], timing[2]); 742527a4f45SDavid Woodhouse } else { 743527a4f45SDavid Woodhouse dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); 7448e5368a1SDavid Woodhouse timing[0] = timing[1] = timing[2] = 0xffffffff; 745527a4f45SDavid Woodhouse } 746527a4f45SDavid Woodhouse } 747527a4f45SDavid Woodhouse 748dcc41bc8SDavid Woodhouse /* Start off by resetting the NAND controller completely */ 749195a253bSDavid Woodhouse cafe_writel(cafe, 1, NAND_RESET); 750195a253bSDavid Woodhouse cafe_writel(cafe, 0, NAND_RESET); 751195a253bSDavid Woodhouse 7528e5368a1SDavid Woodhouse cafe_writel(cafe, timing[0], NAND_TIMING1); 7538e5368a1SDavid Woodhouse cafe_writel(cafe, timing[1], NAND_TIMING2); 7548e5368a1SDavid Woodhouse cafe_writel(cafe, timing[2], NAND_TIMING3); 755dcc41bc8SDavid Woodhouse 756195a253bSDavid Woodhouse cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); 7572db6346fSThomas Gleixner err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, 7582db6346fSThomas Gleixner "CAFE NAND", mtd); 7595467fb02SDavid Woodhouse if (err) { 7605467fb02SDavid Woodhouse dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); 761f02ea4e6SHuang Shijie goto out_ior; 7625467fb02SDavid Woodhouse } 763f7c37d7bSDavid Woodhouse 7645467fb02SDavid Woodhouse /* Disable master reset, enable NAND clock */ 765195a253bSDavid Woodhouse ctrl = cafe_readl(cafe, GLOBAL_CTRL); 7665467fb02SDavid Woodhouse ctrl &= 0xffffeff0; 7675467fb02SDavid Woodhouse ctrl |= 0x00007000; 768195a253bSDavid Woodhouse cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); 769195a253bSDavid Woodhouse cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); 770195a253bSDavid Woodhouse cafe_writel(cafe, 0, NAND_DMA_CTRL); 7715467fb02SDavid Woodhouse 772195a253bSDavid Woodhouse cafe_writel(cafe, 0x7006, GLOBAL_CTRL); 773195a253bSDavid Woodhouse cafe_writel(cafe, 0x700a, GLOBAL_CTRL); 7745467fb02SDavid Woodhouse 775f02ea4e6SHuang Shijie /* Enable NAND IRQ in global IRQ mask register */ 776f02ea4e6SHuang Shijie cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); 777f02ea4e6SHuang Shijie cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", 778f02ea4e6SHuang Shijie cafe_readl(cafe, GLOBAL_CTRL), 779f02ea4e6SHuang Shijie cafe_readl(cafe, GLOBAL_IRQ_MASK)); 780f02ea4e6SHuang Shijie 78173a27db8SMiquel Raynal /* Do not use the DMA during the NAND identification */ 78273a27db8SMiquel Raynal cafe->usedma = 0; 783f02ea4e6SHuang Shijie 784f02ea4e6SHuang Shijie /* Scan to find existence of the device */ 78573a27db8SMiquel Raynal cafe->nand.dummy_controller.ops = &cafe_nand_controller_ops; 786*00ad378fSBoris Brezillon err = nand_scan(&cafe->nand, 2); 78772480e4eSMasahiro Yamada if (err) 788f02ea4e6SHuang Shijie goto out_irq; 789f02ea4e6SHuang Shijie 7905467fb02SDavid Woodhouse pci_set_drvdata(pdev, mtd); 7919c37f332SDavid Woodhouse 79268874414SPhilip Rakity mtd->name = "cafe_nand"; 793a446c998SMiquel Raynal err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); 794a446c998SMiquel Raynal if (err) 795a446c998SMiquel Raynal goto out_cleanup_nand; 7964d32de81SDmitry Eremin-Solenikov 7975467fb02SDavid Woodhouse goto out; 7985467fb02SDavid Woodhouse 799a446c998SMiquel Raynal out_cleanup_nand: 800a446c998SMiquel Raynal nand_cleanup(&cafe->nand); 8015467fb02SDavid Woodhouse out_irq: 8025467fb02SDavid Woodhouse /* Disable NAND IRQ in global IRQ mask register */ 803195a253bSDavid Woodhouse cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); 8045467fb02SDavid Woodhouse free_irq(pdev->irq, mtd); 8055467fb02SDavid Woodhouse out_ior: 8065467fb02SDavid Woodhouse pci_iounmap(pdev, cafe->mmio); 8075467fb02SDavid Woodhouse out_free_mtd: 808e787dfd1SBoris BREZILLON kfree(cafe); 8095467fb02SDavid Woodhouse out: 8105467fb02SDavid Woodhouse return err; 8115467fb02SDavid Woodhouse } 8125467fb02SDavid Woodhouse 813810b7e06SBill Pemberton static void cafe_nand_remove(struct pci_dev *pdev) 8145467fb02SDavid Woodhouse { 8155467fb02SDavid Woodhouse struct mtd_info *mtd = pci_get_drvdata(pdev); 8164bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 817d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 8185467fb02SDavid Woodhouse 8195467fb02SDavid Woodhouse /* Disable NAND IRQ in global IRQ mask register */ 820195a253bSDavid Woodhouse cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); 8215467fb02SDavid Woodhouse free_irq(pdev->irq, mtd); 8225467fb02SDavid Woodhouse nand_release(mtd); 8238c61b7a7SSegher Boessenkool free_rs(cafe->rs); 8245467fb02SDavid Woodhouse pci_iounmap(pdev, cafe->mmio); 825f880b07bSMasahiro Yamada dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); 826e787dfd1SBoris BREZILLON kfree(cafe); 8275467fb02SDavid Woodhouse } 8285467fb02SDavid Woodhouse 829377ace08SMárton Németh static const struct pci_device_id cafe_nand_tbl[] = { 830514fca43SDavid Woodhouse { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, 831514fca43SDavid Woodhouse PCI_ANY_ID, PCI_ANY_ID }, 83206ed24e5SDavid Woodhouse { } 8335467fb02SDavid Woodhouse }; 8345467fb02SDavid Woodhouse 8355467fb02SDavid Woodhouse MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); 8365467fb02SDavid Woodhouse 8371fcf8ce5SDavid Woodhouse static int cafe_nand_resume(struct pci_dev *pdev) 8381fcf8ce5SDavid Woodhouse { 8391fcf8ce5SDavid Woodhouse uint32_t ctrl; 8401fcf8ce5SDavid Woodhouse struct mtd_info *mtd = pci_get_drvdata(pdev); 8414bd4ebccSBoris BREZILLON struct nand_chip *chip = mtd_to_nand(mtd); 842d699ed25SBoris BREZILLON struct cafe_priv *cafe = nand_get_controller_data(chip); 8431fcf8ce5SDavid Woodhouse 8441fcf8ce5SDavid Woodhouse /* Start off by resetting the NAND controller completely */ 8451fcf8ce5SDavid Woodhouse cafe_writel(cafe, 1, NAND_RESET); 8461fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0, NAND_RESET); 8471fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); 8481fcf8ce5SDavid Woodhouse 8491fcf8ce5SDavid Woodhouse /* Restore timing configuration */ 8501fcf8ce5SDavid Woodhouse cafe_writel(cafe, timing[0], NAND_TIMING1); 8511fcf8ce5SDavid Woodhouse cafe_writel(cafe, timing[1], NAND_TIMING2); 8521fcf8ce5SDavid Woodhouse cafe_writel(cafe, timing[2], NAND_TIMING3); 8531fcf8ce5SDavid Woodhouse 8541fcf8ce5SDavid Woodhouse /* Disable master reset, enable NAND clock */ 8551fcf8ce5SDavid Woodhouse ctrl = cafe_readl(cafe, GLOBAL_CTRL); 8561fcf8ce5SDavid Woodhouse ctrl &= 0xffffeff0; 8571fcf8ce5SDavid Woodhouse ctrl |= 0x00007000; 8581fcf8ce5SDavid Woodhouse cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); 8591fcf8ce5SDavid Woodhouse cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); 8601fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0, NAND_DMA_CTRL); 8611fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0x7006, GLOBAL_CTRL); 8621fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0x700a, GLOBAL_CTRL); 8631fcf8ce5SDavid Woodhouse 8641fcf8ce5SDavid Woodhouse /* Set up DMA address */ 8651fcf8ce5SDavid Woodhouse cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); 8661fcf8ce5SDavid Woodhouse if (sizeof(cafe->dmaaddr) > 4) 8671fcf8ce5SDavid Woodhouse /* Shift in two parts to shut the compiler up */ 8681fcf8ce5SDavid Woodhouse cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); 8691fcf8ce5SDavid Woodhouse else 8701fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0, NAND_DMA_ADDR1); 8711fcf8ce5SDavid Woodhouse 8721fcf8ce5SDavid Woodhouse /* Enable NAND IRQ in global IRQ mask register */ 8731fcf8ce5SDavid Woodhouse cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); 8741fcf8ce5SDavid Woodhouse return 0; 8751fcf8ce5SDavid Woodhouse } 8761fcf8ce5SDavid Woodhouse 8775467fb02SDavid Woodhouse static struct pci_driver cafe_nand_pci_driver = { 8785467fb02SDavid Woodhouse .name = "CAFÉ NAND", 8795467fb02SDavid Woodhouse .id_table = cafe_nand_tbl, 8805467fb02SDavid Woodhouse .probe = cafe_nand_probe, 8815153b88cSBill Pemberton .remove = cafe_nand_remove, 8825467fb02SDavid Woodhouse .resume = cafe_nand_resume, 8835467fb02SDavid Woodhouse }; 8845467fb02SDavid Woodhouse 8854d16cd65SAxel Lin module_pci_driver(cafe_nand_pci_driver); 8865467fb02SDavid Woodhouse 8875467fb02SDavid Woodhouse MODULE_LICENSE("GPL"); 8885467fb02SDavid Woodhouse MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 889f7c37d7bSDavid Woodhouse MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); 890