From 1d10dcd041aaeae9fd7c821005692898a0303382 Mon Sep 17 00:00:00 2001 From: "Hunter, Jon" Date: Sat, 26 Jul 2008 18:59:16 -0500 Subject: Add support for OMAP5912 and OMAP16xx to usbdcore_omap1510.c Add support to drivers/usb/usbdcore_omap1510.c for OMAP5912 and OMAP16xx devices. Signed-off-by: Jon Hunter Signed-off-by: Markus Klotzbuecher --- drivers/usb/usbdcore_omap1510.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/usbdcore_omap1510.c b/drivers/usb/usbdcore_omap1510.c index 84bb936d869..4e3239f5809 100644 --- a/drivers/usb/usbdcore_omap1510.c +++ b/drivers/usb/usbdcore_omap1510.c @@ -28,7 +28,7 @@ #include -#if defined(CONFIG_OMAP1510) && defined(CONFIG_USB_DEVICE) +#if ((defined(CONFIG_OMAP1510) || defined(CONFIG_OMAP1610)) && defined(CONFIG_USB_DEVICE)) #include #ifdef CONFIG_OMAP_SX1 @@ -1109,21 +1109,43 @@ int udc_init (void) */ outw ((1 << 4) | (1 << 5), CLOCK_CTRL); UDCREG (CLOCK_CTRL); + +#ifdef CONFIG_OMAP1510 + /* This code was originally implemented for OMAP1510 and + * therefore is only applicable for OMAP1510 boards. For + * OMAP5912 or OMAP16xx the register APLL_CTRL does not + * exist and DPLL_CTRL is already configured. + */ + /* Set and check APLL */ outw (0x0008, APLL_CTRL); UDCREG (APLL_CTRL); /* Set and check DPLL */ outw (0x2210, DPLL_CTRL); UDCREG (DPLL_CTRL); - /* Set and check SOFT */ - outw ((1 << 4) | (1 << 3) | 1, SOFT_REQ); +#endif + /* Set and check SOFT + * The below line of code has been changed to perform a + * read-modify-write instead of a simple write for + * configuring the SOFT_REQ register. This allows the code + * to be compatible with OMAP5912 and OMAP16xx devices + */ + outw ((1 << 4) | (1 << 3) | 1 | (inw(SOFT_REQ)), SOFT_REQ); + /* Short delay to wait for DPLL */ udelay (1000); /* Print banner with device revision */ udc_rev = inw (UDC_REV) & 0xff; +#ifdef CONFIG_OMAP1510 printf ("USB: TI OMAP1510 USB function module rev %d.%d\n", udc_rev >> 4, udc_rev & 0xf); +#endif + +#ifdef CONFIG_OMAP1610 + printf ("USB: TI OMAP5912 USB function module rev %d.%d\n", + udc_rev >> 4, udc_rev & 0xf); +#endif #ifdef CONFIG_OMAP_SX1 i2c_read (0x32, 0x04, 1, &value, 1); -- cgit v1.2.3 From fd0f2f3796ff2a7a32d35deb1b7996e485849df7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 9 Jul 2008 21:07:38 +0900 Subject: usb: add support for R8A66597 usb controller add support for Renesas R8A66597 usb controller. This patch supports USB Host mode. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Markus Klotzbuecher --- drivers/usb/Makefile | 1 + drivers/usb/r8a66597-hcd.c | 924 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/r8a66597.h | 663 ++++++++++++++++++++++++++++++++ 3 files changed, 1588 insertions(+) create mode 100644 drivers/usb/r8a66597-hcd.c create mode 100644 drivers/usb/r8a66597.h (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 252b00e6527..2bdbb59cd56 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libusb.a COBJS-y += isp116x-hcd.o COBJS-y += sl811_usb.o COBJS-y += usb_ohci.o +COBJS-y += r8a66597-hcd.o COBJS-y += usbdcore.o COBJS-y += usbdcore_ep0.o COBJS-y += usbdcore_mpc8xx.o diff --git a/drivers/usb/r8a66597-hcd.c b/drivers/usb/r8a66597-hcd.c new file mode 100644 index 00000000000..3eb4cb03f08 --- /dev/null +++ b/drivers/usb/r8a66597-hcd.c @@ -0,0 +1,924 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008 Yoshihiro Shimoda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +#include "r8a66597.h" + +#if defined(CONFIG_USB_R8A66597_HCD) + +#ifdef R8A66597_DEBUG +#define R8A66597_DPRINT printf +#else +#define R8A66597_DPRINT(...) +#endif + +static const char hcd_name[] = "r8a66597_hcd"; +static unsigned short clock = CONFIG_R8A66597_XTAL; +static unsigned short vif = CONFIG_R8A66597_LDRV; +static unsigned short endian = CONFIG_R8A66597_ENDIAN; +static struct r8a66597 gr8a66597; + +static void set_devadd_reg(struct r8a66597 *r8a66597, u8 r8a66597_address, + u16 usbspd, u8 upphub, u8 hubport, int port) +{ + u16 val; + unsigned long devadd_reg = get_devadd_addr(r8a66597_address); + + val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001); + r8a66597_write(r8a66597, val, devadd_reg); +} + +static int r8a66597_clock_enable(struct r8a66597 *r8a66597) +{ + u16 tmp; + int i = 0; + +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + do { + r8a66597_write(r8a66597, SCKE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & SCKE) != SCKE); + r8a66597_write(r8a66597, 0x04, 0x02); +#else + do { + r8a66597_write(r8a66597, USBE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & USBE) != USBE); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0); + + i = 0; + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + do { + udelay(1000); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 500) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & SCKE) != SCKE); +#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */ + + return 0; +} + +static void r8a66597_clock_disable(struct r8a66597 *r8a66597) +{ + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + udelay(1); +#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + r8a66597_bclr(r8a66597, PLLC, SYSCFG0); + r8a66597_bclr(r8a66597, XCKE, SYSCFG0); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); +#endif +} + +static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port) +{ + u16 val; + + val = port ? DRPD : DCFM | DRPD; + r8a66597_bset(r8a66597, val, get_syscfg_reg(port)); + r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); + + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port)); +} + +static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port) +{ + u16 val, tmp; + + r8a66597_write(r8a66597, 0, get_intenb_reg(port)); + r8a66597_write(r8a66597, 0, get_intsts_reg(port)); + + r8a66597_port_power(r8a66597, port, 0); + + do { + tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS; + udelay(640); + } while (tmp == EDGESTS); + + val = port ? DRPD : DCFM | DRPD; + r8a66597_bclr(r8a66597, val, get_syscfg_reg(port)); + r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); +} + +static int enable_controller(struct r8a66597 *r8a66597) +{ + int ret, port; + + ret = r8a66597_clock_enable(r8a66597); + if (ret < 0) + return ret; + + r8a66597_bset(r8a66597, vif & LDRV, PINCFG); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, INTL, SOFCFG); + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTENB1); + r8a66597_write(r8a66597, 0, INTENB2); + + r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL); + r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL); + r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL); + r8a66597_bset(r8a66597, TRNENSEL, SOFCFG); + + for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) + r8a66597_enable_port(r8a66597, port); + + return 0; +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ + int i; + + if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE)) + return; + + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTSTS0); + + r8a66597_write(r8a66597, 0, D0FIFOSEL); + r8a66597_write(r8a66597, 0, D1FIFOSEL); + r8a66597_write(r8a66597, 0, DCPCFG); + r8a66597_write(r8a66597, 0x40, DCPMAXP); + r8a66597_write(r8a66597, 0, DCPCTR); + + for (i = 0; i <= 10; i++) + r8a66597_write(r8a66597, 0, get_devadd_addr(i)); + for (i = 1; i <= 5; i++) { + r8a66597_write(r8a66597, 0, get_pipetre_addr(i)); + r8a66597_write(r8a66597, 0, get_pipetrn_addr(i)); + } + for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) { + r8a66597_write(r8a66597, 0, get_pipectr_addr(i)); + r8a66597_write(r8a66597, i, PIPESEL); + r8a66597_write(r8a66597, 0, PIPECFG); + r8a66597_write(r8a66597, 0, PIPEBUF); + r8a66597_write(r8a66597, 0, PIPEMAXP); + r8a66597_write(r8a66597, 0, PIPEPERI); + } + + for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) + r8a66597_disable_port(r8a66597, i); + + r8a66597_clock_disable(r8a66597); +} + +static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg, + u16 mask, u16 loop) +{ + u16 tmp; + int i = 0; + + do { + tmp = r8a66597_read(r8a66597, reg); + if (i++ > 1000000) { + printf("register%lx, loop %x is timeout\n", reg, loop); + break; + } + } while ((tmp & mask) != loop); +} + +static void pipe_buffer_setting(struct r8a66597 *r8a66597, + struct usb_device *dev, unsigned long pipe) +{ + u16 val = 0; + u16 pipenum, bufnum, maxpacket; + + if (usb_pipein(pipe)) { + pipenum = BULK_IN_PIPENUM; + bufnum = BULK_IN_BUFNUM; + maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + } else { + pipenum = BULK_OUT_PIPENUM; + bufnum = BULK_OUT_BUFNUM; + maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; + } + + if (r8a66597->pipe_config & (1 << pipenum)) + return; + r8a66597->pipe_config |= (1 << pipenum); + + r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum)); + r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum)); + r8a66597_write(r8a66597, pipenum, PIPESEL); + + /* FIXME: This driver support bulk transfer only. */ + if (!usb_pipein(pipe)) + val |= R8A66597_DIR; + else + val |= R8A66597_SHTNAK; + val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe); + r8a66597_write(r8a66597, val, PIPECFG); + + r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF); + r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) | + maxpacket, PIPEMAXP); + r8a66597_write(r8a66597, 0, PIPEPERI); + r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum)); +} + +static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev, + struct devrequest *setup) +{ + int i; + unsigned short *p = (unsigned short *)setup; + unsigned long setup_addr = USBREQ; + u16 intsts1; + int timeout = 3000; + u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum; + + r8a66597_write(r8a66597, make_devsel(devsel) | + (8 << dev->maxpacketsize), DCPMAXP); + r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1); + + for (i = 0; i < 4; i++) { + r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr); + setup_addr += 2; + } + r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + r8a66597_write(r8a66597, SUREQ, DCPCTR); + + while (1) { + intsts1 = r8a66597_read(r8a66597, INTSTS1); + if (intsts1 & SACK) + break; + if (intsts1 & SIGN) { + printf("setup packet send error\n"); + return -1; + } + if (timeout-- < 0) { + printf("setup packet timeout\n"); + return -1; + } + udelay(500); + } + + return 0; +} + +static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len) +{ + u16 tmp, bufsize; + u16 *buf; + size_t size; + + R8A66597_DPRINT("%s\n", __func__); + + r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM, + MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM); + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set (%x)\n", __func__, tmp); + return -1; + } + + /* prepare parameters */ + bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; + buf = (u16 *)(buffer + dev->act_len); + size = min((int)bufsize, transfer_len - dev->act_len); + + /* write fifo */ + r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + if (buffer) { + r8a66597_write_fifo(r8a66597, CFIFO, buf, size); + r8a66597_write(r8a66597, BVAL, CFIFOCTR); + } + + /* update parameters */ + dev->act_len += size; + + r8a66597_mdfy(r8a66597, PID_BUF, PID, + get_pipectr_addr(BULK_OUT_PIPENUM)); + + while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM))) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + + if (dev->act_len >= transfer_len) + r8a66597_mdfy(r8a66597, PID_NAK, PID, + get_pipectr_addr(BULK_OUT_PIPENUM)); + + return 0; +} + +static int receive_bulk_packet(struct r8a66597 *r8a66597, + struct usb_device *dev, + unsigned long pipe, + void *buffer, int transfer_len) +{ + u16 tmp; + u16 *buf; + const u16 pipenum = BULK_IN_PIPENUM; + int rcv_len; + int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + + R8A66597_DPRINT("%s\n", __func__); + + /* prepare */ + if (dev->act_len == 0) { + r8a66597_mdfy(r8a66597, PID_NAK, PID, + get_pipectr_addr(pipenum)); + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + + r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum)); + r8a66597_write(r8a66597, + (transfer_len + maxpacket - 1) / maxpacket, + get_pipetrn_addr(pipenum)); + r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum)); + + r8a66597_mdfy(r8a66597, PID_BUF, PID, + get_pipectr_addr(pipenum)); + } + + r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum); + + while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum))) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set. (%x)\n", __func__, tmp); + return -1; + } + + buf = (u16 *)(buffer + dev->act_len); + rcv_len = tmp & DTLN; + dev->act_len += rcv_len; + + if (buffer) { + if (rcv_len == 0) + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + else + r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len); + } + + return 0; +} + +static int receive_control_packet(struct r8a66597 *r8a66597, + struct usb_device *dev, + void *buffer, int transfer_len) +{ + u16 tmp; + int rcv_len; + + /* FIXME: limit transfer size : 64byte or less */ + + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_bset(r8a66597, SQSET, DCPCTR); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + + while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001)) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + + r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set. (%x)\n", __func__, tmp); + return -1; + } + + rcv_len = tmp & DTLN; + dev->act_len += rcv_len; + + r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + + if (buffer) { + if (rcv_len == 0) + r8a66597_write(r8a66597, BCLR, DCPCTR); + else + r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len); + } + + return 0; +} + +static int send_status_packet(struct r8a66597 *r8a66597, + unsigned long pipe) +{ + r8a66597_bset(r8a66597, SQSET, DCPCTR); + r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + + if (usb_pipein(pipe)) { + r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR); + } else { + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + } + r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + + while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001)) + if (ctrlc()) + return -1; + + return 0; +} + +static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port) +{ + int count = R8A66597_MAX_SAMPLING; + unsigned short syssts, old_syssts; + + R8A66597_DPRINT("%s\n", __func__); + + old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); + while (count > 0) { + wait_ms(R8A66597_RH_POLL_TIME); + + syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); + if (syssts == old_syssts) { + count--; + } else { + count = R8A66597_MAX_SAMPLING; + old_syssts = syssts; + } + } +} + +static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port) +{ + wait_ms(10); + r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port)); + wait_ms(50); + r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port)); + wait_ms(50); +} + +static int check_usb_device_connecting(struct r8a66597 *r8a66597) +{ + int timeout = 10000; /* 100usec * 10000 = 1sec */ + int i; + + for (i = 0; i < 5; i++) { + /* check a usb cable connect */ + while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) { + if (timeout-- < 0) { + printf("%s timeout.\n", __func__); + return -1; + } + udelay(100); + } + + /* check a data line */ + r8a66597_check_syssts(r8a66597, 0); + + r8a66597_bus_reset(r8a66597, 0); + r8a66597->speed = get_rh_usb_speed(r8a66597, 0); + + if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) { + r8a66597->port_change = USB_PORT_STAT_C_CONNECTION; + r8a66597->port_status = USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE; + return 0; /* success */ + } + + R8A66597_DPRINT("USB device has detached. retry = %d\n", i); + r8a66597_write(r8a66597, ~DTCH, INTSTS1); + } + + return -1; /* fail */ +} + +/* based on usb_ohci.c */ +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; */ + + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 34, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '8', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'A', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '6', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '6', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '5', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '9', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '7', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + unsigned char data[32]; + + R8A66597_DPRINT("%s\n", __func__); + + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + printf("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = cpu_to_le16 (cmd->value); + wIndex = cpu_to_le16 (cmd->index); + wLength = cpu_to_le16 (cmd->length); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + *(__u16 *)buffer = cpu_to_le16(1); + len = 2; + break; + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)buffer = cpu_to_le16(0); + len = 2; + break; + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)buffer = cpu_to_le16(0); + len = 2; + break; + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)buffer = cpu_to_le32(0); + len = 4; + break; + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *)buffer = cpu_to_le32(r8a66597->port_status | + (r8a66597->port_change << 16)); + len = 4; + break; + case RH_CLEAR_FEATURE | RH_ENDPOINT: + case RH_CLEAR_FEATURE | RH_CLASS: + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_C_PORT_CONNECTION: + r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION; + break; + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + break; + case (RH_PORT_RESET): + r8a66597_bus_reset(r8a66597, 0); + break; + case (RH_PORT_POWER): + break; + case (RH_PORT_ENABLE): + break; + } + break; + case RH_SET_ADDRESS: + gr8a66597.rh_devnum = wValue; + break; + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + memcpy(buffer, root_hub_dev_des, len); + break; + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + memcpy(buffer, root_hub_config_des, len); + break; + case (0x03): /* string descriptors */ + if (wValue == 0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + memcpy(buffer, root_hub_str_index0, len); + } + if (wValue == 0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + memcpy(buffer, root_hub_str_index1, len); + } + break; + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = 0x00000001; + + data[0] = 9; /* min length; */ + data[1] = 0x29; + data[2] = temp & RH_A_NDP; + data[3] = 0; + if (temp & RH_A_PSM) + data[3] |= 0x1; + if (temp & RH_A_NOCP) + data[3] |= 0x10; + else if (temp & RH_A_OCPM) + data[3] |= 0x8; + + /* corresponds to data[4-7] */ + data[5] = (temp & RH_A_POTPGT) >> 24; + data[7] = temp & RH_B_DR; + if (data[2] < 7) { + data[8] = 0xff; + } else { + data[0] += 2; + data[8] = (temp & RH_B_DR) >> 8; + data[10] = data[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data[0], wLength)); + memcpy(buffer, data, len); + break; + } + + case RH_GET_CONFIGURATION: + *(__u8 *) buffer = 0x01; + len = 1; + break; + case RH_SET_CONFIGURATION: + break; + default: + dbg("unsupported root hub command"); + stat = USB_ST_STALLED; + } + + wait_ms(1); + + len = min_t(int, len, leni); + + dev->act_len = len; + dev->status = stat; + + return stat; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + int ret = 0; + + R8A66597_DPRINT("%s\n", __func__); + R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n", + pipe, buffer, transfer_len, dev->devnum); + + set_devadd_reg(r8a66597, dev->devnum, r8a66597->speed, 0, 0, 0); + + pipe_buffer_setting(r8a66597, dev, pipe); + + dev->act_len = 0; + while (dev->act_len < transfer_len && ret == 0) { + if (ctrlc()) + return -1; + + if (usb_pipein(pipe)) + ret = receive_bulk_packet(r8a66597, dev, pipe, buffer, + transfer_len); + else + ret = send_bulk_packet(r8a66597, dev, pipe, buffer, + transfer_len); + } + + if (ret == 0) + dev->status = 0; + + return ret; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ? + 0 : dev->devnum; + + R8A66597_DPRINT("%s\n", __func__); + if (usb_pipedevice(pipe) == r8a66597->rh_devnum) + return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + + R8A66597_DPRINT("%s: setup\n", __func__); + set_devadd_reg(r8a66597, r8a66597_address, r8a66597->speed, 0, 0, 0); + + if (send_setup_packet(r8a66597, dev, setup) < 0) { + printf("setup packet send error\n"); + return -1; + } + + if (usb_pipein(pipe)) + if (receive_control_packet(r8a66597, dev, buffer, + transfer_len) < 0) + return -1; + + if (send_status_packet(r8a66597, pipe) < 0) + return -1; + + dev->status = 0; + + return 0; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + /* no implement */ + R8A66597_DPRINT("%s\n", __func__); + return 0; +} + +void usb_event_poll(void) +{ + /* no implement */ + R8A66597_DPRINT("%s\n", __func__); +} + +int usb_lowlevel_init(void) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + + R8A66597_DPRINT("%s\n", __func__); + + memset(r8a66597, 0, sizeof(r8a66597)); + r8a66597->reg = CONFIG_R8A66597_BASE_ADDR; + + disable_controller(r8a66597); + wait_ms(100); + + enable_controller(r8a66597); + r8a66597_port_power(r8a66597, 0 , 1); + + /* check usb device */ + check_usb_device_connecting(r8a66597); + + wait_ms(50); + + return 0; +} + +int usb_lowlevel_stop(void) +{ + disable_controller(&gr8a66597); + + return 0; +} + +#endif /* CONFIG_USB_R8A66597_HCD */ + diff --git a/drivers/usb/r8a66597.h b/drivers/usb/r8a66597.h new file mode 100644 index 00000000000..54a1f26aefc --- /dev/null +++ b/drivers/usb/r8a66597.h @@ -0,0 +1,663 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008 Yoshihiro Shimoda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#define SYSCFG0 0x00 +#define SYSCFG1 0x02 +#define SYSSTS0 0x04 +#define SYSSTS1 0x06 +#define DVSTCTR0 0x08 +#define DVSTCTR1 0x0A +#define TESTMODE 0x0C +#define PINCFG 0x0E +#define DMA0CFG 0x10 +#define DMA1CFG 0x12 +#define CFIFO 0x14 +#define D0FIFO 0x18 +#define D1FIFO 0x1C +#define CFIFOSEL 0x20 +#define CFIFOCTR 0x22 +#define CFIFOSIE 0x24 +#define D0FIFOSEL 0x28 +#define D0FIFOCTR 0x2A +#define D1FIFOSEL 0x2C +#define D1FIFOCTR 0x2E +#define INTENB0 0x30 +#define INTENB1 0x32 +#define INTENB2 0x34 +#define BRDYENB 0x36 +#define NRDYENB 0x38 +#define BEMPENB 0x3A +#define SOFCFG 0x3C +#define INTSTS0 0x40 +#define INTSTS1 0x42 +#define INTSTS2 0x44 +#define BRDYSTS 0x46 +#define NRDYSTS 0x48 +#define BEMPSTS 0x4A +#define FRMNUM 0x4C +#define UFRMNUM 0x4E +#define USBADDR 0x50 +#define USBREQ 0x54 +#define USBVAL 0x56 +#define USBINDX 0x58 +#define USBLENG 0x5A +#define DCPCFG 0x5C +#define DCPMAXP 0x5E +#define DCPCTR 0x60 +#define PIPESEL 0x64 +#define PIPECFG 0x68 +#define PIPEBUF 0x6A +#define PIPEMAXP 0x6C +#define PIPEPERI 0x6E +#define PIPE1CTR 0x70 +#define PIPE2CTR 0x72 +#define PIPE3CTR 0x74 +#define PIPE4CTR 0x76 +#define PIPE5CTR 0x78 +#define PIPE6CTR 0x7A +#define PIPE7CTR 0x7C +#define PIPE8CTR 0x7E +#define PIPE9CTR 0x80 +#define PIPE1TRE 0x90 +#define PIPE1TRN 0x92 +#define PIPE2TRE 0x94 +#define PIPE2TRN 0x96 +#define PIPE3TRE 0x98 +#define PIPE3TRN 0x9A +#define PIPE4TRE 0x9C +#define PIPE4TRN 0x9E +#define PIPE5TRE 0xA0 +#define PIPE5TRN 0xA2 +#define DEVADD0 0xD0 +#define DEVADD1 0xD2 +#define DEVADD2 0xD4 +#define DEVADD3 0xD6 +#define DEVADD4 0xD8 +#define DEVADD5 0xDA +#define DEVADD6 0xDC +#define DEVADD7 0xDE +#define DEVADD8 0xE0 +#define DEVADD9 0xE2 +#define DEVADDA 0xE4 + +/* System Configuration Control Register */ +#define XTAL 0xC000 /* b15-14: Crystal selection */ +#define XTAL48 0x8000 /* 48MHz */ +#define XTAL24 0x4000 /* 24MHz */ +#define XTAL12 0x0000 /* 12MHz */ +#define XCKE 0x2000 /* b13: External clock enable */ +#define PLLC 0x0800 /* b11: PLL control */ +#define SCKE 0x0400 /* b10: USB clock enable */ +#define PCSDIS 0x0200 /* b9: not CS wakeup */ +#define LPSME 0x0100 /* b8: Low power sleep mode */ +#define HSE 0x0080 /* b7: Hi-speed enable */ +#define DCFM 0x0040 /* b6: Controller function select */ +#define DRPD 0x0020 /* b5: D+/- pull down control */ +#define DPRPU 0x0010 /* b4: D+ pull up control */ +#define USBE 0x0001 /* b0: USB module operation enable */ + +/* System Configuration Status Register */ +#define OVCBIT 0x8000 /* b15-14: Over-current bit */ +#define OVCMON 0xC000 /* b15-14: Over-current monitor */ +#define SOFEA 0x0020 /* b5: SOF monitor */ +#define IDMON 0x0004 /* b3: ID-pin monitor */ +#define LNST 0x0003 /* b1-0: D+, D- line status */ +#define SE1 0x0003 /* SE1 */ +#define FS_KSTS 0x0002 /* Full-Speed K State */ +#define FS_JSTS 0x0001 /* Full-Speed J State */ +#define LS_JSTS 0x0002 /* Low-Speed J State */ +#define LS_KSTS 0x0001 /* Low-Speed K State */ +#define SE0 0x0000 /* SE0 */ + +/* Device State Control Register */ +#define EXTLP0 0x0400 /* b10: External port */ +#define VBOUT 0x0200 /* b9: VBUS output */ +#define WKUP 0x0100 /* b8: Remote wakeup */ +#define RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define USBRST 0x0040 /* b6: USB reset enable */ +#define RESUME 0x0020 /* b5: Resume enable */ +#define UACT 0x0010 /* b4: USB bus enable */ +#define RHST 0x0007 /* b1-0: Reset handshake status */ +#define HSPROC 0x0004 /* HS handshake is processing */ +#define HSMODE 0x0003 /* Hi-Speed mode */ +#define FSMODE 0x0002 /* Full-Speed mode */ +#define LSMODE 0x0001 /* Low-Speed mode */ +#define UNDECID 0x0000 /* Undecided */ + +/* Test Mode Register */ +#define UTST 0x000F /* b3-0: Test select */ +#define H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define H_TST_K 0x000A /* HOST TEST K */ +#define H_TST_J 0x0009 /* HOST TEST J */ +#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define P_TST_K 0x0002 /* PERI TEST K */ +#define P_TST_J 0x0001 /* PERI TEST J */ +#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +/* Data Pin Configuration Register */ +#define LDRV 0x8000 /* b15: Drive Current Adjust */ +#define VIF1 0x0000 /* VIF = 1.8V */ +#define VIF3 0x8000 /* VIF = 3.3V */ +#define INTA 0x0001 /* b1: USB INT-pin active */ + +/* DMAx Pin Configuration Register */ +#define DREQA 0x4000 /* b14: Dreq active select */ +#define BURST 0x2000 /* b13: Burst mode */ +#define DACKA 0x0400 /* b10: Dack active select */ +#define DFORM 0x0380 /* b9-7: DMA mode select */ +#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define DENDA 0x0040 /* b6: Dend active select */ +#define PKTM 0x0020 /* b5: Packet mode */ +#define DENDE 0x0010 /* b4: Dend enable */ +#define OBUS 0x0004 /* b2: OUTbus mode */ + +/* CFIFO/DxFIFO Port Select Register */ +#define RCNT 0x8000 /* b15: Read count mode */ +#define REW 0x4000 /* b14: Buffer rewind */ +#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define DREQE 0x1000 /* b12: DREQ output enable */ +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define MBW 0x0800 +#else +#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */ +#endif +#define MBW_8 0x0000 /* 8bit */ +#define MBW_16 0x0400 /* 16bit */ +#define BIGEND 0x0100 /* b8: Big endian mode */ +#define BYTE_LITTLE 0x0000 /* little dendian */ +#define BYTE_BIG 0x0100 /* big endifan */ +#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define CURPIPE 0x000F /* b2-0: PIPE select */ + +/* CFIFO/DxFIFO Port Control Register */ +#define BVAL 0x8000 /* b15: Buffer valid flag */ +#define BCLR 0x4000 /* b14: Buffer clear */ +#define FRDY 0x2000 /* b13: FIFO ready */ +#define DTLN 0x0FFF /* b11-0: FIFO received data length */ + +/* Interrupt Enable Register 0 */ +#define VBSE 0x8000 /* b15: VBUS interrupt */ +#define RSME 0x4000 /* b14: Resume interrupt */ +#define SOFE 0x2000 /* b13: Frame update interrupt */ +#define DVSE 0x1000 /* b12: Device state transition interrupt */ +#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ + +/* Interrupt Enable Register 1 */ +#define OVRCRE 0x8000 /* b15: Over-current interrupt */ +#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ +#define EOFERRE 0x0040 /* b6: EOF error interrupt */ +#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ + +/* BRDY Interrupt Enable/Status Register */ +#define BRDY9 0x0200 /* b9: PIPE9 */ +#define BRDY8 0x0100 /* b8: PIPE8 */ +#define BRDY7 0x0080 /* b7: PIPE7 */ +#define BRDY6 0x0040 /* b6: PIPE6 */ +#define BRDY5 0x0020 /* b5: PIPE5 */ +#define BRDY4 0x0010 /* b4: PIPE4 */ +#define BRDY3 0x0008 /* b3: PIPE3 */ +#define BRDY2 0x0004 /* b2: PIPE2 */ +#define BRDY1 0x0002 /* b1: PIPE1 */ +#define BRDY0 0x0001 /* b1: PIPE0 */ + +/* NRDY Interrupt Enable/Status Register */ +#define NRDY9 0x0200 /* b9: PIPE9 */ +#define NRDY8 0x0100 /* b8: PIPE8 */ +#define NRDY7 0x0080 /* b7: PIPE7 */ +#define NRDY6 0x0040 /* b6: PIPE6 */ +#define NRDY5 0x0020 /* b5: PIPE5 */ +#define NRDY4 0x0010 /* b4: PIPE4 */ +#define NRDY3 0x0008 /* b3: PIPE3 */ +#define NRDY2 0x0004 /* b2: PIPE2 */ +#define NRDY1 0x0002 /* b1: PIPE1 */ +#define NRDY0 0x0001 /* b1: PIPE0 */ + +/* BEMP Interrupt Enable/Status Register */ +#define BEMP9 0x0200 /* b9: PIPE9 */ +#define BEMP8 0x0100 /* b8: PIPE8 */ +#define BEMP7 0x0080 /* b7: PIPE7 */ +#define BEMP6 0x0040 /* b6: PIPE6 */ +#define BEMP5 0x0020 /* b5: PIPE5 */ +#define BEMP4 0x0010 /* b4: PIPE4 */ +#define BEMP3 0x0008 /* b3: PIPE3 */ +#define BEMP2 0x0004 /* b2: PIPE2 */ +#define BEMP1 0x0002 /* b1: PIPE1 */ +#define BEMP0 0x0001 /* b0: PIPE0 */ + +/* SOF Pin Configuration Register */ +#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ +#define BRDYM 0x0040 /* b6: BRDY clear timing */ +#define INTL 0x0020 /* b5: Interrupt sense select */ +#define EDGESTS 0x0010 /* b4: */ +#define SOFMODE 0x000C /* b3-2: SOF pin select */ +#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ +#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +/* Interrupt Status Register 0 */ +#define VBINT 0x8000 /* b15: VBUS interrupt */ +#define RESM 0x4000 /* b14: Resume interrupt */ +#define SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define DVST 0x1000 /* b12: Device state transition interrupt */ +#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define VBSTS 0x0080 /* b7: VBUS input port */ +#define DVSQ 0x0070 /* b6-4: Device state */ +#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define DS_SUSP 0x0040 /* Suspend */ +#define DS_CNFG 0x0030 /* Configured */ +#define DS_ADDS 0x0020 /* Address */ +#define DS_DFLT 0x0010 /* Default */ +#define DS_POWR 0x0000 /* Powered */ +#define DVSQS 0x0030 /* b5-4: Device state */ +#define VALID 0x0008 /* b3: Setup packet detected flag */ +#define CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define CS_SQER 0x0006 /* Sequence error */ +#define CS_WRND 0x0005 /* Control write nodata status stage */ +#define CS_WRSS 0x0004 /* Control write status stage */ +#define CS_WRDS 0x0003 /* Control write data stage */ +#define CS_RDSS 0x0002 /* Control read status stage */ +#define CS_RDDS 0x0001 /* Control read data stage */ +#define CS_IDST 0x0000 /* Idle or setup stage */ + +/* Interrupt Status Register 1 */ +#define OVRCR 0x8000 /* b15: Over-current interrupt */ +#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define DTCH 0x1000 /* b12: Detach sense interrupt */ +#define ATTCH 0x0800 /* b11: Attach sense interrupt */ +#define EOFERR 0x0040 /* b6: EOF-error interrupt */ +#define SIGN 0x0020 /* b5: Setup ignore interrupt */ +#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ + +/* Frame Number Register */ +#define OVRN 0x8000 /* b15: Overrun error */ +#define CRCE 0x4000 /* b14: Received data error */ +#define FRNM 0x07FF /* b10-0: Frame number */ + +/* Micro Frame Number Register */ +#define UFRNM 0x0007 /* b2-0: Micro frame number */ + +/* Default Control Pipe Maxpacket Size Register */ +/* Pipe Maxpacket Size Register */ +#define DEVSEL 0xF000 /* b15-14: Device address select */ +#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ + +/* Default Control Pipe Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define SUREQ 0x4000 /* b14: Send USB request */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define SUREQCLR 0x0800 /* b11: stop setup request */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PINGE 0x0010 /* b4: ping enable */ +#define CCPL 0x0004 /* b2: Enable control transfer complete */ +#define PID 0x0003 /* b1-0: Response PID */ +#define PID_STALL11 0x0003 /* STALL */ +#define PID_STALL 0x0002 /* STALL */ +#define PID_BUF 0x0001 /* BUF */ +#define PID_NAK 0x0000 /* NAK */ + +/* Pipe Window Select Register */ +#define PIPENM 0x0007 /* b2-0: Pipe select */ + +/* Pipe Configuration Register */ +#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ +#define R8A66597_ISO 0xC000 /* Isochronous */ +#define R8A66597_INT 0x8000 /* Interrupt */ +#define R8A66597_BULK 0x4000 /* Bulk */ +#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ +#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ +#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ +#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ + +/* Pipe Buffer Configuration Register */ +#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ +#define PIPE0BUF 256 +#define PIPExBUF 64 + +/* Pipe Maxpacket Size Register */ +#define MXPS 0x07FF /* b10-0: Maxpacket size */ + +/* Pipe Cycle Configuration Register */ +#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ +#define IITV 0x0007 /* b2-0: Isochronous interval */ + +/* Pipex Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define ATREPM 0x0400 /* b10: Auto repeat mode */ +#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PID 0x0003 /* b1-0: Response PID */ + +/* PIPExTRE */ +#define TRENB 0x0200 /* b9: Transaction counter enable */ +#define TRCLR 0x0100 /* b8: Transaction counter clear */ + +/* PIPExTRN */ +#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +/* DEVADDx */ +#define UPPHUB 0x7800 +#define HUBPORT 0x0700 +#define USBSPD 0x00C0 +#define RTPORT 0x0001 + +#define R8A66597_MAX_NUM_PIPE 10 +#define R8A66597_BUF_BSIZE 8 +#define R8A66597_MAX_DEVICE 10 +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define R8A66597_MAX_ROOT_HUB 1 +#else +#define R8A66597_MAX_ROOT_HUB 2 +#endif +#define R8A66597_MAX_SAMPLING 5 +#define R8A66597_RH_POLL_TIME 10 + +#define BULK_IN_PIPENUM 3 +#define BULK_IN_BUFNUM 8 + +#define BULK_OUT_PIPENUM 4 +#define BULK_OUT_BUFNUM 40 + +#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5)) +#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9)) +#define make_devsel(addr) (addr << 12) + +struct r8a66597 { + unsigned long reg; + unsigned short pipe_config; /* bit field */ + unsigned short port_status; + unsigned short port_change; + u16 speed; /* HSMODE or FSMODE or LSMODE */ + unsigned char rh_devnum; +}; + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ + return inw(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, + unsigned long offset, void *buf, + int len) +{ + int i; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + unsigned long fifoaddr = r8a66597->reg + offset; + unsigned long count; + unsigned long *p = buf; + + count = len / 4; + for (i = 0; i < count; i++) + inl(p[i], r8a66597->reg + offset); + + if (len & 0x00000003) { + unsigned long tmp = inl(fifoaddr); + memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03); + } +#else + unsigned short *p = buf; + + len = (len + 1) / 2; + for (i = 0; i < len; i++) + p[i] = inw(r8a66597->reg + offset); +#endif +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, + unsigned long offset) +{ + outw(val, r8a66597->reg + offset); +} + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, + unsigned long offset, void *buf, + int len) +{ + int i; + unsigned long fifoaddr = r8a66597->reg + offset; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + unsigned long count; + unsigned char *pb; + unsigned long *p = buf; + + count = len / 4; + for (i = 0; i < count; i++) + outl(p[i], fifoaddr); + + if (len & 0x00000003) { + pb = (unsigned char *)buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) { + if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) + outb(pb[i], fifoaddr + i); + else + outb(pb[i], fifoaddr + 3 - i); + } + } +#else + int odd = len & 0x0001; + unsigned short *p = buf; + + len = len / 2; + for (i = 0; i < len; i++) + outw(p[i], fifoaddr); + + if (odd) { + unsigned char *pb = (unsigned char *)(buf + len); + outb(*pb, fifoaddr); + } +#endif +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + +static inline unsigned long get_syscfg_reg(int port) +{ + return port == 0 ? SYSCFG0 : SYSCFG1; +} + +static inline unsigned long get_syssts_reg(int port) +{ + return port == 0 ? SYSSTS0 : SYSSTS1; +} + +static inline unsigned long get_dvstctr_reg(int port) +{ + return port == 0 ? DVSTCTR0 : DVSTCTR1; +} + +static inline unsigned long get_dmacfg_reg(int port) +{ + return port == 0 ? DMA0CFG : DMA1CFG; +} + +static inline unsigned long get_intenb_reg(int port) +{ + return port == 0 ? INTENB1 : INTENB2; +} + +static inline unsigned long get_intsts_reg(int port) +{ + return port == 0 ? INTSTS1 : INTSTS2; +} + +static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + return r8a66597_read(r8a66597, dvstctr_reg) & RHST; +} + +static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, + int power) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + if (power) + r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); + else + r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); +} + +#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) +#define get_devadd_addr(address) (DEVADD0 + address * 2) + + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + + +#endif /* __R8A66597_H__ */ + -- cgit v1.2.3 From d5d28fe4aad5f4535400647a5617c11039506467 Mon Sep 17 00:00:00 2001 From: David Saada Date: Mon, 31 Mar 2008 02:37:38 -0700 Subject: QE UEC: Add MII Commands Add MII commands to the UEC driver. Note that once a UEC device is selected, any device on its MDIO bus can be addressed. Signed-off-by: David Saada Signed-off-by: Ben Warren --- drivers/qe/uec.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers') diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c index d2c430b8a6f..049a74d5cb6 100644 --- a/drivers/qe/uec.c +++ b/drivers/qe/uec.c @@ -29,6 +29,7 @@ #include "uccf.h" #include "uec.h" #include "uec_phy.h" +#include "miiphy.h" #if defined(CONFIG_QE) @@ -125,6 +126,17 @@ static uec_info_t eth4_uec_info = { }; #endif +#define MAXCONTROLLERS (4) + +static struct eth_device *devlist[MAXCONTROLLERS]; + +static int uec_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value); +static int uec_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value); +u16 phy_read (struct uec_mii_info *mii_info, u16 regnum); +void phy_write (struct uec_mii_info *mii_info, u16 regnum, u16 val); + static int uec_mac_enable(uec_private_t *uec, comm_dir_e mode) { uec_t *uec_regs; @@ -1334,6 +1346,8 @@ int uec_initialize(int index) return -EINVAL; } + devlist[index] = dev; + uec->uec_info = uec_info; sprintf(dev->name, "FSL UEC%d", index); @@ -1356,6 +1370,45 @@ int uec_initialize(int index) return err; } +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) + miiphy_register(dev->name, uec_miiphy_read, uec_miiphy_write); +#endif + return 1; } + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) + +/* + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +static int uec_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + *value = uec_read_phy_reg(devlist[0], addr, reg); + + return 0; +} + +/* + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +static int uec_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + uec_write_phy_reg(devlist[0], addr, reg, value); + + return 0; +} + +#endif + #endif /* CONFIG_QE */ -- cgit v1.2.3 From d9d78ee46d9a396d0a81d00c2b003a9bd32c2e61 Mon Sep 17 00:00:00 2001 From: Ben Warren Date: Thu, 7 Aug 2008 23:26:35 -0700 Subject: QE UEC: Fix compiler warnings Moved static functions earlier in file so forward declarations are not needed. Signed-off-by: Ben Warren --- drivers/qe/uec.c | 69 +++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c index 049a74d5cb6..ba89247e4fc 100644 --- a/drivers/qe/uec.c +++ b/drivers/qe/uec.c @@ -130,10 +130,6 @@ static uec_info_t eth4_uec_info = { static struct eth_device *devlist[MAXCONTROLLERS]; -static int uec_miiphy_read(char *devname, unsigned char addr, - unsigned char reg, unsigned short *value); -static int uec_miiphy_write(char *devname, unsigned char addr, - unsigned char reg, unsigned short value); u16 phy_read (struct uec_mii_info *mii_info, u16 regnum); void phy_write (struct uec_mii_info *mii_info, u16 regnum, u16 val); @@ -641,6 +637,39 @@ static void phy_change(struct eth_device *dev) adjust_link(dev); } +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) + +/* + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +static int uec_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + *value = uec_read_phy_reg(devlist[0], addr, reg); + + return 0; +} + +/* + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +static int uec_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + uec_write_phy_reg(devlist[0], addr, reg, value); + + return 0; +} + +#endif + static int uec_set_mac_address(uec_private_t *uec, u8 *mac_addr) { uec_t *uec_regs; @@ -1378,37 +1407,5 @@ int uec_initialize(int index) return 1; } -#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ - && !defined(BITBANGMII) - -/* - * Read a MII PHY register. - * - * Returns: - * 0 on success - */ -static int uec_miiphy_read(char *devname, unsigned char addr, - unsigned char reg, unsigned short *value) -{ - *value = uec_read_phy_reg(devlist[0], addr, reg); - - return 0; -} - -/* - * Write a MII PHY register. - * - * Returns: - * 0 on success - */ -static int uec_miiphy_write(char *devname, unsigned char addr, - unsigned char reg, unsigned short value) -{ - uec_write_phy_reg(devlist[0], addr, reg, value); - - return 0; -} - -#endif #endif /* CONFIG_QE */ -- cgit v1.2.3 From 2a112b234d879f6390503a5f4e38246acce9d0b0 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Fri, 8 Aug 2008 16:39:54 +0200 Subject: CFI: allow for dynamically determined flash sizes and addresses The CFI driver allowed only for static initializers in the CFG_FLASH_BANKS_LIST definition, i. e. it did not allow to map several flash banks contiguously if the bank sizes were not known in advance, which kind of violates U-Boot's design philosophy. (will be used for example by the TQM8xxL boards) Signed-off-by: Wolfgang Denk --- drivers/mtd/cfi_flash.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 12647ef9861..479075ccb19 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -158,13 +158,13 @@ static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT }; /* use CFG_MAX_FLASH_BANKS_DETECT if defined */ #ifdef CFG_MAX_FLASH_BANKS_DETECT -static ulong bank_base[CFG_MAX_FLASH_BANKS_DETECT] = CFG_FLASH_BANKS_LIST; -flash_info_t flash_info[CFG_MAX_FLASH_BANKS_DETECT]; /* FLASH chips info */ +# define CFI_MAX_FLASH_BANKS CFG_MAX_FLASH_BANKS_DETECT #else -static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST; -flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* FLASH chips info */ +# define CFI_MAX_FLASH_BANKS CFG_MAX_FLASH_BANKS #endif +flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */ + /* * Check if chip width is defined. If not, start detecting with 8bit. */ @@ -1912,12 +1912,14 @@ unsigned long flash_init (void) char *s = getenv("unlock"); #endif +#define BANK_BASE(i) (((unsigned long [CFI_MAX_FLASH_BANKS])CFG_FLASH_BANKS_LIST)[i]) + /* Init: no FLASHes known */ for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; - if (!flash_detect_legacy (bank_base[i], i)) - flash_get_size (bank_base[i], i); + if (!flash_detect_legacy (BANK_BASE(i), i)) + flash_get_size (BANK_BASE(i), i); size += flash_info[i].size; if (flash_info[i].flash_id == FLASH_UNKNOWN) { #ifndef CFG_FLASH_QUIET_TEST -- cgit v1.2.3 From f77d92a3f56d88e63cc02226a1204b3bdbac6961 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Sat, 9 Aug 2008 01:39:09 +0400 Subject: DataFlash: AT45DB021 fix and AT45DB081 support Fix for page size of AT45DB021. Also adding bigger AT45DB081 which comes with some newer boards. Signed-off-by: Sergey Lapin --- drivers/mtd/dataflash.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/dataflash.c b/drivers/mtd/dataflash.c index 0ad48cdae91..049da69fec3 100644 --- a/drivers/mtd/dataflash.c +++ b/drivers/mtd/dataflash.c @@ -56,7 +56,7 @@ int AT91F_DataflashInit (void) switch (dfcode) { case AT45DB021: dataflash_info[i].Device.pages_number = 1024; - dataflash_info[i].Device.pages_size = 263; + dataflash_info[i].Device.pages_size = 264; dataflash_info[i].Device.page_offset = 9; dataflash_info[i].Device.byte_mask = 0x300; dataflash_info[i].Device.cs = cs[i].cs; @@ -65,6 +65,19 @@ int AT91F_DataflashInit (void) dataflash_info[i].id = dfcode; found[i] += dfcode;; break; + + case AT45DB081: + dataflash_info[i].Device.pages_number = 4096; + dataflash_info[i].Device.pages_size = 264; + dataflash_info[i].Device.page_offset = 9; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + case AT45DB161: dataflash_info[i].Device.pages_number = 4096; dataflash_info[i].Device.pages_size = 528; -- cgit v1.2.3 From d9015f6a50d7258125349ef5c2af836458a0029a Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 8 Aug 2008 18:00:39 +0200 Subject: video: fix bug in logo_plot If logo_plot() should ever be called with x starting position other than zero and for pixel depths greater than 8bpp, logo colors distortion will be observed. This patch fixes the issue. Signed-off-by: Anatolij Gustschin --- drivers/video/cfb_console.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 68b9861d41f..97a37ba50c7 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -1071,7 +1071,9 @@ void logo_plot (void *screen, int width, int x, int y) int ycount = VIDEO_LOGO_HEIGHT; unsigned char r, g, b, *logo_red, *logo_blue, *logo_green; unsigned char *source; - unsigned char *dest = (unsigned char *)screen + ((y * width * VIDEO_PIXEL_SIZE) + x); + unsigned char *dest = (unsigned char *)screen + + ((y * width * VIDEO_PIXEL_SIZE) + + x * VIDEO_PIXEL_SIZE); #ifdef CONFIG_VIDEO_BMP_LOGO source = bmp_logo_bitmap; -- cgit v1.2.3 From e84d568fa2a9f4ce7888141e71676368ef6b3f25 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 8 Aug 2008 18:00:40 +0200 Subject: video: fix bug in cfb_console code FILL_15BIT_555RGB macro extension for pixel swapping by commit bed53753dd1d7e6bcbea4339be0fb7760214cc35 introduced a bug in cfb_console: Bitmaps with odd-numbered width won't be rendered correctly and even U-Boot crashes are observed on some platforms while repeated rendering of such bitmaps with "bmp display". Also if a bitmap is rendered to an odd-numbered x starting position, the same problem occurs. This patch is an attempt to fix it. Signed-off-by: Anatolij Gustschin --- drivers/video/cfb_console.c | 59 +++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 97a37ba50c7..d313e9098c9 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -751,24 +751,10 @@ void video_puts (const char *s) fb ++; \ } -#if !defined(VIDEO_FB_16BPP_PIXEL_SWAP) #define FILL_15BIT_555RGB(r,g,b) { \ *(unsigned short *)fb = SWAP16((unsigned short)(((r>>3)<<10) | ((g>>3)<<5) | (b>>3))); \ fb += 2; \ } -#else -static int tgl; -static unsigned short p0; -#define FILL_15BIT_555RGB(r,g,b) { \ - if (!tgl++) { \ - p0 = SWAP16((unsigned short)(((r>>3)<<10) | ((g>>3)<<5) | (b>>3))); \ - } else { \ - tgl=0; \ - *(unsigned long *)(fb-2) = (SWAP16((unsigned short)(((r>>3)<<10) | ((g>>3)<<5) | (b>>3)))<<16) | p0; \ - } \ - fb += 2; \ -} -#endif #define FILL_16BIT_565RGB(r,g,b) { \ *(unsigned short *)fb = SWAP16((unsigned short)((((r)>>3)<<11) | (((g)>>2)<<5) | ((b)>>3))); \ @@ -796,6 +782,20 @@ static unsigned short p0; } #endif +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) +static void inline fill_555rgb_pswap(uchar *fb, int x, + u8 r, u8 g, u8 b) +{ + ushort *dst = (ushort *)fb; + ushort color = (ushort)(((r >> 3) << 10) | + ((g >> 3) << 5) | + (b >> 3)); + if (x & 1) + *(--dst) = color; + else + *(++dst) = color; +} +#endif /* * Display the BMP file located at address bmp_image. @@ -927,11 +927,20 @@ int video_display_bitmap (ulong bmp_image, int x, int y) break; case GDF_15BIT_555RGB: while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif WATCHDOG_RESET (); xcount = width; while (xcount--) { cte = bmp->color_table[*bmap++]; +#if !defined(VIDEO_FB_16BPP_PIXEL_SWAP) FILL_15BIT_555RGB (cte.red, cte.green, cte.blue); +#else + fill_555rgb_pswap (fb, xpos++, cte.red, + cte.green, cte.blue); + fb += 2; +#endif } bmap += padded_line; fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; @@ -993,10 +1002,19 @@ int video_display_bitmap (ulong bmp_image, int x, int y) break; case GDF_15BIT_555RGB: while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif WATCHDOG_RESET (); xcount = width; while (xcount--) { +#if !defined(VIDEO_FB_16BPP_PIXEL_SWAP) FILL_15BIT_555RGB (bmap[2], bmap[1], bmap[0]); +#else + fill_555rgb_pswap (fb, xpos++, bmap[2], + bmap[1], bmap[0]); + fb += 2; +#endif bmap += 3; } bmap += padded_line; @@ -1103,6 +1121,9 @@ void logo_plot (void *screen, int width, int x, int y) } while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif xcount = VIDEO_LOGO_WIDTH; while (xcount--) { r = logo_red[*source - VIDEO_LOGO_LUT_OFFSET]; @@ -1121,15 +1142,7 @@ void logo_plot (void *screen, int width, int x, int y) *(unsigned short *) dest = SWAP16 ((unsigned short) (((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3))); #else - { - if (!tgl++) { - p0 = SWAP16 ((unsigned short) (((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3))); - } else { - *(unsigned long *)(dest-2) = - (SWAP16 ((unsigned short) (((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3)))<<16) | p0; - tgl=0; - } - } + fill_555rgb_pswap (dest, xpos++, r, g, b); #endif break; case GDF_16BIT_565RGB: -- cgit v1.2.3 From 4d57b0fb2927d4f50d834884b4ec4a7ca01708b0 Mon Sep 17 00:00:00 2001 From: Steve Sakoman Date: Mon, 11 Aug 2008 20:26:16 +0200 Subject: OneNAND: Remove unused parameters to onenand_verify_page The block and page parameters of onenand_verify_page() are not used. This causes a compiler error when CONFIG_MTD_ONENAND_VERIFY_WRITE is enabled. Signed-off-by: Steve Sakoman Signed-off-by: Dirk Behme --- drivers/mtd/onenand/onenand_base.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d32e3825580..a7054aebca6 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -680,13 +680,11 @@ int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, * onenand_verify_page - [GENERIC] verify the chip contents after a write * @param mtd MTD device structure * @param buf the databuffer to verify - * @param block block address - * @param page page address * * Check DataRAM area directly */ static int onenand_verify_page(struct mtd_info *mtd, u_char * buf, - loff_t addr, int block, int page) + loff_t addr) { struct onenand_chip *this = mtd->priv; void __iomem *dataram0, *dataram1; @@ -783,7 +781,7 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, written += thislen; /* Only check verify write turn on */ - ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page); + ret = onenand_verify_page(mtd, (u_char *) buf, to); if (ret) { MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret); -- cgit v1.2.3 From 3cf8a234b8e8c02e4da1f23566043bc288b05220 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Mon, 11 Aug 2008 09:16:25 -0500 Subject: Fix compile error related to r8a66597-hcd & usb When building the 8544DS board we get this error: In file included from r8a66597-hcd.c:22: u-boot/include/usb.h:190:2: error: #error USB Lowlevel not defined make[1]: *** [r8a66597-hcd.o] Error 1 The cleanest fix is to only build r8a66597-hcd.c if CONFIG_USB_R8A66597_HCD is set. Signed-off-by: Kumar Gala --- drivers/usb/Makefile | 3 ++- drivers/usb/r8a66597-hcd.c | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 2bdbb59cd56..c67a490f0a9 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -25,10 +25,11 @@ include $(TOPDIR)/config.mk LIB := $(obj)libusb.a +COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o + COBJS-y += isp116x-hcd.o COBJS-y += sl811_usb.o COBJS-y += usb_ohci.o -COBJS-y += r8a66597-hcd.o COBJS-y += usbdcore.o COBJS-y += usbdcore_ep0.o COBJS-y += usbdcore_mpc8xx.o diff --git a/drivers/usb/r8a66597-hcd.c b/drivers/usb/r8a66597-hcd.c index 3eb4cb03f08..0d3931e10f3 100644 --- a/drivers/usb/r8a66597-hcd.c +++ b/drivers/usb/r8a66597-hcd.c @@ -24,8 +24,6 @@ #include "r8a66597.h" -#if defined(CONFIG_USB_R8A66597_HCD) - #ifdef R8A66597_DEBUG #define R8A66597_DPRINT printf #else @@ -919,6 +917,3 @@ int usb_lowlevel_stop(void) return 0; } - -#endif /* CONFIG_USB_R8A66597_HCD */ - -- cgit v1.2.3 From cd82919e6c8a73b363a26f34b734923844e52d1c Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Tue, 12 Aug 2008 16:08:38 +0200 Subject: Coding style cleanup, update CHANGELOG, prepare release Signed-off-by: Wolfgang Denk --- drivers/usb/r8a66597.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/r8a66597.h b/drivers/usb/r8a66597.h index 54a1f26aefc..9af6446c134 100644 --- a/drivers/usb/r8a66597.h +++ b/drivers/usb/r8a66597.h @@ -593,7 +593,6 @@ static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, /* Our Vendor Specific Request */ #define RH_SET_EP 0x2000 - /* Hub port features */ #define RH_PORT_CONNECTION 0x00 #define RH_PORT_ENABLE 0x01 @@ -620,7 +619,6 @@ static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, #define RH_REQ_ERR -1 #define RH_NACK 0x00 - /* OHCI ROOT HUB REGISTER MASKS */ /* roothub.portstatus [i] bits */ @@ -658,6 +656,4 @@ static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, #define RH_A_NOCP (1 << 12) /* no over current protection */ #define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - #endif /* __R8A66597_H__ */ - -- cgit v1.2.3 From cfa460adfdefcc30d104e1a9ee44994ee349bb7b Mon Sep 17 00:00:00 2001 From: William Juul Date: Wed, 31 Oct 2007 13:53:06 +0100 Subject: Update MTD to that of Linux 2.6.22.1 A lot changed in the Linux MTD code, since it was last ported from Linux to U-Boot. This patch takes U-Boot NAND support to the level of Linux 2.6.22.1 and will enable support for very large NAND devices (4KB pages) and ease the compatibility between U-Boot and Linux filesystems. This patch is tested on two custom boards with PPC and ARM processors running YAFFS in U-Boot and Linux using gcc-4.1.2 cross compilers. MAKEALL ppc/arm has some issues: * DOC/OneNand/nand_spl is not building (I have not tried porting these parts, and since I do not have any HW and I am not familiar with this code/HW I think its best left to someone else.) Except for the issues mentioned above, I have ported all drivers necessary to run MAKEALL ppc/arm without errors and warnings. Many drivers were trivial to port, but some were not so trivial. The following drivers must be examined carefully and maybe rewritten to some degree: cpu/ppc4xx/ndfc.c cpu/arm926ejs/davinci/nand.c board/delta/nand.c board/zylonite/nand.c Signed-off-by: William Juul Signed-off-by: Stig Olsen Signed-off-by: Scott Wood --- drivers/mtd/nand/diskonchip.c | 554 +++---- drivers/mtd/nand/nand_base.c | 3513 +++++++++++++++++++++-------------------- drivers/mtd/nand/nand_bbt.c | 548 ++++--- drivers/mtd/nand/nand_ecc.c | 21 +- drivers/mtd/nand/nand_ids.c | 91 +- drivers/mtd/nand/nand_util.c | 359 +++-- 6 files changed, 2770 insertions(+), 2316 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index fdd85c159d7..a03f982be5f 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -16,7 +16,7 @@ * * Interface to generic NAND code for M-Systems DiskOnChip devices * - * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $ + * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $ */ #include @@ -39,13 +39,13 @@ #include /* Where to look for the devices? */ -#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS -#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 #endif static unsigned long __initdata doc_locations[] = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, @@ -65,7 +65,7 @@ static unsigned long __initdata doc_locations[] = { 0xff000000, #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) 0xff000000, -##else +#else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif 0xffffffff }; @@ -77,7 +77,7 @@ struct doc_priv { unsigned long physadr; u_char ChipID; u_char CDSNControl; - int chips_per_floor; /* The number of chips detected on each floor */ + int chips_per_floor; /* The number of chips detected on each floor */ int curfloor; int curchip; int mh0_page; @@ -85,14 +85,10 @@ struct doc_priv { struct mtd_info *nextdoc; }; -/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL - MediaHeader. The spec says to just keep going, I think, but that's just - silly. */ -#define MAX_MEDIAHEADER_SCAN 8 - /* This is the syndrome computed by the HW ecc generator upon reading an empty page, one with all 0xff for data and stored ecc code. */ static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; + /* This is the ecc value computed by the HW ecc generator upon writing an empty page, one with all 0xff for data. */ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; @@ -103,35 +99,36 @@ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int bitmask); static void doc200x_select_chip(struct mtd_info *mtd, int chip); -static int debug=0; +static int debug = 0; module_param(debug, int, 0); -static int try_dword=1; +static int try_dword = 1; module_param(try_dword, int, 0); -static int no_ecc_failures=0; +static int no_ecc_failures = 0; module_param(no_ecc_failures, int, 0); -#ifdef CONFIG_MTD_PARTITIONS -static int no_autopart=0; +static int no_autopart = 0; module_param(no_autopart, int, 0); -#endif -#ifdef MTD_NAND_DISKONCHIP_BBTWRITE -static int inftl_bbt_write=1; +static int show_firmware_partition = 0; +module_param(show_firmware_partition, int, 0); + +#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE +static int inftl_bbt_write = 1; #else -static int inftl_bbt_write=0; +static int inftl_bbt_write = 0; #endif module_param(inftl_bbt_write, int, 0); -static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; module_param(doc_config_location, ulong, 0); MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); - /* Sector size for HW ECC */ #define SECTOR_SIZE 512 /* The sector bytes are packed into NB_DATA 10 bit words */ @@ -155,7 +152,7 @@ static struct rs_control *rs_decoder; * some comments, improved a minor bit and converted it to make use * of the generic Reed-Solomon libary. tglx */ -static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) +static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) { int i, j, nerr, errpos[8]; uint8_t parity; @@ -176,11 +173,11 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] * where x = alpha^(FCR + i) */ - for(j = 1; j < NROOTS; j++) { - if(ds[j] == 0) + for (j = 1; j < NROOTS; j++) { + if (ds[j] == 0) continue; tmp = rs->index_of[ds[j]]; - for(i = 0; i < NROOTS; i++) + for (i = 0; i < NROOTS; i++) s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; } @@ -201,7 +198,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) * but they are given by the design of the de/encoder circuit * in the DoC ASIC's. */ - for(i = 0;i < nerr; i++) { + for (i = 0; i < nerr; i++) { int index, bitpos, pos = 1015 - errpos[i]; uint8_t val; if (pos >= NB_DATA && pos < 1019) @@ -213,8 +210,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) can be modified since pos is even */ index = (pos >> 3) ^ 1; bitpos = pos & 7; - if ((index >= 0 && index < SECTOR_SIZE) || - index == (SECTOR_SIZE + 1)) { + if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { val = (uint8_t) (errval[i] >> (2 + bitpos)); parity ^= val; if (index < SECTOR_SIZE) @@ -224,9 +220,8 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) bitpos = (bitpos + 10) & 7; if (bitpos == 0) bitpos = 8; - if ((index >= 0 && index < SECTOR_SIZE) || - index == (SECTOR_SIZE + 1)) { - val = (uint8_t)(errval[i] << (8 - bitpos)); + if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] << (8 - bitpos)); parity ^= val; if (index < SECTOR_SIZE) data[index] ^= val; @@ -261,7 +256,8 @@ static int _DoC_WaitReady(struct doc_priv *doc) void __iomem *docptr = doc->virtadr; unsigned long timeo = jiffies + (HZ * 10); - if(debug) printk("_DoC_WaitReady...\n"); + if (debug) + printk("_DoC_WaitReady...\n"); /* Out-of-line routine to wait for chip response */ if (DoC_is_MillenniumPlus(doc)) { while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { @@ -306,7 +302,8 @@ static inline int DoC_WaitReady(struct doc_priv *doc) DoC_Delay(doc, 2); } - if(debug) printk("DoC_WaitReady OK\n"); + if (debug) + printk("DoC_WaitReady OK\n"); return ret; } @@ -316,7 +313,8 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; - if(debug)printk("write_byte %02x\n", datum); + if (debug) + printk("write_byte %02x\n", datum); WriteDOC(datum, docptr, CDSNSlowIO); WriteDOC(datum, docptr, 2k_CDSN_IO); } @@ -331,37 +329,39 @@ static u_char doc2000_read_byte(struct mtd_info *mtd) ReadDOC(docptr, CDSNSlowIO); DoC_Delay(doc, 2); ret = ReadDOC(docptr, 2k_CDSN_IO); - if (debug) printk("read_byte returns %02x\n", ret); + if (debug) + printk("read_byte returns %02x\n", ret); return ret; } -static void doc2000_writebuf(struct mtd_info *mtd, - const u_char *buf, int len) +static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - if (debug)printk("writebuf of %d bytes: ", len); - for (i=0; i < len; i++) { + if (debug) + printk("writebuf of %d bytes: ", len); + for (i = 0; i < len; i++) { WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); if (debug && i < 16) printk("%02x ", buf[i]); } - if (debug) printk("\n"); + if (debug) + printk("\n"); } -static void doc2000_readbuf(struct mtd_info *mtd, - u_char *buf, int len) +static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - if (debug)printk("readbuf of %d bytes: ", len); + if (debug) + printk("readbuf of %d bytes: ", len); - for (i=0; i < len; i++) { + for (i = 0; i < len; i++) { buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); } } @@ -374,28 +374,28 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd, void __iomem *docptr = doc->virtadr; int i; - if (debug) printk("readbuf_dword of %d bytes: ", len); + if (debug) + printk("readbuf_dword of %d bytes: ", len); - if (unlikely((((unsigned long)buf)|len) & 3)) { - for (i=0; i < len; i++) { - *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); + if (unlikely((((unsigned long)buf) | len) & 3)) { + for (i = 0; i < len; i++) { + *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); } } else { - for (i=0; i < len; i+=4) { - *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); + for (i = 0; i < len; i += 4) { + *(uint32_t*) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); } } } -static int doc2000_verifybuf(struct mtd_info *mtd, - const u_char *buf, int len) +static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - for (i=0; i < len; i++) + for (i = 0; i < len; i++) if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) return -EFAULT; return 0; @@ -408,12 +408,15 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) uint16_t ret; doc200x_select_chip(mtd, nr); - doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_READID); - doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); - doc200x_hwcontrol(mtd, NAND_CTL_SETALE); - this->write_byte(mtd, 0); - doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* We cant' use dev_ready here, but at least we wait for the + * command to complete + */ + udelay(50); ret = this->read_byte(mtd) << 8; ret |= this->read_byte(mtd); @@ -426,12 +429,13 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) } ident; void __iomem *docptr = doc->virtadr; - doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); - doc2000_write_byte(mtd, NAND_CMD_READID); - doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); - doc200x_hwcontrol(mtd, NAND_CTL_SETALE); - doc2000_write_byte(mtd, 0); - doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + udelay(50); ident.dword = readl(docptr + DoC_2k_CDSN_IO); if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { @@ -465,7 +469,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd) printk(KERN_DEBUG "Detected %d chips per floor.\n", i); } -static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) { struct doc_priv *doc = this->priv; @@ -496,30 +500,28 @@ static u_char doc2001_read_byte(struct mtd_info *mtd) struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; - /*ReadDOC(docptr, CDSNSlowIO); */ + //ReadDOC(docptr, CDSNSlowIO); /* 11.4.5 -- delay twice to allow extended length cycle */ DoC_Delay(doc, 2); ReadDOC(docptr, ReadPipeInit); - /*return ReadDOC(docptr, Mil_CDSN_IO); */ + //return ReadDOC(docptr, Mil_CDSN_IO); return ReadDOC(docptr, LastDataRead); } -static void doc2001_writebuf(struct mtd_info *mtd, - const u_char *buf, int len) +static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - for (i=0; i < len; i++) + for (i = 0; i < len; i++) WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); /* Terminate write pipeline */ WriteDOC(0x00, docptr, WritePipeTerm); } -static void doc2001_readbuf(struct mtd_info *mtd, - u_char *buf, int len) +static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; @@ -529,15 +531,14 @@ static void doc2001_readbuf(struct mtd_info *mtd, /* Start read pipeline */ ReadDOC(docptr, ReadPipeInit); - for (i=0; i < len-1; i++) + for (i = 0; i < len - 1; i++) buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); /* Terminate read pipeline */ buf[i] = ReadDOC(docptr, LastDataRead); } -static int doc2001_verifybuf(struct mtd_info *mtd, - const u_char *buf, int len) +static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; @@ -547,7 +548,7 @@ static int doc2001_verifybuf(struct mtd_info *mtd, /* Start read pipeline */ ReadDOC(docptr, ReadPipeInit); - for (i=0; i < len-1; i++) + for (i = 0; i < len - 1; i++) if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { ReadDOC(docptr, LastDataRead); return i; @@ -567,81 +568,84 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd) ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit); ret = ReadDOC(docptr, Mplus_LastDataRead); - if (debug) printk("read_byte returns %02x\n", ret); + if (debug) + printk("read_byte returns %02x\n", ret); return ret; } -static void doc2001plus_writebuf(struct mtd_info *mtd, - const u_char *buf, int len) +static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - if (debug)printk("writebuf of %d bytes: ", len); - for (i=0; i < len; i++) { + if (debug) + printk("writebuf of %d bytes: ", len); + for (i = 0; i < len; i++) { WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); if (debug && i < 16) printk("%02x ", buf[i]); } - if (debug) printk("\n"); + if (debug) + printk("\n"); } -static void doc2001plus_readbuf(struct mtd_info *mtd, - u_char *buf, int len) +static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - if (debug)printk("readbuf of %d bytes: ", len); + if (debug) + printk("readbuf of %d bytes: ", len); /* Start read pipeline */ ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit); - for (i=0; i < len-2; i++) { + for (i = 0; i < len - 2; i++) { buf[i] = ReadDOC(docptr, Mil_CDSN_IO); if (debug && i < 16) printk("%02x ", buf[i]); } /* Terminate read pipeline */ - buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); + buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead); if (debug && i < 16) - printk("%02x ", buf[len-2]); - buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); + printk("%02x ", buf[len - 2]); + buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); if (debug && i < 16) - printk("%02x ", buf[len-1]); - if (debug) printk("\n"); + printk("%02x ", buf[len - 1]); + if (debug) + printk("\n"); } -static int doc2001plus_verifybuf(struct mtd_info *mtd, - const u_char *buf, int len) +static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; - if (debug)printk("verifybuf of %d bytes: ", len); + if (debug) + printk("verifybuf of %d bytes: ", len); /* Start read pipeline */ ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit); - for (i=0; i < len-2; i++) + for (i = 0; i < len - 2; i++) if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { ReadDOC(docptr, Mplus_LastDataRead); ReadDOC(docptr, Mplus_LastDataRead); return i; } - if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead)) - return len-2; - if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead)) - return len-1; + if (buf[len - 2] != ReadDOC(docptr, Mplus_LastDataRead)) + return len - 2; + if (buf[len - 1] != ReadDOC(docptr, Mplus_LastDataRead)) + return len - 1; return 0; } @@ -652,7 +656,8 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) void __iomem *docptr = doc->virtadr; int floor = 0; - if(debug)printk("select chip (%d)\n", chip); + if (debug) + printk("select chip (%d)\n", chip); if (chip == -1) { /* Disable flash internally */ @@ -661,7 +666,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) } floor = chip / doc->chips_per_floor; - chip -= (floor * doc->chips_per_floor); + chip -= (floor * doc->chips_per_floor); /* Assert ChipEnable and deassert WriteProtect */ WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); @@ -678,65 +683,54 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip) void __iomem *docptr = doc->virtadr; int floor = 0; - if(debug)printk("select chip (%d)\n", chip); + if (debug) + printk("select chip (%d)\n", chip); if (chip == -1) return; floor = chip / doc->chips_per_floor; - chip -= (floor * doc->chips_per_floor); + chip -= (floor * doc->chips_per_floor); /* 11.4.4 -- deassert CE before changing chip */ - doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); WriteDOC(floor, docptr, FloorSelect); WriteDOC(chip, docptr, CDSNDeviceSelect); - doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); doc->curchip = chip; doc->curfloor = floor; } -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) +#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; - switch(cmd) { - case NAND_CTL_SETNCE: - doc->CDSNControl |= CDSN_CTRL_CE; - break; - case NAND_CTL_CLRNCE: - doc->CDSNControl &= ~CDSN_CTRL_CE; - break; - case NAND_CTL_SETCLE: - doc->CDSNControl |= CDSN_CTRL_CLE; - break; - case NAND_CTL_CLRCLE: - doc->CDSNControl &= ~CDSN_CTRL_CLE; - break; - case NAND_CTL_SETALE: - doc->CDSNControl |= CDSN_CTRL_ALE; - break; - case NAND_CTL_CLRALE: - doc->CDSNControl &= ~CDSN_CTRL_ALE; - break; - case NAND_CTL_SETWP: - doc->CDSNControl |= CDSN_CTRL_WP; - break; - case NAND_CTL_CLRWP: - doc->CDSNControl &= ~CDSN_CTRL_WP; - break; + if (ctrl & NAND_CTRL_CHANGE) { + doc->CDSNControl &= ~CDSN_CTRL_MSK; + doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; + if (debug) + printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + /* 11.4.3 -- 4 NOPs after CSDNControl write */ + DoC_Delay(doc, 4); + } + if (cmd != NAND_CMD_NONE) { + if (DoC_is_2000(doc)) + doc2000_write_byte(mtd, cmd); + else + doc2001_write_byte(mtd, cmd); } - if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); - WriteDOC(doc->CDSNControl, docptr, CDSNControl); - /* 11.4.3 -- 4 NOPs after CSDNControl write */ - DoC_Delay(doc, 4); } -static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; @@ -757,9 +751,9 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col if (command == NAND_CMD_SEQIN) { int readcmd; - if (column >= mtd->oobblock) { + if (column >= mtd->writesize) { /* OOB area */ - column -= mtd->oobblock; + column -= mtd->writesize; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ @@ -783,25 +777,26 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col WriteDOC(column, docptr, Mplus_FlashAddress); } if (page_addr != -1) { - WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress); - WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); /* One more address cycle for higher density devices */ if (this->chipsize & 0x0c000000) { - WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); printk("high density\n"); } } WriteDOC(0, docptr, Mplus_WritePipeTerm); WriteDOC(0, docptr, Mplus_WritePipeTerm); /* deassert ALE */ - if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID) + if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_READID) WriteDOC(0, docptr, Mplus_FlashControl); } /* * program and erase have their own busy handlers * status and sequential in needs no delay - */ + */ switch (command) { case NAND_CMD_PAGEPROG: @@ -818,26 +813,26 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); WriteDOC(0, docptr, Mplus_WritePipeTerm); WriteDOC(0, docptr, Mplus_WritePipeTerm); - while ( !(this->read_byte(mtd) & 0x40)); + while (!(this->read_byte(mtd) & 0x40)) ; return; - /* This applies to read commands */ + /* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given * command delay - */ + */ if (!this->dev_ready) { - udelay (this->chip_delay); + udelay(this->chip_delay); return; } } /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ - ndelay (100); + ndelay(100); /* wait until command is processed */ - while (!this->dev_ready(mtd)); + while (!this->dev_ready(mtd)) ; } static int doc200x_dev_ready(struct mtd_info *mtd) @@ -850,23 +845,25 @@ static int doc200x_dev_ready(struct mtd_info *mtd) /* 11.4.2 -- must NOP four times before checking FR/B# */ DoC_Delay(doc, 4); if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { - if(debug) + if (debug) printk("not ready\n"); return 0; } - if (debug)printk("was ready\n"); + if (debug) + printk("was ready\n"); return 1; } else { /* 11.4.2 -- must NOP four times before checking FR/B# */ DoC_Delay(doc, 4); if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { - if(debug) + if (debug) printk("not ready\n"); return 0; } /* 11.4.2 -- Must NOP twice if it's ready */ DoC_Delay(doc, 2); - if (debug)printk("was ready\n"); + if (debug) + printk("was ready\n"); return 1; } } @@ -885,7 +882,7 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ - switch(mode) { + switch (mode) { case NAND_ECC_READ: WriteDOC(DOC_ECC_RESET, docptr, ECCConf); WriteDOC(DOC_ECC_EN, docptr, ECCConf); @@ -904,7 +901,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ - switch(mode) { + switch (mode) { case NAND_ECC_READ: WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); @@ -917,8 +914,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) } /* This code is only called on write */ -static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - unsigned char *ecc_code) +static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; @@ -962,7 +958,8 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, often. It could be optimized away by examining the data in the writebuf routine, and remembering the result. */ for (i = 0; i < 512; i++) { - if (dat[i] == 0xff) continue; + if (dat[i] == 0xff) + continue; emptymatch = 0; break; } @@ -970,17 +967,20 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, /* If emptymatch still =1, we do have an all-0xff data buffer. Return all-0xff ecc value instead of the computed one, so it'll look just like a freshly-erased page. */ - if (emptymatch) memset(ecc_code, 0xff, 6); + if (emptymatch) + memset(ecc_code, 0xff, 6); #endif return 0; } -static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) { int i, ret = 0; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; + uint8_t calc_ecc[6]; volatile u_char dummy; int emptymatch = 1; @@ -1013,18 +1013,20 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ all-0xff data and stored ecc block. Check the stored ecc. */ if (emptymatch) { for (i = 0; i < 6; i++) { - if (read_ecc[i] == 0xff) continue; + if (read_ecc[i] == 0xff) + continue; emptymatch = 0; break; } } /* If emptymatch still =1, check the data block. */ if (emptymatch) { - /* Note: this somewhat expensive test should not be triggered - often. It could be optimized away by examining the data in - the readbuf routine, and remembering the result. */ + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the readbuf routine, and remembering the result. */ for (i = 0; i < 512; i++) { - if (dat[i] == 0xff) continue; + if (dat[i] == 0xff) + continue; emptymatch = 0; break; } @@ -1033,7 +1035,8 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ erased block, in which case the ECC will not come out right. We'll suppress the error and tell the caller everything's OK. Because it is. */ - if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); + if (!emptymatch) + ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); if (ret > 0) printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); } @@ -1048,13 +1051,22 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ return ret; } -/*u_char mydatabuf[528]; */ - -static struct nand_oobinfo doc200x_oobinfo = { - .useecc = MTD_NANDECC_AUTOPLACE, +//u_char mydatabuf[528]; + +/* The strange out-of-order .oobfree list below is a (possibly unneeded) + * attempt to retain compatibility. It used to read: + * .oobfree = { {8, 8} } + * Since that leaves two bytes unusable, it was changed. But the following + * scheme might affect existing jffs2 installs by moving the cleanmarker: + * .oobfree = { {6, 10} } + * jffs2 seems to handle the above gracefully, but the current scheme seems + * safer. The only problem with it is that any code that parses oobfree must + * be able to handle out-of-order segments. + */ +static struct nand_ecclayout doc200x_oobinfo = { .eccbytes = 6, .eccpos = {0, 1, 2, 3, 4, 5}, - .oobfree = { {8, 8} } + .oobfree = {{8, 8}, {6, 2}} }; /* Find the (I)NFTL Media Header, and optionally also the mirror media header. @@ -1063,28 +1075,28 @@ static struct nand_oobinfo doc200x_oobinfo = { either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media header. The page #s of the found media headers are placed in mh0_page and mh1_page in the DOC private structure. */ -static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, - const char *id, int findmirror) +static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); + unsigned offs; int ret; size_t retlen; - end = min(end, mtd->size); /* paranoia */ - for (offs = 0; offs < end; offs += mtd->erasesize) { - ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); - if (retlen != mtd->oobblock) continue; + for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { + ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) + continue; if (ret) { - printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", - offs); + printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); } - if (memcmp(buf, id, 6)) continue; + if (memcmp(buf, id, 6)) + continue; printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); if (doc->mh0_page == -1) { doc->mh0_page = offs >> this->page_shift; - if (!findmirror) return 1; + if (!findmirror) + return 1; continue; } doc->mh1_page = offs >> this->page_shift; @@ -1097,8 +1109,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, /* Only one mediaheader was found. We want buf to contain a mediaheader on return, so we'll have to re-read the one we found. */ offs = doc->mh0_page << this->page_shift; - ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); - if (retlen != mtd->oobblock) { + ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) { /* Insanity. Give up. */ printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); return 0; @@ -1106,8 +1118,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, return 1; } -static inline int __init nftl_partscan(struct mtd_info *mtd, - struct mtd_partition *parts) +static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; @@ -1115,19 +1126,23 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, u_char *buf; struct NFTLMediaHeader *mh; const unsigned psize = 1 << this->page_shift; + int numparts = 0; unsigned blocks, maxblocks; int offs, numheaders; - buf = kmalloc(mtd->oobblock, GFP_KERNEL); + buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; } - if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; - mh = (struct NFTLMediaHeader *) buf; + if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) + goto out; + mh = (struct NFTLMediaHeader *)buf; + + mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); + mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); + mh->FormattedSize = le32_to_cpu(mh->FormattedSize); -/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ -/* if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */ printk(KERN_INFO " DataOrgID = %s\n" " NumEraseUnits = %d\n" " FirstPhysicalEUN = %d\n" @@ -1136,7 +1151,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, mh->DataOrgID, mh->NumEraseUnits, mh->FirstPhysicalEUN, mh->FormattedSize, mh->UnitSizeFactor); -/*#endif */ blocks = mtd->size >> this->phys_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); @@ -1145,8 +1159,8 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, /* Auto-determine UnitSizeFactor. The constraints are: - There can be at most 32768 virtual blocks. - There can be at most (virtual block size - page size) - virtual blocks (because MediaHeader+BBT must fit in 1). - */ + virtual blocks (because MediaHeader+BBT must fit in 1). + */ mh->UnitSizeFactor = 0xff; while (blocks > maxblocks) { blocks >>= 1; @@ -1179,31 +1193,35 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, offs <<= this->page_shift; offs += mtd->erasesize; - /*parts[0].name = " DiskOnChip Boot / Media Header partition"; */ - /*parts[0].offset = 0; */ - /*parts[0].size = offs; */ + if (show_firmware_partition == 1) { + parts[0].name = " DiskOnChip Firmware / Media Header partition"; + parts[0].offset = 0; + parts[0].size = offs; + numparts = 1; + } + + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = offs; + parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; - parts[0].name = " DiskOnChip BDTL partition"; - parts[0].offset = offs; - parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + offs += parts[numparts].size; + numparts++; - offs += parts[0].size; if (offs < mtd->size) { - parts[1].name = " DiskOnChip Remainder partition"; - parts[1].offset = offs; - parts[1].size = mtd->size - offs; - ret = 2; - goto out; + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = offs; + parts[numparts].size = mtd->size - offs; + numparts++; } - ret = 1; -out: + + ret = numparts; + out: kfree(buf); return ret; } /* This is a stripped-down copy of the code in inftlmount.c */ -static inline int __init inftl_partscan(struct mtd_info *mtd, - struct mtd_partition *parts) +static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; @@ -1220,15 +1238,16 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, if (inftl_bbt_write) end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); - buf = kmalloc(mtd->oobblock, GFP_KERNEL); + buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; } - if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; + if (!find_media_headers(mtd, buf, "BNAND", 0)) + goto out; doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); - mh = (struct INFTLMediaHeader *) buf; + mh = (struct INFTLMediaHeader *)buf; mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); @@ -1237,8 +1256,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, mh->FormatFlags = le32_to_cpu(mh->FormatFlags); mh->PercentUsed = le32_to_cpu(mh->PercentUsed); -/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ -/* if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */ printk(KERN_INFO " bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" @@ -1256,7 +1273,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf, mh->PercentUsed); -/*#endif */ vshift = this->phys_erase_shift + mh->BlockMultiplierBits; @@ -1282,8 +1298,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, ip->spareUnits = le32_to_cpu(ip->spareUnits); ip->Reserved0 = le32_to_cpu(ip->Reserved0); -/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ -/* if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */ printk(KERN_INFO " PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" @@ -1293,16 +1307,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, i, ip->virtualUnits, ip->firstUnit, ip->lastUnit, ip->flags, ip->spareUnits); -/*#endif */ -/* - if ((i == 0) && (ip->firstUnit > 0)) { + if ((show_firmware_partition == 1) && + (i == 0) && (ip->firstUnit > 0)) { parts[0].name = " DiskOnChip IPL / Media Header partition"; parts[0].offset = 0; parts[0].size = mtd->erasesize * ip->firstUnit; numparts = 1; } -*/ if (ip->flags & INFTL_BINARY) parts[numparts].name = " DiskOnChip BDK partition"; @@ -1311,8 +1323,10 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, parts[numparts].offset = ip->firstUnit << vshift; parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; numparts++; - if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; - if (ip->flags & INFTL_LAST) break; + if (ip->lastUnit > lastvunit) + lastvunit = ip->lastUnit; + if (ip->flags & INFTL_LAST) + break; } lastvunit++; if ((lastvunit << vshift) < end) { @@ -1322,7 +1336,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, numparts++; } ret = numparts; -out: + out: kfree(buf); return ret; } @@ -1334,11 +1348,12 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd) struct doc_priv *doc = this->priv; struct mtd_partition parts[2]; - memset((char *) parts, 0, sizeof(parts)); + memset((char *)parts, 0, sizeof(parts)); /* On NFTL, we have to find the media headers before we can read the BBTs, since they're stored in the media header eraseblocks. */ numparts = nftl_partscan(mtd, parts); - if (!numparts) return -EIO; + if (!numparts) + return -EIO; this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | NAND_BBT_VERSION; @@ -1385,8 +1400,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) this->bbt_td->pages[0] = 2; this->bbt_md = NULL; } else { - this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | - NAND_BBT_VERSION; + this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; if (inftl_bbt_write) this->bbt_td->options |= NAND_BBT_WRITE; this->bbt_td->offs = 8; @@ -1396,8 +1410,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) this->bbt_td->reserved_block_code = 0x01; this->bbt_td->pattern = "MSYS_BBT"; - this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | - NAND_BBT_VERSION; + this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; if (inftl_bbt_write) this->bbt_md->options |= NAND_BBT_WRITE; this->bbt_md->offs = 8; @@ -1412,12 +1425,13 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) At least as nand_bbt.c is currently written. */ if ((ret = nand_scan_bbt(mtd, NULL))) return ret; - memset((char *) parts, 0, sizeof(parts)); + memset((char *)parts, 0, sizeof(parts)); numparts = inftl_partscan(mtd, parts); /* At least for now, require the INFTL Media Header. We could probably do without it for non-INFTL use, since all it gives us is autopartitioning, but I want to give it more thought. */ - if (!numparts) return -EIO; + if (!numparts) + return -EIO; add_mtd_device(mtd); #ifdef CONFIG_MTD_PARTITIONS if (!no_autopart) @@ -1431,7 +1445,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - this->write_byte = doc2000_write_byte; this->read_byte = doc2000_read_byte; this->write_buf = doc2000_writebuf; this->read_buf = doc2000_readbuf; @@ -1449,7 +1462,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - this->write_byte = doc2001_write_byte; this->read_byte = doc2001_read_byte; this->write_buf = doc2001_writebuf; this->read_buf = doc2001_readbuf; @@ -1481,16 +1493,15 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - this->write_byte = NULL; this->read_byte = doc2001plus_read_byte; this->write_buf = doc2001plus_writebuf; this->read_buf = doc2001plus_readbuf; this->verify_buf = doc2001plus_verifybuf; this->scan_bbt = inftl_scan_bbt; - this->hwcontrol = NULL; + this->cmd_ctrl = NULL; this->select_chip = doc2001plus_select_chip; this->cmdfunc = doc2001plus_command; - this->enable_hwecc = doc2001plus_enable_hwecc; + this->ecc.hwctl = doc2001plus_enable_hwecc; doc->chips_per_floor = 1; mtd->name = "DiskOnChip Millennium Plus"; @@ -1498,7 +1509,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) return 1; } -static inline int __init doc_probe(unsigned long physadr) +static int __init doc_probe(unsigned long physadr) { unsigned char ChipID; struct mtd_info *mtd; @@ -1527,20 +1538,16 @@ static inline int __init doc_probe(unsigned long physadr) save_control = ReadDOC(virtadr, DOCControl); /* Reset the DiskOnChip ASIC */ - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, - virtadr, DOCControl); - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, - virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); /* Enable the DiskOnChip ASIC */ - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, - virtadr, DOCControl); - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, - virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); ChipID = ReadDOC(virtadr, ChipID); - switch(ChipID) { + switch (ChipID) { case DOC_ChipID_Doc2k: reg = DoC_2k_ECCStatus; break; @@ -1556,15 +1563,13 @@ static inline int __init doc_probe(unsigned long physadr) ReadDOC(virtadr, Mplus_Power); /* Reset the Millennium Plus ASIC */ - tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | - DOC_MODE_BDECT; + tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); mdelay(1); /* Enable the Millennium Plus ASIC */ - tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | - DOC_MODE_BDECT; + tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); mdelay(1); @@ -1588,7 +1593,7 @@ static inline int __init doc_probe(unsigned long physadr) goto notfound; } /* Check the TOGGLE bit in the ECC register */ - tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; if ((tmp == tmpb) || (tmp != tmpc)) { @@ -1618,11 +1623,11 @@ static inline int __init doc_probe(unsigned long physadr) if (ChipID == DOC_ChipID_DocMilPlus16) { WriteDOC(~newval, virtadr, Mplus_AliasResolution); oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); - WriteDOC(newval, virtadr, Mplus_AliasResolution); /* restore it */ + WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it } else { WriteDOC(~newval, virtadr, AliasResolution); oldval = ReadDOC(doc->virtadr, AliasResolution); - WriteDOC(newval, virtadr, AliasResolution); /* restore it */ + WriteDOC(newval, virtadr, AliasResolution); // restore it } newval = ~newval; if (oldval == newval) { @@ -1634,16 +1639,13 @@ static inline int __init doc_probe(unsigned long physadr) printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); len = sizeof(struct mtd_info) + - sizeof(struct nand_chip) + - sizeof(struct doc_priv) + - (2 * sizeof(struct nand_bbt_descr)); - mtd = kmalloc(len, GFP_KERNEL); + sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); + mtd = kzalloc(len, GFP_KERNEL); if (!mtd) { printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); ret = -ENOMEM; goto fail; } - memset(mtd, 0, len); nand = (struct nand_chip *) (mtd + 1); doc = (struct doc_priv *) (nand + 1); @@ -1655,17 +1657,19 @@ static inline int __init doc_probe(unsigned long physadr) nand->priv = doc; nand->select_chip = doc200x_select_chip; - nand->hwcontrol = doc200x_hwcontrol; + nand->cmd_ctrl = doc200x_hwcontrol; nand->dev_ready = doc200x_dev_ready; nand->waitfunc = doc200x_wait; nand->block_bad = doc200x_block_bad; - nand->enable_hwecc = doc200x_enable_hwecc; - nand->calculate_ecc = doc200x_calculate_ecc; - nand->correct_data = doc200x_correct_data; + nand->ecc.hwctl = doc200x_enable_hwecc; + nand->ecc.calculate = doc200x_calculate_ecc; + nand->ecc.correct = doc200x_correct_data; - nand->autooob = &doc200x_oobinfo; - nand->eccmode = NAND_ECC_HW6_512; - nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; + nand->ecc.layout = &doc200x_oobinfo; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = 512; + nand->ecc.bytes = 6; + nand->options = NAND_USE_FLASH_BBT; doc->physadr = physadr; doc->virtadr = virtadr; @@ -1699,11 +1703,11 @@ static inline int __init doc_probe(unsigned long physadr) doclist = mtd; return 0; -notfound: + notfound: /* Put back the contents of the DOCControl register, in case it's not actually a DiskOnChip. */ WriteDOC(save_control, virtadr, DOCControl); -fail: + fail: iounmap(virtadr); return ret; } @@ -1740,7 +1744,7 @@ static int __init init_nanddoc(void) */ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); if (!rs_decoder) { - printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); return -ENOMEM; } @@ -1750,7 +1754,7 @@ static int __init init_nanddoc(void) if (ret < 0) goto outerr; } else { - for (i=0; (doc_locations[i] != 0xffffffff); i++) { + for (i = 0; (doc_locations[i] != 0xffffffff); i++) { doc_probe(doc_locations[i]); } } @@ -1762,7 +1766,7 @@ static int __init init_nanddoc(void) goto outerr; } return 0; -outerr: + outerr: free_rs(rs_decoder); return ret; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6416d1529e9..aeb179731d6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -10,39 +10,21 @@ * http://www.linux-mtd.infradead.org/tech/nand.html * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * 2002 Thomas Gleixner (tglx@linutronix.de) + * 2002-2006 Thomas Gleixner (tglx@linutronix.de) * - * 02-08-2004 tglx: support for strange chips, which cannot auto increment - * pages on read / read_oob - * - * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes - * pointed this out, as he marked an auto increment capable chip - * as NOAUTOINCR in the board driver. - * Make reads over block boundaries work too - * - * 04-14-2004 tglx: first working version for 2k page size chips - * - * 05-19-2004 tglx: Basic support for Renesas AG-AND chips - * - * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared - * among multiple independend devices. Suggestions and initial patch - * from Ben Dooks - * - * Credits: + * Credits: * David Woodhouse for adding multichip support * * Aleph One Ltd. and Toby Churchill Ltd. for supporting the * rework for 2K page size chips * - * TODO: + * TODO: * Enable cached programming for 2k page size chips * Check, if mtd->ecctype should be set to MTD_ECC_HW * if we have HW ecc support. * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * - * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -51,8 +33,10 @@ /* XXX U-BOOT XXX */ #if 0 +#include #include #include +#include #include #include #include @@ -62,6 +46,7 @@ #include #include #include +#include #include #ifdef CONFIG_MTD_PARTITIONS @@ -72,10 +57,13 @@ #include +#define ENOTSUPP 524 /* Operation is not supported */ + #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) #include #include +#include #include #include #include @@ -89,83 +77,67 @@ #endif /* Define default oob placement schemes for large and small page devices */ -static struct nand_oobinfo nand_oob_8 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_8 = { .eccbytes = 3, .eccpos = {0, 1, 2}, - .oobfree = { {3, 2}, {6, 2} } + .oobfree = { + {.offset = 3, + .length = 2}, + {.offset = 6, + .length = 2}} }; -static struct nand_oobinfo nand_oob_16 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_16 = { .eccbytes = 6, .eccpos = {0, 1, 2, 3, 6, 7}, - .oobfree = { {8, 8} } + .oobfree = { + {.offset = 8, + . length = 8}} }; -static struct nand_oobinfo nand_oob_64 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_64 = { .eccbytes = 24, .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { {2, 38} } + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + .length = 38}} }; -static struct nand_oobinfo nand_oob_128 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_128 = { .eccbytes = 48, .eccpos = { - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - .oobfree = { {2, 78} } + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + .length = 78}} }; -/* This is used for padding purposes in nand_write_oob */ -static u_char *ffchars; + +static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, + int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); /* - * NAND low-level MTD interface functions + * For devices which display every fart in the system on a seperate LED. Is + * compiled away when LED support is disabled. */ -static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); -static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); -static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); - -static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); -static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); -static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); -static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); -static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, - size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); -static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); /* XXX U-BOOT XXX */ #if 0 -static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t * retlen); -static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); -#endif -static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); -static void nand_sync (struct mtd_info *mtd); - -/* Some internal functions */ -static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, - struct nand_oobinfo *oobsel, int mode); -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE -static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, - u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); -#else -#define nand_verify_pages(...) (0) +DEFINE_LED_TRIGGER(nand_led_trigger); #endif -static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); - /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure @@ -174,33 +146,25 @@ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int n */ /* XXX U-BOOT XXX */ #if 0 -static void nand_release_device (struct mtd_info *mtd) +static void nand_release_device(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; /* De-select the NAND device */ - this->select_chip(mtd, -1); - /* Do we have a hardware controller ? */ - if (this->controller) { - spin_lock(&this->controller->lock); - this->controller->active = NULL; - spin_unlock(&this->controller->lock); - } - /* Release the chip */ - spin_lock (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock (&this->chip_lock); + chip->select_chip(mtd, -1); + + /* Release the controller and the chip */ + spin_lock(&chip->controller->lock); + chip->controller->active = NULL; + chip->state = FL_READY; + wake_up(&chip->controller->wq); + spin_unlock(&chip->controller->lock); } #else static void nand_release_device (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; this->select_chip(mtd, -1); /* De-select the NAND device */ - if (ffchars) { - kfree(ffchars); - ffchars = NULL; - } } #endif @@ -210,23 +174,10 @@ static void nand_release_device (struct mtd_info *mtd) * * Default read function for 8bit buswith */ -static u_char nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd->priv; - return readb(this->IO_ADDR_R); -} - -/** - * nand_write_byte - [DEFAULT] write one byte to the chip - * @mtd: MTD device structure - * @byte: pointer to data byte to write - * - * Default write function for 8it buswith - */ -static void nand_write_byte(struct mtd_info *mtd, u_char byte) +static uint8_t nand_read_byte(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; - writeb(byte, this->IO_ADDR_W); + struct nand_chip *chip = mtd->priv; + return readb(chip->IO_ADDR_R); } /** @@ -236,24 +187,10 @@ static void nand_write_byte(struct mtd_info *mtd, u_char byte) * Default read function for 16bit buswith with * endianess conversion */ -static u_char nand_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd->priv; - return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); -} - -/** - * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip - * @mtd: MTD device structure - * @byte: pointer to data byte to write - * - * Default write function for 16bit buswith with - * endianess conversion - */ -static void nand_write_byte16(struct mtd_info *mtd, u_char byte) +static uint8_t nand_read_byte16(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; - writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); + struct nand_chip *chip = mtd->priv; + return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); } /** @@ -265,40 +202,26 @@ static void nand_write_byte16(struct mtd_info *mtd, u_char byte) */ static u16 nand_read_word(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; - return readw(this->IO_ADDR_R); -} - -/** - * nand_write_word - [DEFAULT] write one word to the chip - * @mtd: MTD device structure - * @word: data word to write - * - * Default write function for 16bit buswith without - * endianess conversion - */ -static void nand_write_word(struct mtd_info *mtd, u16 word) -{ - struct nand_chip *this = mtd->priv; - writew(word, this->IO_ADDR_W); + struct nand_chip *chip = mtd->priv; + return readw(chip->IO_ADDR_R); } /** * nand_select_chip - [DEFAULT] control CE line * @mtd: MTD device structure - * @chip: chipnumber to select, -1 for deselect + * @chipnr: chipnumber to select, -1 for deselect * * Default select function for 1 chip devices. */ -static void nand_select_chip(struct mtd_info *mtd, int chip) +static void nand_select_chip(struct mtd_info *mtd, int chipnr) { - struct nand_chip *this = mtd->priv; - switch(chip) { + struct nand_chip *chip = mtd->priv; + + switch (chipnr) { case -1: - this->hwcontrol(mtd, NAND_CTL_CLRNCE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); break; case 0: - this->hwcontrol(mtd, NAND_CTL_SETNCE); break; default: @@ -314,13 +237,13 @@ static void nand_select_chip(struct mtd_info *mtd, int chip) * * Default write function for 8bit buswith */ -static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; - for (i=0; iIO_ADDR_W); + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); } /** @@ -331,13 +254,13 @@ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) * * Default read function for 8bit buswith */ -static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { int i; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; - for (i=0; iIO_ADDR_R); + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); } /** @@ -348,15 +271,14 @@ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) * * Default verify function for 8bit buswith */ -static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; - for (i=0; iIO_ADDR_R)) + for (i = 0; i < len; i++) + if (buf[i] != readb(chip->IO_ADDR_R)) return -EFAULT; - return 0; } @@ -368,15 +290,15 @@ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) * * Default write function for 16bit buswith */ -static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; - for (i=0; iIO_ADDR_W); + for (i = 0; i < len; i++) + writew(p[i], chip->IO_ADDR_W); } @@ -388,15 +310,15 @@ static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) * * Default read function for 16bit buswith */ -static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) { int i; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; - for (i=0; iIO_ADDR_R); + for (i = 0; i < len; i++) + p[i] = readw(chip->IO_ADDR_R); } /** @@ -407,15 +329,15 @@ static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) * * Default verify function for 16bit buswith */ -static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; - for (i=0; iIO_ADDR_R)) + for (i = 0; i < len; i++) + if (p[i] != readw(chip->IO_ADDR_R)) return -EFAULT; return 0; @@ -432,38 +354,36 @@ static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { int page, chipnr, res = 0; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; u16 bad; - page = (int)(ofs >> this->page_shift) & this->pagemask; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; if (getchip) { - chipnr = (int)(ofs >> this->chip_shift); + chipnr = (int)(ofs >> chip->chip_shift); - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd, FL_READING); + nand_get_device(chip, mtd, FL_READING); /* Select the NAND device */ - this->select_chip(mtd, chipnr); + chip->select_chip(mtd, chipnr); } - if (this->options & NAND_BUSWIDTH_16) { - this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page); - bad = cpu_to_le16(this->read_word(mtd)); - if (this->badblockpos & 0x1) - bad >>= 1; + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, + page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; if ((bad & 0xFF) != 0xff) res = 1; } else { - this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page); - if (this->read_byte(mtd) != 0xff) + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); + if (chip->read_byte(mtd) != 0xff) res = 1; } - if (getchip) { - /* Deselect and wake up anyone waiting on the device */ + if (getchip) nand_release_device(mtd); - } return res; } @@ -478,22 +398,33 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) */ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *this = mtd->priv; - u_char buf[2] = {0, 0}; - size_t retlen; - int block; + struct nand_chip *chip = mtd->priv; + uint8_t buf[2] = { 0, 0 }; + int block, ret; /* Get block number */ - block = ((int) ofs) >> this->bbt_erase_shift; - this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + block = (int)(ofs >> chip->bbt_erase_shift); + if (chip->bbt) + chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ - if (this->options & NAND_USE_FLASH_BBT) - return nand_update_bbt (mtd, ofs); + if (chip->options & NAND_USE_FLASH_BBT) + ret = nand_update_bbt(mtd, ofs); + else { + /* We write two bytes, so we dont have to mess with 16 bit + * access + */ + ofs += mtd->oobsize; + chip->ops.len = chip->ops.ooblen = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; - /* We write two bytes, so we dont have to mess with 16 bit access */ - ofs += mtd->oobsize + (this->badblockpos & ~0x01); - return nand_write_oob (mtd, ofs , 2, &retlen, buf); + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + } + if (!ret) + mtd->ecc_stats.badblocks++; + return ret; } /** @@ -503,12 +434,12 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) * * The function expects, that the device is already selected */ -static int nand_check_wp (struct mtd_info *mtd) +static int nand_check_wp(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; /* Check the WP bit */ - this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); - return (this->read_byte(mtd) & 0x80) ? 0 : 1; + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } /** @@ -521,16 +452,46 @@ static int nand_check_wp (struct mtd_info *mtd) * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */ -static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, + int allowbbt) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; - if (!this->bbt) - return this->block_bad(mtd, ofs, getchip); + if (!chip->bbt) + return chip->block_bad(mtd, ofs, getchip); /* Return info from the table */ - return nand_isbad_bbt (mtd, ofs, allowbbt); + return nand_isbad_bbt(mtd, ofs, allowbbt); +} + +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +/* XXX U-BOOT XXX */ +#if 0 +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + unsigned long timeo = jiffies + 2; + + led_trigger_event(nand_led_trigger, LED_FULL); + /* wait until command is processed or timeout occures */ + do { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + } while (time_before(jiffies, timeo)); + led_trigger_event(nand_led_trigger, LED_OFF); } +EXPORT_SYMBOL_GPL(nand_wait_ready); +#else +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + nand_wait(mtd, chip); +} +#endif /** * nand_command - [DEFAULT] Send command to NAND device @@ -542,21 +503,21 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i * Send command to NAND device. This function is used for small page * devices (256/512 Bytes per page) */ -static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { - register struct nand_chip *this = mtd->priv; + register struct nand_chip *chip = mtd->priv; + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); /* * Write out the command to the device. */ if (command == NAND_CMD_SEQIN) { int readcmd; - if (column >= mtd->oobblock) { + if (column >= mtd->writesize) { /* OOB area */ - column -= mtd->oobblock; + column -= mtd->writesize; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ @@ -565,38 +526,37 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in column -= 256; readcmd = NAND_CMD_READ1; } - this->write_byte(mtd, readcmd); + chip->cmd_ctrl(mtd, readcmd, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; } - this->write_byte(mtd, command); + chip->cmd_ctrl(mtd, command, ctrl); - /* Set ALE and clear CLE to start address cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - - if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); - - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) - column >>= 1; - this->write_byte(mtd, column); - } - if (page_addr != -1) { - this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); - this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); - /* One more address cycle for devices > 32MiB */ - if (this->chipsize > (32 << 20)) - this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); - } - /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); + /* + * Address cycle, when necessary + */ + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); + /* One more address cycle for devices > 32MiB */ + if (chip->chipsize > (32 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers * status and sequential in needs no delay - */ + */ switch (command) { case NAND_CMD_PAGEPROG: @@ -607,32 +567,32 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in return; case NAND_CMD_RESET: - if (this->dev_ready) + if (chip->dev_ready) break; - udelay(this->chip_delay); - this->hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_STATUS); - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; return; - /* This applies to read commands */ + /* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given * command delay - */ - if (!this->dev_ready) { - udelay (this->chip_delay); + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); return; } } - /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ - ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + ndelay(100); + + nand_wait_ready(mtd); } /** @@ -642,55 +602,53 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in * @column: the column address for this command, -1 if none * @page_addr: the page address for this command, -1 if none * - * Send command to NAND device. This is the version for the new large page devices - * We dont have the seperate regions as we have in the small page devices. - * We must emulate NAND_CMD_READOOB to keep the code compatible. - * + * Send command to NAND device. This is the version for the new large page + * devices We dont have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. */ -static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { - register struct nand_chip *this = mtd->priv; + register struct nand_chip *chip = mtd->priv; /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { - column += mtd->oobblock; + column += mtd->writesize; command = NAND_CMD_READ0; } - - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); - /* Write out the command to the device. */ - this->write_byte(mtd, command); - /* End command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); + /* Command latch cycle */ + chip->cmd_ctrl(mtd, command & 0xff, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16) column >>= 1; - this->write_byte(mtd, column & 0xff); - this->write_byte(mtd, column >> 8); + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, column >> 8, ctrl); } if (page_addr != -1) { - this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); - this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); /* One more address cycle for devices > 128MiB */ - if (this->chipsize > (128 << 20)) - this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); + if (chip->chipsize > (128 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); } - /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers - * status and sequential in needs no delay - */ + * status, sequential in, and deplete1 need no delay + */ switch (command) { case NAND_CMD_CACHEDPROG: @@ -698,51 +656,69 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: return; + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(chip->chip_delay); + return; case NAND_CMD_RESET: - if (this->dev_ready) + if (chip->dev_ready) break; - udelay(this->chip_delay); - this->hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_STATUS); - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); return; case NAND_CMD_READ0: - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); - /* Write out the start read command */ - this->write_byte(mtd, NAND_CMD_READSTART); - /* End command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - /* Fall through into ready check */ - - /* This applies to read commands */ + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given * command delay - */ - if (!this->dev_ready) { - udelay (this->chip_delay); + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); return; } } /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ - ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + ndelay(100); + + nand_wait_ready(mtd); } /** * nand_get_device - [GENERIC] Get chip for selected access - * @this: the nand chip descriptor + * @chip: the nand chip descriptor * @mtd: MTD device structure * @new_state: the state which is requested * @@ -750,100 +726,96 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, */ /* XXX U-BOOT XXX */ #if 0 -static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +static int +nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) { - struct nand_chip *active = this; - - DECLARE_WAITQUEUE (wait, current); + spinlock_t *lock = &chip->controller->lock; + wait_queue_head_t *wq = &chip->controller->wq; + DECLARE_WAITQUEUE(wait, current); + retry: + spin_lock(lock); - /* - * Grab the lock and see if the device is available - */ -retry: /* Hardware controller shared among independend devices */ - if (this->controller) { - spin_lock (&this->controller->lock); - if (this->controller->active) - active = this->controller->active; - else - this->controller->active = this; - spin_unlock (&this->controller->lock); - } + /* Hardware controller shared among independend devices */ + if (!chip->controller->active) + chip->controller->active = chip; - if (active == this) { - spin_lock (&this->chip_lock); - if (this->state == FL_READY) { - this->state = new_state; - spin_unlock (&this->chip_lock); - return; - } + if (chip->controller->active == chip && chip->state == FL_READY) { + chip->state = new_state; + spin_unlock(lock); + return 0; } - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&active->wq, &wait); - spin_unlock (&active->chip_lock); - schedule (); - remove_wait_queue (&active->wq, &wait); + if (new_state == FL_PM_SUSPENDED) { + spin_unlock(lock); + return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); goto retry; } #else -static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) {} +static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +{ + return 0; +} #endif /** * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure - * @this: NAND chip structure - * @state: state to select the max. timeout value + * @chip: NAND chip structure * * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs - * -*/ + */ /* XXX U-BOOT XXX */ #if 0 -static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - unsigned long timeo = jiffies; - int status; + + unsigned long timeo = jiffies; + int status, state = chip->state; if (state == FL_ERASING) - timeo += (HZ * 400) / 1000; + timeo += (HZ * 400) / 1000; else - timeo += (HZ * 20) / 1000; + timeo += (HZ * 20) / 1000; + + led_trigger_event(nand_led_trigger, LED_FULL); /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ - ndelay (100); + ndelay(100); - if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) - this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); + if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) + chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); else - this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); while (time_before(jiffies, timeo)) { - /* Check, if we were interrupted */ - if (this->state != state) - return 0; - - if (this->dev_ready) { - if (this->dev_ready(mtd)) + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) break; } else { - if (this->read_byte(mtd) & NAND_STATUS_READY) + if (chip->read_byte(mtd) & NAND_STATUS_READY) break; } - yield (); + cond_resched(); } - status = (int) this->read_byte(mtd); - return status; + led_trigger_event(nand_led_trigger, LED_OFF); - return 0; + status = (int)chip->read_byte(mtd); + return status; } #else -static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) { unsigned long timeo; + int state = this->state; if (state == FL_ERASING) timeo = (CFG_HZ * 400) / 1000; @@ -881,1211 +853,1135 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) #endif /** - * nand_write_page - [GENERIC] write one page - * @mtd: MTD device structure - * @this: NAND chip structure - * @page: startpage inside the chip, must be called with (page & this->pagemask) - * @oob_buf: out of band data buffer - * @oobsel: out of band selecttion structre - * @cached: 1 = enable cached programming if supported by chip - * - * Nand_page_program function is used for write and writev ! - * This function will always program a full page of data - * If you call it with a non page aligned buffer, you're lost :) - * - * Cached programming is not supported yet. + * nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data */ -static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, - u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) { - int i, status; - u_char ecc_code[NAND_MAX_OOBSIZE]; - int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; - uint *oob_config = oobsel->eccpos; - int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; - int eccbytes = 0; + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} - /* FIXME: Enable cached programming */ - cached = 0; +/** + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; - /* Send command to begin auto page programming */ - this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); + chip->ecc.read_page_raw(mtd, chip, buf); - /* Write out complete page of data, take care of eccmode */ - switch (eccmode) { - /* No ecc, write all */ - case NAND_ECC_NONE: - printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); - this->write_buf(mtd, this->data_poi, mtd->oobblock); - break; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); - /* Software ecc 3/256, write all */ - case NAND_ECC_SOFT: - for (; eccsteps; eccsteps--) { - this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); - for (i = 0; i < 3; i++, eccidx++) - oob_buf[oob_config[eccidx]] = ecc_code[i]; - datidx += this->eccsize; - } - this->write_buf(mtd, this->data_poi, mtd->oobblock); - break; - default: - eccbytes = this->eccbytes; - for (; eccsteps; eccsteps--) { - /* enable hardware ecc logic for write */ - this->enable_hwecc(mtd, NAND_ECC_WRITE); - this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); - this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); - for (i = 0; i < eccbytes; i++, eccidx++) - oob_buf[oob_config[eccidx]] = ecc_code[i]; - /* If the hardware ecc provides syndromes then - * the ecc code must be written immediately after - * the data bytes (words) */ - if (this->options & NAND_HWECC_SYNDROME) - this->write_buf(mtd, ecc_code, eccbytes); - datidx += this->eccsize; - } - break; - } + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; - /* Write out OOB data */ - if (this->options & NAND_HWECC_SYNDROME) - this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); - else - this->write_buf(mtd, oob_buf, mtd->oobsize); - - /* Send command to actually program the data */ - this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); - - if (!cached) { - /* call wait ready function */ - status = this->waitfunc (mtd, this, FL_WRITING); - /* See if device thinks it succeeded */ - if (status & 0x01) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "%s: Failed write, page 0x%08x, ", - __FUNCTION__, page); - return -EIO; - } - } else { - /* FIXME: Implement cached programming ! */ - /* wait until cache is ready*/ - /* status = this->waitfunc (mtd, this, FL_CACHEDRPG); */ + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; } return 0; } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /** - * nand_verify_pages - [GENERIC] verify the chip contents after a write - * @mtd: MTD device structure - * @this: NAND chip structure - * @page: startpage inside the chip, must be called with (page & this->pagemask) - * @numpages: number of pages to verify - * @oob_buf: out of band data buffer - * @oobsel: out of band selecttion structre - * @chipnr: number of the current chip - * @oobmode: 1 = full buffer verify, 0 = ecc only + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data * - * The NAND device assumes that it is always writing to a cleanly erased page. - * Hence, it performs its internal write verification only on bits that - * transitioned from 1 to 0. The device does NOT verify the whole page on a - * byte by byte basis. It is possible that the page was not completely erased - * or the page is becoming unusable due to wear. The read with ECC would catch - * the error later when the ECC page check fails, but we would rather catch - * it early in the page write stage. Better to write no data than invalid data. + * Not for syndrome calculating ecc controllers which need a special oob layout */ -static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, - u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) -{ - int i, j, datidx = 0, oobofs = 0, res = -EIO; - int eccsteps = this->eccsteps; - int hweccbytes; - u_char oobdata[64]; - - hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; - - /* Send command to read back the first page */ - this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); - - for(;;) { - for (j = 0; j < eccsteps; j++) { - /* Loop through and verify the data */ - if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " - "Failed write verify, page 0x%08x ", - __FUNCTION__, page); - goto out; - } - datidx += mtd->eccsize; - /* Have we a hw generator layout ? */ - if (!hweccbytes) - continue; - if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " - "Failed write verify, page 0x%08x ", - __FUNCTION__, page); - goto out; - } - oobofs += hweccbytes; - } +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - /* check, if we must compare all data or if we just have to - * compare the ecc bytes - */ - if (oobmode) { - if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " - "Failed write verify, page 0x%08x ", - __FUNCTION__, page); - goto out; - } - } else { - /* Read always, else autoincrement fails */ - this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); - - if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { - int ecccnt = oobsel->eccbytes; - - for (i = 0; i < ecccnt; i++) { - int idx = oobsel->eccpos[i]; - if (oobdata[idx] != oob_buf[oobofs + idx] ) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "%s: Failed ECC write " - "verify, page 0x%08x, " - "%6i bytes were succesful\n", - __FUNCTION__, page, i); - goto out; - } - } - } - } - oobofs += mtd->oobsize - hweccbytes * eccsteps; - page++; - numpages--; - - /* Apply delay or wait for ready/busy pin - * Do this before the AUTOINCR check, so no problems - * arise if a chip which does auto increment - * is marked as NOAUTOINCR by the board driver. - * Do this also before returning, so the chip is - * ready for the next command. - */ - if (!this->dev_ready) - udelay (this->chip_delay); - else - while (!this->dev_ready(mtd)); + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; - /* All done, return happy */ - if (!numpages) - return 0; + eccsteps = chip->ecc.steps; + p = buf; + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; - /* Check, if the chip supports auto page increment */ - if (!NAND_CANAUTOINCR(this)) - this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; } - /* - * Terminate the read command. We come here in case of an error - * So we must issue a reset command. - */ -out: - this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); - return res; + return 0; } -#endif /** - * nand_read - [MTD Interface] MTD compability function for nand_read_ecc - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data * - * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL -*/ -static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) { - return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->read_buf(mtd, oob, eccbytes); + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->read_buf(mtd, oob, i); + + return 0; } +/** + * nand_transfer_oob - [Internal] Transfer oob to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops, size_t len) +{ + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, roffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Read request not from offset 0 ? */ + if (unlikely(roffs)) { + if (roffs >= free->length) { + roffs -= free->length; + continue; + } + boffs = free->offset + roffs; + bytes = min_t(size_t, len, + (free->length - roffs)); + roffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(oob, chip->oob_poi + boffs, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} /** - * nand_read_ecc - [MTD Interface] Read data with ECC + * nand_do_read_ops - [Internal] Read data with ECC + * * @mtd: MTD device structure * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data - * @oob_buf: filesystem supplied oob data buffer - * @oobsel: oob selection structure + * @ops: oob ops structure * - * NAND read with ECC + * Internal function. Called with chip held. */ -static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { - int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; - int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; - struct nand_chip *this = mtd->priv; - u_char *data_poi, *oob_data = oob_buf; - u_char ecc_calc[NAND_MAX_OOBSIZE]; - u_char ecc_code[NAND_MAX_OOBSIZE]; - int eccmode, eccsteps; - unsigned *oob_config; - int datidx; - int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; - int eccbytes; - int compareecc = 1; - int oobreadlen; + int chipnr, page, realpage, col, bytes, aligned; + struct nand_chip *chip = mtd->priv; + struct mtd_ecc_stats stats; + int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + int sndcmd = 1; + int ret = 0; + uint32_t readlen = ops->len; + uint32_t oobreadlen = ops->ooblen; + uint8_t *bufpoi, *oob, *buf; + stats = mtd->ecc_stats; - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", - (unsigned int) from, (int) len); + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_read_ecc: Attempt read beyond end of device\n"); - *retlen = 0; - return -EINVAL; - } + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd ,FL_READING); + col = (int)(from & (mtd->writesize - 1)); - /* use userspace supplied oobinfo, if zero */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; + buf = ops->datbuf; + oob = ops->oobbuf; - /* Autoplace of oob data ? Use the default placement scheme */ - if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) - oobsel = this->autooob; + while(1) { + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); - eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; - oob_config = oobsel->eccpos; + /* Is the current page in the buffer ? */ + if (realpage != chip->pagebuf || oob) { + bufpoi = aligned ? buf : chip->buffers->databuf; - /* Select the NAND device */ - chipnr = (int)(from >> this->chip_shift); - this->select_chip(mtd, chipnr); + if (likely(sndcmd)) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } - /* First we calculate the starting page */ - realpage = (int) (from >> this->page_shift); - page = realpage & this->pagemask; + /* Now read the page into the buffer */ + if (unlikely(ops->mode == MTD_OOB_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + else + ret = chip->ecc.read_page(mtd, chip, bufpoi); + if (ret < 0) + break; - /* Get raw starting column */ - col = from & (mtd->oobblock - 1); + /* Transfer not aligned data */ + if (!aligned) { + chip->pagebuf = realpage; + memcpy(buf, chip->buffers->databuf + col, bytes); + } - end = mtd->oobblock; - ecc = this->eccsize; - eccbytes = this->eccbytes; + buf += bytes; + + if (unlikely(oob)) { + /* Raw mode does data:oob:data:oob */ + if (ops->mode != MTD_OOB_RAW) { + int toread = min(oobreadlen, + chip->ecc.layout->oobavail); + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } + } else + buf = nand_transfer_oob(chip, + buf, ops, mtd->oobsize); + } - if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) - compareecc = 0; + if (!(chip->options & NAND_NO_READRDY)) { + /* + * Apply delay or wait for ready/busy pin. Do + * this before the AUTOINCR check, so no + * problems arise if a chip which does auto + * increment is marked as NOAUTOINCR by the + * board driver. + */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + } else { + memcpy(buf, chip->buffers->databuf + col, bytes); + buf += bytes; + } - oobreadlen = mtd->oobsize; - if (this->options & NAND_HWECC_SYNDROME) - oobreadlen -= oobsel->eccbytes; + readlen -= bytes; - /* Loop until all data read */ - while (read < len) { + if (!readlen) + break; - int aligned = (!col && (len - read) >= end); - /* - * If the read is not page aligned, we have to read into data buffer - * due to ecc, else we read into return buffer direct - */ - if (aligned) - data_poi = &buf[read]; - else - data_poi = this->data_buf; + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage++; - /* Check, if we have this page in the buffer - * - * FIXME: Make it work when we must provide oob data too, - * check the usage of data_buf oob field - */ - if (realpage == this->pagebuf && !oob_buf) { - /* aligned read ? */ - if (aligned) - memcpy (data_poi, this->data_buf, end); - goto readdata; + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); } - /* Check, if we must send the read command */ - if (sndcmd) { - this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); - sndcmd = 0; - } + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) + sndcmd = 1; + } - /* get oob area, if we have no oob buffer from fs-driver */ - if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || - oobsel->useecc == MTD_NANDECC_AUTOPL_USR) - oob_data = &this->data_buf[end]; + ops->retlen = ops->len - (size_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; - eccsteps = this->eccsteps; + if (ret) + return ret; - switch (eccmode) { - case NAND_ECC_NONE: { /* No ECC, Read in a page */ -/* XXX U-BOOT XXX */ -#if 0 - static unsigned long lastwhinge = 0; - if ((lastwhinge / HZ) != (jiffies / HZ)) { - printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); - lastwhinge = jiffies; - } -#else - puts("Reading data from NAND FLASH without ECC is not recommended\n"); -#endif - this->read_buf(mtd, data_poi, end); - break; - } + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; - case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ - this->read_buf(mtd, data_poi, end); - for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) - this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); - break; + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} - default: - for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { - this->enable_hwecc(mtd, NAND_ECC_READ); - this->read_buf(mtd, &data_poi[datidx], ecc); - - /* HW ecc with syndrome calculation must read the - * syndrome from flash immidiately after the data */ - if (!compareecc) { - /* Some hw ecc generators need to know when the - * syndrome is read from flash */ - this->enable_hwecc(mtd, NAND_ECC_READSYN); - this->read_buf(mtd, &oob_data[i], eccbytes); - /* We calc error correction directly, it checks the hw - * generator for an error, reads back the syndrome and - * does the error correction on the fly */ - if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " - "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); - ecc_failed++; - } - } else { - this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); - } - } - break; - } +/** + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read + */ +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + int ret; - /* read oobdata */ - this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; - /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ - if (!compareecc) - goto readoob; + nand_get_device(chip, mtd, FL_READING); - /* Pick the ECC bytes out of the oob data */ - for (j = 0; j < oobsel->eccbytes; j++) - ecc_code[j] = oob_data[oob_config[j]]; + chip->ops.len = len; + chip->ops.datbuf = buf; + chip->ops.oobbuf = NULL; - /* correct data, if neccecary */ - for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { - ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); + ret = nand_do_read_ops(mtd, from, &chip->ops); - /* Get next chunk of ecc bytes */ - j += eccbytes; + *retlen = chip->ops.retlen; - /* Check, if we have a fs supplied oob-buffer, - * This is the legacy mode. Used by YAFFS1 - * Should go away some day - */ - if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { - int *p = (int *)(&oob_data[mtd->oobsize]); - p[i] = ecc_status; - } + nand_release_device(mtd); - if (ecc_status == -1) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " - "Failed ECC read, page 0x%08x\n", - page); - ecc_failed++; - } - } + return ret; +} - readoob: - /* check, if we have a fs supplied oob-buffer */ - if (oob_buf) { - /* without autoplace. Legacy mode used by YAFFS1 */ - switch(oobsel->useecc) { - case MTD_NANDECC_AUTOPLACE: - case MTD_NANDECC_AUTOPL_USR: - /* Walk through the autoplace chunks */ - for (i = 0, j = 0; j < mtd->oobavail; i++) { - int from = oobsel->oobfree[i][0]; - int num = oobsel->oobfree[i][1]; - memcpy(&oob_buf[oob+j], &oob_data[from], num); - j+= num; - } - oob += mtd->oobavail; - break; - case MTD_NANDECC_PLACE: - /* YAFFS1 legacy mode */ - oob_data += this->eccsteps * sizeof (int); - default: - oob_data += mtd->oobsize; - } - } - readdata: - /* Partial page read, transfer data into fs buffer */ - if (!aligned) { - for (j = col; j < end && read < len; j++) - buf[read++] = data_poi[j]; - this->pagebuf = realpage; +/** + * nand_read_oob_std - [REPLACABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/** + * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = buf; + int i, toread, sndrnd = 0, pos; + + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + else + chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); } else - read += mtd->oobblock; - - /* Apply delay or wait for ready/busy pin - * Do this before the AUTOINCR check, so no problems - * arise if a chip which does auto increment - * is marked as NOAUTOINCR by the board driver. - */ - if (!this->dev_ready) - udelay (this->chip_delay); - else - while (!this->dev_ready(mtd)); + sndrnd = 1; + toread = min_t(int, length, chunk); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); - if (read == len) - break; + return 1; +} - /* For subsequent reads align to page boundary. */ - col = 0; - /* Increment page address */ - realpage++; +/** + * nand_write_oob_std - [REPLACABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; - page = realpage & this->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - this->select_chip(mtd, -1); - this->select_chip(mtd, chipnr); - } - /* Check, if the chip supports auto page increment - * or if we have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) - sndcmd = 1; - } + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash ! + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; /* - * Return success, if no ECC failures, else -EBADMSG - * fs driver will take care of that, because - * retlen == desired len and result == -EBADMSG + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob */ - *retlen = read; - return ecc_failed ? -EBADMSG : 0; + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + chip->write_buf(mtd, (uint8_t *)&fill, + num); + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; } /** - * nand_read_oob - [MTD Interface] NAND read out-of-band + * nand_do_read_oob - [Intern] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data + * @ops: oob operations description structure * * NAND read out-of-band data from the spare area */ -static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { - int i, col, page, chipnr; - struct nand_chip *this = mtd->priv; - int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; - - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", - (unsigned int) from, (int) len); - - /* Shift to get page */ - page = (int)(from >> this->page_shift); - chipnr = (int)(from >> this->chip_shift); - - /* Mask to get column */ - col = from & (mtd->oobsize - 1); + int page, realpage, chipnr, sndcmd = 1; + struct nand_chip *chip = mtd->priv; + int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + int readlen = ops->ooblen; + int len; + uint8_t *buf = ops->oobbuf; + + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", + (unsigned long long)from, readlen); + + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; - /* Initialize return length value */ - *retlen = 0; + if (unlikely(ops->ooboffs >= len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start read outside oob\n"); + return -EINVAL; + } /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_read_oob: Attempt read beyond end of device\n"); - *retlen = 0; + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd , FL_READING); + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Select the NAND device */ - this->select_chip(mtd, chipnr); + /* Shift to get page */ + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; - /* Send the read command */ - this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); - /* - * Read the data, if we read more than one page - * oob data, let the device transfer the data ! - */ - i = 0; - while (i < len) { - int thislen = mtd->oobsize - col; - thislen = min_t(int, thislen, len); - this->read_buf(mtd, &buf[i], thislen); - i += thislen; - - /* Apply delay or wait for ready/busy pin - * Do this before the AUTOINCR check, so no problems - * arise if a chip which does auto increment - * is marked as NOAUTOINCR by the board driver. - */ - if (!this->dev_ready) - udelay (this->chip_delay); - else - while (!this->dev_ready(mtd)); - - /* Read more ? */ - if (i < len) { - page++; - col = 0; - - /* Check, if we cross a chip boundary */ - if (!(page & this->pagemask)) { - chipnr++; - this->select_chip(mtd, -1); - this->select_chip(mtd, chipnr); - } + while(1) { + sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); - /* Check, if the chip supports auto page increment - * or if we have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { - /* For subsequent page reads set offset to 0 */ - this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); - } + len = min(len, readlen); + buf = nand_transfer_oob(chip, buf, ops, len); + + if (!(chip->options & NAND_NO_READRDY)) { + /* + * Apply delay or wait for ready/busy pin. Do this + * before the AUTOINCR check, so no problems arise if a + * chip which does auto increment is marked as + * NOAUTOINCR by the board driver. + */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); } - } - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); + readlen -= len; + if (!readlen) + break; + + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) + sndcmd = 1; + } - /* Return happy */ - *retlen = len; + ops->oobretlen = ops->ooblen; return 0; } /** - * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band * @mtd: MTD device structure - * @buf: temporary buffer * @from: offset to read from - * @len: number of bytes to read - * @ooblen: number of oob data bytes to read + * @ops: oob operation description structure * - * Read raw data including oob into buffer + * NAND read data and/or out-of-band data */ -int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen) +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { - struct nand_chip *this = mtd->priv; - int page = (int) (from >> this->page_shift); - int chip = (int) (from >> this->chip_shift); - int sndcmd = 1; - int cnt = 0; - int pagesize = mtd->oobblock + mtd->oobsize; - int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_read_raw: Attempt read beyond end of device\n"); + if (ops->datbuf && (from + ops->len) > mtd->size) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd , FL_READING); + nand_get_device(chip, mtd, FL_READING); - this->select_chip (mtd, chip); + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; - /* Add requested oob length */ - len += ooblen; + default: + goto out; + } - while (len) { - if (sndcmd) - this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); - sndcmd = 0; + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); - this->read_buf (mtd, &buf[cnt], pagesize); + out: + nand_release_device(mtd); + return ret; +} - len -= pagesize; - cnt += pagesize; - page++; - if (!this->dev_ready) - udelay (this->chip_delay); - else - while (!this->dev_ready(mtd)); +/** + * nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} - /* Check, if the chip supports auto page increment */ - if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) - sndcmd = 1; - } +/** + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - return 0; + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->ecc.write_page_raw(mtd, chip, buf); } +/** + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} /** - * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer - * @mtd: MTD device structure - * @fsbuf: buffer given by fs driver - * @oobsel: out of band selection structre - * @autoplace: 1 = place given buffer into the oob bytes - * @numpages: number of pages to prepare - * - * Return: - * 1. Filesystem buffer available and autoplacement is off, - * return filesystem buffer - * 2. No filesystem buffer or autoplace is off, return internal - * buffer - * 3. Filesystem buffer is given and autoplace selected - * put data from fs buffer into internal buffer and - * retrun internal buffer - * - * Note: The internal buffer is filled with 0xff. This must - * be done only once, when no autoplacement happens - * Autoplacement sets the buffer dirty flag, which - * forces the 0xff fill before using the buffer again. + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer * -*/ -static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, - int autoplace, int numpages) + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static void nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) { - struct nand_chip *this = mtd->priv; - int i, len, ofs; + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; - /* Zero copy fs supplied buffer */ - if (fsbuf && !autoplace) - return fsbuf; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - /* Check, if the buffer must be filled with ff again */ - if (this->oobdirty) { - memset (this->oob_buf, 0xff, - mtd->oobsize << (this->phys_erase_shift - this->page_shift)); - this->oobdirty = 0; - } + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); - /* If we have no autoplacement or no fs buffer use the internal one */ - if (!autoplace || !fsbuf) - return this->oob_buf; - - /* Walk through the pages and place the data */ - this->oobdirty = 1; - ofs = 0; - while (numpages--) { - for (i = 0, len = 0; len < mtd->oobavail; i++) { - int to = ofs + oobsel->oobfree[i][0]; - int num = oobsel->oobfree[i][1]; - memcpy (&this->oob_buf[to], fsbuf, num); - len += num; - fsbuf += num; + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; } - ofs += mtd->oobavail; } - return this->oob_buf; -} -#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); +} /** - * nand_write - [MTD Interface] compability function for nand_write_ecc + * nand_write_page - [REPLACEABLE] write one page * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes + * @chip: NAND chip descriptor * @buf: the data to write - * - * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL - * -*/ -static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) { - return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; } +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 + /** - * nand_write_ecc - [MTD Interface] NAND write with ECC + * nand_do_write_ops - [Internal] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * @eccbuf: filesystem supplied oob data buffer - * @oobsel: oob selection structure + * @ops: oob operations description structure * * NAND write with ECC */ -static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, - size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { - int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; - int autoplace = 0, numpages, totalpages; - struct nand_chip *this = mtd->priv; - u_char *oobbuf, *bufstart; - int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); - - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", - (unsigned int) to, (int) len); - - /* Initialize retlen, in case of early exit */ - *retlen = 0; + int chipnr, realpage, page, blockmask, column; + struct nand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret, subpage; - /* Do not allow write past end of device */ - if ((to + len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: Attempt to write past end of page\n"); - return -EINVAL; - } + ops->retlen = 0; + if (!writelen) + return 0; /* reject writes, which are not page aligned */ - if (NOTALIGNED (to) || NOTALIGNED(len)) { - printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd, FL_WRITING); + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); - /* Calculate chipnr */ - chipnr = (int)(to >> this->chip_shift); - /* Select the NAND device */ - this->select_chip(mtd, chipnr); + if (subpage && oob) + return -EINVAL; + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - printk (KERN_NOTICE "nand_write_ecc: Device is write protected\n"); - goto out; + printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n"); + return -EIO; } - /* if oobsel is NULL, use chip defaults */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); + + while(1) { + int bytes = mtd->writesize; + int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write ? */ + if (unlikely(column || writelen < (mtd->writesize - 1))) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } - /* Autoplace of oob data ? Use the default placement scheme */ - if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { - oobsel = this->autooob; - autoplace = 1; - } - if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) - autoplace = 1; + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); - /* Setup variables and oob buffer */ - totalpages = len >> this->page_shift; - page = (int) (to >> this->page_shift); - /* Invalidate the page cache, if we write to the cached page */ - if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) - this->pagebuf = -1; - - /* Set it relative to chip */ - page &= this->pagemask; - startpage = page; - /* Calc number of pages we can write in one go */ - numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages); - oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); - bufstart = (u_char *)buf; - - /* Loop until all data is written */ - while (written < len) { - - this->data_poi = (u_char*) &buf[written]; - /* Write one page. If this is the last page to write - * or the last page in this block, then use the - * real pageprogram command, else select cached programming - * if supported by the chip. - */ - ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); - if (ret) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: write_page failed %d\n", ret); - goto out; - } - /* Next oob page */ - oob += mtd->oobsize; - /* Update written bytes count */ - written += mtd->oobblock; - if (written == len) - goto cmp; + ret = chip->write_page(mtd, chip, wbuf, page, cached, + (ops->mode == MTD_OOB_RAW)); + if (ret) + break; - /* Increment page address */ - page++; - - /* Have we hit a block boundary ? Then we have to verify and - * if verify is ok, we have to setup the oob buffer for - * the next pages. - */ - if (!(page & (ppblock - 1))){ - int ofs; - this->data_poi = bufstart; - ret = nand_verify_pages (mtd, this, startpage, - page - startpage, - oobbuf, oobsel, chipnr, (eccbuf != NULL)); - if (ret) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " - "verify_pages failed %d\n", ret); - goto out; - } - *retlen = written; - bufstart = (u_char*) &buf[written]; - - ofs = autoplace ? mtd->oobavail : mtd->oobsize; - if (eccbuf) - eccbuf += (page - startpage) * ofs; - totalpages -= page - startpage; - numpages = min (totalpages, ppblock); - page &= this->pagemask; - startpage = page; - oob = 0; - this->oobdirty = 1; - oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, - autoplace, numpages); - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - this->select_chip(mtd, -1); - this->select_chip(mtd, chipnr); - } + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); } } - /* Verify the remaining pages */ -cmp: - this->data_poi = bufstart; - ret = nand_verify_pages (mtd, this, startpage, totalpages, - oobbuf, oobsel, chipnr, (eccbuf != NULL)); - if (!ret) - *retlen = written; - else - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: verify_pages failed %d\n", ret); - -out: - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; return ret; } - /** - * nand_write_oob - [MTD Interface] NAND write out-of-band + * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to * @len: number of bytes to write * @retlen: pointer to variable to store the number of written bytes * @buf: the data to write * - * NAND write out-of-band + * NAND write with ECC */ -static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) { - int column, page, status, ret = -EIO, chipnr; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; + int ret; - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int) to, (int) len); + /* Do not allow reads past end of device */ + if ((to + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; - /* Shift to get page */ - page = (int) (to >> this->page_shift); - chipnr = (int) (to >> this->chip_shift); + nand_get_device(chip, mtd, FL_WRITING); + + chip->ops.len = len; + chip->ops.datbuf = (uint8_t *)buf; + chip->ops.oobbuf = NULL; - /* Mask to get column */ - column = to & (mtd->oobsize - 1); + ret = nand_do_write_ops(mtd, to, &chip->ops); - /* Initialize return length value */ - *retlen = 0; + *retlen = chip->ops.retlen; + + nand_release_device(mtd); + + return ret; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band + */ +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, page, status, len; + struct nand_chip *chip = mtd->priv; + + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", + (unsigned int)to, (int)ops->ooblen); + + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; /* Do not allow write past end of page */ - if ((column + len) > mtd->oobsize) { + if ((ops->ooboffs + ops->ooblen) > len) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd, FL_WRITING); + if (unlikely(ops->ooboffs >= len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start write outside oob\n"); + return -EINVAL; + } - /* Select the NAND device */ - this->select_chip(mtd, chipnr); + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt write beyond end of device\n"); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Reset the chip. Some chips (like the Toshiba TC5832DC found - in one of my DiskOnChip 2000 test units) will clear the whole - data page too if we don't do this. I have no clue why, but - I seem to have 'fixed' it in the doc2000 driver in - August 1999. dwmw2. */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Check, if it is write protected */ if (nand_check_wp(mtd)) - goto out; + return -EROFS; /* Invalidate the page cache, if we write to the cached page */ - if (page == this->pagebuf) - this->pagebuf = -1; - - if (NAND_MUST_PAD(this)) { - /* Write out desired data */ - this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); - if (!ffchars) { - if (!(ffchars = kmalloc (mtd->oobsize, GFP_KERNEL))) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " - "No memory for padding array, " - "need %d bytes", mtd->oobsize); - ret = -ENOMEM; - goto out; - } - memset(ffchars, 0xff, mtd->oobsize); - } - /* prepad 0xff for partial programming */ - this->write_buf(mtd, ffchars, column); - /* write data */ - this->write_buf(mtd, buf, len); - /* postpad 0xff for partial programming */ - this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); - } else { - /* Write out desired data */ - this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); - /* write data */ - this->write_buf(mtd, buf, len); - } - /* Send command to program the OOB data */ - this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = this->waitfunc (mtd, this, FL_WRITING); - - /* See if device thinks it succeeded */ - if (status & 0x01) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Failed write, page 0x%08x\n", page); - ret = -EIO; - goto out; - } - /* Return happy */ - *retlen = len; + if (page == chip->pagebuf) + chip->pagebuf = -1; -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); + memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(chip, ops->oobbuf, ops); + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + memset(chip->oob_poi, 0xff, mtd->oobsize); - if (this->verify_buf(mtd, buf, len)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Failed write verify, page 0x%08x\n", page); - ret = -EIO; - goto out; - } -#endif - ret = 0; -out: - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); + if (status) + return status; - return ret; -} + ops->oobretlen = ops->ooblen; -/* XXX U-BOOT XXX */ -#if 0 -/** - * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc - * @mtd: MTD device structure - * @vecs: the iovectors to write - * @count: number of vectors - * @to: offset to write to - * @retlen: pointer to variable to store the number of written bytes - * - * NAND write with kvec. This just calls the ecc function - */ -static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, - loff_t to, size_t * retlen) -{ - return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); + return 0; } /** - * nand_writev_ecc - [MTD Interface] write with iovec with ecc + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @mtd: MTD device structure - * @vecs: the iovectors to write - * @count: number of vectors * @to: offset to write to - * @retlen: pointer to variable to store the number of written bytes - * @eccbuf: filesystem supplied oob data buffer - * @oobsel: oob selection structure - * - * NAND write with iovec with ecc + * @ops: oob operation description structure */ -static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, - loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { - int i, page, len, total_len, ret = -EIO, written = 0, chipnr; - int oob, numpages, autoplace = 0, startpage; - struct nand_chip *this = mtd->priv; - int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); - u_char *oobbuf, *bufstart; - - /* Preset written len for early exit */ - *retlen = 0; + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; - /* Calculate total length of data */ - total_len = 0; - for (i = 0; i < count; i++) - total_len += (int) vecs[i].iov_len; - - MTDDEBUG (MTD_DEBUG_LEVEL3, - "nand_writev: to = 0x%08x, len = %i, count = %ld\n", - (unsigned int) to, (unsigned int) total_len, count); - - /* Do not allow write past end of page */ - if ((to + total_len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_writev: Attempted write past end of device\n"); - return -EINVAL; - } + ops->retlen = 0; - /* reject writes, which are not page aligned */ - if (NOTALIGNED (to) || NOTALIGNED(total_len)) { - printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd, FL_WRITING); + nand_get_device(chip, mtd, FL_WRITING); - /* Get the current chip-nr */ - chipnr = (int) (to >> this->chip_shift); - /* Select the NAND device */ - this->select_chip(mtd, chipnr); + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) + default: goto out; - - /* if oobsel is NULL, use chip defaults */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - - /* Autoplace of oob data ? Use the default placement scheme */ - if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { - oobsel = this->autooob; - autoplace = 1; } - if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) - autoplace = 1; - - /* Setup start page */ - page = (int) (to >> this->page_shift); - /* Invalidate the page cache, if we write to the cached page */ - if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) - this->pagebuf = -1; - - startpage = page & this->pagemask; - - /* Loop until all kvec' data has been written */ - len = 0; - while (count) { - /* If the given tuple is >= pagesize then - * write it out from the iov - */ - if ((vecs->iov_len - len) >= mtd->oobblock) { - /* Calc number of pages we can write - * out of this iov in one go */ - numpages = (vecs->iov_len - len) >> this->page_shift; - /* Do not cross block boundaries */ - numpages = min (ppblock - (startpage & (ppblock - 1)), numpages); - oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); - bufstart = (u_char *)vecs->iov_base; - bufstart += len; - this->data_poi = bufstart; - oob = 0; - for (i = 1; i <= numpages; i++) { - /* Write one page. If this is the last page to write - * then use the real pageprogram command, else select - * cached programming if supported by the chip. - */ - ret = nand_write_page (mtd, this, page & this->pagemask, - &oobbuf[oob], oobsel, i != numpages); - if (ret) - goto out; - this->data_poi += mtd->oobblock; - len += mtd->oobblock; - oob += mtd->oobsize; - page++; - } - /* Check, if we have to switch to the next tuple */ - if (len >= (int) vecs->iov_len) { - vecs++; - len = 0; - count--; - } - } else { - /* We must use the internal buffer, read data out of each - * tuple until we have a full page to write - */ - int cnt = 0; - while (cnt < mtd->oobblock) { - if (vecs->iov_base != NULL && vecs->iov_len) - this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; - /* Check, if we have to switch to the next tuple */ - if (len >= (int) vecs->iov_len) { - vecs++; - len = 0; - count--; - } - } - this->pagebuf = page; - this->data_poi = this->data_buf; - bufstart = this->data_poi; - numpages = 1; - oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); - ret = nand_write_page (mtd, this, page & this->pagemask, - oobbuf, oobsel, 0); - if (ret) - goto out; - page++; - } - this->data_poi = bufstart; - ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); - if (ret) - goto out; - - written += mtd->oobblock * numpages; - /* All done ? */ - if (!count) - break; + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); - startpage = page & this->pagemask; - /* Check, if we cross a chip boundary */ - if (!startpage) { - chipnr++; - this->select_chip(mtd, -1); - this->select_chip(mtd, chipnr); - } - } - ret = 0; -out: - /* Deselect and wake up anyone waiting on the device */ + out: nand_release_device(mtd); - - *retlen = written; return ret; } -#endif /** * single_erease_cmd - [GENERIC] NAND standard block erase command function @@ -2094,12 +1990,12 @@ out: * * Standard erase command for NAND chips */ -static void single_erase_cmd (struct mtd_info *mtd, int page) +static void single_erase_cmd(struct mtd_info *mtd, int page) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ - this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); - this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); } /** @@ -2110,15 +2006,15 @@ static void single_erase_cmd (struct mtd_info *mtd, int page) * AND multi block erase command function * Erase 4 consecutive blocks */ -static void multi_erase_cmd (struct mtd_info *mtd, int page) +static void multi_erase_cmd(struct mtd_info *mtd, int page) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ - this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); - this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); - this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); - this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); - this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); } /** @@ -2128,35 +2024,39 @@ static void multi_erase_cmd (struct mtd_info *mtd, int page) * * Erase one ore more blocks */ -static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) +static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) { - return nand_erase_nand (mtd, instr, 0); + return nand_erase_nand(mtd, instr, 0); } +#define BBT_PAGE_MASK 0xffffff3f /** - * nand_erase_intern - [NAND Interface] erase block(s) + * nand_erase_nand - [Internal] erase block(s) * @mtd: MTD device structure * @instr: erase instruction * @allowbbt: allow erasing the bbt area * * Erase one ore more blocks */ -int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt) { int page, len, status, pages_per_block, ret, chipnr; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; + unsigned int bbt_masked_page = 0xffffffff; MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); /* Start address must align on block boundary */ - if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { + if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ - if (instr->len & ((1 << this->phys_erase_shift) - 1)) { + if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); return -EINVAL; @@ -2172,19 +2072,18 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb instr->fail_addr = 0xffffffff; /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd, FL_ERASING); + nand_get_device(chip, mtd, FL_ERASING); /* Shift to get first page */ - page = (int) (instr->addr >> this->page_shift); - chipnr = (int) (instr->addr >> this->chip_shift); + page = (int)(instr->addr >> chip->page_shift); + chipnr = (int)(instr->addr >> chip->chip_shift); /* Calculate pages in each block */ - pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); - + pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + /* Select the NAND device */ - this->select_chip(mtd, chipnr); + chip->select_chip(mtd, chipnr); - /* Check the WP bit */ /* Check, if it is write protected */ if (nand_check_wp(mtd)) { MTDDEBUG (MTD_DEBUG_LEVEL0, @@ -2193,52 +2092,92 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb goto erase_exit; } + /* + * If BBT requires refresh, set the BBT page mask to see if the BBT + * should be rewritten. Otherwise the mask is set to 0xffffffff which + * can not be matched. This is also done when the bbt is actually + * erased to avoid recusrsive updates + */ + if (chip->options & BBT_AUTO_REFRESH && !allowbbt) + bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { -#ifndef NAND_ALLOW_ERASE_ALL - /* Check if we have a bad block, we do not erase bad blocks ! */ - if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { - printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); + /* + * heck if we have a bad block, we do not erase bad blocks ! + */ + if (nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, 0, allowbbt)) { + printk(KERN_WARNING "nand_erase: attempt to erase a " + "bad block at page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } -#endif - /* Invalidate the page cache, if we erase the block which contains - the current cached page */ - if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) - this->pagebuf = -1; - this->erase_cmd (mtd, page & this->pagemask); + /* + * Invalidate the page cache, if we erase the block which + * contains the current cached page + */ + if (page <= chip->pagebuf && chip->pagebuf < + (page + pages_per_block)) + chip->pagebuf = -1; + + chip->erase_cmd(mtd, page & chip->pagemask); + + status = chip->waitfunc(mtd, chip); - status = this->waitfunc (mtd, this, FL_ERASING); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_ERASING, + status, page); /* See if block erase succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; - instr->fail_addr = (page << this->page_shift); + instr->fail_addr = (page << chip->page_shift); goto erase_exit; } + /* + * If BBT requires refresh, set the BBT rewrite flag to the + * page being erased + */ + if (bbt_masked_page != 0xffffffff && + (page & BBT_PAGE_MASK) == bbt_masked_page) + rewrite_bbt[chipnr] = (page << chip->page_shift); + /* Increment page address and decrement length */ - len -= (1 << this->phys_erase_shift); + len -= (1 << chip->phys_erase_shift); page += pages_per_block; /* Check, if we cross a chip boundary */ - if (len && !(page & this->pagemask)) { + if (len && !(page & chip->pagemask)) { chipnr++; - this->select_chip(mtd, -1); - this->select_chip(mtd, chipnr); + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + + /* + * If BBT requires refresh and BBT-PERCHIP, set the BBT + * page mask to see if this BBT should be rewritten + */ + if (bbt_masked_page != 0xffffffff && + (chip->bbt_td->options & NAND_BBT_PERCHIP)) + bbt_masked_page = chip->bbt_td->pages[chipnr] & + BBT_PAGE_MASK; } } instr->state = MTD_ERASE_DONE; -erase_exit: + erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; /* Do call back function */ @@ -2248,6 +2187,23 @@ erase_exit: /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); + /* + * If BBT requires refresh and erase was successful, rewrite any + * selected bad block tables + */ + if (bbt_masked_page == 0xffffffff || ret) + return ret; + + for (chipnr = 0; chipnr < chip->numchips; chipnr++) { + if (!rewrite_bbt[chipnr]) + continue; + /* update the BBT for chip */ + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " + "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + chip->bbt_td->pages[chipnr]); + nand_update_bbt(mtd, rewrite_bbt[chipnr]); + } + /* Return more or less happy */ return ret; } @@ -2258,41 +2214,40 @@ erase_exit: * * Sync is actually a wait for chip ready function */ -static void nand_sync (struct mtd_info *mtd) +static void nand_sync(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd, FL_SYNCING); + nand_get_device(chip, mtd, FL_SYNCING); /* Release it and go back */ - nand_release_device (mtd); + nand_release_device(mtd); } - /** - * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * nand_block_isbad - [MTD Interface] Check if block at offset is bad * @mtd: MTD device structure - * @ofs: offset relative to mtd start + * @offs: offset relative to mtd start */ -static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) +static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) { /* Check for invalid offset */ - if (ofs > mtd->size) + if (offs > mtd->size) return -EINVAL; - return nand_block_checkbad (mtd, ofs, 1, 0); + return nand_block_checkbad(mtd, offs, 1, 0); } /** - * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad * @mtd: MTD device structure * @ofs: offset relative to mtd start */ -static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; int ret; if ((ret = nand_block_isbad(mtd, ofs))) { @@ -2302,419 +2257,553 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) return ret; } - return this->block_markbad(mtd, ofs); + return chip->block_markbad(mtd, ofs); } /** - * nand_scan - [NAND Interface] Scan for the NAND device + * nand_suspend - [MTD Interface] Suspend the NAND flash * @mtd: MTD device structure - * @maxchips: Number of chips to scan for - * - * This fills out all the not initialized function pointers - * with the defaults. - * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. Buffers are allocated if - * they are not provided by the board driver - * */ -int nand_scan (struct mtd_info *mtd, int maxchips) +static int nand_suspend(struct mtd_info *mtd) { - int i, j, nand_maf_id, nand_dev_id, busw; - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; + + return nand_get_device(chip, mtd, FL_PM_SUSPENDED); +} + +/** + * nand_resume - [MTD Interface] Resume the NAND flash + * @mtd: MTD device structure + */ +static void nand_resume(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; - /* Get buswidth to select the correct functions*/ - busw = this->options & NAND_BUSWIDTH_16; + if (chip->state == FL_PM_SUSPENDED) + nand_release_device(mtd); + else + printk(KERN_ERR "nand_resume() called for a chip which is not " + "in suspended state\n"); +} +/* + * Set default functions + */ +static void nand_set_defaults(struct nand_chip *chip, int busw) +{ /* check for proper chip_delay setup, set 20us if not */ - if (!this->chip_delay) - this->chip_delay = 20; + if (!chip->chip_delay) + chip->chip_delay = 20; /* check, if a user supplied command function given */ - if (this->cmdfunc == NULL) - this->cmdfunc = nand_command; + if (chip->cmdfunc == NULL) + chip->cmdfunc = nand_command; /* check, if a user supplied wait function given */ - if (this->waitfunc == NULL) - this->waitfunc = nand_wait; - - if (!this->select_chip) - this->select_chip = nand_select_chip; - if (!this->write_byte) - this->write_byte = busw ? nand_write_byte16 : nand_write_byte; - if (!this->read_byte) - this->read_byte = busw ? nand_read_byte16 : nand_read_byte; - if (!this->write_word) - this->write_word = nand_write_word; - if (!this->read_word) - this->read_word = nand_read_word; - if (!this->block_bad) - this->block_bad = nand_block_bad; - if (!this->block_markbad) - this->block_markbad = nand_default_block_markbad; - if (!this->write_buf) - this->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!this->read_buf) - this->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!this->verify_buf) - this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; - if (!this->scan_bbt) - this->scan_bbt = nand_default_bbt; + if (chip->waitfunc == NULL) + chip->waitfunc = nand_wait; + + if (!chip->select_chip) + chip->select_chip = nand_select_chip; + if (!chip->read_byte) + chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!chip->read_word) + chip->read_word = nand_read_word; + if (!chip->block_bad) + chip->block_bad = nand_block_bad; + if (!chip->block_markbad) + chip->block_markbad = nand_default_block_markbad; + if (!chip->write_buf) + chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!chip->read_buf) + chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!chip->verify_buf) + chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; + if (!chip->scan_bbt) + chip->scan_bbt = nand_default_bbt; + + if (!chip->controller) { + chip->controller = &chip->hwcontrol; + + /* XXX U-BOOT XXX */ +#if 0 + spin_lock_init(&chip->controller->lock); + init_waitqueue_head(&chip->controller->wq); +#endif + } + +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported + */ +static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + struct nand_chip *chip, + int busw, int *maf_id) +{ + struct nand_flash_dev *type = NULL; + int i, dev_id, maf_idx; /* Select the device */ - this->select_chip(mtd, 0); + chip->select_chip(mtd, 0); /* Send the command for reading device ID */ - this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ - nand_maf_id = this->read_byte(mtd); - nand_dev_id = this->read_byte(mtd); + *maf_id = chip->read_byte(mtd); + dev_id = chip->read_byte(mtd); - /* Print and store flash device information */ + /* Lookup the flash id */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (dev_id == nand_flash_ids[i].id) { + type = &nand_flash_ids[i]; + break; + } + } - if (nand_dev_id != nand_flash_ids[i].id) - continue; + if (!type) + return ERR_PTR(-ENODEV); + + if (!mtd->name) + mtd->name = type->name; + + chip->chipsize = type->chipsize << 20; + + /* Newer devices have all the information in additional id bytes */ + if (!type->pagesize) { + int extid; + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = chip->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = chip->read_byte(mtd); + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; - if (!mtd->name) mtd->name = nand_flash_ids[i].name; - this->chipsize = nand_flash_ids[i].chipsize << 20; - - /* New devices have all the information in additional id bytes */ - if (!nand_flash_ids[i].pagesize) { - int extid; - /* The 3rd id byte contains non relevant data ATM */ - extid = this->read_byte(mtd); - /* The 4th id byte is the important one */ - extid = this->read_byte(mtd); - /* Calc pagesize */ - mtd->oobblock = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } else { + /* + * Old devices have chip data hardcoded in the device id table + */ + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + busw = type->options & NAND_BUSWIDTH_16; + } - } else { - /* Old devices have this data hardcoded in the - * device id table */ - mtd->erasesize = nand_flash_ids[i].erasesize; - mtd->oobblock = nand_flash_ids[i].pagesize; - mtd->oobsize = mtd->oobblock / 32; - busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; - } + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { + if (nand_manuf_ids[maf_idx].id == *maf_id) + break; + } - /* Check, if buswidth is correct. Hardware drivers should set - * this correct ! */ - if (busw != (this->options & NAND_BUSWIDTH_16)) { - printk (KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, - nand_manuf_ids[i].name , mtd->name); - printk (KERN_WARNING - "NAND bus width %d instead %d bit\n", - (this->options & NAND_BUSWIDTH_16) ? 16 : 8, - busw ? 16 : 8); - this->select_chip(mtd, -1); - return 1; - } + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct ! + */ + if (busw != (chip->options & NAND_BUSWIDTH_16)) { + printk(KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, + dev_id, nand_manuf_ids[maf_idx].name, mtd->name); + printk(KERN_WARNING "NAND bus width %d instead %d bit\n", + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + return ERR_PTR(-EINVAL); + } - /* Calculate the address shift from the page size */ - this->page_shift = ffs(mtd->oobblock) - 1; - this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; - this->chip_shift = ffs(this->chipsize) - 1; - - /* Set the bad block position */ - this->badblockpos = mtd->oobblock > 512 ? - NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; - - /* Get chip options, preserve non chip based options */ - this->options &= ~NAND_CHIPOPTIONS_MSK; - this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; - /* Set this as a default. Board drivers can override it, if neccecary */ - this->options |= NAND_NO_AUTOINCR; - /* Check if this is a not a samsung device. Do not clear the options - * for chips which are not having an extended id. - */ - if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) - this->options &= ~NAND_SAMSUNG_LP_OPTIONS; + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + /* Convert chipsize to number of pages per chip -1. */ + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; - /* Check for AND chips with 4 page planes */ - if (this->options & NAND_4PAGE_ARRAY) - this->erase_cmd = multi_erase_cmd; - else - this->erase_cmd = single_erase_cmd; + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; + chip->chip_shift = ffs(chip->chipsize) - 1; - /* Do not replace user supplied command function ! */ - if (mtd->oobblock > 512 && this->cmdfunc == nand_command) - this->cmdfunc = nand_command_lp; + /* Set the bad block position */ + chip->badblockpos = mtd->writesize > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; - /* Try to identify manufacturer */ - for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { - if (nand_manuf_ids[j].id == nand_maf_id) - break; - } - break; - } + /* Get chip options, preserve non chip based options */ + chip->options &= ~NAND_CHIPOPTIONS_MSK; + chip->options |= type->options & NAND_CHIPOPTIONS_MSK; - if (!nand_flash_ids[i].name) { -#ifndef CFG_NAND_QUIET_TEST - printk (KERN_WARNING "No NAND device found!!!\n"); -#endif - this->select_chip(mtd, -1); - return 1; - } + /* + * Set chip as a default. Board drivers can override it, if necessary + */ + chip->options |= NAND_NO_AUTOINCR; - for (i=1; i < maxchips; i++) { - this->select_chip(mtd, i); + /* Check if chip is a not a samsung device. Do not clear the + * options for chips which are not having an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; - /* Send the command for reading device ID */ - this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + /* Check for AND chips with 4 page planes */ + if (chip->options & NAND_4PAGE_ARRAY) + chip->erase_cmd = multi_erase_cmd; + else + chip->erase_cmd = single_erase_cmd; + + /* Do not replace user supplied command function ! */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + printk(KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, + nand_manuf_ids[maf_idx].name, type->name); + + return type; +} + +/** + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This is the first phase of the normal nand_scan() function. It + * reads the flash ID and sets up MTD fields accordingly. + * + * The mtd->owner field must be set to the module of the caller. + */ +int nand_scan_ident(struct mtd_info *mtd, int maxchips) +{ + int i, busw, nand_maf_id; + struct nand_chip *chip = mtd->priv; + struct nand_flash_dev *type; + + /* Get buswidth to select the correct functions */ + busw = chip->options & NAND_BUSWIDTH_16; + /* Set the default functions */ + nand_set_defaults(chip, busw); + + /* Read the flash type */ + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + + if (IS_ERR(type)) { + printk(KERN_WARNING "No NAND device found!!!\n"); + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + /* Check for a chip array */ + for (i = 1; i < maxchips; i++) { + chip->select_chip(mtd, i); + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ - if (nand_maf_id != this->read_byte(mtd) || - nand_dev_id != this->read_byte(mtd)) + if (nand_maf_id != chip->read_byte(mtd) || + type->id != chip->read_byte(mtd)) break; } if (i > 1) printk(KERN_INFO "%d NAND chips detected\n", i); - /* Allocate buffers, if neccecary */ - if (!this->oob_buf) { - size_t len; - len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); - this->oob_buf = kmalloc (len, GFP_KERNEL); - if (!this->oob_buf) { - printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); - return -ENOMEM; - } - this->options |= NAND_OOBBUF_ALLOC; - } + /* Store the number of chips and calc total size for mtd */ + chip->numchips = i; + mtd->size = i * chip->chipsize; - if (!this->data_buf) { - size_t len; - len = mtd->oobblock + mtd->oobsize; - this->data_buf = kmalloc (len, GFP_KERNEL); - if (!this->data_buf) { - if (this->options & NAND_OOBBUF_ALLOC) - kfree (this->oob_buf); - printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); - return -ENOMEM; - } - this->options |= NAND_DATABUF_ALLOC; - } + return 0; +} - /* Store the number of chips and calc total size for mtd */ - this->numchips = i; - mtd->size = i * this->chipsize; - /* Convert chipsize to number of pages per chip -1. */ - this->pagemask = (this->chipsize >> this->page_shift) - 1; - /* Preset the internal oob buffer */ - memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); - - /* If no default placement scheme is given, select an - * appropriate one */ - if (!this->autooob) { - /* Select the appropriate default oob placement scheme for - * placement agnostic filesystems */ + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This is the second phase of the normal nand_scan() function. It + * fills out all the uninitialized function pointers with the defaults + * and scans for a bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (!(chip->options & NAND_OWN_BUFFERS)) + chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); + if (!chip->buffers) + return -ENOMEM; + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + /* + * If no default placement scheme is given, select an appropriate one + */ + if (!chip->ecc.layout) { switch (mtd->oobsize) { case 8: - this->autooob = &nand_oob_8; + chip->ecc.layout = &nand_oob_8; break; case 16: - this->autooob = &nand_oob_16; + chip->ecc.layout = &nand_oob_16; break; case 64: - this->autooob = &nand_oob_64; + chip->ecc.layout = &nand_oob_64; break; case 128: - this->autooob = &nand_oob_128; + chip->ecc.layout = &nand_oob_128; break; default: - printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", - mtd->oobsize); -/* BUG(); */ + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); +// BUG(); } } - /* The number of bytes available for the filesystem to place fs dependend - * oob data */ - mtd->oobavail = 0; - for (i=0; this->autooob->oobfree[i][1]; i++) - mtd->oobavail += this->autooob->oobfree[i][1]; + if (!chip->write_page) + chip->write_page = nand_write_page; /* - * check ECC mode, default to software - * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize - * fallback to software ECC - */ - this->eccsize = 256; /* set default eccsize */ - this->eccbytes = 3; - - switch (this->eccmode) { - case NAND_ECC_HW12_2048: - if (mtd->oobblock < 2048) { - printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", - mtd->oobblock); - this->eccmode = NAND_ECC_SOFT; - this->calculate_ecc = nand_calculate_ecc; - this->correct_data = nand_correct_data; - } else - this->eccsize = 2048; - break; - - case NAND_ECC_HW3_512: - case NAND_ECC_HW6_512: - case NAND_ECC_HW8_512: - if (mtd->oobblock == 256) { - printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); - this->eccmode = NAND_ECC_SOFT; - this->calculate_ecc = nand_calculate_ecc; - this->correct_data = nand_correct_data; - } else - this->eccsize = 512; /* set eccsize to 512 */ - break; + * check ECC mode, default to software if 3byte/512byte hardware ECC is + * selected and we have 256 byte pagesize fallback to software ECC + */ + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; + + switch (chip->ecc.mode) { + case NAND_ECC_HW: + /* Use standard hwecc read page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_std; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_std; + + case NAND_ECC_HW_SYNDROME: + if (!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) { + printk(KERN_WARNING "No ECC functions supplied, " + "Hardware ECC not possible\n"); + BUG(); + } + /* Use standard syndrome read/write page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_syndrome; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_syndrome; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= chip->ecc.size) + break; + printk(KERN_WARNING "%d byte HW ECC not possible on " + "%d byte page size, fallback to SW ECC\n", + chip->ecc.size, mtd->writesize); + chip->ecc.mode = NAND_ECC_SOFT; - case NAND_ECC_HW3_256: + case NAND_ECC_SOFT: + chip->ecc.calculate = nand_calculate_ecc; + chip->ecc.correct = nand_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = 256; + chip->ecc.bytes = 3; break; case NAND_ECC_NONE: - printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); - this->eccmode = NAND_ECC_NONE; - break; - - case NAND_ECC_SOFT: - this->calculate_ecc = nand_calculate_ecc; - this->correct_data = nand_correct_data; + printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " + "This is not recommended !!\n"); + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = mtd->writesize; + chip->ecc.bytes = 0; break; default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); -/* BUG(); */ - } - - /* Check hardware ecc function availability and adjust number of ecc bytes per - * calculation step - */ - switch (this->eccmode) { - case NAND_ECC_HW12_2048: - this->eccbytes += 4; - case NAND_ECC_HW8_512: - this->eccbytes += 2; - case NAND_ECC_HW6_512: - this->eccbytes += 3; - case NAND_ECC_HW3_512: - case NAND_ECC_HW3_256: - if (this->calculate_ecc && this->correct_data && this->enable_hwecc) - break; - printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); -/* BUG(); */ + printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", + chip->ecc.mode); + BUG(); } - mtd->eccsize = this->eccsize; + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + chip->ecc.layout->oobavail = 0; + for (i = 0; chip->ecc.layout->oobfree[i].length; i++) + chip->ecc.layout->oobavail += + chip->ecc.layout->oobfree[i].length; + mtd->oobavail = chip->ecc.layout->oobavail; - /* Set the number of read / write steps for one page to ensure ECC generation */ - switch (this->eccmode) { - case NAND_ECC_HW12_2048: - this->eccsteps = mtd->oobblock / 2048; - break; - case NAND_ECC_HW3_512: - case NAND_ECC_HW6_512: - case NAND_ECC_HW8_512: - this->eccsteps = mtd->oobblock / 512; - break; - case NAND_ECC_HW3_256: - case NAND_ECC_SOFT: - this->eccsteps = mtd->oobblock / 256; - break; + /* + * Set the number of read / write steps for one page depending on ECC + * mode + */ + chip->ecc.steps = mtd->writesize / chip->ecc.size; + if(chip->ecc.steps * chip->ecc.size != mtd->writesize) { + printk(KERN_WARNING "Invalid ecc parameters\n"); + BUG(); + } + chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; - case NAND_ECC_NONE: - this->eccsteps = 1; - break; + /* + * Allow subpage writes up to ecc.steps. Not possible for MLC + * FLASH. + */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + switch(chip->ecc.steps) { + case 2: + mtd->subpage_sft = 1; + break; + case 4: + case 8: + mtd->subpage_sft = 2; + break; + } } + chip->subpagesize = mtd->writesize >> mtd->subpage_sft; -/* XXX U-BOOT XXX */ -#if 0 - /* Initialize state, waitqueue and spinlock */ - this->state = FL_READY; - init_waitqueue_head (&this->wq); - spin_lock_init (&this->chip_lock); -#endif + /* Initialize state */ + chip->state = FL_READY; /* De-select the device */ - this->select_chip(mtd, -1); + chip->select_chip(mtd, -1); /* Invalidate the pagebuffer reference */ - this->pagebuf = -1; + chip->pagebuf = -1; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; - mtd->ecctype = MTD_ECC_SW; + mtd->flags = MTD_CAP_NANDFLASH; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; - mtd->read_ecc = nand_read_ecc; - mtd->write_ecc = nand_write_ecc; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; -/* XXX U-BOOT XXX */ -#if 0 - mtd->readv = NULL; - mtd->writev = nand_writev; - mtd->writev_ecc = nand_writev_ecc; -#endif mtd->sync = nand_sync; -/* XXX U-BOOT XXX */ -#if 0 mtd->lock = NULL; mtd->unlock = NULL; - mtd->suspend = NULL; - mtd->resume = NULL; -#endif + mtd->suspend = nand_suspend; + mtd->resume = nand_resume; mtd->block_isbad = nand_block_isbad; mtd->block_markbad = nand_block_markbad; - /* and make the autooob the default one */ - memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); -/* XXX U-BOOT XXX */ + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = chip->ecc.layout; + + /* Check, if we should skip the bad block table scan */ + if (chip->options & NAND_SKIP_BBTSCAN) + return 0; + + /* Build bad block table */ + return chip->scan_bbt(mtd); +} + +/* module_text_address() isn't exported, and it's mostly a pointless + test if this is a module _anyway_ -- they'd have to try _really_ hard + to call us from in-kernel code if the core NAND support is modular. */ +#ifdef MODULE +#define caller_is_module() (1) +#else +#define caller_is_module() \ + module_text_address((unsigned long)__builtin_return_address(0)) +#endif + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This fills out all the uninitialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + * The mtd->owner field must be set to the module of the caller + * + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ + int ret; + + /* Many callers got this wrong, so check for it for a while... */ + /* XXX U-BOOT XXX */ #if 0 - mtd->owner = THIS_MODULE; + if (!mtd->owner && caller_is_module()) { + printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); + BUG(); + } #endif - /* Build bad block table */ - return this->scan_bbt (mtd); + + ret = nand_scan_ident(mtd, maxchips); + if (!ret) + ret = nand_scan_tail(mtd); + return ret; } /** * nand_release - [NAND Interface] Free resources held by the NAND device * @mtd: MTD device structure - */ -void nand_release (struct mtd_info *mtd) +*/ +void nand_release(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; + struct nand_chip *chip = mtd->priv; #ifdef CONFIG_MTD_PARTITIONS /* Deregister partitions */ - del_mtd_partitions (mtd); + del_mtd_partitions(mtd); #endif /* Deregister the device */ -/* XXX U-BOOT XXX */ + /* XXX U-BOOT XXX */ #if 0 - del_mtd_device (mtd); + del_mtd_device(mtd); #endif - /* Free bad block table memory, if allocated */ - if (this->bbt) - kfree (this->bbt); - /* Buffer allocated by nand_scan ? */ - if (this->options & NAND_OOBBUF_ALLOC) - kfree (this->oob_buf); - /* Buffer allocated by nand_scan ? */ - if (this->options & NAND_DATABUF_ALLOC) - kfree (this->data_buf); + + /* Free bad block table memory */ + kfree(chip->bbt); + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); +} + +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL_GPL(nand_scan); +EXPORT_SYMBOL_GPL(nand_scan_ident); +EXPORT_SYMBOL_GPL(nand_scan_tail); +EXPORT_SYMBOL_GPL(nand_release); + +static int __init nand_base_init(void) +{ + led_trigger_register_simple("nand-disk", &nand_led_trigger); + return 0; +} + +static void __exit nand_base_exit(void) +{ + led_trigger_unregister_simple(nand_led_trigger); } +module_init(nand_base_init); +module_exit(nand_base_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill , Thomas Gleixner "); +MODULE_DESCRIPTION("Generic NAND flash driver code"); +#endif + #endif + diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index a97743b45e8..acf1cf54339 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,7 +6,7 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ + * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -48,7 +48,7 @@ * * Following assumptions are made: * - bbts start at a page boundary, if autolocated on a block boundary - * - the space neccecary for a bbt in FLASH does not exceed a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary * */ @@ -63,6 +63,19 @@ #include +/* XXX U-BOOT XXX */ +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + /** * check_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search @@ -76,9 +89,9 @@ * pattern area contain 0xff * */ -static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int i, end; + int i, end = 0; uint8_t *p = buf; end = paglen + td->offs; @@ -96,9 +109,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des return -1; } - p += td->len; - end += td->len; if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; for (i = end; i < len; i++) { if (*p++ != 0xff) return -1; @@ -107,6 +120,29 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des return 0; } +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check + * +*/ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[td->offs + i] != td->pattern[i]) + return -1; + } + return 0; +} + /** * read_bbt - [GENERIC] Read the bad block table starting from page * @mtd: MTD device structure @@ -120,8 +156,8 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des * Read the bad block table starting from page. * */ -static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, - int bits, int offs, int reserved_block_code) +static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, + int bits, int offs, int reserved_block_code) { int res, i, j, act = 0; struct nand_chip *this = mtd->priv; @@ -130,17 +166,17 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, uint8_t msk = (uint8_t) ((1 << bits) - 1); totlen = (num * bits) >> 3; - from = ((loff_t)page) << this->page_shift; + from = ((loff_t) page) << this->page_shift; while (totlen) { - len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); - res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); + len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); + res = mtd->read(mtd, from, len, &retlen, buf); if (res < 0) { if (retlen != len) { - printk (KERN_INFO "nand_bbt: Error reading bad block table\n"); + printk(KERN_INFO "nand_bbt: Error reading bad block table\n"); return res; } - printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); + printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); } /* Analyse data */ @@ -150,22 +186,23 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; - if (reserved_block_code && - (tmp == reserved_block_code)) { - printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + if (reserved_block_code && (tmp == reserved_block_code)) { + printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + mtd->ecc_stats.bbtblocks++; continue; } /* Leave it for now, if its matured we can move this * message to MTD_DEBUG_LEVEL0 */ - printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); /* Factory marked bad or worn out ? */ if (tmp == 0) this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); else this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + mtd->ecc_stats.badblocks++; } } totlen -= len; @@ -185,7 +222,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, * Read the bad block table for all chips starting at a given page * We assume that the bbt bits are in consecutive order. */ -static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) { struct nand_chip *this = mtd->priv; int res = 0, i; @@ -209,6 +246,42 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des return 0; } +/* + * Scan read raw data from flash + */ +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = len; + + return mtd->read_oob(mtd, offs, &ops); +} + +/* + * Scan write data with oob to flash + */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd->write_oob(mtd, offs, &ops); +} + /** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * @mtd: MTD device structure @@ -220,28 +293,84 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des * We assume that the bbt bits are in consecutive order. * */ -static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, - struct nand_bbt_descr *md) +static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) { struct nand_chip *this = mtd->priv; /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); - td->version[0] = buf[mtd->oobblock + td->veroffs]; - printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); + scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, + mtd->writesize); + td->version[0] = buf[mtd->writesize + td->veroffs]; + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); - md->version[0] = buf[mtd->oobblock + md->veroffs]; - printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); + scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, + mtd->writesize); + md->version[0] = buf[mtd->writesize + md->veroffs]; + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); } - return 1; } +/* + * Scan a given block full + */ +static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, size_t readlen, + int scanlen, int len) +{ + int ret, j; + + ret = scan_read_raw(mtd, buf, offs, readlen); + if (ret) + return ret; + + for (j = 0; j < len; j++, buf += scanlen) { + if (check_pattern(buf, scanlen, mtd->writesize, bd)) + return 1; + } + return 0; +} + +/* + * Scan a given block partially + */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int len) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + for (j = 0; j < len; j++) { + /* + * Read the full oob until read_oob is fixed to + * handle single byte reads for 16 bit + * buswidth + */ + ret = mtd->read_oob(mtd, offs, &ops); + if (ret) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + /** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure @@ -253,13 +382,16 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; - int i, j, numblocks, len, scanlen; + int i, numblocks, len, scanlen; int startblock; loff_t from; - size_t readlen, ooblen; + size_t readlen; + + printk(KERN_INFO "Scanning device for bad blocks\n"); if (bd->options & NAND_BBT_SCANALLPAGES) len = 1 << (this->bbt_erase_shift - this->page_shift); @@ -269,21 +401,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc else len = 1; } - scanlen = mtd->oobblock + mtd->oobsize; - readlen = len * mtd->oobblock; - ooblen = len * mtd->oobsize; + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->writesize + mtd->oobsize; + readlen = len * mtd->writesize; + } if (chip == -1) { - /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it - * makes shifting and masking less painful */ + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 + * below as it makes shifting and masking less painful */ numblocks = mtd->size >> (this->bbt_erase_shift - 1); startblock = 0; from = 0; } else { if (chip >= this->numchips) { - printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", - chip + 1, this->numchips); - return; + printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; @@ -292,16 +431,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } for (i = startblock; i < numblocks;) { - nand_read_raw (mtd, buf, from, readlen, ooblen); - for (j = 0; j < len; j++) { - if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - break; - } + int ret; + + if (bd->options & NAND_BBT_SCANALLPAGES) + ret = scan_block_full(mtd, bd, from, buf, readlen, + scanlen, len); + else + ret = scan_block_fast(mtd, bd, from, buf, len); + + if (ret < 0) + return ret; + + if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int)from); + mtd->ecc_stats.badblocks++; } + i += 2; from += (1 << this->bbt_erase_shift); } + return 0; } /** @@ -316,22 +467,23 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc * block. * If the option NAND_BBT_PERCHIP is given, each chip is searched * for a bbt, which contains the bad block information of this chip. - * This is neccecary to provide support for certain DOC devices. + * This is necessary to provide support for certain DOC devices. * * The bbt ident pattern resides in the oob area of the first page * in a block. */ -static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, chips; int bits, startblock, block, dir; - int scanlen = mtd->oobblock + mtd->oobsize; + int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; /* Search direction top -> down ? */ if (td->options & NAND_BBT_LASTBLOCK) { - startblock = (mtd->size >> this->bbt_erase_shift) -1; + startblock = (mtd->size >> this->bbt_erase_shift) - 1; dir = -1; } else { startblock = 0; @@ -357,13 +509,16 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr td->pages[i] = -1; /* Scan the maximum number of blocks */ for (block = 0; block < td->maxblocks; block++) { + int actblock = startblock + dir * block; + loff_t offs = actblock << this->bbt_erase_shift; + /* Read first page */ - nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); - if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { - td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); + scan_read_raw(mtd, buf, offs, mtd->writesize); + if (!check_pattern(buf, scanlen, mtd->writesize, td)) { + td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { - td->version[i] = buf[mtd->oobblock + td->veroffs]; + td->version[i] = buf[mtd->writesize + td->veroffs]; } break; } @@ -373,9 +528,10 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* Check, if we found a bbt for each requested chip */ for (i = 0; i < chips; i++) { if (td->pages[i] == -1) - printk (KERN_WARNING "Bad block table not found for chip %d\n", i); + printk(KERN_WARNING "Bad block table not found for chip %d\n", i); else - printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]); + printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], + td->version[i]); } return 0; } @@ -389,21 +545,19 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr * * Search and read the bad block table(s) */ -static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md) +static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) { /* Search the primary table */ - search_bbt (mtd, buf, td); + search_bbt(mtd, buf, td); /* Search the mirror table */ if (md) - search_bbt (mtd, buf, md); + search_bbt(mtd, buf, md); /* Force result check */ return 1; } - /** * write_bbt - [GENERIC] (Re)write the bad block table * @@ -416,25 +570,31 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, * (Re)write the bad block table * */ -static int write_bbt (struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) { struct nand_chip *this = mtd->priv; - struct nand_oobinfo oobinfo; struct erase_info einfo; int i, j, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs; + int nrchips, bbtoffs, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; if (!rcode) rcode = 0xff; /* Write bad block table per chip rather than per device ? */ if (td->options & NAND_BBT_PERCHIP) { - numblocks = (int) (this->chipsize >> this->bbt_erase_shift); + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); /* Full device write or specific chip ? */ if (chipsel == -1) { nrchips = this->numchips; @@ -443,7 +603,7 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf, chip = chipsel; } } else { - numblocks = (int) (mtd->size >> this->bbt_erase_shift); + numblocks = (int)(mtd->size >> this->bbt_erase_shift); nrchips = 1; } @@ -472,27 +632,38 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf, for (i = 0; i < td->maxblocks; i++) { int block = startblock + dir * i; /* Check, if the block is bad */ - switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { + switch ((this->bbt[block >> 2] >> + (2 * (block & 0x03))) & 0x03) { case 0x01: case 0x03: continue; } - page = block << (this->bbt_erase_shift - this->page_shift); + page = block << + (this->bbt_erase_shift - this->page_shift); /* Check, if the block is used by the mirror table */ if (!md || md->pages[chip] != page) goto write; } - printk (KERN_ERR "No space left to write bad block table\n"); + printk(KERN_ERR "No space left to write bad block table\n"); return -ENOSPC; -write: + write: /* Set up shift count and masks for the flash table */ bits = td->options & NAND_BBT_NRBITS_MSK; + msk[2] = ~rcode; switch (bits) { - case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; - case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; - case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; - case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; default: return -EINVAL; } @@ -500,82 +671,92 @@ write: to = ((loff_t) page) << this->page_shift; - memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); - oobinfo.useecc = MTD_NANDECC_PLACEONLY; - /* Must we save the block contents ? */ if (td->options & NAND_BBT_SAVECONTENT) { /* Make it block aligned */ to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); len = 1 << this->bbt_erase_shift; - res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + res = mtd->read(mtd, to, len, &retlen, buf); if (res < 0) { if (retlen != len) { - printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n"); + printk(KERN_INFO "nand_bbt: Error " + "reading block for writing " + "the bad block table\n"); return res; } - printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); + printk(KERN_WARNING "nand_bbt: ECC error " + "while reading block for writing " + "bad block table\n"); } + /* Read oob data */ + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd->read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.oobretlen != ops.ooblen) + goto outerr; + /* Calc the byte offset in the buffer */ pageoffs = page - (int)(to >> this->page_shift); offs = pageoffs << this->page_shift; /* Preset the bbt area with 0xff */ - memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); - /* Preset the bbt's oob area with 0xff */ - memset (&buf[len + pageoffs * mtd->oobsize], 0xff, - ((len >> this->page_shift) - pageoffs) * mtd->oobsize); - if (td->options & NAND_BBT_VERSION) { - buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; - } + memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + } else { /* Calc length */ len = (size_t) (numblocks >> sft); /* Make it page aligned ! */ - len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); + len = (len + (mtd->writesize - 1)) & + ~(mtd->writesize - 1); /* Preset the buffer with 0xff */ - memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); + memset(buf, 0xff, len + + (len >> this->page_shift)* mtd->oobsize); offs = 0; + ooboffs = len; /* Pattern is located in oob area of first page */ - memcpy (&buf[len + td->offs], td->pattern, td->len); - if (td->options & NAND_BBT_VERSION) { - buf[len + td->veroffs] = td->version[chip]; - } + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); } + if (td->options & NAND_BBT_VERSION) + buf[ooboffs + td->veroffs] = td->version[chip]; + /* walk through the memory table */ - for (i = 0; i < numblocks; ) { + for (i = 0; i < numblocks;) { uint8_t dat; dat = this->bbt[bbtoffs + (i >> 2)]; - for (j = 0; j < 4; j++ , i++) { + for (j = 0; j < 4; j++, i++) { int sftcnt = (i << (3 - sft)) & sftmsk; /* Do not store the reserved bbt blocks ! */ - buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); + buf[offs + (i >> sft)] &= + ~(msk[dat & 0x03] << sftcnt); dat >>= 2; } } - memset (&einfo, 0, sizeof (einfo)); + memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; - einfo.addr = (unsigned long) to; + einfo.addr = (unsigned long)to; einfo.len = 1 << this->bbt_erase_shift; - res = nand_erase_nand (mtd, &einfo, 1); - if (res < 0) { - printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); - return res; - } + res = nand_erase_nand(mtd, &einfo, 1); + if (res < 0) + goto outerr; - res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); - if (res < 0) { - printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); - return res; - } - printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", - (unsigned int) to, td->version[chip]); + res = scan_write_bbt(mtd, to, len, buf, &buf[len]); + if (res < 0) + goto outerr; + + printk(KERN_DEBUG "Bad block table written to 0x%08x, version " + "0x%02X\n", (unsigned int)to, td->version[chip]); /* Mark it as used */ td->pages[chip] = page; } return 0; + + outerr: + printk(KERN_WARNING + "nand_bbt: Error while writing bad block table %d\n", res); + return res; } /** @@ -586,29 +767,27 @@ write: * The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ -static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - /* Ensure that we only scan for the pattern and nothing else */ - bd->options = 0; - create_bbt (mtd, this->data_buf, bd, -1); - return 0; + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt(mtd, this->buffers->databuf, bd, -1); } /** - * check_create - [GENERIC] create and write bbt(s) if neccecary + * check_create - [GENERIC] create and write bbt(s) if necessary * @mtd: MTD device structure * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * * The function checks the results of the previous call to read_bbt - * and creates / updates the bbt(s) if neccecary - * Creation is neccecary if no bbt was found for the chip/device - * Update is neccecary if one of the tables is missing or the + * and creates / updates the bbt(s) if necessary + * Creation is necessary if no bbt was found for the chip/device + * Update is necessary if one of the tables is missing or the * version nr. of one table is less than the other */ -static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) { int i, chips, writeops, chipsel, res; struct nand_chip *this = mtd->priv; @@ -676,35 +855,35 @@ static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des rd = td; goto writecheck; } -create: + create: /* Create the bad block table by scanning the device ? */ if (!(td->options & NAND_BBT_CREATE)) continue; /* Create the table in memory by scanning the chip(s) */ - create_bbt (mtd, buf, bd, chipsel); + create_bbt(mtd, buf, bd, chipsel); td->version[i] = 1; if (md) md->version[i] = 1; -writecheck: + writecheck: /* read back first ? */ if (rd) - read_abs_bbt (mtd, buf, rd, chipsel); + read_abs_bbt(mtd, buf, rd, chipsel); /* If they weren't versioned, read both. */ if (rd2) - read_abs_bbt (mtd, buf, rd2, chipsel); + read_abs_bbt(mtd, buf, rd2, chipsel); /* Write the bad block table to the device ? */ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { - res = write_bbt (mtd, buf, td, md, chipsel); + res = write_bbt(mtd, buf, td, md, chipsel); if (res < 0) return res; } /* Write the mirror bad block table to the device ? */ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt (mtd, buf, md, td, chipsel); + res = write_bbt(mtd, buf, md, td, chipsel); if (res < 0) return res; } @@ -721,7 +900,7 @@ writecheck: * accidental erasures / writes. The regions are identified by * the mark 0x02. */ -static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, j, chips, block, nrblocks, update; @@ -739,7 +918,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) for (i = 0; i < chips; i++) { if ((td->options & NAND_BBT_ABSPAGE) || !(td->options & NAND_BBT_WRITE)) { - if (td->pages[i] == -1) continue; + if (td->pages[i] == -1) + continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); block <<= 1; oldval = this->bbt[(block >> 3)]; @@ -759,7 +939,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) oldval = this->bbt[(block >> 3)]; newval = oldval | (0x2 << (block & 0x06)); this->bbt[(block >> 3)] = newval; - if (oldval != newval) update = 1; + if (oldval != newval) + update = 1; block += 2; } /* If we want reserved blocks to be recorded to flash, and some @@ -784,7 +965,7 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) * by calling the nand_free_bbt function. * */ -int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; int len, res = 0; @@ -793,53 +974,56 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) struct nand_bbt_descr *md = this->bbt_md; len = mtd->size >> (this->bbt_erase_shift + 2); - /* Allocate memory (2bit per block) */ - this->bbt = kmalloc (len, GFP_KERNEL); + /* Allocate memory (2bit per block) and clear the memory bad block table */ + this->bbt = kzalloc(len, GFP_KERNEL); if (!this->bbt) { - printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); + printk(KERN_ERR "nand_scan_bbt: Out of memory\n"); return -ENOMEM; } - /* Clear the memory bad block table */ - memset (this->bbt, 0x00, len); /* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ - if (!td) - return nand_memory_bbt(mtd, bd); + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree(this->bbt); + this->bbt = NULL; + } + return res; + } /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; - buf = kmalloc (len, GFP_KERNEL); + buf = vmalloc(len); if (!buf) { - printk (KERN_ERR "nand_bbt: Out of memory\n"); - kfree (this->bbt); + printk(KERN_ERR "nand_bbt: Out of memory\n"); + kfree(this->bbt); this->bbt = NULL; return -ENOMEM; } /* Is the bbt at a given page ? */ if (td->options & NAND_BBT_ABSPAGE) { - res = read_abs_bbts (mtd, buf, td, md); + res = read_abs_bbts(mtd, buf, td, md); } else { /* Search the bad block table using a pattern in oob */ - res = search_read_bbts (mtd, buf, td, md); + res = search_read_bbts(mtd, buf, td, md); } if (res) - res = check_create (mtd, buf, bd); + res = check_create(mtd, buf, bd); /* Prevent the bbt regions from erasing / writing */ - mark_bbt_region (mtd, td); + mark_bbt_region(mtd, td); if (md) - mark_bbt_region (mtd, md); + mark_bbt_region(mtd, md); - kfree (buf); + vfree(buf); return res; } - /** * nand_update_bbt - [NAND Interface] update bad block table(s) * @mtd: MTD device structure @@ -847,7 +1031,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) * * The function updates the bad block table(s) */ -int nand_update_bbt (struct mtd_info *mtd, loff_t offs) +int nand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct nand_chip *this = mtd->priv; int len, res = 0, writeops = 0; @@ -863,9 +1047,9 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs) /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; - buf = kmalloc (len, GFP_KERNEL); + buf = kmalloc(len, GFP_KERNEL); if (!buf) { - printk (KERN_ERR "nand_update_bbt: Out of memory\n"); + printk(KERN_ERR "nand_update_bbt: Out of memory\n"); return -ENOMEM; } @@ -873,7 +1057,7 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs) /* Do we have a bbt per chip ? */ if (td->options & NAND_BBT_PERCHIP) { - chip = (int) (offs >> this->chip_shift); + chip = (int)(offs >> this->chip_shift); chipsel = chip; } else { chip = 0; @@ -886,29 +1070,26 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs) /* Write the bad block table to the device ? */ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { - res = write_bbt (mtd, buf, td, md, chipsel); + res = write_bbt(mtd, buf, td, md, chipsel); if (res < 0) goto out; } /* Write the mirror bad block table to the device ? */ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt (mtd, buf, md, td, chipsel); + res = write_bbt(mtd, buf, md, td, chipsel); } -out: - kfree (buf); + out: + kfree(buf); return res; } /* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - * - * The memory based patterns just - */ + * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static struct nand_bbt_descr smallpage_memorybased = { - .options = 0, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern @@ -922,14 +1103,14 @@ static struct nand_bbt_descr largepage_memorybased = { }; static struct nand_bbt_descr smallpage_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern }; static struct nand_bbt_descr largepage_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 0, .len = 2, .pattern = scan_ff_pattern @@ -977,7 +1158,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { * support for the device and calls the nand_scan_bbt function * */ -int nand_default_bbt (struct mtd_info *mtd) +int nand_default_bbt(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; @@ -987,7 +1168,7 @@ int nand_default_bbt (struct mtd_info *mtd) * of the good / bad information, so we _must_ store * this information in a good / bad table during * startup - */ + */ if (this->options & NAND_IS_AND) { /* Use the default pattern descriptors */ if (!this->bbt_td) { @@ -995,10 +1176,9 @@ int nand_default_bbt (struct mtd_info *mtd) this->bbt_md = &bbt_mirror_descr; } this->options |= NAND_USE_FLASH_BBT; - return nand_scan_bbt (mtd, &agand_flashbased); + return nand_scan_bbt(mtd, &agand_flashbased); } - /* Is a flash based bad block table requested ? */ if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern descriptors */ @@ -1007,18 +1187,17 @@ int nand_default_bbt (struct mtd_info *mtd) this->bbt_md = &bbt_mirror_descr; } if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->oobblock > 512) ? - &largepage_flashbased : &smallpage_flashbased; + this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; } } else { this->bbt_td = NULL; this->bbt_md = NULL; if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->oobblock > 512) ? - &largepage_memorybased : &smallpage_memorybased; + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; } } - return nand_scan_bbt (mtd, this->badblock_pattern); + return nand_scan_bbt(mtd, this->badblock_pattern); } /** @@ -1027,26 +1206,35 @@ int nand_default_bbt (struct mtd_info *mtd) * @offs: offset in the device * @allowbbt: allow access to bad block table region * - */ -int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) +*/ +int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct nand_chip *this = mtd->priv; int block; - uint8_t res; + uint8_t res; /* Get block number * 2 */ - block = (int) (offs >> (this->bbt_erase_shift - 1)); + block = (int)(offs >> (this->bbt_erase_shift - 1)); res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: " "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1); switch ((int)res) { - case 0x00: return 0; - case 0x01: return 1; - case 0x02: return allowbbt ? 0 : 1; + case 0x00: + return 0; + case 0x01: + return 1; + case 0x02: + return allowbbt ? 0 : 1; } return 1; } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_scan_bbt); +EXPORT_SYMBOL(nand_default_bbt); +#endif + #endif diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 4c532b0794e..e1d5154db22 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -7,7 +7,9 @@ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) * Toshiba America Electronics Components, Inc. * - * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $ + * Copyright (C) 2006 Thomas Gleixner + * + * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -39,6 +41,14 @@ #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) +/* XXX U-BOOT XXX */ +#if 0 +#include +#include +#include +#include +#endif + #include /* @@ -128,6 +138,10 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, return 0; } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_calculate_ecc); +#endif #endif /* CONFIG_NAND_SPL */ static inline int countbits(uint32_t byte) @@ -197,4 +211,9 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat, return -1; } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_correct_data); +#endif + #endif diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 73634903952..f8b96cf025d 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -2,8 +2,8 @@ * drivers/mtd/nandids.c * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) - * - * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ + * + * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,7 +16,6 @@ #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) #include - /* * Chip ID list * @@ -29,13 +28,15 @@ * 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { + +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, @@ -44,6 +45,7 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +#endif {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, @@ -61,52 +63,72 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, - /* These are the new chips with large page size. The pagesize - * and the erasesize is determined from the extended id bytes - */ + /* + * These are the new chips with large page size. The pagesize and the + * erasesize is determined from the extended id bytes + */ +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + + /*512 Megabit */ + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, + /* 1 Gigabit */ - {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, /* 2 Gigabit */ - {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, /* 4 Gigabit */ - {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, /* 8 Gigabit */ - {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, /* 16 Gigabit */ - {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - - /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! - * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes - * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 - * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go - * There are more speed improvements for reads and writes possible, but not implemented now + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + + /* + * Renesas AND 1 Gigabit. Those chips do not support extended id and + * have a strange page/block layout ! The chosen minimum erasesize is + * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page + * planes 1 block = 2 pages, but due to plane arrangement the blocks + * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would + * increase the eraseblock size so we chose a combined one which can be + * erased in one go There are more speed improvements for reads and + * writes possible, but not implemented now */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, + NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | + BBT_AUTO_REFRESH + }, {NULL,} }; @@ -121,6 +143,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_NATIONAL, "National"}, {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix"}, {NAND_MFR_MICRON, "Micron"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 828cc338adb..78e70cc807d 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -39,6 +39,9 @@ #include #include + +#include +#include #include #include @@ -69,71 +72,33 @@ static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) { struct jffs2_unknown_node cleanmarker; - int clmpos = 0; - int clmlen = 8; erase_info_t erase; ulong erase_length; - int isNAND; int bbtest = 1; int result; int percent_complete = -1; int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; const char *mtd_device = meminfo->name; + struct mtd_oob_ops oob_opts; + struct nand_chip *chip = meminfo->priv; + uint8_t buf[64]; + memset(buf, 0, sizeof(buf)); memset(&erase, 0, sizeof(erase)); + memset(&oob_opts, 0, sizeof(oob_opts)); erase.mtd = meminfo; erase.len = meminfo->erasesize; erase.addr = opts->offset; erase_length = opts->length; - isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; - if (opts->jffs2) { - cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - if (isNAND) { - struct nand_oobinfo *oobinfo = &meminfo->oobinfo; - - /* check for autoplacement */ - if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { - /* get the position of the free bytes */ - if (!oobinfo->oobfree[0][1]) { - printf(" Eeep. Autoplacement selected " - "and no empty space in oob\n"); - return -1; - } - clmpos = oobinfo->oobfree[0][0]; - clmlen = oobinfo->oobfree[0][1]; - if (clmlen > 8) - clmlen = 8; - } else { - /* legacy mode */ - switch (meminfo->oobsize) { - case 8: - clmpos = 6; - clmlen = 2; - break; - case 16: - clmpos = 8; - clmlen = 8; - break; - case 64: - clmpos = 16; - clmlen = 8; - break; - } - } - - cleanmarker.totlen = cpu_to_je32(8); - } else { - cleanmarker.totlen = - cpu_to_je32(sizeof(struct jffs2_unknown_node)); - } - cleanmarker.hdr_crc = cpu_to_je32( - crc32_no_comp(0, (unsigned char *) &cleanmarker, - sizeof(struct jffs2_unknown_node) - 4)); - } + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.totlen = cpu_to_je32(8); + cleanmarker.hdr_crc = cpu_to_je32( + crc32_no_comp(0, (unsigned char *) &cleanmarker, + sizeof(struct jffs2_unknown_node) - 4)); /* scrub option allows to erase badblock. To prevent internal * check from erase() method, set block check method to dummy @@ -163,7 +128,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) for (; erase.addr < opts->offset + erase_length; erase.addr += meminfo->erasesize) { - + WATCHDOG_RESET (); if (!opts->scrub && bbtest) { @@ -194,25 +159,21 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) /* format for JFFS2 ? */ if (opts->jffs2) { - /* write cleanmarker */ - if (isNAND) { - size_t written; - result = meminfo->write_oob(meminfo, - erase.addr + clmpos, - clmlen, - &written, - (unsigned char *) - &cleanmarker); - if (result != 0) { - printf("\n%s: MTD writeoob failure: %d\n", - mtd_device, result); - continue; - } - } else { - printf("\n%s: this erase routine only supports" - " NAND devices!\n", - mtd_device); + chip->ops.len = chip->ops.ooblen = 64; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + result = meminfo->write_oob(meminfo, + erase.addr + meminfo->oobsize, + &chip->ops); + if (result != 0) { + printf("\n%s: MTD writeoob failure: %d\n", + mtd_device, result); + continue; } + else + printf("%s: MTD writeoob at 0x%08x\n",mtd_device, erase.addr + meminfo->oobsize ); } if (!opts->quiet) { @@ -232,11 +193,11 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) percent_complete = percent; printf("\rErasing at 0x%x -- %3d%% complete.", - erase.addr, percent); + erase.addr, percent); if (opts->jffs2 && result == 0) - printf(" Cleanmarker written at 0x%x.", - erase.addr); + printf(" Cleanmarker written at 0x%x.", + erase.addr); } } } @@ -253,6 +214,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) return 0; } +/* XXX U-BOOT XXX */ +#if 0 + #define MAX_PAGE_SIZE 2048 #define MAX_OOB_SIZE 64 @@ -263,26 +227,189 @@ static unsigned char data_buf[MAX_PAGE_SIZE]; static unsigned char oob_buf[MAX_OOB_SIZE]; /* OOB layouts to pass into the kernel as default */ -static struct nand_oobinfo none_oobinfo = { +static struct nand_ecclayout none_ecclayout = { .useecc = MTD_NANDECC_OFF, }; -static struct nand_oobinfo jffs2_oobinfo = { +static struct nand_ecclayout jffs2_ecclayout = { .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, .eccpos = { 0, 1, 2, 3, 6, 7 } }; -static struct nand_oobinfo yaffs_oobinfo = { +static struct nand_ecclayout yaffs_ecclayout = { .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, .eccpos = { 8, 9, 10, 13, 14, 15} }; -static struct nand_oobinfo autoplace_oobinfo = { +static struct nand_ecclayout autoplace_ecclayout = { .useecc = MTD_NANDECC_AUTOPLACE }; +#endif + +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + * + * Copied from nand_base.c + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 + + +/* copied from nand_base.c: nand_do_write_ops() + * Only very small changes + */ +int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) +{ + int chipnr, realpage, page, blockmask, column; + struct nand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret, subpage; + + ops->retlen = 0; + if (!writelen) + return 0; + + printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); + + /* reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); + + if (subpage && oob) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write oob to subpage\n"); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* XXX U-BOOT XXX */ +#if 0 + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + return -EIO; +#endif + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) { + printf("!oob, writing %d bytes with 0xff to chip->oob_poi (0x%08x)\n", mtd->oobsize, chip->oob_poi); + memset(chip->oob_poi, 0xff, mtd->oobsize); + } + + while(1) { + int bytes = mtd->writesize; + int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write ? */ + if (unlikely(column || writelen < (mtd->writesize - 1))) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } + + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); + + ret = chip->write_page(mtd, chip, wbuf, page, cached, + (ops->mode == MTD_OOB_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + return ret; +} + +/* XXX U-BOOT XXX */ +#if 0 /** * nand_write_opts: - write image to NAND flash with support for various options * @@ -301,9 +428,9 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) int blockstart = -1; loff_t offs; int readlen; - int oobinfochanged = 0; + int ecclayoutchanged = 0; int percent_complete = -1; - struct nand_oobinfo old_oobinfo; + struct nand_ecclayout old_ecclayout; ulong mtdoffset = opts->offset; ulong erasesize_blockalign; u_char *buffer = opts->buffer; @@ -324,35 +451,35 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) - && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) - && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { + if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) + && !(meminfo->oobsize == 8 && meminfo->writesize == 256) + && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { printf("Unknown flash (not normal NAND)\n"); return -1; } /* read the current oob info */ - memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); + memcpy(&old_ecclayout, &meminfo->ecclayout, sizeof(old_ecclayout)); /* write without ecc? */ if (opts->noecc) { - memcpy(&meminfo->oobinfo, &none_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; + memcpy(&meminfo->ecclayout, &none_ecclayout, + sizeof(meminfo->ecclayout)); + ecclayoutchanged = 1; } /* autoplace ECC? */ - if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + if (opts->autoplace && (old_ecclayout.useecc != MTD_NANDECC_AUTOPLACE)) { - memcpy(&meminfo->oobinfo, &autoplace_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; + memcpy(&meminfo->ecclayout, &autoplace_ecclayout, + sizeof(meminfo->ecclayout)); + ecclayoutchanged = 1; } /* force OOB layout for jffs2 or yaffs? */ if (opts->forcejffs2 || opts->forceyaffs) { - struct nand_oobinfo *oobsel = - opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; + struct nand_ecclayout *oobsel = + opts->forcejffs2 ? &jffs2_ecclayout : &yaffs_ecclayout; if (meminfo->oobsize == 8) { if (opts->forceyaffs) { @@ -361,15 +488,15 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) goto restoreoob; } /* Adjust number of ecc bytes */ - jffs2_oobinfo.eccbytes = 3; + jffs2_ecclayout.eccbytes = 3; } - memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); + memcpy(&meminfo->ecclayout, oobsel, sizeof(meminfo->ecclayout)); } /* get image length */ imglen = opts->length; - pagelen = meminfo->oobblock + pagelen = meminfo->writesize + ((opts->writeoob != 0) ? meminfo->oobsize : 0); /* check, if file is pagealigned */ @@ -379,11 +506,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } /* check, if length fits into device */ - if (((imglen / pagelen) * meminfo->oobblock) + if (((imglen / pagelen) * meminfo->writesize) > (meminfo->size - opts->offset)) { printf("Image %d bytes, NAND page %d bytes, " "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->oobblock, meminfo->size); + imglen, pagelen, meminfo->writesize, meminfo->size); printf("Input block does not fit into device\n"); goto restoreoob; } @@ -437,11 +564,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } while (offs < blockstart + erasesize_blockalign); } - readlen = meminfo->oobblock; + readlen = meminfo->writesize; if (opts->pad && (imglen < readlen)) { readlen = imglen; memset(data_buf + readlen, 0xff, - meminfo->oobblock - readlen); + meminfo->writesize - readlen); } /* read page data from input memory buffer */ @@ -474,7 +601,7 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) /* write out the page data */ result = meminfo->write(meminfo, mtdoffset, - meminfo->oobblock, + meminfo->writesize, &written, (unsigned char *) &data_buf); @@ -505,16 +632,16 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } } - mtdoffset += meminfo->oobblock; + mtdoffset += meminfo->writesize; } if (!opts->quiet) printf("\n"); restoreoob: - if (oobinfochanged) { - memcpy(&meminfo->oobinfo, &old_oobinfo, - sizeof(meminfo->oobinfo)); + if (ecclayoutchanged) { + memcpy(&meminfo->ecclayout, &old_ecclayout, + sizeof(meminfo->ecclayout)); } if (imglen > 0) { @@ -548,22 +675,22 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) int result; /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) - && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) - && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { + if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) + && !(meminfo->oobsize == 8 && meminfo->writesize == 256) + && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { printf("Unknown flash (not normal NAND)\n"); return -1; } - pagelen = meminfo->oobblock + pagelen = meminfo->writesize + ((opts->readoob != 0) ? meminfo->oobsize : 0); /* check, if length is not larger than device */ - if (((imglen / pagelen) * meminfo->oobblock) + if (((imglen / pagelen) * meminfo->writesize) > (meminfo->size - opts->offset)) { printf("Image %d bytes, NAND page %d bytes, " "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->oobblock, meminfo->size); + imglen, pagelen, meminfo->writesize, meminfo->size); printf("Input block is larger than device\n"); return -1; } @@ -621,7 +748,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) /* read page data to memory buffer */ result = meminfo->read(meminfo, mtdoffset, - meminfo->oobblock, + meminfo->writesize, &readlen, (unsigned char *) &data_buf); @@ -685,7 +812,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) } } - mtdoffset += meminfo->oobblock; + mtdoffset += meminfo->writesize; } if (!opts->quiet) @@ -699,7 +826,10 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) /* return happy */ return 0; } +#endif +/* XXX U-BOOT XXX */ +#if 0 /****************************************************************************** * Support for locking / unlocking operations of some NAND devices *****************************************************************************/ @@ -784,7 +914,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset) this->select_chip(meminfo, chipnr); - if ((offset & (meminfo->oobblock - 1)) != 0) { + if ((offset & (meminfo->writesize - 1)) != 0) { printf ("nand_get_lock_status: " "Start address must be beginning of " "nand page!\n"); @@ -813,7 +943,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset) * @param meminfo nand mtd instance * @param start start byte address * @param length number of bytes to unlock (must be a multiple of - * page size nand->oobblock) + * page size nand->writesize) * * @return 0 on success, -1 in case of error */ @@ -839,14 +969,14 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) goto out; } - if ((start & (meminfo->oobblock - 1)) != 0) { + if ((start & (meminfo->writesize - 1)) != 0) { printf ("nand_unlock: Start address must be beginning of " "nand page!\n"); ret = -1; goto out; } - if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { + if (length == 0 || (length & (meminfo->writesize - 1)) != 0) { printf ("nand_unlock: Length must be a multiple of nand page " "size!\n"); ret = -1; @@ -875,5 +1005,6 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) this->select_chip(meminfo, -1); return ret; } +#endif #endif -- cgit v1.2.3 From 4cbb651b29cb64d378a06729970e1e153bb605b1 Mon Sep 17 00:00:00 2001 From: William Juul Date: Thu, 8 Nov 2007 10:39:53 +0100 Subject: Remove white space at end. Signed-off-by: William Juul Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_base.c | 4 ++-- drivers/mtd/nand/nand_util.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index aeb179731d6..e21a18baa4a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2080,7 +2080,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Calculate pages in each block */ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); - + /* Select the NAND device */ chip->select_chip(mtd, chipnr); @@ -2748,7 +2748,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } #endif - + ret = nand_scan_ident(mtd, maxchips); if (!ret) ret = nand_scan_tail(mtd); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 78e70cc807d..9fe486698c1 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -128,7 +128,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) for (; erase.addr < opts->offset + erase_length; erase.addr += meminfo->erasesize) { - + WATCHDOG_RESET (); if (!opts->scrub && bbtest) { @@ -163,7 +163,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) chip->ops.datbuf = NULL; chip->ops.oobbuf = buf; chip->ops.ooboffs = chip->badblockpos & ~0x01; - + result = meminfo->write_oob(meminfo, erase.addr + meminfo->oobsize, &chip->ops); @@ -254,7 +254,7 @@ static struct nand_ecclayout autoplace_ecclayout = { * @chip: nand chip structure * @oob: oob data buffer * @ops: oob ops structure - * + * * Copied from nand_base.c */ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, @@ -314,13 +314,13 @@ int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; - + ops->retlen = 0; if (!writelen) return 0; printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); - + /* reject writes, which are not page aligned */ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { printk(KERN_NOTICE "nand_write: " -- cgit v1.2.3 From 5e1dae5c3db7f4026f31b6a2a81ecd9e9dee475f Mon Sep 17 00:00:00 2001 From: William Juul Date: Fri, 9 Nov 2007 13:32:30 +0100 Subject: Fixing coding style issues - Fixing leading white spaces - Fixing indentation where 4 spaces are used instead of tab - Removing C++ comments (//), wherever I introduced them Signed-off-by: William Juul Signed-off-by: Scott Wood --- drivers/mtd/nand/diskonchip.c | 10 +++++----- drivers/mtd/nand/nand_base.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index a03f982be5f..ce197f5ad16 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -500,11 +500,11 @@ static u_char doc2001_read_byte(struct mtd_info *mtd) struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; - //ReadDOC(docptr, CDSNSlowIO); + /*ReadDOC(docptr, CDSNSlowIO); */ /* 11.4.5 -- delay twice to allow extended length cycle */ DoC_Delay(doc, 2); ReadDOC(docptr, ReadPipeInit); - //return ReadDOC(docptr, Mil_CDSN_IO); + /*return ReadDOC(docptr, Mil_CDSN_IO); */ return ReadDOC(docptr, LastDataRead); } @@ -1051,7 +1051,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, return ret; } -//u_char mydatabuf[528]; +/*u_char mydatabuf[528]; */ /* The strange out-of-order .oobfree list below is a (possibly unneeded) * attempt to retain compatibility. It used to read: @@ -1623,11 +1623,11 @@ static int __init doc_probe(unsigned long physadr) if (ChipID == DOC_ChipID_DocMilPlus16) { WriteDOC(~newval, virtadr, Mplus_AliasResolution); oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); - WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it + WriteDOC(newval, virtadr, Mplus_AliasResolution); /* restore it */ } else { WriteDOC(~newval, virtadr, AliasResolution); oldval = ReadDOC(doc->virtadr, AliasResolution); - WriteDOC(newval, virtadr, AliasResolution); // restore it + WriteDOC(newval, virtadr, AliasResolution); /* restore it */ } newval = ~newval; if (oldval == newval) { diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e21a18baa4a..4b1c564f750 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2557,7 +2557,7 @@ int nand_scan_tail(struct mtd_info *mtd) default: printk(KERN_WARNING "No oob scheme defined for " "oobsize %d\n", mtd->oobsize); -// BUG(); +/* BUG(); */ } } -- cgit v1.2.3 From 12072264528eba33737bc9674e19f0e925ffda23 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Sat, 5 Jan 2008 16:43:25 +0100 Subject: NAND: Change nand_wait_ready() to not call nand_wait() This patch changes nand_wait_ready() to not just call nand_wait(), since this will send a new command to the NAND chip. We just want to wait for the chip to become ready here. Signed-off-by: Stefan Roese --- drivers/mtd/nand/nand_base.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 4b1c564f750..f577ed6294c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -489,7 +489,16 @@ EXPORT_SYMBOL_GPL(nand_wait_ready); void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - nand_wait(mtd, chip); + u32 timeo = (CFG_HZ * 20) / 1000; + + reset_timer(); + + /* wait until command is processed or timeout occures */ + while (get_timer(0) < timeo) { + if (chip->dev_ready) + if (chip->dev_ready(mtd)) + break; + } } #endif -- cgit v1.2.3 From e52b34d40a8a646e3d11638ea8797e96398dba13 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 10 Jan 2008 18:47:33 +0100 Subject: NAND: Make NAND driver less verbose per default This patch turns off printing of bad blocks per default upon bootup. This can always be shown via the "nand bad" command later. Signed-off-by: Stefan Roese --- drivers/mtd/nand/nand_base.c | 6 +++--- drivers/mtd/nand/nand_bbt.c | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index f577ed6294c..5661a8e4c70 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2466,9 +2466,9 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, - nand_manuf_ids[maf_idx].name, type->name); + MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, + nand_manuf_ids[maf_idx].name, type->name); return type; } diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index acf1cf54339..84479473b6d 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -391,7 +391,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, loff_t from; size_t readlen; - printk(KERN_INFO "Scanning device for bad blocks\n"); + MTDDEBUG (MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n"); if (bd->options & NAND_BBT_SCANALLPAGES) len = 1 << (this->bbt_erase_shift - this->page_shift); @@ -444,8 +444,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int)from); mtd->ecc_stats.badblocks++; } -- cgit v1.2.3 From 41ef8c716e93fdf50efe9c1ba733ca6675daaca6 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 18 Mar 2008 15:29:14 -0500 Subject: Don't panic if a controller driver does ecc its own way. Some hardware, such as the enhanced local bus controller used on some mpc83xx chips, does ecc transparently when reading and writing data, rather than providing a generic calculate/correct mechanism that can be exported to the nand subsystem. The subsystem should not BUG() when calculate, correct, or hwctl are missing, if the methods that call them have been overridden. Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5661a8e4c70..7bceea8ac3d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2595,8 +2595,12 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; case NAND_ECC_HW_SYNDROME: - if (!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) { + if ((!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) && + (!chip->ecc.read_page || + chip->ecc.read_page == nand_read_page_hwecc || + !chip->ecc.write_page || + chip->ecc.write_page == nand_write_page_hwecc)) { printk(KERN_WARNING "No ECC functions supplied, " "Hardware ECC not possible\n"); BUG(); -- cgit v1.2.3 From 9fd020d6b4b36b9fb67cd834bc1ae7fdba15ee9e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 21 Mar 2008 16:12:51 -0500 Subject: Freescale eLBC FCM NAND driver This is a driver for the Flash Control Machine of the enhanched Local Bus Controller found on some Freescale chips (such as the mpc8313 and the mpc8379). Signed-off-by: Scott Wood --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/fsl_elbc_nand.c | 759 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 760 insertions(+) create mode 100644 drivers/mtd/nand/fsl_elbc_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 7bd22a0c9d7..ffb31695945 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -32,6 +32,7 @@ COBJS-y += nand_ecc.o COBJS-y += nand_bbt.o COBJS-y += nand_util.o +COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-y += fsl_upm.o COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c new file mode 100644 index 00000000000..c1644c0f6af --- /dev/null +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -0,0 +1,759 @@ +/* Freescale Enhanced Local Bus Controller FCM NAND driver + * + * Copyright (c) 2006-2008 Freescale Semiconductor + * + * Authors: Nick Spence , + * Scott Wood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef VERBOSE_DEBUG +#define DEBUG_ELBC +#define vdbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define vdbg(format, arg...) do {} while (0) +#endif + +/* Can't use plain old DEBUG because the linux mtd + * headers define it as a macro. + */ +#ifdef DEBUG_ELBC +#define dbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +#define MAX_BANKS 8 +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ +#define FCM_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for FCM */ + +#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) + +struct fsl_elbc_ctrl; + +/* mtd information per set */ + +struct fsl_elbc_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct fsl_elbc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + u8 __iomem *vbase; /* Chip select base virtual address */ + int page_size; /* NAND page size (0=512, 1=2048) */ + unsigned int fmr; /* FCM Flash Mode Register value */ +}; + +/* overview of the fsl elbc controller */ + +struct fsl_elbc_ctrl { + struct nand_hw_control controller; + struct fsl_elbc_mtd *chips[MAX_BANKS]; + + /* device info */ + lbus83xx_t *regs; + u8 __iomem *addr; /* Address of assigned FCM buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from LTESR after last op */ + unsigned int mdr; /* UPM/FCM Data Register value */ + unsigned int use_mdr; /* Non zero if the MDR is to be set */ + unsigned int oob; /* Non zero if operating on OOB data */ + uint8_t *oob_poi; /* Place to write ECC after read back */ +}; + +/* These map to the positions used by the FCM hardware ECC generator */ + +/* Small Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { + .eccbytes = 3, + .eccpos = {6, 7, 8}, + .oobfree = { {0, 5}, {9, 7} }, + .oobavail = 12, +}; + +/* Small Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { + .eccbytes = 3, + .eccpos = {8, 9, 10}, + .oobfree = { {0, 5}, {6, 2}, {11, 5} }, + .oobavail = 12, +}; + +/* Large Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { + .eccbytes = 12, + .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, + .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, + .oobavail = 48, +}; + +/* Large Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { + .eccbytes = 12, + .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, + .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, + .oobavail = 48, +}; + +/*=================================*/ + +/* + * Set up the FCM hardware block and page address fields, and the fcm + * structure addr field to point to the correct FCM buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + lbus83xx_t *lbc = ctrl->regs; + int buf_num; + + ctrl->page = page_addr; + + out_be32(&lbc->fbar, + page_addr >> (chip->phys_erase_shift - chip->page_shift)); + + if (priv->page_size) { + out_be32(&lbc->fpar, + ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | + (oob ? FPAR_LP_MS : 0) | column); + buf_num = (page_addr & 1) << 2; + } else { + out_be32(&lbc->fpar, + ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | + (oob ? FPAR_SP_MS : 0) | column); + buf_num = page_addr & 7; + } + + ctrl->addr = priv->vbase + buf_num * 1024; + ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ctrl->index += priv->page_size ? 2048 : 512; + + vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " + "index %x, pes %d ps %d\n", + buf_num, ctrl->addr, priv->vbase, ctrl->index, + chip->phys_erase_shift, chip->page_shift); +} + +/* + * execute FCM command and wait for it to complete + */ +static int fsl_elbc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + lbus83xx_t *lbc = ctrl->regs; + long long end_tick; + u32 ltesr; + + /* Setup the FMR[OP] to execute without write protection */ + out_be32(&lbc->fmr, priv->fmr | 3); + if (ctrl->use_mdr) + out_be32(&lbc->mdr, ctrl->mdr); + + vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", + in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); + vdbg("fsl_elbc_run_command: fbar=%08x fpar=%08x " + "fbcr=%08x bank=%d\n", + in_be32(&lbc->fbar), in_be32(&lbc->fpar), + in_be32(&lbc->fbcr), priv->bank); + + /* execute special operation */ + out_be32(&lbc->lsor, priv->bank); + + /* wait for FCM complete flag or timeout */ + end_tick = usec2ticks(FCM_TIMEOUT_MSECS * 1000) + get_ticks(); + + ltesr = 0; + while (end_tick > get_ticks()) { + ltesr = in_be32(&lbc->ltesr); + if (ltesr & LTESR_CC) + break; + } + + ctrl->status = ltesr & LTESR_NAND_MASK; + out_be32(&lbc->ltesr, ctrl->status); + out_be32(&lbc->lteatr, 0); + + /* store mdr value in case it was needed */ + if (ctrl->use_mdr) + ctrl->mdr = in_be32(&lbc->mdr); + + ctrl->use_mdr = 0; + + vdbg("fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", + ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); + + /* returns 0 on success otherwise non-zero) */ + return ctrl->status == LTESR_CC ? 0 : -EIO; +} + +static void fsl_elbc_do_read(struct nand_chip *chip, int oob) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + lbus83xx_t *lbc = ctrl->regs; + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + + out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + } else { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + + if (oob) + out_be32(&lbc->fcr, + NAND_CMD_READOOB << FCR_CMD0_SHIFT); + else + out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the FCM */ +static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + lbus83xx_t *lbc = ctrl->regs; + + ctrl->use_mdr = 0; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ctrl->index = 0; + + switch (command) { + /* READ0 and READ1 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ1: + column += 256; + + /* fall-through */ + case NAND_CMD_READ0: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ + set_addr(mtd, 0, page_addr, 0); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ctrl->index += column; + + fsl_elbc_do_read(chip, 0); + fsl_elbc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + + return; + + /* READID must read all 5 possible bytes while CEB is active */ + case NAND_CMD_READID: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_UA << FIR_OP1_SHIFT) | + (FIR_OP_RBW << FIR_OP2_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); + /* 5 bytes for manuf, device and exts */ + out_be32(&lbc->fbcr, 5); + ctrl->read_bytes = 5; + ctrl->use_mdr = 1; + ctrl->mdr = 0; + + set_addr(mtd, 0, 0, 0); + fsl_elbc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " + "page_addr: 0x%x.\n", page_addr); + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_PA << FIR_OP1_SHIFT) | + (FIR_OP_CM1 << FIR_OP2_SHIFT)); + + out_be32(&lbc->fcr, + (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); + + out_be32(&lbc->fbcr, 0); + ctrl->read_bytes = 0; + + fsl_elbc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 fcr; + vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " + "page_addr: 0x%x, column: 0x%x.\n", + page_addr, column); + + ctrl->column = column; + ctrl->oob = 0; + + if (priv->page_size) { + fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_WB << FIR_OP3_SHIFT) | + (FIR_OP_CW1 << FIR_OP4_SHIFT)); + } else { + fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP1_SHIFT) | + (FIR_OP_CA << FIR_OP2_SHIFT) | + (FIR_OP_PA << FIR_OP3_SHIFT) | + (FIR_OP_WB << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT)); + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; + ctrl->oob = 1; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; + } else { + /* Second 256 bytes --> READ1 */ + fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; + } + } + + out_be32(&lbc->fcr, fcr); + set_addr(mtd, column, page_addr, ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + int full_page; + vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " + "writing %d bytes.\n", ctrl->index); + + /* if the write did not start at 0 or is not a full page + * then set the exact length, otherwise use a full page + * write so the HW generates the ECC. + */ + if (ctrl->oob || ctrl->column != 0 || + ctrl->index != mtd->writesize + mtd->oobsize) { + out_be32(&lbc->fbcr, ctrl->index); + full_page = 0; + } else { + out_be32(&lbc->fbcr, 0); + full_page = 1; + } + + fsl_elbc_run_command(mtd); + + /* Read back the page in order to fill in the ECC for the + * caller. Is this really needed? + */ + if (full_page && ctrl->oob_poi) { + out_be32(&lbc->fbcr, 3); + set_addr(mtd, 6, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + 9; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + + memcpy_fromio(ctrl->oob_poi + 6, + &ctrl->addr[ctrl->index], 3); + ctrl->index += 3; + } + + ctrl->oob_poi = NULL; + return; + } + + /* CMD_STATUS must read the status byte while CEB is active */ + /* Note - it does not wait for the ready line */ + case NAND_CMD_STATUS: + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return; + + /* RESET without waiting for the ready line */ + case NAND_CMD_RESET: + dbg("fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); + out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); + fsl_elbc_run_command(mtd); + return; + + default: + printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", + command); + } +} + +static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the FCM Controller Data Buffer + */ +static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len < 0) { + printf("write_buf of %d bytes", len); + ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - ctrl->index) { + printf("write_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, bufsize - ctrl->index); + len = bufsize - ctrl->index; + } + + memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + ctrl->index += len; +} + +/* + * read a byte from either the FCM hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_elbc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + /* If there are still bytes in the FCM, then use the next byte. */ + if (ctrl->index < ctrl->read_bytes) + return in_8(&ctrl->addr[ctrl->index++]); + + printf("read_byte beyond end of buffer\n"); + return ERR_BYTE; +} + +/* + * Read from the FCM Controller Data Buffer + */ +static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); + memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); + ctrl->index += avail; + + if (len > avail) + printf("read_buf beyond end of buffer " + "(%d requested, %d available)\n", + len, avail); +} + +/* + * Verify buffer against the FCM Controller Data Buffer + */ +static int fsl_elbc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int i; + + if (len < 0) { + printf("write_buf of %d bytes", len); + return -EINVAL; + } + + if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { + printf("verify_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, ctrl->read_bytes - ctrl->index); + + ctrl->index = ctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) + break; + + ctrl->index += len; + return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + lbus83xx_t *lbc = ctrl->regs; + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ctrl->use_mdr = 0; + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return fsl_elbc_read_byte(mtd); +} + +static int fsl_elbc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf) +{ + fsl_elbc_read_buf(mtd, buf, mtd->writesize); + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static void fsl_elbc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + ctrl->oob_poi = chip->oob_poi; +} + +static struct fsl_elbc_ctrl *elbc_ctrl; + +static void fsl_elbc_ctrl_init(void) +{ + immap_t *im = (immap_t *)CFG_IMMR; + + elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL); + if (!elbc_ctrl) + return; + + elbc_ctrl->regs = &im->lbus; + + /* clear event registers */ + out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK); + out_be32(&elbc_ctrl->regs->lteatr, 0); + + /* Enable interrupts for any detected events */ + out_be32(&elbc_ctrl->regs->lteir, LTESR_NAND_MASK); + + elbc_ctrl->read_bytes = 0; + elbc_ctrl->index = 0; + elbc_ctrl->addr = NULL; +} + +int board_nand_init(struct nand_chip *nand) +{ + struct fsl_elbc_mtd *priv; + uint32_t br, or; + + if (!elbc_ctrl) { + fsl_elbc_ctrl_init(); + if (!elbc_ctrl) + return -1; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ctrl = elbc_ctrl; + priv->vbase = nand->IO_ADDR_R; + + /* Find which chip select it is connected to. It'd be nice + * if we could pass more than one datum to the NAND driver... + */ + for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { + br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br); + or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + + if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM && + (br & or & BR_BA) == (phys_addr_t)nand->IO_ADDR_R) + break; + } + + if (priv->bank >= MAX_BANKS) { + printf("fsl_elbc_nand: address did not match any " + "chip selects\n"); + return -ENODEV; + } + + elbc_ctrl->chips[priv->bank] = priv; + + /* fill in nand_chip structure */ + /* set up function call table */ + nand->read_byte = fsl_elbc_read_byte; + nand->write_buf = fsl_elbc_write_buf; + nand->read_buf = fsl_elbc_read_buf; + nand->verify_buf = fsl_elbc_verify_buf; + nand->select_chip = fsl_elbc_select_chip; + nand->cmdfunc = fsl_elbc_cmdfunc; + nand->waitfunc = fsl_elbc_wait; + + /* set up nand options */ + nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + + nand->controller = &elbc_ctrl->controller; + nand->priv = priv; + + nand->ecc.read_page = fsl_elbc_read_page; + nand->ecc.write_page = fsl_elbc_write_page; + + /* If CS Base Register selects full hardware ECC then use it */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.mode = NAND_ECC_HW; + + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_sp_eccm1 : + &fsl_elbc_oob_sp_eccm0; + + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.steps = 1; + } else { + /* otherwise fall back to default software ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + } + + priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); + + /* adjust Option Register and ECC to match Flash page size */ + if (or & OR_FCM_PGS) { + priv->page_size = 1; + + /* adjust ecc setup if needed */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.steps = 4; + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_lp_eccm1 : + &fsl_elbc_oob_lp_eccm0; + } + } + + return 0; +} -- cgit v1.2.3 From 9c814b0a716aae884bec977b9a032dfa59cfb79a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 28 Mar 2008 22:10:54 +0300 Subject: fsl_elbc_nand: workaround for hangs during nand write Using current driver elbc sometimes hangs during nand write. Reading back last byte helps though (thanks to Scott Wood for the idea). Signed-off-by: Anton Vorontsov Signed-off-by: Scott Wood --- drivers/mtd/nand/fsl_elbc_nand.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index c1644c0f6af..ab3e0fdf564 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -489,7 +489,7 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) struct fsl_elbc_ctrl *ctrl = priv->ctrl; unsigned int bufsize = mtd->writesize + mtd->oobsize; - if (len < 0) { + if (len <= 0) { printf("write_buf of %d bytes", len); ctrl->status = 0; return; @@ -503,6 +503,15 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) } memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + /* + * This is workaround for the weird elbc hangs during nand write, + * Scott Wood says: "...perhaps difference in how long it takes a + * write to make it through the localbus compared to a write to IMMR + * is causing problems, and sync isn't helping for some reason." + * Reading back the last byte helps though. + */ + in_8(&ctrl->addr[ctrl->index] + len - 1); + ctrl->index += len; } -- cgit v1.2.3 From 300253306acc72b1b2e9faf0987f86551151d7cf Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 22 May 2008 15:02:46 -0500 Subject: fsl_elbc_nand: Hard-code the FBAR/FPAR split. The hardware has separate registers for block and page-within-block, but the division between the two has no apparent relation to the actual erase block size of the NAND chip. Signed-off-by: Scott Wood --- drivers/mtd/nand/fsl_elbc_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index ab3e0fdf564..0bd1bdbcf00 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -138,15 +138,14 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) ctrl->page = page_addr; - out_be32(&lbc->fbar, - page_addr >> (chip->phys_erase_shift - chip->page_shift)); - if (priv->page_size) { + out_be32(&lbc->fbar, page_addr >> 6); out_be32(&lbc->fpar, ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | (oob ? FPAR_LP_MS : 0) | column); buf_num = (page_addr & 1) << 2; } else { + out_be32(&lbc->fbar, page_addr >> 5); out_be32(&lbc->fpar, ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | (oob ? FPAR_SP_MS : 0) | column); -- cgit v1.2.3 From e1c3dbada349992875934575c97b328ab2cb33ca Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 12 Jun 2008 11:10:21 -0500 Subject: nand: fsl_upm: convert to updated MTD NAND infrastructure Signed-off-by: Anton Vorontsov Signed-off-by: Scott Wood --- drivers/mtd/nand/fsl_upm.c | 68 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 67ae9c8d5b1..e651903040b 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -20,8 +20,6 @@ #include #include -static int fsl_upm_in_pattern; - static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) { clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); @@ -51,49 +49,38 @@ static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, u32 cmd) } } -static void nand_hwcontrol (struct mtd_info *mtd, int cmd) +static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *chip = mtd->priv; struct fsl_upm_nand *fun = chip->priv; - switch (cmd) { - case NAND_CTL_SETCLE: - fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); - fsl_upm_in_pattern++; - break; - case NAND_CTL_SETALE: - fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); - fsl_upm_in_pattern++; - break; - case NAND_CTL_CLRCLE: - case NAND_CTL_CLRALE: + if (!(ctrl & fun->last_ctrl)) { fsl_upm_end_pattern(&fun->upm); - fsl_upm_in_pattern--; - break; + + if (cmd == NAND_CMD_NONE) + return; + + fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); } -} -static void nand_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *chip = mtd->priv; + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + else if (ctrl & NAND_CLE) + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + } - if (fsl_upm_in_pattern) { - struct fsl_upm_nand *fun = chip->priv; - - fsl_upm_run_pattern(&fun->upm, fun->width, byte); - - /* - * Some boards/chips needs this. At least on MPC8360E-RDK we - * need it. Probably weird chip, because I don't see any need - * for this on MPC8555E + Samsung K9F1G08U0A. Usually here are - * 0-2 unexpected busy states per block read. - */ - if (fun->wait_pattern) { - while (!fun->dev_ready()) - debug("unexpected busy state\n"); - } - } else { - out_8(chip->IO_ADDR_W, byte); + fsl_upm_run_pattern(&fun->upm, fun->width, cmd); + + /* + * Some boards/chips needs this. At least on MPC8360E-RDK we + * need it. Probably weird chip, because I don't see any need + * for this on MPC8555E + Samsung K9F1G08U0A. Usually here are + * 0-2 unexpected busy states per block read. + */ + if (fun->wait_pattern) { + while (!fun->dev_ready()) + debug("unexpected busy state\n"); } } @@ -148,13 +135,14 @@ int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) if (fun->width != 8 && fun->width != 16 && fun->width != 32) return -ENOSYS; + fun->last_ctrl = NAND_CLE; + chip->priv = fun; chip->chip_delay = fun->chip_delay; - chip->eccmode = NAND_ECC_SOFT; - chip->hwcontrol = nand_hwcontrol; + chip->ecc.mode = NAND_ECC_SOFT; + chip->cmd_ctrl = fun_cmd_ctrl; chip->read_byte = nand_read_byte; chip->read_buf = nand_read_buf; - chip->write_byte = nand_write_byte; chip->write_buf = nand_write_buf; chip->verify_buf = nand_verify_buf; if (fun->dev_ready) -- cgit v1.2.3 From dfbf617ff055e4216f78d358b0867c548916d14b Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 12 Jun 2008 13:20:16 -0500 Subject: NAND read/write fix Implement block-skipping read/write, based on a patch from Morten Ebbell Hestens . Signed-off-by: Morten Ebbell Hestnes Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_util.c | 762 ++++++++++--------------------------------- 1 file changed, 181 insertions(+), 581 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 9fe486698c1..02fe914db8b 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -248,586 +248,6 @@ static struct nand_ecclayout autoplace_ecclayout = { }; #endif - -/** - * nand_fill_oob - [Internal] Transfer client buffer to oob - * @chip: nand chip structure - * @oob: oob data buffer - * @ops: oob ops structure - * - * Copied from nand_base.c - */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) -{ - size_t len = ops->ooblen; - - switch(ops->mode) { - - case MTD_OOB_PLACE: - case MTD_OOB_RAW: - memcpy(chip->oob_poi + ops->ooboffs, oob, len); - return oob + len; - - case MTD_OOB_AUTO: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, woffs = ops->ooboffs; - size_t bytes = 0; - - for(; free->length && len; free++, len -= bytes) { - /* Write request not from offset 0 ? */ - if (unlikely(woffs)) { - if (woffs >= free->length) { - woffs -= free->length; - continue; - } - boffs = free->offset + woffs; - bytes = min_t(size_t, len, - (free->length - woffs)); - woffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(chip->oob_poi + boffs, oob, bytes); - oob += bytes; - } - return oob; - } - default: - BUG(); - } - return NULL; -} - -#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 - - -/* copied from nand_base.c: nand_do_write_ops() - * Only very small changes - */ -int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) -{ - int chipnr, realpage, page, blockmask, column; - struct nand_chip *chip = mtd->priv; - uint32_t writelen = ops->len; - uint8_t *oob = ops->oobbuf; - uint8_t *buf = ops->datbuf; - int ret, subpage; - - ops->retlen = 0; - if (!writelen) - return 0; - - printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); - - /* reject writes, which are not page aligned */ - if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { - printk(KERN_NOTICE "nand_write: " - "Attempt to write not page aligned data\n"); - return -EINVAL; - } - - column = to & (mtd->writesize - 1); - subpage = column || (writelen & (mtd->writesize - 1)); - - if (subpage && oob) { - printk(KERN_NOTICE "nand_write: " - "Attempt to write oob to subpage\n"); - return -EINVAL; - } - - chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* XXX U-BOOT XXX */ -#if 0 - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) - return -EIO; -#endif - - realpage = (int)(to >> chip->page_shift); - page = realpage & chip->pagemask; - blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - - /* Invalidate the page cache, when we write to the cached page */ - if (to <= (chip->pagebuf << chip->page_shift) && - (chip->pagebuf << chip->page_shift) < (to + ops->len)) - chip->pagebuf = -1; - - /* If we're not given explicit OOB data, let it be 0xFF */ - if (likely(!oob)) { - printf("!oob, writing %d bytes with 0xff to chip->oob_poi (0x%08x)\n", mtd->oobsize, chip->oob_poi); - memset(chip->oob_poi, 0xff, mtd->oobsize); - } - - while(1) { - int bytes = mtd->writesize; - int cached = writelen > bytes && page != blockmask; - uint8_t *wbuf = buf; - - /* Partial page write ? */ - if (unlikely(column || writelen < (mtd->writesize - 1))) { - cached = 0; - bytes = min_t(int, bytes - column, (int) writelen); - chip->pagebuf = -1; - memset(chip->buffers->databuf, 0xff, mtd->writesize); - memcpy(&chip->buffers->databuf[column], buf, bytes); - wbuf = chip->buffers->databuf; - } - - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); - - ret = chip->write_page(mtd, chip, wbuf, page, cached, - (ops->mode == MTD_OOB_RAW)); - if (ret) - break; - - writelen -= bytes; - if (!writelen) - break; - - column = 0; - buf += bytes; - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - - ops->retlen = ops->len - writelen; - if (unlikely(oob)) - ops->oobretlen = ops->ooblen; - return ret; -} - -/* XXX U-BOOT XXX */ -#if 0 -/** - * nand_write_opts: - write image to NAND flash with support for various options - * - * @param meminfo NAND device to erase - * @param opts write options (@see nand_write_options) - * @return 0 in case of success - * - * This code is ported from nandwrite.c from Linux mtd utils by - * Steven J. Hill and Thomas Gleixner. - */ -int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) -{ - int imglen = 0; - int pagelen; - int baderaseblock; - int blockstart = -1; - loff_t offs; - int readlen; - int ecclayoutchanged = 0; - int percent_complete = -1; - struct nand_ecclayout old_ecclayout; - ulong mtdoffset = opts->offset; - ulong erasesize_blockalign; - u_char *buffer = opts->buffer; - size_t written; - int result; - - if (opts->pad && opts->writeoob) { - printf("Can't pad when oob data is present.\n"); - return -1; - } - - /* set erasesize to specified number of blocks - to match - * jffs2 (virtual) block size */ - if (opts->blockalign == 0) { - erasesize_blockalign = meminfo->erasesize; - } else { - erasesize_blockalign = meminfo->erasesize * opts->blockalign; - } - - /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) - && !(meminfo->oobsize == 8 && meminfo->writesize == 256) - && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - /* read the current oob info */ - memcpy(&old_ecclayout, &meminfo->ecclayout, sizeof(old_ecclayout)); - - /* write without ecc? */ - if (opts->noecc) { - memcpy(&meminfo->ecclayout, &none_ecclayout, - sizeof(meminfo->ecclayout)); - ecclayoutchanged = 1; - } - - /* autoplace ECC? */ - if (opts->autoplace && (old_ecclayout.useecc != MTD_NANDECC_AUTOPLACE)) { - - memcpy(&meminfo->ecclayout, &autoplace_ecclayout, - sizeof(meminfo->ecclayout)); - ecclayoutchanged = 1; - } - - /* force OOB layout for jffs2 or yaffs? */ - if (opts->forcejffs2 || opts->forceyaffs) { - struct nand_ecclayout *oobsel = - opts->forcejffs2 ? &jffs2_ecclayout : &yaffs_ecclayout; - - if (meminfo->oobsize == 8) { - if (opts->forceyaffs) { - printf("YAFSS cannot operate on " - "256 Byte page size\n"); - goto restoreoob; - } - /* Adjust number of ecc bytes */ - jffs2_ecclayout.eccbytes = 3; - } - - memcpy(&meminfo->ecclayout, oobsel, sizeof(meminfo->ecclayout)); - } - - /* get image length */ - imglen = opts->length; - pagelen = meminfo->writesize - + ((opts->writeoob != 0) ? meminfo->oobsize : 0); - - /* check, if file is pagealigned */ - if ((!opts->pad) && ((imglen % pagelen) != 0)) { - printf("Input block length is not page aligned\n"); - goto restoreoob; - } - - /* check, if length fits into device */ - if (((imglen / pagelen) * meminfo->writesize) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->writesize, meminfo->size); - printf("Input block does not fit into device\n"); - goto restoreoob; - } - - if (!opts->quiet) - printf("\n"); - - /* get data from input and write to the device */ - while (imglen && (mtdoffset < meminfo->size)) { - - WATCHDOG_RESET (); - - /* - * new eraseblock, check for bad block(s). Stay in the - * loop to be sure if the offset changes because of - * a bad block, that the next block that will be - * written to is also checked. Thus avoiding errors if - * the block(s) after the skipped block(s) is also bad - * (number of blocks depending on the blockalign - */ - while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { - blockstart = mtdoffset & (~erasesize_blockalign+1); - offs = blockstart; - baderaseblock = 0; - - /* check all the blocks in an erase block for - * bad blocks */ - do { - int ret = meminfo->block_isbad(meminfo, offs); - - if (ret < 0) { - printf("Bad block check failed\n"); - goto restoreoob; - } - if (ret == 1) { - baderaseblock = 1; - if (!opts->quiet) - printf("\rBad block at 0x%lx " - "in erase block from " - "0x%x will be skipped\n", - (long) offs, - blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart - + erasesize_blockalign; - } - offs += erasesize_blockalign - / opts->blockalign; - } while (offs < blockstart + erasesize_blockalign); - } - - readlen = meminfo->writesize; - if (opts->pad && (imglen < readlen)) { - readlen = imglen; - memset(data_buf + readlen, 0xff, - meminfo->writesize - readlen); - } - - /* read page data from input memory buffer */ - memcpy(data_buf, buffer, readlen); - buffer += readlen; - - if (opts->writeoob) { - /* read OOB data from input memory block, exit - * on failure */ - memcpy(oob_buf, buffer, meminfo->oobsize); - buffer += meminfo->oobsize; - - /* write OOB data first, as ecc will be placed - * in there*/ - result = meminfo->write_oob(meminfo, - mtdoffset, - meminfo->oobsize, - &written, - (unsigned char *) - &oob_buf); - - if (result != 0) { - printf("\nMTD writeoob failure: %d\n", - result); - goto restoreoob; - } - imglen -= meminfo->oobsize; - } - - /* write out the page data */ - result = meminfo->write(meminfo, - mtdoffset, - meminfo->writesize, - &written, - (unsigned char *) &data_buf); - - if (result != 0) { - printf("writing NAND page at offset 0x%lx failed\n", - mtdoffset); - goto restoreoob; - } - imglen -= readlen; - - if (!opts->quiet) { - unsigned long long n = (unsigned long long) - (opts->length-imglen) * 100; - int percent; - - do_div(n, opts->length); - percent = (int)n; - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - printf("\rWriting data at 0x%lx " - "-- %3d%% complete.", - mtdoffset, percent); - percent_complete = percent; - } - } - - mtdoffset += meminfo->writesize; - } - - if (!opts->quiet) - printf("\n"); - -restoreoob: - if (ecclayoutchanged) { - memcpy(&meminfo->ecclayout, &old_ecclayout, - sizeof(meminfo->ecclayout)); - } - - if (imglen > 0) { - printf("Data did not fit into device, due to bad blocks\n"); - return -1; - } - - /* return happy */ - return 0; -} - -/** - * nand_read_opts: - read image from NAND flash with support for various options - * - * @param meminfo NAND device to erase - * @param opts read options (@see struct nand_read_options) - * @return 0 in case of success - * - */ -int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) -{ - int imglen = opts->length; - int pagelen; - int baderaseblock; - int blockstart = -1; - int percent_complete = -1; - loff_t offs; - size_t readlen; - ulong mtdoffset = opts->offset; - u_char *buffer = opts->buffer; - int result; - - /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) - && !(meminfo->oobsize == 8 && meminfo->writesize == 256) - && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - pagelen = meminfo->writesize - + ((opts->readoob != 0) ? meminfo->oobsize : 0); - - /* check, if length is not larger than device */ - if (((imglen / pagelen) * meminfo->writesize) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->writesize, meminfo->size); - printf("Input block is larger than device\n"); - return -1; - } - - if (!opts->quiet) - printf("\n"); - - /* get data from input and write to the device */ - while (imglen && (mtdoffset < meminfo->size)) { - - WATCHDOG_RESET (); - - /* - * new eraseblock, check for bad block(s). Stay in the - * loop to be sure if the offset changes because of - * a bad block, that the next block that will be - * written to is also checked. Thus avoiding errors if - * the block(s) after the skipped block(s) is also bad - * (number of blocks depending on the blockalign - */ - while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { - blockstart = mtdoffset & (~meminfo->erasesize+1); - offs = blockstart; - baderaseblock = 0; - - /* check all the blocks in an erase block for - * bad blocks */ - do { - int ret = meminfo->block_isbad(meminfo, offs); - - if (ret < 0) { - printf("Bad block check failed\n"); - return -1; - } - if (ret == 1) { - baderaseblock = 1; - if (!opts->quiet) - printf("\rBad block at 0x%lx " - "in erase block from " - "0x%x will be skipped\n", - (long) offs, - blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart - + meminfo->erasesize; - } - offs += meminfo->erasesize; - - } while (offs < blockstart + meminfo->erasesize); - } - - - /* read page data to memory buffer */ - result = meminfo->read(meminfo, - mtdoffset, - meminfo->writesize, - &readlen, - (unsigned char *) &data_buf); - - if (result != 0) { - printf("reading NAND page at offset 0x%lx failed\n", - mtdoffset); - return -1; - } - - if (imglen < readlen) { - readlen = imglen; - } - - memcpy(buffer, data_buf, readlen); - buffer += readlen; - imglen -= readlen; - - if (opts->readoob) { - result = meminfo->read_oob(meminfo, - mtdoffset, - meminfo->oobsize, - &readlen, - (unsigned char *) - &oob_buf); - - if (result != 0) { - printf("\nMTD readoob failure: %d\n", - result); - return -1; - } - - - if (imglen < readlen) { - readlen = imglen; - } - - memcpy(buffer, oob_buf, readlen); - - buffer += readlen; - imglen -= readlen; - } - - if (!opts->quiet) { - unsigned long long n = (unsigned long long) - (opts->length-imglen) * 100; - int percent; - - do_div(n, opts->length); - percent = (int)n; - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - if (!opts->quiet) - printf("\rReading data from 0x%lx " - "-- %3d%% complete.", - mtdoffset, percent); - percent_complete = percent; - } - } - - mtdoffset += meminfo->writesize; - } - - if (!opts->quiet) - printf("\n"); - - if (imglen > 0) { - printf("Could not read entire image due to bad blocks\n"); - return -1; - } - - /* return happy */ - return 0; -} -#endif - /* XXX U-BOOT XXX */ #if 0 /****************************************************************************** @@ -1007,4 +427,184 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) } #endif -#endif +/** + * get_len_incl_bad + * + * Check if length including bad blocks fits into device. + * + * @param nand NAND device + * @param offset offset in flash + * @param length image length + * @return image length including bad blocks + */ +static size_t get_len_incl_bad (nand_info_t *nand, size_t offset, + const size_t length) +{ + size_t len_incl_bad = 0; + size_t len_excl_bad = 0; + size_t block_len; + + while (len_excl_bad < length) { + block_len = nand->erasesize - (offset & (nand->erasesize - 1)); + + if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) + len_excl_bad += block_len; + + len_incl_bad += block_len; + offset += block_len; + + if ((offset + len_incl_bad) >= nand->size) + break; + } + + return len_incl_bad; +} + +/** + * nand_write_skip_bad: + * + * Write image to NAND flash. + * Blocks that are marked bad are skipped and the is written to the next + * block instead as long as the image is short enough to fit even after + * skipping the bad blocks. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length + * @param buf buffer to read from + * @return 0 in case of success + */ +int nand_write_skip_bad(nand_info_t *nand, size_t offset, size_t *length, + u_char *buffer) +{ + int rval; + size_t left_to_write = *length; + size_t len_incl_bad; + u_char *p_buffer = buffer; + + /* Reject writes, which are not page aligned */ + if ((offset & (nand->writesize - 1)) != 0 || + (*length & (nand->writesize - 1)) != 0) { + printf ("Attempt to write non page aligned data\n"); + return -EINVAL; + } + + len_incl_bad = get_len_incl_bad (nand, offset, *length); + + if ((offset + len_incl_bad) >= nand->size) { + printf ("Attempt to write outside the flash area\n"); + return -EINVAL; + } + + if (len_incl_bad == *length) { + rval = nand_write (nand, offset, length, buffer); + if (rval != 0) { + printf ("NAND write to offset %x failed %d\n", + offset, rval); + return rval; + } + } + + while (left_to_write > 0) { + size_t block_offset = offset & (nand->erasesize - 1); + size_t write_size; + + if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { + printf ("Skip bad block 0x%08x\n", + offset & ~(nand->erasesize - 1)); + offset += nand->erasesize - block_offset; + continue; + } + + if (left_to_write < (nand->erasesize - block_offset)) + write_size = left_to_write; + else + write_size = nand->erasesize - block_offset; + + rval = nand_write (nand, offset, &write_size, p_buffer); + if (rval != 0) { + printf ("NAND write to offset %x failed %d\n", + offset, rval); + *length -= left_to_write; + return rval; + } + + left_to_write -= write_size; + offset += write_size; + p_buffer += write_size; + } + + return 0; +} + +/** + * nand_read_skip_bad: + * + * Read image from NAND flash. + * Blocks that are marked bad are skipped and the next block is readen + * instead as long as the image is short enough to fit even after skipping the + * bad blocks. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length, on return holds remaining bytes to read + * @param buffer buffer to write to + * @return 0 in case of success + */ +int nand_read_skip_bad(nand_info_t *nand, size_t offset, size_t *length, + u_char *buffer) +{ + int rval; + size_t left_to_read = *length; + size_t len_incl_bad; + u_char *p_buffer = buffer; + + len_incl_bad = get_len_incl_bad (nand, offset, *length); + + if ((offset + len_incl_bad) >= nand->size) { + printf ("Attempt to read outside the flash area\n"); + return -EINVAL; + } + + if (len_incl_bad == *length) { + rval = nand_read (nand, offset, length, buffer); + if (rval != 0) { + printf ("NAND read from offset %x failed %d\n", + offset, rval); + return rval; + } + } + + while (left_to_read > 0) { + size_t block_offset = offset & (nand->erasesize - 1); + size_t read_length; + + if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { + printf ("Skipping bad block 0x%08x\n", + offset & ~(nand->erasesize - 1)); + offset += nand->erasesize - block_offset; + continue; + } + + if (left_to_read < (nand->erasesize - block_offset)) + read_length = left_to_read; + else + read_length = nand->erasesize - block_offset; + + rval = nand_read (nand, offset, &read_length, p_buffer); + if (rval != 0) { + printf ("NAND read from offset %x failed %d\n", + offset, rval); + *length -= left_to_read; + return rval; + } + + left_to_read -= read_length; + offset += read_length; + p_buffer += read_length; + } + + return 0; +} + +#endif /* defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */ -- cgit v1.2.3 From 13f0fd94e3cae6f8a0d9fba5d367e311edc8ebde Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Mon, 30 Jun 2008 15:34:40 +0200 Subject: NAND: Scan bad blocks lazily. Rather than scanning on boot, scan upon the first attempt to check the badness of a block. This speeds up boot when not using NAND, and reduces the likelihood of needing to reflash via JTAG if NAND becomes nonfunctional. Signed-off-by: Ilya Yanok Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_base.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7bceea8ac3d..cf2f3745542 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -457,6 +457,11 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, { struct nand_chip *chip = mtd->priv; + if (!(chip->options & NAND_BBT_SCANNED)) { + chip->scan_bbt(mtd); + chip->options |= NAND_BBT_SCANNED; + } + if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); @@ -2721,10 +2726,9 @@ int nand_scan_tail(struct mtd_info *mtd) /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) - return 0; + chip->options |= NAND_BBT_SCANNED; - /* Build bad block table */ - return chip->scan_bbt(mtd); + return 0; } /* module_text_address() isn't exported, and it's mostly a pointless -- cgit v1.2.3 From eafcabd15f00c142156235c519fcc55b10993241 Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Sun, 22 Jun 2008 16:30:06 +0200 Subject: NAND: chip->state does not always get set. Fixes an issue with chip->state not always being set causing troubles. Signed-off-by: Marcel Ziswiler Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_base.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index cf2f3745542..a29ff1146f2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -773,6 +773,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) #else static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) { + this->state = new_state; return 0; } #endif -- cgit v1.2.3 From 195ccfc5991d48764b2519941e3507f693851d5d Mon Sep 17 00:00:00 2001 From: Fathi BOUDRA Date: Wed, 6 Aug 2008 10:06:20 +0200 Subject: OneNAND: Fill in MTD function pointers for OneNAND. onenand_print_device_info(): - Now returns a string to be placed in mtd->name, rather than calling printf. - Remove verbose parameter as it becomes useless. Signed-off-by: Fathi Boudra Signed-off-by: Scott Wood --- drivers/mtd/onenand/onenand_base.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a7054aebca6..ded1706abbe 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -19,6 +19,7 @@ #include #include +#include /* It should access 16-bit instead of 8-bit */ static inline void *memcpy_16(void *dst, const void *src, unsigned int len) @@ -1110,21 +1111,21 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) * * Print device ID */ -void onenand_print_device_info(int device, int verbose) +char * onenand_print_device_info(int device) { int vcc, demuxed, ddp, density; - - if (!verbose) - return; + char *dev_info = malloc(80); vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", + sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)", demuxed ? "" : "Muxed ", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", device); + + return dev_info; } static const struct onenand_manufacturers onenand_manuf_ids[] = { @@ -1203,7 +1204,7 @@ static int onenand_probe(struct mtd_info *mtd) } /* Flash device information */ - onenand_print_device_info(dev_id, 0); + mtd->name = onenand_print_device_info(dev_id); this->device_id = dev_id; density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; @@ -1239,6 +1240,17 @@ static int onenand_probe(struct mtd_info *mtd) this->options |= ONENAND_CONT_LOCK; } + mtd->erase = onenand_erase; + mtd->read = onenand_read; + mtd->write = onenand_write; + mtd->read_ecc = onenand_read_ecc; + mtd->write_ecc = onenand_write_ecc; + mtd->read_oob = onenand_read_oob; + mtd->write_oob = onenand_write_oob; + mtd->sync = onenand_sync; + mtd->block_isbad = onenand_block_isbad; + mtd->block_markbad = onenand_block_markbad; + return 0; } -- cgit v1.2.3 From 6c58a030f86829fa4f0d4337cf4b794c41a1823e Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:38 +0200 Subject: serial: move CFG_SCIF_CONSOLE to CONFIG_SCIF_CONSOLE move also conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/serial/Makefile | 2 +- drivers/serial/serial_sh.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index de6fbab740c..3716974c62e 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -35,7 +35,7 @@ COBJS-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o COBJS-y += serial_pl010.o COBJS-y += serial_pl011.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o -COBJS-y += serial_sh.o +COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o COBJS-$(CONFIG_USB_TTY) += usbtty.o COBJS := $(COBJS-y) diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index 0801ac4a8c8..2b9eeed47e0 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -20,8 +20,6 @@ #include #include -#ifdef CFG_SCIF_CONSOLE - #if defined (CONFIG_CONS_SCIF0) #define SCIF_BASE SCIF0_BASE #elif defined (CONFIG_CONS_SCIF1) @@ -215,5 +213,3 @@ int serial_getc(void) return ch; } - -#endif /* CFG_SCIF_CONSOLE */ -- cgit v1.2.3 From 1a6ffbfaf4353bec379ed1fcfc54b6f1a30af09a Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:39 +0200 Subject: serial: move CFG_NS9750_UART to CONFIG_NS9750_UART move also conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/serial/Makefile | 2 +- drivers/serial/ns9750_serial.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 3716974c62e..2384735a2ce 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -27,7 +27,7 @@ LIB := $(obj)libserial.a COBJS-$(CONFIG_ATMEL_USART) += atmel_usart.o COBJS-$(CONFIG_MCFUART) += mcfuart.o -COBJS-y += ns9750_serial.o +COBJS-$(CONFIG_NS9750_UART) += ns9750_serial.o COBJS-y += ns16550.o COBJS-$(CONFIG_DRIVER_S3C4510_UART) += s3c4510b_uart.o COBJS-y += serial.o diff --git a/drivers/serial/ns9750_serial.c b/drivers/serial/ns9750_serial.c index 02c0d395209..e9645a053de 100644 --- a/drivers/serial/ns9750_serial.c +++ b/drivers/serial/ns9750_serial.c @@ -28,8 +28,6 @@ #include -#ifdef CFG_NS9750_UART - #include "ns9750_bbus.h" /* for GPIOs */ #include "ns9750_ser.h" /* for serial configuration */ @@ -210,5 +208,3 @@ static unsigned int calcRxCharGapRegister( void ) { return NS9750_SER_RX_CHAR_TIMER_TRUN; } - -#endif /* CFG_NS9750_UART */ -- cgit v1.2.3 From 1a02806c4b1b4a09ad4e95d3aac3783889e5f8d7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:39 +0200 Subject: drivers/block: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/block/Makefile | 10 +++++----- drivers/block/ahci.c | 3 --- drivers/block/ata_piix.c | 4 ---- drivers/block/sil680.c | 5 +---- drivers/block/sym53c8xx.c | 5 ----- drivers/block/systemace.c | 3 --- 6 files changed, 6 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/block/Makefile b/drivers/block/Makefile index a09cd2a603b..642582b0881 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -25,14 +25,14 @@ include $(TOPDIR)/config.mk LIB := $(obj)libblock.a -COBJS-y += ahci.o -COBJS-y += ata_piix.o +COBJS-$(CONFIG_SCSI_AHCI) += ahci.o +COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o COBJS-$(CONFIG_LIBATA) += libata.o COBJS-$(CONFIG_SATA_SIL3114) += sata_sil3114.o -COBJS-y += sil680.o -COBJS-y += sym53c8xx.o -COBJS-y += systemace.o +COBJS-$(CONFIG_IDE_SIL680) += sil680.o +COBJS-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o +COBJS-$(CONFIG_SYSTEMACE) += systemace.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 3d82c625a35..52fd1080e61 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -26,8 +26,6 @@ */ #include -#ifdef CONFIG_SCSI_AHCI - #include #include #include @@ -700,4 +698,3 @@ void scsi_print_error(ccb * pccb) { /*The ahci error info can be read in the ahci driver*/ } -#endif diff --git a/drivers/block/ata_piix.c b/drivers/block/ata_piix.c index 441a4dcd8d6..4c26b36f5ab 100644 --- a/drivers/block/ata_piix.c +++ b/drivers/block/ata_piix.c @@ -35,8 +35,6 @@ #include #include -#ifdef CFG_ATA_PIIX /*ata_piix driver */ - extern block_dev_desc_t sata_dev_desc[CFG_SATA_MAX_DEVICE]; extern int curr_device; @@ -756,5 +754,3 @@ int scan_sata(int dev) { return 0; } - -#endif diff --git a/drivers/block/sil680.c b/drivers/block/sil680.c index a6143df4c06..052c3d3671a 100644 --- a/drivers/block/sil680.c +++ b/drivers/block/sil680.c @@ -27,7 +27,7 @@ * The following parameters must be defined in the configuration file * of the target board: * - * #define CFG_IDE_SIL680 + * #define CONFIG_IDE_SIL680 * * #define CONFIG_PCI_PNP * NOTE it may also be necessary to define this if the default of 8 is @@ -54,7 +54,6 @@ */ #include -#if defined(CFG_IDE_SIL680) #include #include #include @@ -106,5 +105,3 @@ int ide_preinit (void) void ide_set_reset (int flag) { return; } - -#endif /* CFG_IDE_SIL680 */ diff --git a/drivers/block/sym53c8xx.c b/drivers/block/sym53c8xx.c index b880435f1ec..44e998b5564 100644 --- a/drivers/block/sym53c8xx.c +++ b/drivers/block/sym53c8xx.c @@ -35,8 +35,6 @@ #include -#ifdef CONFIG_SCSI_SYM53C8XX - #include #include #include @@ -870,6 +868,3 @@ void scsi_chip_init(void) #endif } #endif - - -#endif /* CONFIG_SCSI_SYM53C8XX */ diff --git a/drivers/block/systemace.c b/drivers/block/systemace.c index 7d82c27c6ed..dfaab528bf4 100644 --- a/drivers/block/systemace.c +++ b/drivers/block/systemace.c @@ -44,8 +44,6 @@ #include #include -#ifdef CONFIG_SYSTEMACE - /* * The ace_readw and writew functions read/write 16bit words, but the * offset value is the BYTE offset as most used in the Xilinx @@ -255,4 +253,3 @@ static unsigned long systemace_read(int dev, unsigned long start, return blkcnt; } -#endif /* CONFIG_SYSTEMACE */ -- cgit v1.2.3 From 88f57e093114a44aa9a858d52b099bcc52034a8c Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:39 +0200 Subject: drivers/dma: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/dma/MCD_dmaApi.c | 3 --- drivers/dma/MCD_tasks.c | 4 ---- drivers/dma/MCD_tasksInit.c | 4 ---- drivers/dma/Makefile | 2 +- 4 files changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/MCD_dmaApi.c b/drivers/dma/MCD_dmaApi.c index b0062b77329..5c95651d580 100644 --- a/drivers/dma/MCD_dmaApi.c +++ b/drivers/dma/MCD_dmaApi.c @@ -24,8 +24,6 @@ #include -#ifdef CONFIG_FSLDMAFEC - #include #include #include @@ -1023,4 +1021,3 @@ static void MCD_memcpy(int *dest, int *src, u32 size) for (i = 0; i < size; i += sizeof(int), dest++, src++) *dest = *src; } -#endif /* CONFIG_FSLDMAFEC */ diff --git a/drivers/dma/MCD_tasks.c b/drivers/dma/MCD_tasks.c index 06a2d532648..4f6e3462614 100644 --- a/drivers/dma/MCD_tasks.c +++ b/drivers/dma/MCD_tasks.c @@ -24,8 +24,6 @@ #include -#ifdef CONFIG_FSLDMAFEC - #include u32 MCD_varTab0[]; @@ -2430,5 +2428,3 @@ u32 MCD_ENetXmit_TDT[] = { #ifdef MCD_INCLUDE_EU MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; #endif - -#endif /* CONFIG_FSLDMAFEC */ diff --git a/drivers/dma/MCD_tasksInit.c b/drivers/dma/MCD_tasksInit.c index cf567db5a0b..2f198754e58 100644 --- a/drivers/dma/MCD_tasksInit.c +++ b/drivers/dma/MCD_tasksInit.c @@ -28,8 +28,6 @@ * Do not edit! */ -#ifdef CONFIG_FSLDMAFEC - #include extern dmaRegs *MCD_dmaBar; @@ -242,5 +240,3 @@ void MCD_startDmaENetXmit(char *bDBase, char *currBD, char *xmitFifoPtr, /* Set the task's Enable bit in its Task Control Register */ MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; } - -#endif /* CONFIG_FSLDMAFEC */ diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 7e17360de54..cf29efa0272 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libdma.a -COBJS-y += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o +COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) -- cgit v1.2.3 From 65e41ea0548b86e3d7892defac8e4dc1ea70aed1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:40 +0200 Subject: drivers/input: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/input/Makefile | 8 +++++--- drivers/input/i8042.c | 4 ---- drivers/input/keyboard.c | 4 ---- drivers/input/pc_keyb.c | 4 ---- drivers/input/ps2mult.c | 4 ---- drivers/input/ps2ser.c | 4 ---- 6 files changed, 5 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 2933cb6451d..9a144073999 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -25,9 +25,11 @@ include $(TOPDIR)/config.mk LIB := $(obj)libinput.a -COBJS-y += i8042.o -COBJS-y += keyboard.o -COBJS-y += pc_keyb.o ps2ser.o ps2mult.o +COBJS-$(CONFIG_I8042_KBD) += i8042.o +ifdef CONFIG_PS2KBD +COBJS-y += keyboard.o pc_keyb.o +COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o +endif COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 22c2a4e3a02..d152768b8e0 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -27,8 +27,6 @@ #include -#ifdef CONFIG_I8042_KBD - #ifdef CONFIG_USE_CPCIDVI extern u8 gt_cpcidvi_in8(u32 offset); extern void gt_cpcidvi_out8(u32 offset, u8 data); @@ -670,5 +668,3 @@ static int kbd_reset (void) return 0; } - -#endif /* CONFIG_I8042_KBD */ diff --git a/drivers/input/keyboard.c b/drivers/input/keyboard.c index 54182a79b1d..a634d76d6c2 100644 --- a/drivers/input/keyboard.c +++ b/drivers/input/keyboard.c @@ -11,8 +11,6 @@ #include -#ifdef CONFIG_PS2KBD - #include #include @@ -301,5 +299,3 @@ int kbd_init (void) } return error; } - -#endif /* CONFIG_PS2KBD */ diff --git a/drivers/input/pc_keyb.c b/drivers/input/pc_keyb.c index 33e7c5f1248..25ad3e40650 100644 --- a/drivers/input/pc_keyb.c +++ b/drivers/input/pc_keyb.c @@ -13,8 +13,6 @@ #include -#ifdef CONFIG_PS2KBD - #include #include @@ -252,5 +250,3 @@ void pckbd_leds(unsigned char leds) kbd_send_data(KBD_CMD_SET_LEDS); kbd_send_data(leds); } - -#endif /* CONFIG_PS2KBD */ diff --git a/drivers/input/ps2mult.c b/drivers/input/ps2mult.c index 9515a0fbf54..ecd585323f9 100644 --- a/drivers/input/ps2mult.c +++ b/drivers/input/ps2mult.c @@ -16,8 +16,6 @@ #include -#ifdef CONFIG_PS2MULT - #include #include #include @@ -462,5 +460,3 @@ int ps2mult_request_irq(void (*handler)(void *)) return 0; } - -#endif /* CONFIG_PS2MULT */ diff --git a/drivers/input/ps2ser.c b/drivers/input/ps2ser.c index c1741eac67d..480ffa25a2b 100644 --- a/drivers/input/ps2ser.c +++ b/drivers/input/ps2ser.c @@ -15,8 +15,6 @@ #include -#ifdef CONFIG_PS2SERIAL - #include #include #include @@ -326,5 +324,3 @@ static void ps2ser_interrupt(void *dev_id) ps2mult_callback(atomic_read(&ps2buf_cnt)); } } - -#endif /* CONFIG_PS2SERIAL */ -- cgit v1.2.3 From 55d6d2d39fe3fe87802e399aa17539368b495d2e Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:40 +0200 Subject: drivers/misc: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/misc/Makefile | 6 +++--- drivers/misc/ali512x.c | 5 ----- drivers/misc/ns87308.c | 4 ---- drivers/misc/status_led.c | 4 ---- drivers/serial/serial.c | 4 ++-- 5 files changed, 5 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fe8d3d87e96..01e0f393d25 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -25,10 +25,10 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmisc.a -COBJS-y += ali512x.o -COBJS-y += ns87308.o -COBJS-y += status_led.o +COBJS-$(CONFIG_ALI152X) += ali512x.o COBJS-$(CONFIG_FSL_LAW) += fsl_law.o +COBJS-$(CONFIG_NS87308) += ns87308.o +COBJS-$(CONFIG_STATUS_LED) += status_led.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/misc/ali512x.c b/drivers/misc/ali512x.c index 90b45d98b8c..d6a2c1f5775 100644 --- a/drivers/misc/ali512x.c +++ b/drivers/misc/ali512x.c @@ -32,8 +32,6 @@ #include -#ifdef CONFIG_ALI152X - #include #include #include @@ -418,6 +416,3 @@ int ali512x_cio_in(int pin) return data & bit; } - - -#endif diff --git a/drivers/misc/ns87308.c b/drivers/misc/ns87308.c index cf4d3595e5d..6642c2e072d 100644 --- a/drivers/misc/ns87308.c +++ b/drivers/misc/ns87308.c @@ -23,8 +23,6 @@ #include -#ifdef CFG_NS87308 - #include void initialise_ns87308 (void) @@ -117,5 +115,3 @@ void initialise_ns87308 (void) PNP_PGCS_CSLINE_CONF(2, CFG_NS87308_CS2_CONF); #endif } - -#endif diff --git a/drivers/misc/status_led.c b/drivers/misc/status_led.c index ddb6c22e890..4ba3e180440 100644 --- a/drivers/misc/status_led.c +++ b/drivers/misc/status_led.c @@ -35,8 +35,6 @@ /* ------------------------------------------------------------------------- */ -#ifdef CONFIG_STATUS_LED - typedef struct { led_id_t mask; int state; @@ -127,5 +125,3 @@ void status_led_set (int led, int state) } __led_set (ld->mask, state); } - -#endif /* CONFIG_STATUS_LED */ diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 8bbfcf9c0cb..b361eef9a41 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -26,7 +26,7 @@ #ifdef CFG_NS16550_SERIAL #include -#ifdef CFG_NS87308 +#ifdef CONFIG_NS87308 #include #endif @@ -159,7 +159,7 @@ int serial_init (void) { int clock_divisor; -#ifdef CFG_NS87308 +#ifdef CONFIG_NS87308 initialise_ns87308(); #endif -- cgit v1.2.3 From ab6878c7bc68a7b5e5b731655bdc13221bbfc493 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:40 +0200 Subject: drivers/pci: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/pci/Makefile | 10 ++++------ drivers/pci/fsl_pci_init.c | 4 ---- drivers/pci/pci.c | 4 ---- drivers/pci/pci_auto.c | 4 ---- drivers/pci/pci_indirect.c | 2 -- drivers/pci/tsi108_pci.c | 4 ---- drivers/pci/w83c553f.c | 4 ---- 7 files changed, 4 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index dec93b94d7e..bffb1eb1a13 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -25,15 +25,13 @@ include $(TOPDIR)/config.mk LIB := $(obj)libpci.a -COBJS-y += fsl_pci_init.o -COBJS-y += pci.o -COBJS-y += pci_auto.o -COBJS-y += pci_indirect.o -COBJS-y += tsi108_pci.o -COBJS-y += w83c553f.o +COBJS-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o +COBJS-$(CONFIG_PCI) += pci.o pci_auto.o pci_indirect.o COBJS-$(CONFIG_SH4_PCI) += pci_sh4.o COBJS-$(CONFIG_SH7751_PCI) +=pci_sh7751.o COBJS-$(CONFIG_SH7780_PCI) +=pci_sh7780.o +COBJS-$(CONFIG_TSI108_PCI) += tsi108_pci.o +COBJS-$(CONFIG_WINBOND_83C553) += w83c553f.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c index a7afa908a2d..bb2813f1292 100644 --- a/drivers/pci/fsl_pci_init.c +++ b/drivers/pci/fsl_pci_init.c @@ -18,8 +18,6 @@ #include -#ifdef CONFIG_FSL_PCI_INIT - /* * PCI/PCIE Controller initialization for mpc85xx/mpc86xx soc's * @@ -197,5 +195,3 @@ fsl_pci_init(struct pci_controller *hose) pci_hose_write_config_word(hose, dev, PCI_SEC_STATUS, 0xffff); } } - -#endif /* CONFIG_FSL_PCI */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 16180cbe51a..b5eea89e7d4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -30,8 +30,6 @@ #include -#ifdef CONFIG_PCI - #include #include #include @@ -544,5 +542,3 @@ void pci_init(void) /* now call board specific pci_init()... */ pci_init_board(); } - -#endif /* CONFIG_PCI */ diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index eb6959376a7..2acf9bf7808 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -15,8 +15,6 @@ #include -#ifdef CONFIG_PCI - #include #undef DEBUG @@ -408,5 +406,3 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) return sub_bus; } - -#endif /* CONFIG_PCI */ diff --git a/drivers/pci/pci_indirect.c b/drivers/pci/pci_indirect.c index 55517a8fed3..ab51f8d01e8 100644 --- a/drivers/pci/pci_indirect.c +++ b/drivers/pci/pci_indirect.c @@ -11,7 +11,6 @@ #include -#ifdef CONFIG_PCI #if (!defined(__I386__) && !defined(CONFIG_IXDP425)) #include @@ -135,4 +134,3 @@ void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) } #endif /* !__I386__ && !CONFIG_IXDP425 */ -#endif /* CONFIG_PCI */ diff --git a/drivers/pci/tsi108_pci.c b/drivers/pci/tsi108_pci.c index 4f02cb85e8c..edd614f236e 100644 --- a/drivers/pci/tsi108_pci.c +++ b/drivers/pci/tsi108_pci.c @@ -27,8 +27,6 @@ #include -#ifdef CONFIG_TSI108_PCI - #include #include #include @@ -182,5 +180,3 @@ void ft_pci_setup(void *blob, bd_t *bd) } } #endif /* CONFIG_OF_LIBFDT */ - -#endif /* CONFIG_TSI108_PCI */ diff --git a/drivers/pci/w83c553f.c b/drivers/pci/w83c553f.c index 9ea08a2565a..d7355a4084d 100644 --- a/drivers/pci/w83c553f.c +++ b/drivers/pci/w83c553f.c @@ -30,8 +30,6 @@ #include #include -#ifdef CFG_WINBOND_83C553 - #include #include @@ -222,5 +220,3 @@ void initialise_dma(void) out8(W83C553F_DMA1 + W83C553F_DMA1_CS, 0x00); out16(W83C553F_DMA2 + W83C553F_DMA2_CS, 0x0000); } - -#endif /* CFG_WINBOND_83C553 */ -- cgit v1.2.3 From 7ba44a5521cdb7fa1c72864025cde1e21a6f6921 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:41 +0200 Subject: drivers/qe: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/qe/Makefile | 3 ++- drivers/qe/qe.c | 3 --- drivers/qe/uccf.c | 2 -- drivers/qe/uec.c | 5 ----- drivers/qe/uec_phy.c | 3 --- 5 files changed, 2 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/qe/Makefile b/drivers/qe/Makefile index 45a2fff62ca..18fe9ce614a 100644 --- a/drivers/qe/Makefile +++ b/drivers/qe/Makefile @@ -25,8 +25,9 @@ include $(TOPDIR)/config.mk LIB := $(obj)qe.a COBJS-$(CONFIG_OF_LIBFDT) += fdt.o -COBJS := qe.o uccf.o uec.o uec_phy.o $(COBJS-y) +COBJS-$(CONFIG_QE) += qe.o uccf.o uec.o uec_phy.o +COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index 7b6ecd753d2..e914d01d805 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -27,7 +27,6 @@ #include "asm/immap_qe.h" #include "qe.h" -#if defined(CONFIG_QE) qe_map_t *qe_immr = NULL; static qe_snum_t snums[QE_NUM_OF_SNUM]; @@ -466,5 +465,3 @@ U_BOOT_CMD( "fw [] - Upload firmware binary at address to " "the QE,\n\twith optional length verification.\n" ); - -#endif /* CONFIG_QE */ diff --git a/drivers/qe/uccf.c b/drivers/qe/uccf.c index 4a327ab48f6..7f6337bac8e 100644 --- a/drivers/qe/uccf.c +++ b/drivers/qe/uccf.c @@ -28,7 +28,6 @@ #include "qe.h" #include "uccf.h" -#if defined(CONFIG_QE) void ucc_fast_transmit_on_demand(ucc_fast_private_t *uccf) { out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); @@ -401,4 +400,3 @@ int ucc_fast_init(ucc_fast_info_t *uf_info, ucc_fast_private_t **uccf_ret) *uccf_ret = uccf; return 0; } -#endif /* CONFIG_QE */ diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c index ba89247e4fc..344c6499999 100644 --- a/drivers/qe/uec.c +++ b/drivers/qe/uec.c @@ -31,8 +31,6 @@ #include "uec_phy.h" #include "miiphy.h" -#if defined(CONFIG_QE) - #ifdef CONFIG_UEC_ETH1 static uec_info_t eth1_uec_info = { .uf_info = { @@ -1406,6 +1404,3 @@ int uec_initialize(int index) return 1; } - - -#endif /* CONFIG_QE */ diff --git a/drivers/qe/uec_phy.c b/drivers/qe/uec_phy.c index 423ba789e9e..186922e0c51 100644 --- a/drivers/qe/uec_phy.c +++ b/drivers/qe/uec_phy.c @@ -26,8 +26,6 @@ #include "uec_phy.h" #include "miiphy.h" -#if defined(CONFIG_QE) - #define ugphy_printk(format, arg...) \ printf(format "\n", ## arg) @@ -677,4 +675,3 @@ void change_phy_interface_mode (struct eth_device *dev, enet_interface_e mode) marvell_phy_interface_mode (dev, mode); #endif } -#endif /* CONFIG_QE */ -- cgit v1.2.3 From 00b1883a4cac59d97cd297b1a3a398db85982865 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:42 +0200 Subject: drivers/mtd: Move conditional compilation to Makefile rename CFG_FLASH_CFI_DRIVER to CONFIG_FLASH_CFI_DRIVER Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/mtd/Makefile | 6 +++--- drivers/mtd/at45.c | 3 --- drivers/mtd/cfi_flash.c | 3 --- drivers/mtd/mw_eeprom.c | 5 ----- 4 files changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index ff932a1b6bf..6538f7a1586 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,11 +25,11 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmtd.a -COBJS-y += at45.o -COBJS-y += cfi_flash.o +COBJS-$(CONFIG_HAS_DATAFLASH) += at45.o +COBJS-$(CONFIG_FLASH_CFI_DRIVER) += cfi_flash.o COBJS-$(CONFIG_HAS_DATAFLASH) += dataflash.o -COBJS-y += mw_eeprom.o COBJS-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o +COBJS-$(CONFIG_MW_EEPROM) += mw_eeprom.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/at45.c b/drivers/mtd/at45.c index a9d13ff90ef..d1a60aac90b 100644 --- a/drivers/mtd/at45.c +++ b/drivers/mtd/at45.c @@ -20,8 +20,6 @@ #include #include - -#ifdef CONFIG_HAS_DATAFLASH #include /* @@ -559,4 +557,3 @@ int AT91F_DataflashProbe(int cs, AT91PS_DataflashDesc pDesc) AT91F_DataFlashGetStatus(pDesc); return ((pDesc->command[1] == 0xFF) ? 0 : pDesc->command[1] & 0x3C); } -#endif diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 479075ccb19..402d835bf98 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -39,7 +39,6 @@ #include #include #include -#ifdef CFG_FLASH_CFI_DRIVER /* * This file implements a Common Flash Interface (CFI) driver for @@ -2015,5 +2014,3 @@ unsigned long flash_init (void) #endif return (size); } - -#endif /* CFG_FLASH_CFI */ diff --git a/drivers/mtd/mw_eeprom.c b/drivers/mtd/mw_eeprom.c index 2b3348810d2..f32ced4c2e0 100644 --- a/drivers/mtd/mw_eeprom.c +++ b/drivers/mtd/mw_eeprom.c @@ -1,9 +1,6 @@ /* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */ #include - -#ifdef CONFIG_MW_EEPROM - #include /* @@ -237,5 +234,3 @@ int mw_eeprom_probe(int dev) } return 0; } - -#endif -- cgit v1.2.3 From 4fb09b81920e5dfdfc4576883186733f0bd6059c Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:42 +0200 Subject: drivers/mtd/onenand: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/mtd/onenand/Makefile | 3 ++- drivers/mtd/onenand/onenand_base.c | 5 ----- drivers/mtd/onenand/onenand_bbt.c | 5 ----- drivers/mtd/onenand/onenand_uboot.c | 5 ----- 4 files changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 92074b25528..1d35a57d84f 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -25,8 +25,9 @@ include $(TOPDIR)/config.mk LIB := $(obj)libonenand.a -COBJS := onenand_uboot.o onenand_base.o onenand_bbt.o +COBJS-$(CONFIG_CMD_ONENAND) := onenand_uboot.o onenand_base.o onenand_bbt.o +COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ded1706abbe..7c9438be316 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -10,9 +10,6 @@ */ #include - -#ifdef CONFIG_CMD_ONENAND - #include #include #include @@ -1304,5 +1301,3 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) void onenand_release(struct mtd_info *mtd) { } - -#endif /* CONFIG_CMD_ONENAND */ diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 87344ab65f1..0abaa1ab38c 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -15,9 +15,6 @@ */ #include - -#ifdef CONFIG_CMD_ONENAND - #include #include #include @@ -261,5 +258,3 @@ int onenand_default_bbt(struct mtd_info *mtd) return onenand_scan_bbt(mtd, bbm->badblock_pattern); } - -#endif /* CFG_CMD_ONENAND */ diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c index bd7466ac9d9..d614450616f 100644 --- a/drivers/mtd/onenand/onenand_uboot.c +++ b/drivers/mtd/onenand/onenand_uboot.c @@ -14,9 +14,6 @@ */ #include - -#ifdef CONFIG_CMD_ONENAND - #include #include #include @@ -37,5 +34,3 @@ void onenand_init(void) puts("OneNAND: "); print_size(onenand_mtd.size, "\n"); } - -#endif /* CONFIG_CMD_ONENAND */ -- cgit v1.2.3 From cc4a0ceeac5462106172d0cc9d9d542233aa3ab2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:43 +0200 Subject: drivers/mtd/nand: Move conditional compilation to Makefile rename CFG_NAND_LEGACY to CONFIG_NAND_LEGACY Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/mtd/nand/Makefile | 10 +++++++--- drivers/mtd/nand/diskonchip.c | 2 +- drivers/mtd/nand/fsl_upm.c | 3 --- drivers/mtd/nand/nand.c | 5 ----- drivers/mtd/nand/nand_base.c | 5 ----- drivers/mtd/nand/nand_bbt.c | 5 ----- drivers/mtd/nand/nand_ecc.c | 4 ---- drivers/mtd/nand/nand_ids.c | 4 ---- drivers/mtd/nand/nand_util.c | 5 ----- drivers/mtd/nand_legacy/nand_legacy.c | 2 +- 10 files changed, 9 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index ffb31695945..19233105a20 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -25,15 +25,19 @@ include $(TOPDIR)/config.mk LIB := $(obj)libnand.a +ifdef CONFIG_CMD_NAND +ifndef CONFIG_NAND_LEGACY COBJS-y += nand.o COBJS-y += nand_base.o -COBJS-y += nand_ids.o -COBJS-y += nand_ecc.o COBJS-y += nand_bbt.o +COBJS-y += nand_ecc.o +COBJS-y += nand_ids.o COBJS-y += nand_util.o +endif COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o -COBJS-y += fsl_upm.o +COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +endif COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index ce197f5ad16..4cba8100a5f 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -21,7 +21,7 @@ #include -#if !defined(CFG_NAND_LEGACY) +#if !defined(CONFIG_NAND_LEGACY) #include #include diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index e651903040b..1a1d8c4e612 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -11,8 +11,6 @@ */ #include - -#if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_FSL_UPM) #include #include #include @@ -150,4 +148,3 @@ int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) return 0; } -#endif /* CONFIG_CMD_NAND */ diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c index e44470eb6cc..ebd2acd02bf 100644 --- a/drivers/mtd/nand/nand.c +++ b/drivers/mtd/nand/nand.c @@ -22,9 +22,6 @@ */ #include - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - #include #ifndef CFG_NAND_BASE_LIST @@ -79,5 +76,3 @@ void nand_init(void) board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); #endif } - -#endif diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a29ff1146f2..0913bb87418 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -59,8 +59,6 @@ #define ENOTSUPP 524 /* Operation is not supported */ -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - #include #include #include @@ -2822,6 +2820,3 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Steven J. Hill , Thomas Gleixner "); MODULE_DESCRIPTION("Generic NAND flash driver code"); #endif - -#endif - diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 84479473b6d..b3b740da641 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -53,9 +53,6 @@ */ #include - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - #include #include #include @@ -1237,5 +1234,3 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) EXPORT_SYMBOL(nand_scan_bbt); EXPORT_SYMBOL(nand_default_bbt); #endif - -#endif diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index e1d5154db22..ee1f6cc8a85 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -39,8 +39,6 @@ #include -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - /* XXX U-BOOT XXX */ #if 0 #include @@ -215,5 +213,3 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat, #if 0 EXPORT_SYMBOL(nand_correct_data); #endif - -#endif diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index f8b96cf025d..2ff75c94e91 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -12,9 +12,6 @@ */ #include - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - #include /* * Chip ID list @@ -147,4 +144,3 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_MICRON, "Micron"}, {0x0, "Unknown"} }; -#endif diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 02fe914db8b..22820d55d02 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -31,9 +31,6 @@ */ #include - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - #include #include #include @@ -606,5 +603,3 @@ int nand_read_skip_bad(nand_info_t *nand, size_t offset, size_t *length, return 0; } - -#endif /* defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */ diff --git a/drivers/mtd/nand_legacy/nand_legacy.c b/drivers/mtd/nand_legacy/nand_legacy.c index fafefad60fe..8f7dc3901af 100644 --- a/drivers/mtd/nand_legacy/nand_legacy.c +++ b/drivers/mtd/nand_legacy/nand_legacy.c @@ -15,7 +15,7 @@ #include #include -#if defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY) +#if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY) #include #include -- cgit v1.2.3 From 9483df6408c25f16060432de3868901e352e23bc Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 13 Aug 2008 01:40:43 +0200 Subject: drivers/mtd/nand_legacy: Move conditional compilation to Makefile Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- drivers/mtd/nand_legacy/Makefile | 5 ++++- drivers/mtd/nand_legacy/nand_legacy.c | 5 ----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand_legacy/Makefile b/drivers/mtd/nand_legacy/Makefile index 4e29c36e704..a1a9cc92b59 100644 --- a/drivers/mtd/nand_legacy/Makefile +++ b/drivers/mtd/nand_legacy/Makefile @@ -25,8 +25,11 @@ include $(TOPDIR)/config.mk LIB := $(obj)libnand_legacy.a -COBJS := nand_legacy.o +ifdef CONFIG_CMD_NAND +COBJS-$(CONFIG_NAND_LEGACY) := nand_legacy.o +endif +COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/mtd/nand_legacy/nand_legacy.c b/drivers/mtd/nand_legacy/nand_legacy.c index 8f7dc3901af..bf5565a8fbe 100644 --- a/drivers/mtd/nand_legacy/nand_legacy.c +++ b/drivers/mtd/nand_legacy/nand_legacy.c @@ -14,9 +14,6 @@ #include #include #include - -#if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY) - #include #include #include @@ -1608,5 +1605,3 @@ int read_jffs2_nand(size_t start, size_t len, start, len, retlen, buf); } #endif /* CONFIG_JFFS2_NAND */ - -#endif -- cgit v1.2.3