Hi Ben, oh dear, how i hate the ebu ...
i will start by testing this patch on the fritz3370 where i have been seeing jffs2 errors. in general i am all for merging this, i will need a few days to think about this. John On 21/01/2014 20:50, Ben Mulvihill wrote: > Together with other contributors at the now defunct psidoc site, I > have produced a device tree and some patches to get the BT Home Hub 2B > (Lantiq Danube-S) working with recent trunk builds. More work is needed > before they are ready for submitting, but before going any further, I'd > be very grateful for some advice from John Crispin or anyone else who > wants to chip in. > > The Home Hub 2B has both nand and nor flash connected to the ebu, plus > an ath9k pci wireless card, and the main difficulty is getting all three > working at the same time without interfering with each other. The nand > driver currently in trunk works fine, provided that open-drain is > enabled on the ebu cs1 pin, and provided that wireless is off. However, > the only way to get the nor chip to work reliably seems to be disable > open-drain on ebu cs1, at which point the nand driver generates jffs2 > errors. Regardless whether open-drain is enabled or not, switching on > wireless also causes jffs2 errors. > > The solution we are using (basically an updated version of a patch > produced by BTSimonh for the 3.3.8 kernel) is to disable open drain on > ebu cs1 and patch the nand driver as follows: > - move locking from the individual read and write functions to the > chip select function, so that an entire read or write operation can > complete uninterrupted; > - mask pci request lines during nand access; > - additional waiting after each nand command. > An option in the device tree enables or disables the above. > The latest version of my patch is appended to the message, for comment > only - I am not submitting it at this stage. > > It solves the problem, and the factory firmware does something similar, > but it feels like a hack to work round a bug elsewhere. Could it not be > that that the ebu needs to be set up differently for this hardware > configuration? Is some chip select logic needed in the nor driver? > I'd love to understand what the settings in the ebu registers actually > do, but am I right in thinking that there are no publicly available > datasheets for Lantiq SOCs? > > Any enlightenment you can provide will be gratefully received. > > Thank you, > > Ben Mulvihill > > --- a/drivers/mtd/nand/xway_nand.c 2014-01-21 20:21:11.000000000 +0100 > +++ b/drivers/mtd/nand/xway_nand.c 2014-01-21 20:29:16.000000000 +0100 > @@ -54,8 +54,28 @@ > #define NAND_CON_CSMUX (1 << 1) > #define NAND_CON_NANDM 1 > > +#define DANUBE_PCI_REG32( addr ) (*(volatile u32 *)(addr)) > +#define PCI_CR_PR_OFFSET (KSEG1+0x1E105400) > +#define PCI_CR_PC_ARB (PCI_CR_PR_OFFSET + 0x0080) > + > static u32 xway_latchcmd; > > +/* > + * req_mask provides a mechanism to prevent interference between > + * nand and pci (probably only relevant for the BT Home Hub 2B). > + * Setting it causes the corresponding pci req pins to be masked > + * during nand access, and also moves ebu locking from the read/write > + * functions to the chip select function to ensure that the whole > + * operation runs with interrupts disabled. > + * In addition it switches on some extra waiting in xway_cmd_ctrl(). > + * This seems to be necessary if the ebu_cs1 pin has open-drain disabled, > + * which in turn seems to be necessary for the nor chip to be recognised > + * reliably, on a board (Home Hub 2B again) which has both nor and nand. > + */ > + > +static __be32 req_mask = 0; > +static int pci_masked = 0; > + > static void xway_reset_chip(struct nand_chip *chip) > { > unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W; > @@ -80,15 +100,36 @@ > > static void xway_select_chip(struct mtd_info *mtd, int chip) > { > + static unsigned long flags; > > switch (chip) { > case -1: > ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON); > ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON); > + > + if (req_mask) { > + BUG_ON(!pci_masked); > + /* Unmask all external PCI request */ > + DANUBE_PCI_REG32(PCI_CR_PC_ARB) &= ~(req_mask << 16); > + pci_masked = 0; > + > + spin_unlock_irqrestore(&ebu_lock, flags); > + } > + > break; > case 0: > + if (req_mask) { > + BUG_ON(pci_masked); > + spin_lock_irqsave(&ebu_lock, flags); > + > + /* Mask all external PCI request */ > + DANUBE_PCI_REG32(PCI_CR_PC_ARB) |= (req_mask << 16); > + pci_masked = 1; > + } > + > ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON); > ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON); > + > break; > default: > BUG(); > @@ -101,6 +142,12 @@ > unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; > unsigned long flags; > > + if (req_mask) { > + if (cmd != NAND_CMD_STATUS) > + ltq_ebu_w32(EBU_NAND_WAIT, 0); /* Clear nand ready */ > + } > + > + > if (ctrl & NAND_CTRL_CHANGE) { > if (ctrl & NAND_CLE) > xway_latchcmd = NAND_WRITE_CMD; > @@ -109,11 +156,31 @@ > } > > if (cmd != NAND_CMD_NONE) { > - spin_lock_irqsave(&ebu_lock, flags); > + if (!req_mask) > + spin_lock_irqsave(&ebu_lock, flags); > writeb(cmd, (void __iomem *) (nandaddr | xway_latchcmd)); > while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) > ; > - spin_unlock_irqrestore(&ebu_lock, flags); > + if (!req_mask) > + spin_unlock_irqrestore(&ebu_lock, flags); > + } > + > + if (req_mask) { > + /* > + * program and erase have their own busy handlers > + * status and sequential in needs no delay > + */ > + switch (cmd) { > + case NAND_CMD_ERASE1: > + case NAND_CMD_SEQIN: > + case NAND_CMD_STATUS: > + case NAND_CMD_READID: > + return; > + } > + > + /* wait until command is processed */ > + while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD) == 0) > + ; > } > } > > @@ -129,9 +196,11 @@ > unsigned long flags; > int ret; > > - spin_lock_irqsave(&ebu_lock, flags); > + if (!req_mask) > + spin_lock_irqsave(&ebu_lock, flags); > ret = ltq_r8((void __iomem *)(nandaddr | NAND_READ_DATA)); > - spin_unlock_irqrestore(&ebu_lock, flags); > + if (!req_mask) > + spin_unlock_irqrestore(&ebu_lock, flags); > > return ret; > } > @@ -143,10 +212,12 @@ > unsigned long flags; > int i; > > - spin_lock_irqsave(&ebu_lock, flags); > + if (!req_mask) > + spin_lock_irqsave(&ebu_lock, flags); > for (i = 0; i < len; i++) > buf[i] = ltq_r8((void __iomem *)(nandaddr | NAND_READ_DATA)); > - spin_unlock_irqrestore(&ebu_lock, flags); > + if (!req_mask) > + spin_unlock_irqrestore(&ebu_lock, flags); > } > > static void xway_write_buf(struct mtd_info *mtd, const u_char *buf, int len) > @@ -156,16 +227,20 @@ > unsigned long flags; > int i; > > - spin_lock_irqsave(&ebu_lock, flags); > + if (!req_mask) > + spin_lock_irqsave(&ebu_lock, flags); > for (i = 0; i < len; i++) > ltq_w8(buf[i], (void __iomem *)nandaddr); > - spin_unlock_irqrestore(&ebu_lock, flags); > + if (!req_mask) > + spin_unlock_irqrestore(&ebu_lock, flags); > } > > static int xway_nand_probe(struct platform_device *pdev) > { > struct nand_chip *this = platform_get_drvdata(pdev); > unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; > + const __be32 *req_mask_ptr = of_get_property(pdev->dev.of_node, > + "req-mask", NULL); > const __be32 *cs = of_get_property(pdev->dev.of_node, > "lantiq,cs", NULL); > u32 cs_flag = 0; > @@ -174,6 +249,8 @@ > if (cs && (*cs == 1)) > cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1; > > + req_mask = (req_mask_ptr ? *req_mask_ptr : 0); > + > /* setup the EBU to run in NAND mode on our base addr */ > ltq_ebu_w32(CPHYSADDR(nandaddr) > | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1); > _______________________________________________ > openwrt-devel mailing list > openwrt-devel@lists.openwrt.org > https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel > _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel