aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bios_emulator/x86emu/prim_ops.c5
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/dwc_ahsata.c969
-rw-r--r--drivers/block/dwc_ahsata.h335
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/rda_dma.c143
-rw-r--r--drivers/gpio/mxc_gpio.c3
-rw-r--r--drivers/i2c/Makefile3
-rw-r--r--drivers/i2c/designware_i2c.c (renamed from drivers/i2c/spr_i2c.c)15
-rw-r--r--drivers/i2c/designware_i2c.h146
-rw-r--r--drivers/i2c/sh_sh7734_i2c.c387
-rw-r--r--drivers/i2c/tegra_i2c.c14
-rw-r--r--drivers/input/Makefile3
-rw-r--r--drivers/input/input.c430
-rw-r--r--drivers/input/key_matrix.c208
-rw-r--r--drivers/input/tegra-kbc.c375
-rw-r--r--drivers/misc/Makefile4
-rw-r--r--drivers/misc/factory.c715
-rw-r--r--drivers/misc/pmic_dialog.c37
-rw-r--r--drivers/misc/pmic_max8997.c43
-rw-r--r--drivers/misc/prdinfo.c344
-rw-r--r--drivers/mmc/Makefile15
-rw-r--r--drivers/mmc/bfin_sdh.c1
-rw-r--r--drivers/mmc/fsl_esdhc.c39
-rw-r--r--drivers/mmc/mmc.c949
-rw-r--r--drivers/mmc/mmc_parts.c725
-rw-r--r--drivers/mmc/mmc_private.h45
-rw-r--r--drivers/mmc/mmc_spl_load.c111
-rw-r--r--drivers/mmc/mmc_write.c187
-rw-r--r--drivers/mmc/mxsmmc.c5
-rw-r--r--drivers/mmc/omap_hsmmc.c114
-rw-r--r--drivers/mmc/rda_mmc.c892
-rw-r--r--drivers/mmc/rda_mmc.h221
-rw-r--r--drivers/mmc/rda_mmc_legacy.c2419
-rw-r--r--drivers/mmc/s5p_mmc.c490
-rw-r--r--drivers/mmc/s5p_sdhci.c98
-rw-r--r--drivers/mmc/sdhci.c24
-rw-r--r--drivers/mtd/mtdconcat.c39
-rw-r--r--drivers/mtd/mtdcore.c16
-rw-r--r--drivers/mtd/mtdpart.c2
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c22
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c2
-rw-r--r--drivers/mtd/nand/mxc_nand.c33
-rw-r--r--drivers/mtd/nand/nand_base.c352
-rw-r--r--drivers/mtd/nand/nand_bbt.c323
-rw-r--r--drivers/mtd/nand/nand_ids.c223
-rw-r--r--drivers/mtd/nand/nand_spl_load.c36
-rw-r--r--drivers/mtd/nand/nand_spl_simple.c170
-rw-r--r--drivers/mtd/nand/nand_util.c741
-rw-r--r--drivers/mtd/nand/rda_nand.c12
-rw-r--r--drivers/mtd/nand/rda_nand_base.c95
-rw-r--r--drivers/mtd/nand/rda_nand_v1.c1661
-rw-r--r--drivers/mtd/nand/rda_nand_v2.c941
-rw-r--r--drivers/mtd/nand/rda_nand_v3.c2050
-rw-r--r--drivers/mtd/nand/rda_spi_nand.c1654
-rw-r--r--drivers/mtd/onenand/onenand_base.c2
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c2
-rw-r--r--drivers/mtd/onenand/onenand_uboot.c2
-rw-r--r--drivers/mtd/onenand/samsung.c2
-rw-r--r--drivers/mtd/ubi/build.c63
-rw-r--r--drivers/mtd/ubi/eba.c23
-rw-r--r--drivers/mtd/ubi/kapi.c27
-rw-r--r--drivers/mtd/ubi/misc.c10
-rw-r--r--drivers/mtd/ubi/upd.c13
-rw-r--r--drivers/mtd/ubi/vmt.c6
-rw-r--r--drivers/mtd/ubi/vtbl.c10
-rw-r--r--drivers/net/4xx_enet.c3
-rw-r--r--drivers/net/altera_tse.c3
-rw-r--r--drivers/net/armada100_fec.c3
-rw-r--r--drivers/net/at91_emac.c3
-rw-r--r--drivers/net/ax88180.c3
-rw-r--r--drivers/net/bfin_mac.c5
-rw-r--r--drivers/net/bfin_mac.h2
-rw-r--r--drivers/net/calxedaxgmac.c6
-rw-r--r--drivers/net/cs8900.c3
-rw-r--r--drivers/net/davinci_emac.c2
-rw-r--r--drivers/net/dc2114x.c4
-rw-r--r--drivers/net/designware.c5
-rw-r--r--drivers/net/dm9000x.c3
-rw-r--r--drivers/net/dnet.c3
-rw-r--r--drivers/net/e1000.c5
-rw-r--r--drivers/net/eepro100.c7
-rw-r--r--drivers/net/enc28j60.c2
-rw-r--r--drivers/net/ep93xx_eth.c2
-rw-r--r--drivers/net/ethoc.c2
-rw-r--r--drivers/net/fec_mxc.c90
-rw-r--r--drivers/net/fm/eth.c2
-rw-r--r--drivers/net/fm/fm.c2
-rw-r--r--drivers/net/fsl_mcdmafec.c7
-rw-r--r--drivers/net/ftgmac100.c3
-rw-r--r--drivers/net/ftmac100.c3
-rw-r--r--drivers/net/greth.c2
-rw-r--r--drivers/net/inca-ip_sw.c4
-rw-r--r--drivers/net/ks8695eth.c3
-rw-r--r--drivers/net/lan91c96.c4
-rw-r--r--drivers/net/macb.c3
-rw-r--r--drivers/net/mcffec.c3
-rw-r--r--drivers/net/mpc512x_fec.c4
-rw-r--r--drivers/net/mpc5xxx_fec.c2
-rw-r--r--drivers/net/mvgbe.c3
-rw-r--r--drivers/net/natsemi.c6
-rw-r--r--drivers/net/ne2000_base.c2
-rw-r--r--drivers/net/netarm_eth.c2
-rw-r--r--drivers/net/netconsole.c134
-rw-r--r--drivers/net/ns8382x.c6
-rw-r--r--drivers/net/pcnet.c6
-rw-r--r--drivers/net/plb2800_eth.c6
-rw-r--r--drivers/net/rtl8139.c4
-rw-r--r--drivers/net/rtl8169.c2
-rw-r--r--drivers/net/sh_eth.c36
-rw-r--r--drivers/net/sh_eth.h86
-rw-r--r--drivers/net/smc91111.c3
-rw-r--r--drivers/net/smc911x.c3
-rw-r--r--drivers/net/tsec.c5
-rw-r--r--drivers/net/tsi108_eth.c14
-rw-r--r--drivers/net/uli526x.c6
-rw-r--r--drivers/net/xilinx_axi_emac.c2
-rw-r--r--drivers/net/xilinx_emaclite.c6
-rw-r--r--drivers/net/xilinx_ll_temac_fifo.c3
-rw-r--r--drivers/net/xilinx_ll_temac_fifo.h3
-rw-r--r--drivers/net/xilinx_ll_temac_sdma.c3
-rw-r--r--drivers/net/xilinx_ll_temac_sdma.h3
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/tps6586x.c280
-rw-r--r--drivers/power/twl6035.c65
-rw-r--r--drivers/qe/uec.c2
-rw-r--r--drivers/serial/Makefile12
-rw-r--r--drivers/serial/lpc32xx_hsuart.c112
-rw-r--r--drivers/serial/usb-serial.c871
-rw-r--r--drivers/serial/usbser.h64
-rw-r--r--drivers/spi/mxs_spi.c23
-rw-r--r--drivers/usb/eth/asix.c2
-rw-r--r--drivers/usb/eth/smsc95xx.c3
-rw-r--r--drivers/usb/gadget/Makefile4
-rw-r--r--drivers/usb/gadget/composite.c1082
-rw-r--r--drivers/usb/gadget/config.c1
-rw-r--r--drivers/usb/gadget/core.c50
-rw-r--r--drivers/usb/gadget/ep0.c10
-rw-r--r--drivers/usb/gadget/epautoconf.c1
-rw-r--r--drivers/usb/gadget/ether.c8
-rw-r--r--drivers/usb/gadget/f_fastboot.c473
-rw-r--r--drivers/usb/gadget/fastboot.c986
-rw-r--r--drivers/usb/gadget/fastboot.h8
-rw-r--r--drivers/usb/gadget/s3c_udc_otg.c58
-rw-r--r--drivers/usb/gadget/s3c_udc_otg_xfer_dma.c289
-rw-r--r--drivers/usb/gadget/usbstring.c1
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-hcd.c288
-rw-r--r--drivers/usb/host/ehci-mx6.c5
-rw-r--r--drivers/usb/host/ehci-mxc.c4
-rw-r--r--drivers/usb/host/ehci-s5p.c110
-rw-r--r--drivers/usb/musb/Makefile4
-rw-r--r--drivers/usb/musb/musb_core.c2
-rw-r--r--drivers/usb/musb/musb_hcd.c5
-rw-r--r--drivers/usb/musb/musb_udc.c247
-rw-r--r--drivers/usb/musb/musbhsdma.c118
-rw-r--r--drivers/usb/musb/musbhsdma.h95
-rw-r--r--drivers/usb/musb/rda.c212
-rw-r--r--drivers/usb/musb/rda.h45
-rw-r--r--drivers/video/Makefile4
-rw-r--r--drivers/video/exynos_fb.c128
-rw-r--r--drivers/video/exynos_fb.h61
-rw-r--r--drivers/video/exynos_fimd.c354
-rw-r--r--drivers/video/exynos_mipi_dsi.c253
-rw-r--r--drivers/video/exynos_mipi_dsi_common.c637
-rw-r--r--drivers/video/exynos_mipi_dsi_common.h48
-rw-r--r--drivers/video/exynos_mipi_dsi_lowlevel.c652
-rw-r--r--drivers/video/exynos_mipi_dsi_lowlevel.h111
-rw-r--r--drivers/video/s6e8ax0.c256
170 files changed, 25685 insertions, 1953 deletions
diff --git a/drivers/bios_emulator/x86emu/prim_ops.c b/drivers/bios_emulator/x86emu/prim_ops.c
index 7553087b26..5f6c795fb7 100644
--- a/drivers/bios_emulator/x86emu/prim_ops.c
+++ b/drivers/bios_emulator/x86emu/prim_ops.c
@@ -118,11 +118,6 @@ static u32 x86emu_parity_tab[8] =
#define PARITY(x) (((x86emu_parity_tab[(x) / 32] >> ((x) % 32)) & 1) == 0)
#define XOR2(x) (((x) ^ ((x)>>1)) & 0x1)
-/*----------------------------- Implementation ----------------------------*/
-int abs(int v)
-{
- return (v>0)?v:-v;
-}
/*----------------------------- Implementation ----------------------------*/
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 98560ef76f..b9c20475c2 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -27,6 +27,7 @@ LIB := $(obj)libblock.o
COBJS-$(CONFIG_SCSI_AHCI) += ahci.o
COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o
+COBJS-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o
COBJS-$(CONFIG_IDE_FTIDE020) += ftide020.o
COBJS-$(CONFIG_LIBATA) += libata.o
diff --git a/drivers/block/dwc_ahsata.c b/drivers/block/dwc_ahsata.c
new file mode 100644
index 0000000000..2703d3dc84
--- /dev/null
+++ b/drivers/block/dwc_ahsata.c
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Terry Lv <r65388@freescale.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ */
+
+#include <libata.h>
+#include <ahci.h>
+#include <fis.h>
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <asm/arch/clock.h>
+#include "dwc_ahsata.h"
+
+struct sata_port_regs {
+ u32 clb;
+ u32 clbu;
+ u32 fb;
+ u32 fbu;
+ u32 is;
+ u32 ie;
+ u32 cmd;
+ u32 res1[1];
+ u32 tfd;
+ u32 sig;
+ u32 ssts;
+ u32 sctl;
+ u32 serr;
+ u32 sact;
+ u32 ci;
+ u32 sntf;
+ u32 res2[1];
+ u32 dmacr;
+ u32 res3[1];
+ u32 phycr;
+ u32 physr;
+};
+
+struct sata_host_regs {
+ u32 cap;
+ u32 ghc;
+ u32 is;
+ u32 pi;
+ u32 vs;
+ u32 ccc_ctl;
+ u32 ccc_ports;
+ u32 res1[2];
+ u32 cap2;
+ u32 res2[30];
+ u32 bistafr;
+ u32 bistcr;
+ u32 bistfctr;
+ u32 bistsr;
+ u32 bistdecr;
+ u32 res3[2];
+ u32 oobr;
+ u32 res4[8];
+ u32 timer1ms;
+ u32 res5[1];
+ u32 gparam1r;
+ u32 gparam2r;
+ u32 pparamr;
+ u32 testr;
+ u32 versionr;
+ u32 idr;
+};
+
+#define MAX_DATA_BYTES_PER_SG (4 * 1024 * 1024)
+#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG)
+
+#define writel_with_flush(a, b) do { writel(a, b); readl(b); } while (0)
+
+static int is_ready;
+
+static inline u32 ahci_port_base(u32 base, u32 port)
+{
+ return base + 0x100 + (port * 0x80);
+}
+
+static int waiting_for_cmd_completed(u8 *offset,
+ int timeout_msec,
+ u32 sign)
+{
+ int i;
+ u32 status;
+
+ for (i = 0;
+ ((status = readl(offset)) & sign) && i < timeout_msec;
+ ++i)
+ mdelay(1);
+
+ return (i < timeout_msec) ? 0 : -1;
+}
+
+static int ahci_setup_oobr(struct ahci_probe_ent *probe_ent,
+ int clk)
+{
+ struct sata_host_regs *host_mmio =
+ (struct sata_host_regs *)probe_ent->mmio_base;
+
+ writel(SATA_HOST_OOBR_WE, &(host_mmio->oobr));
+ writel(0x02060b14, &(host_mmio->oobr));
+
+ return 0;
+}
+
+static int ahci_host_init(struct ahci_probe_ent *probe_ent)
+{
+ u32 tmp, cap_save, num_ports;
+ int i, j, timeout = 1000;
+ struct sata_port_regs *port_mmio = NULL;
+ struct sata_host_regs *host_mmio =
+ (struct sata_host_regs *)probe_ent->mmio_base;
+ int clk = mxc_get_clock(MXC_SATA_CLK);
+
+ cap_save = readl(&(host_mmio->cap));
+ cap_save |= SATA_HOST_CAP_SSS;
+
+ /* global controller reset */
+ tmp = readl(&(host_mmio->ghc));
+ if ((tmp & SATA_HOST_GHC_HR) == 0)
+ writel_with_flush(tmp | SATA_HOST_GHC_HR, &(host_mmio->ghc));
+
+ while ((readl(&(host_mmio->ghc)) & SATA_HOST_GHC_HR)
+ && --timeout)
+ ;
+
+ if (timeout <= 0) {
+ debug("controller reset failed (0x%x)\n", tmp);
+ return -1;
+ }
+
+ /* Set timer 1ms */
+ writel(clk / 1000, &(host_mmio->timer1ms));
+
+ ahci_setup_oobr(probe_ent, 0);
+
+ writel_with_flush(SATA_HOST_GHC_AE, &(host_mmio->ghc));
+ writel(cap_save, &(host_mmio->cap));
+ num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1;
+ writel_with_flush((1 << num_ports) - 1,
+ &(host_mmio->pi));
+
+ /*
+ * Determine which Ports are implemented by the DWC_ahsata,
+ * by reading the PI register. This bit map value aids the
+ * software to determine how many Ports are available and
+ * which Port registers need to be initialized.
+ */
+ probe_ent->cap = readl(&(host_mmio->cap));
+ probe_ent->port_map = readl(&(host_mmio->pi));
+
+ /* Determine how many command slots the HBA supports */
+ probe_ent->n_ports =
+ (probe_ent->cap & SATA_HOST_CAP_NP_MASK) + 1;
+
+ debug("cap 0x%x port_map 0x%x n_ports %d\n",
+ probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
+
+ for (i = 0; i < probe_ent->n_ports; i++) {
+ probe_ent->port[i].port_mmio =
+ ahci_port_base((u32)host_mmio, i);
+ port_mmio =
+ (struct sata_port_regs *)probe_ent->port[i].port_mmio;
+
+ /* Ensure that the DWC_ahsata is in idle state */
+ tmp = readl(&(port_mmio->cmd));
+
+ /*
+ * When P#CMD.ST, P#CMD.CR, P#CMD.FRE and P#CMD.FR
+ * are all cleared, the Port is in an idle state.
+ */
+ if (tmp & (SATA_PORT_CMD_CR | SATA_PORT_CMD_FR |
+ SATA_PORT_CMD_FRE | SATA_PORT_CMD_ST)) {
+
+ /*
+ * System software places a Port into the idle state by
+ * clearing P#CMD.ST and waiting for P#CMD.CR to return
+ * 0 when read.
+ */
+ tmp &= ~SATA_PORT_CMD_ST;
+ writel_with_flush(tmp, &(port_mmio->cmd));
+
+ /*
+ * spec says 500 msecs for each bit, so
+ * this is slightly incorrect.
+ */
+ mdelay(500);
+
+ timeout = 1000;
+ while ((readl(&(port_mmio->cmd)) & SATA_PORT_CMD_CR)
+ && --timeout)
+ ;
+
+ if (timeout <= 0) {
+ debug("port reset failed (0x%x)\n", tmp);
+ return -1;
+ }
+ }
+
+ /* Spin-up device */
+ tmp = readl(&(port_mmio->cmd));
+ writel((tmp | SATA_PORT_CMD_SUD), &(port_mmio->cmd));
+
+ /* Wait for spin-up to finish */
+ timeout = 1000;
+ while (!(readl(&(port_mmio->cmd)) | SATA_PORT_CMD_SUD)
+ && --timeout)
+ ;
+ if (timeout <= 0) {
+ debug("Spin-Up can't finish!\n");
+ return -1;
+ }
+
+ for (j = 0; j < 100; ++j) {
+ mdelay(10);
+ tmp = readl(&(port_mmio->ssts));
+ if (((tmp & SATA_PORT_SSTS_DET_MASK) == 0x3) ||
+ ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x1))
+ break;
+ }
+
+ /* Wait for COMINIT bit 26 (DIAG_X) in SERR */
+ timeout = 1000;
+ while (!(readl(&(port_mmio->serr)) | SATA_PORT_SERR_DIAG_X)
+ && --timeout)
+ ;
+ if (timeout <= 0) {
+ debug("Can't find DIAG_X set!\n");
+ return -1;
+ }
+
+ /*
+ * For each implemented Port, clear the P#SERR
+ * register, by writing ones to each implemented\
+ * bit location.
+ */
+ tmp = readl(&(port_mmio->serr));
+ debug("P#SERR 0x%x\n",
+ tmp);
+ writel(tmp, &(port_mmio->serr));
+
+ /* Ack any pending irq events for this port */
+ tmp = readl(&(host_mmio->is));
+ debug("IS 0x%x\n", tmp);
+ if (tmp)
+ writel(tmp, &(host_mmio->is));
+
+ writel(1 << i, &(host_mmio->is));
+
+ /* set irq mask (enables interrupts) */
+ writel(DEF_PORT_IRQ, &(port_mmio->ie));
+
+ /* register linkup ports */
+ tmp = readl(&(port_mmio->ssts));
+ debug("Port %d status: 0x%x\n", i, tmp);
+ if ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x03)
+ probe_ent->link_port_map |= (0x01 << i);
+ }
+
+ tmp = readl(&(host_mmio->ghc));
+ debug("GHC 0x%x\n", tmp);
+ writel(tmp | SATA_HOST_GHC_IE, &(host_mmio->ghc));
+ tmp = readl(&(host_mmio->ghc));
+ debug("GHC 0x%x\n", tmp);
+
+ return 0;
+}
+
+static void ahci_print_info(struct ahci_probe_ent *probe_ent)
+{
+ struct sata_host_regs *host_mmio =
+ (struct sata_host_regs *)probe_ent->mmio_base;
+ u32 vers, cap, impl, speed;
+ const char *speed_s;
+ const char *scc_s;
+
+ vers = readl(&(host_mmio->vs));
+ cap = probe_ent->cap;
+ impl = probe_ent->port_map;
+
+ speed = (cap & SATA_HOST_CAP_ISS_MASK)
+ >> SATA_HOST_CAP_ISS_OFFSET;
+ if (speed == 1)
+ speed_s = "1.5";
+ else if (speed == 2)
+ speed_s = "3";
+ else
+ speed_s = "?";
+
+ scc_s = "SATA";
+
+ printf("AHCI %02x%02x.%02x%02x "
+ "%u slots %u ports %s Gbps 0x%x impl %s mode\n",
+ (vers >> 24) & 0xff,
+ (vers >> 16) & 0xff,
+ (vers >> 8) & 0xff,
+ vers & 0xff,
+ ((cap >> 8) & 0x1f) + 1,
+ (cap & 0x1f) + 1,
+ speed_s,
+ impl,
+ scc_s);
+
+ printf("flags: "
+ "%s%s%s%s%s%s"
+ "%s%s%s%s%s%s%s\n",
+ cap & (1 << 31) ? "64bit " : "",
+ cap & (1 << 30) ? "ncq " : "",
+ cap & (1 << 28) ? "ilck " : "",
+ cap & (1 << 27) ? "stag " : "",
+ cap & (1 << 26) ? "pm " : "",
+ cap & (1 << 25) ? "led " : "",
+ cap & (1 << 24) ? "clo " : "",
+ cap & (1 << 19) ? "nz " : "",
+ cap & (1 << 18) ? "only " : "",
+ cap & (1 << 17) ? "pmp " : "",
+ cap & (1 << 15) ? "pio " : "",
+ cap & (1 << 14) ? "slum " : "",
+ cap & (1 << 13) ? "part " : "");
+}
+
+static int ahci_init_one(int pdev)
+{
+ int rc;
+ struct ahci_probe_ent *probe_ent = NULL;
+
+ probe_ent = malloc(sizeof(struct ahci_probe_ent));
+ memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
+ probe_ent->dev = pdev;
+
+ probe_ent->host_flags = ATA_FLAG_SATA
+ | ATA_FLAG_NO_LEGACY
+ | ATA_FLAG_MMIO
+ | ATA_FLAG_PIO_DMA
+ | ATA_FLAG_NO_ATAPI;
+
+ probe_ent->mmio_base = CONFIG_DWC_AHSATA_BASE_ADDR;
+
+ /* initialize adapter */
+ rc = ahci_host_init(probe_ent);
+ if (rc)
+ goto err_out;
+
+ ahci_print_info(probe_ent);
+
+ /* Save the private struct to block device struct */
+ sata_dev_desc[pdev].priv = (void *)probe_ent;
+
+ return 0;
+
+err_out:
+ return rc;
+}
+
+static int ahci_fill_sg(struct ahci_probe_ent *probe_ent,
+ u8 port, unsigned char *buf, int buf_len)
+{
+ struct ahci_ioports *pp = &(probe_ent->port[port]);
+ struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
+ u32 sg_count, max_bytes;
+ int i;
+
+ max_bytes = MAX_DATA_BYTES_PER_SG;
+ sg_count = ((buf_len - 1) / max_bytes) + 1;
+ if (sg_count > AHCI_MAX_SG) {
+ printf("Error:Too much sg!\n");
+ return -1;
+ }
+
+ for (i = 0; i < sg_count; i++) {
+ ahci_sg->addr =
+ cpu_to_le32((u32)buf + i * max_bytes);
+ ahci_sg->addr_hi = 0;
+ ahci_sg->flags_size = cpu_to_le32(0x3fffff &
+ (buf_len < max_bytes
+ ? (buf_len - 1)
+ : (max_bytes - 1)));
+ ahci_sg++;
+ buf_len -= max_bytes;
+ }
+
+ return sg_count;
+}
+
+static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts)
+{
+ struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot +
+ AHCI_CMD_SLOT_SZ * cmd_slot);
+
+ memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ);
+ cmd_hdr->opts = cpu_to_le32(opts);
+ cmd_hdr->status = 0;
+ cmd_hdr->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
+ cmd_hdr->tbl_addr_hi = 0;
+}
+
+#define AHCI_GET_CMD_SLOT(c) ((c) ? ffs(c) : 0)
+
+static int ahci_exec_ata_cmd(struct ahci_probe_ent *probe_ent,
+ u8 port, struct sata_fis_h2d *cfis,
+ u8 *buf, u32 buf_len, s32 is_write)
+{
+ struct ahci_ioports *pp = &(probe_ent->port[port]);
+ struct sata_port_regs *port_mmio =
+ (struct sata_port_regs *)pp->port_mmio;
+ u32 opts;
+ int sg_count = 0, cmd_slot = 0;
+
+ cmd_slot = AHCI_GET_CMD_SLOT(readl(&(port_mmio->ci)));
+ if (32 == cmd_slot) {
+ printf("Can't find empty command slot!\n");
+ return 0;
+ }
+
+ /* Check xfer length */
+ if (buf_len > MAX_BYTES_PER_TRANS) {
+ printf("Max transfer length is %dB\n\r",
+ MAX_BYTES_PER_TRANS);
+ return 0;
+ }
+
+ memcpy((u8 *)(pp->cmd_tbl), cfis, sizeof(struct sata_fis_h2d));
+ if (buf && buf_len)
+ sg_count = ahci_fill_sg(probe_ent, port, buf, buf_len);
+ opts = (sizeof(struct sata_fis_h2d) >> 2) | (sg_count << 16);
+ if (is_write)
+ opts |= 0x40;
+ ahci_fill_cmd_slot(pp, cmd_slot, opts);
+
+ writel_with_flush(1 << cmd_slot, &(port_mmio->ci));
+
+ if (waiting_for_cmd_completed((u8 *)&(port_mmio->ci),
+ 10000, 0x1 << cmd_slot)) {
+ printf("timeout exit!\n");
+ return -1;
+ }
+ debug("ahci_exec_ata_cmd: %d byte transferred.\n",
+ pp->cmd_slot->status);
+
+ return buf_len;
+}
+
+static void ahci_set_feature(u8 dev, u8 port)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 1 << 7;
+ cfis->command = ATA_CMD_SET_FEATURES;
+ cfis->features = SETFEATURES_XFER;
+ cfis->sector_count = ffs(probe_ent->udma_mask + 1) + 0x3e;
+
+ ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, READ_CMD);
+}
+
+static int ahci_port_start(struct ahci_probe_ent *probe_ent,
+ u8 port)
+{
+ struct ahci_ioports *pp = &(probe_ent->port[port]);
+ struct sata_port_regs *port_mmio =
+ (struct sata_port_regs *)pp->port_mmio;
+ u32 port_status;
+ u32 mem;
+ int timeout = 10000000;
+
+ debug("Enter start port: %d\n", port);
+ port_status = readl(&(port_mmio->ssts));
+ debug("Port %d status: %x\n", port, port_status);
+ if ((port_status & 0xf) != 0x03) {
+ printf("No Link on this port!\n");
+ return -1;
+ }
+
+ mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024);
+ if (!mem) {
+ free(pp);
+ printf("No mem for table!\n");
+ return -ENOMEM;
+ }
+
+ mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */
+ memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+
+ /*
+ * First item in chunk of DMA memory: 32-slot command table,
+ * 32 bytes each in size
+ */
+ pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
+ debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot);
+ mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS);
+
+ /*
+ * Second item: Received-FIS area, 256-Byte aligned
+ */
+ pp->rx_fis = mem;
+ mem += AHCI_RX_FIS_SZ;
+
+ /*
+ * Third item: data area for storing a single command
+ * and its scatter-gather table
+ */
+ pp->cmd_tbl = mem;
+ debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl);
+
+ mem += AHCI_CMD_TBL_HDR;
+
+ writel_with_flush(0x00004444, &(port_mmio->dmacr));
+ pp->cmd_tbl_sg = (struct ahci_sg *)mem;
+ writel_with_flush((u32)pp->cmd_slot, &(port_mmio->clb));
+ writel_with_flush(pp->rx_fis, &(port_mmio->fb));
+
+ /* Enable FRE */
+ writel_with_flush((SATA_PORT_CMD_FRE | readl(&(port_mmio->cmd))),
+ &(port_mmio->cmd));
+
+ /* Wait device ready */
+ while ((readl(&(port_mmio->tfd)) & (SATA_PORT_TFD_STS_ERR |
+ SATA_PORT_TFD_STS_DRQ | SATA_PORT_TFD_STS_BSY))
+ && --timeout)
+ ;
+ if (timeout <= 0) {
+ debug("Device not ready for BSY, DRQ and"
+ "ERR in TFD!\n");
+ return -1;
+ }
+
+ writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
+ PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
+ PORT_CMD_START, &(port_mmio->cmd));
+
+ debug("Exit start port %d\n", port);
+
+ return 0;
+}
+
+int init_sata(int dev)
+{
+ int i;
+ u32 linkmap;
+ struct ahci_probe_ent *probe_ent = NULL;
+
+ if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) {
+ printf("The sata index %d is out of ranges\n\r", dev);
+ return -1;
+ }
+
+ ahci_init_one(dev);
+
+ probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ linkmap = probe_ent->link_port_map;
+
+ if (0 == linkmap) {
+ printf("No port device detected!\n");
+ return 1;
+ }
+
+ for (i = 0; i < probe_ent->n_ports; i++) {
+ if ((linkmap >> i) && ((linkmap >> i) & 0x01)) {
+ if (ahci_port_start(probe_ent, (u8)i)) {
+ printf("Can not start port %d\n", i);
+ return 1;
+ }
+ probe_ent->hard_port_no = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void dwc_ahsata_print_info(int dev)
+{
+ block_dev_desc_t *pdev = &(sata_dev_desc[dev]);
+
+ printf("SATA Device Info:\n\r");
+#ifdef CONFIG_SYS_64BIT_LBA
+ printf("S/N: %s\n\rProduct model number: %s\n\r"
+ "Firmware version: %s\n\rCapacity: %lld sectors\n\r",
+ pdev->product, pdev->vendor, pdev->revision, pdev->lba);
+#else
+ printf("S/N: %s\n\rProduct model number: %s\n\r"
+ "Firmware version: %s\n\rCapacity: %ld sectors\n\r",
+ pdev->product, pdev->vendor, pdev->revision, pdev->lba);
+#endif
+}
+
+static void dwc_ahsata_identify(int dev, u16 *id)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+ u8 port = probe_ent->hard_port_no;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = ATA_CMD_ID_ATA;
+
+ ahci_exec_ata_cmd(probe_ent, port, cfis,
+ (u8 *)id, ATA_ID_WORDS * 2, READ_CMD);
+ ata_swap_buf_le16(id, ATA_ID_WORDS);
+}
+
+static void dwc_ahsata_xfer_mode(int dev, u16 *id)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+
+ probe_ent->pio_mask = id[ATA_ID_PIO_MODES];
+ probe_ent->udma_mask = id[ATA_ID_UDMA_MODES];
+ debug("pio %04x, udma %04x\n\r",
+ probe_ent->pio_mask, probe_ent->udma_mask);
+}
+
+static u32 dwc_ahsata_rw_cmd(int dev, u32 start, u32 blkcnt,
+ u8 *buffer, int is_write)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+ u8 port = probe_ent->hard_port_no;
+ u32 block;
+
+ block = start;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
+ cfis->device = ATA_LBA;
+
+ cfis->device |= (block >> 24) & 0xf;
+ cfis->lba_high = (block >> 16) & 0xff;
+ cfis->lba_mid = (block >> 8) & 0xff;
+ cfis->lba_low = block & 0xff;
+ cfis->sector_count = (u8)(blkcnt & 0xff);
+
+ if (ahci_exec_ata_cmd(probe_ent, port, cfis,
+ buffer, ATA_SECT_SIZE * blkcnt, is_write) > 0)
+ return blkcnt;
+ else
+ return 0;
+}
+
+void dwc_ahsata_flush_cache(int dev)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+ u8 port = probe_ent->hard_port_no;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = ATA_CMD_FLUSH;
+
+ ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0);
+}
+
+static u32 dwc_ahsata_rw_cmd_ext(int dev, u32 start, lbaint_t blkcnt,
+ u8 *buffer, int is_write)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+ u8 port = probe_ent->hard_port_no;
+ u64 block;
+
+ block = (u64)start;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+
+ cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
+ : ATA_CMD_READ_EXT;
+
+ cfis->lba_high_exp = (block >> 40) & 0xff;
+ cfis->lba_mid_exp = (block >> 32) & 0xff;
+ cfis->lba_low_exp = (block >> 24) & 0xff;
+ cfis->lba_high = (block >> 16) & 0xff;
+ cfis->lba_mid = (block >> 8) & 0xff;
+ cfis->lba_low = block & 0xff;
+ cfis->device = ATA_LBA;
+ cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
+ cfis->sector_count = blkcnt & 0xff;
+
+ if (ahci_exec_ata_cmd(probe_ent, port, cfis, buffer,
+ ATA_SECT_SIZE * blkcnt, is_write) > 0)
+ return blkcnt;
+ else
+ return 0;
+}
+
+u32 dwc_ahsata_rw_ncq_cmd(int dev, u32 start, lbaint_t blkcnt,
+ u8 *buffer, int is_write)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+ u8 port = probe_ent->hard_port_no;
+ u64 block;
+
+ if (sata_dev_desc[dev].lba48 != 1) {
+ printf("execute FPDMA command on non-LBA48 hard disk\n\r");
+ return -1;
+ }
+
+ block = (u64)start;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+
+ cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE
+ : ATA_CMD_FPDMA_READ;
+
+ cfis->lba_high_exp = (block >> 40) & 0xff;
+ cfis->lba_mid_exp = (block >> 32) & 0xff;
+ cfis->lba_low_exp = (block >> 24) & 0xff;
+ cfis->lba_high = (block >> 16) & 0xff;
+ cfis->lba_mid = (block >> 8) & 0xff;
+ cfis->lba_low = block & 0xff;
+
+ cfis->device = ATA_LBA;
+ cfis->features_exp = (blkcnt >> 8) & 0xff;
+ cfis->features = blkcnt & 0xff;
+
+ /* Use the latest queue */
+ ahci_exec_ata_cmd(probe_ent, port, cfis,
+ buffer, ATA_SECT_SIZE * blkcnt, is_write);
+
+ return blkcnt;
+}
+
+void dwc_ahsata_flush_cache_ext(int dev)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ struct sata_fis_h2d h2d, *cfis = &h2d;
+ u8 port = probe_ent->hard_port_no;
+
+ memset(cfis, 0, sizeof(struct sata_fis_h2d));
+
+ cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
+ cfis->pm_port_c = 0x80; /* is command */
+ cfis->command = ATA_CMD_FLUSH_EXT;
+
+ ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0);
+}
+
+static void dwc_ahsata_init_wcache(int dev, u16 *id)
+{
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+
+ if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
+ probe_ent->flags |= SATA_FLAG_WCACHE;
+ if (ata_id_has_flush(id))
+ probe_ent->flags |= SATA_FLAG_FLUSH;
+ if (ata_id_has_flush_ext(id))
+ probe_ent->flags |= SATA_FLAG_FLUSH_EXT;
+}
+
+u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt,
+ void *buffer, int is_write)
+{
+ u32 start, blks;
+ u8 *addr;
+ int max_blks;
+
+ start = blknr;
+ blks = blkcnt;
+ addr = (u8 *)buffer;
+
+ max_blks = ATA_MAX_SECTORS_LBA48;
+
+ do {
+ if (blks > max_blks) {
+ if (max_blks != dwc_ahsata_rw_cmd_ext(dev, start,
+ max_blks, addr, is_write))
+ return 0;
+ start += max_blks;
+ blks -= max_blks;
+ addr += ATA_SECT_SIZE * max_blks;
+ } else {
+ if (blks != dwc_ahsata_rw_cmd_ext(dev, start,
+ blks, addr, is_write))
+ return 0;
+ start += blks;
+ blks = 0;
+ addr += ATA_SECT_SIZE * blks;
+ }
+ } while (blks != 0);
+
+ return blkcnt;
+}
+
+u32 ata_low_level_rw_lba28(int dev, u32 blknr, lbaint_t blkcnt,
+ void *buffer, int is_write)
+{
+ u32 start, blks;
+ u8 *addr;
+ int max_blks;
+
+ start = blknr;
+ blks = blkcnt;
+ addr = (u8 *)buffer;
+
+ max_blks = ATA_MAX_SECTORS;
+ do {
+ if (blks > max_blks) {
+ if (max_blks != dwc_ahsata_rw_cmd(dev, start,
+ max_blks, addr, is_write))
+ return 0;
+ start += max_blks;
+ blks -= max_blks;
+ addr += ATA_SECT_SIZE * max_blks;
+ } else {
+ if (blks != dwc_ahsata_rw_cmd(dev, start,
+ blks, addr, is_write))
+ return 0;
+ start += blks;
+ blks = 0;
+ addr += ATA_SECT_SIZE * blks;
+ }
+ } while (blks != 0);
+
+ return blkcnt;
+}
+
+/*
+ * SATA interface between low level driver and command layer
+ */
+ulong sata_read(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer)
+{
+ u32 rc;
+
+ if (sata_dev_desc[dev].lba48)
+ rc = ata_low_level_rw_lba48(dev, blknr, blkcnt,
+ buffer, READ_CMD);
+ else
+ rc = ata_low_level_rw_lba28(dev, blknr, blkcnt,
+ buffer, READ_CMD);
+ return rc;
+}
+
+ulong sata_write(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer)
+{
+ u32 rc;
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ u32 flags = probe_ent->flags;
+
+ if (sata_dev_desc[dev].lba48) {
+ rc = ata_low_level_rw_lba48(dev, blknr, blkcnt,
+ buffer, WRITE_CMD);
+ if ((flags & SATA_FLAG_WCACHE) &&
+ (flags & SATA_FLAG_FLUSH_EXT))
+ dwc_ahsata_flush_cache_ext(dev);
+ } else {
+ rc = ata_low_level_rw_lba28(dev, blknr, blkcnt,
+ buffer, WRITE_CMD);
+ if ((flags & SATA_FLAG_WCACHE) &&
+ (flags & SATA_FLAG_FLUSH))
+ dwc_ahsata_flush_cache(dev);
+ }
+ return rc;
+}
+
+int scan_sata(int dev)
+{
+ u8 serial[ATA_ID_SERNO_LEN + 1] = { 0 };
+ u8 firmware[ATA_ID_FW_REV_LEN + 1] = { 0 };
+ u8 product[ATA_ID_PROD_LEN + 1] = { 0 };
+ u16 *id;
+ u64 n_sectors;
+ struct ahci_probe_ent *probe_ent =
+ (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
+ u8 port = probe_ent->hard_port_no;
+ block_dev_desc_t *pdev = &(sata_dev_desc[dev]);
+
+ id = (u16 *)malloc(ATA_ID_WORDS * 2);
+ if (!id) {
+ printf("id malloc failed\n\r");
+ return -1;
+ }
+
+ /* Identify device to get information */
+ dwc_ahsata_identify(dev, id);
+
+ /* Serial number */
+ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+ memcpy(pdev->product, serial, sizeof(serial));
+
+ /* Firmware version */
+ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
+ memcpy(pdev->revision, firmware, sizeof(firmware));
+
+ /* Product model */
+ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
+ memcpy(pdev->vendor, product, sizeof(product));
+
+ /* Totoal sectors */
+ n_sectors = ata_id_n_sectors(id);
+ pdev->lba = (u32)n_sectors;
+
+ pdev->type = DEV_TYPE_HARDDISK;
+ pdev->blksz = ATA_SECT_SIZE;
+ pdev->lun = 0 ;
+
+ /* Check if support LBA48 */
+ if (ata_id_has_lba48(id)) {
+ pdev->lba48 = 1;
+ debug("Device support LBA48\n\r");
+ }
+
+ /* Get the NCQ queue depth from device */
+ probe_ent->flags &= (~SATA_FLAG_Q_DEP_MASK);
+ probe_ent->flags |= ata_id_queue_depth(id);
+
+ /* Get the xfer mode from device */
+ dwc_ahsata_xfer_mode(dev, id);
+
+ /* Get the write cache status from device */
+ dwc_ahsata_init_wcache(dev, id);
+
+ /* Set the xfer mode to highest speed */
+ ahci_set_feature(dev, port);
+
+ free((void *)id);
+
+ dwc_ahsata_print_info(dev);
+
+ is_ready = 1;
+
+ return 0;
+}
diff --git a/drivers/block/dwc_ahsata.h b/drivers/block/dwc_ahsata.h
new file mode 100644
index 0000000000..84860ea492
--- /dev/null
+++ b/drivers/block/dwc_ahsata.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Terry Lv <r65388@freescale.com>
+ *
+ * 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
+ */
+
+#ifndef __FSL_SATA_H__
+#define __FSL_SATA_H__
+
+#define DWC_AHSATA_MAX_CMD_SLOTS 32
+
+/* Max host controller numbers */
+#define SATA_HC_MAX_NUM 4
+/* Max command queue depth per host controller */
+#define DWC_AHSATA_HC_MAX_CMD 32
+/* Max port number per host controller */
+#define SATA_HC_MAX_PORT 16
+
+/* Generic Host Register */
+
+/* HBA Capabilities Register */
+#define SATA_HOST_CAP_S64A 0x80000000
+#define SATA_HOST_CAP_SNCQ 0x40000000
+#define SATA_HOST_CAP_SSNTF 0x20000000
+#define SATA_HOST_CAP_SMPS 0x10000000
+#define SATA_HOST_CAP_SSS 0x08000000
+#define SATA_HOST_CAP_SALP 0x04000000
+#define SATA_HOST_CAP_SAL 0x02000000
+#define SATA_HOST_CAP_SCLO 0x01000000
+#define SATA_HOST_CAP_ISS_MASK 0x00f00000
+#define SATA_HOST_CAP_ISS_OFFSET 20
+#define SATA_HOST_CAP_SNZO 0x00080000
+#define SATA_HOST_CAP_SAM 0x00040000
+#define SATA_HOST_CAP_SPM 0x00020000
+#define SATA_HOST_CAP_PMD 0x00008000
+#define SATA_HOST_CAP_SSC 0x00004000
+#define SATA_HOST_CAP_PSC 0x00002000
+#define SATA_HOST_CAP_NCS 0x00001f00
+#define SATA_HOST_CAP_CCCS 0x00000080
+#define SATA_HOST_CAP_EMS 0x00000040
+#define SATA_HOST_CAP_SXS 0x00000020
+#define SATA_HOST_CAP_NP_MASK 0x0000001f
+
+/* Global HBA Control Register */
+#define SATA_HOST_GHC_AE 0x80000000
+#define SATA_HOST_GHC_IE 0x00000002
+#define SATA_HOST_GHC_HR 0x00000001
+
+/* Interrupt Status Register */
+
+/* Ports Implemented Register */
+
+/* AHCI Version Register */
+#define SATA_HOST_VS_MJR_MASK 0xffff0000
+#define SATA_HOST_VS_MJR_OFFSET 16
+#define SATA_HOST_VS_MJR_MNR 0x0000ffff
+
+/* Command Completion Coalescing Control */
+#define SATA_HOST_CCC_CTL_TV_MASK 0xffff0000
+#define SATA_HOST_CCC_CTL_TV_OFFSET 16
+#define SATA_HOST_CCC_CTL_CC_MASK 0x0000ff00
+#define SATA_HOST_CCC_CTL_CC_OFFSET 8
+#define SATA_HOST_CCC_CTL_INT_MASK 0x000000f8
+#define SATA_HOST_CCC_CTL_INT_OFFSET 3
+#define SATA_HOST_CCC_CTL_EN 0x00000001
+
+/* Command Completion Coalescing Ports */
+
+/* HBA Capabilities Extended Register */
+#define SATA_HOST_CAP2_APST 0x00000004
+
+/* BIST Activate FIS Register */
+#define SATA_HOST_BISTAFR_NCP_MASK 0x0000ff00
+#define SATA_HOST_BISTAFR_NCP_OFFSET 8
+#define SATA_HOST_BISTAFR_PD_MASK 0x000000ff
+#define SATA_HOST_BISTAFR_PD_OFFSET 0
+
+/* BIST Control Register */
+#define SATA_HOST_BISTCR_FERLB 0x00100000
+#define SATA_HOST_BISTCR_TXO 0x00040000
+#define SATA_HOST_BISTCR_CNTCLR 0x00020000
+#define SATA_HOST_BISTCR_NEALB 0x00010000
+#define SATA_HOST_BISTCR_LLC_MASK 0x00000700
+#define SATA_HOST_BISTCR_LLC_OFFSET 8
+#define SATA_HOST_BISTCR_ERREN 0x00000040
+#define SATA_HOST_BISTCR_FLIP 0x00000020
+#define SATA_HOST_BISTCR_PV 0x00000010
+#define SATA_HOST_BISTCR_PATTERN_MASK 0x0000000f
+#define SATA_HOST_BISTCR_PATTERN_OFFSET 0
+
+/* BIST FIS Count Register */
+
+/* BIST Status Register */
+#define SATA_HOST_BISTSR_FRAMERR_MASK 0x0000ffff
+#define SATA_HOST_BISTSR_FRAMERR_OFFSET 0
+#define SATA_HOST_BISTSR_BRSTERR_MASK 0x00ff0000
+#define SATA_HOST_BISTSR_BRSTERR_OFFSET 16
+
+/* BIST DWORD Error Count Register */
+
+/* OOB Register*/
+#define SATA_HOST_OOBR_WE 0x80000000
+#define SATA_HOST_OOBR_cwMin_MASK 0x7f000000
+#define SATA_HOST_OOBR_cwMAX_MASK 0x00ff0000
+#define SATA_HOST_OOBR_ciMin_MASK 0x0000ff00
+#define SATA_HOST_OOBR_ciMax_MASK 0x000000ff
+
+/* Timer 1-ms Register */
+
+/* Global Parameter 1 Register */
+#define SATA_HOST_GPARAM1R_ALIGN_M 0x80000000
+#define SATA_HOST_GPARAM1R_RX_BUFFER 0x40000000
+#define SATA_HOST_GPARAM1R_PHY_DATA_MASK 0x30000000
+#define SATA_HOST_GPARAM1R_PHY_RST 0x08000000
+#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK 0x07e00000
+#define SATA_HOST_GPARAM1R_PHY_STAT_MASK 0x001f8000
+#define SATA_HOST_GPARAM1R_LATCH_M 0x00004000
+#define SATA_HOST_GPARAM1R_BIST_M 0x00002000
+#define SATA_HOST_GPARAM1R_PHY_TYPE 0x00001000
+#define SATA_HOST_GPARAM1R_RETURN_ERR 0x00000400
+#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK 0x00000300
+#define SATA_HOST_GPARAM1R_S_HADDR 0X00000080
+#define SATA_HOST_GPARAM1R_M_HADDR 0X00000040
+
+/* Global Parameter 2 Register */
+#define SATA_HOST_GPARAM2R_DEV_CP 0x00004000
+#define SATA_HOST_GPARAM2R_DEV_MP 0x00002000
+#define SATA_HOST_GPARAM2R_DEV_ENCODE_M 0x00001000
+#define SATA_HOST_GPARAM2R_RXOOB_CLK_M 0x00000800
+#define SATA_HOST_GPARAM2R_RXOOB_M 0x00000400
+#define SATA_HOST_GPARAM2R_TX_OOB_M 0x00000200
+#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK 0x000001ff
+
+/* Port Parameter Register */
+#define SATA_HOST_PPARAMR_TX_MEM_M 0x00000200
+#define SATA_HOST_PPARAMR_TX_MEM_S 0x00000100
+#define SATA_HOST_PPARAMR_RX_MEM_M 0x00000080
+#define SATA_HOST_PPARAMR_RX_MEM_S 0x00000040
+#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK 0x00000038
+#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK 0x00000007
+
+/* Test Register */
+#define SATA_HOST_TESTR_PSEL_MASK 0x00070000
+#define SATA_HOST_TESTR_TEST_IF 0x00000001
+
+/* Port Register Descriptions */
+/* Port# Command List Base Address Register */
+#define SATA_PORT_CLB_CLB_MASK 0xfffffc00
+
+/* Port# Command List Base Address Upper 32-Bits Register */
+
+/* Port# FIS Base Address Register */
+#define SATA_PORT_FB_FB_MASK 0xfffffff0
+
+/* Port# FIS Base Address Upper 32-Bits Register */
+
+/* Port# Interrupt Status Register */
+#define SATA_PORT_IS_CPDS 0x80000000
+#define SATA_PORT_IS_TFES 0x40000000
+#define SATA_PORT_IS_HBFS 0x20000000
+#define SATA_PORT_IS_HBDS 0x10000000
+#define SATA_PORT_IS_IFS 0x08000000
+#define SATA_PORT_IS_INFS 0x04000000
+#define SATA_PORT_IS_OFS 0x01000000
+#define SATA_PORT_IS_IPMS 0x00800000
+#define SATA_PORT_IS_PRCS 0x00400000
+#define SATA_PORT_IS_DMPS 0x00000080
+#define SATA_PORT_IS_PCS 0x00000040
+#define SATA_PORT_IS_DPS 0x00000020
+#define SATA_PORT_IS_UFS 0x00000010
+#define SATA_PORT_IS_SDBS 0x00000008
+#define SATA_PORT_IS_DSS 0x00000004
+#define SATA_PORT_IS_PSS 0x00000002
+#define SATA_PORT_IS_DHRS 0x00000001
+
+/* Port# Interrupt Enable Register */
+#define SATA_PORT_IE_CPDE 0x80000000
+#define SATA_PORT_IE_TFEE 0x40000000
+#define SATA_PORT_IE_HBFE 0x20000000
+#define SATA_PORT_IE_HBDE 0x10000000
+#define SATA_PORT_IE_IFE 0x08000000
+#define SATA_PORT_IE_INFE 0x04000000
+#define SATA_PORT_IE_OFE 0x01000000
+#define SATA_PORT_IE_IPME 0x00800000
+#define SATA_PORT_IE_PRCE 0x00400000
+#define SATA_PORT_IE_DMPE 0x00000080
+#define SATA_PORT_IE_PCE 0x00000040
+#define SATA_PORT_IE_DPE 0x00000020
+#define SATA_PORT_IE_UFE 0x00000010
+#define SATA_PORT_IE_SDBE 0x00000008
+#define SATA_PORT_IE_DSE 0x00000004
+#define SATA_PORT_IE_PSE 0x00000002
+#define SATA_PORT_IE_DHRE 0x00000001
+
+/* Port# Command Register */
+#define SATA_PORT_CMD_ICC_MASK 0xf0000000
+#define SATA_PORT_CMD_ASP 0x08000000
+#define SATA_PORT_CMD_ALPE 0x04000000
+#define SATA_PORT_CMD_DLAE 0x02000000
+#define SATA_PORT_CMD_ATAPI 0x01000000
+#define SATA_PORT_CMD_APSTE 0x00800000
+#define SATA_PORT_CMD_ESP 0x00200000
+#define SATA_PORT_CMD_CPD 0x00100000
+#define SATA_PORT_CMD_MPSP 0x00080000
+#define SATA_PORT_CMD_HPCP 0x00040000
+#define SATA_PORT_CMD_PMA 0x00020000
+#define SATA_PORT_CMD_CPS 0x00010000
+#define SATA_PORT_CMD_CR 0x00008000
+#define SATA_PORT_CMD_FR 0x00004000
+#define SATA_PORT_CMD_MPSS 0x00002000
+#define SATA_PORT_CMD_CCS_MASK 0x00001f00
+#define SATA_PORT_CMD_FRE 0x00000010
+#define SATA_PORT_CMD_CLO 0x00000008
+#define SATA_PORT_CMD_POD 0x00000004
+#define SATA_PORT_CMD_SUD 0x00000002
+#define SATA_PORT_CMD_ST 0x00000001
+
+/* Port# Task File Data Register */
+#define SATA_PORT_TFD_ERR_MASK 0x0000ff00
+#define SATA_PORT_TFD_STS_MASK 0x000000ff
+#define SATA_PORT_TFD_STS_ERR 0x00000001
+#define SATA_PORT_TFD_STS_DRQ 0x00000008
+#define SATA_PORT_TFD_STS_BSY 0x00000080
+
+/* Port# Signature Register */
+
+/* Port# Serial ATA Status {SStatus} Register */
+#define SATA_PORT_SSTS_IPM_MASK 0x00000f00
+#define SATA_PORT_SSTS_SPD_MASK 0x000000f0
+#define SATA_PORT_SSTS_DET_MASK 0x0000000f
+
+/* Port# Serial ATA Control {SControl} Register */
+#define SATA_PORT_SCTL_IPM_MASK 0x00000f00
+#define SATA_PORT_SCTL_SPD_MASK 0x000000f0
+#define SATA_PORT_SCTL_DET_MASK 0x0000000f
+
+/* Port# Serial ATA Error {SError} Register */
+#define SATA_PORT_SERR_DIAG_X 0x04000000
+#define SATA_PORT_SERR_DIAG_F 0x02000000
+#define SATA_PORT_SERR_DIAG_T 0x01000000
+#define SATA_PORT_SERR_DIAG_S 0x00800000
+#define SATA_PORT_SERR_DIAG_H 0x00400000
+#define SATA_PORT_SERR_DIAG_C 0x00200000
+#define SATA_PORT_SERR_DIAG_D 0x00100000
+#define SATA_PORT_SERR_DIAG_B 0x00080000
+#define SATA_PORT_SERR_DIAG_W 0x00040000
+#define SATA_PORT_SERR_DIAG_I 0x00020000
+#define SATA_PORT_SERR_DIAG_N 0x00010000
+#define SATA_PORT_SERR_ERR_E 0x00000800
+#define SATA_PORT_SERR_ERR_P 0x00000400
+#define SATA_PORT_SERR_ERR_C 0x00000200
+#define SATA_PORT_SERR_ERR_T 0x00000100
+#define SATA_PORT_SERR_ERR_M 0x00000002
+#define SATA_PORT_SERR_ERR_I 0x00000001
+
+/* Port# Serial ATA Active {SActive} Register */
+
+/* Port# Command Issue Register */
+
+/* Port# Serial ATA Notification Register */
+
+/* Port# DMA Control Register */
+#define SATA_PORT_DMACR_RXABL_MASK 0x0000f000
+#define SATA_PORT_DMACR_TXABL_MASK 0x00000f00
+#define SATA_PORT_DMACR_RXTS_MASK 0x000000f0
+#define SATA_PORT_DMACR_TXTS_MASK 0x0000000f
+
+/* Port# PHY Control Register */
+
+/* Port# PHY Status Register */
+
+#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
+
+/* DW0
+*/
+#define CMD_HDR_DI_CFL_MASK 0x0000001f
+#define CMD_HDR_DI_CFL_OFFSET 0
+#define CMD_HDR_DI_A 0x00000020
+#define CMD_HDR_DI_W 0x00000040
+#define CMD_HDR_DI_P 0x00000080
+#define CMD_HDR_DI_R 0x00000100
+#define CMD_HDR_DI_B 0x00000200
+#define CMD_HDR_DI_C 0x00000400
+#define CMD_HDR_DI_PMP_MASK 0x0000f000
+#define CMD_HDR_DI_PMP_OFFSET 12
+#define CMD_HDR_DI_PRDTL 0xffff0000
+#define CMD_HDR_DI_PRDTL_OFFSET 16
+
+/* prde_fis_len
+*/
+#define CMD_HDR_PRD_ENTRY_SHIFT 16
+#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
+#define CMD_HDR_FIS_LEN_SHIFT 2
+
+/* attribute
+*/
+#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
+#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
+/* Snoop enable for all descriptor */
+#define CMD_HDR_ATTR_SNOOP 0x00000200
+#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
+#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
+/* BIST - require the host to enter BIST mode */
+#define CMD_HDR_ATTR_BIST 0x00000040
+#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
+#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
+
+#define FLAGS_DMA 0x00000000
+#define FLAGS_FPDMA 0x00000001
+
+#define SATA_FLAG_Q_DEP_MASK 0x0000000f
+#define SATA_FLAG_WCACHE 0x00000100
+#define SATA_FLAG_FLUSH 0x00000200
+#define SATA_FLAG_FLUSH_EXT 0x00000400
+
+#define READ_CMD 0
+#define WRITE_CMD 1
+
+extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
+
+#endif /* __FSL_SATA_H__ */
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 5d864b56ef..c01ffa54fe 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -29,6 +29,7 @@ COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o
COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o
COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o
+COBJS-$(CONFIG_NAND_RDA_DMA) += rda_dma.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/dma/rda_dma.c b/drivers/dma/rda_dma.c
new file mode 100644
index 0000000000..c7f74c9930
--- /dev/null
+++ b/drivers/dma/rda_dma.c
@@ -0,0 +1,143 @@
+/***************************************************************
+*
+* Copyright(C) RDA Micro Company.,2012
+* All Rights Reserved. Confidential
+*
+****************************************************************
+*
+* Project: RDA8810
+* File Name: drivers/dma/rda_dma.c
+*
+* Author: Jason Tao
+* Creation Date: 2012-11-15
+*
+*****************************************************************
+*
+* Implementation of functions of DMA
+*
+*****************************************************************
+*/
+
+#include <common.h>
+#include <asm/arch/rda_iomap.h>
+#include <asm/arch/dma.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <linux/compiler.h>
+
+/* 0x20820000 */
+static void __iomem *dma_base = (void __iomem *)RDA_DMA_BASE;
+
+static inline void __dma_write(u32 val, u32 offset, u8 ch)
+{
+ __raw_writel(val, dma_base + offset);
+}
+
+static inline u32 __dma_read(u32 offset, u8 ch)
+{
+ u32 val;
+
+ val = __raw_readl(dma_base + offset);
+ return val;
+}
+
+int rda_set_dma_params(u8 ch, struct rda_dma_chan_params *params)
+{
+ dma_addr_t src;
+ dma_addr_t dst;
+ u32 size;
+ u32 mode;
+
+ if (!params) {
+ printf("rda8810 dma : Invalid parameter!\n");
+ return -EINVAL;
+ }
+
+ src = params->src_addr;
+ dst = params->dst_addr;
+ size = params->xfer_size;
+ mode = (params->dma_mode & RDA_DMA_MODE_MASK);
+#if 0
+ printf("[rda_dma] : src = 0x%08x, dst = 0x%08x, size = 0x%x\n", src,
+ dst, size);
+#endif /* #if 0 */
+ switch (mode) {
+ case RDA_DMA_FW_MODE:
+ /*
+ * DMA transits data by a word(4 bytes).
+ * The mask is only for NANDFC that supports no more than 2048 words(8192 bytes),
+ * but our address's unit is byte. So we have to mask 0x1FFF.
+ */
+ dst &= 0x1FFF;
+ break;
+
+ case RDA_DMA_FR_MODE:
+ src &= 0x1FFF;
+ break;
+
+ case RDA_DMA_NOR_MODE:
+ /* Nothing to do */
+ break;
+
+ default:
+ printf("[rda8810 dma] : Invalid mode of dma!\r\n");
+ return -EINVAL;
+ }
+
+ /* Fill src address */
+ __dma_write(src, RDA_DMA_SRC_REG, ch);
+ /* Fill dst address */
+ __dma_write(dst, RDA_DMA_DST_REG, ch);
+ /* Fill size */
+ __dma_write(size, RDA_DMA_XFER_SIZE_REG, ch);
+
+ /* Set ctl flag */
+ __dma_write(mode, RDA_DMA_CTL_REG, ch);
+
+ return 0;
+}
+
+void rda_start_dma(u8 ch)
+{
+ u32 reg = 0;
+
+ reg = __dma_read(RDA_DMA_CTL_REG, ch);
+
+ reg |= RDA_DMA_CTL_EN;
+ __dma_write(reg, RDA_DMA_CTL_REG, ch);
+
+ return;
+}
+
+void rda_stop_dma(u8 ch)
+{
+ u32 reg = RDA_DMA_CTL_INT_CLE;
+
+ __dma_write(reg, RDA_DMA_CTL_REG, ch);
+ return;
+}
+
+void rda_poll_dma(u8 ch)
+{
+ u32 reg = 0;
+
+ while (!((reg = __dma_read(RDA_DMA_STATUS_REG, ch)) & RDA_DMA_STA_INT)) {
+ asm("nop");
+ }
+
+ return;
+}
+
+int rda_request_dma(u8 * dma_ch_out)
+{
+ /* Clear interrupt flag and disble dma. */
+ rda_stop_dma(0);
+ *dma_ch_out = 0;
+
+ return 0;
+}
+
+void rda_free_dma(u8 ch)
+{
+ return;
+}
diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c
index f1b1c16b1b..661553552e 100644
--- a/drivers/gpio/mxc_gpio.c
+++ b/drivers/gpio/mxc_gpio.c
@@ -41,7 +41,8 @@ static unsigned long gpio_ports[] = {
[0] = GPIO1_BASE_ADDR,
[1] = GPIO2_BASE_ADDR,
[2] = GPIO3_BASE_ADDR,
-#if defined(CONFIG_MX51) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
+#if defined(CONFIG_MX25) || defined(CONFIG_MX51) || defined(CONFIG_MX53) || \
+ defined(CONFIG_MX6Q)
[3] = GPIO4_BASE_ADDR,
#endif
#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index f86e46c111..5dbdbe3672 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -27,6 +27,7 @@ LIB := $(obj)libi2c.o
COBJS-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o
COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o
+COBJS-$(CONFIG_DW_I2C) += designware_i2c.o
COBJS-$(CONFIG_FSL_I2C) += fsl_i2c.o
COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o
COBJS-$(CONFIG_I2C_MV) += mv_i2c.o
@@ -40,11 +41,11 @@ COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o
COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o
COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o
COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o
-COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o
COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o
COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
+COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/i2c/spr_i2c.c b/drivers/i2c/designware_i2c.c
index eabfe843f7..6d118acec4 100644
--- a/drivers/i2c/spr_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -24,7 +24,7 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
-#include <asm/arch/spr_i2c.h>
+#include "designware_i2c.h"
static struct i2c_regs *const i2c_regs_p =
(struct i2c_regs *)CONFIG_SYS_I2C_BASE;
@@ -40,6 +40,13 @@ static void set_speed(int i2c_spd)
unsigned int cntl;
unsigned int hcnt, lcnt;
unsigned int high, low;
+ unsigned int enbl;
+
+ /* to set speed cltr must be disabled */
+ enbl = readl(&i2c_regs_p->ic_enable);
+ enbl &= ~IC_ENABLE_0B;
+ writel(enbl, &i2c_regs_p->ic_enable);
+
cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK));
@@ -71,6 +78,10 @@ static void set_speed(int i2c_spd)
lcnt = (IC_CLK * low) / NANO_TO_MICRO;
writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt);
+
+ /* re-enable i2c ctrl back now that speed is set */
+ enbl |= IC_ENABLE_0B;
+ writel(enbl, &i2c_regs_p->ic_enable);
}
/*
@@ -113,7 +124,7 @@ int i2c_get_bus_speed(void)
/*
* i2c_init - Init function
* @speed: required i2c speed
- * @slaveadd: slave address for the spear device
+ * @slaveadd: slave address for the device
*
* Initialization function.
*/
diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h
new file mode 100644
index 0000000000..03b520ed43
--- /dev/null
+++ b/drivers/i2c/designware_i2c.h
@@ -0,0 +1,146 @@
+/*
+ * (C) Copyright 2009
+ * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+#ifndef __DW_I2C_H_
+#define __DW_I2C_H_
+
+struct i2c_regs {
+ u32 ic_con;
+ u32 ic_tar;
+ u32 ic_sar;
+ u32 ic_hs_maddr;
+ u32 ic_cmd_data;
+ u32 ic_ss_scl_hcnt;
+ u32 ic_ss_scl_lcnt;
+ u32 ic_fs_scl_hcnt;
+ u32 ic_fs_scl_lcnt;
+ u32 ic_hs_scl_hcnt;
+ u32 ic_hs_scl_lcnt;
+ u32 ic_intr_stat;
+ u32 ic_intr_mask;
+ u32 ic_raw_intr_stat;
+ u32 ic_rx_tl;
+ u32 ic_tx_tl;
+ u32 ic_clr_intr;
+ u32 ic_clr_rx_under;
+ u32 ic_clr_rx_over;
+ u32 ic_clr_tx_over;
+ u32 ic_clr_rd_req;
+ u32 ic_clr_tx_abrt;
+ u32 ic_clr_rx_done;
+ u32 ic_clr_activity;
+ u32 ic_clr_stop_det;
+ u32 ic_clr_start_det;
+ u32 ic_clr_gen_call;
+ u32 ic_enable;
+ u32 ic_status;
+ u32 ic_txflr;
+ u32 ix_rxflr;
+ u32 reserved_1;
+ u32 ic_tx_abrt_source;
+};
+
+#define IC_CLK 166
+#define NANO_TO_MICRO 1000
+
+/* High and low times in different speed modes (in ns) */
+#define MIN_SS_SCL_HIGHTIME 4000
+#define MIN_SS_SCL_LOWTIME 5000
+#define MIN_FS_SCL_HIGHTIME 800
+#define MIN_FS_SCL_LOWTIME 1700
+#define MIN_HS_SCL_HIGHTIME 60
+#define MIN_HS_SCL_LOWTIME 160
+
+/* Worst case timeout for 1 byte is kept as 2ms */
+#define I2C_BYTE_TO (CONFIG_SYS_HZ/500)
+#define I2C_STOPDET_TO (CONFIG_SYS_HZ/500)
+#define I2C_BYTE_TO_BB (I2C_BYTE_TO * 16)
+
+/* i2c control register definitions */
+#define IC_CON_SD 0x0040
+#define IC_CON_RE 0x0020
+#define IC_CON_10BITADDRMASTER 0x0010
+#define IC_CON_10BITADDR_SLAVE 0x0008
+#define IC_CON_SPD_MSK 0x0006
+#define IC_CON_SPD_SS 0x0002
+#define IC_CON_SPD_FS 0x0004
+#define IC_CON_SPD_HS 0x0006
+#define IC_CON_MM 0x0001
+
+/* i2c target address register definitions */
+#define TAR_ADDR 0x0050
+
+/* i2c slave address register definitions */
+#define IC_SLAVE_ADDR 0x0002
+
+/* i2c data buffer and command register definitions */
+#define IC_CMD 0x0100
+
+/* i2c interrupt status register definitions */
+#define IC_GEN_CALL 0x0800
+#define IC_START_DET 0x0400
+#define IC_STOP_DET 0x0200
+#define IC_ACTIVITY 0x0100
+#define IC_RX_DONE 0x0080
+#define IC_TX_ABRT 0x0040
+#define IC_RD_REQ 0x0020
+#define IC_TX_EMPTY 0x0010
+#define IC_TX_OVER 0x0008
+#define IC_RX_FULL 0x0004
+#define IC_RX_OVER 0x0002
+#define IC_RX_UNDER 0x0001
+
+/* fifo threshold register definitions */
+#define IC_TL0 0x00
+#define IC_TL1 0x01
+#define IC_TL2 0x02
+#define IC_TL3 0x03
+#define IC_TL4 0x04
+#define IC_TL5 0x05
+#define IC_TL6 0x06
+#define IC_TL7 0x07
+#define IC_RX_TL IC_TL0
+#define IC_TX_TL IC_TL0
+
+/* i2c enable register definitions */
+#define IC_ENABLE_0B 0x0001
+
+/* i2c status register definitions */
+#define IC_STATUS_SA 0x0040
+#define IC_STATUS_MA 0x0020
+#define IC_STATUS_RFF 0x0010
+#define IC_STATUS_RFNE 0x0008
+#define IC_STATUS_TFE 0x0004
+#define IC_STATUS_TFNF 0x0002
+#define IC_STATUS_ACT 0x0001
+
+/* Speed Selection */
+#define IC_SPEED_MODE_STANDARD 1
+#define IC_SPEED_MODE_FAST 2
+#define IC_SPEED_MODE_MAX 3
+
+#define I2C_MAX_SPEED 3400000
+#define I2C_FAST_SPEED 400000
+#define I2C_STANDARD_SPEED 100000
+
+#endif /* __DW_I2C_H_ */
diff --git a/drivers/i2c/sh_sh7734_i2c.c b/drivers/i2c/sh_sh7734_i2c.c
new file mode 100644
index 0000000000..9da173d31e
--- /dev/null
+++ b/drivers/i2c/sh_sh7734_i2c.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * 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 <common.h>
+#include <i2c.h>
+#include <asm/io.h>
+
+struct sh_i2c {
+ u8 iccr1;
+ u8 iccr2;
+ u8 icmr;
+ u8 icier;
+ u8 icsr;
+ u8 sar;
+ u8 icdrt;
+ u8 icdrr;
+ u8 nf2cyc;
+ u8 __pad0;
+ u8 __pad1;
+};
+
+static struct sh_i2c *base;
+static u8 iccr1_cks, nf2cyc;
+
+/* ICCR1 */
+#define SH_I2C_ICCR1_ICE (1 << 7)
+#define SH_I2C_ICCR1_RCVD (1 << 6)
+#define SH_I2C_ICCR1_MST (1 << 5)
+#define SH_I2C_ICCR1_TRS (1 << 4)
+#define SH_I2C_ICCR1_MTRS \
+ (SH_I2C_ICCR1_MST | SH_I2C_ICCR1_TRS)
+
+/* ICCR1 */
+#define SH_I2C_ICCR2_BBSY (1 << 7)
+#define SH_I2C_ICCR2_SCP (1 << 6)
+#define SH_I2C_ICCR2_SDAO (1 << 5)
+#define SH_I2C_ICCR2_SDAOP (1 << 4)
+#define SH_I2C_ICCR2_SCLO (1 << 3)
+#define SH_I2C_ICCR2_IICRST (1 << 1)
+
+#define SH_I2C_ICIER_TIE (1 << 7)
+#define SH_I2C_ICIER_TEIE (1 << 6)
+#define SH_I2C_ICIER_RIE (1 << 5)
+#define SH_I2C_ICIER_NAKIE (1 << 4)
+#define SH_I2C_ICIER_STIE (1 << 3)
+#define SH_I2C_ICIER_ACKE (1 << 2)
+#define SH_I2C_ICIER_ACKBR (1 << 1)
+#define SH_I2C_ICIER_ACKBT (1 << 0)
+
+#define SH_I2C_ICSR_TDRE (1 << 7)
+#define SH_I2C_ICSR_TEND (1 << 6)
+#define SH_I2C_ICSR_RDRF (1 << 5)
+#define SH_I2C_ICSR_NACKF (1 << 4)
+#define SH_I2C_ICSR_STOP (1 << 3)
+#define SH_I2C_ICSR_ALOVE (1 << 2)
+#define SH_I2C_ICSR_AAS (1 << 1)
+#define SH_I2C_ICSR_ADZ (1 << 0)
+
+#define IRQ_WAIT 1000
+
+static void sh_i2c_send_stop(struct sh_i2c *base)
+{
+ clrbits_8(&base->iccr2, SH_I2C_ICCR2_BBSY | SH_I2C_ICCR2_SCP);
+}
+
+static int check_icsr_bits(struct sh_i2c *base, u8 bits)
+{
+ int i;
+
+ for (i = 0; i < IRQ_WAIT; i++) {
+ if (bits & readb(&base->icsr))
+ return 0;
+ udelay(10);
+ }
+
+ return 1;
+}
+
+static int check_stop(struct sh_i2c *base)
+{
+ int ret = check_icsr_bits(base, SH_I2C_ICSR_STOP);
+ clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
+
+ return ret;
+}
+
+static int check_tend(struct sh_i2c *base, int stop)
+{
+ int ret = check_icsr_bits(base, SH_I2C_ICSR_TEND);
+
+ if (stop) {
+ clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
+ sh_i2c_send_stop(base);
+ }
+
+ clrbits_8(&base->icsr, SH_I2C_ICSR_TEND);
+ return ret;
+}
+
+static int check_tdre(struct sh_i2c *base)
+{
+ return check_icsr_bits(base, SH_I2C_ICSR_TDRE);
+}
+
+static int check_rdrf(struct sh_i2c *base)
+{
+ return check_icsr_bits(base, SH_I2C_ICSR_RDRF);
+}
+
+static int check_bbsy(struct sh_i2c *base)
+{
+ int i;
+
+ for (i = 0 ; i < IRQ_WAIT ; i++) {
+ if (!(SH_I2C_ICCR2_BBSY & readb(&base->iccr2)))
+ return 0;
+ udelay(10);
+ }
+ return 1;
+}
+
+static int check_ackbr(struct sh_i2c *base)
+{
+ int i;
+
+ for (i = 0 ; i < IRQ_WAIT ; i++) {
+ if (!(SH_I2C_ICIER_ACKBR & readb(&base->icier)))
+ return 0;
+ udelay(10);
+ }
+
+ return 1;
+}
+
+static void sh_i2c_reset(struct sh_i2c *base)
+{
+ setbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
+
+ udelay(100);
+
+ clrbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
+}
+
+static int i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg)
+{
+ if (check_bbsy(base)) {
+ puts("i2c bus busy\n");
+ goto fail;
+ }
+
+ setbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
+ clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
+
+ writeb((id << 1), &base->icdrt);
+
+ if (check_tend(base, 0)) {
+ puts("TEND check fail...\n");
+ goto fail;
+ }
+
+ if (check_ackbr(base)) {
+ check_tend(base, 0);
+ sh_i2c_send_stop(base);
+ goto fail;
+ }
+
+ writeb(reg, &base->icdrt);
+
+ if (check_tdre(base)) {
+ puts("TDRE check fail...\n");
+ goto fail;
+ }
+
+ if (check_tend(base, 0)) {
+ puts("TEND check fail...\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+
+ return 1;
+}
+
+static int
+i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 *val, int size)
+{
+ int i;
+
+ if (i2c_set_addr(base, id, reg)) {
+ puts("Fail set slave address\n");
+ return 1;
+ }
+
+ for (i = 0; i < size; i++) {
+ writeb(val[i], &base->icdrt);
+ check_tdre(base);
+ }
+
+ check_tend(base, 1);
+ check_stop(base);
+
+ udelay(100);
+
+ clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
+ clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
+ sh_i2c_reset(base);
+
+ return 0;
+}
+
+static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg)
+{
+ u8 ret = 0;
+
+ if (i2c_set_addr(base, id, reg)) {
+ puts("Fail set slave address\n");
+ goto fail;
+ }
+
+ clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
+ writeb((id << 1) | 1, &base->icdrt);
+
+ if (check_tend(base, 0))
+ puts("TDRE check fail...\n");
+
+ clrsetbits_8(&base->iccr1, SH_I2C_ICCR1_TRS, SH_I2C_ICCR1_MST);
+ clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
+ setbits_8(&base->icier, SH_I2C_ICIER_ACKBT);
+ setbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
+
+ /* read data (dummy) */
+ ret = readb(&base->icdrr);
+
+ if (check_rdrf(base)) {
+ puts("check RDRF error\n");
+ goto fail;
+ }
+
+ clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
+ udelay(1000);
+
+ sh_i2c_send_stop(base);
+
+ if (check_stop(base)) {
+ puts("check STOP error\n");
+ goto fail;
+ }
+
+ clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
+ clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
+
+ /* data read */
+ ret = readb(&base->icdrr);
+
+fail:
+ clrbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
+
+ return ret;
+}
+
+#ifdef CONFIG_I2C_MULTI_BUS
+static unsigned int current_bus;
+
+/**
+ * i2c_set_bus_num - change active I2C bus
+ * @bus: bus index, zero based
+ * @returns: 0 on success, non-0 on failure
+ */
+int i2c_set_bus_num(unsigned int bus)
+{
+ switch (bus) {
+ case 0:
+ base = (void *)CONFIG_SH_I2C_BASE0;
+ break;
+ case 1:
+ base = (void *)CONFIG_SH_I2C_BASE1;
+ break;
+ default:
+ printf("Bad bus: %d\n", bus);
+ return -1;
+ }
+
+ current_bus = bus;
+
+ return 0;
+}
+
+/**
+ * i2c_get_bus_num - returns index of active I2C bus
+ */
+unsigned int i2c_get_bus_num(void)
+{
+ return current_bus;
+}
+#endif
+
+void i2c_init(int speed, int slaveaddr)
+{
+#ifdef CONFIG_I2C_MULTI_BUS
+ current_bus = 0;
+#endif
+ base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0;
+
+ if (speed == 400000)
+ iccr1_cks = 0x07;
+ else
+ iccr1_cks = 0x0F;
+
+ nf2cyc = 1;
+
+ /* Reset */
+ sh_i2c_reset(base);
+
+ /* ICE enable and set clock */
+ writeb(SH_I2C_ICCR1_ICE | iccr1_cks, &base->iccr1);
+ writeb(nf2cyc, &base->nf2cyc);
+}
+
+/*
+ * i2c_read: - Read multiple bytes from an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip: address of the chip which is to be read
+ * @addr: i2c data address within the chip
+ * @alen: length of the i2c data address (1..2 bytes)
+ * @buffer: where to write the data
+ * @len: how much byte do we want to read
+ * @return: 0 in case of success
+ */
+int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len)
+{
+ int i = 0;
+ for (i = 0; i < len; i++)
+ buffer[i] = i2c_raw_read(base, chip, addr + i);
+
+ return 0;
+}
+
+/*
+ * i2c_write: - Write multiple bytes to an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip: address of the chip which is to be written
+ * @addr: i2c data address within the chip
+ * @alen: length of the i2c data address (1..2 bytes)
+ * @buffer: where to find the data to be written
+ * @len: how much byte do we want to read
+ * @return: 0 in case of success
+ */
+int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len)
+{
+ return i2c_raw_write(base, chip, addr, buffer, len);
+}
+
+/*
+ * i2c_probe: - Test if a chip answers for a given i2c address
+ *
+ * @chip: address of the chip which is searched for
+ * @return: 0 if a chip was found, -1 otherwhise
+ */
+int i2c_probe(u8 chip)
+{
+ u8 byte;
+ return i2c_read(chip, 0, 0, &byte, 1);
+}
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c
index 21f6897269..5b6ea0e759 100644
--- a/drivers/i2c/tegra_i2c.c
+++ b/drivers/i2c/tegra_i2c.c
@@ -567,3 +567,17 @@ int i2c_set_bus_num(unsigned int bus)
return 0;
}
#endif
+
+int tegra_i2c_get_dvc_bus_num(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; i++) {
+ struct i2c_bus *bus = &i2c_controllers[i];
+
+ if (bus->inited && bus->is_dvc)
+ return i;
+ }
+
+ return -1;
+}
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 1f4dad35b5..5c831b2611 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -26,10 +26,13 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libinput.o
COBJS-$(CONFIG_I8042_KBD) += i8042.o
+COBJS-$(CONFIG_TEGRA2_KEYBOARD) += tegra-kbc.o
ifdef CONFIG_PS2KBD
COBJS-y += keyboard.o pc_keyb.o
COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o
endif
+COBJS-y += input.o
+COBJS-$(CONFIG_OF_CONTROL) += key_matrix.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/input/input.c b/drivers/input/input.c
new file mode 100644
index 0000000000..4eadd773b4
--- /dev/null
+++ b/drivers/input/input.c
@@ -0,0 +1,430 @@
+/*
+ * Translate key codes into ASCII
+ *
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <stdio_dev.h>
+#include <input.h>
+#include <linux/input.h>
+
+enum {
+ /* These correspond to the lights on the keyboard */
+ FLAG_NUM_LOCK = 1 << 0,
+ FLAG_CAPS_LOCK = 1 << 1,
+ FLAG_SCROLL_LOCK = 1 << 2,
+
+ /* Special flag ORed with key code to indicate release */
+ KEY_RELEASE = 1 << 15,
+ KEY_MASK = 0xfff,
+};
+
+/*
+ * These takes map key codes to ASCII. 0xff means no key, or special key.
+ * Three tables are provided - one for plain keys, one for when the shift
+ * 'modifier' key is pressed and one for when the ctrl modifier key is
+ * pressed.
+ */
+static const uchar kbd_plain_xlate[] = {
+ 0xff, 0x1b, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x00 - 0x0f */
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', '\r', 0xff, 'a', 's', /* 0x10 - 0x1f */
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ '\'', '`', 0xff, '\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */
+ 'b', 'n', 'm', ',' , '.', '/', 0xff, 0xff, 0xff,
+ ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
+ '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
+ '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
+ '\r', 0xff, 0xff
+};
+
+static unsigned char kbd_shift_xlate[] = {
+ 0xff, 0x1b, '!', '@', '#', '$', '%', '^',
+ '&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x00 - 0x0f */
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', '{', '}', '\r', 0xff, 'A', 'S', /* 0x10 - 0x1f */
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+ '"', '~', 0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */
+ 'B', 'N', 'M', '<', '>', '?', 0xff, 0xff, 0xff,
+ ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
+ '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
+ '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
+ '\r', 0xff, 0xff
+};
+
+static unsigned char kbd_ctrl_xlate[] = {
+ 0xff, 0x1b, '1', 0x00, '3', '4', '5', 0x1E,
+ '7', '8', '9', '0', 0x1F, '=', '\b', '\t', /* 0x00 - 0x0f */
+ 0x11, 0x17, 0x05, 0x12, 0x14, 0x18, 0x15, 0x09,
+ 0x0f, 0x10, 0x1b, 0x1d, '\n', 0xff, 0x01, 0x13, /* 0x10 - 0x1f */
+ 0x04, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, ';',
+ '\'', '~', 0x00, 0x1c, 0x1a, 0x18, 0x03, 0x16, /* 0x20 - 0x2f */
+ 0x02, 0x0e, 0x0d, '<', '>', '?', 0xff, 0xff,
+ 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
+ '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
+ '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
+ '\r', 0xff, 0xff
+};
+
+
+int input_queue_ascii(struct input_config *config, int ch)
+{
+ if (config->fifo_in + 1 == INPUT_BUFFER_LEN) {
+ if (!config->fifo_out)
+ return -1; /* buffer full */
+ else
+ config->fifo_in = 0;
+ } else {
+ if (config->fifo_in + 1 == config->fifo_out)
+ return -1; /* buffer full */
+ config->fifo_in++;
+ }
+ config->fifo[config->fifo_in] = (uchar)ch;
+
+ return 0;
+}
+
+int input_tstc(struct input_config *config)
+{
+ if (config->fifo_in == config->fifo_out && config->read_keys) {
+ if (!(*config->read_keys)(config))
+ return 0;
+ }
+ return config->fifo_in != config->fifo_out;
+}
+
+int input_getc(struct input_config *config)
+{
+ int err = 0;
+
+ while (config->fifo_in == config->fifo_out) {
+ if (config->read_keys)
+ err = (*config->read_keys)(config);
+ if (err)
+ return -1;
+ }
+
+ if (++config->fifo_out == INPUT_BUFFER_LEN)
+ config->fifo_out = 0;
+
+ return config->fifo[config->fifo_out];
+}
+
+/**
+ * Process a modifier/special key press or release and decide which key
+ * translation array should be used as a result.
+ *
+ * TODO: Should keep track of modifier press/release
+ *
+ * @param config Input state
+ * @param key Key code to process
+ * @param release 0 if a press, 1 if a release
+ * @return pointer to keycode->ascii translation table that should be used
+ */
+static struct input_key_xlate *process_modifier(struct input_config *config,
+ int key, int release)
+{
+ struct input_key_xlate *table;
+ int flip = -1;
+ int i;
+
+ /* Start with the main table, and see what modifiers change it */
+ assert(config->num_tables > 0);
+ table = &config->table[0];
+ for (i = 1; i < config->num_tables; i++) {
+ struct input_key_xlate *tab = &config->table[i];
+
+ if (key == tab->left_keycode || key == tab->right_keycode)
+ table = tab;
+ }
+
+ /* Handle the lighted keys */
+ if (!release) {
+ switch (key) {
+ case KEY_SCROLLLOCK:
+ flip = FLAG_SCROLL_LOCK;
+ break;
+ case KEY_NUMLOCK:
+ flip = FLAG_NUM_LOCK;
+ break;
+ case KEY_CAPSLOCK:
+ flip = FLAG_CAPS_LOCK;
+ break;
+ }
+ }
+
+ if (flip != -1) {
+ int leds = 0;
+
+ config->leds ^= flip;
+ if (config->flags & FLAG_NUM_LOCK)
+ leds |= INPUT_LED_NUM;
+ if (config->flags & FLAG_CAPS_LOCK)
+ leds |= INPUT_LED_CAPS;
+ if (config->flags & FLAG_SCROLL_LOCK)
+ leds |= INPUT_LED_SCROLL;
+ config->leds = leds;
+ }
+
+ return table;
+}
+
+/**
+ * Search an int array for a key value
+ *
+ * @param array Array to search
+ * @param count Number of elements in array
+ * @param key Key value to find
+ * @return element where value was first found, -1 if none
+ */
+static int array_search(int *array, int count, int key)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (array[i] == key)
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * Sort an array so that those elements that exist in the ordering are
+ * first in the array, and in the same order as the ordering. The algorithm
+ * is O(count * ocount) and designed for small arrays.
+ *
+ * TODO: Move this to common / lib?
+ *
+ * @param dest Array with elements to sort, also destination array
+ * @param count Number of elements to sort
+ * @param order Array containing ordering elements
+ * @param ocount Number of ordering elements
+ * @return number of elements in dest that are in order (these will be at the
+ * start of dest).
+ */
+static int sort_array_by_ordering(int *dest, int count, int *order,
+ int ocount)
+{
+ int temp[count];
+ int dest_count;
+ int same; /* number of elements which are the same */
+ int i;
+
+ /* setup output items, copy items to be sorted into our temp area */
+ memcpy(temp, dest, count * sizeof(*dest));
+ dest_count = 0;
+
+ /* work through the ordering, move over the elements we agree on */
+ for (i = 0; i < ocount; i++) {
+ if (array_search(temp, count, order[i]) != -1)
+ dest[dest_count++] = order[i];
+ }
+ same = dest_count;
+
+ /* now move over the elements that are not in the ordering */
+ for (i = 0; i < count; i++) {
+ if (array_search(order, ocount, temp[i]) == -1)
+ dest[dest_count++] = temp[i];
+ }
+ assert(dest_count == count);
+ return same;
+}
+
+/**
+ * Check a list of key codes against the previous key scan
+ *
+ * Given a list of new key codes, we check how many of these are the same
+ * as last time.
+ *
+ * @param config Input state
+ * @param keycode List of key codes to examine
+ * @param num_keycodes Number of key codes
+ * @param same Returns number of key codes which are the same
+ */
+static int input_check_keycodes(struct input_config *config,
+ int keycode[], int num_keycodes, int *same)
+{
+ /* Select the 'plain' xlate table to start with */
+ if (!config->num_tables) {
+ debug("%s: No xlate tables: cannot decode keys\n", __func__);
+ return -1;
+ }
+
+ /* sort the keycodes into the same order as the previous ones */
+ *same = sort_array_by_ordering(keycode, num_keycodes,
+ config->prev_keycodes, config->num_prev_keycodes);
+
+ memcpy(config->prev_keycodes, keycode, num_keycodes * sizeof(int));
+ config->num_prev_keycodes = num_keycodes;
+
+ return *same != num_keycodes;
+}
+
+/**
+ * Convert a list of key codes into ASCII
+ *
+ * You must call input_check_keycodes() before this. It turns the keycode
+ * list into a list of ASCII characters which are ready to send to the
+ * input layer.
+ *
+ * Characters which were seen last time do not generate fresh ASCII output.
+ *
+ * @param config Input state
+ * @param keycode List of key codes to examine
+ * @param num_keycodes Number of key codes
+ * @param same Number of key codes which are the same
+ */
+static int input_keycodes_to_ascii(struct input_config *config,
+ int keycode[], int num_keycodes, char output_ch[], int same)
+{
+ struct input_key_xlate *table;
+ int ch_count;
+ int i;
+
+ table = &config->table[0];
+
+ /* deal with modifiers first */
+ for (i = 0; i < num_keycodes; i++) {
+ int key = keycode[i] & KEY_MASK;
+
+ if (key >= table->num_entries || table->xlate[key] == 0xff) {
+ table = process_modifier(config, key,
+ keycode[i] & KEY_RELEASE);
+ }
+ }
+
+ /* now find normal keys */
+ for (i = ch_count = 0; i < num_keycodes; i++) {
+ int key = keycode[i];
+
+ if (key < table->num_entries && i >= same) {
+ int ch = table->xlate[key];
+
+ /* If a normal key with an ASCII value, add it! */
+ if (ch != 0xff)
+ output_ch[ch_count++] = (uchar)ch;
+ }
+ }
+
+ /* ok, so return keys */
+ return ch_count;
+}
+
+int input_send_keycodes(struct input_config *config,
+ int keycode[], int num_keycodes)
+{
+ char ch[num_keycodes];
+ int count, i, same = 0;
+ int is_repeat = 0;
+ unsigned delay_ms;
+
+ config->modifiers = 0;
+ if (!input_check_keycodes(config, keycode, num_keycodes, &same)) {
+ /*
+ * Same as last time - is it time for another repeat?
+ * TODO(sjg@chromium.org) We drop repeats here and since
+ * the caller may not call in again for a while, our
+ * auto-repeat speed is not quite correct. We should
+ * insert another character if we later realise that we
+ * have missed a repeat slot.
+ */
+ is_repeat = (int)get_timer(config->next_repeat_ms) >= 0;
+ if (!is_repeat)
+ return 0;
+ }
+
+ count = input_keycodes_to_ascii(config, keycode, num_keycodes,
+ ch, is_repeat ? 0 : same);
+ for (i = 0; i < count; i++)
+ input_queue_ascii(config, ch[i]);
+ delay_ms = is_repeat ?
+ config->repeat_rate_ms :
+ config->repeat_delay_ms;
+
+ config->next_repeat_ms = get_timer(0) + delay_ms;
+ return 0;
+}
+
+int input_add_table(struct input_config *config, int left_keycode,
+ int right_keycode, const uchar *xlate, int num_entries)
+{
+ struct input_key_xlate *table;
+
+ if (config->num_tables == INPUT_MAX_MODIFIERS) {
+ debug("%s: Too many modifier tables\n", __func__);
+ return -1;
+ }
+
+ table = &config->table[config->num_tables++];
+ table->left_keycode = left_keycode;
+ table->right_keycode = right_keycode;
+ table->xlate = xlate;
+ table->num_entries = num_entries;
+
+ return 0;
+}
+
+int input_init(struct input_config *config, int leds, int repeat_delay_ms,
+ int repeat_rate_ms)
+{
+ memset(config, '\0', sizeof(*config));
+ config->leds = leds;
+ config->repeat_delay_ms = repeat_delay_ms;
+ config->repeat_rate_ms = repeat_rate_ms;
+ if (input_add_table(config, -1, -1,
+ kbd_plain_xlate, ARRAY_SIZE(kbd_plain_xlate)) ||
+ input_add_table(config, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
+ kbd_shift_xlate, ARRAY_SIZE(kbd_shift_xlate)) ||
+ input_add_table(config, KEY_LEFTCTRL, KEY_RIGHTCTRL,
+ kbd_ctrl_xlate, ARRAY_SIZE(kbd_ctrl_xlate))) {
+ debug("%s: Could not add modifier tables\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int input_stdio_register(struct stdio_dev *dev)
+{
+ int error;
+
+ error = stdio_register(dev);
+
+ /* check if this is the standard input device */
+ if (!error && strcmp(getenv("stdin"), dev->name) == 0) {
+ /* reassign the console */
+ if (OVERWRITE_CONSOLE ||
+ console_assign(stdin, dev->name))
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/drivers/input/key_matrix.c b/drivers/input/key_matrix.c
new file mode 100644
index 0000000000..84b898ff38
--- /dev/null
+++ b/drivers/input/key_matrix.c
@@ -0,0 +1,208 @@
+/*
+ * Manage Keyboard Matrices
+ *
+ * Copyright (c) 2012 The Chromium OS Authors.
+ * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <fdtdec.h>
+#include <key_matrix.h>
+#include <malloc.h>
+#include <linux/input.h>
+
+/**
+ * Determine if the current keypress configuration can cause key ghosting
+ *
+ * We figure this out by seeing if we have two or more keys in the same
+ * column, as well as two or more keys in the same row.
+ *
+ * @param config Keyboard matrix config
+ * @param keys List of keys to check
+ * @param valid Number of valid keypresses to check
+ * @return 0 if no ghosting is possible, 1 if it is
+ */
+static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys,
+ int valid)
+{
+ int key_in_same_col = 0, key_in_same_row = 0;
+ int i, j;
+
+ for (i = 0; i < valid; i++) {
+ /*
+ * Find 2 keys such that one key is in the same row
+ * and the other is in the same column as the i-th key.
+ */
+ for (j = i + 1; j < valid; j++) {
+ if (keys[j].col == keys[i].col)
+ key_in_same_col = 1;
+ if (keys[j].row == keys[i].row)
+ key_in_same_row = 1;
+ }
+ }
+
+ if (key_in_same_col && key_in_same_row)
+ return 1;
+ else
+ return 0;
+}
+
+int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[],
+ int num_keys, int keycode[], int max_keycodes)
+{
+ const u8 *keymap;
+ int valid, upto;
+ int pos;
+
+ debug("%s: num_keys = %d\n", __func__, num_keys);
+ keymap = config->plain_keycode;
+ for (valid = upto = 0; upto < num_keys; upto++) {
+ struct key_matrix_key *key = &keys[upto];
+
+ debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row,
+ key->col);
+ if (!key->valid)
+ continue;
+ pos = key->row * config->num_cols + key->col;
+ if (config->fn_keycode && pos == config->fn_pos)
+ keymap = config->fn_keycode;
+
+ /* Convert the (row, col) values into a keycode */
+ if (valid < max_keycodes)
+ keycode[valid++] = keymap[pos];
+ debug(" keycode=%d\n", keymap[pos]);
+ }
+
+ /* For a ghost key config, ignore the keypresses for this iteration. */
+ if (valid >= 3 && has_ghosting(config, keys, valid)) {
+ valid = 0;
+ debug(" ghosting detected!\n");
+ }
+ debug(" %d valid keycodes found\n", valid);
+
+ return valid;
+}
+
+/**
+ * Create a new keycode map from some provided data
+ *
+ * This decodes a keycode map in the format used by the fdt, which is one
+ * word per entry, with the row, col and keycode encoded in that word.
+ *
+ * We create a (row x col) size byte array with each entry containing the
+ * keycode for that (row, col). We also search for map_keycode and return
+ * its position if found (this is used for finding the Fn key).
+ *
+ * @param config Key matrix dimensions structure
+ * @param data Keycode data
+ * @param len Number of entries in keycode table
+ * @param map_keycode Key code to find in the map
+ * @param pos Returns position of map_keycode, if found, else -1
+ * @return map Pointer to allocated map
+ */
+static uchar *create_keymap(struct key_matrix *config, u32 *data, int len,
+ int map_keycode, int *pos)
+{
+ uchar *map;
+
+ if (pos)
+ *pos = -1;
+ map = (uchar *)calloc(1, config->key_count);
+ if (!map) {
+ debug("%s: failed to malloc %d bytes\n", __func__,
+ config->key_count);
+ return NULL;
+ }
+
+ for (; len >= sizeof(u32); data++, len -= 4) {
+ u32 tmp = fdt32_to_cpu(*data);
+ int key_code, row, col;
+ int entry;
+
+ row = (tmp >> 24) & 0xff;
+ col = (tmp >> 16) & 0xff;
+ key_code = tmp & 0xffff;
+ entry = row * config->num_cols + col;
+ map[entry] = key_code;
+ if (pos && map_keycode == key_code)
+ *pos = entry;
+ }
+
+ return map;
+}
+
+int key_matrix_decode_fdt(struct key_matrix *config, const void *blob,
+ int node)
+{
+ const struct fdt_property *prop;
+ int offset;
+
+ /* Check each property name for ones that we understand */
+ for (offset = fdt_first_property_offset(blob, node);
+ offset > 0;
+ offset = fdt_next_property_offset(blob, offset)) {
+ const char *name;
+ int len;
+
+ prop = fdt_get_property_by_offset(blob, offset, NULL);
+ name = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+ len = strlen(name);
+
+ /* Name needs to match "1,<type>keymap" */
+ debug("%s: property '%s'\n", __func__, name);
+ if (strncmp(name, "1,", 2) || len < 8 ||
+ strcmp(name + len - 6, "keymap"))
+ continue;
+
+ len -= 8;
+ if (len == 0) {
+ config->plain_keycode = create_keymap(config,
+ (u32 *)prop->data, fdt32_to_cpu(prop->len),
+ KEY_FN, &config->fn_pos);
+ } else if (0 == strncmp(name + 2, "fn-", len)) {
+ config->fn_keycode = create_keymap(config,
+ (u32 *)prop->data, fdt32_to_cpu(prop->len),
+ -1, NULL);
+ } else {
+ debug("%s: unrecognised property '%s'\n", __func__,
+ name);
+ }
+ }
+ debug("%s: Decoded key maps %p, %p from fdt\n", __func__,
+ config->plain_keycode, config->fn_keycode);
+
+ if (!config->plain_keycode) {
+ debug("%s: cannot find keycode-plain map\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int key_matrix_init(struct key_matrix *config, int rows, int cols)
+{
+ memset(config, '\0', sizeof(*config));
+ config->num_rows = rows;
+ config->num_cols = cols;
+ config->key_count = rows * cols;
+ assert(config->key_count > 0);
+
+ return 0;
+}
diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c
new file mode 100644
index 0000000000..f164791bee
--- /dev/null
+++ b/drivers/input/tegra-kbc.c
@@ -0,0 +1,375 @@
+/*
+ * (C) Copyright 2011
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <fdtdec.h>
+#include <input.h>
+#include <key_matrix.h>
+#include <stdio_dev.h>
+#include <tegra-kbc.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/funcmux.h>
+#include <asm/arch/timer.h>
+#include <linux/input.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum {
+ KBC_MAX_GPIO = 24,
+ KBC_MAX_KPENT = 8, /* size of keypress entry queue */
+};
+
+#define KBC_FIFO_TH_CNT_SHIFT 14
+#define KBC_DEBOUNCE_CNT_SHIFT 4
+#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3)
+#define KBC_CONTROL_KBC_EN (1 << 0)
+#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2)
+#define KBC_KPENT_VALID (1 << 7)
+#define KBC_ST_STATUS (1 << 3)
+
+enum {
+ KBC_DEBOUNCE_COUNT = 2,
+ KBC_REPEAT_RATE_MS = 30,
+ KBC_REPEAT_DELAY_MS = 240,
+ KBC_CLOCK_KHZ = 32, /* Keyboard uses a 32KHz clock */
+};
+
+/* keyboard controller config and state */
+static struct keyb {
+ struct input_config input; /* The input layer */
+ struct key_matrix matrix; /* The key matrix layer */
+
+ struct kbc_tegra *kbc; /* tegra keyboard controller */
+ unsigned char inited; /* 1 if keyboard has been inited */
+ unsigned char first_scan; /* 1 if this is our first key scan */
+
+ /*
+ * After init we must wait a short time before polling the keyboard.
+ * This gives the tegra keyboard controller time to react after reset
+ * and lets us grab keys pressed during reset.
+ */
+ unsigned int init_dly_ms; /* Delay before we can read keyboard */
+ unsigned int start_time_ms; /* Time that we inited (in ms) */
+ unsigned int last_poll_ms; /* Time we should last polled */
+ unsigned int next_repeat_ms; /* Next time we repeat a key */
+} config;
+
+/**
+ * reads the keyboard fifo for current keypresses
+ *
+ * @param config Keyboard config
+ * @param fifo Place to put fifo results
+ * @param max_keycodes Maximum number of key codes to put in the fifo
+ * @return number of items put into fifo
+ */
+static int tegra_kbc_find_keys(struct keyb *config, int *fifo,
+ int max_keycodes)
+{
+ struct key_matrix_key keys[KBC_MAX_KPENT], *key;
+ u32 kp_ent = 0;
+ int i;
+
+ for (key = keys, i = 0; i < KBC_MAX_KPENT; i++, key++) {
+ /* Get next word */
+ if (!(i & 3))
+ kp_ent = readl(&config->kbc->kp_ent[i / 4]);
+
+ key->valid = (kp_ent & KBC_KPENT_VALID) != 0;
+ key->row = (kp_ent >> 3) & 0xf;
+ key->col = kp_ent & 0x7;
+
+ /* Shift to get next entry */
+ kp_ent >>= 8;
+ }
+ return key_matrix_decode(&config->matrix, keys, KBC_MAX_KPENT, fifo,
+ max_keycodes);
+}
+
+/**
+ * Process all the keypress sequences in fifo and send key codes
+ *
+ * The fifo contains zero or more keypress sets. Each set
+ * consists of from 1-8 keycodes, representing the keycodes which
+ * were simultaneously pressed during that scan.
+ *
+ * This function works through each set and generates ASCII characters
+ * for each. Not that one set may produce more than one ASCII characters -
+ * for example holding down 'd' and 'f' at the same time will generate
+ * two ASCII characters.
+ *
+ * Note: if fifo_cnt is 0, we will tell the input layer that no keys are
+ * pressed.
+ *
+ * @param config Keyboard config
+ * @param fifo_cnt Number of entries in the keyboard fifo
+ */
+static void process_fifo(struct keyb *config, int fifo_cnt)
+{
+ int fifo[KBC_MAX_KPENT];
+ int cnt = 0;
+
+ /* Always call input_send_keycodes() at least once */
+ do {
+ if (fifo_cnt)
+ cnt = tegra_kbc_find_keys(config, fifo, KBC_MAX_KPENT);
+
+ input_send_keycodes(&config->input, fifo, cnt);
+ } while (--fifo_cnt > 0);
+}
+
+/**
+ * Check the keyboard controller and emit ASCII characters for any keys that
+ * are pressed.
+ *
+ * @param config Keyboard config
+ */
+static void check_for_keys(struct keyb *config)
+{
+ int fifo_cnt;
+
+ if (!config->first_scan &&
+ get_timer(config->last_poll_ms) < KBC_REPEAT_RATE_MS)
+ return;
+ config->last_poll_ms = get_timer(0);
+ config->first_scan = 0;
+
+ /*
+ * Once we get here we know the keyboard has been scanned. So if there
+ * scan waiting for us, we know that nothing is held down.
+ */
+ fifo_cnt = (readl(&config->kbc->interrupt) >> 4) & 0xf;
+ process_fifo(config, fifo_cnt);
+}
+
+/**
+ * In order to detect keys pressed on boot, wait for the hardware to
+ * complete scanning the keys. This includes time to transition from
+ * Wkup mode to Continous polling mode and the repoll time. We can
+ * deduct the time that's already elapsed.
+ *
+ * @param config Keyboard config
+ */
+static void kbd_wait_for_fifo_init(struct keyb *config)
+{
+ if (!config->inited) {
+ unsigned long elapsed_time;
+ long delay_ms;
+
+ elapsed_time = get_timer(config->start_time_ms);
+ delay_ms = config->init_dly_ms - elapsed_time;
+ if (delay_ms > 0) {
+ udelay(delay_ms * 1000);
+ debug("%s: delay %ldms\n", __func__, delay_ms);
+ }
+
+ config->inited = 1;
+ }
+}
+
+/**
+ * Check the tegra keyboard, and send any keys that are pressed.
+ *
+ * This is called by input_tstc() and input_getc() when they need more
+ * characters
+ *
+ * @param input Input configuration
+ * @return 1, to indicate that we have something to look at
+ */
+int tegra_kbc_check(struct input_config *input)
+{
+ kbd_wait_for_fifo_init(&config);
+ check_for_keys(&config);
+
+ return 1;
+}
+
+/**
+ * Test if keys are available to be read
+ *
+ * @return 0 if no keys available, 1 if keys are available
+ */
+static int kbd_tstc(void)
+{
+ /* Just get input to do this for us */
+ return input_tstc(&config.input);
+}
+
+/**
+ * Read a key
+ *
+ * TODO: U-Boot wants 0 for no key, but Ctrl-@ is a valid key...
+ *
+ * @return ASCII key code, or 0 if no key, or -1 if error
+ */
+static int kbd_getc(void)
+{
+ /* Just get input to do this for us */
+ return input_getc(&config.input);
+}
+
+/* configures keyboard GPIO registers to use the rows and columns */
+static void config_kbc_gpio(struct kbc_tegra *kbc)
+{
+ int i;
+
+ for (i = 0; i < KBC_MAX_GPIO; i++) {
+ u32 row_cfg, col_cfg;
+ u32 r_shift = 5 * (i % 6);
+ u32 c_shift = 4 * (i % 8);
+ u32 r_mask = 0x1f << r_shift;
+ u32 c_mask = 0xf << c_shift;
+ u32 r_offs = i / 6;
+ u32 c_offs = i / 8;
+
+ row_cfg = readl(&kbc->row_cfg[r_offs]);
+ col_cfg = readl(&kbc->col_cfg[c_offs]);
+
+ row_cfg &= ~r_mask;
+ col_cfg &= ~c_mask;
+
+ if (i < config.matrix.num_rows) {
+ row_cfg |= ((i << 1) | 1) << r_shift;
+ } else {
+ col_cfg |= (((i - config.matrix.num_rows) << 1) | 1)
+ << c_shift;
+ }
+
+ writel(row_cfg, &kbc->row_cfg[r_offs]);
+ writel(col_cfg, &kbc->col_cfg[c_offs]);
+ }
+}
+
+/**
+ * Start up the keyboard device
+ */
+static void tegra_kbc_open(void)
+{
+ struct kbc_tegra *kbc = config.kbc;
+ unsigned int scan_period;
+ u32 val;
+
+ /*
+ * We will scan at twice the keyboard repeat rate, so that there is
+ * always a scan ready when we check it in check_for_keys().
+ */
+ scan_period = KBC_REPEAT_RATE_MS / 2;
+ writel(scan_period * KBC_CLOCK_KHZ, &kbc->rpt_dly);
+ writel(scan_period * KBC_CLOCK_KHZ, &kbc->init_dly);
+ /*
+ * Before reading from the keyboard we must wait for the init_dly
+ * plus the rpt_delay, plus 2ms for the row scan time.
+ */
+ config.init_dly_ms = scan_period * 2 + 2;
+
+ val = KBC_DEBOUNCE_COUNT << KBC_DEBOUNCE_CNT_SHIFT;
+ val |= 1 << KBC_FIFO_TH_CNT_SHIFT; /* fifo interrupt threshold */
+ val |= KBC_CONTROL_KBC_EN; /* enable */
+ writel(val, &kbc->control);
+
+ config.start_time_ms = get_timer(0);
+ config.last_poll_ms = config.next_repeat_ms = get_timer(0);
+ config.first_scan = 1;
+}
+
+/**
+ * Set up the tegra keyboard. This is called by the stdio device handler
+ *
+ * We want to do this init when the keyboard is actually used rather than
+ * at start-up, since keyboard input may not currently be selected.
+ *
+ * Once the keyboard starts there will be a period during which we must
+ * wait for the keyboard to init. We do this only when a key is first
+ * read - see kbd_wait_for_fifo_init().
+ *
+ * @return 0 if ok, -ve on error
+ */
+static int init_tegra_keyboard(void)
+{
+#ifdef CONFIG_OF_CONTROL
+ int node;
+
+ node = fdtdec_next_compatible(gd->fdt_blob, 0,
+ COMPAT_NVIDIA_TEGRA20_KBC);
+ if (node < 0) {
+ debug("%s: cannot locate keyboard node\n", __func__);
+ return node;
+ }
+ config.kbc = (struct kbc_tegra *)fdtdec_get_addr(gd->fdt_blob,
+ node, "reg");
+ if ((fdt_addr_t)config.kbc == FDT_ADDR_T_NONE) {
+ debug("%s: No keyboard register found\n", __func__);
+ return -1;
+ }
+
+ /* Decode the keyboard matrix information (16 rows, 8 columns) */
+ if (key_matrix_init(&config.matrix, 16, 8)) {
+ debug("%s: Could not init key matrix\n", __func__);
+ return -1;
+ }
+ if (key_matrix_decode_fdt(&config.matrix, gd->fdt_blob, node)) {
+ debug("%s: Could not decode key matrix from fdt\n", __func__);
+ return -1;
+ }
+ if (config.matrix.fn_keycode) {
+ if (input_add_table(&config.input, KEY_FN, -1,
+ config.matrix.fn_keycode,
+ config.matrix.key_count))
+ return -1;
+ }
+#else
+#error "Tegra keyboard driver requires FDT definitions"
+#endif
+
+ /* Set up pin mux and enable the clock */
+ funcmux_select(PERIPH_ID_KBC, FUNCMUX_DEFAULT);
+ clock_enable(PERIPH_ID_KBC);
+ config_kbc_gpio(config.kbc);
+
+ tegra_kbc_open();
+ debug("%s: Tegra keyboard ready\n", __func__);
+
+ return 0;
+}
+
+int drv_keyboard_init(void)
+{
+ struct stdio_dev dev;
+
+ if (input_init(&config.input, 0, KBC_REPEAT_DELAY_MS,
+ KBC_REPEAT_RATE_MS)) {
+ debug("%s: Cannot set up input\n", __func__);
+ return -1;
+ }
+ config.input.read_keys = tegra_kbc_check;
+
+ memset(&dev, '\0', sizeof(dev));
+ strcpy(dev.name, "tegra-kbc");
+ dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
+ dev.getc = kbd_getc;
+ dev.tstc = kbd_tstc;
+ dev.start = init_tegra_keyboard;
+
+ /* Register the device. init_tegra_keyboard() will be called soon */
+ return input_stdio_register(&dev);
+}
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a70970784c..fdf6813149 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -35,10 +35,14 @@ COBJS-$(CONFIG_PDSP188x) += pdsp188x.o
COBJS-$(CONFIG_STATUS_LED) += status_led.o
COBJS-$(CONFIG_TWL4030_LED) += twl4030_led.o
COBJS-$(CONFIG_PMIC) += pmic_core.o
+COBJS-$(CONFIG_DIALOG_PMIC) += pmic_dialog.o
COBJS-$(CONFIG_PMIC_FSL) += pmic_fsl.o
COBJS-$(CONFIG_PMIC_I2C) += pmic_i2c.o
COBJS-$(CONFIG_PMIC_SPI) += pmic_spi.o
COBJS-$(CONFIG_PMIC_MAX8998) += pmic_max8998.o
+COBJS-$(CONFIG_PMIC_MAX8997) += pmic_max8997.o
+COBJS-$(CONFIG_RDA_FACTORY) += factory.o
+COBJS-$(CONFIG_RDA_PRDINFO) += prdinfo.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/misc/factory.c b/drivers/misc/factory.c
new file mode 100644
index 0000000000..addf32ea1d
--- /dev/null
+++ b/drivers/misc/factory.c
@@ -0,0 +1,715 @@
+#include <linux/types.h>
+#include <common.h>
+#include <errno.h>
+#include <nand.h>
+#include <pdl.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <part.h>
+#include <mmc/mmcpart.h>
+#include <jffs2/load_kernel.h>
+#include <asm/arch/rda_iomap.h>
+#include <asm/arch/factory.h>
+#include <asm/arch/rda_sys.h>
+
+enum FACTORY_DATA_STATUS {
+ FACTORY_DATA_UNKNOWN = 0,
+ FACTORY_DATA_INVALID,
+ FACTORY_DATA_VALID,
+};
+
+struct ap_factory_config_v0 {
+ u32 magic;
+ u8 lcd_name[128];
+ u8 clock_config[1024];
+ u8 reserved[1024];
+};
+
+const static char factory_sector_name[] = "factorydata";
+
+static enum FACTORY_DATA_STATUS factory_data_status
+ = FACTORY_DATA_UNKNOWN;
+
+static struct factory_data_sector factory_data
+ __attribute__((__aligned__(32)));
+
+static int factory_data_is_valid(u8 *data, u32 size)
+{
+ struct factory_data_sector *sector = (void *)data;
+ struct ap_factory_config *config = (void *)sector->ap_factory_data;
+ int valid = 0;
+ u32 crc, crc_calc;
+ u32 version;
+
+ BUILD_BUG_ON(sizeof(struct ap_factory_config) > RDA_AP_FACT_LEN);
+
+ memcpy(&version, &config->version, sizeof(version));
+ printf("Factory version: 0x%08X\n", version);
+ if ((version & 0xFFFF0000) == AP_FACTORY_MARK_VERSION) {
+ u32 crc_size = RDA_FACT_TOTAL_LEN;
+ /* Factory structure version the current version */
+ if (version == AP_FACTORY_VERSION_NUMBER) {
+ valid = 1;
+ /* Factory structure version is old, but we can support it */
+ } else if (version == AP_FACTORY_VERSION_1_NUMBER) {
+ crc_size = AP_FACTORY_VERSION_1_LEN;
+ valid = 1;
+ } else {
+ /* Factory structure version is invalid */
+ printf("Factory version is unknown\n");
+ printf("Current code version: 0x%08X\n",
+ AP_FACTORY_VERSION_NUMBER);
+ }
+
+ if(size < crc_size) {
+ valid = 0;
+ printf("Factory data size is too small.\n");
+ }
+
+ if(valid) {
+ memcpy(&crc, &config->crc, sizeof(crc));
+ memset(&config->crc, 0, sizeof(crc));
+ crc_calc = crc32(0, (u8 *)sector, crc_size);
+ memcpy(&config->crc, &crc, sizeof(crc));
+ if (crc == crc_calc) {
+ printf("Factory CRC is OK %#x\n", crc);
+ } else {
+ printf("ERROR: Bad factory CRC, got %#x, expect %#x\n",
+ crc_calc, crc);
+ valid = 0;
+ }
+ }
+ } else {
+ printf("No valid factory data\n");
+ }
+
+ return valid;
+}
+
+extern int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
+static int nand_mtd_factory_init(struct mtd_info **minfo,
+ struct part_info **pinfo)
+{
+ struct mtd_info *nand;
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+ int ret;
+ u32 block_size, page_size;
+ u32 block_nums;
+
+ ret = find_dev_and_part(factory_sector_name, &dev, &pnum, &part);
+ if (ret) {
+ printf("unknown partition name '%s'\n", factory_sector_name);
+ return ret;
+ } else if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ printf("mtd dev type error: %d\n", dev->id->type);
+ return -EIO;
+ }
+ printf("found factorydata part '%s' offset: 0x%llx length: 0x%llx\n",
+ part->name, part->offset, part->size);
+
+ nand = &nand_info[dev->id->num];
+ block_size = nand->erasesize;
+ page_size = nand->writesize;
+
+ printf("nand info: blockSize=%d pageSize=%d\n", block_size, page_size);
+
+ if (block_size < sizeof(struct factory_data_sector)) {
+ printf("Invalid block size: %d (should >= %d)\n",
+ block_size, sizeof(struct factory_data_sector));
+ hang();
+ }
+ block_nums = part->size / nand->erasesize;
+ if (block_nums < 2) {
+ printf("partition for factorydata is too small");
+ printf(" at least two blocks\n");
+ hang();
+ }
+ *minfo = nand;
+ *pinfo = part;
+ return 0;
+}
+
+static int nand_mtd_load_factory(u8 *dst)
+{
+ struct mtd_info *nand;
+ struct part_info *part;
+ size_t size;
+ int ret;
+ u64 offs;
+ u32 block_size;
+ u32 bad_blocks = 0;
+
+ ret = nand_mtd_factory_init(&nand, &part);
+ if (ret)
+ return ret;
+
+ block_size = nand->erasesize;
+ size = sizeof(struct factory_data_sector);
+ offs = part->offset;
+
+ printf("read 0x%x bytes from '%s' offset: 0x%llx\n",
+ size, part->name, offs);
+ ret = nand_read_skip_bad_new(nand, offs, &size, dst, &bad_blocks);
+ if (ret) {
+ printf("nand read fail\n");
+ return ret;
+ }
+
+ if (factory_data_is_valid(dst, size))
+ return 0;
+
+ printf("Try the backup copy of factory data\n");
+
+ size = sizeof(struct factory_data_sector);
+ offs = part->offset + (bad_blocks + 1) * block_size;
+
+ printf("read 0x%x bytes from '%s' offset: 0x%llx\n",
+ size, part->name, offs);
+
+ ret = nand_read_skip_bad_new(nand, offs, &size, dst, &bad_blocks);
+ if (ret) {
+ printf("nand read fail\n");
+ return ret;
+ }
+
+ if (factory_data_is_valid(dst, size))
+ return 0;
+
+ return -EIO;
+}
+
+static void factory_data_prepare_write(u8 *buf)
+{
+ struct factory_data_sector *sector = (void *)buf;
+ struct ap_factory_config *config = (void *)sector->ap_factory_data;
+ u32 version = AP_FACTORY_VERSION_NUMBER;
+ u32 crc_size = sizeof(*sector);
+ u32 crc;
+
+ if(config->version == AP_FACTORY_VERSION_1_NUMBER)
+ crc_size = AP_FACTORY_VERSION_1_LEN;
+ else
+ memcpy(&config->version, &version, sizeof(config->version));
+
+ memset(&config->crc, 0, sizeof(config->crc));
+ crc = crc32(0, (u8 *)sector, crc_size);
+ memcpy(&config->crc, &crc, sizeof(config->crc));
+}
+
+static void factory_data_dump(u8 *data, size_t size, int flash)
+{
+ printf("dump factory data from %s :\n", flash ? "flash" : "tools");
+ rda_dump_buf((char *)data, size);
+}
+
+static int factory_data_check(struct mtd_info *nand, u32 offs, u8 *data)
+{
+ int ret = 0;;
+ u8 *dst;
+ size_t size;
+ u32 bad_blocks = 0;
+ int i;
+
+ if (pdl_dbg_rw_check == 0 && pdl_dbg_factory_part == 0)
+ return 0;
+
+ printf("Check the factory data just written ...\n");
+
+ dst = malloc(sizeof(struct factory_data_sector));
+ if (dst == NULL) {
+ printf("Failed to malloc buffer %d bytes\n",
+ sizeof(struct factory_data_sector));
+ return -ENOMEM;
+ }
+
+ size = sizeof(struct factory_data_sector);
+
+ printf("read 0x%x bytes offset: 0x%08x\n", size, offs);
+ ret = nand_read_skip_bad_new(nand, offs, &size, dst, &bad_blocks);
+ if (ret) {
+ printf("nand read fail\n");
+ goto _exit;
+ }
+
+ if (pdl_dbg_factory_part)
+ factory_data_dump(dst, sizeof(struct factory_data_sector), 1);
+
+ if (pdl_dbg_rw_check) {
+ for (i = 0; i < sizeof(struct factory_data_sector); ++i) {
+ if (data[i] != dst[i])
+ break;
+ }
+ if (i < sizeof(struct factory_data_sector)) {
+ ret = -EIO;
+ size = sizeof(struct factory_data_sector) - i;
+ if (size > 2048)
+ size = 2048;
+ printf("*** CHECK FAILURE: offset=%d\n", i);
+ printf("==> Before %p is:\n", data);
+ rda_dump_buf((char *)&data[i], size);
+ printf("==> After %p is:\n", dst);
+ rda_dump_buf((char *)&dst[i], size);
+ }
+ }
+
+_exit:
+ free(dst);
+ return ret;
+}
+
+static loff_t factory_backup_offs = 0;
+
+static int write_factory(nand_info_t *nand, loff_t offset, size_t size,
+ loff_t max_limit, u8 *buf)
+{
+ nand_erase_options_t opts;
+ struct nand_chip *chip = nand->priv;
+ int ret;
+
+ if (chip->bbt_erase_shift == 0) {
+ if(mtd_mod_by_eb(offset, nand) != 0)
+ return -EINVAL;
+ } else {
+ if ((offset & (nand->erasesize -1)) != 0)
+ return -EINVAL;
+ }
+
+ if (chip->bbt_erase_shift == 0) {
+ while (offset < max_limit) {
+ if (nand_block_isbad (nand,
+ mtd_div_by_eb(offset, nand) * nand->erasesize)) {
+ printf ("Skip bad block 0x%08llx\n",
+ (loff_t)mtd_div_by_eb(offset, nand) * nand->erasesize);
+ offset += nand->erasesize;
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else {
+ while (offset < max_limit) {
+ if (nand_block_isbad (nand,
+ offset & ~(nand->erasesize - 1))) {
+ printf ("Skip bad block 0x%08llx\n",
+ offset & ~(nand->erasesize - 1));
+ offset += nand->erasesize;
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (offset >= max_limit) {
+ printf("no good block for factory data\n");
+ return -EIO;
+ }
+ /* erase the factory partition */
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = offset;
+ opts.length = nand->erasesize;
+ opts.jffs2 = 0;
+ opts.quiet = 0;
+ printf("erase 0x%x bytes to offset: 0x%08x\n",
+ (unsigned int)opts.length,
+ (unsigned int)opts.offset);
+ ret = nand_erase_opts(nand, &opts);
+ if (ret) {
+ printf("nand erase fail\n");
+ return ret;
+ }
+
+ ret = nand_write(nand, offset, &size, buf);
+ if (ret) {
+ printf("nand write fail\n");
+ return ret;
+ } else {
+ /* Check the factory data in flash */
+ ret = factory_data_check(nand, offset, buf);
+ if (ret)
+ return ret;
+ }
+
+ factory_backup_offs = offset + nand->erasesize;
+ return 0;
+}
+
+static int nand_mtd_write_factory(u8 *buf)
+{
+ struct mtd_info *nand;
+ struct part_info *part;
+ int ret;
+ size_t size;
+ u64 offs;
+ loff_t max_limit;
+
+ ret = nand_mtd_factory_init(&nand, &part);
+ if (ret)
+ return ret;
+
+ max_limit = part->offset + part->size;
+
+ /* Dump the data from tools */
+ if (pdl_dbg_factory_part)
+ factory_data_dump(buf, sizeof(struct factory_data_sector), 0);
+
+ /* Set version and crc */
+ factory_data_prepare_write(buf);
+
+ /* write back the factory */
+ size = sizeof(struct factory_data_sector);
+ offs = part->offset;
+ printf("write 0x%x bytes to '%s' offset: 0x%llx\n",
+ size, part->name, offs);
+
+ ret = write_factory(nand, offs, size, max_limit, buf);
+ if (ret) {
+ printf("factorydata write fail\n");
+ return ret;
+ }
+
+ printf("Write the backup copy of factory data\n");
+
+ size = sizeof(struct factory_data_sector);
+ offs = factory_backup_offs;
+ printf("write 0x%x bytes to '%s' offset: 0x%llx\n",
+ size, part->name, offs);
+
+ ret = write_factory(nand, offs, size, max_limit, buf);
+ if (ret) {
+ printf("factorydata backup write fail, ");
+ printf("and only one copy in factorydata\n");
+ }
+ return 0;
+}
+
+#define BACKUP_FACTORY_START 128 /* 128 mmc blocks, 64KB */
+int emmc_load_factory(u8 *dst)
+{
+ disk_partition_t *ptn;
+ block_dev_desc_t *mmc_blkdev;
+ struct mmc *mmc;
+ u64 offset;
+ u64 backup_offset;
+ size_t size;
+ int blksz_shift;
+
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (mmc_blkdev)
+ mmc = container_of(mmc_blkdev, struct mmc, block_dev);
+ else
+ return -1;
+
+ if (!mmc) {
+ printf("mmc doesn't exist");
+ return -1;
+ }
+
+ ptn = partition_find_ptn("factorydata");
+ if(!ptn) {
+ printf("mmc partition table doesn't exist");
+ return -1;
+ }
+
+ blksz_shift = LOG2(ptn->blksz);
+ offset = (u64)ptn->start << blksz_shift;
+ size = sizeof(struct factory_data_sector);
+ printf("read 0x%x bytes from '%s' offset: 0x%08llx\n",
+ size, ptn->name, offset);
+ if (mmc_read(mmc, offset, dst, size) <= 0) {
+ printf("mmc read fail\n");
+ return -EIO;
+ }
+
+ if (factory_data_is_valid(dst, size))
+ return 0;
+
+ printf("Try the backup copy of factory data\n");
+
+ size = sizeof(struct factory_data_sector);
+ backup_offset = offset + (BACKUP_FACTORY_START << blksz_shift);
+
+ printf("read 0x%x bytes from '%s' offset: 0x%08llx\n",
+ size, ptn->name, backup_offset);
+
+ if (mmc_read(mmc, backup_offset, dst, size) <= 0) {
+ printf("mmc read backup factorydata fail\n");
+ return -EIO;
+ }
+
+ if (factory_data_is_valid(dst, size))
+ return 0;
+
+ return -EIO;
+}
+
+static int emmc_write_factory(u8 *buf)
+{
+ disk_partition_t *ptn;
+ block_dev_desc_t *mmc_blkdev;
+ struct mmc *mmc;
+ u64 offset;
+ u64 backup_offset;
+ size_t size;
+ int blksz_shift;
+
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (mmc_blkdev)
+ mmc = container_of(mmc_blkdev, struct mmc, block_dev);
+ else
+ return -1;
+
+ if (!mmc) {
+ printf("mmc doesn't exist");
+ return -1;
+ }
+
+ ptn = partition_find_ptn("factorydata");
+ if (!ptn) {
+ printf("mmc partition table doesn't exist");
+ return -1;
+ }
+
+ /* Dump the data from tools */
+ if (pdl_dbg_factory_part)
+ factory_data_dump(buf, sizeof(struct factory_data_sector), 0);
+
+ /* Set version and crc */
+ factory_data_prepare_write(buf);
+
+ /* write back the factory */
+ size = sizeof(struct factory_data_sector);
+ blksz_shift = LOG2(ptn->blksz);
+ offset = (u64)ptn->start << blksz_shift;
+ printf("write 0x%x bytes to '%s' offset: 0x%llx\n",
+ size, ptn->name, offset);
+
+ if (mmc_write(mmc, offset, buf, size) <= 0) {
+ printf("factorydata write fail\n");
+ return -EIO;
+ }
+
+ printf("Write the backup copy of factory data\n");
+
+ size = sizeof(struct factory_data_sector);
+ backup_offset = offset + (BACKUP_FACTORY_START << blksz_shift);
+ printf("write 0x%x bytes to '%s' offset: 0x%llx\n",
+ size, ptn->name, backup_offset);
+
+ if (mmc_write(mmc, backup_offset, buf, size) <= 0) {
+ printf("factorydata backup write fail, ");
+ printf("and only one copy in factorydata\n");
+ }
+ return 0;
+}
+
+int factory_copy_from_mem(const u8 *buf)
+{
+ u8 *data = (u8 *)image_get_data((const image_header_t *)buf);
+ size_t size = image_get_data_size((const image_header_t *)buf);
+
+ if (factory_data_is_valid(data, size)) {
+ printf("Calibration data is valid, copying to memory\n");
+ memcpy(&factory_data, data, sizeof(factory_data));
+ factory_data_status = FACTORY_DATA_VALID;
+ return 0;
+ } else {
+ printf("Calibration data is invalid\n");
+ return 1;
+ }
+}
+
+int factory_load(void)
+{
+ int ret = 0;
+
+ if (factory_data_status != FACTORY_DATA_UNKNOWN) {
+ printf("Factory already loaded\n");
+ return ret;
+ }
+
+ printf("Load factory from flash ...\n");
+
+ if (rda_media_get() == MEDIA_MMC)
+ ret = emmc_load_factory((u8 *)&factory_data);
+ else
+ ret = nand_mtd_load_factory((u8 *)&factory_data);
+ if (ret) {
+ printf("Load Failed\n");
+ memset(&factory_data, 0, sizeof(factory_data));
+ factory_data_status = FACTORY_DATA_INVALID;
+ return ret;
+ }
+
+ printf("Done\n");
+ factory_data_status = FACTORY_DATA_VALID;
+ return ret;
+}
+
+const unsigned char* factory_get_lcd_name(void)
+{
+ struct ap_factory_config *config =
+ (void *)factory_data.ap_factory_data;
+ factory_load();
+ return config->lcd_name;
+}
+
+const unsigned char* factory_get_bootlogo_name(void)
+{
+ struct ap_factory_config *config =
+ (void *)factory_data.ap_factory_data;
+ factory_load();
+ return config->bootlogo_name;
+}
+
+const unsigned char* factory_get_ap_factory(void)
+{
+ factory_load();
+ return factory_data.ap_factory_data;
+}
+
+const unsigned char* factory_get_modem_factory(void)
+{
+ factory_load();
+ return factory_data.modem_factory_data;
+}
+
+const unsigned char* factory_get_modem_calib(void)
+{
+ factory_load();
+ return factory_data.modem_calib_data;
+}
+
+const unsigned char* factory_get_modem_ext_calib(void)
+{
+ factory_load();
+ return factory_data.modem_ext_calib_data;
+}
+
+unsigned long factory_get_all(unsigned char *buf)
+{
+ struct ap_factory_config *config =
+ (void *)factory_data.ap_factory_data;
+ if (!buf)
+ return 0;
+
+ factory_load();
+ memcpy(buf, &factory_data, sizeof(factory_data));
+
+ if(config->version == AP_FACTORY_VERSION_1_NUMBER)
+ return AP_FACTORY_VERSION_1_LEN;
+
+ return sizeof(factory_data);
+}
+
+int factory_set_ap_factory(unsigned char *data)
+{
+ factory_load();
+ memcpy(factory_data.ap_factory_data, data,
+ sizeof(factory_data.ap_factory_data));
+ return 0;
+}
+
+int factory_set_modem_calib(unsigned char *data)
+{
+ factory_load();
+ memcpy(factory_data.modem_calib_data, data,
+ sizeof(factory_data.modem_calib_data));
+ return 0;
+}
+
+int factory_set_modem_ext_calib(unsigned char *data)
+{
+ factory_load();
+ memcpy(factory_data.modem_ext_calib_data, data,
+ sizeof(factory_data.modem_ext_calib_data));
+ return 0;
+}
+
+int factory_set_modem_factory(unsigned char *data)
+{
+ factory_load();
+ memcpy(factory_data.modem_factory_data, data,
+ sizeof(factory_data.modem_factory_data));
+ return 0;
+}
+
+int factory_burn(void)
+{
+ if (rda_media_get() == MEDIA_MMC)
+ return emmc_write_factory((u8 *)&factory_data);
+ else
+ return nand_mtd_write_factory((u8 *)&factory_data);
+}
+
+int factory_update_modem_calib(unsigned char *data)
+{
+ factory_set_modem_calib(data);
+ return factory_burn();
+}
+
+int factory_update_modem_ext_calib(unsigned char *data)
+{
+ factory_set_modem_ext_calib(data);
+ return factory_burn();
+}
+
+int factory_update_modem_factory(unsigned char *data)
+{
+ factory_set_modem_factory(data);
+ return factory_burn();
+}
+
+int factory_update_ap_factory(unsigned char *data)
+{
+ factory_set_ap_factory(data);
+ return factory_burn();
+}
+
+int factory_update_all(unsigned char *data, unsigned long size)
+{
+ if (!factory_data_is_valid(data, size)) {
+ printf("factory data is invalid.\n");
+ return -EINVAL;
+ }
+ factory_load();
+ memcpy(&factory_data, data, sizeof(factory_data));
+ return factory_burn();
+}
+
+
+/* message rx/tx for PC calib tool and u-boot */
+int factory_get_ap_calib_msg(unsigned int *id, unsigned int *size, unsigned char *data)
+{
+ struct ap_calib_message *msg = (struct ap_calib_message *)RDA_AP_CALIB_MSG_ADDR;
+ if (msg->magic == RDA_AP_CALIB_MSG_MAGIC &&
+ !(msg->id & 0x80000000) ) {
+ *id = msg->id;
+ if (size)
+ *size = msg->size;
+ if (data)
+ memcpy(data, msg->data, msg->size);
+ /* clear */
+ memset(msg, 0, RDA_AP_CALIB_MSG_LEN);
+ flush_dcache_range(RDA_AP_CALIB_MSG_ADDR, RDA_AP_CALIB_MSG_ADDR+RDA_AP_CALIB_MSG_LEN);
+ return 0;
+ }
+ return -1;
+}
+
+int factory_set_ap_calib_msg(unsigned int id, unsigned int size, unsigned char *data)
+{
+ struct ap_calib_message *msg = (struct ap_calib_message *)RDA_AP_CALIB_MSG_ADDR;
+ msg->magic = RDA_AP_CALIB_MSG_MAGIC;
+ msg->id = id;
+ msg->size = size;
+ if (data)
+ memcpy(msg->data, data, size);
+ flush_dcache_range(RDA_AP_CALIB_MSG_ADDR, RDA_AP_CALIB_MSG_ADDR+RDA_AP_CALIB_MSG_LEN);
+ return 0;
+}
+
diff --git a/drivers/misc/pmic_dialog.c b/drivers/misc/pmic_dialog.c
new file mode 100644
index 0000000000..e97af1d1d0
--- /dev/null
+++ b/drivers/misc/pmic_dialog.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <pmic.h>
+#include <dialog_pmic.h>
+
+int pmic_dialog_init(void)
+{
+ struct pmic *p = get_pmic();
+ static const char name[] = "DIALOG_PMIC";
+
+ p->name = name;
+ p->number_of_regs = DIALOG_NUM_OF_REGS;
+
+ p->interface = PMIC_I2C;
+ p->hw.i2c.addr = CONFIG_SYS_DIALOG_PMIC_I2C_ADDR;
+ p->hw.i2c.tx_num = 1;
+ p->bus = I2C_PMIC;
+
+ return 0;
+}
diff --git a/drivers/misc/pmic_max8997.c b/drivers/misc/pmic_max8997.c
new file mode 100644
index 0000000000..62dbc05311
--- /dev/null
+++ b/drivers/misc/pmic_max8997.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <pmic.h>
+#include <max8997_pmic.h>
+
+int pmic_init(void)
+{
+ struct pmic *p = get_pmic();
+ static const char name[] = "MAX8997_PMIC";
+
+ puts("Board PMIC init\n");
+
+ p->name = name;
+ p->interface = PMIC_I2C;
+ p->number_of_regs = PMIC_NUM_OF_REGS;
+ p->hw.i2c.addr = MAX8997_I2C_ADDR;
+ p->hw.i2c.tx_num = 1;
+ p->bus = I2C_PMIC;
+
+ return 0;
+}
diff --git a/drivers/misc/prdinfo.c b/drivers/misc/prdinfo.c
new file mode 100644
index 0000000000..8f42df776e
--- /dev/null
+++ b/drivers/misc/prdinfo.c
@@ -0,0 +1,344 @@
+#include <linux/types.h>
+#include <common.h>
+#include <command.h>
+#include <errno.h>
+#include <nand.h>
+#include <pdl.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <part.h>
+#include <mmc/mmcpart.h>
+#include <asm/arch/hwcfg.h>
+#include <asm/arch/mtdparts_def.h>
+#include <asm/arch/rda_sys.h>
+#include <asm/arch/prdinfo.h>
+
+int nand_read_partition(const char *part_name, unsigned char *data,
+ size_t size, size_t *actual_len);
+int nand_write_partition(const char *part_name, unsigned char *data,
+ size_t size);
+int nand_erase_partition(const char *part_name);
+int emmc_erase_partition(const char *part_name);
+int emmc_read_partition(const char *part_name, unsigned char *data, size_t size,
+ size_t *actual_len);
+int emmc_write_partition(const char *part_name, unsigned char *data, size_t size);
+
+#ifdef CONFIG_SYS_CACHELINE_SIZE
+static unsigned char prdinfo_buf[PRDINFO_TOTAL_SIZE]
+ __attribute__((__aligned__(CONFIG_SYS_CACHELINE_SIZE)));
+#else
+static unsigned char prdinfo_buf[PRDINFO_TOTAL_SIZE]
+ __attribute__((__aligned__(32)));
+#endif
+int prdinfo_load(void)
+{
+ int ret = 0;
+ static int loaded = 0;
+ unsigned char *data = prdinfo_buf;
+ struct prdinfo *info = (struct prdinfo *)data;
+ size_t actual_sz = 0;
+ size_t total_sz = PRDINFO_TOTAL_SIZE;
+ uint32_t crc = 0, new_crc = 0;
+
+ if(loaded)
+ return 0;
+
+ if (rda_media_get() == MEDIA_MMC)
+ ret = emmc_read_partition(PRDINFO_PART_NAME, data,
+ total_sz, &actual_sz);
+ else
+ ret = nand_read_partition(PRDINFO_PART_NAME, data,
+ total_sz, &actual_sz);
+
+ if(ret)
+ return -1;
+
+ /* crc verify */
+ memcpy(&crc, &data[PRDINFO_CRC_OFFSET], sizeof(uint32_t));
+ new_crc = crc32(0, data, total_sz - sizeof(uint32_t));
+
+ /* if data is invalid, fill by zero */
+ if(crc == 0 || crc == 0xffffffff || crc != new_crc)
+ memset((void *)data, 0, total_sz);
+
+ if(info->pdl_image_count > PDL_IMAGE_MAX_NUM)
+ info->pdl_image_count = PDL_IMAGE_MAX_NUM;
+
+ loaded = 1;
+ return 0;
+}
+
+int prdinfo_save(void)
+{
+ int ret = 0;
+ unsigned char *data = prdinfo_buf;
+ size_t total_sz = PRDINFO_TOTAL_SIZE;
+ uint32_t *crc_p;
+
+ crc_p = (uint32_t *)&data[PRDINFO_CRC_OFFSET];
+ *crc_p = crc32(0, data, total_sz - sizeof(uint32_t));
+
+ if (rda_media_get() == MEDIA_MMC) {
+ ret = emmc_write_partition(PRDINFO_PART_NAME, data, total_sz);
+ } else {
+ ret = nand_erase_partition(PRDINFO_PART_NAME);
+ if(!ret)
+ ret = nand_write_partition(PRDINFO_PART_NAME, data, total_sz);
+ }
+
+ return ret;
+}
+
+int prdinfo_update_all(char *buf, unsigned long sz)
+{
+ if(sz != PRDINFO_TOTAL_SIZE) {
+ printf("%s: size %lu is invaild.\n", __func__, sz);
+ return -1;
+ }
+ memcpy(prdinfo_buf, buf, sz);
+ prdinfo_save();
+ return 0;
+}
+
+int prdinfo_set_data(struct prdinfo *info)
+{
+ memcpy(prdinfo_buf, (void *)info, sizeof(struct prdinfo));
+ prdinfo_save();
+ return 0;
+}
+
+int prdinfo_get_data(struct prdinfo *info)
+{
+ if(prdinfo_load())
+ return -1;
+ memcpy((void *)info, prdinfo_buf, sizeof(struct prdinfo));
+ return 0;
+}
+
+static int prdinfo_clear(void)
+{
+ memset(prdinfo_buf, 0, PRDINFO_TOTAL_SIZE);
+
+ if (rda_media_get() == MEDIA_MMC)
+ return emmc_erase_partition(PRDINFO_PART_NAME);
+ else
+ return nand_erase_partition(PRDINFO_PART_NAME);
+}
+
+void prdinfo_dump(void)
+{
+ int i;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ printf("product infomation:\n");
+ printf(" Download : %s\n", info->pdl_result ? "OK" : "None");
+ for(i = 0; i < info->pdl_image_count; i++) {
+ int r = info->pdl_images[i].result;
+ printf(" %10s: %s\n", info->pdl_images[i].name,
+ r ? "OK" : "None");
+ }
+ printf(" Calibration : %s\n", info->cali_result ? "OK" : "None");
+ printf(" Autocall Test : %s\n", info->autocall_result ? "OK" : "None");
+ printf(" Factory Test : %s\n", info->factorytest_result ? "OK" : "None");
+ printf(" IMEI Write : %s\n", info->imei_result ? "OK" : "None");
+ printf(" PSN Write : %s\n", info->psn_result ? "OK" : "None");
+ printf(" MBSN Write : %s\n", info->mbsn_result ? "OK" : "None");
+ printf(" MACAddr Write : %s\n", info->macaddr_result ? "OK" : "None");
+ printf(" BTAddr Write : %s\n", info->btaddr_result ? "OK" : "None");
+ printf(" FTM TAG : %#x\n", info->factory_tag);
+ printf(" Reserve Data : ");
+ for(i = 0; i < 10; i++) {
+ printf("0x%x ", ((u32)info->reserve[i]) & 0xff);
+ }
+ printf("\n");
+}
+
+int prdinfo_set_pdl_result(uint8_t rt, uint32_t ftm_tag)
+{
+ int ret = 0;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ ret = prdinfo_load();
+ if(!ret) {
+ info->pdl_result = !!rt;
+ info->factory_tag = ftm_tag ? FACTORY_TAG_NUM : 0;
+ ret = prdinfo_save();
+ }
+ return ret;
+}
+
+int prdinfo_set_cali_result(uint8_t rt)
+{
+ int ret = 0;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ ret = prdinfo_load();
+ if(!ret) {
+ info->cali_result = !!rt;
+ ret = prdinfo_save();
+ }
+ return ret;
+}
+
+int prdinfo_set_autocall_result(uint8_t rt)
+{
+ int ret = 0;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ ret = prdinfo_load();
+ if(!ret) {
+ info->autocall_result = !!rt;
+ ret = prdinfo_save();
+ }
+ return ret;
+}
+
+int prdinfo_set_coupling_result(uint8_t rt)
+{
+ int ret = 0;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ ret = prdinfo_load();
+ if(!ret) {
+ info->reserve[0] = !!rt;
+ ret = prdinfo_save();
+ }
+ return ret;
+}
+
+int prdinfo_set_factorytest_result(uint8_t rt)
+{
+ int ret = 0;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ ret = prdinfo_load();
+ if(!ret) {
+ info->factorytest_result = !!rt;
+ ret = prdinfo_save();
+ }
+ return ret;
+}
+
+int prdinfo_init(char *image_list)
+{
+ int ret = 0;
+ int i, len;
+ char *buff, *p, *q;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ /* clear old data first */
+ prdinfo_clear();
+
+ len = strlen(image_list);
+ if(len < 1) {
+ printf("%s: name list is empty!\n", __func__);
+ return -1;
+ }
+
+ buff = malloc(len + 1);
+ if(!buff)
+ return -1;
+ memcpy(buff, image_list, len+1);
+
+ /* get image names */
+ p = buff;
+ i = 0;
+ while(1) {
+ q = strstr(p, ",");
+ if(q) *q = '\0';
+
+ if(strlen(p) > 0) {
+ strncpy(info->pdl_images[i++].name, p, PDL_IMAGE_NAME_LEN-1);
+ if(i >= PDL_IMAGE_MAX_NUM) break;
+ }
+
+ if(!q) break;
+
+ p = q + 1;
+ if(*p == '\0') break;
+ }
+ info->pdl_image_count = i;
+
+ /* write data to flash */
+ ret = prdinfo_save();
+
+ free(buff);
+ return ret;
+
+}
+
+int prdinfo_set_pdl_image_download_result(char *image_name, uint8_t dl_rt)
+{
+ int ret = 0;
+ int update = 0;
+ int i;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ ret = prdinfo_load();
+ if(ret)
+ return ret;
+
+ for(i = 0; i < info->pdl_image_count; i++) {
+ if(strcmp(image_name, info->pdl_images[i].name) == 0) {
+ if(info->pdl_images[i].result != !!dl_rt) {
+ info->pdl_images[i].result = !!dl_rt;
+ update = 1;
+ }
+ break;
+ }
+ }
+
+ if(update) {
+ ret = prdinfo_save();
+ printf("part '%s', set download result '%s'\n", image_name,
+ (!!dl_rt) ? "success" : "fail");
+ }
+
+ return ret;
+}
+
+uint32_t prdinfo_get_factory_tag(void)
+{
+ uint32_t tag = 0;
+ struct prdinfo *info = (struct prdinfo *)prdinfo_buf;
+
+ if(!prdinfo_load())
+ tag = info->factory_tag;
+ return tag;
+}
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+#ifdef CONFIG_CMD_PRDINFO
+int do_dump_prdinfo(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ prdinfo_dump();
+ return CMD_RET_SUCCESS;
+}
+
+int do_rda_check_factory_tag(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ if(prdinfo_get_factory_tag() == FACTORY_TAG_NUM){
+ enum rda_bm_type bm;
+ char str[2];
+
+ bm = rda_bm_get();
+ if(bm == RDA_BM_NORMAL) {
+ rda_bm_set(RDA_BM_FACTORY);
+ sprintf(str, "%1d", RDA_BM_FACTORY);
+ setenv("bootmode", str);
+ printf("Found factory tag, change bootmode to %1d\n", RDA_BM_FACTORY);
+ }
+ }
+ return 0;
+}
+
+U_BOOT_CMD(prdinfo, 1, 1, do_dump_prdinfo,
+ "dump product infomation", "");
+
+U_BOOT_CMD(rdachkfactag , 1, 1, do_rda_check_factory_tag,
+ "check FTM tag 0xFAC12345 in 'misc' part", "");
+
+#endif
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index c245352aca..40c79b3f30 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -25,6 +25,14 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmmc.o
+ifdef CONFIG_SPL_BUILD
+ifdef CONFIG_SPL_EMMC_LOAD
+COBJS-y += mmc_spl_load.o
+endif
+else
+COBJS-$(CONFIG_GENERIC_MMC) += mmc_write.o
+endif
+
COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
@@ -39,10 +47,15 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o
COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
-COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o
+COBJS-$(CONFIG_RDA_MMC) += rda_mmc.o
+COBJS-$(CONFIG_RDA_MMC_LEGACY) += rda_mmc_legacy.o
COBJS-$(CONFIG_SDHCI) += sdhci.o
+COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o
COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o
+ifndef CONFIG_SPL_BUILD
+COBJS-$(CONFIG_PARTITIONS) += mmc_parts.o
+endif
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/mmc/bfin_sdh.c b/drivers/mmc/bfin_sdh.c
index 08fc5c1d3e..8d59d46c64 100644
--- a/drivers/mmc/bfin_sdh.c
+++ b/drivers/mmc/bfin_sdh.c
@@ -256,7 +256,6 @@ int bfin_mmc_init(bd_t *bis)
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->f_max = get_sclk();
mmc->f_min = mmc->f_max >> 9;
- mmc->block_dev.part_type = PART_TYPE_DOS;
mmc->b_max = 0;
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index a2f35e3e99..07370b5729 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -307,19 +307,56 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
#else
esdhc_write32(&regs->xfertyp, xfertyp);
#endif
+
+ /* Mask all irqs */
+ esdhc_write32(&regs->irqsigen, 0);
+
/* Wait for the command to complete */
- while (!(esdhc_read32(&regs->irqstat) & IRQSTAT_CC))
+ while (!(esdhc_read32(&regs->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE)))
;
irqstat = esdhc_read32(&regs->irqstat);
esdhc_write32(&regs->irqstat, irqstat);
+ /* Reset CMD and DATA portions on error */
+ if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) {
+ esdhc_write32(&regs->sysctl, esdhc_read32(&regs->sysctl) |
+ SYSCTL_RSTC);
+ while (esdhc_read32(&regs->sysctl) & SYSCTL_RSTC)
+ ;
+
+ if (data) {
+ esdhc_write32(&regs->sysctl,
+ esdhc_read32(&regs->sysctl) |
+ SYSCTL_RSTD);
+ while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTD))
+ ;
+ }
+ }
+
if (irqstat & CMD_ERR)
return COMM_ERR;
if (irqstat & IRQSTAT_CTOE)
return TIMEOUT;
+ /* Workaround for ESDHC errata ENGcm03648 */
+ if (!data && (cmd->resp_type & MMC_RSP_BUSY)) {
+ int timeout = 2500;
+
+ /* Poll on DATA0 line for cmd with busy signal for 250 ms */
+ while (timeout > 0 && !(esdhc_read32(&regs->prsstat) &
+ PRSSTAT_DAT0)) {
+ udelay(100);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ printf("Timeout waiting for DAT0 to go high!\n");
+ return TIMEOUT;
+ }
+ }
+
/* Copy the response to the response buffer */
if (cmd->resp_type & MMC_RSP_136) {
u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 596732e80e..1d09a56f3e 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -4,23 +4,7 @@
*
* Based vaguely on the Linux code
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * 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
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
@@ -31,6 +15,10 @@
#include <malloc.h>
#include <linux/list.h>
#include <div64.h>
+#include "mmc_private.h"
+
+//#define CONFIG_MMC_TRACE
+//#define CONFIG_MMC_DDR_MODE
/* Set block count limit because of 16 bit register limit on some hardware*/
#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
@@ -40,118 +28,58 @@
static struct list_head mmc_devices;
static int cur_dev_num = -1;
-int __board_mmc_getcd(struct mmc *mmc) {
- return -1;
-}
-
-int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,
- alias("__board_mmc_getcd")));
+/*
+* Manufacturer ID list
+*/
+static const struct mmc_manufacturers mmc_manuf_ids[] = {
+ {MMC_MFR_TOSHIBA, "Toshiba"},
+ {MMC_MFR_SAMSUNG, "Samsung"},
+ {MMC_MFR_SANDISK, "SanDisk"},
+ {MMC_MFR_GIGADEVICE, "GigaDevice"},
+ {MMC_MFR_HYNIX, "Hynix"},
+ {MMC_MFR_MICRON, "Micron"},
+ {MMC_MFR_MICRON1, "Micron"},
+ {0x0, "Unknown"}
+};
-#ifdef CONFIG_MMC_BOUNCE_BUFFER
-static int mmc_bounce_need_bounce(struct mmc_data *orig)
+int __weak board_mmc_getwp(struct mmc *mmc)
{
- ulong addr, len;
-
- if (orig->flags & MMC_DATA_READ)
- addr = (ulong)orig->dest;
- else
- addr = (ulong)orig->src;
-
- if (addr % ARCH_DMA_MINALIGN) {
- debug("MMC: Unaligned data destination address %08lx!\n", addr);
- return 1;
- }
-
- len = (ulong)(orig->blocksize * orig->blocks);
- if (len % ARCH_DMA_MINALIGN) {
- debug("MMC: Unaligned data destination length %08lx!\n", len);
- return 1;
- }
-
- return 0;
+ return -1;
}
-static int mmc_bounce_buffer_start(struct mmc_data *backup,
- struct mmc_data *orig)
+int mmc_getwp(struct mmc *mmc)
{
- ulong origlen, len;
- void *buffer;
+ int wp;
- if (!orig)
- return 0;
-
- if (!mmc_bounce_need_bounce(orig))
- return 0;
+ wp = board_mmc_getwp(mmc);
- memcpy(backup, orig, sizeof(struct mmc_data));
-
- origlen = orig->blocksize * orig->blocks;
- len = roundup(origlen, ARCH_DMA_MINALIGN);
- buffer = memalign(ARCH_DMA_MINALIGN, len);
- if (!buffer) {
- puts("MMC: Error allocating MMC bounce buffer!\n");
- return 1;
- }
-
- if (orig->flags & MMC_DATA_READ) {
- orig->dest = buffer;
- } else {
- memcpy(buffer, orig->src, origlen);
- orig->src = buffer;
+ if (wp < 0) {
+ if (mmc->getwp)
+ wp = mmc->getwp(mmc);
+ else
+ wp = 0;
}
- return 0;
+ return wp;
}
-static void mmc_bounce_buffer_stop(struct mmc_data *backup,
- struct mmc_data *orig)
-{
- ulong len;
-
- if (!orig)
- return;
-
- if (!mmc_bounce_need_bounce(backup))
- return;
-
- if (backup->flags & MMC_DATA_READ) {
- len = backup->blocksize * backup->blocks;
- memcpy(backup->dest, orig->dest, len);
- free(orig->dest);
- orig->dest = backup->dest;
- } else {
- free((void *)orig->src);
- orig->src = backup->src;
- }
-
- return;
-
+int __board_mmc_getcd(struct mmc *mmc) {
+ return -1;
}
-#else
-static inline int mmc_bounce_buffer_start(struct mmc_data *backup,
- struct mmc_data *orig) { return 0; }
-static inline void mmc_bounce_buffer_stop(struct mmc_data *backup,
- struct mmc_data *orig) { }
-#endif
+
+int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,
+ alias("__board_mmc_getcd")));
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
- struct mmc_data backup;
int ret;
- memset(&backup, 0, sizeof(backup));
-
- ret = mmc_bounce_buffer_start(&backup, data);
- if (ret)
- return ret;
-
#ifdef CONFIG_MMC_TRACE
int i;
u8 *ptr;
printf("CMD_SEND:%d\n", cmd->cmdidx);
printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
- printf("\t\tFLAG\t\t\t %d\n", cmd->flags);
ret = mmc->send_cmd(mmc, cmd, data);
switch (cmd->resp_type) {
case MMC_RSP_NONE:
@@ -197,7 +125,6 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
#else
ret = mmc->send_cmd(mmc, cmd, data);
#endif
- mmc_bounce_buffer_stop(&backup, data);
return ret;
}
@@ -213,7 +140,6 @@ int mmc_send_status(struct mmc *mmc, int timeout)
cmd.resp_type = MMC_RSP_R1;
if (!mmc_host_is_spi(mmc))
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
do {
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -223,8 +149,10 @@ int mmc_send_status(struct mmc *mmc, int timeout)
MMC_STATE_PRG)
break;
else if (cmd.response[0] & MMC_STATUS_MASK) {
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Status Error: 0x%08X\n",
cmd.response[0]);
+#endif
return COMM_ERR;
}
} else if (--retries < 0)
@@ -238,10 +166,14 @@ int mmc_send_status(struct mmc *mmc, int timeout)
status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
printf("CURR STATE:%d\n", status);
#endif
- if (!timeout) {
+ if (timeout <= 0) {
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Timeout waiting card ready\n");
+#endif
return TIMEOUT;
}
+ if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
+ return SWITCH_ERR;
return 0;
}
@@ -250,10 +182,14 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
{
struct mmc_cmd cmd;
+#ifdef CONFIG_MMC_DDR_MODE
+ if (mmc->card_caps & MMC_MODE_DDR_52MHz)
+ return 0;
+#endif
+
cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = len;
- cmd.flags = 0;
return mmc_send_cmd(mmc, &cmd, NULL);
}
@@ -270,175 +206,15 @@ struct mmc *find_mmc_device(int dev_num)
return m;
}
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC Device %d not found\n", dev_num);
+#endif
return NULL;
}
-static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
-{
- struct mmc_cmd cmd;
- ulong end;
- int err, start_cmd, end_cmd;
-
- if (mmc->high_capacity)
- end = start + blkcnt - 1;
- else {
- end = (start + blkcnt - 1) * mmc->write_bl_len;
- start *= mmc->write_bl_len;
- }
-
- if (IS_SD(mmc)) {
- start_cmd = SD_CMD_ERASE_WR_BLK_START;
- end_cmd = SD_CMD_ERASE_WR_BLK_END;
- } else {
- start_cmd = MMC_CMD_ERASE_GROUP_START;
- end_cmd = MMC_CMD_ERASE_GROUP_END;
- }
-
- cmd.cmdidx = start_cmd;
- cmd.cmdarg = start;
- cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
- goto err_out;
-
- cmd.cmdidx = end_cmd;
- cmd.cmdarg = end;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
- goto err_out;
-
- cmd.cmdidx = MMC_CMD_ERASE;
- cmd.cmdarg = SECURE_ERASE;
- cmd.resp_type = MMC_RSP_R1b;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
- goto err_out;
-
- return 0;
-
-err_out:
- puts("mmc erase failed\n");
- return err;
-}
-
-static unsigned long
-mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
-{
- int err = 0;
- struct mmc *mmc = find_mmc_device(dev_num);
- lbaint_t blk = 0, blk_r = 0;
-
- if (!mmc)
- return -1;
-
- if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
- printf("\n\nCaution! Your devices Erase group is 0x%x\n"
- "The erase range would be change to 0x%lx~0x%lx\n\n",
- mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
- ((start + blkcnt + mmc->erase_grp_size)
- & ~(mmc->erase_grp_size - 1)) - 1);
-
- while (blk < blkcnt) {
- blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
- mmc->erase_grp_size : (blkcnt - blk);
- err = mmc_erase_t(mmc, start + blk, blk_r);
- if (err)
- break;
-
- blk += blk_r;
- }
-
- return blk;
-}
-
-static ulong
-mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
-{
- struct mmc_cmd cmd;
- struct mmc_data data;
- int timeout = 1000;
-
- if ((start + blkcnt) > mmc->block_dev.lba) {
- printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
- start + blkcnt, mmc->block_dev.lba);
- return 0;
- }
-
- if (blkcnt > 1)
- cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
- else
- cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
-
- if (mmc->high_capacity)
- cmd.cmdarg = start;
- else
- cmd.cmdarg = start * mmc->write_bl_len;
-
- cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
-
- data.src = src;
- data.blocks = blkcnt;
- data.blocksize = mmc->write_bl_len;
- data.flags = MMC_DATA_WRITE;
-
- if (mmc_send_cmd(mmc, &cmd, &data)) {
- printf("mmc write failed\n");
- return 0;
- }
-
- /* SPI multiblock writes terminate using a special
- * token, not a STOP_TRANSMISSION request.
- */
- if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
- cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
- cmd.cmdarg = 0;
- cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
- if (mmc_send_cmd(mmc, &cmd, NULL)) {
- printf("mmc fail to send stop cmd\n");
- return 0;
- }
- }
-
- /* Waiting for the ready status */
- if (mmc_send_status(mmc, timeout))
- return 0;
-
- return blkcnt;
-}
-
-static ulong
-mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
-{
- lbaint_t cur, blocks_todo = blkcnt;
-
- struct mmc *mmc = find_mmc_device(dev_num);
- if (!mmc)
- return 0;
-
- if (mmc_set_blocklen(mmc, mmc->write_bl_len))
- return 0;
-
- do {
- cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo;
- if(mmc_write_blocks(mmc, start, cur, src) != cur)
- return 0;
- blocks_todo -= cur;
- start += cur;
- src += cur * mmc->write_bl_len;
- } while (blocks_todo > 0);
-
- return blkcnt;
-}
-
-int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
+static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
+ lbaint_t blkcnt)
{
struct mmc_cmd cmd;
struct mmc_data data;
@@ -454,7 +230,6 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
cmd.cmdarg = start * mmc->read_bl_len;
cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
data.dest = dst;
data.blocks = blkcnt;
@@ -468,9 +243,10 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("mmc fail to send stop cmd\n");
+#endif
return 0;
}
}
@@ -478,7 +254,7 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
return blkcnt;
}
-static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
+static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst)
{
lbaint_t cur, blocks_todo = blkcnt;
@@ -490,8 +266,10 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
return 0;
if ((start + blkcnt) > mmc->block_dev.lba) {
- printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
start + blkcnt, mmc->block_dev.lba);
+#endif
return 0;
}
@@ -510,7 +288,7 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
return blkcnt;
}
-int mmc_go_idle(struct mmc* mmc)
+static int mmc_go_idle(struct mmc *mmc)
{
struct mmc_cmd cmd;
int err;
@@ -520,7 +298,6 @@ int mmc_go_idle(struct mmc* mmc)
cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_NONE;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -532,8 +309,7 @@ int mmc_go_idle(struct mmc* mmc)
return 0;
}
-int
-sd_send_op_cond(struct mmc *mmc)
+static int sd_send_op_cond(struct mmc *mmc)
{
int timeout = 1000;
int err;
@@ -543,7 +319,6 @@ sd_send_op_cond(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -584,7 +359,6 @@ sd_send_op_cond(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -600,66 +374,92 @@ sd_send_op_cond(struct mmc *mmc)
return 0;
}
+/* We pass in the cmd since otherwise the init seems to fail */
+static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd,
+ int use_arg)
+{
+ int err;
+
+ cmd->cmdidx = MMC_CMD_SEND_OP_COND;
+ cmd->resp_type = MMC_RSP_R3;
+ cmd->cmdarg = 0;
+ if (use_arg && !mmc_host_is_spi(mmc)) {
+ cmd->cmdarg =
+ (mmc->voltages &
+ (mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
+ (mmc->op_cond_response & OCR_ACCESS_MODE);
+ }
+ if (mmc->host_caps & MMC_MODE_HC)
+ cmd->cmdarg |= OCR_HCS;
+
+ err = mmc_send_cmd(mmc, cmd, NULL);
+ if (err)
+ return err;
+ mmc->op_cond_response = cmd->response[0];
+ mmc->ocr = cmd->response[0];
+ return 0;
+}
+
int mmc_send_op_cond(struct mmc *mmc)
{
- int timeout = 10000;
struct mmc_cmd cmd;
- int err;
+ int err, i;
/* Some cards seem to need this */
mmc_go_idle(mmc);
/* Asking to the card its capabilities */
- cmd.cmdidx = MMC_CMD_SEND_OP_COND;
- cmd.resp_type = MMC_RSP_R3;
- cmd.cmdarg = 0;
- cmd.flags = 0;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
-
- if (err)
- return err;
-
- udelay(1000);
-
- do {
- cmd.cmdidx = MMC_CMD_SEND_OP_COND;
- cmd.resp_type = MMC_RSP_R3;
- cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 :
- (mmc->voltages &
- (cmd.response[0] & OCR_VOLTAGE_MASK)) |
- (cmd.response[0] & OCR_ACCESS_MODE));
-
- if (mmc->host_caps & MMC_MODE_HC)
- cmd.cmdarg |= OCR_HCS;
-
- cmd.flags = 0;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
-
+ mmc->op_cond_pending = 1;
+ for (i = 0; i < 2; i++) {
+ err = mmc_send_op_cond_iter(mmc, &cmd, i != 0);
if (err)
return err;
- udelay(1000);
- } while (!(cmd.response[0] & OCR_BUSY) && timeout--);
+ /* exit if not busy (flag seems to be inverted) */
+ if (mmc->op_cond_response & OCR_BUSY)
+ return 0;
+ }
+ return IN_PROGRESS;
+}
- if (timeout <= 0)
- return UNUSABLE_ERR;
+int mmc_complete_op_cond(struct mmc *mmc)
+{
+ struct mmc_cmd cmd;
+ int timeout = 1000;
+ uint start;
+ int err;
+
+ mmc->op_cond_pending = 0;
+
+ /* If device is ready, do not check OCR again.
+ * For NCMMCS16G, another OCR reading may cause that it's not working. */
+ if(!(mmc->ocr & OCR_BUSY))
+ {
+ start = get_timer(0);
+ do {
+ err = mmc_send_op_cond_iter(mmc, &cmd, 1);
+ if (err)
+ return err;
+ if (get_timer(start) > timeout)
+ return UNUSABLE_ERR;
+ udelay(100);
+ } while (!(mmc->op_cond_response & OCR_BUSY));
+ }
if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
+
+ mmc->ocr = cmd.response[0];
}
mmc->version = MMC_VERSION_UNKNOWN;
- mmc->ocr = cmd.response[0];
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
mmc->rca = 0;
@@ -668,7 +468,7 @@ int mmc_send_op_cond(struct mmc *mmc)
}
-int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
+static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
{
struct mmc_cmd cmd;
struct mmc_data data;
@@ -678,11 +478,10 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
- cmd.flags = 0;
- data.dest = ext_csd;
+ data.dest = (char *)ext_csd;
data.blocks = 1;
- data.blocksize = 512;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
data.flags = MMC_DATA_READ;
err = mmc_send_cmd(mmc, &cmd, &data);
@@ -691,7 +490,7 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
}
-int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
+static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
{
struct mmc_cmd cmd;
int timeout = 1000;
@@ -702,7 +501,6 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) |
(value << 8);
- cmd.flags = 0;
ret = mmc_send_cmd(mmc, &cmd, NULL);
@@ -714,9 +512,9 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
}
-int mmc_change_freq(struct mmc *mmc)
+static int mmc_change_freq(struct mmc *mmc)
{
- ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
char cardtype;
int err;
@@ -739,7 +537,7 @@ int mmc_change_freq(struct mmc *mmc)
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
if (err)
- return err;
+ return err == SWITCH_ERR ? 0 : err;
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, ext_csd);
@@ -752,10 +550,43 @@ int mmc_change_freq(struct mmc *mmc)
return 0;
/* High Speed is set, there are two types: 52MHz and 26MHz */
- if (cardtype & MMC_HS_52MHZ)
+ if (cardtype & EXT_CSD_CARD_TYPE_52) {
+#ifdef CONFIG_MMC_DDR_MODE
+ if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
+ mmc->card_caps |= MMC_MODE_DDR_52MHz;
+#endif
mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
- else
+ } else {
mmc->card_caps |= MMC_MODE_HS;
+ }
+
+ return 0;
+}
+
+static int mmc_set_capacity(struct mmc *mmc, int part_num)
+{
+ switch (part_num) {
+ case 0:
+ mmc->capacity = mmc->capacity_user;
+ break;
+ case 1:
+ case 2:
+ mmc->capacity = mmc->capacity_boot;
+ break;
+ case 3:
+ mmc->capacity = mmc->capacity_rpmb;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ mmc->capacity = mmc->capacity_gp[part_num - 4];
+ break;
+ default:
+ return -1;
+ }
+
+ mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
return 0;
}
@@ -763,13 +594,18 @@ int mmc_change_freq(struct mmc *mmc)
int mmc_switch_part(int dev_num, unsigned int part_num)
{
struct mmc *mmc = find_mmc_device(dev_num);
+ int ret;
if (!mmc)
return -1;
- return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
- (mmc->part_config & ~PART_ACCESS_MASK)
- | (part_num & PART_ACCESS_MASK));
+ ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
+ (mmc->part_config & ~PART_ACCESS_MASK)
+ | (part_num & PART_ACCESS_MASK));
+ if (ret)
+ return ret;
+
+ return mmc_set_capacity(mmc, part_num);
}
int mmc_getcd(struct mmc *mmc)
@@ -778,13 +614,17 @@ int mmc_getcd(struct mmc *mmc)
cd = board_mmc_getcd(mmc);
- if ((cd < 0) && mmc->getcd)
- cd = mmc->getcd(mmc);
+ if (cd < 0) {
+ if (mmc->getcd)
+ cd = mmc->getcd(mmc);
+ else
+ cd = 1;
+ }
return cd;
}
-int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
+static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
{
struct mmc_cmd cmd;
struct mmc_data data;
@@ -795,7 +635,6 @@ int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
cmd.cmdarg = (mode << 31) | 0xffffff;
cmd.cmdarg &= ~(0xf << (group * 4));
cmd.cmdarg |= value << (group * 4);
- cmd.flags = 0;
data.dest = (char *)resp;
data.blocksize = 64;
@@ -806,7 +645,7 @@ int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
}
-int sd_change_freq(struct mmc *mmc)
+static int sd_change_freq(struct mmc *mmc)
{
int err;
struct mmc_cmd cmd;
@@ -824,7 +663,6 @@ int sd_change_freq(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -834,7 +672,6 @@ int sd_change_freq(struct mmc *mmc)
cmd.cmdidx = SD_CMD_APP_SEND_SCR;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
- cmd.flags = 0;
timeout = 3;
@@ -865,6 +702,8 @@ retry_scr:
break;
case 2:
mmc->version = SD_VERSION_2;
+ if ((mmc->scr[0] >> 15) & 0x1)
+ mmc->version = SD_VERSION_3;
break;
default:
mmc->version = SD_VERSION_1_0;
@@ -947,7 +786,7 @@ static const int multipliers[] = {
80,
};
-void mmc_set_ios(struct mmc *mmc)
+static void mmc_set_ios(struct mmc *mmc)
{
mmc->set_ios(mmc);
}
@@ -965,21 +804,21 @@ void mmc_set_clock(struct mmc *mmc, uint clock)
mmc_set_ios(mmc);
}
-void mmc_set_bus_width(struct mmc *mmc, uint width)
+static void mmc_set_bus_width(struct mmc *mmc, uint width)
{
mmc->bus_width = width;
mmc_set_ios(mmc);
}
-int mmc_startup(struct mmc *mmc)
+static int mmc_startup(struct mmc *mmc)
{
- int err, width;
+ int err, i;
uint mult, freq;
u64 cmult, csize, capacity;
struct mmc_cmd cmd;
- ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512);
- ALLOC_CACHE_ALIGN_BUFFER(char, test_csd, 512);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+ ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
int timeout = 1000;
#ifdef CONFIG_MMC_SPI_CRC_ON
@@ -987,7 +826,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 1;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -1000,7 +838,6 @@ int mmc_startup(struct mmc *mmc)
MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = 0;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1018,7 +855,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
cmd.cmdarg = mmc->rca << 16;
cmd.resp_type = MMC_RSP_R6;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1033,7 +869,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_SEND_CSD;
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1096,21 +931,24 @@ int mmc_startup(struct mmc *mmc)
cmult = (mmc->csd[2] & 0x00038000) >> 15;
}
- mmc->capacity = (csize + 1) << (cmult + 2);
- mmc->capacity *= mmc->read_bl_len;
+ mmc->capacity_user = (csize + 1) << (cmult + 2);
+ mmc->capacity_user *= mmc->read_bl_len;
+ mmc->capacity_boot = 0;
+ mmc->capacity_rpmb = 0;
+ for (i = 0; i < 4; i++)
+ mmc->capacity_gp[i] = 0;
- if (mmc->read_bl_len > 512)
- mmc->read_bl_len = 512;
+ if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
+ mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
- if (mmc->write_bl_len > 512)
- mmc->write_bl_len = 512;
+ if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
+ mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
/* Select the card, and put it into Transfer Mode */
if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
cmd.cmdidx = MMC_CMD_SELECT_CARD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -1125,7 +963,7 @@ int mmc_startup(struct mmc *mmc)
if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
/* check ext_csd version and capacity */
err = mmc_send_ext_csd(mmc, ext_csd);
- if (!err & (ext_csd[EXT_CSD_REV] >= 2)) {
+ if (!err && (ext_csd[EXT_CSD_REV] >= 2)) {
/*
* According to the JEDEC Standard, the value of
* ext_csd's capacity is valid if the value is more
@@ -1135,20 +973,48 @@ int mmc_startup(struct mmc *mmc)
| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
- capacity *= 512;
+ capacity *= MMC_MAX_BLOCK_LEN;
if ((capacity >> 20) > 2 * 1024)
- mmc->capacity = capacity;
+ mmc->capacity_user = capacity;
+ }
+
+ switch (ext_csd[EXT_CSD_REV]) {
+ case 1:
+ mmc->version = MMC_VERSION_4_1;
+ break;
+ case 2:
+ mmc->version = MMC_VERSION_4_2;
+ break;
+ case 3:
+ mmc->version = MMC_VERSION_4_3;
+ break;
+ case 5:
+ mmc->version = MMC_VERSION_4_41;
+ break;
+ case 6:
+ mmc->version = MMC_VERSION_4_5;
+ break;
}
/*
- * Check whether GROUP_DEF is set, if yes, read out
- * group size from ext_csd directly, or calculate
- * the group size from the csd value.
+ * Host needs to enable ERASE_GRP_DEF bit if device is
+ * partitioned. This bit will be lost every time after a reset
+ * or power off. This will affect erase size.
*/
- if (ext_csd[EXT_CSD_ERASE_GROUP_DEF])
+ if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
+ (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_ERASE_GROUP_DEF, 1);
+
+ if (err)
+ return err;
+
+ /* Read out group size from ext_csd */
mmc->erase_grp_size =
- ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 512 * 1024;
- else {
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
+ MMC_MAX_BLOCK_LEN * 1024;
+ } else {
+ /* Calculate the group size from the csd value. */
int erase_gsz, erase_gmul;
erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
@@ -1157,10 +1023,28 @@ int mmc_startup(struct mmc *mmc)
}
/* store the partition info of emmc */
- if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT)
+ if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
+ ext_csd[EXT_CSD_BOOT_MULT])
mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
+
+ mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
+
+ mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
+
+ for (i = 0; i < 4; i++) {
+ int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
+ mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
+ (ext_csd[idx + 1] << 8) + ext_csd[idx];
+ mmc->capacity_gp[i] *=
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+ mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ }
}
+ err = mmc_set_capacity(mmc, mmc->part_num);
+ if (err)
+ return err;
+
if (IS_SD(mmc))
err = sd_change_freq(mmc);
else
@@ -1177,7 +1061,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -1186,7 +1069,6 @@ int mmc_startup(struct mmc *mmc)
cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 2;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
@@ -1195,23 +1077,59 @@ int mmc_startup(struct mmc *mmc)
}
if (mmc->card_caps & MMC_MODE_HS)
- mmc_set_clock(mmc, 50000000);
+ mmc->tran_speed = 50000000;
else
- mmc_set_clock(mmc, 25000000);
+ mmc->tran_speed = 25000000;
} else {
- for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) {
- /* Set the card to use 4 bit*/
+ int idx;
+
+ /* An array of possible bus widths in order of preference */
+ static unsigned ext_csd_bits[] = {
+#ifdef CONFIG_MMC_DDR_MODE
+ EXT_CSD_DDR_BUS_WIDTH_8,
+ EXT_CSD_DDR_BUS_WIDTH_4,
+#endif
+ EXT_CSD_BUS_WIDTH_8,
+ EXT_CSD_BUS_WIDTH_4,
+ EXT_CSD_BUS_WIDTH_1,
+ };
+
+ /* An array to map CSD bus widths to host cap bits */
+ static unsigned ext_to_hostcaps[] = {
+#ifdef CONFIG_MMC_DDR_MODE
+ [EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz,
+ [EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz,
+#endif
+ [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
+ [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
+ };
+
+ /* An array to map chosen bus width to an integer */
+ static unsigned widths[] = {
+#ifdef CONFIG_MMC_DDR_MODE
+ 0x80000008, 0x80000004, /* ddr mode */
+#endif
+ 8, 4, 1,
+ };
+
+ for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
+ unsigned int extw = ext_csd_bits[idx];
+
+ /*
+ * Check to make sure the controller supports
+ * this bus width, if it's more than 1
+ */
+ if (extw != EXT_CSD_BUS_WIDTH_1 &&
+ !(mmc->host_caps & ext_to_hostcaps[extw]))
+ continue;
+
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH, width);
+ EXT_CSD_BUS_WIDTH, extw);
if (err)
continue;
- if (!width) {
- mmc_set_bus_width(mmc, 1);
- break;
- } else
- mmc_set_bus_width(mmc, 4 * width);
+ mmc_set_bus_width(mmc, widths[idx]);
err = mmc_send_ext_csd(mmc, test_csd);
if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \
@@ -1225,38 +1143,76 @@ int mmc_startup(struct mmc *mmc)
&& memcmp(&ext_csd[EXT_CSD_SEC_CNT], \
&test_csd[EXT_CSD_SEC_CNT], 4) == 0) {
- mmc->card_caps |= width;
+ mmc->card_caps |= ext_to_hostcaps[extw];
break;
}
}
if (mmc->card_caps & MMC_MODE_HS) {
if (mmc->card_caps & MMC_MODE_HS_52MHz)
- mmc_set_clock(mmc, 52000000);
+ mmc->tran_speed = 52000000;
else
- mmc_set_clock(mmc, 26000000);
- } else
- mmc_set_clock(mmc, 20000000);
+ mmc->tran_speed = 26000000;
+ }
}
+ mmc_set_clock(mmc, mmc->tran_speed);
+
/* fill in device description */
mmc->block_dev.lun = 0;
mmc->block_dev.type = 0;
mmc->block_dev.blksz = mmc->read_bl_len;
+ mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
- sprintf(mmc->block_dev.vendor, "Man %06x Snr %08x", mmc->cid[0] >> 8,
- (mmc->cid[2] << 8) | (mmc->cid[3] >> 24));
- sprintf(mmc->block_dev.product, "%c%c%c%c%c", mmc->cid[0] & 0xff,
- (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
- (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
- sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28,
- (mmc->cid[2] >> 24) & 0xf);
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
+ mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
+ (mmc->cid[3] >> 16) & 0xffff);
+ sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
+ (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
+ (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
+ (mmc->cid[2] >> 24) & 0xff);
+ sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
+ (mmc->cid[2] >> 16) & 0xf);
+#else
+ mmc->block_dev.vendor[0] = 0;
+ mmc->block_dev.product[0] = 0;
+ mmc->block_dev.revision[0] = 0;
+#endif
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
init_part(&mmc->block_dev);
+#endif
return 0;
}
-int mmc_send_if_cond(struct mmc *mmc)
+static void print_mmcinfo(struct mmc *mmc)
+{
+ int maf_idx;
+ for (maf_idx = 0; mmc_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
+ if (mmc_manuf_ids[maf_idx].id == (mmc->cid[0] >> 24))
+ break;
+ }
+
+ printf("-------------MMC information-----------\n");
+ printf("Manufacturer ID: %x, Name: %s \n", mmc->cid[0] >> 24, mmc_manuf_ids[maf_idx].name);
+ printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
+
+ printf("Tran Speed: %d\n", mmc->tran_speed);
+ printf("Rd Block Len: %d\n", mmc->read_bl_len);
+
+ printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC",
+ (mmc->version >> 8) & 0xf, mmc->version & 0xff);
+
+ printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
+ puts("Capacity: ");
+ //print_size(mmc->capacity, "\n");
+
+ printf("Bus Width: %d-bit\n", mmc->bus_width);
+ printf("-----------------------------------------\n");
+}
+
+static int mmc_send_if_cond(struct mmc *mmc)
{
struct mmc_cmd cmd;
int err;
@@ -1265,7 +1221,6 @@ int mmc_send_if_cond(struct mmc *mmc)
/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;
cmd.resp_type = MMC_RSP_R7;
- cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -1303,18 +1258,22 @@ int mmc_register(struct mmc *mmc)
block_dev_desc_t *mmc_get_dev(int dev)
{
struct mmc *mmc = find_mmc_device(dev);
+ if (!mmc || mmc_init(mmc))
+ return NULL;
- return mmc ? &mmc->block_dev : NULL;
+ return &mmc->block_dev;
}
#endif
-int mmc_init(struct mmc *mmc)
+int mmc_start_init(struct mmc *mmc)
{
int err;
if (mmc_getcd(mmc) == 0) {
mmc->has_init = 0;
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: no card present\n");
+#endif
return NO_CARD_ERR;
}
@@ -1348,17 +1307,51 @@ int mmc_init(struct mmc *mmc)
if (err == TIMEOUT) {
err = mmc_send_op_cond(mmc);
- if (err) {
+ if (err && err != IN_PROGRESS) {
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Card did not respond to voltage select!\n");
+#endif
return UNUSABLE_ERR;
}
}
- err = mmc_startup(mmc);
+ if (err == IN_PROGRESS)
+ mmc->init_in_progress = 1;
+
+ return err;
+}
+
+static int mmc_complete_init(struct mmc *mmc)
+{
+ int err = 0;
+
+ if (mmc->op_cond_pending)
+ err = mmc_complete_op_cond(mmc);
+
+ if (!err)
+ err = mmc_startup(mmc);
if (err)
mmc->has_init = 0;
else
mmc->has_init = 1;
+ mmc->init_in_progress = 0;
+ return err;
+}
+
+int mmc_init(struct mmc *mmc)
+{
+ int err = IN_PROGRESS;
+ unsigned start = get_timer(0);
+
+ if (mmc->has_init)
+ return 0;
+ if (!mmc->init_in_progress)
+ err = mmc_start_init(mmc);
+
+ if (!err || err == IN_PROGRESS)
+ err = mmc_complete_init(mmc);
+ debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
+ print_mmcinfo(mmc);
return err;
}
@@ -1374,6 +1367,8 @@ static int __def_mmc_init(bd_t *bis)
int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+
void print_mmc_devices(char separator)
{
struct mmc *m;
@@ -1391,11 +1386,34 @@ void print_mmc_devices(char separator)
printf("\n");
}
+#else
+void print_mmc_devices(char separator) { }
+#endif
+
int get_mmc_num(void)
{
return cur_dev_num;
}
+void mmc_set_preinit(struct mmc *mmc, int preinit)
+{
+ mmc->preinit = preinit;
+}
+
+static void do_preinit(void)
+{
+ struct mmc *m;
+ struct list_head *entry;
+
+ list_for_each(entry, &mmc_devices) {
+ m = list_entry(entry, struct mmc, link);
+
+ if (m->preinit)
+ mmc_start_init(m);
+ }
+}
+
+
int mmc_initialize(bd_t *bis)
{
INIT_LIST_HEAD (&mmc_devices);
@@ -1404,7 +1422,144 @@ int mmc_initialize(bd_t *bis)
if (board_mmc_init(bis) < 0)
cpu_mmc_init(bis);
+#ifndef CONFIG_SPL_BUILD
print_mmc_devices(',');
+#endif
+ do_preinit();
return 0;
}
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+/*
+ * This function changes the size of boot partition and the size of rpmb
+ * partition present on EMMC devices.
+ *
+ * Input Parameters:
+ * struct *mmc: pointer for the mmc device strcuture
+ * bootsize: size of boot partition
+ * rpmbsize: size of rpmb partition
+ *
+ * Returns 0 on success.
+ */
+
+int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize,
+ unsigned long rpmbsize)
+{
+ int err;
+ struct mmc_cmd cmd;
+
+ /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = MMC_CMD62_ARG1;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error1 = %d\n", err);
+ return err;
+ }
+
+ /* Boot partition changing mode */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = MMC_CMD62_ARG2;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error2 = %d\n", err);
+ return err;
+ }
+ /* boot partition size is multiple of 128KB */
+ bootsize = (bootsize * 1024) / 128;
+
+ /* Arg: boot partition size */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = bootsize;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error3 = %d\n", err);
+ return err;
+ }
+ /* RPMB partition size is multiple of 128KB */
+ rpmbsize = (rpmbsize * 1024) / 128;
+ /* Arg: RPMB partition size */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = rpmbsize;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error4 = %d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * This function shall form and send the commands to open / close the
+ * boot partition specified by user.
+ *
+ * Input Parameters:
+ * ack: 0x0 - No boot acknowledge sent (default)
+ * 0x1 - Boot acknowledge sent during boot operation
+ * part_num: User selects boot data that will be sent to master
+ * 0x0 - Device not boot enabled (default)
+ * 0x1 - Boot partition 1 enabled for boot
+ * 0x2 - Boot partition 2 enabled for boot
+ * access: User selects partitions to access
+ * 0x0 : No access to boot partition (default)
+ * 0x1 : R/W boot partition 1
+ * 0x2 : R/W boot partition 2
+ * 0x3 : R/W Replay Protected Memory Block (RPMB)
+ *
+ * Returns 0 on success.
+ */
+int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
+{
+ int err;
+ struct mmc_cmd cmd;
+
+ /* Boot ack enable, boot partition enable , boot partition access */
+ cmd.cmdidx = MMC_CMD_SWITCH;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_PART_CONF << 16) |
+ ((EXT_CSD_BOOT_ACK(ack) |
+ EXT_CSD_BOOT_PART_NUM(part_num) |
+ EXT_CSD_PARTITION_ACCESS(access)) << 8);
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ if (access) {
+ debug("mmc boot partition#%d open fail:Error1 = %d\n",
+ part_num, err);
+ } else {
+ debug("mmc boot partition#%d close fail:Error = %d\n",
+ part_num, err);
+ }
+ return err;
+ }
+
+ if (access) {
+ /* 4bit transfer mode at booting time. */
+ cmd.cmdidx = MMC_CMD_SWITCH;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_BOOT_BUS_WIDTH << 16) |
+ ((1 << 0) << 8);
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc boot partition#%d open fail:Error2 = %d\n",
+ part_num, err);
+ return err;
+ }
+ }
+ return 0;
+}
+#endif
diff --git a/drivers/mmc/mmc_parts.c b/drivers/mmc/mmc_parts.c
new file mode 100644
index 0000000000..c50d75fd76
--- /dev/null
+++ b/drivers/mmc/mmc_parts.c
@@ -0,0 +1,725 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <linux/string.h>
+#include "common.h"
+#include <malloc.h>
+#include <asm/arch/rda_sys.h>
+#include "mmc.h"
+#include <part.h>
+
+#define EFI_ENTRIES 32
+
+static int part_nums;
+
+/* To support the Android-style naming of flash */
+#define MAX_PTN (CONFIG_MAX_PARTITION_NUM - CONFIG_MIN_PARTITION_NUM + 1)
+static disk_partition_t ptable[MAX_PTN];
+
+/* special size referring to all the remaining space in a partition */
+#define SIZE_REMAINING (u64)(-1)
+
+/* special offset value, it is used when not provided by user
+ *
+ * this value is used temporarily during parsing, later such offests
+ * are recalculated */
+#define OFFSET_NOT_SPECIFIED (u64)(-1)
+
+
+/* a bunch of this is duplicate of part_efi.c and part_efi.h
+ * we should consider putting this code in there and layer
+ * the partition info.
+ */
+#define EFI_VERSION 0x00010000
+#define EFI_NAMELEN 36
+
+static const u8 partition_type[16] = {
+ 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
+ 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
+};
+
+static const u8 random_uuid[16] = {
+ 0xff, 0x1f, 0xf2, 0xf9, 0xd4, 0xa8, 0x0e, 0x5f,
+ 0x97, 0x46, 0x59, 0x48, 0x69, 0xae, 0xc3, 0x4e,
+};
+
+struct efi_entry {
+ u8 type_uuid[16];
+ u8 uniq_uuid[16];
+ u64 first_lba;
+ u64 last_lba;
+ u64 attr;
+ u16 name[EFI_NAMELEN];
+};
+
+struct efi_header {
+ u8 magic[8];
+
+ u32 version;
+ u32 header_sz;
+
+ u32 crc32;
+ u32 reserved;
+
+ u64 header_lba;
+ u64 backup_lba;
+ u64 first_lba;
+ u64 last_lba;
+
+ u8 volume_uuid[16];
+
+ u64 entries_lba;
+
+ u32 entries_count;
+ u32 entries_size;
+ u32 entries_crc32;
+} __attribute__((packed));
+
+struct ptable {
+ u8 mbr[512];
+ union {
+ struct efi_header header;
+ u8 block[512];
+ };
+ struct efi_entry entry[EFI_ENTRIES];
+};
+
+static struct ptable the_ptable;
+
+/* in a board specific file */
+struct fbt_partition {
+ char name[32];
+ char type[32];
+ u64 offset;
+ u64 size;
+ unsigned int attr;
+};
+/* For the 16GB eMMC part used in Tungsten, the erase group size is 512KB.
+ * So every partition should be at least 512KB to make it possible to use
+ * the mmc erase operation when doing 'fastboot erase'.
+ * However, the xloader is an exception because in order for the OMAP4 ROM
+ * bootloader to find it, it must be at offset 0KB, 128KB, 256KB, or 384KB.
+ * Since the partition table is at 0KB, we choose 128KB. Special care
+ * must be kept to prevent erase the partition table when/if the xloader
+ * partition is being erased.
+ */
+struct fbt_partition fbt_partitions[EFI_ENTRIES];
+
+/**
+ * Parses a string into a number. The number stored at ptr is
+ * potentially suffixed with K (for kilobytes, or 1024 bytes),
+ * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
+ * 1073741824). If the number is suffixed with K, M, or G, then
+ * the return value is the number multiplied by one kilobyte, one
+ * megabyte, or one gigabyte, respectively.
+ *
+ * @param ptr where parse begins
+ * @param retptr output pointer to next char after parse completes (output)
+ * @return resulting unsigned int
+ */
+static unsigned long memsize_parse (const char *const ptr, const char **retptr)
+{
+ unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0);
+
+ switch (**retptr) {
+ case 'G':
+ case 'g':
+ ret <<= 10;
+ case 'M':
+ case 'm':
+ ret <<= 10;
+ case 'K':
+ case 'k':
+ ret <<= 10;
+ (*retptr)++;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+#define MIN_PART_SIZE 4096
+/**
+ * Parse one partition definition, allocate memory and return pointer to this
+ * location in retpart.
+ *
+ * @param partdef pointer to the partition definition string i.e. <part-def>
+ * @param ret output pointer to next char after parse completes (output)
+ * @param retpart pointer to the allocated partition (output)
+ * @return 0 on success, 1 otherwise
+ */
+static int part_parse(const char *const partdef, const char **ret, struct fbt_partition *part)
+{
+ u64 size;
+ u64 offset;
+ const char *name;
+ int name_len;
+ unsigned int mask_flags;
+ const char *p;
+
+ p = partdef;
+ *ret = NULL;
+
+ /* fetch the partition size */
+ if (*p == '-') {
+ /* assign all remaining space to this partition */
+ debug("'-': remaining size assigned\n");
+ size = SIZE_REMAINING;
+ p++;
+ } else {
+ size = memsize_parse(p, &p);
+ if (size < MIN_PART_SIZE) {
+ printf("partition size too small (%llx)\n", size);
+ return 1;
+ }
+ }
+
+ /* check for offset */
+ offset = OFFSET_NOT_SPECIFIED;
+ if (*p == '@') {
+ p++;
+ offset = memsize_parse(p, &p);
+ }
+
+ /* now look for the name */
+ if (*p == '(') {
+ name = ++p;
+ if ((p = strchr(name, ')')) == NULL) {
+ printf("no closing ) found in partition name\n");
+ return 1;
+ }
+ name_len = p - name + 1;
+ if ((name_len - 1) == 0) {
+ printf("empty partition name\n");
+ return 1;
+ }
+ p++;
+ } else {
+ /* 0x00000000@0x00000000 */
+ name_len = 22;
+ name = NULL;
+ }
+
+ /* test for options */
+ mask_flags = 0;
+ if (strncmp(p, "ro", 2) == 0) {
+ mask_flags |= 1;
+ p += 2;
+ }
+
+ /* check for next partition definition */
+ if (*p == ',') {
+ if (size == SIZE_REMAINING) {
+ *ret = NULL;
+ printf("no partitions allowed after a fill-up partition\n");
+ return 1;
+ }
+ *ret = ++p;
+ } else if ((*p == ';') || (*p == '\0')) {
+ *ret = p;
+ } else {
+ printf("unexpected character '%c' at the end of partition\n", *p);
+ *ret = NULL;
+ return 1;
+ }
+
+ memset(part, 0, sizeof(struct fbt_partition));
+ part->size = size;
+ part->offset = offset;
+ part->attr = mask_flags;
+
+ if (name) {
+ /* copy user provided name */
+ strncpy(part->name, name, name_len - 1);
+ } else {
+ /* auto generated name in form of size@offset */
+ sprintf(part->name, "0x%llx@0x%llx", size, offset);
+ }
+
+ part->name[name_len - 1] = '\0';
+
+ debug("+ partition: name %-22s size 0x%llx offset 0x%llx mask flags %x\n",
+ part->name, part->size,
+ part->offset, part->attr);
+
+ return 0;
+}
+
+/**
+ * Performs sanity check for supplied partition. Offset and size are verified
+ * to be within valid range. Partition type is checked and either
+ * parts_validate_nor() or parts_validate_nand() is called with the argument
+ * of part.
+ *
+ * @param id of the parent device
+ * @param part partition to validate
+ * @return 0 if partition is valid, 1 otherwise
+ */
+static int part_validate(struct mmc *mmc, struct fbt_partition *part)
+{
+
+ if (part->size == SIZE_REMAINING)
+ part->size = mmc->capacity - part->offset;
+/*
+
+ if (part->offset > id->size) {
+ printf("%s: offset %08x beyond flash size 0x%llx\n",
+ id->mtd_id, part->offset, id->size);
+ return 1;
+ }
+
+ if (((u64)part->offset + (u64)part->size) <= (u64)part->offset) {
+ printf("%s%d: partition (%s) size too big\n",
+ MTD_DEV_TYPE(id->type), id->num, part->name);
+ return 1;
+ }
+
+ if (part->offset + part->size > id->size) {
+ printf("%s: partitioning exceeds flash size %llx, part %x+%x\n",
+ id->mtd_id, id->size, part->offset, part->size);
+ return 1;
+ }
+*/
+ /*
+ * Now we need to check if the partition starts and ends on
+ * sector (eraseblock) regions
+ */
+ return 0;
+}
+
+static const char *const mmcparts_default = MTDPARTS_DEF;
+
+int mmc_part_parse(struct mmc *mmc)
+{
+ const char *p;
+ struct fbt_partition *part;
+ u64 offset;
+ int index = 0;
+ int err = 0;
+
+ p = mmcparts_default;
+ offset = 0;
+ while (p && (*p != '\0') && (*p != ';')) {
+ err = 1;
+ part = &fbt_partitions[index];
+ if ((part_parse(p, &p, part) != 0))
+ break;
+
+ /* calculate offset when not specified */
+ if (part->offset == OFFSET_NOT_SPECIFIED)
+ part->offset = offset;
+ else
+ offset = part->offset;
+
+ /* verify alignment and size */
+ if (part_validate(mmc, part) != 0)
+ break;
+
+ offset += part->size;
+
+ /* partition is ok, add it to the list */
+ index++;
+ if (index > EFI_ENTRIES)
+ break;
+ err = 0;
+ debug("++++++++++++ partition: name %-22s size 0x%llx offset 0x%llx mask flags %x\n",
+ part->name, part->size,
+ part->offset, part->attr);
+ }
+ part_nums = index;
+ return err;
+}
+
+
+static void init_mbr(u8 *mbr, u32 blocks)
+{
+ mbr[0x1be] = 0x00; // nonbootable
+ mbr[0x1bf] = 0xFF; // bogus CHS
+ mbr[0x1c0] = 0xFF;
+ mbr[0x1c1] = 0xFF;
+
+ mbr[0x1c2] = 0xEE; // GPT partition
+ mbr[0x1c3] = 0xFF; // bogus CHS
+ mbr[0x1c4] = 0xFF;
+ mbr[0x1c5] = 0xFF;
+
+ mbr[0x1c6] = 0x01; // start
+ mbr[0x1c7] = 0x00;
+ mbr[0x1c8] = 0x00;
+ mbr[0x1c9] = 0x00;
+
+ memcpy(mbr + 0x1ca, &blocks, sizeof(u32));
+
+ mbr[0x1fe] = 0x55;
+ mbr[0x1ff] = 0xaa;
+}
+
+static void start_ptbl(struct ptable *ptbl, unsigned blocks)
+{
+ struct efi_header *hdr = &ptbl->header;
+
+ memset(ptbl, 0, sizeof(*ptbl));
+
+ init_mbr(ptbl->mbr, blocks - 1);
+
+ memcpy(hdr->magic, "EFI PART", 8);
+ hdr->version = EFI_VERSION;
+ hdr->header_sz = sizeof(struct efi_header);
+ hdr->header_lba = 1;
+ hdr->backup_lba = blocks - 1;
+ hdr->first_lba = 34;
+ hdr->last_lba = blocks - 1;
+ memcpy(hdr->volume_uuid, random_uuid, 16);
+ hdr->entries_lba = 2;
+ hdr->entries_count = EFI_ENTRIES;
+ hdr->entries_size = sizeof(struct efi_entry);
+}
+
+static void end_ptbl(struct ptable *ptbl)
+{
+ struct efi_header *hdr = &ptbl->header;
+ u32 n;
+
+ n = crc32(0, 0, 0);
+ n = crc32(n, (void*) ptbl->entry, sizeof(ptbl->entry));
+ hdr->entries_crc32 = n;
+
+ n = crc32(0, 0, 0);
+ n = crc32(0, (void*) &ptbl->header, sizeof(ptbl->header));
+ hdr->crc32 = n;
+}
+
+static int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name)
+{
+ struct efi_header *hdr = &ptbl->header;
+ struct efi_entry *entry = ptbl->entry;
+ unsigned n;
+
+ if (first < 34) {
+ printf("partition '%s' overlaps partition table\n", name);
+ return -1;
+ }
+
+ if (last > hdr->last_lba) {
+ printf("partition '%s' does not fit\n", name);
+ return -1;
+ }
+ for (n = 0; n < EFI_ENTRIES; n++, entry++) {
+ if (entry->last_lba)
+ continue;
+ memcpy(entry->type_uuid, partition_type, 16);
+ memcpy(entry->uniq_uuid, random_uuid, 16);
+ entry->uniq_uuid[0] = n;
+ entry->first_lba = first;
+ entry->last_lba = last;
+ for (n = 0; (n < EFI_NAMELEN) && *name; n++)
+ entry->name[n] = *name++;
+ return 0;
+ }
+ printf("out of partition table entries\n");
+ return -1;
+}
+
+static unsigned int pcount;
+void fbt_add_ptn(disk_partition_t *ptn)
+{
+ if (pcount < MAX_PTN) {
+ memcpy(ptable + pcount, ptn, sizeof(*ptn));
+ pcount++;
+ }
+}
+
+
+static block_dev_desc_t *mmc_blkdev;
+
+static int __def_fbt_load_ptbl(void)
+{
+ u64 length;
+ disk_partition_t ptn;
+ int n;
+ int res = -1;
+ block_dev_desc_t *blkdev = mmc_blkdev;
+ unsigned long blksz = blkdev->blksz;
+
+ init_part(blkdev);
+ if (blkdev->part_type == PART_TYPE_UNKNOWN) {
+ printf("unknown partition table on %s\n", CONFIG_MMC_DEV_NAME);
+ return -1;
+ }
+
+ printf("lba size = %lu\n", blksz);
+ printf("lba_start partition_size name\n");
+ printf("========= ====================== ==============\n");
+ for (n = CONFIG_MIN_PARTITION_NUM; n <= CONFIG_MAX_PARTITION_NUM; n++) {
+ if (get_partition_info(blkdev, n, &ptn))
+ continue; /* No partition <n> */
+ if (!ptn.size || !ptn.blksz || !ptn.name[0])
+ continue; /* Partition <n> is empty (or sick) */
+ fbt_add_ptn(&ptn);
+
+ length = (u64)blksz * ptn.size;
+ if (length > (1024 * 1024))
+ printf(" %8lu %12llu(%7lluM) %s\n",
+ ptn.start,
+ length, length/(1024*1024),
+ ptn.name);
+ else
+ printf(" %8lu %12llu(%7lluK) %s\n",
+ ptn.start,
+ length, length/1024,
+ ptn.name);
+ res = 0;
+ }
+ printf("========= ====================== ==============\n");
+ return res;
+}
+
+int board_fbt_load_ptbl(void)
+ __attribute__((weak, alias("__def_fbt_load_ptbl")));
+
+disk_partition_t *partition_find_ptn(const char *name);
+
+static int fbt_load_partition_table(void)
+{
+ if (board_fbt_load_ptbl()) {
+ printf("board_fbt_load_ptbl() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+disk_partition_t *partition_find_ptn(const char *name)
+{
+ unsigned int n;
+
+ if (pcount == 0) {
+ if (fbt_load_partition_table()) {
+ printf("Unable to load partition table, aborting\n");
+ return NULL;
+ }
+ }
+
+ for (n = 0; n < pcount; n++)
+ if (!strcmp((char *)ptable[n].name, name))
+ return ptable + n;
+ return NULL;
+}
+
+void fbt_reset_ptn(void)
+{
+ pcount = 0;
+ if (fbt_load_partition_table())
+ printf("Unable to load partition table\n");
+}
+
+static int do_format(void)
+{
+ struct ptable *ptbl = &the_ptable;
+ unsigned next;
+ int n;
+ block_dev_desc_t *dev_desc;
+ unsigned long blocks_to_write, result;
+ struct mmc *mmc;
+
+ dev_desc = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!dev_desc) {
+ printf("error getting device %s\n", CONFIG_MMC_DEV_NAME);
+ return -1;
+ }
+ if (!dev_desc->lba) {
+ printf("device %s has no space\n", CONFIG_MMC_DEV_NAME);
+ return -1;
+ }
+ mmc = container_of(dev_desc, struct mmc, block_dev);
+
+ mmc_part_parse(mmc);
+ start_ptbl(ptbl, dev_desc->lba);
+ for (n = 0; n < part_nums; n++) {
+ u64 sz = fbt_partitions[n].size / 512;
+ next = fbt_partitions[n].offset / 512;
+ if (fbt_partitions[n].name[0] == '-') {
+ next += sz;
+ continue;
+ }
+ if (sz == 0)
+ sz = dev_desc->lba - next;
+
+ if (add_ptn(ptbl, next, next + sz - 1, fbt_partitions[n].name))
+ return -1;
+ }
+ end_ptbl(ptbl);
+
+ blocks_to_write = DIV_ROUND_UP(sizeof(struct ptable), dev_desc->blksz);
+ result = dev_desc->block_write(dev_desc->dev, 0, blocks_to_write, ptbl);
+ if (result != blocks_to_write) {
+ printf("\nFormat failed, block_write() returned %lu instead of %lu\n",
+ result, blocks_to_write);
+ return -1;
+ }
+
+ debug("\nnew partition table of %lu %lu-byte blocks\n",
+ blocks_to_write, dev_desc->blksz);
+ fbt_reset_ptn();
+
+ return 0;
+}
+
+/* check if the partition table is same with the table stored in device */
+int mmc_check_parts(int *same)
+{
+ struct ptable *ptbl = NULL;
+ struct ptable *ptbl_r = NULL;
+ unsigned next;
+ int n;
+ block_dev_desc_t *dev_desc;
+ unsigned long blocks_to_read, result;
+ struct mmc *mmc;
+
+ if(!same) return -1;
+ *same = 0;
+
+ dev_desc = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!dev_desc) {
+ printf("error getting device %s\n", CONFIG_MMC_DEV_NAME);
+ return -1;
+ }
+ if (!dev_desc->lba) {
+ printf("device %s has no space\n", CONFIG_MMC_DEV_NAME);
+ return -1;
+ }
+ mmc = container_of(dev_desc, struct mmc, block_dev);
+
+ ptbl = (struct ptable *)malloc(sizeof(struct ptable));
+ if(!ptbl) {
+ printf("can't malloc memory.\n");
+ return -1;
+ }
+ ptbl_r = (struct ptable *)malloc(sizeof(struct ptable));
+ if(!ptbl_r) {
+ printf("can't malloc memory.\n");
+ free(ptbl);
+ return -1;
+ }
+
+ mmc_part_parse(mmc);
+ start_ptbl(ptbl, dev_desc->lba);
+ for (n = 0; n < part_nums; n++) {
+ u64 sz = fbt_partitions[n].size / 512;
+ next = fbt_partitions[n].offset / 512;
+ if (fbt_partitions[n].name[0] == '-') {
+ next += sz;
+ continue;
+ }
+ if (sz == 0)
+ sz = dev_desc->lba - next;
+
+ if (add_ptn(ptbl, next, next + sz - 1, fbt_partitions[n].name)) {
+ free(ptbl);
+ free(ptbl_r);
+ return -1;
+ }
+ }
+ end_ptbl(ptbl);
+
+ memset((void *)ptbl_r, 0, sizeof(struct ptable));
+ blocks_to_read = DIV_ROUND_UP(sizeof(struct ptable), dev_desc->blksz);
+ result = dev_desc->block_read(dev_desc->dev, 0, blocks_to_read, ptbl_r);
+ if (result != blocks_to_read) {
+ printf("\nCheck Failed, block_read() returned %lu instead of %lu\n",
+ result, blocks_to_read);
+ free(ptbl);
+ free(ptbl_r);
+ return -1;
+ }
+
+#if 0
+ /* dump */
+ printf("New paritition table:\n");
+ for(n = 0; n < sizeof(struct ptable); n++) {
+ char *p = (char *)ptbl;
+ printf("%02x ", p[n]);
+ if((n + 1) % 32 == 0 )
+ printf("\n");
+ }
+ printf("Old partition table:\n");
+ for(n = 0; n < sizeof(struct ptable); n++) {
+ char *p = (char *)ptbl_r;
+ printf("%02x ", p[n]);
+ if((n + 1) % 32 == 0 )
+ printf("\n");
+ }
+#endif
+
+ if(memcmp(ptbl, ptbl_r, sizeof(struct ptable)) == 0) {
+ *same = 1;
+ }
+
+ free(ptbl);
+ free(ptbl_r);
+ return 0;
+}
+
+int mmc_parts_format(void)
+{
+ if (!mmc_blkdev) {
+ printf("mmc block device hasn't initialize\n");
+ return -1;
+ }
+
+ return do_format();
+}
+
+int mmc_parts_init(void)
+{
+ struct mmc* mmc = NULL;
+
+ /* We register only one device. So, the dev id is always 0 */
+ mmc = find_mmc_device(CONFIG_MMC_DEV_NUM);
+ if (!mmc) {
+ printf("make_write_mbr_ebr: mmc device not found!!\n");
+ return -1;
+ }
+ mmc_init(mmc);
+
+ if (part_nums > EFI_ENTRIES) {
+ printf("too many mmc parts\n");
+ return -1;
+ }
+
+
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!mmc_blkdev) {
+ printf("%s: fastboot device %s not found\n",
+ __func__, CONFIG_MMC_DEV_NAME);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h
new file mode 100644
index 0000000000..16dcf9ff61
--- /dev/null
+++ b/drivers/mmc/mmc_private.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008,2010 Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based (loosely) on the Linux code
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _MMC_PRIVATE_H_
+#define _MMC_PRIVATE_H_
+
+#include <mmc.h>
+
+extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data);
+extern int mmc_send_status(struct mmc *mmc, int timeout);
+extern int mmc_set_blocklen(struct mmc *mmc, int len);
+
+#ifndef CONFIG_SPL_BUILD
+
+extern unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt);
+
+extern ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt,
+ const void *src);
+
+#else /* CONFIG_SPL_BUILD */
+
+/* SPL will never write or erase, declare dummies to reduce code size. */
+
+static inline unsigned long mmc_berase(int dev_num, lbaint_t start,
+ lbaint_t blkcnt)
+{
+ return 0;
+}
+
+static inline ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt,
+ const void *src)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SPL_BUILD */
+
+#endif /* _MMC_PRIVATE_H_ */
diff --git a/drivers/mmc/mmc_spl_load.c b/drivers/mmc/mmc_spl_load.c
new file mode 100644
index 0000000000..a9f36a748c
--- /dev/null
+++ b/drivers/mmc/mmc_spl_load.c
@@ -0,0 +1,111 @@
+#include <common.h>
+#include <mmc.h>
+#include <asm/errno.h>
+#include <asm/arch/spl_board_info.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_SPL_CHECK_IMAGE
+int check_uimage(unsigned int *buf);
+#endif
+
+static struct mmc *boot_mmc = NULL;
+
+int emmc_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+ unsigned long err;
+ unsigned int start_block, block_num, block_size;
+
+ block_size = 512;
+ start_block = offs/block_size;
+ block_num = (size + block_size - 1)/block_size;
+
+ if (!boot_mmc) {
+ printf("spl emmc not initialized\n");
+ return -ENODEV;
+ }
+
+ err = boot_mmc->block_dev.block_read(CONFIG_MMC_DEV_NUM,
+ start_block,
+ block_num, (void *)dst);
+
+ if (err <= 0) {
+ printf("spl emmc read err %d\n", (unsigned int)err);
+ return err;
+ }
+ return 0;
+}
+
+extern int mmc_set_host_mclk_adj_inv(struct mmc *mmc, u8 adj, u8 inv);
+
+void emmc_init(void)
+{
+ int err;
+ int i = 0;
+ struct spl_emmc_info *info = get_bd_spl_emmc_info();
+
+ info->manufacturer_id = 0;
+
+ mmc_initialize(gd->bd);
+
+ boot_mmc = find_mmc_device(CONFIG_MMC_DEV_NUM);
+ if (!boot_mmc) {
+ puts("spl emmc device not found\n");
+ return;
+ }
+
+ for(; i < 32; i++){
+ int adj = i % 16;
+ int inv = (i >= 16)? 1: 0;
+
+ if(mmc_set_host_mclk_adj_inv(boot_mmc, adj, inv) < 0){
+ printf("mmc_set_host_mclk_adj_inv failed \n");
+ boot_mmc = NULL;
+ return;
+ }
+
+ err = mmc_init(boot_mmc);
+ if (err) {
+ printf("spl emmc init failed, err %d\n", err);
+ } else
+ break;
+ }
+
+ if (i < 32){
+ info->manufacturer_id = boot_mmc->cid[0] >> 24;
+ printf("boot_mmc mfr id = %x \n", boot_mmc->cid[0] >> 24);
+ } else{
+ printf("After loop, spl emmc init failed.\n");
+ boot_mmc = NULL;
+ return;
+ }
+}
+
+/*
+ * The main entry for EMMC booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from EMMC into SDRAM and starts it from there.
+ */
+void emmc_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+
+ /*
+ * Load U-Boot image from EMMC into RAM
+ */
+ emmc_spl_load_image(CONFIG_SYS_EMMC_U_BOOT_OFFS,
+ CONFIG_SYS_EMMC_U_BOOT_SIZE,
+ (void *)CONFIG_SYS_EMMC_U_BOOT_DST);
+
+#ifdef CONFIG_SPL_CHECK_IMAGE
+ if (check_uimage((unsigned int*)CONFIG_SYS_EMMC_U_BOOT_DST)) {
+ printf("EMMC boot failed.\n");
+ return;
+ }
+#endif
+
+ /*
+ * Jump to U-Boot image
+ */
+ uboot = (void *)CONFIG_SYS_EMMC_U_BOOT_START;
+ (*uboot)();
+}
diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c
new file mode 100644
index 0000000000..2bc21d32db
--- /dev/null
+++ b/drivers/mmc/mmc_write.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based vaguely on the Linux code
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <part.h>
+#include "mmc_private.h"
+
+static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
+{
+ struct mmc_cmd cmd;
+ ulong end;
+ int err, start_cmd, end_cmd;
+
+ if (mmc->high_capacity) {
+ end = start + blkcnt - 1;
+ } else {
+ end = (start + blkcnt - 1) * mmc->write_bl_len;
+ start *= mmc->write_bl_len;
+ }
+
+ if (IS_SD(mmc)) {
+ start_cmd = SD_CMD_ERASE_WR_BLK_START;
+ end_cmd = SD_CMD_ERASE_WR_BLK_END;
+ } else {
+ start_cmd = MMC_CMD_ERASE_GROUP_START;
+ end_cmd = MMC_CMD_ERASE_GROUP_END;
+ }
+
+ cmd.cmdidx = start_cmd;
+ cmd.cmdarg = start;
+ cmd.resp_type = MMC_RSP_R1;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto err_out;
+
+ cmd.cmdidx = end_cmd;
+ cmd.cmdarg = end;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto err_out;
+
+ cmd.cmdidx = MMC_CMD_ERASE;
+ cmd.cmdarg = SECURE_ERASE;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ puts("mmc erase failed\n");
+ return err;
+}
+
+unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt)
+{
+ int err = 0;
+ struct mmc *mmc = find_mmc_device(dev_num);
+ lbaint_t blk = 0, blk_r = 0;
+ int timeout = 1000;
+
+ if (!mmc)
+ return -1;
+
+ if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
+ printf("\n\nCaution! Your devices Erase group is 0x%x\n"
+ "The erase range would be change to "
+ "0x" LBAF "~0x" LBAF "\n\n",
+ mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
+ ((start + blkcnt + mmc->erase_grp_size)
+ & ~(mmc->erase_grp_size - 1)) - 1);
+
+ /* try to erase all at one time */
+ err = mmc_erase_t(mmc, start, blkcnt);
+ if(!err) {
+ if(!mmc_send_status(mmc, timeout*10))
+ return blkcnt;
+ }
+
+ /* erase group one by one */
+ while (blk < blkcnt) {
+ blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
+ mmc->erase_grp_size : (blkcnt - blk);
+ err = mmc_erase_t(mmc, start + blk, blk_r);
+ if (err)
+ break;
+
+ blk += blk_r;
+
+ /* Waiting for the ready status */
+ if (mmc_send_status(mmc, timeout))
+ return 0;
+ }
+
+ return blk;
+}
+
+static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
+ lbaint_t blkcnt, const void *src)
+{
+ struct mmc_cmd cmd;
+ struct mmc_data data;
+ int timeout = 1000;
+
+ if ((start + blkcnt) > mmc->block_dev.lba) {
+ printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
+ start + blkcnt, mmc->block_dev.lba);
+ return 0;
+ }
+
+ if (blkcnt == 0)
+ return 0;
+ else if (blkcnt == 1)
+ cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
+ else
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+
+ if (mmc->high_capacity)
+ cmd.cmdarg = start;
+ else
+ cmd.cmdarg = start * mmc->write_bl_len;
+
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.src = src;
+ data.blocks = blkcnt;
+ data.blocksize = mmc->write_bl_len;
+ data.flags = MMC_DATA_WRITE;
+
+ if (mmc_send_cmd(mmc, &cmd, &data)) {
+ printf("mmc write failed\n");
+ return 0;
+ }
+
+ /* SPI multiblock writes terminate using a special
+ * token, not a STOP_TRANSMISSION request.
+ */
+ if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+ if (mmc_send_cmd(mmc, &cmd, NULL)) {
+ printf("mmc fail to send stop cmd\n");
+ return 0;
+ }
+ }
+
+ /* Waiting for the ready status */
+ if (mmc_send_status(mmc, timeout))
+ return 0;
+
+ return blkcnt;
+}
+
+ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src)
+{
+ lbaint_t cur, blocks_todo = blkcnt;
+
+ struct mmc *mmc = find_mmc_device(dev_num);
+ if (!mmc)
+ return 0;
+
+ if (mmc_set_blocklen(mmc, mmc->write_bl_len))
+ return 0;
+
+ do {
+ cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo;
+ if (mmc_write_blocks(mmc, start, cur, src) != cur)
+ return 0;
+ blocks_todo -= cur;
+ start += cur;
+ src += cur * mmc->write_bl_len;
+ } while (blocks_todo > 0);
+
+ return blkcnt;
+}
diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c
index 35c6bdabb0..4187a94120 100644
--- a/drivers/mmc/mxsmmc.c
+++ b/drivers/mmc/mxsmmc.c
@@ -133,7 +133,8 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
/* READ or WRITE */
if (data->flags & MMC_DATA_READ) {
ctrl0 |= SSP_CTRL0_READ;
- } else if (priv->mmc_is_wp(mmc->block_dev.dev)) {
+ } else if (priv->mmc_is_wp &&
+ priv->mmc_is_wp(mmc->block_dev.dev)) {
printf("MMC%d: Can not write a locked card!\n",
mmc->block_dev.dev);
return UNUSABLE_ERR;
@@ -406,7 +407,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
*/
mmc->f_min = 400000;
mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2;
- mmc->b_max = 0x40;
+ mmc->b_max = 0x20;
mmc_register(mmc);
return 0;
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index 2400db2f35..afd9b30b51 100644
--- a/drivers/mmc/omap_hsmmc.c
+++ b/drivers/mmc/omap_hsmmc.c
@@ -29,10 +29,15 @@
#include <i2c.h>
#include <twl4030.h>
#include <twl6030.h>
+#include <twl6035.h>
#include <asm/io.h>
#include <asm/arch/mmc_host_def.h>
#include <asm/arch/sys_proto.h>
+/* common definitions for all OMAPs */
+#define SYSCTL_SRC (1 << 25)
+#define SYSCTL_SRD (1 << 26)
+
/* If we fail after 1 second wait, something is really bad */
#define MAX_RETRY_MS 1000
@@ -45,8 +50,8 @@ static struct mmc hsmmc_dev[2];
static void omap4_vmmc_pbias_config(struct mmc *mmc)
{
u32 value = 0;
- struct omap4_sys_ctrl_regs *const ctrl =
- (struct omap4_sys_ctrl_regs *)SYSCTRL_GENERAL_CORE_BASE;
+ struct omap_sys_ctrl_regs *const ctrl =
+ (struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE;
value = readl(&ctrl->control_pbiaslite);
@@ -60,17 +65,51 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc)
}
#endif
-unsigned char mmc_board_init(struct mmc *mmc)
+#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER)
+static void omap5_pbias_config(struct mmc *mmc)
{
-#if defined(CONFIG_TWL4030_POWER)
- twl4030_power_mmc_init();
+ u32 value = 0;
+ struct omap_sys_ctrl_regs *const ctrl =
+ (struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE;
+
+ value = readl(&ctrl->control_pbias);
+ value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ);
+ value |= SDCARD_BIAS_HIZ_MODE;
+ writel(value, &ctrl->control_pbias);
+
+ twl6035_mmc1_poweron_ldo();
+
+ value = readl(&ctrl->control_pbias);
+ value &= ~SDCARD_BIAS_HIZ_MODE;
+ value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ;
+ writel(value, &ctrl->control_pbias);
+
+ value = readl(&ctrl->control_pbias);
+ if (value & (1 << 23)) {
+ value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ);
+ value |= SDCARD_BIAS_HIZ_MODE;
+ writel(value, &ctrl->control_pbias);
+ }
+}
#endif
+unsigned char mmc_board_init(struct mmc *mmc)
+{
#if defined(CONFIG_OMAP34XX)
t2_t *t2_base = (t2_t *)T2_BASE;
struct prcm *prcm_base = (struct prcm *)PRCM_BASE;
+ u32 pbias_lite;
- writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 |
+ pbias_lite = readl(&t2_base->pbias_lite);
+ pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0);
+ writel(pbias_lite, &t2_base->pbias_lite);
+#endif
+#if defined(CONFIG_TWL4030_POWER)
+ twl4030_power_mmc_init();
+ mdelay(100); /* ramp-up delay from Linux code */
+#endif
+#if defined(CONFIG_OMAP34XX)
+ writel(pbias_lite | PBIASLITEPWRDNZ1 |
PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0,
&t2_base->pbias_lite);
@@ -80,6 +119,11 @@ unsigned char mmc_board_init(struct mmc *mmc)
writel(readl(&t2_base->devconf1) | MMCSDIO2ADPCLKISEL,
&t2_base->devconf1);
+ /* Change from default of 52MHz to 26MHz if necessary */
+ if (!(mmc->host_caps & MMC_MODE_HS_52MHz))
+ writel(readl(&t2_base->ctl_prog_io1) & ~CTLPROGIO1SPEEDCTRL,
+ &t2_base->ctl_prog_io1);
+
writel(readl(&prcm_base->fclken1_core) |
EN_MMC1 | EN_MMC2 | EN_MMC3,
&prcm_base->fclken1_core);
@@ -94,6 +138,10 @@ unsigned char mmc_board_init(struct mmc *mmc)
if (mmc->block_dev.dev == 0)
omap4_vmmc_pbias_config(mmc);
#endif
+#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER)
+ if (mmc->block_dev.dev == 0)
+ omap5_pbias_config(mmc);
+#endif
return 0;
}
@@ -189,6 +237,27 @@ static int mmc_init_setup(struct mmc *mmc)
return 0;
}
+/*
+ * MMC controller internal finite state machine reset
+ *
+ * Used to reset command or data internal state machines, using respectively
+ * SRC or SRD bit of SYSCTL register
+ */
+static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
+{
+ ulong start;
+
+ mmc_reg_out(&mmc_base->sysctl, bit, bit);
+
+ start = get_timer(0);
+ while ((readl(&mmc_base->sysctl) & bit) != 0) {
+ if (get_timer(0) - start > MAX_RETRY_MS) {
+ printf("%s: timedout waiting for sysctl %x to clear\n",
+ __func__, bit);
+ return;
+ }
+ }
+}
static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
@@ -209,7 +278,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
start = get_timer(0);
while (readl(&mmc_base->stat)) {
if (get_timer(0) - start > MAX_RETRY_MS) {
- printf("%s: timedout waiting for stat!\n", __func__);
+ printf("%s: timedout waiting for STAT (%x) to clear\n",
+ __func__, readl(&mmc_base->stat));
return TIMEOUT;
}
}
@@ -277,9 +347,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
}
} while (!mmc_stat);
- if ((mmc_stat & IE_CTO) != 0)
+ if ((mmc_stat & IE_CTO) != 0) {
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
return TIMEOUT;
- else if ((mmc_stat & ERRI_MASK) != 0)
+ } else if ((mmc_stat & ERRI_MASK) != 0)
return -1;
if (mmc_stat & CC_MASK) {
@@ -330,6 +401,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size)
}
} while (mmc_stat == 0);
+ if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0)
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
+
if ((mmc_stat & ERRI_MASK) != 0)
return 1;
@@ -382,6 +456,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
}
} while (mmc_stat == 0);
+ if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0)
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
+
if ((mmc_stat & ERRI_MASK) != 0)
return 1;
@@ -463,7 +540,7 @@ static void mmc_set_ios(struct mmc *mmc)
writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
}
-int omap_mmc_init(int dev_index)
+int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max)
{
struct mmc *mmc;
@@ -494,11 +571,22 @@ int omap_mmc_init(int dev_index)
return 1;
}
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS |
- MMC_MODE_HC;
+ mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS |
+ MMC_MODE_HC) & ~host_caps_mask;
mmc->f_min = 400000;
- mmc->f_max = 52000000;
+
+ if (f_max != 0)
+ mmc->f_max = f_max;
+ else {
+ if (mmc->host_caps & MMC_MODE_HS) {
+ if (mmc->host_caps & MMC_MODE_HS_52MHz)
+ mmc->f_max = 52000000;
+ else
+ mmc->f_max = 26000000;
+ } else
+ mmc->f_max = 20000000;
+ }
mmc->b_max = 0;
diff --git a/drivers/mmc/rda_mmc.c b/drivers/mmc/rda_mmc.c
new file mode 100644
index 0000000000..1a527e810e
--- /dev/null
+++ b/drivers/mmc/rda_mmc.c
@@ -0,0 +1,892 @@
+#include <asm/io.h>
+#include "common.h"
+#include <errno.h>
+#include <mmc.h>
+#include <malloc.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_mmc.h>
+#include <asm/arch/reg_ifc.h>
+#include <asm/arch/ifc.h>
+#include <asm/arch/rda_sys.h>
+#include <mmc/mmcpart.h>
+#include <rda/tgt_ap_board_config.h>
+#include <asm/arch/spl_board_info.h>
+
+//#define SDMMC_DEBUG
+
+#ifdef CONFIG_RDA_FPGA
+#define CONFIG_APB2_CLOCK 26000000
+#else
+#define CONFIG_APB2_CLOCK 200000000
+#endif
+
+#define SECOND * CONFIG_SYS_HZ_CLOCK
+#define MCD_CMD_TIMEOUT ( 2 SECOND / 10 )
+#define MCD_RESP_TIMEOUT ( 2 SECOND / 10 )
+#define MCD_DATA_TIMEOUT ( 2 SECOND )
+#define MMC_DEV_NUM 2
+struct mmc_host {
+ int dev_num;
+ u32 phyaddr;
+ u8 mclk_adj;
+ u8 mclk_inv;
+ u16 reserved;
+};
+
+struct emmc_mfr_mclk_adj_inv {
+ u8 mfr_id;
+ u8 mclk_adj;
+ u8 mclk_inv;
+ u8 mclk_inv_kernel;/*Because the u-boot and kernel mode isn't same, so inv set is different sometime*/
+};
+
+static const struct emmc_mfr_mclk_adj_inv emmc_mclk_adj_inv[] = {
+ {MMC_MFR_TOSHIBA, 0, 0, 1},
+ {MMC_MFR_GIGADEVICE, 0, 0, 1},
+ {MMC_MFR_SAMSUNG, 0, 0, 1},
+ {MMC_MFR_SANDISK, 0, 0, 1},
+ {MMC_MFR_HYNIX, 0, 0, 1},
+ {MMC_MFR_MICRON, 0, 0, 1},
+ {MMC_MFR_MICRON1, 0, 0, 1},
+ {0, 0, 0, 0},
+};
+
+struct mmc mmc_device_glob[MMC_DEV_NUM];
+struct mmc_host mmc_host_dev[MMC_DEV_NUM];
+static int rda_ddr_mode = 0;
+
+typedef struct
+{
+ /// This address in the system memory
+ u8* sysMemAddr;
+ /// Quantity of data to transfer, in blocks
+ u32 blockNum;
+ /// Block size
+ u32 blockSize;
+ HAL_SDMMC_DIRECTION_T direction;
+ HAL_IFC_REQUEST_ID_T ifcReq;
+ u32 channel;
+} HAL_SDMMC_TRANSFER_T;
+
+u64 hal_getticks(void)
+{
+ return get_ticks();
+}
+
+static void hal_send_cmd(struct mmc *dev, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+ u32 configReg = 0;
+ struct mmc_host *host = dev->priv;
+ u32 ddr_bits = 0;
+ HWP_SDMMC_T * hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+#if defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8810H)
+ /* check ddr mode is enabled */
+ if(rda_ddr_mode) {
+ ddr_bits = (1<<17) | (1<<23);
+ }
+#endif
+
+ //hwp_sdmmc->SDMMC_CONFIG = SDMMC_AUTO_FLAG_EN;
+ hwp_sdmmc->SDMMC_CONFIG = 0x00000000 | ddr_bits;
+
+ configReg = SDMMC_SDMMC_SENDCMD;
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ configReg |= SDMMC_RSP_EN;
+ if (cmd->resp_type & MMC_RSP_136)
+ configReg |= SDMMC_RSP_SEL_R2;
+ else if (cmd->resp_type & MMC_RSP_CRC)
+ configReg |= SDMMC_RSP_SEL_OTHER;
+ else
+ configReg |= SDMMC_RSP_SEL_R3;
+ }
+
+ /* cases for data transfer */
+ if (cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_READ);
+ } else if (cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_READ | SDMMC_S_M_SEL_MULTIPLE);
+ } else if (cmd->cmdidx == MMC_CMD_WRITE_SINGLE_BLOCK) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_WRITE);
+#if defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8810H)
+ /* when do write, the edge selection is reverse.
+ * it's a workaround. */
+ if(rda_ddr_mode) {
+ ddr_bits = (1<<17);
+ }
+#endif
+ } else if (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_WRITE | SDMMC_S_M_SEL_MULTIPLE);
+#if defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8810H)
+ /* when do write, the edge selection is reverse.
+ * it's a workaround. */
+ if(rda_ddr_mode) {
+ ddr_bits = (1<<17);
+ }
+#endif
+ } else if (cmd->cmdidx == SD_CMD_APP_SEND_SCR) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_READ);
+ } else if (cmd->cmdidx == SD_CMD_SWITCH_FUNC && data) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_READ);
+ } else if (cmd->cmdidx == MMC_CMD_SEND_EXT_CSD) {
+ configReg |= (SDMMC_RD_WT_EN | SDMMC_RD_WT_SEL_READ);
+ }
+
+ configReg |= ddr_bits;
+#ifdef SDMMC_DEBUG
+ printf(" idx_reg = 0x%08x, arg_reg = 0x%08x, config = 0x%08x\n",
+ SDMMC_COMMAND(cmd->cmdidx),
+ SDMMC_ARGUMENT(cmd->cmdarg),
+ configReg);
+#endif
+
+ hwp_sdmmc->SDMMC_CMD_INDEX = SDMMC_COMMAND(cmd->cmdidx);
+ hwp_sdmmc->SDMMC_CMD_ARG = SDMMC_ARGUMENT(cmd->cmdarg);
+ //hwp_sdmmc->SDMMC_CONFIG = configReg |SDMMC_AUTO_FLAG_EN;
+ hwp_sdmmc->SDMMC_CONFIG = configReg;
+}
+
+static int hal_cmd_done(struct mmc *dev)
+{
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+ return (!(hwp_sdmmc->SDMMC_STATUS & SDMMC_NOT_SDMMC_OVER));
+}
+
+static int hal_wait_cmd_done(struct mmc *dev)
+{
+ u64 startTime = hal_getticks();
+ u64 time_out;
+
+ time_out = MCD_CMD_TIMEOUT;
+
+ while(hal_getticks() - startTime < time_out && !hal_cmd_done(dev));
+
+ if (!hal_cmd_done(dev))
+ {
+ printf("cmd waiting timeout\n");
+ return TIMEOUT;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+HAL_SDMMC_OP_STATUS_T hal_get_op_status(struct mmc *dev)
+{
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+ return ((HAL_SDMMC_OP_STATUS_T)(u32)hwp_sdmmc->SDMMC_STATUS);
+}
+
+static int hal_wait_cmd_resp(struct mmc *dev)
+{
+ HAL_SDMMC_OP_STATUS_T status = hal_get_op_status(dev);
+ u64 startTime = hal_getticks();
+ u64 rsp_time_out;
+
+ rsp_time_out = MCD_RESP_TIMEOUT;
+
+ while(hal_getticks() - startTime < rsp_time_out &&status.fields.noResponseReceived){
+ status = hal_get_op_status(dev);
+ }
+
+ if (status.fields.noResponseReceived){
+ printf(" response timeout\n");
+ return TIMEOUT;
+ }
+
+ if(status.fields.responseCrcError){
+ printf(" response CRC error\n");
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static void hal_get_resp(struct mmc *dev, struct mmc_cmd *cmd)
+{
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ cmd->response[0] = hwp_sdmmc->SDMMC_RESP_ARG3;
+ cmd->response[1] = hwp_sdmmc->SDMMC_RESP_ARG2;
+ cmd->response[2] = hwp_sdmmc->SDMMC_RESP_ARG1;
+ cmd->response[3] = hwp_sdmmc->SDMMC_RESP_ARG0 << 1;
+ }else {
+ cmd->response[0] = hwp_sdmmc->SDMMC_RESP_ARG3;
+ cmd->response[1] = 0;
+ cmd->response[2] = 0;
+ cmd->response[3] = 0;
+ }
+ }
+ else {
+ cmd->response[0] = 0;
+ cmd->response[1] = 0;
+ cmd->response[2] = 0;
+ cmd->response[3] = 0;
+ }
+}
+
+static int hal_data_transfer_start(struct mmc *dev,HAL_SDMMC_TRANSFER_T* transfer)
+{
+ u32 length = 0;
+ u32 lengthExp = 0;
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+ length = transfer->blockSize;
+
+ // The block size register
+ while (length != 1)
+ {
+ length >>= 1;
+ lengthExp++;
+ }
+
+ // Configure amount of data
+ hwp_sdmmc->SDMMC_BLOCK_CNT = SDMMC_SDMMC_BLOCK_CNT(transfer->blockNum);
+ hwp_sdmmc->SDMMC_BLOCK_SIZE = SDMMC_SDMMC_BLOCK_SIZE(lengthExp);
+
+ // Configure Bytes reordering
+ hwp_sdmmc->apbi_ctrl_sdmmc = SDMMC_SOFT_RST_L | SDMMC_L_ENDIAN(1);
+ //hwp_sdmmc->apbi_ctrl_sdmmc = SDMMC_L_ENDIAN(1);
+
+ switch (transfer->direction){
+ case HAL_SDMMC_DIRECTION_READ:
+ if (0 == host->dev_num)
+ transfer->ifcReq = HAL_IFC_SDMMC_RX;
+ else
+ transfer->ifcReq = HAL_IFC_SDMMC3_RX;
+ break;
+
+ case HAL_SDMMC_DIRECTION_WRITE:
+ if (0 == host->dev_num)
+ transfer->ifcReq = HAL_IFC_SDMMC_TX;
+ else
+ transfer->ifcReq = HAL_IFC_SDMMC3_TX;
+ break;
+
+ default:
+ printf("hal_SdmmcTransfer with wrong direction %d\n", transfer->direction);
+ return -EILSEQ;
+ }
+
+ transfer->channel = hal_IfcTransferStart(transfer->ifcReq, transfer->sysMemAddr,
+ transfer->blockNum*transfer->blockSize,
+ HAL_IFC_SIZE_32_MODE_MANUAL);
+ if (transfer->channel == HAL_UNKNOWN_CHANNEL){
+ printf("hal_IfcTransferStart error\n");
+ return -EILSEQ;
+ }
+ return 0;
+}
+
+static void hal_data_transfer_stop(struct mmc *dev, HAL_SDMMC_TRANSFER_T* transfer)
+{
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+ // Configure amount of data
+ hwp_sdmmc->SDMMC_BLOCK_CNT = SDMMC_SDMMC_BLOCK_CNT(0);
+ hwp_sdmmc->SDMMC_BLOCK_SIZE = SDMMC_SDMMC_BLOCK_SIZE(0);
+
+ // Put the FIFO in reset state.
+ //hwp_sdmmc->apbi_ctrl_sdmmc = 0 | SDMMC_L_ENDIAN(1);
+ hwp_sdmmc->apbi_ctrl_sdmmc = SDMMC_SOFT_RST_L | SDMMC_L_ENDIAN(1);
+
+ hal_IfcChannelFlush(transfer->ifcReq, transfer->channel);
+ while(!hal_IfcChannelIsFifoEmpty(transfer->ifcReq, transfer->channel));
+ hal_IfcChannelRelease(transfer->ifcReq, transfer->channel);
+ transfer->channel = HAL_UNKNOWN_CHANNEL;
+}
+
+static int hal_data_transfer_done(struct mmc *dev, HAL_SDMMC_TRANSFER_T* transfer)
+{
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+
+ // The link is not full duplex. We check both the
+ // direction, but only one can be in progress at a time.
+
+ if (transfer->channel != HAL_UNKNOWN_CHANNEL)
+ {
+ // Operation in progress is a read.
+ // The SDMMC module itself can know it has finished
+ if ((hwp_sdmmc->SDMMC_INT_STATUS & SDMMC_DAT_OVER_INT)
+ && (hwp_sysIfc->std_ch[transfer->channel].tc == 0))
+ {
+ // Transfer is over
+ hwp_sdmmc->SDMMC_INT_CLEAR = SDMMC_DAT_OVER_CL;
+ hal_IfcChannelRelease(transfer->ifcReq, transfer->channel);
+
+ // We finished a read
+ transfer->channel = HAL_UNKNOWN_CHANNEL;
+
+ // Put the FIFO in reset state.
+ //hwp_sdmmc->apbi_ctrl_sdmmc = 0 | SDMMC_L_ENDIAN(1);
+ hwp_sdmmc->apbi_ctrl_sdmmc = SDMMC_SOFT_RST_L | SDMMC_L_ENDIAN(1);
+
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ printf("unknown channel\n");
+ return 1;
+ }
+}
+
+static int hal_wait_data_transfer_done(struct mmc *dev,
+ HAL_SDMMC_TRANSFER_T* transfer)
+{
+ u64 startTime = hal_getticks();
+ u64 tran_time_out = MCD_DATA_TIMEOUT * transfer->blockNum;
+#ifdef SDMMC_DEBUG
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+#endif
+
+ // Wait
+ while(!hal_data_transfer_done(dev, transfer)) {
+ if (hal_getticks() - startTime > (tran_time_out)) {
+ printf("data transfer timeout\n");
+#ifdef SDMMC_DEBUG
+ printf("SDMMC_STATUS=%#x, INT_STATUS=%#x\n", hwp_sdmmc->SDMMC_STATUS,
+ hwp_sdmmc->SDMMC_INT_STATUS);
+#endif
+ hal_data_transfer_stop(dev, transfer);
+ return TIMEOUT;
+ }
+ }
+ return 0;
+}
+
+static int hal_data_read_check_crc(struct mmc *dev)
+{
+ HAL_SDMMC_OP_STATUS_T operationStatus;
+
+ operationStatus = hal_get_op_status(dev);
+ if (operationStatus.fields.dataError != 0) {
+ // 0 means no CRC error during transmission
+#ifdef SDMMC_DEBUG
+ printf("data_read_check_crc fail, status:%08x\n",
+ operationStatus.reg);
+#endif
+ return -EILSEQ;
+ } else {
+ return 0;
+ }
+}
+
+static int hal_data_write_check_crc(struct mmc *dev)
+{
+ HAL_SDMMC_OP_STATUS_T operationStatus;
+
+ operationStatus = hal_get_op_status(dev);
+ if (operationStatus.fields.crcStatus != 2) {
+ // 0b010 = transmissionRight TODO a macro ?
+#ifdef SDMMC_DEBUG
+ printf("data_write_check_crc fail, status:%08x\n",
+ operationStatus.reg);
+#endif
+ return -EILSEQ;
+ } else {
+ return 0;
+ }
+}
+
+/* send command to the mmc card and wait for results */
+static int do_command(struct mmc *dev,
+ struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ int result;
+ //struct mmc_host *host = dev->priv;
+
+#ifdef SDMMC_DEBUG
+ printf("do_command, cmdidx = %d, cmdarg = 0x%08x, resp_type = %x\n",
+ cmd->cmdidx, cmd->cmdarg, cmd->resp_type);
+#endif
+
+ hal_send_cmd(dev, cmd, data);
+ result = hal_wait_cmd_done(dev);
+ if (result)
+ return result;
+ if (cmd->resp_type & MMC_RSP_PRESENT)
+ {
+ result= hal_wait_cmd_resp(dev);
+ if (result)
+ return result;
+ }
+ hal_get_resp(dev, cmd);
+
+#ifdef SDMMC_DEBUG
+ printf(" response: %08x %08x %08x %08x\n",
+ cmd->response[0], cmd->response[1],
+ cmd->response[2], cmd->response[3]);
+#endif
+
+ /* After CMD2 set RCA to a none zero value. */
+ //if (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)
+ // dev->rca = 10;
+
+ /* After CMD3 open drain is switched off and push pull is used. */
+ //if (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR) {
+ // we don't actually need this
+ //}
+
+ return result;
+}
+
+static int do_data_transfer(struct mmc *dev,
+ struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ int result = 0;
+ //struct mmc_host *host = dev->priv;
+ HAL_SDMMC_TRANSFER_T data_transfer;
+
+#ifdef SDMMC_DEBUG
+ printf("do_data_transfer, cmdidx = %d, flag = %d, blks = %d, addr = 0x%08x\n",
+ cmd->cmdidx, data->flags, data->blocks,
+ (data->flags & MMC_DATA_READ)?(u32)data->dest:(u32)data->src);
+#endif
+
+ if (data->flags & MMC_DATA_READ) {
+ flush_dcache_range((ulong)data->dest, (ulong)data->dest +
+ data->blocks * data->blocksize);
+
+ data_transfer.sysMemAddr = (u8*)data->dest;
+ data_transfer.blockNum = data->blocks;
+ data_transfer.blockSize = data->blocksize;
+ data_transfer.direction = HAL_SDMMC_DIRECTION_READ;
+
+ // Initiate data migration through Ifc.
+ result = hal_data_transfer_start(dev, &data_transfer);
+ if (result)
+ return result;
+
+ result = do_command(dev, cmd, data);
+ if (result) {
+ hal_data_transfer_stop(dev, &data_transfer);
+ return result;
+ }
+
+ result = hal_wait_data_transfer_done(dev, &data_transfer);
+ if (result)
+ {
+ hal_data_transfer_stop(dev, &data_transfer);
+ return result;
+ }
+
+ result = hal_data_read_check_crc(dev);
+ if (result)
+ {
+ printf("data read crc check fail\n");
+ return result;
+ }
+#ifdef SDMMC_DEBUG
+ {
+ int i;
+ for (i=0;i<data->blocksize;i++)
+ {
+ printf("%02x ", data->dest[i]);
+ if ((i+1)%16 == 0)
+ printf("\n");
+ }
+ }
+#endif
+ invalidate_dcache_range((ulong)data->dest,
+ (ulong)data->dest + data->blocks * data->blocksize);
+ } else if (data->flags & MMC_DATA_WRITE) {
+ flush_dcache_range((ulong)data->src, (ulong)data->src +
+ data->blocks * data->blocksize);
+
+ data_transfer.sysMemAddr = (u8*)data->src;
+ data_transfer.blockNum = data->blocks;
+ data_transfer.blockSize = data->blocksize;
+ data_transfer.direction = HAL_SDMMC_DIRECTION_WRITE;
+
+ // Initiate data migration through Ifc.
+ result = hal_data_transfer_start(dev, &data_transfer);
+ if (result)
+ return result;
+
+ result = do_command(dev, cmd, data);
+ if (result) {
+ hal_data_transfer_stop(dev, &data_transfer);
+ return result;
+ }
+
+ result = hal_wait_data_transfer_done(dev, &data_transfer);
+ if (result)
+ {
+ hal_data_transfer_stop(dev, &data_transfer);
+ return result;
+ }
+
+ result = hal_data_write_check_crc(dev);
+ if (result)
+ {
+ printf("data write crc check fail\n");
+ return result;
+ }
+ }
+
+ return result;
+}
+
+static int host_request(struct mmc *dev,
+ struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ int result;
+
+ if (data)
+ result = do_data_transfer(dev, cmd, data);
+ else
+ result = do_command(dev, cmd, data);
+
+ return result;
+}
+
+/* MMC uses open drain drivers in the enumeration phase */
+static int mmc_host_reset(struct mmc *dev)
+{
+ return 0;
+}
+
+static void host_set_ios(struct mmc *dev)
+{
+ struct mmc_host *host = dev->priv;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+ unsigned long clk_div;
+ unsigned long apb2_clock = CONFIG_APB2_CLOCK;
+
+ /* Ramp up the clock rate */
+ if (dev->clock) {
+ clk_div = apb2_clock / (2 * dev->clock);
+ if (apb2_clock % (2 * dev->clock))
+ clk_div ++;
+
+ if (clk_div >= 1) {
+ clk_div -= 1;
+ }
+ if (clk_div > 255) {
+ /* clock too slow */
+ clk_div = 255;
+ }
+
+ hwp_sdmmc->SDMMC_TRANS_SPEED = SDMMC_SDMMC_TRANS_SPEED(clk_div);
+ if( host->dev_num == 0 ) {
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = SDMMC_SDMMC_MCLK_ADJUST(_TGT_AP_SDMMC1_MCLK_ADJ);
+#if defined(_TGT_AP_SDMMC1_MCLK_INV) && (_TGT_AP_SDMMC1_MCLK_INV)
+ hwp_sdmmc->SDMMC_MCLK_ADJUST |= SDMMC_CLK_INV;
+#endif
+#ifdef CONFIG_RDA_FPGA
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = 0;
+#endif
+ } else {
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = SDMMC_SDMMC_MCLK_ADJUST(host->mclk_adj);
+ if(host->mclk_inv)
+ hwp_sdmmc->SDMMC_MCLK_ADJUST |= SDMMC_CLK_INV;
+ }
+#ifdef SDMMC_DEBUG
+ printf("set clock to %d, div = %lu\n", dev->clock, clk_div);
+#endif
+ }
+
+ /* Set the bus width */
+ if (dev->bus_width) {
+#ifdef SDMMC_DEBUG
+ printf("set bus_width to %#x\n", dev->bus_width);
+#endif
+
+ switch (dev->bus_width & 0xf) {
+ case 1:
+ hwp_sdmmc->SDMMC_DATA_WIDTH = 1;
+ break;
+ case 4:
+ hwp_sdmmc->SDMMC_DATA_WIDTH = 4;
+ break;
+ case 8:
+ hwp_sdmmc->SDMMC_DATA_WIDTH = 8;
+ break;
+ default:
+ printf("invalid bus width\n");
+ break;
+ }
+
+ /* check ddr enable/disable bit from bus_width */
+ if(dev->bus_width & 0x80000000) {
+ rda_ddr_mode = 1;
+#ifdef SDMMC_DEBUG
+ printf("mmc ddr mode is enabled.\n");
+#endif
+ } else {
+ rda_ddr_mode = 0;
+ }
+ }
+}
+
+
+struct mmc *alloc_mmc_struct(unsigned int dev_num)
+{
+ struct mmc_host *host = NULL;
+ struct mmc *mmc_device = NULL;
+
+ if (dev_num >= MMC_DEV_NUM)
+ return NULL;
+
+ host = &mmc_host_dev[dev_num];
+ if (!host)
+ return NULL;
+
+ host->dev_num = dev_num;
+ if (0 == dev_num)
+ host->phyaddr = RDA_SDMMC1_BASE;
+ else
+ host->phyaddr = RDA_SDMMC3_BASE;
+
+ mmc_device = &mmc_device_glob[dev_num];
+ if (!mmc_device)
+ goto err;
+
+ mmc_device->priv = host;
+ return mmc_device;
+
+err:
+ return NULL;
+}
+
+#define VOLTAGE_WINDOW_MMC 0x00FF8080
+
+/*
+ * mmc_host_init - initialize the mmc controller.
+ * Set initial clock and power for mmc slot.
+ * Initialize mmc struct and register with mmc framework.
+ */
+static int rda_mmc_host_init(struct mmc *dev)
+{
+ struct mmc_host *host = dev->priv;
+ unsigned long clk_div;
+ HWP_SDMMC_T* hwp_sdmmc = (HWP_SDMMC_T*)(host->phyaddr);
+ struct spl_emmc_info *info = get_bd_spl_emmc_info();
+
+ // clk_div = 0; // for APB2 = 48M, 48/2
+ //clk_div = 1; // for APB2 = 120M, 120/4
+ clk_div = 0x21; // for APB2 = 240M, 240/16
+
+ host->reserved = 0;
+
+ // We don't use interrupts.
+ hwp_sdmmc->SDMMC_INT_MASK = 0x0;
+ hwp_sdmmc->SDMMC_TRANS_SPEED = SDMMC_SDMMC_TRANS_SPEED(clk_div);
+ if (0 == host->dev_num){
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = SDMMC_SDMMC_MCLK_ADJUST(_TGT_AP_SDMMC1_MCLK_ADJ);
+#if defined(_TGT_AP_SDMMC1_MCLK_INV) && (_TGT_AP_SDMMC1_MCLK_INV)
+ hwp_sdmmc->SDMMC_MCLK_ADJUST |= SDMMC_CLK_INV;
+#endif
+#ifdef CONFIG_RDA_FPGA
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = 0;
+#endif
+ dev->host_caps = MMC_MODE_4BIT | MMC_MODE_HS | MMC_MODE_HC;
+ } else if (1 == host->dev_num){
+ host->mclk_adj = 0;
+ host->mclk_inv = 0;
+ if (info->manufacturer_id){
+ int i;
+
+ for(i = 0; emmc_mclk_adj_inv[i].mfr_id != 0; i++ )
+ if (info->manufacturer_id == emmc_mclk_adj_inv[i].mfr_id ){
+ host->mclk_adj = emmc_mclk_adj_inv[i].mclk_adj;
+ host->mclk_inv = emmc_mclk_adj_inv[i].mclk_inv;
+ break;
+ }
+
+ if (emmc_mclk_adj_inv[i].mfr_id == 0)
+ printf("Cannot find the emmc corresponding mclk adj and inv.Now use default zero. Please add it \n");
+ } else
+ printf("Spl init emmc first, use default zero mclk adj and inv.\n");
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = SDMMC_SDMMC_MCLK_ADJUST(host->mclk_adj);
+ if(host->mclk_inv)
+ hwp_sdmmc->SDMMC_MCLK_ADJUST |= SDMMC_CLK_INV;
+
+#if defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8810H)
+ dev->host_caps = MMC_MODE_8BIT | MMC_MODE_HS | MMC_MODE_HC | MMC_MODE_DDR_52MHz;
+#else
+ dev->host_caps = MMC_MODE_8BIT | MMC_MODE_HS | MMC_MODE_HC;
+#endif
+ } else{
+ printf("invalid mmc device number \n");
+ }
+
+ sprintf(dev->name, "MMC");
+ dev->clock = 400000;
+ dev->send_cmd = host_request;
+ dev->set_ios = host_set_ios;
+ dev->init = mmc_host_reset;
+ dev->voltages = VOLTAGE_WINDOW_MMC;
+ dev->f_min = 400000;
+ if( host->dev_num == 0 ) {
+ dev->f_max = _TGT_AP_SDMMC1_MAX_FREQ;
+ } else {
+ dev->f_max = _TGT_AP_SDMMC3_MAX_FREQ;
+ }
+
+#ifdef CONFIG_RDA_FPGA
+ printf("mmc host %d init, speed=0x%x, adjust = 0x%x \n",
+ host->dev_num, hwp_sdmmc->SDMMC_TRANS_SPEED,hwp_sdmmc->SDMMC_MCLK_ADJUST);
+#endif
+ return 0;
+}
+int mmc_set_host_mclk_adj_inv(struct mmc *mmc, u8 adj, u8 inv)
+{
+ struct mmc_host *host = mmc->priv;
+
+ if (!host)
+ return -1;
+
+ host->mclk_adj = adj;
+ host->mclk_inv = inv;
+
+ return 0;
+}
+
+/*
+ * MMC write function
+ */
+int mmc_read(struct mmc *mmc, u64 from, uchar *buf, int size)
+{
+ unsigned long cnt;
+ unsigned int start_block, block_num, block_size;
+ int blksz_shift;
+
+ if (!mmc) {
+#ifdef SDMMC_DEBUG
+ printf("mmc_read: mmc device not found!!\n");
+#endif
+ return -1;
+ }
+
+ block_size = mmc->read_bl_len;
+ blksz_shift = mmc->block_dev.log2blksz;
+ start_block = from >> blksz_shift;
+ block_num = (size + block_size - 1) >> blksz_shift;
+
+ /* Read the header too to avoid extra memcpy */
+ cnt = mmc->block_dev.block_read(mmc->block_dev.dev,
+ start_block,
+ block_num, (void *)buf);
+ if (cnt != block_num) {
+#ifdef SDMMC_DEBUG
+ printf("mmc blk read error!\n");
+#endif
+ return -1;
+ }
+
+ return size;
+}
+
+
+int mmc_write(struct mmc *mmc, u64 to, uchar *buf, int size)
+{
+ unsigned long cnt;
+ unsigned int start_block, block_num, block_size;
+ int blksz_shift;
+
+ if (!mmc) {
+#ifdef SDMMC_DEBUG
+ printf("mmc_write: mmc device not found!!\n");
+#endif
+ return -1;
+ }
+
+ blksz_shift = mmc->block_dev.log2blksz;
+ block_size = mmc->write_bl_len;
+ start_block = to >> blksz_shift;
+ block_num = (size + block_size - 1) >> blksz_shift;
+
+ cnt = mmc->block_dev.block_write(mmc->block_dev.dev,
+ start_block,
+ block_num, (void *)buf);
+ if (cnt != block_num) {
+#ifdef SDMMC_DEBUG
+ printf("mmc blk write error!\n");
+#endif
+ return -1;
+ }
+
+ return size;
+}
+
+static void setup_bb_cfg_sdmmc(void)
+{
+#ifdef CONFIG_SDMMC_BOOT
+ unsigned long temp;
+
+ /* pinmux for sdmmc0 */
+ temp = readl(0x11a09008);
+ temp &= ~(0x3F<<9);
+ writel(temp, 0x11a09008);
+#endif
+}
+
+#define RDA_MMC_MAX_BLOCKS (1024)
+int rda_mmc_init(void)
+{
+ int error,i;
+ struct mmc *dev;
+
+#ifdef SDMMC_DEBUG
+ printf("MMC: rda_mmc_init\n");
+#endif
+ setup_bb_cfg_sdmmc();
+
+ for(i = 0; i < MMC_DEV_NUM; i++){
+ dev = alloc_mmc_struct(i);
+ if (!dev)
+ return -1;
+
+ error = rda_mmc_host_init(dev);
+ if (error) {
+ printf("rda_mmc_host_init %d, error = %d\n",
+ dev->block_dev.dev, error);
+ return -1;
+ }
+
+ dev->b_max = RDA_MMC_MAX_BLOCKS;
+ mmc_register(dev);
+ printf("MMC: registered mmc interface %d\n",
+ dev->block_dev.dev);
+ }
+
+ return 0;
+}
+
+int board_mmc_init(bd_t *bis)
+{
+ int err = -1;
+
+ err = rda_mmc_init();
+ if (err)
+ return err;
+
+#if defined(CONFIG_PARTITIONS) && !defined(CONFIG_SPL_BUILD)
+ if (rda_media_get() == MEDIA_MMC) {
+ err = mmc_parts_init();
+ if (err)
+ return err;
+ }
+#endif
+ return err;
+}
+
diff --git a/drivers/mmc/rda_mmc.h b/drivers/mmc/rda_mmc.h
new file mode 100644
index 0000000000..64e08a738f
--- /dev/null
+++ b/drivers/mmc/rda_mmc.h
@@ -0,0 +1,221 @@
+#ifndef _SDMMC_H_
+#define _SDMMC_H_
+
+#include <asm/arch/hardware.h>
+
+// =============================================================================
+// TYPES
+// =============================================================================
+
+typedef struct
+{
+ u8 mid;
+ u32 oid;
+ u32 pnm1;
+ u8 pnm2;
+ u8 prv;
+ u32 psn;
+ u32 mdt;
+ u8 crc;
+} MCD_CID_FORMAT_T;
+
+
+// =============================================================================
+// MCD_CARD_STATE_T
+// -----------------------------------------------------------------------------
+/// The state of the card when receiving the command. If the command execution
+/// causes a state change, it will be visible to the host in the response to
+/// the next command. The four bits are interpreted as a binary coded number
+/// between 0 and 15.
+// =============================================================================
+typedef enum
+{
+ MCD_CARD_STATE_IDLE = 0,
+ MCD_CARD_STATE_READY = 1,
+ MCD_CARD_STATE_IDENT = 2,
+ MCD_CARD_STATE_STBY = 3,
+ MCD_CARD_STATE_TRAN = 4,
+ MCD_CARD_STATE_DATA = 5,
+ MCD_CARD_STATE_RCV = 6,
+ MCD_CARD_STATE_PRG = 7,
+ MCD_CARD_STATE_DIS = 8
+} MCD_CARD_STATE_T;
+
+
+// =============================================================================
+// MCD_CARD_STATUS_T
+// -----------------------------------------------------------------------------
+/// Card status as returned by R1 reponses (spec V2 pdf p.)
+// =============================================================================
+typedef union
+{
+ u32 reg;
+ struct
+ {
+ u32 :3;
+ u32 akeSeqError :1;
+ u32 :1;
+ u32 appCmd :1;
+ u32 :2;
+ u32 readyForData :1;
+ MCD_CARD_STATE_T currentState :4;
+ u32 eraseReset :1;
+ u32 cardEccDisabled :1;
+ u32 wpEraseSkip :1;
+ u32 csdOverwrite :1;
+ u32 :2;
+ u32 error :1;
+ u32 ccError :1;
+ u32 cardEccFailed :1;
+ u32 illegalCommand :1;
+ u32 comCrcError :1;
+ u32 lockUnlockFail :1;
+ u32 cardIsLocked :1;
+ u32 wpViolation :1;
+ u32 eraseParam :1;
+ u32 eraseSeqError :1;
+ u32 blockLenError :1;
+ u32 addressError :1;
+ u32 outOfRange :1;
+ } fields;
+} MCD_CARD_STATUS_T;
+
+// =============================================================================
+// MCD_CSD_T
+// -----------------------------------------------------------------------------
+/// This structure contains the fields of the MMC chip's register.
+/// For more details, please refer to your MMC specification.
+// =============================================================================
+typedef struct
+{ // Ver 2. // Ver 1.0 (if different)
+ u8 csdStructure; // 127:126
+ u8 specVers; // 125:122
+ u8 taac; // 119:112
+ u8 nsac; // 111:104
+ u8 tranSpeed; // 103:96
+ UINT16 ccc; // 95:84
+ u8 readBlLen; // 83:80
+ BOOL readBlPartial; // 79:79
+ BOOL writeBlkMisalign; // 78:78
+ BOOL readBlkMisalign; // 77:77
+ BOOL dsrImp; // 76:76
+ u32 cSize; // 69:48 // 73:62
+ u8 vddRCurrMin; // 61:59
+ u8 vddRCurrMax; // 58:56
+ u8 vddWCurrMin; // 55:53
+ u8 vddWCurrMax; // 52:50
+ u8 cSizeMult; // 49:47
+ // FIXME
+ u8 eraseBlkEnable;
+ u8 eraseGrpSize; // ??? 46:42
+ // FIXME
+ u8 sectorSize;
+ u8 eraseGrpMult; // ??? 41:37
+
+ u8 wpGrpSize; // 38:32
+ BOOL wpGrpEnable; // 31:31
+ u8 defaultEcc; // 30:29
+ u8 r2wFactor; // 28:26
+ u8 writeBlLen; // 25:22
+ BOOL writeBlPartial; // 21:21
+ BOOL contentProtApp; // 16:16
+ BOOL fileFormatGrp; // 15:15
+ BOOL copy; // 14:14
+ BOOL permWriteProtect; // 13:13
+ BOOL tmpWriteProtect; // 12:12
+ u8 fileFormat; // 11:10
+ u8 ecc; // 9:8
+ u8 crc; // 7:1
+ /// This field is not from the CSD register.
+ /// This is the actual block number.
+ u32 blockNumber;
+} MCD_CSD_T;
+
+
+// =============================================================================
+// MCD_ERR_T
+// -----------------------------------------------------------------------------
+/// Type used to describe the error status of the MMC driver.
+// =============================================================================
+typedef enum
+{
+ MCD_ERR_NO = 0,
+ MCD_ERR_CARD_TIMEOUT = 1,
+ MCD_ERR_DMA_BUSY = 3,
+ MCD_ERR_CSD = 4,
+ MCD_ERR_SPI_BUSY = 5,
+ MCD_ERR_BLOCK_LEN = 6,
+ MCD_ERR_CARD_NO_RESPONSE,
+ MCD_ERR_CARD_RESPONSE_BAD_CRC,
+ MCD_ERR_CMD,
+ MCD_ERR_UNUSABLE_CARD,
+ MCD_ERR_NO_CARD,
+ MCD_ERR_NO_HOTPLUG,
+
+ /// A general error value
+ MCD_ERR,
+} MCD_ERR_T;
+
+// =============================================================================
+// MCD_STATUS_T
+// -----------------------------------------------------------------------------
+/// Status of card
+// =============================================================================
+typedef enum
+{
+ // Card present and mcd is open
+ MCD_STATUS_OPEN,
+ // Card present and mcd is not open
+ MCD_STATUS_NOTOPEN_PRESENT,
+ // Card not present
+ MCD_STATUS_NOTPRESENT,
+ // Card removed, still open (please close !)
+ MCD_STATUS_OPEN_NOTPRESENT
+} MCD_STATUS_T ;
+
+// =============================================================================
+// MCD_CARD_SIZE_T
+// -----------------------------------------------------------------------------
+/// Card size
+// =============================================================================
+typedef struct
+{
+ u32 nbBlock;
+ u32 blockLen;
+} MCD_CARD_SIZE_T ;
+
+
+// =============================================================================
+// MCD_CARD_VER
+// -----------------------------------------------------------------------------
+/// Card version
+// =============================================================================
+
+typedef enum
+{
+ MCD_CARD_V1,
+ MCD_CARD_V2
+}MCD_CARD_VER;
+
+
+// =============================================================================
+// MCD_CARD_ID
+// -----------------------------------------------------------------------------
+/// Card version
+// =============================================================================
+
+typedef enum
+{
+ MCD_CARD_ID_0,
+ MCD_CARD_ID_1,
+ MCD_CARD_ID_NO,
+}MCD_CARD_ID;
+
+
+MCD_ERR_T mcd_Open(MCD_CSD_T* mcdCsd, MCD_CARD_VER mcdVer);
+MCD_ERR_T mcd_Close(void);
+MCD_ERR_T mcd_Write(u32 startAddr, u8* blockWr, u32 size);
+MCD_ERR_T mcd_Read(u32 startAddr, u8* blockRd, u32 size);
+
+#endif /* _SDMMC_H_ */
+
diff --git a/drivers/mmc/rda_mmc_legacy.c b/drivers/mmc/rda_mmc_legacy.c
new file mode 100644
index 0000000000..f7f2d7c61a
--- /dev/null
+++ b/drivers/mmc/rda_mmc_legacy.c
@@ -0,0 +1,2419 @@
+#include <common.h>
+#include <part.h>
+#include <fat.h>
+#include <mmc.h>
+
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_mmc.h>
+#include <asm/arch/reg_ifc.h>
+#include <asm/arch/ifc.h>
+
+#include "rda_mmc.h"
+
+#ifdef DEBUG
+#include <assert.h>
+#else
+#define assert(...)
+#endif
+
+#define MCD_TRACE(...)
+
+// =============================================================================
+// HAL_SDMMC_TRANSFER_T
+// -----------------------------------------------------------------------------
+/// Describe a transfer between the SDmmc module and the SD card
+// =============================================================================
+typedef struct
+{
+ /// This address in the system memory
+ u8* sysMemAddr;
+ /// Address in the SD card
+ u8* sdCardAddr;
+ /// Quantity of data to transfer, in blocks
+ u32 blockNum;
+ /// Block size
+ u32 blockSize;
+ HAL_SDMMC_DIRECTION_T direction;
+} HAL_SDMMC_TRANSFER_T;
+
+// =============================================================================
+// Global variables
+// =============================================================================
+u8 g_halSdmmcWriteCh = HAL_UNKNOWN_CHANNEL;
+u8 g_halSdmmcReadCh = HAL_UNKNOWN_CHANNEL;
+
+/// SDMMC clock frequency.
+static u32 g_halSdmmcFreq = 200000;
+
+//=============================================================================
+// hal_SdmmcUpdateDivider
+//-----------------------------------------------------------------------------
+/// Update the SDMMC module divider to keep the desired frequency, whatever
+/// the system frequency is.
+/// @param sysFreq System Frequency currently applied or that will be applied
+/// if faster.
+//=============================================================================
+//static void hal_SdmmcUpdateDivider(HAL_SYS_FREQ_T sysFreq);
+
+//=============================================================================
+// hal_SdmmcOpen
+//-----------------------------------------------------------------------------
+/// Open the SDMMC module. Take a resource.
+//=============================================================================
+void hal_SdmmcOpen(u8 clk_adj)
+{
+ // Make sure the last clock is set
+ //hal_SdmmcUpdateDivider(hal_SysGetFreq());
+
+ // Take the module out of reset
+ //hwp_sysCtrl->REG_DBG = SYS_CTRL_PROTECT_UNLOCK;
+ //hwp_sysCtrl->Sys_Rst_Clr = SYS_CTRL_CLR_RST_SDMMC;
+ //hwp_sysCtrl->REG_DBG = SYS_CTRL_PROTECT_LOCK;
+
+ // We don't use interrupts.
+ hwp_sdmmc->SDMMC_INT_MASK = 0x0;
+ hwp_sdmmc->SDMMC_MCLK_ADJUST = clk_adj;
+
+ // Take a resource (The idea is to be able to get a 25Mhz clock)
+ //hal_SysRequestFreq(HAL_SYS_FREQ_SDMMC, HAL_SYS_FREQ_52M, hal_SdmmcUpdateDivider);
+}
+
+//=============================================================================
+// hal_SdmmcClose
+//-----------------------------------------------------------------------------
+/// Close the SDMMC module. Take a resource.
+//=============================================================================
+void hal_SdmmcClose(void)
+{
+ // Free a resource
+ //hal_SysRequestFreq(HAL_SYS_FREQ_SDMMC, HAL_SYS_FREQ_32K, NULL);
+
+ // Put the module in reset, as its clock is free running.
+ //hwp_sysCtrl->REG_DBG = SYS_CTRL_PROTECT_UNLOCK;
+ //hwp_sysCtrl->Sys_Rst_Set = SYS_CTRL_SET_RST_SDMMC;
+ //hwp_sysCtrl->REG_DBG = SYS_CTRL_PROTECT_LOCK;
+}
+
+//=============================================================================
+// hal_SdmmcWakeUp
+//-----------------------------------------------------------------------------
+/// This function requests a resource of #HAL_SYS_FREQ_52M.
+/// hal_SdmmcSleep() must be called before any other
+//=============================================================================
+void hal_SdmmcWakeUp(void)
+{
+ // Take a resource (The idea is to be able to get a 25Mhz clock)
+ //hal_SysRequestFreq(HAL_SYS_FREQ_SDMMC, HAL_SYS_FREQ_52M, hal_SdmmcUpdateDivider);
+}
+
+//=============================================================================
+// hal_SdmmcSleep
+//-----------------------------------------------------------------------------
+/// This function release the resource to #HAL_SYS_FREQ_32K.
+//=============================================================================
+void hal_SdmmcSleep(void)
+{
+ // We just release the resource, because the clock gating in sdmmc controller
+ // will disable the clock. We should wait for the clock to be actually disabled
+ // but the module does not seam to have a status for that...
+
+ // Free a resource
+ //hal_SysRequestFreq(HAL_SYS_FREQ_SDMMC, HAL_SYS_FREQ_32K, NULL);
+}
+
+// =============================================================================
+// hal_SdmmcSendCmd
+// -----------------------------------------------------------------------------
+/// Send a command to a SD card plugged to the sdmmc port.
+/// @param cmd Command
+/// @param arg Argument of the command
+/// @param suspend Feature not implemented yet.
+// =============================================================================
+void hal_SdmmcSendCmd(HAL_SDMMC_CMD_T cmd, u32 arg, BOOL suspend)
+{
+ u32 configReg = 0;
+ //hwp_sdmmc->SDMMC_CONFIG = SDMMC_AUTO_FLAG_EN;
+ hwp_sdmmc->SDMMC_CONFIG = 0x00000000;
+
+ switch (cmd)
+ {
+ case HAL_SDMMC_CMD_GO_IDLE_STATE:
+ configReg = SDMMC_SDMMC_SENDCMD;
+ break;
+
+
+ case HAL_SDMMC_CMD_ALL_SEND_CID:
+ configReg = SDMMC_RSP_SEL_R2 | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x51
+ break;
+
+ case HAL_SDMMC_CMD_SEND_RELATIVE_ADDR:
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x11
+ break;
+
+ case HAL_SDMMC_CMD_SEND_IF_COND:
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x11
+ break;
+
+ case HAL_SDMMC_CMD_SET_DSR:
+ configReg = SDMMC_SDMMC_SENDCMD;
+ break;
+
+ case HAL_SDMMC_CMD_SELECT_CARD:
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD;
+ break;
+
+ case HAL_SDMMC_CMD_SEND_CSD :
+ configReg = SDMMC_RSP_SEL_R2 | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD;
+ break;
+
+ case HAL_SDMMC_CMD_STOP_TRANSMISSION :
+ configReg = 0; // TODO
+ break;
+
+ case HAL_SDMMC_CMD_SEND_STATUS :
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD;
+ break;
+
+ case HAL_SDMMC_CMD_SET_BLOCKLEN :
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD;
+ break;
+
+ case HAL_SDMMC_CMD_READ_SINGLE_BLOCK :
+ configReg = SDMMC_RD_WT_SEL_READ
+ | SDMMC_RD_WT_EN
+ | SDMMC_RSP_SEL_OTHER
+ | SDMMC_RSP_EN
+ | SDMMC_SDMMC_SENDCMD; // 0x111
+ break;
+
+ case HAL_SDMMC_CMD_READ_MULT_BLOCK :
+ configReg = SDMMC_S_M_SEL_MULTIPLE
+ | SDMMC_RD_WT_SEL_READ
+ | SDMMC_RD_WT_EN
+ | SDMMC_RSP_SEL_OTHER
+ | SDMMC_RSP_EN
+ | SDMMC_SDMMC_SENDCMD; // 0x511;
+ break;
+
+ case HAL_SDMMC_CMD_WRITE_SINGLE_BLOCK :
+ configReg = SDMMC_RD_WT_SEL_WRITE
+ | SDMMC_RD_WT_EN
+ | SDMMC_RSP_SEL_OTHER
+ | SDMMC_RSP_EN
+ | SDMMC_SDMMC_SENDCMD; // 0x311
+ break;
+
+ case HAL_SDMMC_CMD_WRITE_MULT_BLOCK :
+ configReg = SDMMC_S_M_SEL_MULTIPLE
+ | SDMMC_RD_WT_SEL_WRITE
+ | SDMMC_RD_WT_EN
+ | SDMMC_RSP_SEL_OTHER
+ | SDMMC_RSP_EN
+ | SDMMC_SDMMC_SENDCMD; // 0x711
+ break;
+
+ case HAL_SDMMC_CMD_APP_CMD :
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x11
+ break;
+
+ case HAL_SDMMC_CMD_SET_BUS_WIDTH :
+ case HAL_SDMMC_CMD_SWITCH:
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x11
+ break;
+
+ case HAL_SDMMC_CMD_SEND_NUM_WR_BLOCKS :
+ configReg = SDMMC_RD_WT_SEL_READ
+ | SDMMC_RD_WT_EN
+ | SDMMC_RSP_SEL_OTHER
+ | SDMMC_RSP_EN
+ | SDMMC_SDMMC_SENDCMD; // 0x111
+ break;
+
+ case HAL_SDMMC_CMD_SET_WR_BLK_COUNT :
+ configReg = SDMMC_RSP_SEL_OTHER | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x11
+ break;
+
+ case HAL_SDMMC_CMD_MMC_SEND_OP_COND:
+ case HAL_SDMMC_CMD_SEND_OP_COND:
+ configReg = SDMMC_RSP_SEL_R3 | SDMMC_RSP_EN | SDMMC_SDMMC_SENDCMD; // 0x31
+ break;
+
+ default:
+ break;
+ }
+
+ // TODO Add suspend management
+ hwp_sdmmc->SDMMC_CMD_INDEX = SDMMC_COMMAND(cmd);
+ hwp_sdmmc->SDMMC_CMD_ARG = SDMMC_ARGUMENT(arg);
+ //hwp_sdmmc->SDMMC_CONFIG = configReg |SDMMC_AUTO_FLAG_EN;
+ hwp_sdmmc->SDMMC_CONFIG = configReg ;
+}
+
+// =============================================================================
+// hal_SdmmcNeedResponse
+// -----------------------------------------------------------------------------
+/// Tells if the given command waits for a reponse.
+/// @return \c TRUE if the command needs an answer, \c FALSE otherwise.
+// =============================================================================
+BOOL hal_SdmmcNeedResponse(HAL_SDMMC_CMD_T cmd)
+{
+ switch (cmd)
+ {
+ case HAL_SDMMC_CMD_GO_IDLE_STATE:
+ case HAL_SDMMC_CMD_SET_DSR:
+ case HAL_SDMMC_CMD_STOP_TRANSMISSION:
+ return FALSE;
+ break;
+
+
+ case HAL_SDMMC_CMD_ALL_SEND_CID:
+ case HAL_SDMMC_CMD_SEND_RELATIVE_ADDR:
+ case HAL_SDMMC_CMD_SEND_IF_COND:
+
+ case HAL_SDMMC_CMD_SELECT_CARD:
+ case HAL_SDMMC_CMD_SEND_CSD:
+ case HAL_SDMMC_CMD_SEND_STATUS:
+ case HAL_SDMMC_CMD_SET_BLOCKLEN:
+ case HAL_SDMMC_CMD_READ_SINGLE_BLOCK:
+ case HAL_SDMMC_CMD_READ_MULT_BLOCK:
+ case HAL_SDMMC_CMD_WRITE_SINGLE_BLOCK:
+ case HAL_SDMMC_CMD_WRITE_MULT_BLOCK:
+ case HAL_SDMMC_CMD_APP_CMD:
+ case HAL_SDMMC_CMD_SET_BUS_WIDTH:
+ case HAL_SDMMC_CMD_SEND_NUM_WR_BLOCKS:
+ case HAL_SDMMC_CMD_SET_WR_BLK_COUNT:
+ case HAL_SDMMC_CMD_MMC_SEND_OP_COND:
+ case HAL_SDMMC_CMD_SEND_OP_COND:
+ case HAL_SDMMC_CMD_SWITCH:
+ return TRUE;
+ break;
+
+ default:
+ //assert(FALSE, "Unsupported SDMMC command:%d", cmd);
+ // Dummy return, for the compiler to be pleased.
+ return FALSE;
+ break;
+ }
+}
+
+// =============================================================================
+// hal_SdmmcCmdDone
+// -----------------------------------------------------------------------------
+/// @return \c TRUE of there is not command pending or being processed.
+// =============================================================================
+BOOL hal_SdmmcCmdDone(void)
+{
+ return (!(hwp_sdmmc->SDMMC_STATUS & SDMMC_NOT_SDMMC_OVER));
+}
+
+// =============================================================================
+// hal_SdmmcGetCardDetectPinLevel
+// -----------------------------------------------------------------------------
+/// @return \c TRUE if card detect (DAT3) line is high,
+/// \c FALSE if the line is low.
+/// User must check with SD spec and board pull-up/down resistor to
+/// interpret this value.
+// =============================================================================
+BOOL hal_SdmmcGetCardDetectPinLevel(void)
+{
+#if 0
+#if (CHIP_ASIC_ID == CHIP_ASIC_ID_GREENSTONE)
+ u32 value;
+
+ hal_GpioSetIn(HAL_GPIO_26);
+ //IOMUX control is useless, the input pins are not filtered but goes to both
+ //SDMMC and GPIO module
+ //hwp_extApb->GPIO_Mode &= ~REGS_MODE_PIN_SDMMC_DATA3;
+ value = hal_GpioGet(HAL_GPIO_26);
+ //hwp_extApb->GPIO_Mode |= REGS_MODE_PIN_SDMMC_DATA3;
+
+ //HAL_TRACE(TSTDOUT, 0, "----- Value %i\n", value);
+
+ return value != 0;
+#else
+ assert(FALSE, "TODO: hal_SdmmcGetCardDetectPinLevel not implemented for your chip!");
+ return FALSE;
+#endif
+#endif
+ return TRUE;
+}
+
+//=============================================================================
+// hal_SdmmcUpdateDivider
+//-----------------------------------------------------------------------------
+/// Update the SDMMC module divider to keep the desired frequency, whatever
+/// the system frequency is.
+/// @param sysFreq System Frequency currently applied or that will be applied
+/// if faster.
+//=============================================================================
+static void hal_SdmmcUpdateDivider(void)
+{
+ //if (g_halSdmmcFreq != 0)
+ //{
+ // u32 divider = (sysFreq-1)/(2*g_halSdmmcFreq) + 1;
+ // if (divider>1)
+ // {
+ // divider = divider-1;
+ // }
+
+ // if (divider > 0xFF)
+ // {
+ // divider = 0xFF;
+ // }
+
+ // wngl, use 25M/8 as clock
+ hwp_sdmmc->SDMMC_TRANS_SPEED = SDMMC_SDMMC_TRANS_SPEED(8);
+ //}
+}
+
+// =============================================================================
+// hal_SdmmcSetClk
+// -----------------------------------------------------------------------------
+/// Set the SDMMC clock frequency to something inferior but close to that,
+/// taking into account system frequency.
+// =============================================================================
+void hal_SdmmcSetClk(u32 clock)
+{
+ // TODO Add assert to stay on supported values ?
+ g_halSdmmcFreq = clock;
+
+ // Update the divider takes care of the registers configuration
+ hal_SdmmcUpdateDivider();
+}
+
+// =============================================================================
+// hal_SdmmcGetOpStatus
+// -----------------------------------------------------------------------------
+/// @return The operational status of the SDMMC module.
+// =============================================================================
+HAL_SDMMC_OP_STATUS_T hal_SdmmcGetOpStatus(void)
+{
+ return ((HAL_SDMMC_OP_STATUS_T)(u32)hwp_sdmmc->SDMMC_STATUS);
+}
+
+
+// =============================================================================
+// hal_SdmmcGetResp
+// -----------------------------------------------------------------------------
+/// This function is to be used to get the argument of the response of a
+/// command. It is needed to provide the command index and its application
+/// specific status, in order to determine the type of answer expected.
+///
+/// @param cmd Command to send
+/// @param arg Pointer to a four 32 bit word array, where the argument will be
+/// stored. Only the first case is set in case of a response of type R1, R3 or
+/// R6, 4 if it is a R2 response.
+/// @param suspend Unsupported
+// =============================================================================
+void hal_SdmmcGetResp(HAL_SDMMC_CMD_T cmd, u32* arg, BOOL suspend)
+{
+ // TODO Check in the spec for all the commands response types
+ switch (cmd)
+ {
+ // If they require a response, it is cargoed
+ // with a 32 bit argument.
+ case HAL_SDMMC_CMD_GO_IDLE_STATE :
+ case HAL_SDMMC_CMD_SEND_RELATIVE_ADDR :
+ case HAL_SDMMC_CMD_SEND_IF_COND :
+ case HAL_SDMMC_CMD_SET_DSR :
+ case HAL_SDMMC_CMD_SELECT_CARD :
+ case HAL_SDMMC_CMD_STOP_TRANSMISSION :
+ case HAL_SDMMC_CMD_SEND_STATUS :
+ case HAL_SDMMC_CMD_SET_BLOCKLEN :
+ case HAL_SDMMC_CMD_READ_SINGLE_BLOCK :
+ case HAL_SDMMC_CMD_READ_MULT_BLOCK :
+ case HAL_SDMMC_CMD_WRITE_SINGLE_BLOCK :
+ case HAL_SDMMC_CMD_WRITE_MULT_BLOCK :
+ case HAL_SDMMC_CMD_APP_CMD :
+ case HAL_SDMMC_CMD_SET_BUS_WIDTH :
+ case HAL_SDMMC_CMD_SEND_NUM_WR_BLOCKS :
+ case HAL_SDMMC_CMD_SET_WR_BLK_COUNT :
+ case HAL_SDMMC_CMD_MMC_SEND_OP_COND :
+ case HAL_SDMMC_CMD_SEND_OP_COND :
+ case HAL_SDMMC_CMD_SWITCH:
+ arg[0] = hwp_sdmmc->SDMMC_RESP_ARG3;
+ arg[1] = 0;
+ arg[2] = 0;
+ arg[3] = 0;
+ break;
+
+ // Those response arguments are 128 bits
+ case HAL_SDMMC_CMD_ALL_SEND_CID :
+ case HAL_SDMMC_CMD_SEND_CSD :
+ arg[0] = hwp_sdmmc->SDMMC_RESP_ARG0;
+ arg[1] = hwp_sdmmc->SDMMC_RESP_ARG1;
+ arg[2] = hwp_sdmmc->SDMMC_RESP_ARG2;
+ arg[3] = hwp_sdmmc->SDMMC_RESP_ARG3;
+ break;
+
+ default:
+ //assert(FALSE, "hal_SdmmcGetResp called with "
+ // "an unsupported command: %d", cmd);
+ break;
+ }
+}
+
+// =============================================================================
+// hal_SdmmcGetResp32BitsArgument
+// -----------------------------------------------------------------------------
+/// This function is to be used to get the argument of the response of a
+/// command triggerring a response with a 32 bits argument (typically,
+/// R1, R3 or R6).
+/// @return 32 bits of arguments of a 48 bits response token
+// =============================================================================
+u32 hal_SdmmcGetResp32BitsArgument(void)
+{
+ return hwp_sdmmc->SDMMC_RESP_ARG3;
+}
+
+// =============================================================================
+// hal_SdmmcGetResp128BitsArgument
+// -----------------------------------------------------------------------------
+/// This function is to be used to get the argument of the response of a
+/// command triggerring a response with a 128 bits argument (typically,
+/// R2).
+/// @param Pointer to a 4 case arrays of 32 bits where the argument of the
+/// function will be stored.
+// =============================================================================
+void hal_SdmmcGetResp128BitsArgument(u32* buf)
+{
+ buf[0] = hwp_sdmmc->SDMMC_RESP_ARG0;
+ buf[1] = hwp_sdmmc->SDMMC_RESP_ARG1;
+ buf[2] = hwp_sdmmc->SDMMC_RESP_ARG2;
+ buf[3] = hwp_sdmmc->SDMMC_RESP_ARG3;
+}
+
+// =============================================================================
+// hal_SdmmcEnterDataTransferMode
+// -----------------------------------------------------------------------------
+/// Configure the SDMMC module to support data transfers
+/// FIXME Find out why we need that out of the transfer functions...
+// =============================================================================
+void hal_SdmmcEnterDataTransferMode(void)
+{
+ hwp_sdmmc->SDMMC_CONFIG |= SDMMC_RD_WT_EN;
+}
+
+// =============================================================================
+// hal_SdmmcSetDataWidth
+// -----------------------------------------------------------------------------
+/// Set the data bus width
+/// @param width Number of line of the SD data bus used.
+// =============================================================================
+void hal_SdmmcSetDataWidth(HAL_SDMMC_DATA_BUS_WIDTH_T width)
+{
+ switch (width)
+ {
+ case HAL_SDMMC_DATA_BUS_WIDTH_1:
+ hwp_sdmmc->SDMMC_DATA_WIDTH = 1;
+ break;
+
+ case HAL_SDMMC_DATA_BUS_WIDTH_4:
+ hwp_sdmmc->SDMMC_DATA_WIDTH = 4;
+ break;
+
+ default:
+ //assert(FALSE, "hal_SdmmcSetDataWidth(%d) is an invalid width",
+ // width);
+ break;
+ }
+}
+
+// =============================================================================
+// hal_SdmmcTransfer
+// -----------------------------------------------------------------------------
+/// Start the ifc transfer to feed the SDMMC controller fifo.
+/// @param transfer Transfer to program.
+/// @return HAL_ERR_NO or HAL_ERR_RESOURCE_BUSY.
+// =============================================================================
+int hal_SdmmcTransfer(HAL_SDMMC_TRANSFER_T* transfer)
+{
+ u8 channel = 0;
+ HAL_IFC_REQUEST_ID_T ifcReq = HAL_IFC_NO_REQWEST;
+ u32 length = 0;
+ u32 lengthExp = 0;
+
+ //assert((transfer->blockSize>=4) && (transfer->blockSize<=2048),
+ // "Block Length(%d) is invalid!\n", transfer->blockSize);
+
+ length = transfer->blockSize;
+
+ // The block size register
+ while (length != 1)
+ {
+ length >>= 1;
+ lengthExp++;
+ }
+
+ // Configure amount of data
+ hwp_sdmmc->SDMMC_BLOCK_CNT = SDMMC_SDMMC_BLOCK_CNT(transfer->blockNum);
+ hwp_sdmmc->SDMMC_BLOCK_SIZE = SDMMC_SDMMC_BLOCK_SIZE(lengthExp);
+
+ // Configure Bytes reordering
+ hwp_sdmmc->apbi_ctrl_sdmmc = SDMMC_SOFT_RST_L | SDMMC_L_ENDIAN(1);
+
+ switch (transfer->direction)
+ {
+ case HAL_SDMMC_DIRECTION_READ:
+ ifcReq = HAL_IFC_SDMMC_RX;
+ break;
+
+ case HAL_SDMMC_DIRECTION_WRITE:
+ ifcReq = HAL_IFC_SDMMC_TX;
+ break;
+
+ default:
+ assert(FALSE, "hal_SdmmcTransfer with erroneous %d direction",
+ transfer->direction);
+ break;
+ }
+
+ channel = hal_IfcTransferStart(ifcReq, transfer->sysMemAddr,
+ transfer->blockNum*transfer->blockSize,
+ HAL_IFC_SIZE_32_MODE_MANUAL);
+ if (channel == HAL_UNKNOWN_CHANNEL)
+ {
+ printf("Chal_SdmmcTransfer error\n");
+ return -1;
+ }
+ else
+ {
+ // Record Channel used.
+ if (transfer->direction == HAL_SDMMC_DIRECTION_READ)
+ {
+ g_halSdmmcReadCh = channel;
+ }
+ else
+ {
+ g_halSdmmcWriteCh = channel;
+ }
+ return 0;
+ }
+}
+
+// =============================================================================
+// hal_SdmmcTransferDone
+// -----------------------------------------------------------------------------
+/// Check the end of transfer status.
+/// Attention: This means the SDMMC module has finished to transfer data.
+/// In case of a read operation, the Ifc will finish its transfer shortly
+/// after. Considering the way this function is used (after reading at least
+/// 512 bytes, and flushing cache before releasing the data to the user), and
+/// the fifo sizes, this is closely equivalent to the end of the transfer.
+/// @return \c TRUE if a transfer is over, \c FALSE otherwise.
+// =============================================================================
+BOOL hal_SdmmcTransferDone(void)
+{
+ // The link is not full duplex. We check both the
+ // direction, but only one can be in progress at a time.
+
+ if (g_halSdmmcReadCh != HAL_UNKNOWN_CHANNEL)
+ {
+ // Operation in progress is a read.
+ // The SDMMC module itself can know it has finished
+ if ((hwp_sdmmc->SDMMC_INT_STATUS & SDMMC_DAT_OVER_INT)
+ && (hwp_sysIfc->std_ch[g_halSdmmcReadCh].tc == 0))
+ {
+ // Transfer is over
+ hwp_sdmmc->SDMMC_INT_CLEAR = SDMMC_DAT_OVER_CL;
+ hal_IfcChannelRelease(HAL_IFC_SDMMC_RX, g_halSdmmcReadCh);
+
+ // We finished a read
+ g_halSdmmcReadCh = HAL_UNKNOWN_CHANNEL;
+
+ // Put the FIFO in reset state.
+ hwp_sdmmc->apbi_ctrl_sdmmc = 0 | SDMMC_L_ENDIAN(1);
+
+ return TRUE;
+ }
+ }
+
+ if (g_halSdmmcWriteCh != HAL_UNKNOWN_CHANNEL)
+ {
+ // Operation in progress is a write.
+ // The SDMMC module itself can know it has finished
+ if ((hwp_sdmmc->SDMMC_INT_STATUS & SDMMC_DAT_OVER_INT)
+ && (hwp_sysIfc->std_ch[g_halSdmmcWriteCh].tc == 0))
+ {
+ // Transfer is over
+ hwp_sdmmc->SDMMC_INT_CLEAR = SDMMC_DAT_OVER_CL;
+ hal_IfcChannelRelease(HAL_IFC_SDMMC_TX, g_halSdmmcWriteCh);
+
+ // We finished a write
+ g_halSdmmcWriteCh = HAL_UNKNOWN_CHANNEL;
+
+ // Put the FIFO in reset state.
+ hwp_sdmmc->apbi_ctrl_sdmmc = 0 | SDMMC_L_ENDIAN(1);
+
+ return TRUE;
+ }
+ }
+
+ // there's still data running through a pipe (or no transfer in progress ...)
+ return FALSE;
+}
+
+// =============================================================================
+// hal_SdmmcStopTransfer
+// -----------------------------------------------------------------------------
+/// Stop the ifc transfer feeding the SDMMC controller fifo.
+/// @param transfer Transfer to program.
+/// @return #HAL_ERR_NO
+// =============================================================================
+void hal_SdmmcStopTransfer(HAL_SDMMC_TRANSFER_T* transfer)
+{
+ // Configure amount of data
+ hwp_sdmmc->SDMMC_BLOCK_CNT = SDMMC_SDMMC_BLOCK_CNT(0);
+ hwp_sdmmc->SDMMC_BLOCK_SIZE = SDMMC_SDMMC_BLOCK_SIZE(0);
+
+ // Put the FIFO in reset state.
+ hwp_sdmmc->apbi_ctrl_sdmmc = 0 | SDMMC_L_ENDIAN(1);
+
+ if (transfer->direction == HAL_SDMMC_DIRECTION_READ)
+ {
+ hal_IfcChannelFlush(HAL_IFC_SDMMC_RX, g_halSdmmcReadCh);
+ while(!hal_IfcChannelIsFifoEmpty(HAL_IFC_SDMMC_RX, g_halSdmmcReadCh));
+ hal_IfcChannelRelease(HAL_IFC_SDMMC_RX, g_halSdmmcReadCh);
+ g_halSdmmcReadCh = HAL_UNKNOWN_CHANNEL;
+ }
+ else
+ {
+ hal_IfcChannelFlush(HAL_IFC_SDMMC_TX, g_halSdmmcWriteCh);
+ while(!hal_IfcChannelIsFifoEmpty(HAL_IFC_SDMMC_TX, g_halSdmmcReadCh));
+ hal_IfcChannelRelease(HAL_IFC_SDMMC_TX, g_halSdmmcWriteCh);
+ g_halSdmmcWriteCh = HAL_UNKNOWN_CHANNEL;
+ }
+}
+
+// =============================================================================
+// hal_SdmmcIsBusy
+// -----------------------------------------------------------------------------
+/// Check if the SD/MMC is busy.
+///
+/// @return \c TRUE if the SD/MMC controller is busy.
+/// \c FALSE otherwise.
+// =============================================================================
+BOOL hal_SdmmcIsBusy(void)
+{
+ //if (g_halSdmmcReadCh != HAL_UNKNOWN_CHANNEL
+ // || g_halSdmmcWriteCh != HAL_UNKNOWN_CHANNEL
+ // || ((hwp_sdmmc->SDMMC_STATUS & (SDMMC_NOT_SDMMC_OVER | SDMMC_BUSY | SDMMC_DL_BUSY)) != 0)
+ // )
+ if ((hwp_sdmmc->SDMMC_STATUS & (SDMMC_NOT_SDMMC_OVER | SDMMC_BUSY | SDMMC_DL_BUSY)) != 0)
+ {
+ // SD/MMc is busy doing something.
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// =============================================================================
+// MACROS
+// =============================================================================
+#define BROAD_ADDR 0
+#define NOT_SDMMC_OVER (1<<0)
+
+#define MCD_MAX_BLOCK_NUM 128
+
+#define MCD_SDMMC_OCR_TIMEOUT (1 SECOND) // the card is supposed to answer within 1s
+ // - Max wait is 128 * 10ms=1,28s
+ // TODO: where those 10ms come from ??
+
+// Command 8 things: cf spec Vers.2 section 4.3.13
+#define MCD_CMD8_CHECK_PATTERN 0xaa
+#define MCD_CMD8_VOLTAGE_SEL (0x1<<8)
+//#define MCD_CMD8_VOLTAGE_SEL (0x2<<8) // wngl, change to low voltage
+#define MCD_OCR_HCS (1<<30)
+#define MCD_OCR_CCS_MASK (1<<30)
+
+#define SECOND * CONFIG_SYS_HZ_CLOCK
+// Timeouts for V1
+#define MCD_CMD_TIMEOUT_V1 ( 1 SECOND / 1 )
+#define MCD_RESP_TIMEOUT_V1 ( 1 SECOND / 1 )
+#define MCD_TRAN_TIMEOUT_V1 ( 1 SECOND / 1 )
+#define MCD_READ_TIMEOUT_V1 ( 5 SECOND )
+#define MCD_WRITE_TIMEOUT_V1 ( 5 SECOND )
+
+// Timeouts for V2
+#define MCD_CMD_TIMEOUT_V2 ( 1 SECOND / 10 )
+#define MCD_RESP_TIMEOUT_V2 ( 1 SECOND / 10 )
+#define MCD_TRAN_TIMEOUT_V2 ( 1 SECOND / 10 )
+#define MCD_READ_TIMEOUT_V2 ( 5 SECOND )
+#define MCD_WRITE_TIMEOUT_V2 (5 SECOND )
+
+// =============================================================================
+// Global variables
+// =============================================================================
+
+// Spec Ver2 p96
+#define MCD_SDMMC_OCR_VOLT_PROF_MASK 0x00ff8000
+
+static u32 g_mcdOcrReg = MCD_SDMMC_OCR_VOLT_PROF_MASK;
+
+/// Relative Card Address Register
+/// Nota RCA is sixteen bit long, but is always used
+/// as the 16 upper bits of a 32 bits word. Therefore
+/// is variable is in fact (RCA<<16), to avoid operation
+/// (shift, unshift), to always place the RCA value as the
+/// upper bits of a data word.
+static u32 g_mcdRcaReg = 0x00000000;
+
+// Driver Stage Register p.118
+// (To adjust bus capacitance)
+// TODO Tune and put in calibration ?
+static u32 g_mcdSdmmcDsr = 0x04040000;
+
+
+static u32 g_mcdSdmmcFreq = 200000;
+
+static MCD_CID_FORMAT_T g_mcdCidReg;
+static u32 g_mcdBlockLen = 0;
+static u32 g_mcdNbBlock = 0;
+static BOOL g_mcdCardIsSdhc = FALSE;
+
+static MCD_STATUS_T g_mcdStatus = MCD_STATUS_NOTOPEN_PRESENT;
+
+//static CONST TGT_MCD_CONFIG_T* g_mcdConfig=NULL;
+//static MCD_CARD_DETECT_HANDLER_T g_mcdCardDetectHandler=NULL;
+
+/// Semaphore to ensure proper concurrency of the MCD accesses
+/// among all tasks.
+static u32 g_mcdSemaphore = 0xFF;
+
+/// Current in-progress transfer, if any.
+static HAL_SDMMC_TRANSFER_T g_mcdSdmmcTransfer =
+ {
+ .sysMemAddr = 0,
+ .sdCardAddr = 0,
+ .blockNum = 0,
+ .blockSize = 0,
+ .direction = HAL_SDMMC_DIRECTION_WRITE,
+ };
+
+static MCD_CSD_T g_mcdLatestCsd;
+
+static MCD_CARD_VER g_mcdVer = MCD_CARD_V2;
+
+// =============================================================================
+// Functions
+// =============================================================================
+u32 hal_TimGetUpTime(void)
+{
+ return (u32)get_ticks();
+}
+// =============================================================================
+// Functions for the HAL Driver ?
+// =============================================================================
+#if 0
+/// Macro to easily implement concurrency in the MCD driver.
+/// Enter in the 'critical section'.
+#define MCD_CS_ENTER \
+ if (g_mcdSemaphore != 0xFF) \
+ { \
+ sxr_TakeSemaphore(g_mcdSemaphore); \
+ } \
+ else \
+ { \
+ return MCD_ERR; \
+ }
+
+
+/// Macro to easily implement concurrency in the MCD driver.
+/// Exit in the 'critical section'.
+#define MCD_CS_EXIT \
+ { \
+ sxr_ReleaseSemaphore(g_mcdSemaphore); \
+ }
+#endif
+
+MCD_ERR_T mcd_GetCardSize(MCD_CARD_SIZE_T* size)
+{
+ //MCD_CS_ENTER;
+
+ size->blockLen = g_mcdBlockLen;
+ size->nbBlock = g_mcdNbBlock;
+
+ //MCD_CS_EXIT;
+ return MCD_ERR_NO;
+}
+
+// Wait for a command to be done or timeouts
+static MCD_ERR_T mcd_SdmmcWaitCmdOver(void)
+{
+ u32 startTime = hal_TimGetUpTime();
+ u32 time_out;
+
+ time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_CMD_TIMEOUT_V1:MCD_CMD_TIMEOUT_V2;
+
+ while(hal_TimGetUpTime() - startTime < time_out && !hal_SdmmcCmdDone());
+
+ if (!hal_SdmmcCmdDone())
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "SDC Waiting is Time out");
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+ else
+ {
+ return MCD_ERR_NO;
+ }
+}
+
+#if 0
+/// Update g_mcdStatus
+/// @return TRUE is card present (only exact when GPIO is used for card detect.)
+static BOOL mcd_CardDetectUpdateStatus(void)
+{
+ if(NULL == g_mcdConfig)
+ {
+ g_mcdConfig = tgt_GetMcdConfig();
+ }
+
+ if(g_mcdConfig->cardDetectGpio != HAL_GPIO_NONE)
+ {
+ BOOL gpioState = !!hal_GpioGet(g_mcdConfig->cardDetectGpio);
+ // CARD ?
+ if(gpioState == g_mcdConfig->gpioCardDetectHigh)
+ { // card detected
+ switch(g_mcdStatus)
+ {
+ case MCD_STATUS_OPEN_NOTPRESENT: // wait for the close !
+ case MCD_STATUS_OPEN:
+ // No change
+ break;
+ default:
+ g_mcdStatus = MCD_STATUS_NOTOPEN_PRESENT;
+ }
+ return TRUE;
+ }
+ else
+ { // no card
+ switch(g_mcdStatus)
+ {
+ case MCD_STATUS_OPEN_NOTPRESENT:
+ case MCD_STATUS_OPEN:
+ g_mcdStatus = MCD_STATUS_OPEN_NOTPRESENT;
+ break;
+ default:
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+ }
+ return FALSE;
+ }
+ }
+ // estimated
+ switch(g_mcdStatus)
+ {
+ case MCD_STATUS_OPEN:
+ case MCD_STATUS_NOTOPEN_PRESENT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void mcd_CardDetectHandler(void)
+{
+ BOOL CardPresent = mcd_CardDetectUpdateStatus();
+
+ g_mcdCardDetectHandler(CardPresent);
+}
+
+// =============================================================================
+// mcd_SetCardDetectHandler
+// -----------------------------------------------------------------------------
+/// Register a handler for card detection
+///
+/// @param handler function called when insertion/removal is detected.
+// =============================================================================
+MCD_ERR_T mcd_SetCardDetectHandler(MCD_CARD_DETECT_HANDLER_T handler)
+{
+ if(NULL == g_mcdConfig)
+ {
+ g_mcdConfig = tgt_GetMcdConfig();
+ }
+
+ if(g_mcdConfig->cardDetectGpio == HAL_GPIO_NONE)
+ {
+ return MCD_ERR_NO_HOTPLUG;
+ }
+
+ if(NULL != handler)
+ {
+ HAL_GPIO_CFG_T cfg =
+ {
+ .direction = HAL_GPIO_DIRECTION_INPUT,
+ .irqMask =
+ {
+ .rising = TRUE,
+ .falling = TRUE,
+ .debounce = TRUE,
+ .level = FALSE
+ },
+ .irqHandler = mcd_CardDetectHandler
+ };
+
+ hal_GpioOpen(g_mcdConfig->cardDetectGpio, &cfg);
+ g_mcdCardDetectHandler = handler;
+ }
+ else
+ {
+ hal_GpioClose(g_mcdConfig->cardDetectGpio);
+ g_mcdCardDetectHandler = NULL;
+ }
+
+ return MCD_ERR_NO;
+}
+
+// =============================================================================
+// mcd_CardStatus
+// -----------------------------------------------------------------------------
+/// Return the card status
+///
+/// @return Card status see #MCD_STATUS_T
+// =============================================================================
+MCD_STATUS_T mcd_CardStatus(void)
+{
+ mcd_CardDetectUpdateStatus();
+ return g_mcdStatus;
+}
+#endif
+
+// =============================================================================
+// mcd_SdmmcWaitResp
+// -----------------------------------------------------------------------------
+/// Wait for a response for a time configured by MCD_RESP_TIMEOUT
+/// @return MCD_ERR_NO if a response with a good crc was received,
+/// MCD_ERR_CARD_NO_RESPONSE if no response was received within the
+/// driver configured timeout.
+// MCD_ERR_CARD_RESPONSE_BAD_CRC if the received response presented
+// a bad CRC.
+// =============================================================================
+static MCD_ERR_T mcd_SdmmcWaitResp(void)
+{
+ HAL_SDMMC_OP_STATUS_T status = hal_SdmmcGetOpStatus();
+ u32 startTime = hal_TimGetUpTime();
+ u32 rsp_time_out;
+
+ rsp_time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_RESP_TIMEOUT_V1:MCD_RESP_TIMEOUT_V2;
+
+ while(hal_TimGetUpTime() - startTime < rsp_time_out &&status.fields.noResponseReceived)
+ {
+ status = hal_SdmmcGetOpStatus();
+ }
+
+ if (status.fields.noResponseReceived)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Response is Time out");
+ return MCD_ERR_CARD_NO_RESPONSE;
+ }
+
+ if(status.fields.responseCrcError)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Response CRC is Error");
+ return MCD_ERR_CARD_RESPONSE_BAD_CRC;
+ }
+
+ return MCD_ERR_NO;
+}
+
+//=============================================================================
+// mcd_SdmmcReadCheckCrc
+//-----------------------------------------------------------------------------
+/// Check the read state of the sdmmc card.
+/// @return MCD_ERR_NO if the Crc of read data was correct,
+/// MCD_ERR_CARD_RESPONSE_BAD_CRC otherwise.
+//=============================================================================
+static MCD_ERR_T mcd_SdmmcReadCheckCrc(void)
+{
+ HAL_SDMMC_OP_STATUS_T operationStatus;
+ operationStatus = hal_SdmmcGetOpStatus();
+ if (operationStatus.fields.dataError != 0) // 0 means no CRC error during transmission
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "sdC status:%0x", operationStatus.reg);
+ return MCD_ERR_CARD_RESPONSE_BAD_CRC;
+ }
+ else
+ {
+ return MCD_ERR_NO;
+ }
+}
+
+//=============================================================================
+// mcd_SdmmcWriteCheckCrc
+//-----------------------------------------------------------------------------
+/// Check the crc state of the write operation of the sdmmc card.
+/// @return MCD_ERR_NO if the Crc of read data was correct,
+/// MCD_ERR_CARD_RESPONSE_BAD_CRC otherwise.
+//=============================================================================
+static MCD_ERR_T mcd_SdmmcWriteCheckCrc(void)
+{
+ HAL_SDMMC_OP_STATUS_T operationStatus;
+ operationStatus = hal_SdmmcGetOpStatus();
+
+ if (operationStatus.fields.crcStatus != 2) // 0b010 = transmissionRight TODO a macro ?
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "sdC status:%0x", operationStatus.reg);
+ return MCD_ERR_CARD_RESPONSE_BAD_CRC;
+ }
+ else
+ {
+ return MCD_ERR_NO;
+ }
+}
+
+// =============================================================================
+// mcd_SdmmcSendCmd
+// -----------------------------------------------------------------------------
+/// Send a command to the card, and fetch the response if one is expected.
+/// @param cmd CMD to send
+/// @param arg Argument of the ACMD.
+/// @param resp Buffer to store card response.
+/// @param suspend Not supported.
+/// @return MCD_ERR_NO if a response with a good crc was received,
+/// MCD_ERR_CARD_NO_RESPONSE if no reponse was received within the
+/// driver configured timeout.
+/// MCD_ERR_CARD_RESPONSE_BAD_CRC if the received response presented
+/// a bad CRC.
+/// MCD_ERR_CARD_TIMEOUT if the card timedout during procedure.
+// =============================================================================
+static MCD_ERR_T mcd_SdmmcSendCmd(HAL_SDMMC_CMD_T cmd, u32 arg,
+ u32* resp, BOOL suspend)
+{
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+ MCD_CARD_STATUS_T cardStatus = {0};
+ u32 cmd55Response[4] = {0, 0, 0, 0};
+ if (cmd & HAL_SDMMC_ACMD_SEL)
+ {
+ // This is an application specific command,
+ // we send the CMD55 first
+ hal_SdmmcSendCmd(HAL_SDMMC_CMD_APP_CMD, g_mcdRcaReg, FALSE);
+
+ // Wait for command over
+ if (MCD_ERR_CARD_TIMEOUT == mcd_SdmmcWaitCmdOver())
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Cmd55 Send is Time out");
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+
+ // Wait for response
+ if (hal_SdmmcNeedResponse(HAL_SDMMC_CMD_APP_CMD))
+ {
+ errStatus = mcd_SdmmcWaitResp();
+ }
+ if (MCD_ERR_NO != errStatus)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "cmd55 response error");
+ return errStatus;
+ }
+
+ // Fetch response
+ hal_SdmmcGetResp(HAL_SDMMC_CMD_APP_CMD, cmd55Response, FALSE);
+
+ cardStatus.reg = cmd55Response[0];
+ if(HAL_SDMMC_CMD_SEND_OP_COND == cmd) // for some special card
+ {
+ //if (!(cardStatus.fields.readyForData) || !(cardStatus.fields.appCmd))
+ if (!(cardStatus.fields.appCmd))
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "cmd55(acmd41) status=%0x", cardStatus.reg);
+ return MCD_ERR;
+ }
+ }
+ else
+ {
+ if (!(cardStatus.fields.readyForData) || !(cardStatus.fields.appCmd))
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "cmd55 status=%0x", cardStatus.reg);
+ return MCD_ERR;
+ }
+
+ }
+ }
+
+ // Send proper command. If it was an ACMD, the CMD55 have just been sent.
+ hal_SdmmcSendCmd(cmd, arg, suspend);
+
+ // Wait for command to be sent
+ errStatus = mcd_SdmmcWaitCmdOver();
+
+
+ if (MCD_ERR_CARD_TIMEOUT == errStatus)
+ {
+ if (cmd & HAL_SDMMC_ACMD_SEL)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "ACMD %d Sending Timed out", (cmd & HAL_SDMMC_CMD_MASK));
+ }
+ else
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "CMD %d Sending Timed out", (cmd & HAL_SDMMC_CMD_MASK));
+ }
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+
+ // Wait for response and get its argument
+ if (hal_SdmmcNeedResponse(cmd))
+ {
+ errStatus = mcd_SdmmcWaitResp();
+ }
+
+ if (MCD_ERR_NO != errStatus)
+ {
+ if (cmd & HAL_SDMMC_ACMD_SEL)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "ACMD %d Response Error", (cmd & HAL_SDMMC_CMD_MASK));
+ }
+ else
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "CMD %d Response Error", (cmd & HAL_SDMMC_CMD_MASK));
+ return errStatus;
+ }
+ }
+
+ // Fetch response
+ hal_SdmmcGetResp(cmd, resp, FALSE);
+
+ //FOR DEBUG: MCD_TRACE(MCD_INFO_TRC, 0, "CMD %d Response is %#x", (cmd & HAL_SDMMC_CMD_MASK), resp[0]);
+
+ return MCD_ERR_NO;
+}
+
+// =============================================================================
+// mcd_SdmmcInitCid
+// -----------------------------------------------------------------------------
+/// Set the CID in the driver from the data read on the card.
+/// @param cid 4 word array read from the card, holding the CID register value.
+// =============================================================================
+static MCD_ERR_T mcd_SdmmcInitCid(u32* cid)
+{
+ // Fill the structure with the register bitfields value.
+ g_mcdCidReg.mid = (u8)((cid[3]&(0xff<<24))>>24);
+ g_mcdCidReg.oid = (cid[3]&(0xffff<<8))>>8;
+ g_mcdCidReg.pnm2 = (u8)(cid[3]&0xff);
+ g_mcdCidReg.pnm1 = cid[2];
+ g_mcdCidReg.prv = (u8)((cid[1]&(0xff<<24))>>24);
+ g_mcdCidReg.psn = (cid[1]&0xffffff)<<8;
+ g_mcdCidReg.psn = g_mcdCidReg.psn|((cid[0]&(0xff<<23))>>23);
+ g_mcdCidReg.mdt = (cid[0]&(0xfff<<7))>>7;
+ g_mcdCidReg.crc = (u8)(cid[0]&0x7f);
+
+ return MCD_ERR_NO;
+}
+
+#define MCD_CSD_VERSION_1 0
+#define MCD_CSD_VERSION_2 1
+// =============================================================================
+// mcd_SdmmcInitCsd
+// -----------------------------------------------------------------------------
+/// Fill MCD_CSD_T structure from the array of data read from the card
+///
+/// @param csd Pointer to the structure
+/// @param csdRaw Pointer to the raw data.
+/// @return MCD_ERR_NO
+// =============================================================================
+static MCD_ERR_T mcd_SdmmcInitCsd(MCD_CSD_T* csd, u32* csdRaw)
+{
+ // CF SD spec version2, CSD version 1 ?
+ csd->csdStructure = (u8)((csdRaw[3]&(0x3<<30))>>30);
+
+ // Byte 47 to 75 are different depending on the version
+ // of the CSD srtucture.
+ csd->specVers = (u8)((csdRaw[3]&(0xf<26))>>26);
+ csd->taac = (u8)((csdRaw[3]&(0xff<<16))>>16);
+ csd->nsac = (u8)((csdRaw[3]&(0xff<<8))>>8);
+ csd->tranSpeed = (u8)(csdRaw[3]&0xff);
+
+ csd->ccc = (csdRaw[2]&(0xfff<<20))>>20;
+ csd->readBlLen = (u8)((csdRaw[2]&(0xf<<16))>>16);
+ csd->readBlPartial = (u8)((csdRaw[2]&(0x1<<15))>>15);
+ csd->writeBlkMisalign = (u8)((csdRaw[2]&(0x1<<14))>>14);
+ csd->readBlkMisalign = (u8)((csdRaw[2]&(0x1<<13))>>13);
+ csd->dsrImp = (u8)((csdRaw[2]&(0x1<<12))>>12);
+
+ if (csd->csdStructure == MCD_CSD_VERSION_1)
+ {
+ csd->cSize = (csdRaw[2]&0x3ff)<<2;
+
+ csd->cSize = csd->cSize|((csdRaw[1]&(0x3<<30))>>30);
+ csd->vddRCurrMin = (u8)((csdRaw[1]&(0x7<<27))>>27);
+ csd->vddRCurrMax = (u8)((csdRaw[1]&(0x7<<24))>>24);
+ csd->vddWCurrMin = (u8)((csdRaw[1]&(0x7<<21))>>21);
+ csd->vddWCurrMax = (u8)((csdRaw[1]&(0x7<<18))>>18);
+ csd->cSizeMult = (u8)((csdRaw[1]&(0x7<<15))>>15);
+
+ // Block number: cf Spec Version 2 page 103 (116).
+ csd->blockNumber = (csd->cSize + 1)<<(csd->cSizeMult + 2);
+ }
+ else
+ {
+ // csd->csdStructure == MCD_CSD_VERSION_2
+ csd->cSize = ((csdRaw[2]&0x3f))|((csdRaw[1]&(0xffff<<16))>>16);
+
+ // Other fields are undefined --> zeroed
+ csd->vddRCurrMin = 0;
+ csd->vddRCurrMax = 0;
+ csd->vddWCurrMin = 0;
+ csd->vddWCurrMax = 0;
+ csd->cSizeMult = 0;
+
+ // Block number: cf Spec Version 2 page 109 (122).
+ csd->blockNumber = (csd->cSize + 1) * 1024;
+ //should check incompatible size and return MCD_ERR_UNUSABLE_CARD;
+ }
+
+ csd->eraseBlkEnable = (u8)((csdRaw[1]&(0x1<<14))>>14);
+ csd->sectorSize = (u8)((csdRaw[1]&(0x7f<<7))>>7);
+ csd->wpGrpSize = (u8)(csdRaw[1]&0x7f);
+
+ csd->wpGrpEnable = (u8)((csdRaw[0]&(0x1<<31))>>31);
+ csd->r2wFactor = (u8)((csdRaw[0]&(0x7<<26))>>26);
+ csd->writeBlLen = (u8)((csdRaw[0]&(0xf<<22))>>22);
+ csd->writeBlPartial = (u8)((csdRaw[0]&(0x1<<21))>>21);
+ csd->fileFormatGrp = (u8)((csdRaw[0]&(0x1<<15))>>15);
+ csd->copy = (u8)((csdRaw[0]&(0x1<<14))>>14);
+ csd->permWriteProtect = (u8)((csdRaw[0]&(0x1<<13))>>13);
+ csd->tmpWriteProtect = (u8)((csdRaw[0]&(0x1<<12))>>12);
+ csd->fileFormat = (u8)((csdRaw[0]&(0x3<<10))>>10);
+ csd->crc = (u8)((csdRaw[0]&(0x7f<<1))>>1);
+
+
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:csdStructure = %d", csd->csdStructure );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:specVers = %d", csd->specVers );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:taac = %d", csd->taac );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:nsac = %d", csd->nsac );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:tranSpeed = %d", csd->tranSpeed );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:ccc = %d", csd->ccc );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:readBlLen = %d", csd->readBlLen );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:readBlPartial = %d", csd->readBlPartial );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:writeBlkMisalign = %d", csd->writeBlkMisalign );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:readBlkMisalign = %d", csd->readBlkMisalign );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:dsrImp = %d", csd->dsrImp );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:cSize = %d", csd->cSize );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:vddRCurrMin = %d", csd->vddRCurrMin );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:vddRCurrMax = %d", csd->vddRCurrMax );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:vddWCurrMin = %d", csd->vddWCurrMin );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:vddWCurrMax = %d", csd->vddWCurrMax );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:cSizeMult = %d", csd->cSizeMult );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:eraseBlkEnable = %d", csd->eraseBlkEnable );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:sectorSize = %d", csd->sectorSize );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:wpGrpSize = %d", csd->wpGrpSize );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:wpGrpEnable = %d", csd->wpGrpEnable );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:r2wFactor = %d", csd->r2wFactor );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:writeBlLen = %d", csd->writeBlLen );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:writeBlPartial = %d", csd->writeBlPartial );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:fileFormatGrp = %d", csd->fileFormatGrp );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:copy = %d", csd->copy );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:permWriteProtect = %d", csd->permWriteProtect );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:tmpWriteProtect = %d", csd->tmpWriteProtect );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:fileFormat = %d", csd->fileFormat );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:crc = %d", csd->crc );
+ MCD_TRACE(MCD_INFO_TRC, 0, "CSD:block number = %d", csd->blockNumber );
+
+ return MCD_ERR_NO;
+}
+
+// =============================================================================
+// FUNCTIONS (public)
+// =============================================================================
+
+// =============================================================================
+// mcd_ReadCsd
+// -----------------------------------------------------------------------------
+/// @brief Read the MMC CSD register
+/// @param mcdCsd Pointer to the structure where the MMC CSD register info
+/// are going to be written.
+// =============================================================================
+static MCD_ERR_T mcd_ReadCsd(MCD_CSD_T* mcdCsd)
+{
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+ u32 response[4];
+
+ // Get card CSD
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SEND_CSD, g_mcdRcaReg, response, FALSE);
+ if (errStatus == MCD_ERR_NO)
+ {
+ errStatus = mcd_SdmmcInitCsd(mcdCsd, response);
+ }
+
+ // Store it localy
+ // FIXME: Is this real ? cf Physical Spec version 2
+ // page 59 (72) about CMD16 : default block length
+ // is 512 bytes. Isn't 512 bytes a good block
+ // length ? And I quote : "In both cases, if block length is set larger
+ // than 512Bytes, the card sets the BLOCK_LEN_ERROR bit."
+ g_mcdBlockLen = (1 << mcdCsd->readBlLen);
+ //if (g_mcdBlockLen > 512)
+ //{
+ g_mcdBlockLen = 512;
+ //}
+ //g_mcdNbBlock = mcdCsd->blockNumber * ((1 << mcdCsd->readBlLen)/g_mcdBlockLen);
+ g_mcdNbBlock = mcdCsd->blockNumber * ((1 << mcdCsd->readBlLen)>>9);
+
+ return errStatus;
+}
+
+
+// =============================================================================
+// mcd_Open
+// -----------------------------------------------------------------------------
+/// @brief Open the SD-MMC chip
+/// This function does the init process of the MMC chip, including reseting
+/// the chip, sending a command of init to MMC, and reading the CSD
+/// configurations.
+///
+/// @param mcdCsd Pointer to the structure where the MMC CSD register info
+/// are going to be written.
+///
+/// @param mcdVer is t card version.
+// =============================================================================
+MCD_ERR_T mcd_Open(MCD_CSD_T* mcdCsd, MCD_CARD_VER mcdVer)
+{
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+ u32 response[4] = {0, 0, 0, 0};
+ MCD_CARD_STATUS_T cardStatus = {0};
+ BOOL isMmc = FALSE;
+ HAL_SDMMC_DATA_BUS_WIDTH_T dataBusWidth;
+ u32 startTime = 0;
+
+ //MCD_PROFILE_FUNCTION_ENTER(mcd_Open);
+ MCD_TRACE(MCD_INFO_TRC, 0, "mcd_Open starts ...");
+
+#if 0
+ // Check concurrency. Only one mcd_Open.
+ u32 cs = hal_SysEnterCriticalSection();
+ if (g_mcdSemaphore == 0xFF)
+ {
+ // Create semaphore and go on with the driver.
+
+ // NOTE: This semaphore is never deleted for now, as
+ // 1. sema deletion while other task is waiting for it will cause an error;
+ // 2. sema deletion will make trouble if already-open state is considered OK.
+ g_mcdSemaphore = sxr_NewSemaphore(1);
+ }
+ hal_SysExitCriticalSection(cs);
+
+ // Following operation should be kept protected
+ MCD_CS_ENTER;
+
+ if(NULL == g_mcdConfig)
+ {
+ g_mcdConfig = tgt_GetMcdConfig();
+ }
+
+ if(g_mcdConfig->cardDetectGpio != HAL_GPIO_NONE)
+ {
+ // Only if GPIO for detection exists, else we try to open anyway.
+ if(!mcd_CardDetectUpdateStatus())
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "mcd_Open: GPIO detection - no card");
+ MCD_CS_EXIT;
+ MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR_NO_CARD;
+ }
+ }
+
+ if (g_mcdStatus == MCD_STATUS_OPEN)
+ {
+ // Already opened, just return OK
+ MCD_TRACE(MCD_INFO_TRC, 0, "mcd_Open: Already opened");
+ *mcdCsd = g_mcdLatestCsd;
+
+ MCD_CS_EXIT;
+ MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR_NO;
+ }
+#endif
+
+ //printf("mcd_Open\n");
+
+ if(MCD_CARD_V1 == mcdVer)
+ {
+ hal_SdmmcOpen(0x05);
+ }
+ else
+ {
+ hal_SdmmcOpen(0x0);
+ }
+
+ ///@todo: should use g_mcdConfig->dat3HasPullDown if true, we can handle
+ /// basic card detection as follows:
+ /// call hal_SdmmcGetCardDetectPinLevel() to check if card as changed (back 1)
+ /// send command ACMD42 to disable internal pull up (so pin goes to 0)
+ /// meaning if pin is 1, there was a removal, file system should be told to
+ /// remount the card as it might be a different one (or the content might have
+ /// been changed externally).
+
+ // RCA = 0x0000 for card identification phase.
+ g_mcdRcaReg = 0;
+
+ g_mcdSdmmcFreq = 200000;
+ hal_SdmmcSetClk(g_mcdSdmmcFreq);
+
+ // assume it's not an SDHC card
+ g_mcdCardIsSdhc = FALSE;
+ g_mcdOcrReg = MCD_SDMMC_OCR_VOLT_PROF_MASK;
+
+ //printf("CMD0\n");
+
+ // Send Power On command
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_GO_IDLE_STATE, BROAD_ADDR, response, FALSE);
+ if (MCD_ERR_NO != errStatus)
+ {
+ printf("CMD0 Error\n");
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Power on, Initialize Failed");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Error Status = %d", errStatus);
+ hal_SdmmcClose();
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+ else
+ {
+ //printf("CMD0 Done\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Card successfully in Idle state");
+ }
+
+ //printf("CMD8\n");
+
+ // Check if the card is a spec vers.2 one
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SEND_IF_COND, (MCD_CMD8_VOLTAGE_SEL | MCD_CMD8_CHECK_PATTERN), response, FALSE);
+ if (MCD_ERR_NO != errStatus)
+ {
+ printf("CMD8 Error\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "This card doesn't comply to the spec version 2.0");
+ }
+ else
+ {
+ // This card comply to the SD spec version 2. Is it compatible with the
+ // proposed voltage, and is it an high capacity one ?
+ if (response[0] != (MCD_CMD8_VOLTAGE_SEL | MCD_CMD8_CHECK_PATTERN))
+ {
+ printf("CMD8, Not Supported\n");
+ // Bad pattern or unsupported voltage.
+ MCD_TRACE(MCD_INFO_TRC, 0, "Bad pattern or unsupported voltage");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+ //g_mcdVer = MCD_CARD_V1;
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR_UNUSABLE_CARD;
+ }
+ else
+ {
+ //printf("CMD8, Supported\n");
+ g_mcdOcrReg |= MCD_OCR_HCS;
+ }
+ }
+
+ // TODO HCS mask bit to ACMD 41 if high capacity
+ // Send OCR, as long as the card return busy
+ startTime = hal_TimGetUpTime();
+
+ while(1)
+ {
+ if(hal_TimGetUpTime() - startTime > MCD_SDMMC_OCR_TIMEOUT )
+ {
+ printf("ACMD41, Retry Timeout\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "SD OCR timeout");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR;
+ }
+
+ //printf("ACMD41\n");
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SEND_OP_COND, g_mcdOcrReg, response, FALSE);
+ if (errStatus == MCD_ERR_CARD_NO_RESPONSE)
+ {
+ printf("ACMD41, No Response, MMC Card?\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Inserted Card is a MMC");
+ // CMD 55 was not recognised: this is not an SD card !
+ isMmc = TRUE;
+ break;
+ }
+
+ // Bit 31 is power up busy status bit(pdf spec p. 109)
+ g_mcdOcrReg = (response[0] & 0x7fffffff);
+
+ // Power up busy bit is set to 1,
+ // we can continue ...
+ if (response[0] & 0x80000000)
+ {
+ //printf("ACMD41, PowerUp done\n");
+ // Version 2?
+ if((g_mcdOcrReg & MCD_OCR_HCS) == MCD_OCR_HCS)
+ {
+ // Card is V2: check for high capacity
+ if (response[0] & MCD_OCR_CCS_MASK)
+ {
+ g_mcdCardIsSdhc = TRUE;
+ printf("Card is SDHC\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Card is SDHC");
+ }
+ else
+ {
+ g_mcdCardIsSdhc = FALSE;
+ printf("Card is V2, but NOT SDHC\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Card is standard capacity SD");
+ }
+ }
+ else {
+ printf("Card is NOT V2\n");
+ }
+ MCD_TRACE(MCD_INFO_TRC, 0, "Inserted Card is a SD card");
+ break;
+ }
+ }
+
+ if (isMmc)
+ {
+ printf("MMC Card\n");
+ while(1)
+ {
+ if(hal_TimGetUpTime() - startTime > MCD_SDMMC_OCR_TIMEOUT )
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "MMC OCR timeout");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR;
+ }
+
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_MMC_SEND_OP_COND, g_mcdOcrReg, response, FALSE);
+ if (errStatus == MCD_ERR_CARD_NO_RESPONSE)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "MMC OCR didn't answer");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR;
+ }
+
+ // Bit 31 is power up busy status bit(pdf spec p. 109)
+ g_mcdOcrReg = (response[0] & 0x7fffffff);
+
+ // Power up busy bit is set to 1,
+ // we can continue ...
+ if (response[0] & 0x80000000)
+ {
+ break;
+ }
+ }
+
+ }
+
+ //printf("CMD2\n");
+
+ // Get the CID of the card.
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_ALL_SEND_CID, BROAD_ADDR, response, FALSE);
+ if(MCD_ERR_NO != errStatus)
+ {
+ printf("CMD2 Fail\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Get CID, Initialize Failed");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+ mcd_SdmmcInitCid(response);
+
+ //printf("CMD3\n");
+
+ // Get card RCA
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SEND_RELATIVE_ADDR, BROAD_ADDR, response, FALSE);
+ if (MCD_ERR_NO != errStatus)
+ {
+ printf("CMD3 Fail\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Get Relative Address, Initialize Failed");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+
+ // Spec Ver 2 pdf p. 81 - rca are the 16 upper bit of this
+ // R6 answer. (lower bits are stuff bits)
+ g_mcdRcaReg = response[0] & 0xffff0000;
+
+ //printf("CMD9\n");
+ // Get card CSD
+ errStatus = mcd_ReadCsd(mcdCsd);
+ if (errStatus != MCD_ERR_NO)
+ {
+ printf("CMD9 Fail\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Get CSD, Initialize Failed");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+
+ // If possible, set the DSR
+ if (mcdCsd->dsrImp)
+ {
+ //printf("CMD4\n");
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SET_DSR, g_mcdSdmmcDsr, response, FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ printf("CMD4 Fail\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Set DSR, Initialize Failed");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+ }
+
+ //printf("CMD7\n");
+ // Select the card
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SELECT_CARD, g_mcdRcaReg, response, FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ printf("CMD7 Fail\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Select Card, Initialize Failed");
+ MCD_TRACE(MCD_INFO_TRC, 0, "errStatus = %d", errStatus);
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+ // That command returns the card status, we check we're in data mode.
+ cardStatus.reg = response[0];
+
+ if(!(cardStatus.fields.readyForData))
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "CMD7 status=%0x", cardStatus.reg);
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR;
+ }
+
+ // Set the bus width (use 4 data lines for SD cards only)
+ if (isMmc)
+ {
+ dataBusWidth = HAL_SDMMC_DATA_BUS_WIDTH_1;
+ }
+ else
+ {
+
+ dataBusWidth = MCD_CARD_V1 == mcdVer ? HAL_SDMMC_DATA_BUS_WIDTH_1 : HAL_SDMMC_DATA_BUS_WIDTH_4;
+
+ }
+
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SET_BUS_WIDTH, dataBusWidth,
+ response, FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Set Bus, Initialize Failed");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+
+ // That command returns the card status, in tran state ?
+ cardStatus.reg = response[0];
+ if ( !(cardStatus.fields.appCmd)
+ || !(cardStatus.fields.readyForData)
+ || (cardStatus.fields.currentState != MCD_CARD_STATE_TRAN))
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "ACMD6 status=%0x", cardStatus.reg);
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR;
+ }
+
+ // Configure the controller to use that many lines.
+ hal_SdmmcSetDataWidth(dataBusWidth);
+
+ // Configure the block lenght
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SET_BLOCKLEN, g_mcdBlockLen, response, FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Because Set Block Length, Initialize Failed");
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return errStatus;
+ }
+
+ // That command returns the card status, in tran state ?
+ cardStatus.reg = response[0];
+ {
+ MCD_CARD_STATUS_T expectedCardStatus;
+ expectedCardStatus.reg = 0;
+ expectedCardStatus.fields.readyForData = 1;
+ expectedCardStatus.fields.currentState = MCD_CARD_STATE_TRAN;
+
+ if (cardStatus.reg != expectedCardStatus.reg)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "CMD16 status=%0x", cardStatus.reg);
+ hal_SdmmcClose();
+ g_mcdStatus = MCD_STATUS_NOTPRESENT;
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR;
+ }
+ }
+
+ hal_SdmmcEnterDataTransferMode();
+
+
+ // Set the clock of the SD bus for the fastest possible rate.
+
+ g_mcdSdmmcFreq = MCD_CARD_V1 == mcdVer ? 6000000 : 20000000;
+
+ hal_SdmmcSetClk(g_mcdSdmmcFreq);
+
+ g_mcdLatestCsd = *mcdCsd;
+ g_mcdStatus = MCD_STATUS_OPEN;
+ hal_SdmmcSleep();
+ g_mcdVer = mcdVer;
+
+ //printf("mcd_Open Done\n");
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Open);
+ return MCD_ERR_NO;
+}
+
+// =============================================================================
+// mcd_Close
+// -----------------------------------------------------------------------------
+/// Close MCD.
+///
+/// To be called at the end of the operations
+/// @return MCD_ERR_NO if a response with a good crc was received,
+/// MCD_ERR_CARD_NO_RESPONSE if no reponse was received within the
+/// driver configured timeout.
+/// MCD_ERR_CARD_RESPONSE_BAD_CRC if the received response presented
+/// a bad CRC.
+/// MCD_ERR_CARD_TIMEOUT if the card timedout during procedure.
+// =============================================================================
+MCD_ERR_T mcd_Close(void)
+{
+ MCD_TRACE(MCD_INFO_TRC, 0, "mcd_Close");
+
+ if (g_mcdSemaphore == 0xFF)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "mcd_Close: Never opened before");
+ return MCD_ERR_NO;
+ }
+
+ //MCD_CS_ENTER;
+
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+
+ //MCD_PROFILE_FUNCTION_ENTER(mcd_Close);
+
+ // Don't close the MCD driver if a transfer is in progress,
+ // and be definitive about it:
+ if (hal_SdmmcIsBusy() == TRUE)
+ {
+ MCD_TRACE(MCD_WARN_TRC, 0, "MCD: Attempt to close MCD while a transfer is in progress");
+ }
+
+ hal_SdmmcWakeUp();
+
+ // Brutal force stop current transfer, if any.
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+
+ // Close the SDMMC module
+ hal_SdmmcClose();
+
+ g_mcdStatus = MCD_STATUS_NOTOPEN_PRESENT; // without GPIO
+ //mcd_CardDetectUpdateStatus(); // Test GPIO for card detect
+
+ //MCD_CS_EXIT;
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Close);
+ return errStatus;
+}
+
+//=============================================================================
+// mcd_SdmmcTranState
+//-----------------------------------------------------------------------------
+/// Blocking function checking that the card is in the transfer state,
+/// acknowledging thus that, for example, end of transmission.
+/// @param iter Number of attempt to be made.
+/// @param duration Sleeping time before the next attempt (in sys ticks).
+//=============================================================================
+static MCD_ERR_T mcd_SdmmcTranState(u32 iter)
+{
+ u32 cardResponse[4] = {0, 0, 0, 0};
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+ MCD_CARD_STATUS_T cardStatusTranState;
+ // Using reg to clear all bit of the bitfields that are not
+ // explicitly set.
+ cardStatusTranState.reg = 0;
+ cardStatusTranState.fields.readyForData = 1;
+ cardStatusTranState.fields.currentState = MCD_CARD_STATE_TRAN;
+ u32 startTime = hal_TimGetUpTime();
+ u32 time_out;
+
+ while(1)
+ {
+ //printf("CMD13, Set Trans State\n");
+
+ errStatus = mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SEND_STATUS, g_mcdRcaReg, cardResponse, FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ printf("CMD13, Fail\n");
+ // error while sending the command
+ MCD_TRACE(MCD_INFO_TRC, 0, "Sd Tran Read Aft State error! err nb:%d", errStatus);
+ return MCD_ERR;
+ }
+ else if (cardResponse[0] == cardStatusTranState.reg)
+ {
+ //printf("CMD13, Done\n");
+ // the status is the expected one - success
+ return MCD_ERR_NO;
+ }
+ else
+ {
+ // try again
+ // check for timeout
+ time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_CMD_TIMEOUT_V1:MCD_CMD_TIMEOUT_V2;
+ if (hal_TimGetUpTime() - startTime > time_out )
+ {
+ printf("CMD13, Timeout\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Sd Tran don't finished");
+ MCD_TRACE(MCD_INFO_TRC, 0, "current state =%0x", cardResponse[0]);
+ return MCD_ERR;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// mcd_SdmmcMultBlockWrite
+//-----------------------------------------------------------------------------
+/// Write one or a bunch of data blocks.
+/// @param blockAddr Address on the card where to write data.
+/// @param pWrite Pointer towards the buffer of data to write.
+/// @param blockNum Number of blocks to write.
+//=============================================================================
+static MCD_ERR_T mcd_SdmmcMultBlockWrite(u8* blockAddr, u8* pWrite, u32 blockNum)
+{
+ u32 cardResponse[4] = {0, 0, 0, 0};
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+ MCD_CARD_STATUS_T cardStatusTranState;
+ // Using reg to clear all bit of the bitfields that are not
+ // explicitly set.
+ cardStatusTranState.reg = 0;
+ cardStatusTranState.fields.readyForData = 1;
+ cardStatusTranState.fields.currentState = MCD_CARD_STATE_TRAN;
+
+ MCD_CARD_STATUS_T cardStatusPreErasedState;
+ cardStatusPreErasedState.reg = 0;
+ cardStatusPreErasedState.fields.appCmd = 1;
+ cardStatusPreErasedState.fields.readyForData = 1;
+ cardStatusPreErasedState.fields.currentState = MCD_CARD_STATE_TRAN;
+
+ MCD_CARD_STATUS_T cardStatusResponse = {0,};
+
+ u32 startTime = 0;
+
+ HAL_SDMMC_CMD_T writeCmd;
+ u32 tran_time_out;
+ u32 write_time_out;
+
+ g_mcdSdmmcTransfer.sysMemAddr = (u8*) pWrite;
+ g_mcdSdmmcTransfer.sdCardAddr = blockAddr;
+ g_mcdSdmmcTransfer.blockNum = blockNum;
+ g_mcdSdmmcTransfer.blockSize = g_mcdBlockLen;
+ g_mcdSdmmcTransfer.direction = HAL_SDMMC_DIRECTION_WRITE;
+
+
+// FIXME find what the heck is that !:
+// program_right_num=0;
+
+ // Check buffer.
+ assert(pWrite != NULL, "SDMMC write: Buffer is NULL");
+ assert(((u32)pWrite & 0x3) ==0,
+ "SDMMC write: buffer is not aligned! addr=%08x", pWrite);
+ assert(blockNum>=1 && blockNum<= MCD_MAX_BLOCK_NUM,
+ "Block Num is overflow");
+
+ // Check that the card is in tran (Transmission) state.
+ tran_time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_TRAN_TIMEOUT_V1:MCD_TRAN_TIMEOUT_V2;
+ if (MCD_ERR_NO != mcd_SdmmcTranState(tran_time_out))
+ // 5200000, 0, initially, that is to say rougly 0.1 sec ?
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Write on Sdmmc while not in Tran state");
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+
+ // The command for single block or multiple blocks are differents
+ if (blockNum == 1)
+ {
+ writeCmd = HAL_SDMMC_CMD_WRITE_SINGLE_BLOCK;
+ }
+ else
+ {
+ writeCmd = HAL_SDMMC_CMD_WRITE_MULT_BLOCK;
+ }
+
+ // PreErasing, to accelerate the multi-block write operations
+ if (blockNum >1)
+ {
+ //printf("SET_WR_BLK_COUNT\n");
+ if(MCD_ERR == mcd_SdmmcSendCmd(HAL_SDMMC_CMD_SET_WR_BLK_COUNT, blockNum, cardResponse, FALSE))
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Set Pre-erase Failed");
+ return MCD_ERR;
+ }
+
+ // Advance compatibility,to support 1.0 t-flash.
+ if (cardResponse[0] != cardStatusPreErasedState.reg)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "warning ACMD23 status=%0x", cardResponse[0]);
+ // cardResponse[0] = cardStatusPreErasedState.reg;
+ // return MCD_ERR;
+ }
+ }
+
+
+ // Initiate data migration through Ifc.
+ if (hal_SdmmcTransfer(&g_mcdSdmmcTransfer) != 0)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "write sd no ifc channel");
+ return MCD_ERR_DMA_BUSY;
+ }
+
+ //printf("writeCmd\n");
+
+ // Initiate data migration of multiple blocks through SD bus.
+ errStatus = mcd_SdmmcSendCmd(writeCmd,
+ (u32)blockAddr,
+ cardResponse,
+ FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ printf("writeCmd Fail, err = %d\n", (int)errStatus);
+ MCD_TRACE(MCD_INFO_TRC, 0, "Set sd write had error");
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+ return MCD_ERR_CMD;
+ }
+
+ cardStatusResponse.reg = cardResponse[0] ;
+ // Check for error, by listing all valid states
+ // TODO: FIXME, more states could be legal here
+ // The sixteen uper bits are error bits: they all must be null
+ // (Physical Spec V.2, p71)
+ if ((cardResponse[0] != cardStatusTranState.reg)
+ && !((cardStatusResponse.fields.readyForData == 1)
+ && (cardStatusResponse.fields.currentState == MCD_CARD_STATE_RCV)
+ && ((cardStatusResponse.reg&0xFFFF0000) == 0))
+ && !(cardStatusResponse.fields.currentState == MCD_CARD_STATE_RCV
+ && ((cardStatusResponse.reg&0xFFFF0000) == 0))
+ && !(cardStatusResponse.fields.currentState == MCD_CARD_STATE_PRG
+ && ((cardStatusResponse.reg&0xFFFF0000) == 0))
+ )
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Write block,Card Reponse: %x", cardResponse[0]);
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+ return MCD_ERR;
+ }
+
+
+ // Wait
+ startTime = hal_TimGetUpTime();
+ write_time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_WRITE_TIMEOUT_V1:MCD_WRITE_TIMEOUT_V2;
+ while(!hal_SdmmcTransferDone())
+ {
+ if (hal_TimGetUpTime() - startTime > (write_time_out*blockNum))
+ {
+ printf("writeCmd Timeout\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Write on Sdmmc timeout");
+ // Abort the transfert.
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+ }
+ //printf("writeCmd Done\n");
+
+
+ // Nota: CMD12 (stop transfer) is automatically
+ // sent by the SDMMC controller.
+
+ if (mcd_SdmmcWriteCheckCrc() != MCD_ERR_NO)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "SDMMC Write error");
+ return MCD_ERR;
+ }
+ //printf("CheckCRC Done\n");
+
+ // Check that the card is in tran (Transmission) state.
+ if (MCD_ERR_NO != mcd_SdmmcTranState(tran_time_out))
+ // 5200000, 0, initially, that is to say rougly 0.1 sec ?
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "Write on Sdmmc while not in Tran state");
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+ //printf("Check Tran State Done\n");
+ return MCD_ERR_NO;
+
+}
+
+//=============================================================================
+// mcd_SdmmcMultBlockRead
+//-----------------------------------------------------------------------------
+/// Read one or a bunch of data blocks.
+/// @param blockAddr Address on the card where to read data.
+/// @param pRead Pointer towards the buffer of data to read.
+/// @param blockNum Number of blocks to read.
+//=============================================================================
+static MCD_ERR_T mcd_SdmmcMultBlockRead(u8* blockAddr, u8* pRead, u32 blockNum)
+{
+ u32 cardResponse[4] = {0, 0, 0, 0};
+ MCD_ERR_T errStatus = MCD_ERR_NO;
+ HAL_SDMMC_CMD_T readCmd;
+ MCD_CARD_STATUS_T cardStatusTranState;
+ // Using reg to clear all bit of the bitfields that are not
+ // explicitly set.
+ cardStatusTranState.reg = 0;
+ cardStatusTranState.fields.readyForData = 1;
+ cardStatusTranState.fields.currentState = MCD_CARD_STATE_TRAN;
+
+ u32 startTime=0;
+ u32 tran_time_out;
+ u32 read_time_out;
+
+ g_mcdSdmmcTransfer.sysMemAddr = (u8*) pRead;
+ g_mcdSdmmcTransfer.sdCardAddr = blockAddr;
+ g_mcdSdmmcTransfer.blockNum = blockNum;
+ g_mcdSdmmcTransfer.blockSize = g_mcdBlockLen;
+ g_mcdSdmmcTransfer.direction = HAL_SDMMC_DIRECTION_READ;
+
+ // Check buffer.
+ assert(pRead != NULL, "SDMMC write: Buffer is NULL");
+ assert(((u32)pRead & 0x3) ==0, "SDMMC write: buffer is not aligned");
+ assert(blockNum>=1 && blockNum<= MCD_MAX_BLOCK_NUM,
+ "Block Num is overflow");
+
+ // Command are different for reading one or several blocks of data
+ if (blockNum == 1)
+ {
+ readCmd = HAL_SDMMC_CMD_READ_SINGLE_BLOCK;
+ }
+ else
+ {
+ readCmd = HAL_SDMMC_CMD_READ_MULT_BLOCK;
+ }
+
+ // Initiate data migration through Ifc.
+ if (hal_SdmmcTransfer(&g_mcdSdmmcTransfer) != 0)
+ {
+ MCD_TRACE(MCD_INFO_TRC, 0, "write sd no ifc channel");
+ return MCD_ERR_DMA_BUSY;
+ }
+
+ // Initiate data migration of multiple blocks through SD bus.
+ errStatus = mcd_SdmmcSendCmd(readCmd,
+ (u32)blockAddr,
+ cardResponse,
+ FALSE);
+ if (errStatus != MCD_ERR_NO)
+ {
+ printf("mcd_SdmmcMultBlockRead, send command error\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Set sd write had error");
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+ return MCD_ERR_CMD;
+ }
+
+ if (cardResponse[0] != cardStatusTranState.reg)
+ {
+ printf("mcd_SdmmcMultBlockRead, command response error\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "CMD%d status=%0x", cardResponse[0], readCmd);
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+ return MCD_ERR;
+ }
+
+ // Wait
+ startTime = hal_TimGetUpTime();
+ read_time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_READ_TIMEOUT_V1:MCD_READ_TIMEOUT_V2;
+ while(!hal_SdmmcTransferDone())
+ {
+ if (hal_TimGetUpTime() - startTime > (read_time_out*blockNum))
+ {
+ printf("mcd_SdmmcMultBlockRead, timeout\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Read on Sdmmc timeout");
+ // Abort the transfert.
+ hal_SdmmcStopTransfer(&g_mcdSdmmcTransfer);
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+ }
+
+ // Nota: CMD12 (stop transfer) is automatically
+ // sent by the SDMMC controller.
+
+ if (mcd_SdmmcReadCheckCrc() != MCD_ERR_NO)
+ {
+ printf("mcd_SdmmcMultBlockRead, crc error\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "sdc read state error");
+ return MCD_ERR;
+ }
+
+ tran_time_out = (MCD_CARD_V1 == g_mcdVer) ? MCD_TRAN_TIMEOUT_V1:MCD_TRAN_TIMEOUT_V2;
+ // Check that the card is in tran (Transmission) state.
+ if (MCD_ERR_NO != mcd_SdmmcTranState(tran_time_out))
+ // 5200000, 0, initially, that is to say rougly 0.1 sec ?
+ {
+ printf("mcd_SdmmcMultBlockRead, trans state timeout\n");
+ MCD_TRACE(MCD_INFO_TRC, 0, "Read on Sdmmc while not in Tran state");
+ return MCD_ERR_CARD_TIMEOUT;
+ }
+
+ // Flush Cache
+ //hal_SysInvalidateCache(pRead, blockNum * g_mcdBlockLen);
+
+ return MCD_ERR_NO;
+}
+
+// =============================================================================
+// mcd_Write
+// -----------------------------------------------------------------------------
+/// @brief Write a block of data to MMC.
+///
+/// This function is used to write blocks of data on the MMC.
+/// @param startAddr Start Adress of the SD memory block where the
+/// data will be written
+/// @param blockWr Pointer to the block of data to write. Must be aligned
+/// on a 32 bits boundary.
+/// @param size Number of bytes to write. Must be an interger multiple of the
+/// sector size of the card
+// =============================================================================
+MCD_ERR_T mcd_Write(u32 startAddr, u8* blockWr, u32 size)
+{
+ //MCD_CS_ENTER;
+ u8* tmpAdd = NULL;
+ MCD_ERR_T value = MCD_ERR_NO;
+ u32 i = 0;
+
+ //MCD_PROFILE_FUNCTION_ENTER(mcd_Write);
+ assert(g_mcdBlockLen != 0, "mcd_Write called before a successful mcd_Open");
+ assert(startAddr % g_mcdBlockLen == 0,
+ "write card address is not aligned");
+ assert((size % g_mcdBlockLen) == 0, "mcd_Write size (%d) is not a multiple of"
+ "the block size (%d)",
+ size, g_mcdBlockLen);
+
+ //printf("mcd_Write\n");
+
+ //if(!mcd_CardDetectUpdateStatus())
+ //{
+ // MCD_PROFILE_FUNCTION_EXIT(mcd_Write);
+ // MCD_CS_EXIT;
+ // return MCD_ERR_NO_CARD;
+ //}
+
+ hal_SdmmcWakeUp();
+ // Addresses are block number for high capacity card
+ if (g_mcdCardIsSdhc)
+ {
+ tmpAdd = (u8*) (startAddr);
+ }
+ else
+ {
+ tmpAdd = (u8*) (startAddr * g_mcdBlockLen);
+ }
+ if(MCD_CARD_V1 == g_mcdVer)
+ {
+ //printf("mcd_Write, V1\n");
+
+ //for(i = 0; i < size/g_mcdBlockLen; i++)
+ for(i = 0; i < size>>9; i++)
+ {
+ value = mcd_SdmmcMultBlockWrite(tmpAdd + i*g_mcdBlockLen, blockWr + i*g_mcdBlockLen, 1);
+ if(value != MCD_ERR_NO)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ //printf("mcd_Write, V2\n");
+ //value = mcd_SdmmcMultBlockWrite(tmpAdd, blockWr, size/g_mcdBlockLen);
+ value = mcd_SdmmcMultBlockWrite(tmpAdd, blockWr, size>>9);
+ }
+
+ hal_SdmmcSleep();
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Write);
+ //MCD_CS_EXIT;
+ return value;
+}
+
+// =============================================================================
+// mcd_Read
+// -----------------------------------------------------------------------------
+/// @brief Read using pattern mode.
+/// @ param startAddr: of the SD memory block where the data
+/// will be read
+/// @param blockRd Pointer to the buffer where the data will be stored. Must be aligned
+/// on a 32 bits boundary.
+/// @param size Number of bytes to read. Must be an interger multiple of the
+/// sector size of the card.
+// =============================================================================
+MCD_ERR_T mcd_Read(u32 startAddr, u8* blockRd, u32 size)
+{
+ //MCD_CS_ENTER;
+ u8* tmpAdd = NULL;
+ MCD_ERR_T value = MCD_ERR_NO;
+
+ //MCD_PROFILE_FUNCTION_ENTER(mcd_Read);
+ assert(g_mcdBlockLen != 0, "mcd_Read called before a successful mcd_Open_V1 or mcd_Open_V2");
+ assert(startAddr % g_mcdBlockLen == 0,
+ "read card address is not aligned");
+ assert((size % g_mcdBlockLen) == 0, "mcd_Read size (%d) is not a multiple of"
+ "the block size (%d)",
+ size, g_mcdBlockLen);
+
+ //printf("mcd_Read\n");
+
+ //if(!mcd_CardDetectUpdateStatus())
+ //{
+ // MCD_PROFILE_FUNCTION_EXIT(mcd_Read);
+ // MCD_CS_EXIT;
+ // return MCD_ERR_NO_CARD;
+ //}
+
+ hal_SdmmcWakeUp();
+ // Addresses are block number for high capacity card
+ if (g_mcdCardIsSdhc)
+ {
+ tmpAdd = (u8*) (startAddr);
+ }
+ else
+ {
+ tmpAdd = (u8*) (startAddr * g_mcdBlockLen);
+ }
+
+ //value = mcd_SdmmcMultBlockRead(tmpAdd, blockRd, size/g_mcdBlockLen);
+ value = mcd_SdmmcMultBlockRead(tmpAdd, blockRd, size >> 9);
+
+ hal_SdmmcSleep();
+
+ //MCD_PROFILE_FUNCTION_EXIT(mcd_Read);
+ //MCD_CS_EXIT;
+ return value;
+
+}
+
+BOOL mcd_IsHighCapacityCard(void)
+{
+ if(g_mcdCardIsSdhc == TRUE)
+ {
+ return TRUE;
+ }
+ return FALSE;
+
+}
+
+static block_dev_desc_t mmc_blk_dev;
+static int initialized = 0;
+
+int mmc_set_dev(int dev_num)
+{
+ return 0;
+}
+
+block_dev_desc_t *mmc_get_dev(int dev)
+{
+ if (initialized)
+ return (block_dev_desc_t *) &mmc_blk_dev;
+ else
+ return NULL;
+}
+
+static unsigned long mmc_bread(int dev_num, unsigned long blknr,
+ lbaint_t blkcnt, void *dst)
+{
+ MCD_ERR_T mcd_ret;
+ int block_size, i;
+
+ block_size = 512;
+
+ printf("\nmmc_bread, %ld blks from %ld, dst = 0x%08lx\n",
+ (u32)blkcnt, blknr, (u32)dst);
+ for (i=0;i<blkcnt;i++) {
+ mcd_ret = mcd_Read(blknr + i,
+ (u8 *)(dst) + i*block_size,
+ block_size);
+ if (mcd_ret != MCD_ERR_NO) {
+ printf("Read SD card Failed\n");
+ return i;
+ }
+ }
+ printf("Done\n");
+
+ return i;
+}
+
+int mmc_legacy_init(int dev)
+{
+ MCD_ERR_T mcd_ret;
+ MCD_CSD_T mcd_csd;
+
+ mcd_ret = mcd_Open(&mcd_csd, MCD_CARD_V2);
+ if (mcd_ret != MCD_ERR_NO) {
+ printf("Open SD card Failed\n");
+ return 1;
+ }
+
+ printf("Open SD card Done\n");
+
+ mmc_blk_dev.if_type = IF_TYPE_MMC;
+ mmc_blk_dev.part_type = PART_TYPE_DOS;
+ mmc_blk_dev.dev = 0;
+ mmc_blk_dev.lun = 0;
+ mmc_blk_dev.type = 0;
+
+ /* FIXME fill in the correct size (is set to 32MByte) */
+ mmc_blk_dev.blksz = 512;
+ mmc_blk_dev.lba = 0x10000;
+ mmc_blk_dev.removable = 0;
+ mmc_blk_dev.block_read = mmc_bread;
+
+#if 0
+ if (fat_register_device(&mmc_blk_dev, 1))
+ printf("Could not register MMC fat device\n");
+#else
+ init_part(&mmc_blk_dev);
+#endif
+
+ initialized = 1;
+ return 0;
+}
+
diff --git a/drivers/mmc/s5p_mmc.c b/drivers/mmc/s5p_mmc.c
deleted file mode 100644
index 4ae3aaf773..0000000000
--- a/drivers/mmc/s5p_mmc.c
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * (C) Copyright 2009 SAMSUNG Electronics
- * Minkyu Kang <mk7.kang@samsung.com>
- * Jaehoon Chung <jh80.chung@samsung.com>
- *
- * 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 <common.h>
-#include <mmc.h>
-#include <asm/io.h>
-#include <asm/arch/mmc.h>
-#include <asm/arch/clk.h>
-
-/* support 4 mmc hosts */
-struct mmc mmc_dev[4];
-struct mmc_host mmc_host[4];
-
-static inline struct s5p_mmc *s5p_get_base_mmc(int dev_index)
-{
- unsigned long offset = dev_index * sizeof(struct s5p_mmc);
- return (struct s5p_mmc *)(samsung_get_base_mmc() + offset);
-}
-
-static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
-{
- unsigned char ctrl;
-
- debug("data->dest: %08x\n", (u32)data->dest);
- writel((u32)data->dest, &host->reg->sysad);
- /*
- * DMASEL[4:3]
- * 00 = Selects SDMA
- * 01 = Reserved
- * 10 = Selects 32-bit Address ADMA2
- * 11 = Selects 64-bit Address ADMA2
- */
- ctrl = readb(&host->reg->hostctl);
- ctrl &= ~(3 << 3);
- writeb(ctrl, &host->reg->hostctl);
-
- /* We do not handle DMA boundaries, so set it to max (512 KiB) */
- writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize);
- writew(data->blocks, &host->reg->blkcnt);
-}
-
-static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)
-{
- unsigned short mode;
-
- /*
- * TRNMOD
- * MUL1SIN0[5] : Multi/Single Block Select
- * RD1WT0[4] : Data Transfer Direction Select
- * 1 = read
- * 0 = write
- * ENACMD12[2] : Auto CMD12 Enable
- * ENBLKCNT[1] : Block Count Enable
- * ENDMA[0] : DMA Enable
- */
- mode = (1 << 1) | (1 << 0);
- if (data->blocks > 1)
- mode |= (1 << 5);
- if (data->flags & MMC_DATA_READ)
- mode |= (1 << 4);
-
- writew(mode, &host->reg->trnmod);
-}
-
-static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
- struct mmc_data *data)
-{
- struct mmc_host *host = (struct mmc_host *)mmc->priv;
- int flags, i;
- unsigned int timeout;
- unsigned int mask;
- unsigned int retry = 0x100000;
-
- /* Wait max 10 ms */
- timeout = 10;
-
- /*
- * PRNSTS
- * CMDINHDAT[1] : Command Inhibit (DAT)
- * CMDINHCMD[0] : Command Inhibit (CMD)
- */
- mask = (1 << 0);
- if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY))
- mask |= (1 << 1);
-
- /*
- * We shouldn't wait for data inihibit for stop commands, even
- * though they might use busy signaling
- */
- if (data)
- mask &= ~(1 << 1);
-
- while (readl(&host->reg->prnsts) & mask) {
- if (timeout == 0) {
- printf("%s: timeout error\n", __func__);
- return -1;
- }
- timeout--;
- udelay(1000);
- }
-
- if (data)
- mmc_prepare_data(host, data);
-
- debug("cmd->arg: %08x\n", cmd->cmdarg);
- writel(cmd->cmdarg, &host->reg->argument);
-
- if (data)
- mmc_set_transfer_mode(host, data);
-
- if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
- return -1;
-
- /*
- * CMDREG
- * CMDIDX[13:8] : Command index
- * DATAPRNT[5] : Data Present Select
- * ENCMDIDX[4] : Command Index Check Enable
- * ENCMDCRC[3] : Command CRC Check Enable
- * RSPTYP[1:0]
- * 00 = No Response
- * 01 = Length 136
- * 10 = Length 48
- * 11 = Length 48 Check busy after response
- */
- if (!(cmd->resp_type & MMC_RSP_PRESENT))
- flags = 0;
- else if (cmd->resp_type & MMC_RSP_136)
- flags = (1 << 0);
- else if (cmd->resp_type & MMC_RSP_BUSY)
- flags = (3 << 0);
- else
- flags = (2 << 0);
-
- if (cmd->resp_type & MMC_RSP_CRC)
- flags |= (1 << 3);
- if (cmd->resp_type & MMC_RSP_OPCODE)
- flags |= (1 << 4);
- if (data)
- flags |= (1 << 5);
-
- debug("cmd: %d\n", cmd->cmdidx);
-
- writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg);
-
- for (i = 0; i < retry; i++) {
- mask = readl(&host->reg->norintsts);
- /* Command Complete */
- if (mask & (1 << 0)) {
- if (!data)
- writel(mask, &host->reg->norintsts);
- break;
- }
- }
-
- if (i == retry) {
- printf("%s: waiting for status update\n", __func__);
- return TIMEOUT;
- }
-
- if (mask & (1 << 16)) {
- /* Timeout Error */
- debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
- return TIMEOUT;
- } else if (mask & (1 << 15)) {
- /* Error Interrupt */
- debug("error: %08x cmd %d\n", mask, cmd->cmdidx);
- return -1;
- }
-
- if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (cmd->resp_type & MMC_RSP_136) {
- /* CRC is stripped so we need to do some shifting. */
- for (i = 0; i < 4; i++) {
- unsigned int offset =
- (unsigned int)(&host->reg->rspreg3 - i);
- cmd->response[i] = readl(offset) << 8;
-
- if (i != 3) {
- cmd->response[i] |=
- readb(offset - 1);
- }
- debug("cmd->resp[%d]: %08x\n",
- i, cmd->response[i]);
- }
- } else if (cmd->resp_type & MMC_RSP_BUSY) {
- for (i = 0; i < retry; i++) {
- /* PRNTDATA[23:20] : DAT[3:0] Line Signal */
- if (readl(&host->reg->prnsts)
- & (1 << 20)) /* DAT[0] */
- break;
- }
-
- if (i == retry) {
- printf("%s: card is still busy\n", __func__);
- return TIMEOUT;
- }
-
- cmd->response[0] = readl(&host->reg->rspreg0);
- debug("cmd->resp[0]: %08x\n", cmd->response[0]);
- } else {
- cmd->response[0] = readl(&host->reg->rspreg0);
- debug("cmd->resp[0]: %08x\n", cmd->response[0]);
- }
- }
-
- if (data) {
- while (1) {
- mask = readl(&host->reg->norintsts);
-
- if (mask & (1 << 15)) {
- /* Error Interrupt */
- writel(mask, &host->reg->norintsts);
- printf("%s: error during transfer: 0x%08x\n",
- __func__, mask);
- return -1;
- } else if (mask & (1 << 3)) {
- /*
- * DMA Interrupt, restart the transfer where
- * it was interrupted.
- */
- unsigned int address = readl(&host->reg->sysad);
-
- debug("DMA end\n");
- writel((1 << 3), &host->reg->norintsts);
- writel(address, &host->reg->sysad);
- } else if (mask & (1 << 1)) {
- /* Transfer Complete */
- debug("r/w is done\n");
- break;
- }
- }
- writel(mask, &host->reg->norintsts);
- }
-
- udelay(1000);
- return 0;
-}
-
-static void mmc_change_clock(struct mmc_host *host, uint clock)
-{
- int div;
- unsigned short clk;
- unsigned long timeout;
- unsigned long ctrl2;
-
- /*
- * SELBASECLK[5:4]
- * 00/01 = HCLK
- * 10 = EPLL
- * 11 = XTI or XEXTCLK
- */
- ctrl2 = readl(&host->reg->control2);
- ctrl2 &= ~(3 << 4);
- ctrl2 |= (2 << 4);
- writel(ctrl2, &host->reg->control2);
-
- writew(0, &host->reg->clkcon);
-
- /* XXX: we assume that clock is between 40MHz and 50MHz */
- if (clock == 0)
- goto out;
- else if (clock <= 400000)
- div = 0x100;
- else if (clock <= 20000000)
- div = 4;
- else if (clock <= 26000000)
- div = 2;
- else
- div = 1;
- debug("div: %d\n", div);
-
- div >>= 1;
- /*
- * CLKCON
- * SELFREQ[15:8] : base clock divied by value
- * ENSDCLK[2] : SD Clock Enable
- * STBLINTCLK[1] : Internal Clock Stable
- * ENINTCLK[0] : Internal Clock Enable
- */
- clk = (div << 8) | (1 << 0);
- writew(clk, &host->reg->clkcon);
-
- set_mmc_clk(host->dev_index, div);
-
- /* Wait max 10 ms */
- timeout = 10;
- while (!(readw(&host->reg->clkcon) & (1 << 1))) {
- if (timeout == 0) {
- printf("%s: timeout error\n", __func__);
- return;
- }
- timeout--;
- udelay(1000);
- }
-
- clk |= (1 << 2);
- writew(clk, &host->reg->clkcon);
-
-out:
- host->clock = clock;
-}
-
-static void mmc_set_ios(struct mmc *mmc)
-{
- struct mmc_host *host = mmc->priv;
- unsigned char ctrl;
- unsigned long val;
-
- debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
-
- /*
- * SELCLKPADDS[17:16]
- * 00 = 2mA
- * 01 = 4mA
- * 10 = 7mA
- * 11 = 9mA
- */
- writel(0x3 << 16, &host->reg->control4);
-
- val = readl(&host->reg->control2);
- val &= (0x3 << 4);
-
- val |= (1 << 31) | /* write status clear async mode enable */
- (1 << 30) | /* command conflict mask enable */
- (1 << 14) | /* Feedback Clock Enable for Rx Clock */
- (1 << 8); /* SDCLK hold enable */
-
- writel(val, &host->reg->control2);
-
- /*
- * FCSEL1[15] FCSEL0[7]
- * FCSel[1:0] : Rx Feedback Clock Delay Control
- * Inverter delay means10ns delay if SDCLK 50MHz setting
- * 01 = Delay1 (basic delay)
- * 11 = Delay2 (basic delay + 2ns)
- * 00 = Delay3 (inverter delay)
- * 10 = Delay4 (inverter delay + 2ns)
- */
- writel(0x8080, &host->reg->control3);
-
- mmc_change_clock(host, mmc->clock);
-
- ctrl = readb(&host->reg->hostctl);
-
- /*
- * WIDE8[5]
- * 0 = Depend on WIDE4
- * 1 = 8-bit mode
- * WIDE4[1]
- * 1 = 4-bit mode
- * 0 = 1-bit mode
- */
- if (mmc->bus_width == 8)
- ctrl |= (1 << 5);
- else if (mmc->bus_width == 4)
- ctrl |= (1 << 1);
- else
- ctrl &= ~(1 << 1);
-
- /*
- * OUTEDGEINV[2]
- * 1 = Riging edge output
- * 0 = Falling edge output
- */
- ctrl &= ~(1 << 2);
-
- writeb(ctrl, &host->reg->hostctl);
-}
-
-static void mmc_reset(struct mmc_host *host)
-{
- unsigned int timeout;
-
- /*
- * RSTALL[0] : Software reset for all
- * 1 = reset
- * 0 = work
- */
- writeb((1 << 0), &host->reg->swrst);
-
- host->clock = 0;
-
- /* Wait max 100 ms */
- timeout = 100;
-
- /* hw clears the bit when it's done */
- while (readb(&host->reg->swrst) & (1 << 0)) {
- if (timeout == 0) {
- printf("%s: timeout error\n", __func__);
- return;
- }
- timeout--;
- udelay(1000);
- }
-}
-
-static int mmc_core_init(struct mmc *mmc)
-{
- struct mmc_host *host = (struct mmc_host *)mmc->priv;
- unsigned int mask;
-
- mmc_reset(host);
-
- host->version = readw(&host->reg->hcver);
-
- /* mask all */
- writel(0xffffffff, &host->reg->norintstsen);
- writel(0xffffffff, &host->reg->norintsigen);
-
- writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */
-
- /*
- * NORMAL Interrupt Status Enable Register init
- * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable
- * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable
- * [3] ENSTADMAINT : DMA Interrupt Status Enable
- * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable
- * [0] ENSTACMDCMPLT : Command Complete Status Enable
- */
- mask = readl(&host->reg->norintstsen);
- mask &= ~(0xffff);
- mask |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1) | (1 << 0);
- writel(mask, &host->reg->norintstsen);
-
- /*
- * NORMAL Interrupt Signal Enable Register init
- * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable
- */
- mask = readl(&host->reg->norintsigen);
- mask &= ~(0xffff);
- mask |= (1 << 1);
- writel(mask, &host->reg->norintsigen);
-
- return 0;
-}
-
-static int s5p_mmc_initialize(int dev_index, int bus_width)
-{
- struct mmc *mmc;
-
- mmc = &mmc_dev[dev_index];
-
- sprintf(mmc->name, "SAMSUNG SD/MMC");
- mmc->priv = &mmc_host[dev_index];
- mmc->send_cmd = mmc_send_cmd;
- mmc->set_ios = mmc_set_ios;
- mmc->init = mmc_core_init;
- mmc->getcd = NULL;
-
- mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- if (bus_width == 8)
- mmc->host_caps = MMC_MODE_8BIT;
- else
- mmc->host_caps = MMC_MODE_4BIT;
- mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
-
- mmc->f_min = 400000;
- mmc->f_max = 52000000;
-
- mmc_host[dev_index].dev_index = dev_index;
- mmc_host[dev_index].clock = 0;
- mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index);
- mmc->b_max = 0;
- mmc_register(mmc);
-
- return 0;
-}
-
-int s5p_mmc_init(int dev_index, int bus_width)
-{
- return s5p_mmc_initialize(dev_index, bus_width);
-}
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
new file mode 100644
index 0000000000..1d4481b974
--- /dev/null
+++ b/drivers/mmc/s5p_sdhci.c
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 2012 SAMSUNG Electronics
+ * Jaehoon Chung <jh80.chung@samsung.com>
+ *
+ * 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 <common.h>
+#include <malloc.h>
+#include <sdhci.h>
+#include <asm/arch/mmc.h>
+
+static char *S5P_NAME = "SAMSUNG SDHCI";
+static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
+{
+ unsigned long val, ctrl;
+ /*
+ * SELCLKPADDS[17:16]
+ * 00 = 2mA
+ * 01 = 4mA
+ * 10 = 7mA
+ * 11 = 9mA
+ */
+ sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4);
+
+ val = sdhci_readl(host, SDHCI_CONTROL2);
+ val &= SDHCI_CTRL2_SELBASECLK_SHIFT;
+
+ val |= SDHCI_CTRL2_ENSTAASYNCCLR |
+ SDHCI_CTRL2_ENCMDCNFMSK |
+ SDHCI_CTRL2_ENFBCLKRX |
+ SDHCI_CTRL2_ENCLKOUTHOLD;
+
+ sdhci_writel(host, val, SDHCI_CONTROL2);
+
+ /*
+ * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7]
+ * FCSel[1:0] : Rx Feedback Clock Delay Control
+ * Inverter delay means10ns delay if SDCLK 50MHz setting
+ * 01 = Delay1 (basic delay)
+ * 11 = Delay2 (basic delay + 2ns)
+ * 00 = Delay3 (inverter delay)
+ * 10 = Delay4 (inverter delay + 2ns)
+ */
+ val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1;
+ sdhci_writel(host, val, SDHCI_CONTROL3);
+
+ /*
+ * SELBASECLK[5:4]
+ * 00/01 = HCLK
+ * 10 = EPLL
+ * 11 = XTI or XEXTCLK
+ */
+ ctrl = sdhci_readl(host, SDHCI_CONTROL2);
+ ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3);
+ ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2);
+ sdhci_writel(host, ctrl, SDHCI_CONTROL2);
+}
+
+int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)
+{
+ struct sdhci_host *host = NULL;
+ host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
+ if (!host) {
+ printf("sdhci__host malloc fail!\n");
+ return 1;
+ }
+
+ host->name = S5P_NAME;
+ host->ioaddr = (void *)regbase;
+ host->quirks = quirks;
+
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE;
+ host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+ if (quirks & SDHCI_QUIRK_REG32_RW)
+ host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
+ else
+ host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+
+ host->set_control_reg = &s5p_sdhci_set_control_reg;
+
+ host->host_caps = MMC_MODE_HC;
+
+ add_sdhci(host, max_clk, min_clk);
+ return 0;
+}
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index fc904b5308..1709643da8 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -128,6 +128,7 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
int trans_bytes = 0, is_aligned = 1;
u32 mask, flags, mode;
unsigned int timeout, start_addr = 0;
+ unsigned int retry = 10000;
/* Wait max 10 ms */
timeout = 10;
@@ -210,8 +211,19 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
stat = sdhci_readl(host, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR)
break;
+ if (--retry == 0)
+ break;
} while ((stat & mask) != mask);
+ if (retry == 0) {
+ if (host->quirks & SDHCI_QUIRK_BROKEN_R1B)
+ return 0;
+ else {
+ printf("Timeout for status update!\n");
+ return TIMEOUT;
+ }
+ }
+
if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
sdhci_cmd_done(host, cmd);
sdhci_writel(host, mask, SDHCI_INT_STATUS);
@@ -325,6 +337,9 @@ void sdhci_set_ios(struct mmc *mmc)
u32 ctrl;
struct sdhci_host *host = (struct sdhci_host *)mmc->priv;
+ if (host->set_control_reg)
+ host->set_control_reg(host);
+
if (mmc->clock != host->clock)
sdhci_set_clock(mmc, mmc->clock);
@@ -348,6 +363,9 @@ void sdhci_set_ios(struct mmc *mmc)
else
ctrl &= ~SDHCI_CTRL_HISPD;
+ if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)
+ ctrl &= ~SDHCI_CTRL_HISPD;
+
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
@@ -431,9 +449,15 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
if (caps & SDHCI_CAN_VDD_180)
mmc->voltages |= MMC_VDD_165_195;
+
+ if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE)
+ mmc->voltages |= host->voltages;
+
mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
if (caps & SDHCI_CAN_DO_8BIT)
mmc->host_caps |= MMC_MODE_8BIT;
+ if (host->host_caps)
+ mmc->host_caps |= host->host_caps;
sdhci_reset(host, SDHCI_RESET_ALL);
mmc_register(mmc);
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index fc2270127a..a4b8728cde 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -9,7 +9,7 @@
*/
#include <linux/mtd/mtd.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/concat.h>
#include <ubi_uboot.h>
@@ -289,6 +289,7 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mtd_concat *concat = CONCAT(mtd);
+ struct nand_chip *chip = concat->mtd.priv;
struct mtd_info *subdev;
int i, err;
uint64_t length, offset = 0;
@@ -311,10 +312,19 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
*/
if (!concat->mtd.numeraseregions) {
/* the easy case: device has uniform erase block size */
- if (instr->addr & (concat->mtd.erasesize - 1))
- return -EINVAL;
- if (instr->len & (concat->mtd.erasesize - 1))
- return -EINVAL;
+ if (chip->phys_erase_shift == 0) {
+ if(mtd_mod_by_eb(instr->addr, &concat->mtd))
+ return -EINVAL;
+
+ if (mtd_mod_by_eb(instr->len, &concat->mtd))
+ return -EINVAL;
+ } else {
+ if (instr->addr & (concat->mtd.erasesize - 1))
+ return -EINVAL;
+
+ if (instr->len & (concat->mtd.erasesize - 1))
+ return -EINVAL;
+ }
} else {
/* device has variable erase size */
struct mtd_erase_region_info *erase_regions =
@@ -332,8 +342,13 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
* to-be-erased area begins. Verify that the starting
* offset is aligned to this region's erase size:
*/
- if (instr->addr & (erase_regions[i].erasesize - 1))
- return -EINVAL;
+ if (is_power_of_2(erase_regions[i].erasesize)){
+ if (instr->addr & (erase_regions[i].erasesize - 1))
+ return -EINVAL;
+ } else {
+ if (mtd_mod_by_var(instr->addr, erase_regions[i].erasesize))
+ return -EINVAL;
+ }
/*
* now find the erase region where the to-be-erased area ends:
@@ -345,9 +360,13 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
/*
* check if the ending offset is aligned to this region's erase size
*/
- if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
- 1))
- return -EINVAL;
+ if (is_power_of_2(erase_regions[i].erasesize)){
+ if ((instr->addr + instr->len) & (erase_regions[i].erasesize - 1))
+ return -EINVAL;
+ } else {
+ if (mtd_mod_by_var((instr->addr + instr->len), erase_regions[i].erasesize))
+ return -EINVAL;
+ }
}
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index a195ddab35..fb35596175 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -8,7 +8,7 @@
*/
#include <linux/mtd/mtd.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <ubi_uboot.h>
struct mtd_info *mtd_table[MAX_MTD_DEVICES];
@@ -161,6 +161,7 @@ void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
{
*truncated = 0;
*len_incl_bad = 0;
+ struct nand_chip *chip = mtd->priv;
if (!mtd->block_isbad) {
*len_incl_bad = length;
@@ -176,10 +177,17 @@ void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
return;
}
- block_len = mtd->erasesize - (offset & (mtd->erasesize - 1));
+ if (chip->phys_erase_shift == 0){
+ block_len = mtd->erasesize - mtd_mod_by_eb(offset, mtd);
- if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1)))
- len_excl_bad += block_len;
+ if (!mtd->block_isbad(mtd, mtd_div_by_eb(offset, mtd)*mtd->erasesize))
+ len_excl_bad += block_len;
+ } else {
+ block_len = mtd->erasesize - (offset & (mtd->erasesize - 1));
+
+ if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1)))
+ len_excl_bad += block_len;
+ }
*len_incl_bad += block_len;
offset += block_len;
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index f647e43668..96dcda2b2b 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -17,7 +17,7 @@
#include <linux/list.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
/* Our partition linked list */
struct list_head mtd_partitions;
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1d1b628651..b9e683cdcb 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -63,6 +63,7 @@ COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
+COBJS-$(CONFIG_NAND_RDA) += rda_nand.o rda_spi_nand.o rda_nand_base.o
endif
COBJS := $(COBJS-y)
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 5cac78b296..b3b7c705e1 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -384,19 +384,30 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* READID must read all possible bytes while CEB is active */
case NAND_CMD_READID:
+ case NAND_CMD_PARAM: {
+ int timing = IFC_FIR_OP_RB;
+ if (command == NAND_CMD_PARAM)
+ timing = IFC_FIR_OP_RBCD;
+
out_be32(&ifc->ifc_nand.nand_fir0,
(IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT));
+ (timing << IFC_NAND_FIR0_OP2_SHIFT));
out_be32(&ifc->ifc_nand.nand_fcr0,
- NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT);
- /* 4 bytes for manuf, device and exts */
- out_be32(&ifc->ifc_nand.nand_fbcr, 4);
- ctrl->read_bytes = 4;
+ command << IFC_NAND_FCR0_CMD0_SHIFT);
+ out_be32(&ifc->ifc_nand.row3, column);
+
+ /*
+ * although currently it's 8 bytes for READID, we always read
+ * the maximum 256 bytes(for PARAM)
+ */
+ out_be32(&ifc->ifc_nand.nand_fbcr, 256);
+ ctrl->read_bytes = 256;
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
return;
+ }
/* ERASE1 stores the block and page address */
case NAND_CMD_ERASE1:
@@ -764,6 +775,7 @@ int board_nand_init(struct nand_chip *nand)
if (priv->bank >= MAX_BANKS) {
printf("%s: address did not match any "
"chip selects\n", __func__);
+ kfree(priv);
return -ENODEV;
}
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 7fd8a35479..e6b7a70661 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -29,7 +29,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <asm/errno.h>
#include <asm/io.h>
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 35e89a0f4d..936186f75e 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1302,12 +1302,45 @@ static void mxc_setup_config1(void)
#define mxc_setup_config1()
#endif
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+#endif
+
int board_nand_init(struct nand_chip *this)
{
struct mtd_info *mtd;
uint16_t tmp;
int err = 0;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ this->options |= NAND_USE_FLASH_BBT;
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+#endif
+
/* structures must be linked */
mtd = &host->mtd;
mtd->priv = this;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 44f7b91457..d293f71d3f 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -39,7 +39,7 @@
#include <malloc.h>
#include <watchdog.h>
#include <linux/err.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
@@ -79,7 +79,7 @@ static struct nand_ecclayout nand_oob_16 = {
.eccpos = {0, 1, 2, 3, 6, 7},
.oobfree = {
{.offset = 8,
- . length = 8} }
+ .length = 8} }
};
static struct nand_ecclayout nand_oob_64 = {
@@ -122,16 +122,31 @@ static int check_offs_len(struct mtd_info *mtd,
int ret = 0;
/* Start address must align on block boundary */
- if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
- ret = -EINVAL;
+ if (chip->phys_erase_shift == 0) {
+ if (mtd_mod_by_eb(ofs, mtd)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
+ ret = -EINVAL;
+ }
+ } else {
+ if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
+ ret = -EINVAL;
+ }
}
/* Length must align on block boundary */
- if (len & ((1 << chip->phys_erase_shift) - 1)) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
+ if (chip->phys_erase_shift == 0) {
+ if (mtd_mod_by_eb(len, mtd)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
__func__);
- ret = -EINVAL;
+ ret = -EINVAL;
+ }
+ } else {
+ if (len & ((1 << chip->phys_erase_shift) - 1)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
+ __func__);
+ ret = -EINVAL;
+ }
}
/* Do not allow past end of device */
@@ -350,10 +365,16 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
if (chip->options & NAND_BBT_SCANLASTPAGE)
ofs += mtd->erasesize - mtd->writesize;
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ if ( chip->page_shift == 0)
+ page = mtd_div_by_ws(ofs, mtd) & chip->pagemask;
+ else
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
if (getchip) {
- chipnr = (int)(ofs >> chip->chip_shift);
+ if (chip->chip_shift == 0)
+ chipnr = mtd_div_by_cs(ofs, mtd);
+ else
+ chipnr = (int)(ofs >> chip->chip_shift);
nand_get_device(chip, mtd, FL_READING);
@@ -403,7 +424,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
ofs += mtd->erasesize - mtd->writesize;
/* Get block number */
- block = (int)(ofs >> chip->bbt_erase_shift);
+ if (chip->bbt_erase_shift == 0)
+ block = mtd_div_by_eb(ofs, mtd);
+ else
+ block = (int)(ofs >> chip->bbt_erase_shift);
+
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
@@ -1200,10 +1225,9 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- int chipnr, page, realpage, col, bytes, aligned;
+ int chipnr, page, realpage, col, bytes, aligned, blkcheck;
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;
@@ -1213,15 +1237,30 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
uint8_t *bufpoi, *oob, *buf;
+ if (chip->phys_erase_shift == 0 || chip->page_shift == 0)
+ blkcheck = mtd_div_by_ws(mtd->erasesize, mtd) - 1;
+ else
+ blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+
stats = mtd->ecc_stats;
+ if (chip->chip_shift == 0)
+ chipnr = mtd_div_by_cs(from, mtd);
+ else
+ chipnr = (int)(from >> chip->chip_shift);
- chipnr = (int)(from >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
- realpage = (int)(from >> chip->page_shift);
+ if (chip->page_shift == 0)
+ realpage = mtd_div_by_ws(from, mtd);
+ else
+ realpage = (int)(from >> chip->page_shift);
+
page = realpage & chip->pagemask;
- col = (int)(from & (mtd->writesize - 1));
+ if (chip->page_shift == 0)
+ col = mtd_mod_by_ws(from, mtd);
+ else
+ col = (int)(from & (mtd->writesize - 1));
buf = ops->datbuf;
oob = ops->oobbuf;
@@ -1518,13 +1557,18 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- int page, realpage, chipnr, sndcmd = 1;
+ int page, realpage, chipnr, sndcmd = 1, blkcheck;
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;
+ if (chip->phys_erase_shift == 0 ||chip->page_shift == 0)
+ blkcheck = (mtd->erasesize / mtd->writesize) - 1;
+ else
+ blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+
MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
__func__, (unsigned long long)from, readlen);
@@ -1539,20 +1583,38 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
return -EINVAL;
}
- /* Do not allow reads past end of device */
- if (unlikely(from >= mtd->size ||
+ if (chip->page_shift == 0 || chip->chip_shift == 0) {
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size ||
+ ops->ooboffs + readlen > (mtd_div_by_ws(mtd->size, mtd) -
+ mtd_div_by_ws(from, mtd)) * len)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
+ "of device\n", __func__);
+ return -EINVAL;
+ }
+
+ chipnr = mtd_div_by_cs(from, mtd);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ realpage = mtd_div_by_ws(from, mtd);
+ } else {
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size ||
ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
(from >> chip->page_shift)) * len)) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
"of device\n", __func__);
- return -EINVAL;
- }
+ return -EINVAL;
+ }
- chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
+ chipnr = (int)(from >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ realpage = (int)(from >> chip->page_shift);
+ }
- /* Shift to get page */
- realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
while (1) {
@@ -1903,7 +1965,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
return NULL;
}
-#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
+#define NOTALIGNED(x) ((x % chip->subpagesize) != 0)
/**
* nand_do_write_ops - [Internal] NAND write with ECC
@@ -1932,13 +1994,22 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (!writelen)
return 0;
- column = to & (mtd->writesize - 1);
- subpage = column || (writelen & (mtd->writesize - 1));
+ if (chip->page_shift == 0) {
+ column = mtd_mod_by_ws(to, mtd);
+ subpage = column || mtd_mod_by_ws(writelen, mtd);
+ } else {
+ column = to & (mtd->writesize - 1);
+ subpage = column || (writelen & (mtd->writesize - 1));
+ }
if (subpage && oob)
return -EINVAL;
- chipnr = (int)(to >> chip->chip_shift);
+ if (chip->chip_shift == 0)
+ chipnr = mtd_div_by_cs(to, mtd);
+ else
+ chipnr = (int)(to >> chip->chip_shift);
+
chip->select_chip(mtd, chipnr);
/* Check, if it is write protected */
@@ -1947,14 +2018,25 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
return -EIO;
}
- realpage = (int)(to >> chip->page_shift);
- page = realpage & chip->pagemask;
- blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+ if (chip->page_shift == 0 || chip->phys_erase_shift == 0) {
+ realpage = mtd_div_by_ws(to, mtd);
+ page = realpage & chip->pagemask;
+ blockmask = mtd->erasesize / mtd->writesize - 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;
+ /* Invalidate the page cache, when we write to the cached page */
+ if (to <= chip->pagebuf * mtd->writesize &&
+ chip->pagebuf *mtd->writesize < (to + ops->len))
+ chip->pagebuf = -1;
+ } else {
+ 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))
@@ -2087,21 +2169,39 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
return -EINVAL;
}
- /* Do not allow write past end of device */
- if (unlikely(to >= mtd->size ||
+ if (chip->page_shift == 0 ||chip->chip_shift == 0) {
+ /* Do not allow write past end of device */
+ if (unlikely(to >= mtd->size ||
+ ops->ooboffs + ops->ooblen >
+ (mtd_div_by_ws(mtd->size, mtd) -
+ mtd_div_by_ws(to, mtd)) * len)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
+ "end of device\n", __func__);
+ return -EINVAL;
+ }
+
+ chipnr = mtd_div_by_cs(to, mtd);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ page = mtd_div_by_ws(to, mtd);
+ } else {
+ /* Do not allow write 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, "%s: Attempt write beyond "
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
"end of device\n", __func__);
- return -EINVAL;
- }
+ return -EINVAL;
+ }
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
- /* Shift to get page */
- page = (int)(to >> chip->page_shift);
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
+ }
/*
* Reset the chip. Some chips (like the Toshiba TC5832DC found in one
@@ -2251,12 +2351,20 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* Grab the lock and see if the device is available */
nand_get_device(chip, mtd, FL_ERASING);
- /* Shift to get first page */
- page = (int)(instr->addr >> chip->page_shift);
- chipnr = (int)(instr->addr >> chip->chip_shift);
+ if (chip->page_shift == 0 ||chip->chip_shift == 0) {
+ /* Shift to get first page */
+ page = mtd_div_by_ws(instr->addr, mtd);
+ chipnr = mtd_div_by_cs(instr->addr, mtd);
- /* Calculate pages in each block */
- pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+ /* Calculate pages in each block */
+ pages_per_block = mtd->erasesize / mtd->writesize;
+ } else {
+ page = (int)(instr->addr >> chip->page_shift);
+ chipnr = (int)(instr->addr >> chip->chip_shift);
+
+ /* 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);
@@ -2288,13 +2396,23 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/*
* heck if we have a bad block, we do not erase bad blocks !
*/
- if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) <<
+ if (chip->page_shift == 0) {
+ if (!instr->scrub && nand_block_checkbad(mtd, (loff_t)page *
+ mtd->writesize, 0, allowbbt)) {
+ printk(KERN_WARNING "%s: attempt to erase a bad block "
+ "at page 0x%08x\n", __func__, page);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+ } else {
+ if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) <<
chip->page_shift, 0, allowbbt)) {
- printk(KERN_WARNING "%s: attempt to erase a bad block "
+ printk(KERN_WARNING "%s: attempt to erase a bad block "
"at page 0x%08x\n", __func__, page);
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+ }
/*
* Invalidate the page cache, if we erase the block which
@@ -2317,26 +2435,44 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
status, page);
/* See if block erase succeeded */
- if (status & NAND_STATUS_FAIL) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
- "page 0x%08x\n", __func__, page);
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr =
- ((loff_t)page << chip->page_shift);
- goto erase_exit;
+ if (chip->page_shift == 0) {
+ if (status & NAND_STATUS_FAIL) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
+ "page 0x%08x\n", __func__, page);
+ instr->state = MTD_ERASE_FAILED;
+ instr->fail_addr = (loff_t)page * mtd->writesize;
+ goto erase_exit;
+ }
+ } else {
+ if (status & NAND_STATUS_FAIL) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
+ "page 0x%08x\n", __func__, page);
+ instr->state = MTD_ERASE_FAILED;
+ instr->fail_addr = ((loff_t)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] =
- ((loff_t)page << chip->page_shift);
+ if (chip->page_shift == 0) {
+ if (bbt_masked_page != 0xffffffff &&
+ (page & BBT_PAGE_MASK) == bbt_masked_page)
+ rewrite_bbt[chipnr] = (loff_t)page * mtd->writesize;
+ } else {
+ if (bbt_masked_page != 0xffffffff &&
+ (page & BBT_PAGE_MASK) == bbt_masked_page)
+ rewrite_bbt[chipnr] = ((loff_t)page << chip->page_shift);
+ }
/* Increment page address and decrement length */
- len -= (1 << chip->phys_erase_shift);
+ if (chip->phys_erase_shift == 0)
+ len -= mtd->erasesize;
+ else
+ len -= (1 << chip->phys_erase_shift);
+
page += pages_per_block;
/* Check, if we cross a chip boundary */
@@ -2530,13 +2666,14 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
- printk(KERN_INFO "ONFI flash detected\n");
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "ONFI flash detected\n");
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
- printk(KERN_INFO "ONFI param page %d valid\n", i);
+ MTDDEBUG(MTD_DEBUG_LEVEL0,
+ "ONFI param page %d valid\n", i);
break;
}
}
@@ -2732,10 +2869,14 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/*
* 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;
+ if(chip->init_size) {
+ busw = chip->init_size(mtd, chip, id_data);
+ } else {
+ mtd->erasesize = type->erasesize;
+ mtd->writesize = type->pagesize;
+ mtd->oobsize = mtd->writesize / 32;
+ busw = type->options & NAND_BUSWIDTH_16;
+ }
/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
@@ -2786,18 +2927,57 @@ ident_done:
return ERR_PTR(-EINVAL);
}
- /* 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;
+ if (is_power_of_2(mtd->writesize)) {
+ /* Calculate the address shift from the page size */
+ chip->page_shift = ffs(mtd->writesize) - 1;
+ /* Convert chipsize to number of pages per chip -1. */
+ if (mtd->erasesize == 0x300000) {
+ /*page 0x180 ~ 0x1ff are invalid, so need skip to next block.*/
+ chip->pagemask = 0xffffffff;
+ } else {
+ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+ }
+ } else {
+ chip->page_shift = 0;
+ if (mtd->erasesize == 0x300000) {
+ /*page 0x180 ~ 0x1ff are invalid, so need skip to next block.*/
+ chip->pagemask = 0xffffffff;
+ } else {
+ chip->pagemask = mtd_div_by_ws(chip->chipsize , mtd) - 1;
+ }
+ }
+
+ if (is_power_of_2(mtd->erasesize))
+ chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1;
+ else
+ chip->bbt_erase_shift = chip->phys_erase_shift = 0;
+
+ if(chip->chipsize >> 32){
+ //assume the chipsize is not out of 4096TB(4*1024*1024*GB)
+ //4096TB is 52bits.
+ if(is_power_of_2(chip->chipsize >> 20)){
+ chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
+ chip->chip_shift += 32 - 1;
+ }else{
+ chip->chip_shift = 0;
+ }
+ } else {
+ if (is_power_of_2(chip->chipsize)) {
+ chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
+ }else{
+ chip->chip_shift = 0;
+ }
+ }
+
+ printf("nand page_shift = 0x%x, erase_shift = 0x%x, chip_shift = 0x%x \n", chip->page_shift, chip->bbt_erase_shift, chip->chip_shift);
- chip->bbt_erase_shift = chip->phys_erase_shift =
- ffs(mtd->erasesize) - 1;
- if (chip->chipsize & 0xffffffff)
- chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
- else {
- chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
- chip->chip_shift += 32 - 1;
+ if (mtd->oobsize != 32 && mtd->oobsize != 16) {
+ if (mtd->writesize == 3072)
+ mtd->oobsize = 128;
+ else if (mtd->writesize == 6144)
+ mtd->oobsize = 256;
+ else if (mtd->writesize == 12288)
+ mtd->oobsize = 512;
}
chip->badblockbits = 8;
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 2b730e09c9..74d4fb9a73 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -61,7 +61,7 @@
#include <common.h>
#include <malloc.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
@@ -210,7 +210,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
*
*/
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
- struct nand_bbt_descr *td, int offs)
+ struct nand_bbt_descr *td, loff_t offs)
{
int res, i, j, act = 0;
struct nand_chip *this = mtd->priv;
@@ -223,10 +223,17 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
totlen = (num * bits) >> 3;
marker_len = add_marker_len(td);
- from = ((loff_t) page) << this->page_shift;
+ if (this->page_shift == 0)
+ from = (loff_t)page * mtd->writesize;
+ else
+ from = ((loff_t) page) << this->page_shift;
while (totlen) {
- len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+ if (this->bbt_erase_shift == 0)
+ len = min(totlen, mtd->erasesize);
+ else
+ len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+
if (marker_len) {
/*
* In case the BBT marker is not in the OOB area it
@@ -253,16 +260,29 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
if (tmp == msk)
continue;
if (reserved_block_code && (tmp == reserved_block_code)) {
- printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n",
- (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ if (this->bbt_erase_shift == 0)
+ printk(KERN_DEBUG "nand_read_bbt: " \
+ "Reserved block at 0x%012llx\n",
+ (loff_t)((offs << 2) + (act >> 1)) * mtd->erasesize);
+ else
+ printk(KERN_DEBUG "nand_read_bbt: " \
+ "Reserved block at 0x%012llx\n",
+ (loff_t)((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%012llx\n",
- (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ if (this->bbt_erase_shift == 0)
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "nand_read_bbt: " \
+ "Bad block at 0x%012llx\n",
+ (loff_t)((offs << 2) + (act >> 1)) * mtd->erasesize);
+ else
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "nand_read_bbt: " \
+ "Bad block at 0x%012llx\n",
+ (loff_t)((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);
@@ -294,19 +314,34 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
int res = 0, i;
if (td->options & NAND_BBT_PERCHIP) {
- int offs = 0;
+ loff_t offs = 0;
for (i = 0; i < this->numchips; i++) {
- if (chip == -1 || chip == i)
- res = read_bbt(mtd, buf, td->pages[i],
+ if (chip == -1 || chip == i) {
+ if (this->bbt_erase_shift == 0) {
+ res = read_bbt(mtd, buf, td->pages[i],
+ mtd_div_by_eb(this->chipsize, mtd),
+ td, offs);
+ } else {
+ res = read_bbt(mtd, buf, td->pages[i],
this->chipsize >> this->bbt_erase_shift,
td, offs);
+ }
+ }
if (res)
return res;
- offs += this->chipsize >> (this->bbt_erase_shift + 2);
+ if (this->bbt_erase_shift == 0)
+ offs += mtd_div_by_eb(this->chipsize >> 2, mtd);
+ else
+ offs += this->chipsize >> (this->bbt_erase_shift + 2);
}
} else {
- res = read_bbt(mtd, buf, td->pages[0],
+ if (this->bbt_erase_shift == 0)
+ res = read_bbt(mtd, buf, td->pages[0],
+ mtd_div_by_eb(mtd->size, mtd), td, 0);
+ else
+ res = read_bbt(mtd, buf, td->pages[0],
mtd->size >> this->bbt_erase_shift, td, 0);
+
if (res)
return res;
}
@@ -419,8 +454,13 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
- scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+ if (this->page_shift == 0)
+ scan_read_raw(mtd, buf, (loff_t)td->pages[0] * mtd->writesize,
mtd->writesize, td);
+ else
+ scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+ mtd->writesize, td);
+
td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
td->pages[0], td->version[0]);
@@ -428,8 +468,13 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) {
- scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+ if (this->page_shift == 0)
+ scan_read_raw(mtd, buf, (loff_t)md->pages[0] * mtd->writesize,
+ mtd->writesize, td);
+ else
+ scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
mtd->writesize, td);
+
md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]);
@@ -512,9 +557,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
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);
- else if (bd->options & NAND_BBT_SCAN2NDPAGE)
+ if (bd->options & NAND_BBT_SCANALLPAGES) {
+ if (this->page_shift == 0 || this->bbt_erase_shift == 0)
+ len = mtd->erasesize / mtd->writesize;
+ else
+ len = 1 << (this->bbt_erase_shift - this->page_shift);
+ } else if (bd->options & NAND_BBT_SCAN2NDPAGE)
len = 2;
else
len = 1;
@@ -532,7 +580,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
if (chip == -1) {
/* 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);
+ if (this->bbt_erase_shift == 0)
+ numblocks = mtd_div_by_eb(mtd->size << 1, mtd);
+ else
+ numblocks = mtd->size >> (this->bbt_erase_shift - 1);
+
startblock = 0;
from = 0;
} else {
@@ -541,10 +593,19 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
chip + 1, this->numchips);
return -EINVAL;
}
- numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+
+ if (this->bbt_erase_shift == 0)
+ numblocks = mtd_div_by_eb(this->chipsize << 1, mtd);
+ else
+ numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+
startblock = chip * numblocks;
numblocks += startblock;
- from = (loff_t)startblock << (this->bbt_erase_shift - 1);
+
+ if (this->bbt_erase_shift == 0)
+ from = (loff_t)startblock * (mtd->erasesize >> 1);
+ else
+ from = (loff_t)startblock << (this->bbt_erase_shift - 1);
}
if (this->options & NAND_BBT_SCANLASTPAGE)
@@ -573,7 +634,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
}
i += 2;
- from += (1 << this->bbt_erase_shift);
+
+ if (this->bbt_erase_shift == 0)
+ from += mtd->erasesize;
+ else
+ from += (1 << this->bbt_erase_shift);
}
return 0;
}
@@ -602,11 +667,14 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
int startblock, block, dir;
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;
+ if (this->bbt_erase_shift == 0)
+ startblock = mtd_div_by_eb(mtd->size, mtd) - 1;
+ else
+ startblock = (mtd->size >> this->bbt_erase_shift) - 1;
+
dir = -1;
} else {
startblock = 0;
@@ -616,11 +684,18 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
- bbtblocks = this->chipsize >> this->bbt_erase_shift;
+ if (this->bbt_erase_shift == 0)
+ bbtblocks = mtd_div_by_eb(this->chipsize, mtd);
+ else
+ bbtblocks = this->chipsize >> this->bbt_erase_shift;
+
startblock &= bbtblocks - 1;
} else {
chips = 1;
- bbtblocks = mtd->size >> this->bbt_erase_shift;
+ if (this->bbt_erase_shift == 0)
+ bbtblocks = mtd_div_by_eb(mtd->size, mtd);
+ else
+ bbtblocks = mtd->size >> this->bbt_erase_shift;
}
for (i = 0; i < chips; i++) {
@@ -629,14 +704,21 @@ 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++) {
-
+ loff_t offs;
int actblock = startblock + dir * block;
- loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
+ if (this->bbt_erase_shift == 0)
+ offs = (loff_t)actblock * mtd->erasesize;
+ else
+ offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */
scan_read_raw(mtd, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
- td->pages[i] = actblock << blocktopage;
+ if (this->page_shift == 0 || this->bbt_erase_shift == 0)
+ td->pages[i] = actblock * (mtd->erasesize / mtd->writesize);
+ else
+ td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+
if (td->options & NAND_BBT_VERSION) {
offs = bbt_get_ver_offs(mtd, td);
td->version[i] = buf[offs];
@@ -644,15 +726,19 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
break;
}
}
- startblock += this->chipsize >> this->bbt_erase_shift;
+ if (this->bbt_erase_shift == 0)
+ startblock += mtd_div_by_eb(this->chipsize, mtd);
+ else
+ startblock += this->chipsize >> this->bbt_erase_shift;
}
/* 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);
else
- printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
- td->version[i]);
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "Bad block table found " \
+ "at page %d, version 0x%02X\n", td->pages[i],
+ td->version[i]);
}
return 0;
}
@@ -698,12 +784,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_chip *this = mtd->priv;
struct erase_info einfo;
int i, j, res, chip = 0;
- int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+ int bits, startblock, dir, page, numblocks, sft, sftmsk;
int nrchips, bbtoffs, pageoffs, ooboffs;
uint8_t msk[4];
uint8_t rcode = td->reserved_block_code;
size_t retlen, len = 0;
- loff_t to;
+ loff_t offs, to;
struct mtd_oob_ops ops;
ops.ooblen = mtd->oobsize;
@@ -715,7 +801,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
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);
+ if (this->bbt_erase_shift == 0)
+ numblocks = mtd_div_by_eb(this->chipsize, mtd);
+ else
+ numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
/* Full device write or specific chip ? */
if (chipsel == -1) {
nrchips = this->numchips;
@@ -724,7 +813,11 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
chip = chipsel;
}
} else {
- numblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ if (this->bbt_erase_shift == 0)
+ numblocks = mtd_div_by_eb(mtd->size, mtd);
+ else
+ numblocks = (int)(mtd->size >> this->bbt_erase_shift);
+
nrchips = 1;
}
@@ -759,8 +852,11 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
case 0x03:
continue;
}
- page = block <<
- (this->bbt_erase_shift - this->page_shift);
+ if (this->bbt_erase_shift == 0 || this->page_shift == 0)
+ page = block *(mtd->erasesize / mtd->writesize);
+ else
+ 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;
@@ -789,14 +885,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
}
bbtoffs = chip * (numblocks >> 2);
-
- to = ((loff_t) page) << this->page_shift;
+ if (this->page_shift == 0)
+ to = (loff_t) page * mtd->writesize;
+ else
+ to = ((loff_t) page) << this->page_shift;
/* 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;
+ if (this->bbt_erase_shift == 0) {
+ to = mtd_div_by_eb(to, mtd) * mtd->erasesize;
+ len = mtd->erasesize;
+ } else {
+ to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+ len = 1 << this->bbt_erase_shift;
+ }
res = mtd->read(mtd, to, len, &retlen, buf);
if (res < 0) {
if (retlen != len) {
@@ -810,19 +913,28 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
"bad block table\n");
}
/* Read oob data */
- ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
+ if (this->page_shift == 0)
+ ops.ooblen = mtd_div_by_ws(len, mtd) * mtd->oobsize;
+ else
+ 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;
+ if (this->page_shift == 0) {
+ pageoffs = page - mtd_div_by_ws(to, mtd);
+ offs = pageoffs * mtd->writesize;
+ } else {
+ 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));
ooboffs = len + (pageoffs * mtd->oobsize);
-
} else if (td->options & NAND_BBT_NO_OOB) {
ooboffs = 0;
offs = td->len;
@@ -833,7 +945,11 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
len = (size_t) (numblocks >> sft);
len += offs;
/* Make it page aligned ! */
- len = ALIGN(len, mtd->writesize);
+ if (this->page_shift == 0)
+ len = roundup(len, mtd->writesize);
+ else
+ len = ALIGN(len, mtd->writesize);
+
/* Preset the buffer with 0xff */
memset(buf, 0xff, len);
/* Pattern is located at the begin of first page */
@@ -842,10 +958,18 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
/* Calc length */
len = (size_t) (numblocks >> sft);
/* Make it page aligned ! */
- len = ALIGN(len, mtd->writesize);
+ if (this->page_shift == 0)
+ len = roundup(len, mtd->writesize);
+ else
+ len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
- memset(buf, 0xff, len +
- (len >> this->page_shift)* mtd->oobsize);
+ if (this->page_shift == 0)
+ memset(buf, 0xff, len +
+ mtd_div_by_ws(len, mtd) * mtd->oobsize);
+ else
+ memset(buf, 0xff, len +
+ (len >> this->page_shift)* mtd->oobsize);
+
offs = 0;
ooboffs = len;
/* Pattern is located in oob area of first page */
@@ -871,7 +995,11 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
memset(&einfo, 0, sizeof(einfo));
einfo.mtd = mtd;
einfo.addr = to;
- einfo.len = 1 << this->bbt_erase_shift;
+ if (this->bbt_erase_shift == 0)
+ einfo.len = mtd->erasesize;
+ else
+ einfo.len = 1 << this->bbt_erase_shift;
+
res = nand_erase_nand(mtd, &einfo, 1);
if (res < 0)
goto outerr;
@@ -1047,10 +1175,16 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
- nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ if (this->bbt_erase_shift == 0)
+ nrblocks = mtd_div_by_eb(this->chipsize, mtd);
+ else
+ nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
} else {
chips = 1;
- nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ if (this->bbt_erase_shift == 0)
+ nrblocks = mtd_div_by_eb(mtd->size, mtd);
+ else
+ nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
}
for (i = 0; i < chips; i++) {
@@ -1058,13 +1192,21 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
!(td->options & NAND_BBT_WRITE)) {
if (td->pages[i] == -1)
continue;
- block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+ if (this->bbt_erase_shift == 0 || this->page_shift == 0)
+ block = td->pages[i] / (mtd->erasesize / mtd->writesize);
+ else
+ block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+
block <<= 1;
oldval = this->bbt[(block >> 3)];
newval = oldval | (0x2 << (block & 0x06));
this->bbt[(block >> 3)] = newval;
- if ((oldval != newval) && td->reserved_block_code)
- nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
+ if ((oldval != newval) && td->reserved_block_code) {
+ if (this->bbt_erase_shift == 0)
+ nand_update_bbt(mtd, (loff_t)block * (mtd->erasesize >> 1));
+ else
+ nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
+ }
continue;
}
update = 0;
@@ -1084,8 +1226,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
/* If we want reserved blocks to be recorded to flash, and some
new ones have been marked, then we need to update the stored
bbts. This should only happen once. */
- if (update && td->reserved_block_code)
- nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
+ if (update && td->reserved_block_code) {
+ if (this->bbt_erase_shift == 0)
+ nand_update_bbt(mtd, (loff_t)(block - 2) * (mtd->erasesize >> 1));
+ else
+ nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
+ }
}
}
@@ -1126,15 +1272,26 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
}
- if (bd->options & NAND_BBT_PERCHIP)
- table_size = this->chipsize >> this->bbt_erase_shift;
- else
- table_size = mtd->size >> this->bbt_erase_shift;
+ if (bd->options & NAND_BBT_PERCHIP) {
+ if (this->bbt_erase_shift == 0)
+ table_size = mtd_div_by_eb(this->chipsize, mtd);
+ else
+ table_size = this->chipsize >> this->bbt_erase_shift;
+ } else {
+ if (this->bbt_erase_shift == 0)
+ table_size = mtd_div_by_eb(mtd->size, mtd);
+ else
+ table_size = mtd->size >> this->bbt_erase_shift;
+ }
table_size >>= 3;
table_size *= bits;
if (bd->options & NAND_BBT_NO_OOB)
table_size += pattern_len;
- BUG_ON(table_size > (1 << this->bbt_erase_shift));
+
+ if (this->bbt_erase_shift == 0)
+ BUG_ON(table_size > mtd->erasesize);
+ else
+ BUG_ON(table_size > (1 << this->bbt_erase_shift));
}
/**
@@ -1159,7 +1316,11 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
- len = mtd->size >> (this->bbt_erase_shift + 2);
+ if (this->bbt_erase_shift == 0)
+ len = mtd_div_by_eb(mtd->size >> 2, mtd);
+ else
+ len = mtd->size >> (this->bbt_erase_shift + 2);
+
/* Allocate memory (2bit per block) and clear the memory bad block table */
this->bbt = kzalloc(len, GFP_KERNEL);
if (!this->bbt) {
@@ -1182,8 +1343,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
verify_bbt_descr(mtd, md);
/* Allocate a temporary buffer for one eraseblock incl. oob */
- len = (1 << this->bbt_erase_shift);
- len += (len >> this->page_shift) * mtd->oobsize;
+ if (this->bbt_erase_shift == 0 || this->page_shift == 0) {
+ len = mtd->erasesize;
+ len += mtd_div_by_ws(len, mtd) * mtd->oobsize;
+ } else {
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ }
buf = vmalloc(len);
if (!buf) {
printk(KERN_ERR "nand_bbt: Out of memory\n");
@@ -1232,8 +1398,13 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
return -EINVAL;
/* Allocate a temporary buffer for one eraseblock incl. oob */
- len = (1 << this->bbt_erase_shift);
- len += (len >> this->page_shift) * mtd->oobsize;
+ if (this->bbt_erase_shift == 0 || this->page_shift == 0) {
+ len = mtd->erasesize;
+ len += mtd_div_by_ws(len, mtd) * mtd->oobsize;
+ } else {
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ }
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR "nand_update_bbt: Out of memory\n");
@@ -1244,7 +1415,11 @@ 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);
+ if (this->chip_shift == 0)
+ chip = mtd_div_by_cs(offs, mtd);
+ else
+ chip = (int)(offs >> this->chip_shift);
+
chipsel = chip;
} else {
chip = 0;
@@ -1428,7 +1603,11 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
uint8_t res;
/* Get block number * 2 */
- block = (int)(offs >> (this->bbt_erase_shift - 1));
+ if (this->bbt_erase_shift == 0)
+ block = mtd_div_by_eb(offs << 1, mtd);
+ else
+ 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",
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 39535497f8..580a080c1c 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -11,6 +11,13 @@
#include <common.h>
#include <linux/mtd/nand.h>
+#include <rda/tgt_ap_board_config.h>
+#include <rda/tgt_ap_flash_parts.h>
+
+#if defined(CONFIG_NAND_RDA_V1) && !defined(_TGT_AP_NAND_DISABLE_HEC)
+#define RDA_NAND_WITH_HEC
+#endif
+
/*
* Chip ID list
*
@@ -77,7 +84,7 @@ const struct nand_flash_dev nand_flash_ids[] = {
/*512 Megabit */
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
- {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
+ /*{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},*/
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
@@ -90,7 +97,7 @@ const struct nand_flash_dev nand_flash_ids[] = {
{"NAND 128MiB 3,3V 8-bit", 0xD1, 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},
- {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16},
+ {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16},
/* 2 Gigabit */
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
@@ -101,42 +108,43 @@ const struct nand_flash_dev nand_flash_ids[] = {
/* 4 Gigabit */
{"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 3,3V 8-bit", 0xDC, 0, 384, 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, LP_OPTIONS},
- {"NAND 1GiB 3,3V 8-bit", 0xD3, 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, LP_OPTIONS},
- {"NAND 2GiB 3,3V 8-bit", 0xD5, 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},
/* 32 Gigabit */
{"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS},
- {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
+// {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
{"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16},
{"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16},
/* 64 Gigabit */
{"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS},
- {"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
+ //{"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
{"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16},
{"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16},
/* 128 Gigabit */
{"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS},
- {"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
+ //{"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
{"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16},
{"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16},
/* 256 Gigabit */
{"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS},
- {"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
+ //{"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
{"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16},
{"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16},
@@ -161,6 +169,204 @@ const struct nand_flash_dev nand_flash_ids[] = {
BBT_AUTO_REFRESH
},
+ /*
+ * RDA Normal HW ECC support
+ * use 4K page as 4K page, support SLC nand only
+ */
+ {"NAND 512MB 1,8V 16-bit", 0xbc, 0, 512, 0x40000,
+ LP_OPTIONS16
+ },
+ /*
+ {"NAND 1,8V 16-bit slc", 0xbc, 4096, 512, 0x40000,
+ LP_OPTIONS16
+ },
+ */
+
+#ifdef RDA_NAND_WITH_HEC
+ /*
+ * RDA HEC support, for 4k MLC nand
+ * use 4K page as 2K page, leave 2K space for HEC
+ * therefore, chip_size should be half
+ * and erase_size should be 256*2k = 512k = 0x80000
+ * Now, supported NAND
+ * - Micron MT29F16xxxCA: ID = 2C 48 xx xx (16Gb use as a 8Gb NAND)
+ * - Micron MT29F32xxxCA: ID = 2C 68 xx xx (32Gb use as a 16Gb NAND)
+ * - sptek : ID = 2C 64 xx xx (32Gb use as a 16Gb NAND)
+ * - hynix : ID = AD D7 xx xx (32Gb use as a 16Gb NAND)
+ */
+ {"NAND 1GiB 3,3V 8-bit", 0x48, 2048, 1024, 0x80000,
+ LP_OPTIONS
+ },
+#if(__TGT_AP_FLASH_USE_PART_OF_PAGE__ == 1)
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_4KAS2K_PAGE_DIV__)
+ {"NAND 2GiB 3,3V 8-bit", 0x68, 2048, 2048, 0x80000,
+ LP_OPTIONS
+ },
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_4KAS3K_PAGE_DIV__)
+ {"NAND 3GiB 3,3V 8-bit", 0x68, 3072, 3072, 0xc0000,
+ LP_OPTIONS
+ },
+#endif
+#endif
+
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_8KAS6K_PAGE_DIV__)
+ /*
+ * RDA HEC support, for 8k MLC nand
+ * use 8K page as 6K page, leave 2K space for HEC
+ * therefore, chip_size should be three quarter
+ * and erase_size should be 256*6k = 1536k = 0x180000
+ * Now, supported NAND
+ * - Micron MT29F32xxxDA: ID = 2C 44 xx xx (32Gb use as a 24Gb NAND)
+ */
+ {"NAND 3GiB 3,3V 8-bit", 0x44, 6144, 3072, 0x180000,
+ LP_OPTIONS
+ },
+ {"NAND 6GiB 3,3V 8-bit", 0x64, 6144, 6144, 0x180000,
+ LP_OPTIONS
+ },
+/* //samsung 8k chip
+ {"NAND 3GiB 3,3V 8-bit", 0xd7, 6144, 3072, 0xc0000,
+ LP_OPTIONS
+ },
+*/
+ {"NAND 3GiB 3,3V 8-bit", 0xd7, 6144, 3072, 0x180000,
+ LP_OPTIONS
+ },
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_8KAS4K_PAGE_DIV__)
+ {"NAND 2GiB 3,3V 8-bit", 0x44, 4096, 2048, 0x100000,
+ LP_OPTIONS
+ },
+ {"NAND 4GiB 3,3V 8-bit", 0x64, 4096, 4096, 0x100000,
+ LP_OPTIONS
+ },
+/* //samsung 8k chip
+ {"NAND 2GiB 3,3V 8-bit", 0xd7, 4096, 2048, 0x80000,
+ LP_OPTIONS
+ },
+*/
+ {"NAND 2GiB 3,3V 8-bit", 0xd7, 4096, 2048, 0x100000,
+ LP_OPTIONS
+ },
+#endif
+#endif
+
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_16KAS8K_PAGE_DIV__)
+ {"NAND 2GiB 3,3V 8-bit", 0xd7, 8192, 2048, 0x200000,
+ LP_OPTIONS
+ },
+ {"NAND 4GiB 3,3V 8-bit", 0xde, 8192, 4096, 0x200000,
+ LP_OPTIONS
+ },
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_16KAS12K_PAGE_DIV__)
+ {"NAND 3GiB 3,3V 8-bit", 0xd7, 12288, 3072, 0x300000,
+ LP_OPTIONS
+ },
+ {"NAND 6GiB 3,3V 8-bit", 0xde, 12288, 6144, 0x300000,
+ LP_OPTIONS
+ },
+#endif
+#endif
+#else
+ {"NAND 2GiB 3,3V 8-bit", 0x68, 2048, 2048, 0x80000,
+ LP_OPTIONS
+ },
+ /*
+ * RDA HEC support, for 8k MLC nand
+ * use 8K page as 4K page, leave 4K space for HEC
+ * therefore, chip_size should be half
+ * and erase_size should be 256*4k = 1024k = 0x100000
+ * Now, supported NAND
+ * - Micron MT29F32xxxDA: ID = 2C 44 xx xx (32Gb use as a 16Gb NAND)
+ */
+ {"NAND 2GiB 3,3V 8-bit", 0x44, 4096, 2048, 0x100000,
+ LP_OPTIONS
+ },
+#endif
+ {"NAND 2GiB 3,3V 8-bit", 0x64, 2048, 2048, 0x80000,
+ LP_OPTIONS
+ },
+ {"NAND 2GiB 3,3V 8-bit", 0xd7, 2048, 2048, 0x80000,
+ LP_OPTIONS
+ },
+#else
+ /* 2K page SLC nand */
+ {"NAND 256MiB 3,3V 8-bit", 0xD2, 0, 256, 0, LP_OPTIONS},
+
+ /* 4K page MLC nand */
+ {"NAND 2GiB 3,3V 8-bit", 0x48, 4096, 2048, 0x100000,
+ LP_OPTIONS
+ },
+ {"NAND 4GiB 3,3V 8-bit", 0x68, 4096, 4096, 0x100000,
+ LP_OPTIONS
+ },
+ {"NAND 8GiB 3,3V 8-bit", 0x64, 8192, 8192, 0x200000,
+ LP_OPTIONS
+ },
+ /* 8K page MLC nand */
+ {"NAND 4GiB 3,3V 8-bit", 0x44, 8192, 4096, 0x200000,
+ LP_OPTIONS
+ },
+ /*block size is 3M, so chipsize is time of 3.*/
+ {"NAND 8GiB 3,3V 8-bit", 0x88, 8192, 8190, 0x300000,
+ LP_OPTIONS
+ },
+ /* 16K page MLC nand */
+ {"NAND 8GiB 3,3V 8-bit", 0xd3, 16384, 8192, 0x400000,
+ LP_OPTIONS
+ },
+ /* 16K page MLC nand */
+ {"NAND 8GiB 3,3V 8-bit", 0xd5, 16384, 8192, 0x400000,
+ LP_OPTIONS
+ },
+ /*This is conflict with a 8k nand*/
+ /* 16K page MLC nand */
+ {"NAND 8GiB 3,3V 8-bit", 0xd7, 16384, 4096, 0x400000,
+ LP_OPTIONS
+ },
+ /*
+ {"NAND 8GiB 3,3V 8-bit", 0xd7, 7168, 3584, 0x1c0000,
+ LP_OPTIONS
+ },
+ */
+ /* 16K page MLC nand */
+ {"NAND 8GiB 3,3V 8-bit", 0xde, 16384, 8192, 0x400000,
+ LP_OPTIONS
+ },
+ /*
+ {"NAND 8GiB 3,3V 8-bit", 0xde, 14336, 7168, 0x380000,
+ LP_OPTIONS
+ },
+ */
+ /* 16K page MLC nand */
+ {"NAND 16GiB 3,3V 8-bit", 0x3A, 16384, 16384, 0x400000,
+ LP_OPTIONS
+ },
+ /* 16K page MLC nand */
+ {"NAND 16GiB 3,3V 8-bit", 0x3C, 16384, 32768, 0x400000,
+ LP_OPTIONS
+ },
+#endif /* _TGT_AP_NAND_DISABLE_HEC */
+
+ /* RDA SPI nand support */
+ {"NAND 128MiB 1,8V 4-bit", 0xe1, 2048, 128, 0x20000,
+ LP_OPTIONS},
+ {"NAND 128MiB 3,3V 4-bit", 0xf1, 2048, 128, 0x20000,
+ LP_OPTIONS},
+ {"NAND 256MiB 1,8V 4-bit", 0xe2, 2048, 256, 0x20000,
+ LP_OPTIONS},
+ {"NAND 256MiB 3,3V 4-bit", 0xf2, 2048, 256, 0x20000,
+ LP_OPTIONS},
+ {"NAND 512MiB 1,8V 4-bit", 0xe4, 2048, 512, 0x20000,
+ LP_OPTIONS},
+ {"NAND 512MiB 3,3V 4-bit", 0xf4, 2048, 512, 0x20000,
+ LP_OPTIONS},
+ {"NAND 512MiB 1,8V 4-bit", 0xc4, 4096, 512, 0x40000,
+ LP_OPTIONS},
+ {"NAND 512MiB 3,3V 4-bit", 0xd4, 4096, 512, 0x40000,
+ LP_OPTIONS},
{NULL,}
};
@@ -177,5 +383,6 @@ const struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
{NAND_MFR_AMD, "AMD"},
+ {NAND_MFR_ESMT,"ESMT"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/nand_spl_load.c b/drivers/mtd/nand/nand_spl_load.c
index 215459a83c..bd6c988266 100644
--- a/drivers/mtd/nand/nand_spl_load.c
+++ b/drivers/mtd/nand/nand_spl_load.c
@@ -21,6 +21,10 @@
#include <common.h>
#include <nand.h>
+#ifdef CONFIG_SPL_CHECK_IMAGE
+int check_uimage(unsigned int *buf);
+#endif
+
/*
* The main entry for NAND booting. It's necessary that SDRAM is already
* configured and available since this code loads the main U-Boot image
@@ -28,23 +32,49 @@
*/
void nand_boot(void)
{
+ int ret = 0;
+
__attribute__((noreturn)) void (*uboot)(void);
/*
* Load U-Boot image from NAND into RAM
*/
- nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
+ ret = nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
CONFIG_SYS_NAND_U_BOOT_SIZE,
(void *)CONFIG_SYS_NAND_U_BOOT_DST);
+ if (0 != ret){
+ printf("Read u_boot from nand failed. \n");
+ return;
+ }
+
#ifdef CONFIG_NAND_ENV_DST
- nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
+ ret = nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
(void *)CONFIG_NAND_ENV_DST);
+ if (0 != ret){
+ printf("Read env from nand failed . \n");
+ return;
+ }
#ifdef CONFIG_ENV_OFFSET_REDUND
- nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
+ ret = nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
(void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
+ if (0 != ret){
+ printf("Read redund from nand failed . \n");
+ return;
+ }
+#endif
+#endif
+
+#ifdef CONFIG_SPL_CHECK_IMAGE
+ if (check_uimage((unsigned int*)CONFIG_SYS_NAND_U_BOOT_DST)) {
+ printf("Nand boot failed:DDR error or nand data error!\n");
+#ifdef CONFIG_RDA_FPGA
+ nand_dirty_block_erase(0);
#endif
+ printf("please try to load uboot.img or reset!\n");
+ return;
+ }
#endif
/*
diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c
index 4a4d02f4c8..33795db32f 100644
--- a/drivers/mtd/nand/nand_spl_simple.c
+++ b/drivers/mtd/nand/nand_spl_simple.c
@@ -22,11 +22,13 @@
#include <nand.h>
#include <asm/io.h>
#include <linux/mtd/nand_ecc.h>
+#include <mtd/nand/rda_nand.h>
-static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
static nand_info_t mtd;
static struct nand_chip nand_chip;
+#ifndef CONFIG_NAND_RDA
+static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
CONFIG_SYS_NAND_ECCSIZE)
#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES)
@@ -219,40 +221,178 @@ static int nand_read_page(int block, int page, void *dst)
}
#endif
-int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+#else
+#define MAX_OOBSIZE 32
+static int nand_is_bad_block(int block)
+{
+ struct nand_chip *this = mtd.priv;
+ int page = block * (mtd.erasesize / mtd.writesize);
+ static u_char oob[MAX_OOBSIZE];
+
+ this->cmdfunc(&mtd, NAND_CMD_READOOB, 0, page);
+ this->read_buf(&mtd, oob, 32);
+
+ /*
+ * Read one byte (or two if it's a 16 bit chip).
+ */
+ if (this->options & NAND_BUSWIDTH_16) {
+ unsigned short oob_w = (oob[1] << 8) | oob[0];
+ if (oob_w != 0xffff)
+ return 1;
+ } else {
+ if (oob[0] != 0xff)
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_RDA_FPGA
+void nand_dirty_block_erase(int page)
+{
+ struct nand_chip *this = mtd.priv;
+ /* Send commands to erase a block */
+ this->cmdfunc(&mtd, NAND_CMD_ERASE1, -1, page);
+ this->cmdfunc(&mtd, NAND_CMD_ERASE2, -1, -1);
+}
+#endif
+
+static int nand_read_page(int block, int page, uchar *dst)
+{
+ struct nand_chip *this = mtd.priv;
+ unsigned int page_cnt = mtd.erasesize / mtd.writesize;
+ int page_addr = page + block * page_cnt;
+
+ this->cmdfunc(&mtd, NAND_CMD_READ0, 0, page_addr);
+ this->read_buf(&mtd, dst, mtd.writesize);
+
+ return 0;
+}
+#endif //CONFIG_NAND_RDA
+
+int nand_spl_read_skip_bad(uint64_t offs, uint64_t size, u8 *dst,
+ u32 *skip_blocks)
{
unsigned int block, lastblock;
unsigned int page;
+ unsigned int page_count = 0, page_num;
+ unsigned int bad = 0;
+ unsigned int page_offset = offs % mtd.writesize;
+ unsigned int data_valid_len = mtd.writesize - page_offset;
+
+ if (page_offset) {/* u-boot is not page aligned */
+ block = offs / mtd.erasesize;
+ page = (offs / mtd.writesize) % (mtd.erasesize / mtd.writesize);
+ do {
+ if (!nand_is_bad_block(block)) {
+ nand_read_page(block, page, dst);
+ memcpy(dst, dst + page_offset, data_valid_len);
+ dst += data_valid_len;
+ offs += data_valid_len;
+ size -= data_valid_len;
+ } else {
+ bad++;
+ block++;
+ }
+ page_offset = offs % mtd.writesize;
+ } while(bad < 16 && page_offset);
+ }
/*
* offs has to be aligned to a page address!
*/
- block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
- lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
- page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
+ block = offs / mtd.erasesize;
+ lastblock = (offs + size + mtd.erasesize - 1) / mtd.erasesize;
+ page = (offs / mtd.writesize) % (mtd.erasesize / mtd.writesize);
+ page_num = (size + mtd.writesize - 1) / mtd.writesize;
- while (block <= lastblock) {
+ while (block < lastblock) {
if (!nand_is_bad_block(block)) {
/*
* Skip bad blocks
*/
- while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
+ while (page < (mtd.erasesize / mtd.writesize)) {
nand_read_page(block, page, dst);
- dst += CONFIG_SYS_NAND_PAGE_SIZE;
+ dst += mtd.writesize;
page++;
+ page_count++;
+ if(page_count >= page_num)
+ goto exit;
}
-
page = 0;
} else {
+ bad++;
+ if (bad >= 16) {
+ *skip_blocks = bad;
+ printf("too many bad blocks\n");
+ return -1;
+ }
lastblock++;
}
block++;
}
+exit:
+
+ *skip_blocks = bad;
+
+ if (0 == page_count){
+ printf("Error:read 0 page from nand \n");
+ return -1;
+ }
return 0;
}
+int nand_spl_load_image(uint64_t offs, uint64_t size, void *dst)
+{
+ u32 bad_blocks = 0;
+ struct rda_nand_info *info = nand_chip.priv;
+
+ printf("nand_spl_load_image: offs = 0x%llx, size = 0x%llx \n", offs, size);
+
+ offs = ((offs - CONFIG_MTD_PTBL_SIZE) * info->spl_adjust_ratio) / 2 + CONFIG_MTD_PTBL_SIZE;
+ //size -= (CONFIG_SPL_MAX_SIZE * info->spl_adjust_ratio) / 2 - CONFIG_SPL_MAX_SIZE;
+
+ printf("nand_spl_load_image,adjust: offs = 0x%llx, size = 0x%llx \n", offs, size);
+
+ if(nand_spl_read_skip_bad(offs, size, dst, &bad_blocks)) {
+ printf("fatal error, cannot load bootload....\n");
+ return -1;
+ }
+
+ if (bad_blocks > 0) {
+ printf("spl, there are %d bad block in bootloader partition",
+ bad_blocks);
+ }
+ return 0;
+}
+
+void nand_spl_mtd_info(u32 *page_size, u32 *block_size)
+{
+ *page_size = mtd.writesize;
+ *block_size = mtd.erasesize;
+}
+
+static int nand_sanity_check(void)
+{
+ int ret = 0;
+
+#if defined(NAND_PAGE_SIZE) && defined(NAND_BLOCK_SIZE)
+ if (mtd.writesize != NAND_PAGE_SIZE)
+ ret = -1;
+ if (mtd.erasesize != NAND_BLOCK_SIZE)
+ ret = -1;
+ if (ret) {
+ printf("nand parameters error: real pagesize: %d, blocksize %d\n",
+ mtd.writesize, mtd.erasesize);
+ printf("but We expect pagesize: %d, blocksize %d\n",
+ NAND_PAGE_SIZE, NAND_BLOCK_SIZE);
+ printf("please check device/rda/.../customer.mk\n");
+ }
+#endif
+ return ret;
+}
/* nand_init() - initialize data to make nand usable by SPL */
void nand_init(void)
{
@@ -264,15 +404,15 @@ void nand_init(void)
(void __iomem *)CONFIG_SYS_NAND_BASE;
board_nand_init(&nand_chip);
-#ifdef CONFIG_SPL_NAND_SOFTECC
- if (nand_chip.ecc.mode == NAND_ECC_SOFT) {
- nand_chip.ecc.calculate = nand_calculate_ecc;
- nand_chip.ecc.correct = nand_correct_data;
- }
-#endif
+ if (nand_chip.init_size)
+ nand_chip.init_size(&mtd, &nand_chip, NULL);
if (nand_chip.select_chip)
nand_chip.select_chip(&mtd, 0);
+
+ if(nand_sanity_check()) {
+ for(;;);
+ }
}
/* Unselect after operation */
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 60c778e637..8581a3a1a9 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -49,14 +49,16 @@
#include <linux/mtd/mtd.h>
#include <nand.h>
#include <jffs2/jffs2.h>
+#include <mmc/sparse.h>
typedef struct erase_info erase_info_t;
-typedef struct mtd_info mtd_info_t;
+typedef struct mtd_info mtd_info_t;
/* support only for native endian JFFS2 */
#define cpu_to_je16(x) (x)
#define cpu_to_je32(x) (x)
+extern void udc_irq(void);
/**
* nand_erase_opts: - erase NAND flash with support for various options
* (jffs2 formating)
@@ -68,11 +70,11 @@ typedef struct mtd_info mtd_info_t;
* This code is ported from flash_eraseall.c from Linux mtd utils by
* Arcom Control System Ltd.
*/
-int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
+int nand_erase_opts(nand_info_t * meminfo, const nand_erase_options_t * opts)
{
struct jffs2_unknown_node cleanmarker;
erase_info_t erase;
- unsigned long erase_length, erased_length; /* in blocks */
+ unsigned long erase_length, erased_length; /* in blocks */
int bbtest = 1;
int result;
int percent_complete = -1;
@@ -80,22 +82,29 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
struct mtd_oob_ops oob_opts;
struct nand_chip *chip = meminfo->priv;
- if ((opts->offset & (meminfo->writesize - 1)) != 0) {
- printf("Attempt to erase non page aligned data\n");
- return -1;
+ if (chip->page_shift == 0) {
+ if (mtd_mod_by_ws(opts->offset, meminfo) != 0){
+ printf("Attempt to erase non page aligned data\n");
+ return -1;
+ }
+ } else {
+ if ((opts->offset & (meminfo->writesize - 1)) != 0) {
+ printf("Attempt to erase non page aligned data\n");
+ return -1;
+ }
}
memset(&erase, 0, sizeof(erase));
memset(&oob_opts, 0, sizeof(oob_opts));
erase.mtd = meminfo;
- erase.len = meminfo->erasesize;
+ erase.len = meminfo->erasesize;
erase.addr = opts->offset;
erase_length = lldiv(opts->length + meminfo->erasesize - 1,
meminfo->erasesize);
- cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
- cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
cleanmarker.totlen = cpu_to_je32(8);
/* scrub option allows to erase badblock. To prevent internal
@@ -115,10 +124,10 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
}
for (erased_length = 0;
- erased_length < erase_length;
- erase.addr += meminfo->erasesize) {
+ erased_length < erase_length; erase.addr += meminfo->erasesize) {
- WATCHDOG_RESET ();
+ WATCHDOG_RESET();
+ udc_irq();
if (!opts->scrub && bbtest) {
int ret = meminfo->block_isbad(meminfo, erase.addr);
@@ -136,8 +145,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
} else if (ret < 0) {
printf("\n%s: MTD get bad block failed: %d\n",
- mtd_device,
- ret);
+ mtd_device, ret);
return -1;
}
}
@@ -155,13 +163,12 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) {
chip->ops.ooblen = 8;
chip->ops.datbuf = NULL;
- chip->ops.oobbuf = (uint8_t *)&cleanmarker;
+ chip->ops.oobbuf = (uint8_t *) & cleanmarker;
chip->ops.ooboffs = 0;
chip->ops.mode = MTD_OOB_AUTO;
result = meminfo->write_oob(meminfo,
- erase.addr,
- &chip->ops);
+ erase.addr, &chip->ops);
if (result != 0) {
printf("\n%s: MTD writeoob failure: %d\n",
mtd_device, result);
@@ -183,12 +190,13 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
if (percent != percent_complete) {
percent_complete = percent;
- printf("\rErasing at 0x%llx -- %3d%% complete.",
+ printf("\rErasing at 0x%llx -- %3d%% complete.\n",
erase.addr, percent);
if (opts->jffs2 && result == 0)
- printf(" Cleanmarker written at 0x%llx.",
- erase.addr);
+ printf
+ (" Cleanmarker written at 0x%llx.",
+ erase.addr);
}
}
}
@@ -244,8 +252,7 @@ int nand_lock(struct mtd_info *mtd, int tight)
chip->select_chip(mtd, 0);
chip->cmdfunc(mtd,
- (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),
- -1, -1);
+ (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), -1, -1);
/* call wait ready function */
status = chip->waitfunc(mtd, chip);
@@ -283,27 +290,42 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
struct nand_chip *chip = mtd->priv;
/* select the NAND device */
- chipnr = (int)(offset >> chip->chip_shift);
+ if (chip->chip_shift == 0)
+ chipnr = mtd_div_by_cs(offset, mtd);
+ else
+ chipnr = (int)(offset >> chip->chip_shift);
+
chip->select_chip(mtd, chipnr);
+ if (chip->page_shift == 0) {
+ if (mtd_mod_by_ws(offset, mtd) != 0){
+ printf("nand_get_lock_status: "
+ "Start address must be beginning of " "nand page!\n");
+ ret = -1;
+ goto out;
+ }
- if ((offset & (mtd->writesize - 1)) != 0) {
- printf ("nand_get_lock_status: "
- "Start address must be beginning of "
- "nand page!\n");
- ret = -1;
- goto out;
+ /* check the Lock Status */
+ page = mtd_div_by_ws(offset, mtd);
+ } else {
+ if ((offset & (mtd->writesize - 1)) != 0) {
+ printf("nand_get_lock_status: "
+ "Start address must be beginning of " "nand page!\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* check the Lock Status */
+ page = (int)(offset >> chip->page_shift);
}
- /* check the Lock Status */
- page = (int)(offset >> chip->page_shift);
chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT
- | NAND_LOCK_STATUS_LOCK
- | NAND_LOCK_STATUS_UNLOCK);
+ | NAND_LOCK_STATUS_LOCK
+ | NAND_LOCK_STATUS_UNLOCK);
- out:
+out:
/* de-select the NAND device */
chip->select_chip(mtd, -1);
return ret;
@@ -327,33 +349,53 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
int status;
int page;
struct nand_chip *chip = mtd->priv;
- printf ("nand_unlock: start: %08x, length: %d!\n",
- (int)start, (int)length);
+ printf("nand_unlock: start: %08x, length: %d!\n",
+ (int)start, (int)length);
/* select the NAND device */
- chipnr = (int)(start >> chip->chip_shift);
+ if (chip->chip_shift == 0)
+ chipnr = mtd_div_by_cs(start, mtd);
+ else
+ chipnr = (int)(start >> chip->chip_shift);
+
chip->select_chip(mtd, chipnr);
/* check the WP bit */
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) {
- printf ("nand_unlock: Device is write protected!\n");
+ printf("nand_unlock: Device is write protected!\n");
ret = -1;
goto out;
}
- if ((start & (mtd->erasesize - 1)) != 0) {
- printf ("nand_unlock: Start address must be beginning of "
- "nand block!\n");
- ret = -1;
- goto out;
- }
+ if (chip->phy_erase_shift == 0) {
+ if (mtd_mod_by_eb(start, mtd) != 0) {
+ printf("nand_unlock: Start address must be beginning of "
+ "nand block!\n");
+ ret = -1;
+ goto out;
+ }
- if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
- printf ("nand_unlock: Length must be a multiple of nand block "
- "size %08x!\n", mtd->erasesize);
- ret = -1;
- goto out;
+ if (length == 0 || mtd_mod_by_eb(length, mtd) != 0) {
+ printf("nand_unlock: Length must be a multiple of nand block "
+ "size %08x!\n", mtd->erasesize);
+ ret = -1;
+ goto out;
+ }
+ } else {
+ if ((start & (mtd->erasesize - 1)) != 0) {
+ printf("nand_unlock: Start address must be beginning of "
+ "nand block!\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
+ printf("nand_unlock: Length must be a multiple of nand block "
+ "size %08x!\n", mtd->erasesize);
+ ret = -1;
+ goto out;
+ }
}
/*
@@ -363,11 +405,19 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
length -= mtd->erasesize;
/* submit address of first page to unlock */
- page = (int)(start >> chip->page_shift);
+ if (chip->page_shift == 0)
+ page = mtd_div_by_ws(start, mtd);
+ else
+ page = (int)(start >> chip->page_shift);
+
chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
/* submit ADDRESS of LAST page to unlock */
- page += (int)(length >> chip->page_shift);
+ if (chip->page_shift == 0)
+ page += mtd_div_by_ws(length, mtd);
+ else
+ page += (int)(length >> chip->page_shift);
+
chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask);
/* call wait ready function */
@@ -379,7 +429,7 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
goto out;
}
- out:
+out:
/* de-select the NAND device */
chip->select_chip(mtd, -1);
return ret;
@@ -392,27 +442,39 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
* Check if there are any bad blocks, and whether length including bad
* blocks fits into device
*
- * @param nand NAND device
- * @param offset offset in flash
- * @param length image length
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param length image length
+ * @param max_limit maximun access offset in flash
* @return 0 if the image fits and there are no bad blocks
* 1 if the image fits, but there are bad blocks
* -1 if the image does not fit
*/
-static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
+static int check_skip_len(nand_info_t * nand, loff_t offset, size_t length,
+ loff_t max_limit)
{
size_t len_excl_bad = 0;
int ret = 0;
+ struct nand_chip *chip = nand->priv;
+
+ if (max_limit == 0)
+ max_limit = nand->size;
while (len_excl_bad < length) {
size_t block_len, block_off;
loff_t block_start;
- if (offset >= nand->size)
+ if (offset >= max_limit)
return -1;
- block_start = offset & ~(loff_t)(nand->erasesize - 1);
- block_off = offset & (nand->erasesize - 1);
+ if (chip->phys_erase_shift == 0) {
+ block_start = mtd_div_by_eb(offset, nand) * nand->erasesize;
+ block_off = mtd_mod_by_eb(offset, nand);
+ } else {
+ block_start = offset & ~(loff_t) (nand->erasesize - 1);
+ block_off = offset & (nand->erasesize - 1);
+ }
+
block_len = nand->erasesize - block_off;
if (!nand_block_isbad(nand, block_start))
@@ -427,8 +489,8 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
}
#ifdef CONFIG_CMD_NAND_TRIMFFS
-static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
- const size_t *len)
+static size_t drop_ffs(const nand_info_t * nand, const u_char * buf,
+ const size_t * len)
{
size_t i, l = *len;
@@ -439,7 +501,7 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
/* The resulting length must be aligned to the minimum flash I/O size */
l = i + 1;
l = (l + nand->writesize - 1) / nand->writesize;
- l *= nand->writesize;
+ l *= nand->writesize;
/*
* since the input length may be unaligned, prevent access past the end
@@ -450,7 +512,7 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
#endif
/**
- * nand_write_skip_bad:
+ * nand_write_skip_bad_new:
*
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
@@ -460,18 +522,23 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
- * @param buffer buffer to read from
+ * @param max_limit maximun access offset in flash
+ * @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
+ * @skip_blocks the blocks has been skiped in this write
* @return 0 in case of success
*/
-int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer, int flags)
+int nand_write_skip_bad_new(nand_info_t * nand, loff_t offset, size_t * length,
+ loff_t max_limit, u_char * buffer, int flags,
+ u32 * skip_blocks)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
u_char *p_buffer = buffer;
int need_skip;
+ struct nand_chip *chip = nand->priv;
+ *skip_blocks = 0;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)
@@ -481,8 +548,8 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
- printf ("Attempt to write incomplete page"
- " in yaffs mode\n");
+ printf("Attempt to write incomplete page"
+ " in yaffs mode\n");
return -EINVAL;
}
} else
@@ -502,41 +569,61 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
* you should only start a block skipping access at a
* partition boundary). So don't try to handle that.
*/
- if ((offset & (nand->writesize - 1)) != 0) {
- printf ("Attempt to write non page aligned data\n");
- *length = 0;
- return -EINVAL;
+ if (chip->page_shift == 0) {
+ if (mtd_mod_by_ws(offset, nand) != 0) {
+ printf("Attempt to write non page aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
+ } else {
+ if ((offset & (nand->writesize - 1)) != 0) {
+ printf("Attempt to write non page aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
}
- need_skip = check_skip_len(nand, offset, *length);
+ need_skip = check_skip_len(nand, offset, *length, max_limit);
if (need_skip < 0) {
- printf ("Attempt to write outside the flash area\n");
+ printf("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
- if (!need_skip && !(flags & WITH_DROP_FFS)) {
- rval = nand_write (nand, offset, length, buffer);
+ if (!need_skip && !(flags & WITH_DROP_FFS) && !(flags & WITH_YAFFS_OOB)) {
+ rval = nand_write(nand, offset, length, buffer);
if (rval == 0)
return 0;
*length = 0;
- printf ("NAND write to offset %llx failed %d\n",
- offset, rval);
+ printf("NAND write to offset %llx failed %d\n", offset, rval);
return rval;
}
while (left_to_write > 0) {
- size_t block_offset = offset & (nand->erasesize - 1);
- size_t write_size, truncated_write_size;
+ size_t write_size, truncated_write_size, block_offset;
- WATCHDOG_RESET ();
+ WATCHDOG_RESET();
- if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
- printf ("Skip bad block 0x%08llx\n",
- offset & ~(nand->erasesize - 1));
- offset += nand->erasesize - block_offset;
- continue;
+ if (chip->phys_erase_shift == 0) {
+ block_offset = mtd_mod_by_eb(offset, nand);
+
+ if (nand_block_isbad(nand, mtd_div_by_eb(offset, nand) * nand->erasesize)) {
+ printf("Skip bad block 0x%08llx\n",
+ (loff_t)mtd_div_by_eb(offset, nand) * nand->erasesize);
+ offset += nand->erasesize - block_offset;
+ *skip_blocks += 1;
+ continue;
+ }
+ }else {
+ block_offset = offset & (nand->erasesize - 1);
+
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
+ printf("Skip bad block 0x%08llx\n", offset & ~(nand->erasesize - 1));
+ offset += nand->erasesize - block_offset;
+ *skip_blocks += 1;
+ continue;
+ }
}
if (left_to_write < (blocksize - block_offset))
@@ -564,32 +651,31 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
ops.oobbuf = ops.datbuf + pagesize;
rval = nand->write_oob(nand, offset, &ops);
- if (!rval)
+ if (rval != 0)
break;
offset += pagesize;
p_buffer += pagesize_oob;
}
- }
- else
+ } else
#endif
{
truncated_write_size = write_size;
#ifdef CONFIG_CMD_NAND_TRIMFFS
if (flags & WITH_DROP_FFS)
truncated_write_size = drop_ffs(nand, p_buffer,
- &write_size);
+ &write_size);
#endif
rval = nand_write(nand, offset, &truncated_write_size,
- p_buffer);
+ p_buffer);
offset += write_size;
p_buffer += write_size;
}
if (rval != 0) {
- printf ("NAND write to offset %llx failed %d\n",
- offset, rval);
+ printf("NAND write to offset %llx failed %d\n",
+ offset, rval);
*length -= left_to_write;
return rval;
}
@@ -600,8 +686,358 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
return 0;
}
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SECTOR_SIZE 512
+struct rnftl_oobinfo_t {
+ int16_t sect;
+ int16_t ec; //00, Bad
+ int16_t vtblk; //-1 , free,-2~-10, Snapshot
+ unsigned timestamp:15;
+ unsigned status_page:1;
+ int8_t maigic[4];
+ uint8_t reserved[2];
+};
+
+static int sparse_parse(unsigned char *buffer, sparse_header_t ** header,
+ loff_t max_limit)
+{
+ sparse_header_t *pheader;
+
+ pheader = (sparse_header_t *) buffer;
+ if (pheader->magic != SPARSE_HEADER_MAGIC) {
+ printf("sparse: bad magic.\n");
+ return -1;
+ }
+
+ if (((u64) pheader->total_blks * pheader->blk_sz) > max_limit) {
+ printf("sparse: section size %08llx MB limit: exceeded\n",
+ max_limit / (1024 * 1024));
+ return -1;
+ }
+
+ if ((pheader->major_version != SPARSE_HEADER_MAJOR_VER) ||
+ (pheader->file_hdr_sz != sizeof(sparse_header_t)) ||
+ (pheader->chunk_hdr_sz != sizeof(chunk_header_t))) {
+ printf("sparse: incompatible format\n");
+ return -1;
+ }
+ *header = pheader;
+ return 0;
+}
+
+static int trunk_parse(unsigned char *buffer, sparse_header_t * sparse_header,
+ chunk_header_t ** header)
+{
+ chunk_header_t *pheader;
+
+ pheader = (chunk_header_t *) buffer;
+ if (pheader->chunk_type != CHUNK_TYPE_RAW &&
+ pheader->chunk_type != CHUNK_TYPE_DONT_CARE) {
+ printf("chunk: unknow chunk type:0x%x.\n", pheader->chunk_type);
+ return -1;
+ }
+
+ if (pheader->chunk_sz == 0) {
+ printf("chunk: chunk_sz = 0.\n");
+ return -1;
+ }
+
+ if (pheader->chunk_type == CHUNK_TYPE_RAW &&
+ pheader->total_sz !=
+ (u64) pheader->chunk_sz * sparse_header->blk_sz +
+ sizeof(chunk_header_t)) {
+ printf
+ ("chunk: total_sz error.total_size = 0x%x,chunk_sz = 0x%x.\n",
+ pheader->total_sz, pheader->chunk_sz);
+ return -1;
+ }
+
+ if (pheader->chunk_type == CHUNK_TYPE_DONT_CARE &&
+ pheader->total_sz != sizeof(chunk_header_t)) {
+ printf
+ ("chunk: total_sz error.total_size = 0x%x,chunk_sz = 0x%x.\n",
+ pheader->total_sz, pheader->chunk_sz);
+ return -1;
+ }
+ *header = pheader;
+ return 0;
+}
+
+static int nand_write_nftl(nand_info_t * nand, loff_t offset,
+ unsigned char *buffer, int16_t vt_block,
+ int16_t vt_sect, uint16_t timestamp)
+{
+ int rval;
+ struct mtd_oob_ops ops;
+ struct rnftl_oobinfo_t nftl_oob;
+
+ memset(&ops, 0, sizeof(ops));
+ memset(&nftl_oob, 0, sizeof(nftl_oob));
+ // ops
+ ops.len = nand->writesize;
+ ops.ooblen = sizeof(nftl_oob);
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooboffs = 2;
+ ops.oobbuf = (uint8_t *) & nftl_oob;
+ ops.datbuf = buffer;
+ //nftl_oob
+ nftl_oob.ec = 0;
+ nftl_oob.status_page = 1;
+ memcpy(&(nftl_oob.maigic), "rda", 3);
+ nftl_oob.sect = vt_sect;
+ nftl_oob.vtblk = vt_block;
+ nftl_oob.timestamp = timestamp;
+
+ rval = nand->write_oob(nand, offset, &ops);
+ if (rval != 0) {
+ printf("write_oob fail, rval = %d.\n", rval);
+ return -1;
+ }
+ return 0;
+}
+
+int nand_write_unsparse(nand_info_t * nand, loff_t offset, size_t * length,
+ loff_t max_limit, unsigned char *buffer, int flags,
+ u32 * skip_blocks)
+{
+ int rval = 0;
+ u32 trunk_nr;
+ u64 chunk_len = 0;
+ u64 outlen = 0;
+ loff_t base_offset;
+ uint8_t *page_buffer = NULL;
+ int16_t page_len = 0;
+ int16_t page_per_block;
+ int16_t vt_block = 0;
+ int16_t vt_sect = 0;
+ uint16_t timestamp = 0;
+ sparse_header_t *sparse_h;
+ struct nand_chip *chip = nand->priv;
+
+ printf("nand_write_unsparse begin.\n");
+ printf("offset:0x%08llx.\n", offset);
+ printf("max_limit:0x%08llx.\n", max_limit);
+ printf("flags:0x%x.\n", *length);
+ printf("flags:0x%x.\n", flags);
+ printf("skip_blocks:0x%x.\n", *skip_blocks);
+ base_offset = offset;
+ page_per_block = (nand->erasesize / nand->writesize);
+ rval = sparse_parse(buffer, &sparse_h, max_limit);
+ if (rval) {
+ printf("nand_unsprase: sparse_parse fail.\n");
+ return -1;
+ }
+ buffer += sizeof(sparse_header_t);
+ (*length) -= sizeof(sparse_header_t);
+ page_buffer = malloc(nand->writesize);
+ for (trunk_nr = 0; trunk_nr < sparse_h->total_chunks; trunk_nr++) {
+ chunk_header_t *chunk_h;
+
+ rval = trunk_parse(buffer, sparse_h, &chunk_h);
+ if (rval) {
+ printf("nand_unsprase: trunk_parse fail.\n");
+ free(page_buffer);
+ return -1;
+ }
+ /*
+ printf("trunk:0x%x,0x%x,0x%x.\n",
+ trunk_nr,chunk_h->chunk_type,chunk_h->chunk_sz);
+ */
+ buffer += sizeof(chunk_header_t);
+ (*length) -= sizeof(chunk_header_t);
+ chunk_len = (u64) chunk_h->chunk_sz * (u64) sparse_h->blk_sz;
+ while (chunk_len > 0) {
+ uint8_t *data_buff = page_buffer;
+ if (chunk_h->chunk_type == CHUNK_TYPE_RAW) {
+ memcpy(data_buff + page_len, buffer,
+ SECTOR_SIZE);
+ buffer += SECTOR_SIZE;
+ (*length) -= SECTOR_SIZE;
+ page_len += SECTOR_SIZE;
+ chunk_len -= SECTOR_SIZE;
+
+ } else if (chunk_h->chunk_type == CHUNK_TYPE_DONT_CARE) {
+ if(chunk_len >= (nand->writesize - page_len))
+ chunk_len -= (nand->writesize - page_len);
+ else
+ chunk_len = 0;
+ if (page_len) {
+ printf("write spare data.page_len = 0x%x.\n",
+ page_len);
+ /* memset(data_buff + page_len, 0,nand->writesize - page_len); */
+ } else {
+ /* memset(data_buff, 0,nand->writesize); */
+ data_buff = NULL;
+ }
+ page_len = nand->writesize;
+ } else {
+ printf("nand_unsprase:"
+ "Unknow Trunk type: 0x%x.\n",
+ chunk_h->chunk_type);
+ break;
+ }
+ if (page_len == nand->writesize) {
+ /* skip bad block. */
+ do {
+ size_t block_offset;
+ if (chip->phys_erase_shift == 0) {
+ block_offset = mtd_mod_by_eb(offset, nand);
+ if (nand_block_isbad(nand, mtd_div_by_eb(offset, nand) * nand->erasesize)) {
+ printf("nand_unsprase:Skip bad block 0x%08llx.\n",
+ (loff_t)mtd_div_by_eb(offset, nand) * nand->erasesize);
+ offset += nand->erasesize -block_offset;
+ (*skip_blocks)++;
+ continue;
+ } else {
+ break;
+ }
+ } else {
+ block_offset = offset & (nand->erasesize - 1);
+ if (nand_block_isbad(nand,offset & ~(nand->erasesize - 1))) {
+ printf("nand_unsprase:Skip bad block 0x%08llx.\n",
+ offset & ~(nand->erasesize -1));
+ offset += nand->erasesize -block_offset;
+ (*skip_blocks)++;
+ continue;
+ } else {
+ break;
+ }
+ }
+ } while (1);
+
+ if (offset + nand->writesize >
+ base_offset + max_limit) {
+ printf("nand_unsprase:"
+ "exceed.section max_limit:0x%08llx,offset:0x%08llx.\n",
+ max_limit,
+ offset + nand->writesize -
+ base_offset);
+ free(page_buffer);
+ return -1;
+ }
+ rval = nand_write_nftl(nand, offset, data_buff,
+ vt_block, vt_sect,
+ timestamp);
+ if (rval) {
+ printf("nand_unsprase: nand_write_nftl fail.\n");
+ free(page_buffer);
+ return -1;
+ }
+ page_len = 0;
+ if (vt_sect + 1 == page_per_block) {
+ vt_block++;
+ vt_sect = 0;
+ timestamp = timestamp == 0x7fff ? 0 : timestamp + 1;
+ if ((vt_block % 8) == 0
+ && vt_block >= 8) {
+ printf("write block:0x%x - 0x%x.\n",
+ vt_block - 8, vt_block - 1);
+ }
+ } else {
+ vt_sect++;
+ }
+ offset += nand->writesize;
+ outlen += nand->writesize;
+ }
+ }
+ }
+ if (page_len > 0) {
+ printf("nand_unsprase:last spare data: 0x%x,0x%x,0x%x.\n",
+ vt_block, vt_sect, page_len);
+ /* memset(page_buffer + page_len, 0, nand->writesize - page_len); */
+ if (offset + nand->writesize > base_offset + max_limit) {
+ printf("nand_unsprase:"
+ "exceed max_limit:0x%08llx,offset:0x%08llx.\n",
+ max_limit,
+ offset + nand->writesize - base_offset);
+ free(page_buffer);
+ return -1;
+ }
+ /* skip bad block. */
+ do {
+ size_t block_offset;
+ if (chip->phys_erase_shift == 0) {
+ block_offset = mtd_mod_by_eb(offset, nand);
+ if (nand_block_isbad(nand, mtd_div_by_eb(offset, nand) * nand->erasesize)) {
+ printf("Skip bad block 0x%08llx.\n",
+ (loff_t)mtd_div_by_eb(offset, nand) * nand->erasesize);
+ offset += nand->erasesize - block_offset;
+ (*skip_blocks)++;
+ continue;
+ } else {
+ break;
+ }
+ } else {
+ block_offset = offset & (nand->erasesize - 1);
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
+ printf("Skip bad block 0x%08llx.\n",
+ offset & ~(nand->erasesize - 1));
+ offset += nand->erasesize - block_offset;
+ (*skip_blocks)++;
+ continue;
+ } else {
+ break;
+ }
+ }
+ } while (1);
+
+ rval = nand_write_nftl(nand, offset, page_buffer,
+ vt_block, vt_sect, timestamp);
+ if (rval) {
+ printf("nand_unsprase: nand_write_nftl fail.\n");
+ free(page_buffer);
+ return -1;
+ }
+ page_len = 0;
+ if (vt_sect + 1 == page_per_block) {
+ vt_block++;
+ vt_sect = 0;
+ timestamp = timestamp == 0x7fff ? 0 : timestamp + 1;
+ } else {
+ vt_sect++;
+ }
+ offset += nand->writesize;
+ outlen += nand->writesize;
+ }
+ if (vt_block % 8) {
+ printf("write block:0x%x - 0x%x,vt_sector:0x%x.\n",
+ vt_block - (vt_block % 8), vt_block, vt_sect);
+ }
+ /*
+ while(offset + nand->writesize <= max_limit)
+ {
+ rval = nand_write_nftl(nand, offset, NULL,
+ vt_block, vt_sect,timestamp);
+ if (rval) {
+ printf("nand_unsprase: nand_write_nftl fail.\n");
+ free(page_buffer);
+ return -1;
+ }
+ if (vt_sect + 1 == page_per_block) {
+ vt_block++;
+ vt_sect = 0;
+ timestamp = timestamp == 0x7fff ?
+ 0 : timestamp + 1;
+ } else {
+ vt_sect++;
+ }
+ offset += nand->writesize;
+ outlen += nand->writesize;
+ }
+ */
+ free(page_buffer);
+ printf("nand_unsprase end.\n");
+ // printf("vt_block:0x%x.\n", vt_block);
+ // printf("vt_sect:0x%x.\n", vt_sect);
+ printf("*length:0x%x.\n", *length);
+ printf("offset:0x%08llx.\n", offset);
+ printf("out-length:0x%08llx.\n", outlen);
+ printf("skip_blocks:0x%x.\n", *skip_blocks);
+ return 0;
+}
+
/**
- * nand_read_skip_bad:
+ * nand_read_skip_bad_new:
*
* Read image from NAND flash.
* Blocks that are marked bad are skipped and the next block is readen
@@ -612,51 +1048,75 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
* @param offset offset in flash
* @param length buffer length, on return holds remaining bytes to read
* @param buffer buffer to write to
+ * @param skip_block the bad block skipped
* @return 0 in case of success
*/
-int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer)
+int nand_read_skip_bad_new(nand_info_t * nand, loff_t offset,
+ size_t * length, u_char * buffer, u32 * skip_blocks)
{
int rval;
size_t left_to_read = *length;
u_char *p_buffer = buffer;
int need_skip;
+ struct nand_chip *chip = nand->priv;
- if ((offset & (nand->writesize - 1)) != 0) {
- printf ("Attempt to read non page aligned data\n");
- *length = 0;
- return -EINVAL;
+ *skip_blocks = 0;
+ if (chip->page_shift == 0) {
+ if (mtd_mod_by_ws(offset, nand) != 0) {
+ printf("Attempt to read non page aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
+ } else {
+ if ((offset & (nand->writesize - 1)) != 0) {
+ printf("Attempt to read non page aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
}
- need_skip = check_skip_len(nand, offset, *length);
+ need_skip = check_skip_len(nand, offset, *length, 0);
if (need_skip < 0) {
- printf ("Attempt to read outside the flash area\n");
+ printf("Attempt to read outside the flash area\n");
*length = 0;
return -EINVAL;
}
if (!need_skip) {
- rval = nand_read (nand, offset, length, buffer);
+ rval = nand_read(nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
return 0;
*length = 0;
- printf ("NAND read from offset %llx failed %d\n",
- offset, rval);
+ printf("NAND read from offset %llx failed %d\n", offset, rval);
return rval;
}
while (left_to_read > 0) {
- size_t block_offset = offset & (nand->erasesize - 1);
+ size_t block_offset;
size_t read_length;
- WATCHDOG_RESET ();
+ WATCHDOG_RESET();
+ if (chip->phys_erase_shift == 0) {
+ block_offset = mtd_mod_by_eb(offset, nand);
- if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
- printf ("Skipping bad block 0x%08llx\n",
- offset & ~(nand->erasesize - 1));
- offset += nand->erasesize - block_offset;
- continue;
+ if (nand_block_isbad(nand, mtd_div_by_eb(offset, nand) * nand->erasesize)) {
+ printf("Skipping bad block 0x%08llx\n",
+ (loff_t)mtd_div_by_eb(offset, nand) * nand->erasesize);
+ offset += nand->erasesize - block_offset;
+ *skip_blocks += 1;
+ continue;
+ }
+ } else {
+ block_offset = offset & (nand->erasesize - 1);
+
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
+ printf("Skipping bad block 0x%08llx\n",
+ offset & ~(nand->erasesize - 1));
+ offset += nand->erasesize - block_offset;
+ *skip_blocks += 1;
+ continue;
+ }
}
if (left_to_read < (nand->erasesize - block_offset))
@@ -664,18 +1124,65 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
else
read_length = nand->erasesize - block_offset;
- rval = nand_read (nand, offset, &read_length, p_buffer);
+ rval = nand_read(nand, offset, &read_length, p_buffer);
if (rval && rval != -EUCLEAN) {
- printf ("NAND read from offset %llx failed %d\n",
- offset, rval);
+ printf("NAND read from offset %llx failed %d\n",
+ offset, rval);
*length -= left_to_read;
return rval;
}
left_to_read -= read_length;
- offset += read_length;
- p_buffer += read_length;
+ offset += read_length;
+ p_buffer += read_length;
}
return 0;
}
+
+/**
+ * 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 buffer buffer to read from
+ * @param flags flags modifying the behaviour of the write to NAND
+ * @return 0 in case of success
+ */
+int nand_write_skip_bad(nand_info_t * nand, loff_t offset, size_t * length,
+ u_char * buffer, int flags)
+{
+ u32 bad_blocks = 0;
+
+ return nand_write_skip_bad_new(nand, offset, length, 0,
+ buffer, flags, &bad_blocks);
+}
+
+/**
+ * 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, loff_t offset, size_t * length,
+ u_char * buffer)
+{
+ u32 bad_blocks = 0;
+
+ return nand_read_skip_bad_new(nand, offset, length, buffer,
+ &bad_blocks);
+}
diff --git a/drivers/mtd/nand/rda_nand.c b/drivers/mtd/nand/rda_nand.c
new file mode 100644
index 0000000000..54bb77bc73
--- /dev/null
+++ b/drivers/mtd/nand/rda_nand.c
@@ -0,0 +1,12 @@
+#include <common.h>
+
+#ifdef CONFIG_NAND_RDA_V1
+#include "rda_nand_v1.c"
+#elif defined(CONFIG_NAND_RDA_V2)
+#include "rda_nand_v2.c"
+#elif defined(CONFIG_NAND_RDA_V3)
+#include "rda_nand_v3.c"
+#else
+#error "undefined RDA NAND Controller Version"
+#endif
+
diff --git a/drivers/mtd/nand/rda_nand_base.c b/drivers/mtd/nand/rda_nand_base.c
new file mode 100644
index 0000000000..ae3d01fd49
--- /dev/null
+++ b/drivers/mtd/nand/rda_nand_base.c
@@ -0,0 +1,95 @@
+#include <common.h>
+#include <asm/types.h>
+#include <nand.h>
+
+#include <asm/errno.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_sysctrl.h>
+#include <asm/arch/hwcfg.h>
+#include <mtd/nand/rda_nand.h>
+extern int rda_nand_init(struct nand_chip *nand);
+extern int rda_spi_nand_init(struct nand_chip *nand);
+
+int board_nand_init(struct nand_chip *chip) __attribute__ ((weak));
+
+static int flash_intf_spi = 0;
+
+#if defined(CONFIG_MACH_RDA8810)
+int board_nand_init(struct nand_chip *chip)
+{
+ u16 hwcfg = rda_hwcfg_get();
+ u16 metal_id = rda_metal_id_get();
+
+ if (!(hwcfg & RDA_HW_CFG_BIT_3) && (hwcfg & RDA_HW_CFG_BIT_2)) {
+ printf("metal %d hwcfg %x, use eMMC, skip nand init\n",
+ metal_id, hwcfg);
+ return -ENODEV;
+ }
+
+ if ((metal_id >= 7) && (hwcfg & RDA_HW_CFG_BIT_3)) {
+ printf("metal %d hwcfg %x, use SPI NAND\n",
+ metal_id, hwcfg);
+ flash_intf_spi = 1;
+ return rda_spi_nand_init(chip);
+ } else {
+ printf("metal %d hwcfg %x, use NAND\n",
+ metal_id, hwcfg);
+ return rda_nand_init(chip);
+ }
+}
+#else /* 8810H*/
+#if defined(CONFIG_MACH_RDA8810H)
+int board_nand_init(struct nand_chip *chip)
+{
+ u16 hwcfg = rda_hwcfg_get();
+ u16 metal_id = rda_metal_id_get();
+ enum media_type media = rda_media_get();
+
+ if (media == MEDIA_MMC) {
+ printf("metal %d hwcfg %x, use eMMC, skip nand init\n",
+ metal_id, hwcfg);
+ return -ENODEV;
+ } else if (media == MEDIA_SPINAND) {
+ printf("metal %d hwcfg %x, use SPI NAND\n",
+ metal_id, hwcfg);
+ flash_intf_spi = 1;
+ return rda_spi_nand_init(chip);
+ } else if (media == MEDIA_NAND) {
+ printf("metal %d hwcfg %x, use NAND\n",
+ metal_id, hwcfg);
+ return rda_nand_init(chip);
+ } else {
+ printf("invalid bootmode:hwcfg=0x%x\n", hwcfg);
+ return -ENODEV;
+ }
+}
+#else /* 8810E, 8820, 8850, 8850e */
+int board_nand_init(struct nand_chip *chip)
+{
+ u16 hwcfg = rda_hwcfg_get();
+ u16 metal_id = rda_metal_id_get();
+
+ if (!(hwcfg & RDA_HW_CFG_BIT_3) && (hwcfg & RDA_HW_CFG_BIT_2)) {
+ printf("metal %d hwcfg %x, use eMMC, skip nand init\n",
+ metal_id, hwcfg);
+ return -ENODEV;
+ }
+
+ if (hwcfg & RDA_HW_CFG_BIT_3) {
+ printf("metal %d hwcfg %x, use SPI NAND\n",
+ metal_id, hwcfg);
+ flash_intf_spi = 1;
+ return rda_spi_nand_init(chip);
+ } else {
+ printf("metal %d hwcfg %x, use NAND\n",
+ metal_id, hwcfg);
+ return rda_nand_init(chip);
+ }
+}
+#endif
+#endif
+
+int rda_flash_intf_is_spi(void)
+{
+ return flash_intf_spi;
+}
diff --git a/drivers/mtd/nand/rda_nand_v1.c b/drivers/mtd/nand/rda_nand_v1.c
new file mode 100644
index 0000000000..d485721283
--- /dev/null
+++ b/drivers/mtd/nand/rda_nand_v1.c
@@ -0,0 +1,1661 @@
+#include <common.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <nand.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_nand.h>
+#include <asm/arch/reg_sysctrl.h>
+#include <asm/arch/hwcfg.h>
+#include <mtd/nand/rda_nand.h>
+
+#include <rda/tgt_ap_board_config.h>
+#include <rda/tgt_ap_clock_config.h>
+#include <rda/tgt_ap_flash_parts.h>
+
+#ifdef CONFIG_SPL_BUILD
+#undef CONFIG_NAND_RDA_DMA
+#endif
+
+#ifdef CONFIG_NAND_RDA_DMA
+#include <asm/dma-mapping.h>
+#include <asm/arch/dma.h>
+#endif
+
+//#define NAND_DEBUG
+//#define NAND_DEBUG_VERBOSE
+
+#if (_TGT_NAND_TYPE_ == _PARALLEL_NAND_USED_)
+
+#define hal_gettime get_ticks
+#define SECOND * CONFIG_SYS_HZ_CLOCK
+#define NAND_TIMEOUT ( 2 SECOND )
+#define PLL_BUS_FREQ (_TGT_AP_PLL_BUS_FREQ * 1000000)
+
+/***********************nand global buffer define ************************/
+/******************for 8810 support 8k/16k nand flash*********************/
+#ifdef CONFIG_SPL_BUILD
+/*sram space is not enough, so use sdram space as middle data buffer*/
+static u8 *g_nand_flash_temp_buffer = (u8 *)CONFIG_SPL_NAND_MIDDLE_DATA_BUFFER;
+#else
+#define NAND_FLASH_PHYSICAL_PAGE_SIZE 16384
+#define NAND_FLASH_PHYSICAL_SPARE_SIZE 1280
+static u8 g_nand_flash_temp_buffer[NAND_FLASH_PHYSICAL_PAGE_SIZE + \
+ NAND_FLASH_PHYSICAL_SPARE_SIZE] __attribute__ ((__aligned__(64)));
+#endif
+//#define NAND_CONTROLLER_BUFFER_DATA_SIZE 4096
+//#define NAND_CONTROLLER_BUFFER_OOB_SIZE 224
+#define NAND_CONTROLLER_TEMP_BUFFER_SIZE 4320
+
+/*when system use ext4+ftl mode, FTL oob struct is 12bytes, and is stored in flash OOB space.*/
+/*the nand page size use 3K bytes data + 128 bytes oob, or 6k bytes data + 256 bytes oob, or*/
+/*12k bytes data + 512 bytes oob. but nand controller has hardware ecc error,only could 3K ecc*/
+/*in 4k byte, so OOB could not be ecc protecked. But maybe have some error bits in this space,*/
+/*so the method is: copy FTL oob struct 12 bytes + 4 bytes checksum several times in oob space,*/
+/*when read, find the first right checksum data, then use this array data. */
+#define __FTL_OOB_STRUCT_LENGTH__ 12 //bytes, when FTL oob struct add member, this sould increase.
+#define OOB_SW_PROTECT_SWITCH_ON 1
+
+/* clock division value map */
+const static u8 clk_div_map[] = {
+ 4*60, /* 0 */
+ 4*60, /* 1 */
+ 4*60, /* 2 */
+ 4*60, /* 3 */
+ 4*60, /* 4 */
+ 4*60, /* 5 */
+ 4*60, /* 6 */
+ 4*60, /* 7 */
+ 4*40, /* 8 */
+ 4*30, /* 9 */
+ 4*24, /* 10 */
+ 4*20, /* 11 */
+ 4*17, /* 12 */
+ 4*15, /* 13 */
+ 4*13, /* 14 */
+ 4*12, /* 15 */
+ 4*11, /* 16 */
+ 4*10, /* 17 */
+ 4*9, /* 18 */
+ 4*8, /* 19 */
+ 4*7, /* 20 */
+ 4*13/2, /* 21 */
+ 4*6, /* 22 */
+ 4*11/2, /* 23 */
+ 4*5, /* 24 */
+ 4*9/2, /* 25 */
+ 4*4, /* 26 */
+ 4*7/2, /* 27 */
+ 4*3, /* 28 */
+ 4*5/2, /* 29 */
+ 4*2, /* 30 */
+ 4*1, /* 31 */
+};
+
+static struct nand_ecclayout rda_nand_oob_64 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 62} }
+};
+
+static struct nand_ecclayout rda_nand_oob_128 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 126} }
+};
+
+static struct nand_ecclayout rda_nand_oob_256 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 254} }
+};
+
+static struct nand_ecclayout rda_nand_oob_512 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 510} }
+};
+
+extern void rda_dump_buf(char *data, size_t len);
+
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+static uint32_t nand_checksum32( unsigned char* data, uint32_t data_size)
+{
+ uint32_t checksum = 0;
+ uint32_t i;
+
+ for(i = 0; i < data_size; i++)
+ checksum = ((checksum << 31) | (checksum >> 1)) + (uint32_t)data[i];
+
+ return checksum;
+}
+
+struct select_data{
+ u8 data;
+ u8 count;
+};
+
+static void nand_sw_ecc_check(u8 * buf, u8 *dst, u8 copy_number, u8 array_length)
+{
+ u8 i, j, k, m;
+ struct select_data select[30];
+ u8 temp;
+
+ for(i = 0; i < array_length; i++){
+ m = 0;
+ select[m].data = buf[i];
+ select[m].count = 1;
+ for(j = 1; j < copy_number; j++){
+ k = 0;
+ while(1){
+ if(select[k].data == buf[i + j * (array_length + 4)]){
+ select[k].count++;
+ break;
+ }
+
+ if(k >= m){
+ select[m+1].data = buf[i + j * (array_length + 4)];
+ select[m+1].count = 1;
+ m++;
+ break;
+ }
+ k++;
+ }
+ }
+
+ temp = select[0].count;
+ dst[i] = select[0].data;
+ for(j = 1; j <= m; j++){
+ if(select[j].count > temp){
+ dst[i] = select[j].data;
+ temp = select[j].count;
+ }
+ }
+ }
+}
+#endif
+
+static void hal_send_cmd(unsigned char cmd, unsigned int page_addr)
+{
+ unsigned long cmd_reg;
+
+ cmd_reg = NANDFC_DCMD(cmd) | NANDFC_PAGE_ADDR(page_addr);
+#ifdef NAND_DEBUG
+ printf(" hal_send_cmd 0x%08lx\n", cmd_reg);
+#endif
+ __raw_writel(cmd_reg, NANDFC_REG_DCMD_ADDR);
+}
+
+static void hal_set_col_addr(unsigned int col_addr)
+{
+ __raw_writel(col_addr, NANDFC_REG_COL_ADDR);
+}
+
+static void hal_set_col_cmd(unsigned int col_cmd)
+{
+ __raw_writel(0x50010000 |col_cmd, NANDFC_REG_CMD_DEF_B);
+}
+
+static void hal_flush_buf(struct mtd_info *mtd)
+{
+ /*
+ there is no reg NANDFC_REG_BUF_CTRL
+ */
+ //__raw_writel(0x7, NANDFC_REG_BUF_CTRL);
+}
+
+static unsigned long hal_wait_cmd_complete(void)
+{
+ unsigned long int_stat;
+ unsigned long long wait_time = NAND_TIMEOUT;
+ unsigned long long start_time = hal_gettime();
+ int timeout = 0;
+
+ /* wait done */
+ do {
+ int_stat = __raw_readl(NANDFC_REG_INT_STAT);
+ if (hal_gettime() - start_time >= wait_time) {
+ timeout = 1;
+ }
+ } while (!(int_stat & NANDFC_INT_DONE) && !timeout);
+
+ /* to clear */
+ __raw_writel(int_stat & NANDFC_INT_CLR_MASK, NANDFC_REG_INT_STAT);
+
+ if (timeout) {
+ printf("nand error, cmd timeout\n");
+ return -ETIME;
+ }
+
+ if (int_stat & NANDFC_INT_ERR_ALL) {
+ printf("nand error, int_stat = %lx\n", int_stat);
+ return (int_stat & NANDFC_INT_ERR_ALL);
+ }
+ return 0;
+}
+
+static int init_nand_info(struct rda_nand_info *info,
+ int nand_type, int bus_width_16)
+{
+ info->type = nand_type;
+ switch (nand_type) {
+ case NAND_TYPE_2KL:
+ info->hard_ecc_hec = 0;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 11;
+ info->oob_size = 64;
+ info->page_num_per_block = 64;
+ info->vir_page_size = 2048;
+ info->vir_page_shift = 11;
+ info->vir_oob_size = 64;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 0;
+ break;
+#if(__TGT_AP_FLASH_USE_PART_OF_PAGE__ == 1)
+ case NAND_TYPE_MLC4K:
+ info->hard_ecc_hec = 1;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 12;
+ info->oob_size = 224;
+ info->page_num_per_block = 256;
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_4KAS2K_PAGE_DIV__)
+ info->vir_page_size = 2048;
+ info->vir_oob_size = 64;
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_4KAS3K_PAGE_DIV__)
+ info->vir_page_size = 3072;
+ info->vir_oob_size = 128;
+#endif
+#endif
+ info->vir_page_shift = 0;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 0;
+ break;
+#else
+ case NAND_TYPE_MLC4K:
+ info->hard_ecc_hec = 1;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 12;
+ info->oob_size = 224;
+ info->page_num_per_block = 256;
+ info->vir_page_size = 2048;
+ info->vir_page_shift = 11;
+ info->vir_oob_size = 64;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 0;
+ break;
+#endif
+ case NAND_TYPE_SLC4K:
+ info->hard_ecc_hec = 0;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 12;
+ /* slc 4k, u-boot kernel yaffs use 128 oob */
+ info->oob_size = 128;
+#ifdef _TGT_AP_NAND_DISABLE_HEC
+ /* this flag also implies using MLC */
+ info->page_num_per_block = 256;
+#else
+ info->page_num_per_block = 64;
+#endif
+ info->vir_page_size = 4096;
+ info->vir_page_shift = 12;
+ info->vir_oob_size = 128;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 0;
+ break;
+#if(__TGT_AP_FLASH_USE_PART_OF_PAGE__ == 1)
+ case NAND_TYPE_MLC8K:
+ info->hard_ecc_hec = 1;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 13;
+ info->oob_size = 744;
+ info->page_num_per_block = 256;
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_8KAS6K_PAGE_DIV__)
+ info->vir_page_size = 6144;
+ info->vir_oob_size = 256;
+ info->nand_use_type = 1;
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_8KAS4K_PAGE_DIV__)
+ info->vir_page_size = 4096;
+ info->vir_oob_size = 128;
+ info->nand_use_type = 1;
+#endif
+#endif
+ info->vir_page_shift = 0;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ break;
+#else
+ case NAND_TYPE_MLC8K:
+ info->hard_ecc_hec = 1;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 13;
+ info->oob_size = 744;
+ info->page_num_per_block = 256;
+ info->vir_page_size = 4096;
+ info->vir_page_shift = 12;
+ info->vir_oob_size = 128;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 1;
+ break;
+#endif
+ case NAND_TYPE_SLC8K:
+ info->hard_ecc_hec = 0;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 13;
+ info->oob_size = 744;
+ info->page_num_per_block = 256;
+ info->vir_page_size = 8192;
+ info->vir_page_shift = 13;
+ info->vir_oob_size = 256;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 1;
+ break;
+#if(__TGT_AP_FLASH_USE_PART_OF_PAGE__ == 1)
+ case NAND_TYPE_MLC16K:
+ info->hard_ecc_hec = 1;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 14;
+ info->oob_size = 1280;
+ info->page_num_per_block = 256;
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_16KAS8K_PAGE_DIV__)
+ info->vir_page_size = 8192;
+ info->vir_oob_size = 256;
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_16KAS12K_PAGE_DIV__)
+ info->vir_page_size = 12288;
+ info->vir_oob_size = 512;
+#endif
+#endif
+ info->vir_page_shift = 0;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 3;
+ break;
+#else
+ case NAND_TYPE_MLC16K:
+ info->hard_ecc_hec = 1;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 14;
+ info->oob_size = 1280;
+ info->page_num_per_block = 256;
+ info->vir_page_size = 8192;
+ info->vir_page_shift = 13;
+ info->vir_oob_size = 256;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 3;
+ break;
+#endif
+ case NAND_TYPE_SLC16K:
+ info->hard_ecc_hec = 0;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 14;
+ info->oob_size = 1280;
+ info->page_num_per_block = 256;
+ info->vir_page_size = 16384;
+ info->vir_page_shift = 14;
+ info->vir_oob_size = 512;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ info->nand_use_type = 3;
+ break;
+ default:
+ printf("invalid nand type\n");
+ return -EINVAL;
+ }
+
+ info->logic_page_size = info->vir_page_size / (info->nand_use_type + 1);
+ info->logic_oob_size = info->vir_oob_size / (info->nand_use_type + 1);
+ return 0;
+}
+
+static u32 cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2)
+{
+ u32 newfreq;
+
+ if (reg >= ARRAY_SIZE(clk_div_map)) {
+ printf("nand:Invalid div reg: %u\n", reg);
+ reg = ARRAY_SIZE(clk_div_map) - 1;
+ }
+ /* Assuming basefreq is smaller than 2^31 (2.147G Hz) */
+ newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1);
+ return newfreq;
+}
+
+unsigned long get_master_clk_rate(u32 reg)
+{
+ u32 div2;
+ unsigned long rate;
+
+ div2 = reg & SYS_CTRL_AP_AP_APB2_SRC_SEL;
+ reg = GET_BITFIELD(reg, SYS_CTRL_AP_AP_APB2_FREQ);
+ rate = cal_freq_by_divreg(PLL_BUS_FREQ, reg, div2);
+
+ return rate;
+}
+
+static unsigned long rda_nand_cld_div_tbl[16] = {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 12, 14, 16, 18, 20, 22, 24, 28
+};
+
+static unsigned long hal_calc_divider(struct rda_nand_info *info)
+{
+ unsigned long mclk = info->master_clk;
+ unsigned long div, clk_div;
+ int i;
+
+ div = mclk / info->clk;
+ if (mclk % info->clk)
+ div += 1;
+
+ if (div < 7) {
+ /* 7 is minimal divider by hardware */
+ div = 7;
+ }
+
+ for (i=0;i<16;i++) {
+ if(div <= rda_nand_cld_div_tbl[i]) {
+ clk_div = i;
+ break;
+ }
+ }
+
+ if (i>=16) {
+ clk_div = 15;
+ }
+
+ //printf("NAND: div:%ld, clk_div:%ld\n", div, clk_div);
+ return clk_div;
+}
+
+static int hal_init(struct rda_nand_info *info)
+{
+ unsigned long config_a, config_b;
+ unsigned long clk_div=hal_calc_divider(info);
+
+ // clk_div = 4; // for APB2 = 48MHz, 48/7
+ //clk_div = 11; // for APB2 = 120MHz, 120/18
+ //clk_div = 15; // for APB2 = 240MHz, 240/28
+
+ /* setup config_a and config_b */
+ config_a = NANDFC_CYCLE(clk_div);
+ config_b = 0;
+ config_a |= NANDFC_CHIP_SEL(0x0e);
+ config_a |= NANDFC_POLARITY_IO(0); // set 0 invert IO
+ config_a |= NANDFC_POLARITY_MEM(1); // set 1 invert MEM
+
+ switch (info->type) {
+ case NAND_TYPE_512S:
+ case NAND_TYPE_512L:
+ printf("Not support 512 nand any more\n");
+ return 1;
+ case NAND_TYPE_2KS:
+ config_a |= NANDFC_TIMING(0x7B);
+ break;
+ case NAND_TYPE_2KL:
+ case NAND_TYPE_MLC2K:
+ config_a |= NANDFC_TIMING(0x8B);
+ break;
+ case NAND_TYPE_MLC4K:
+ case NAND_TYPE_SLC4K:
+ config_a |= NANDFC_TIMING(0x8C);
+ break;
+ case NAND_TYPE_MLC8K:
+ case NAND_TYPE_SLC8K:
+ config_a |= NANDFC_TIMING(0x8c);
+ config_b |= 0x20;
+ break;
+ case NAND_TYPE_MLC16K:
+ case NAND_TYPE_SLC16K:
+ config_a |= NANDFC_TIMING(0x8c) | (0x00 << 20);
+ config_b |= 0x10;
+ break;
+ default:
+ printf("invalid nand type\n");
+ return -EINVAL;
+ }
+
+ //enable the 16bit mode;
+ if (info->bus_width_16)
+ config_a |= NANDFC_WDITH_16BIT(1);
+
+ config_b |= NANDFC_HWECC(1);
+ if (info->hard_ecc_hec)
+ config_b |= NANDFC_ECC_MODE(0x02);
+ __raw_writel(config_a, NANDFC_REG_CONFIG_A);
+ __raw_writel(config_b, NANDFC_REG_CONFIG_B);
+
+ /* set readid type, 0x06 for 8 bytes ID */
+ __raw_writel(0x6, NANDFC_REG_IDTPYE);
+
+#ifdef _TGT_AP_NAND_READDELAY
+ {
+ unsigned int delay;
+ /* Set an interval of filter for erasing operation. */
+ delay = __raw_readl(NANDFC_REG_DELAY);
+ delay &= ~0xffff;
+ delay |= _TGT_AP_NAND_READDELAY;
+ __raw_writel(delay, NANDFC_REG_DELAY);
+ printf("Nand delay : 0x%x .\n", (delay & 0xffff));
+ }
+#endif /* #if _TGT_AP_NAND_READDELAY */
+ printf("Nand Init Done, %08lx %08lx\n", config_a, config_b);
+
+ return 0;
+}
+
+static u8 nand_rda_read_byte(struct mtd_info *mtd)
+{
+ u8 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *((u8 *) info->byte_buf + info->index);
+ info->index++;
+#ifdef NAND_DEBUG
+ printf("nand_read_byte, ret = %02x\n", ret);
+#endif
+ return ret;
+}
+
+static u16 nand_rda_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *(u16 *) ((uint8_t *) info->byte_buf + info->index);
+ info->index += 2;
+#ifdef NAND_DEBUG
+ printf("nand_read_word, ret = %04x\n", ret);
+#endif
+ return ret;
+}
+
+#ifdef CONFIG_NAND_RDA_DMA
+static void nand_rda_dma_move_data(struct mtd_info *mtd, uint8_t * dst, uint8_t * src,
+ int len, enum dma_data_direction dir)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t dst_phys_addr, src_phys_addr;
+ void *dst_addr = (void *)dst;
+ void *src_addr = (void *)src;
+ int ret = 0;
+
+ if (((u32)dst_addr & 0x7) != 0 || ((u32)src_addr & 0x7) != 0) {
+ printf("ERROR, nand dma read buffer dst %p or src %p is not 8bytes aligned\n",
+ dst_addr, src_addr);
+ return;
+ }
+
+ if(dir == DMA_FROM_DEVICE){
+ dst_phys_addr = dma_map_single(dst_addr, len, DMA_FROM_DEVICE);
+ src_phys_addr = (dma_addr_t)src;
+ }
+ else if(dir == DMA_TO_DEVICE){
+ dst_phys_addr = (dma_addr_t)dst;
+ src_phys_addr = dma_map_single(src_addr, len, DMA_TO_DEVICE);
+ }
+ else{
+ dst_phys_addr = dma_map_single(dst_addr, len, DMA_BIDIRECTIONAL);
+ src_phys_addr = dma_map_single(src_addr, len, DMA_BIDIRECTIONAL);
+ }
+
+ dma_param.src_addr = src_phys_addr;
+ dma_param.dst_addr = dst_phys_addr;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FR_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : DMA failed to set parameter!\n");
+ if(dir == DMA_FROM_DEVICE)
+ dma_unmap_single(dst_addr, len, DMA_FROM_DEVICE);
+ else if(dir == DMA_TO_DEVICE)
+ dma_unmap_single(src_addr, len, DMA_TO_DEVICE);
+ else{
+ dma_unmap_single(dst_addr, len, DMA_BIDIRECTIONAL);
+ dma_unmap_single(src_addr, len, DMA_BIDIRECTIONAL);
+ }
+ return;
+ }
+
+ /* use flush to avoid annoying unaligned warning */
+ /* however, invalidate after the dma it the right thing to do */
+ if(dir == DMA_FROM_DEVICE)
+ flush_dcache_range((u32)dst_addr, (u32)(dst_addr + len));
+ else if(dir == DMA_TO_DEVICE)
+ flush_dcache_range((u32)src_addr, (u32)(src_addr + len));
+ else{
+ flush_dcache_range((u32)dst_addr, (u32)(dst_addr + len));
+ flush_dcache_range((u32)src_addr, (u32)(src_addr + len));
+ }
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* use flush to avoid annoying unaligned warning */
+ //invalidate_dcache_range((u32)addr, (u32)(addr + len));
+
+ /* Free the specified physical address */
+ if(dir == DMA_FROM_DEVICE)
+ dma_unmap_single(dst_addr, len, DMA_FROM_DEVICE);
+ else if(dir == DMA_TO_DEVICE)
+ dma_unmap_single(src_addr, len, DMA_TO_DEVICE);
+ else{
+ dma_unmap_single(dst_addr, len, DMA_BIDIRECTIONAL);
+ dma_unmap_single(src_addr, len, DMA_BIDIRECTIONAL);
+ }
+}
+#endif/*CONFIG_NAND_RDA_DMA*/
+
+static void nand_rda_logic_read_cache(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ unsigned long cmd_ret;
+
+ hal_set_col_addr(info->col_addr);
+ hal_send_cmd((unsigned char)info->cmd, info->page_addr);
+ cmd_ret = hal_wait_cmd_complete();
+ if (cmd_ret) {
+ printf("logic read fail, cmd = %x, page_addr = %x, col_addr = %x\n",
+ info->cmd, info->page_addr, info->col_addr);
+ }
+}
+
+static void nand_rda_read_cache(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_R);
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[0];
+
+ if(info->nand_use_type > 0){
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)u_nand_temp_buffer,
+ (void *)nand_ptr,
+ info->logic_page_size);
+ memcpy((void *)(u_nand_temp_buffer + mtd->writesize),
+ (void *)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size);
+#else
+ nand_rda_dma_move_data(mtd,
+ u_nand_temp_buffer,
+ nand_ptr,
+ info->logic_page_size,
+ DMA_FROM_DEVICE);
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + mtd->writesize),
+ (uint8_t*)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size,
+ DMA_FROM_DEVICE);
+#endif
+ //printf("\nread chip buffer0:\n");
+ //rda_dump_buf(nand_ptr, 4320);
+
+ info->col_addr += NAND_CONTROLLER_TEMP_BUFFER_SIZE;
+ hal_set_col_cmd(0xe0);
+ info->cmd = NAND_CMD_RNDOUT;
+ nand_rda_logic_read_cache(mtd);
+
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)(u_nand_temp_buffer + info->logic_page_size),
+ (void *)nand_ptr,
+ info->logic_page_size);
+ memcpy((void *)(u_nand_temp_buffer + mtd->writesize + info->logic_oob_size),
+ (void *)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size);
+#else
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + info->logic_page_size),
+ nand_ptr,
+ info->logic_page_size,
+ DMA_FROM_DEVICE);
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + mtd->writesize + info->logic_oob_size),
+ (uint8_t*)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size,
+ DMA_FROM_DEVICE);
+#endif
+
+ //printf("\nread chip buffer1:\n");
+ //rda_dump_buf(nand_ptr, 4320);
+ if(info->type == NAND_TYPE_SLC16K || info->type == NAND_TYPE_MLC16K){
+ info->col_addr += NAND_CONTROLLER_TEMP_BUFFER_SIZE;
+ nand_rda_logic_read_cache(mtd);
+
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)(u_nand_temp_buffer + info->logic_page_size * 2),
+ (void *)nand_ptr,
+ info->logic_page_size);
+ memcpy((void *)(u_nand_temp_buffer + mtd->writesize + info->logic_oob_size * 2),
+ (void *)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size);
+#else
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + info->logic_page_size * 2),
+ nand_ptr,
+ info->logic_page_size,
+ DMA_FROM_DEVICE);
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + mtd->writesize + info->logic_oob_size * 2),
+ (uint8_t*)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size,
+ DMA_FROM_DEVICE);
+#endif
+
+ info->col_addr += NAND_CONTROLLER_TEMP_BUFFER_SIZE;
+ nand_rda_logic_read_cache(mtd);
+
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)(u_nand_temp_buffer + info->logic_page_size * 3),
+ (void *)nand_ptr,
+ info->logic_page_size);
+ memcpy((void *)(u_nand_temp_buffer + mtd->writesize + info->logic_oob_size * 3),
+ (void *)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size);
+#else
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + info->logic_page_size * 3),
+ nand_ptr,
+ info->logic_page_size,
+ DMA_FROM_DEVICE);
+ nand_rda_dma_move_data(mtd,
+ (uint8_t*)(u_nand_temp_buffer + mtd->writesize + info->logic_oob_size * 3),
+ (uint8_t*)(nand_ptr + info->logic_page_size),
+ info->logic_oob_size,
+ DMA_FROM_DEVICE);
+#endif
+ }
+ hal_set_col_cmd(0x30);
+ hal_set_col_addr(0);
+ }
+}
+
+static void nand_rda_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_R + info->read_ptr);
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[info->read_ptr];
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+ uint8_t *oob_temp_ptr = &g_nand_flash_temp_buffer[16384+512];
+ uint8_t oob_byte_count = 2 + __FTL_OOB_STRUCT_LENGTH__ + 2 + 4;
+ uint8_t oob_copy_number = info->vir_oob_size / oob_byte_count;
+ uint32_t checksum, checksum_load;
+ u8 i;
+#endif
+
+#ifdef NAND_DEBUG
+ printf("read : buf addr = 0x%p, len = %d, read_ptr = %d\n", buf, len,
+ info->read_ptr);
+#endif /* NAND_DEBUG */
+
+#if 1//ndef CONFIG_NAND_RDA_DMA
+ if(info->nand_use_type > 0){
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+ memset(oob_temp_ptr, 0xff, info->vir_oob_size);
+ if(info->read_ptr > 0){
+ if(info->vir_page_size == 6144 || info->vir_page_size == 12288){
+ for(i = 0; i < oob_copy_number; i++){
+ u32 index = i * oob_byte_count + oob_byte_count;
+
+ checksum = nand_checksum32(&u_nand_temp_buffer[i * oob_byte_count],
+ oob_byte_count - 4);
+ checksum_load = u_nand_temp_buffer[index - 4];
+ checksum_load |= u_nand_temp_buffer[index - 3] << 8;
+ checksum_load |= u_nand_temp_buffer[index - 2] << 16;
+ checksum_load |= u_nand_temp_buffer[index - 1] << 24;
+ if(checksum == checksum_load || checksum_load == ~0x0){
+ memcpy(oob_temp_ptr,
+ &u_nand_temp_buffer[i * oob_byte_count],
+ oob_byte_count);
+ break;
+ }
+ }
+ if(i == oob_copy_number){
+ printf("6k or 12k oob ecc warning, data select!!!! \n");
+ nand_sw_ecc_check(u_nand_temp_buffer,
+ oob_temp_ptr,
+ oob_copy_number,
+ oob_byte_count - 4);
+ }
+
+ u_nand_temp_buffer = oob_temp_ptr;
+ }
+ }
+#endif
+ memcpy((void *)buf, (void *)u_nand_temp_buffer, len);
+ }else{
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+ if(info->read_ptr > 0){
+ if(info->vir_page_size == 3072){
+ for(i = 0; i < oob_copy_number; i++){
+ u32 index = i * oob_byte_count + oob_byte_count;
+
+ checksum = nand_checksum32(&nand_ptr[i * oob_byte_count],
+ oob_byte_count - 4);
+ checksum_load = nand_ptr[index - 4];
+ checksum_load |= nand_ptr[index - 3] << 8;
+ checksum_load |= nand_ptr[index - 2] << 16;
+ checksum_load |= nand_ptr[index - 1] << 24;
+ if(checksum == checksum_load || checksum_load == ~0x0){
+ memcpy(oob_temp_ptr, &nand_ptr[i * oob_byte_count], oob_byte_count);
+ break;
+ }
+ }
+ if(i == oob_copy_number){
+ printf("3k oob ecc warning, data select!!!! \n");
+ nand_sw_ecc_check(nand_ptr,
+ oob_temp_ptr,
+ oob_copy_number,
+ oob_byte_count - 4);
+ }
+
+ nand_ptr = oob_temp_ptr;
+ }
+ }
+#endif
+ memcpy((void *)buf, (void *)nand_ptr, len);
+ }
+ info->read_ptr += len;
+
+#ifdef NAND_DEBUG_VERBOSE
+ rda_dump_buf((char *)buf, len);
+#endif
+
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy(buf, (void *)nand_ptr, len);
+ info->read_ptr = 0;
+ return;
+ }
+
+ if (((u32)addr & 0x7) != 0) {
+ printf("ERROR, nand dma read buffer %p is not 8bytes aligned\n",
+ addr);
+ return;
+ }
+
+ phys_addr = dma_map_single(addr, len, DMA_FROM_DEVICE);
+
+ dma_param.src_addr = (u32) info->nand_data_phys;
+ dma_param.dst_addr = phys_addr;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FR_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_FROM_DEVICE);
+ return;
+ }
+
+ /* use flush to avoid annoying unaligned warning */
+ /* however, invalidate after the dma it the right thing to do */
+ flush_dcache_range((u32)addr, (u32)(addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* use flush to avoid annoying unaligned warning */
+ //invalidate_dcache_range((u32)addr, (u32)(addr + len));
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_FROM_DEVICE);
+ info->read_ptr += len;
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void nand_rda_write_buf(struct mtd_info *mtd, const uint8_t * buf,
+ int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_W + info->write_ptr);
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[info->write_ptr];
+ uint8_t *temp_ptr = (uint8_t *)buf;
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+ uint8_t *oob_temp_ptr = &g_nand_flash_temp_buffer[16384+512];
+ uint8_t oob_byte_count = 2 + __FTL_OOB_STRUCT_LENGTH__ + 2 + 4;
+ uint8_t oob_copy_number = info->vir_oob_size / oob_byte_count;
+ uint32_t checksum;
+#endif
+
+#ifdef NAND_DEBUG
+ printf("write : buf addr = 0x%p, len = %d, write_ptr = %d\n", buf, len,
+ info->write_ptr);
+#endif /* NAND_DEBUG */
+
+#ifdef NAND_DEBUG_VERBOSE
+ rda_dump_buf((char *)buf, len);
+#endif
+
+#if 1//ndef CONFIG_NAND_RDA_DMA
+ if(info->nand_use_type > 0){
+ if(info->write_ptr > 0){/*write OOB to buffer*/
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+ if(info->vir_page_size == 6144 ||info->vir_page_size == 12288){
+ checksum = nand_checksum32(temp_ptr, oob_byte_count - 4);
+ memcpy(oob_temp_ptr, temp_ptr, oob_byte_count - 4);
+ oob_temp_ptr[oob_byte_count - 4] = (uint8_t)(checksum & 0xff);
+ oob_temp_ptr[oob_byte_count - 3] = (uint8_t)((checksum >> 8) & 0xff);
+ oob_temp_ptr[oob_byte_count - 2] = (uint8_t)((checksum >> 16) & 0xff);
+ oob_temp_ptr[oob_byte_count - 1] = (uint8_t)((checksum >> 24) & 0xff);
+ while(oob_copy_number > 1){
+ oob_copy_number--;
+ memcpy(&oob_temp_ptr[oob_copy_number * oob_byte_count],
+ oob_temp_ptr,
+ oob_byte_count);
+ }
+
+ temp_ptr = oob_temp_ptr;
+ }
+#endif
+ info->write_ptr = info->logic_page_size;
+ u_nand_temp_buffer = &g_nand_flash_temp_buffer[info->write_ptr];
+ memcpy((void *)u_nand_temp_buffer, (void *)temp_ptr, info->logic_oob_size);
+ memcpy((void *)(u_nand_temp_buffer + NAND_CONTROLLER_TEMP_BUFFER_SIZE),
+ (void *)(temp_ptr + info->logic_oob_size), info->logic_oob_size);
+ if(info->type == NAND_TYPE_SLC16K || info->type == NAND_TYPE_MLC16K){
+ memcpy((void *)(u_nand_temp_buffer + NAND_CONTROLLER_TEMP_BUFFER_SIZE * 2),
+ (void *)(temp_ptr + info->logic_oob_size * 2), info->logic_oob_size);
+ memcpy((void *)(u_nand_temp_buffer + NAND_CONTROLLER_TEMP_BUFFER_SIZE * 3),
+ (void *)(temp_ptr + info->logic_oob_size * 3), info->logic_oob_size);
+ }
+ }else{/*write data to buffer*/
+ memcpy((void *)u_nand_temp_buffer, (void *)temp_ptr, info->logic_page_size);
+ memcpy((void *)(u_nand_temp_buffer + NAND_CONTROLLER_TEMP_BUFFER_SIZE),
+ (void *)(temp_ptr + info->logic_page_size), info->logic_page_size);
+ if(info->type == NAND_TYPE_SLC16K || info->type == NAND_TYPE_MLC16K){
+ memcpy((void *)(u_nand_temp_buffer + NAND_CONTROLLER_TEMP_BUFFER_SIZE * 2),
+ (void *)(temp_ptr + info->logic_page_size * 2), info->logic_page_size);
+ memcpy((void *)(u_nand_temp_buffer + NAND_CONTROLLER_TEMP_BUFFER_SIZE * 3),
+ (void *)(temp_ptr + info->logic_page_size * 3), info->logic_page_size);
+ }
+ info->write_ptr += info->logic_page_size;
+ }
+ }else{
+#if (OOB_SW_PROTECT_SWITCH_ON == 1)
+ if(info->write_ptr > 0 && info->vir_page_size == 3072){
+ checksum = nand_checksum32(temp_ptr, oob_byte_count - 4);
+ memcpy(oob_temp_ptr, temp_ptr, oob_byte_count - 4);
+ oob_temp_ptr[oob_byte_count - 4] = (uint8_t)(checksum & 0xff);
+ oob_temp_ptr[oob_byte_count - 3] = (uint8_t)((checksum >> 8) & 0xff);
+ oob_temp_ptr[oob_byte_count - 2] = (uint8_t)((checksum >> 16) & 0xff);
+ oob_temp_ptr[oob_byte_count - 1] = (uint8_t)((checksum >> 24) & 0xff);
+ while(oob_copy_number > 1){
+ oob_copy_number--;
+ memcpy(&oob_temp_ptr[oob_copy_number * oob_byte_count],
+ oob_temp_ptr,
+ oob_byte_count);
+ }
+
+ temp_ptr = oob_temp_ptr;
+ }
+#endif
+ memcpy((void *)nand_ptr, (void *)temp_ptr, len);
+ info->write_ptr += len;
+ }
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy((void *)nand_ptr, (void *)buf, len);
+ info->write_ptr += len;
+ return;
+ }
+
+ if (((u32)addr & 0x7) != 0) {
+ printf("ERROR, nand dma write %p buffer is not 8bytes aligned\n",
+ addr);
+ return;
+ }
+ phys_addr = dma_map_single(addr, len, DMA_TO_DEVICE);
+
+ dma_param.src_addr = phys_addr;
+ dma_param.dst_addr = (u32) info->nand_data_phys;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FW_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_TO_DEVICE);
+ return;
+ }
+
+ flush_dcache_range((u32)addr, (u32)(addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_TO_DEVICE);
+ info->write_ptr += len;
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void nand_rda_write_cache(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_W + info->write_ptr);
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[info->col_addr];
+
+ if(info->nand_use_type > 0){
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)nand_ptr,
+ (void *)u_nand_temp_buffer,
+ NAND_CONTROLLER_TEMP_BUFFER_SIZE);
+#else
+ nand_rda_dma_move_data(mtd,
+ nand_ptr,
+ u_nand_temp_buffer,
+ NAND_CONTROLLER_TEMP_BUFFER_SIZE,
+ DMA_TO_DEVICE);
+#endif
+ }
+}
+
+static void nand_rda_do_cmd_pre(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+
+ __raw_writel(0xfff, NANDFC_REG_INT_STAT);
+ switch (info->cmd) {
+ case NAND_CMD_SEQIN:
+ info->write_ptr = 0;
+ nand_rda_write_cache(mtd);
+ break;
+ case NAND_CMD_READ0:
+ hal_flush_buf(mtd);
+ //info->read_ptr = 0;
+ break;
+ case NAND_CMD_RNDIN:
+ nand_rda_write_cache(mtd);
+ break;
+ default:
+ break;
+ }
+}
+
+static void nand_rda_do_cmd_post(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+ u32 temp;
+
+ switch (info->cmd) {
+ case NAND_CMD_RESET:
+ return;
+ case NAND_CMD_READID:
+ temp = __raw_readl(NANDFC_REG_IDCODE_A);
+ info->byte_buf[0] = temp;
+ temp = __raw_readl(NANDFC_REG_IDCODE_B);
+ info->byte_buf[1] = temp;
+ info->index = 0;
+ break;
+ case NAND_CMD_STATUS:
+ temp = __raw_readl(NANDFC_REG_OP_STATUS);
+ info->byte_buf[0] = (temp & 0xFF);
+ info->index = 0;
+ if (info->byte_buf[0] != 0xe0) {
+ printf("nand error in op status %x\n",
+ info->byte_buf[0]);
+ }
+#ifdef NAND_DEBUG
+ printf("post_cmd read status %x\n", info->byte_buf[0]);
+#endif
+ break;
+ case NAND_CMD_READ0: // NAND_CMD_READOOB goes here too
+ nand_rda_read_cache(mtd);
+ info->index = 0;
+ if(NAND_CMD_READOOB == info->cmd_flag){
+ if(info->nand_use_type > 0){
+ temp = g_nand_flash_temp_buffer[mtd->writesize];
+ temp |= g_nand_flash_temp_buffer[mtd->writesize+1] << 8;
+ info->byte_buf[0] = temp;
+ }else{
+ u8 *nand_ptr = (u8 *) (this->IO_ADDR_R + mtd->writesize);
+ memcpy((u8*)&info->byte_buf[0], nand_ptr, 2);
+ }
+ info->cmd_flag = NAND_CMD_NONE;
+ }
+ break;
+ case NAND_CMD_SEQIN:
+ if(info->nand_use_type > 0){
+ info->logic_operate_time += 1;
+ info->col_addr += NAND_CONTROLLER_TEMP_BUFFER_SIZE;
+ info->cmd = NAND_CMD_RNDIN;
+ }
+ break;
+ case NAND_CMD_RNDIN:
+ if(info->nand_use_type > 0){
+ info->logic_operate_time += 1;
+ info->col_addr += NAND_CONTROLLER_TEMP_BUFFER_SIZE;
+ }
+ break;
+/* delay 10ms, for erase to complete */
+ case NAND_CMD_ERASE1:
+ udelay(10000);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void nand_rda_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+#ifdef NAND_DEBUG
+ printf("nand_rda_hwcontrol, cmd = %x, ctrl = %x, not use anymore\n",
+ cmd, ctrl);
+#endif
+}
+
+static void nand_rda_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ unsigned long cmd_ret;
+
+#ifdef NAND_DEBUG
+ printf("nand_rda_cmdfunc, cmd = %x, page_addr = %x, col_addr = %x\n",
+ command, page_addr, column);
+#endif
+
+ /* remap command */
+ switch (command) {
+ case NAND_CMD_SEQIN: /* 0x80 do nothing, wait for 0x10 */
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = NAND_CMD_NONE;
+ info->write_ptr= column;
+ info->logic_operate_time = 0;
+ break;
+ case NAND_CMD_READSTART: /* hw auto gen 0x30 for read */
+ case NAND_CMD_ERASE2: /* hw auto gen 0xd0 for erase */
+#ifdef NAND_DEBUG
+ printf("erase block, erase_size = %x\n", mtd->erasesize);
+#endif
+ info->cmd = NAND_CMD_NONE;
+ break;
+ case NAND_CMD_PAGEPROG: /* 0x10 do the real program */
+ info->cmd = NAND_CMD_SEQIN;
+ info->col_addr = 0;
+ break;
+ case NAND_CMD_READOOB: /* Emulate NAND_CMD_READOOB */
+ info->page_addr = page_addr;
+ info->cmd = NAND_CMD_READ0;
+ /* Offset of oob */
+ info->col_addr = column;
+ info->read_ptr = mtd->writesize;
+ info->cmd_flag = NAND_CMD_READOOB;
+ break;
+ case NAND_CMD_READ0: /* Emulate NAND_CMD_READOOB */
+ info->read_ptr = 0;
+ /*fall though, don't need break;*/
+ default:
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = command;
+ break;
+ }
+ if (info->cmd == NAND_CMD_NONE) {
+ return;
+ }
+ //info->col_addr >>= 1;
+#ifdef NAND_DEBUG
+ printf("after cmd remap, cmd = %x, page_addr = %x, col_addr = %x\n",
+ info->cmd, info->page_addr, info->col_addr);
+#endif
+
+loop:
+ if (info->col_addr != -1) {
+ hal_set_col_addr(info->col_addr);
+ }
+ if (info->page_addr == -1)
+ info->page_addr = 0;
+
+ nand_rda_do_cmd_pre(mtd);
+
+ hal_send_cmd((unsigned char)info->cmd, info->page_addr);
+ cmd_ret = hal_wait_cmd_complete();
+ if (cmd_ret) {
+ printf("cmd fail, cmd = %x, page_addr = %x, col_addr = %x\n",
+ info->cmd, info->page_addr, info->col_addr);
+ }
+
+ nand_rda_do_cmd_post(mtd);
+
+ if(info->nand_use_type > 0 && info->logic_operate_time <= info->nand_use_type && \
+ (info->cmd == NAND_CMD_SEQIN || info->cmd == NAND_CMD_RNDIN))
+ goto loop;
+
+ nand_wait_ready(mtd);
+}
+
+static int nand_rda_dev_ready(struct mtd_info *mtd)
+{
+ return 1;
+}
+
+static int nand_reset_flash(void)
+{
+ int ret = 0;
+
+ hal_send_cmd(NAND_CMD_RESET, 0);
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf("reset flash failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int nand_read_id(unsigned int id[2])
+{
+ int ret = 0;
+
+ hal_send_cmd(NAND_CMD_READID, 0);
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf("nand_read_id failed\n");
+ return ret;
+ }
+
+ id[0] = __raw_readl(NANDFC_REG_IDCODE_A);
+ id[1] = __raw_readl(NANDFC_REG_IDCODE_B);
+
+ printf("Nand ID: %08x %08x\n", id[0], id[1]);
+
+ return ret;
+}
+
+static void read_ID(struct mtd_info *mtd)
+{
+ unsigned int id[2];
+
+ nand_read_id(id);
+}
+
+#if(__TGT_AP_FLASH_USE_PART_OF_PAGE__ == 1)
+static void nand_page_type_setting(struct rda_nand_info *info, int *bus_width_16)
+{
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_4KAS3K_PAGE_DIV__)
+ printf("MLC 4K as 3K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ info->spl_adjust_ratio = 3;//actally ratio * 2
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_4KAS2K_PAGE_DIV__)
+ printf("MLC 4K as 2K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ info->spl_adjust_ratio = 2;//actally ratio * 2
+#endif
+#endif
+
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_8KAS4K_PAGE_DIV__)
+ printf("MLC 8K as 4K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ info->spl_adjust_ratio = 4;//actally ratio * 2
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_8KAS6K_PAGE_DIV__)
+ printf("MLC 8K as 6K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ info->spl_adjust_ratio = 6;//actally ratio * 2
+#endif
+#endif
+
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_16KAS8K_PAGE_DIV__)
+ printf("MLC 16K as 8K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ info->spl_adjust_ratio = 8;//actally ratio * 2
+#else
+#if(__TGT_AP_FLASH_LOGIC_PAGE_SIZE_N_KB__ == __TGT_AP_FLASH_USE_16KAS12K_PAGE_DIV__)
+ printf("MLC 16K as 12K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ info->spl_adjust_ratio = 12;//actally ratio * 2
+#endif
+#endif
+}
+#endif
+
+/********************************************************************************
+******************page size bits(bit[1...0] of the fourth id byte)***************
+1.hynix nand
+ 1) bit1 bit0 pagesize 2) bit1 bit0 pagesize
+ 0 0 2K 0 0 4K
+ 0 1 4k 0 1 8K
+ 1 0 8k 1 0 16K
+ 1 1 null 1 1 32K
+ nand ID:
+ (1)AD D7 94 DA 74 C3 (1)AD D7 94 91 60 44
+*********************************************************************************/
+static int nand_get_type(struct rda_nand_info *info, unsigned int id[2],
+ int *rda_nand_type, int *bus_width_16)
+{
+ u16 hwcfg_nand_type;
+ int metal_id;
+
+ info->spl_adjust_ratio = 2;
+ metal_id = rda_metal_id_get();
+
+#if defined(CONFIG_MACH_RDA8810) || defined(CONFIG_MACH_RDA8850)
+ printf("RDA8810/RDA8850 get type, metal %d\n", metal_id);
+
+ if (metal_id == 0) {
+ hwcfg_nand_type = rda_hwcfg_get() & 0x03;
+ switch(hwcfg_nand_type) {
+ case 0b00:
+ printf("MLC 4K, 8bit bus\n");
+ *rda_nand_type = NAND_TYPE_MLC4K;
+ *bus_width_16 = 0;
+ break;
+ case 0b10:
+ printf("SLC 4K page, 16bit bus\n");
+ *rda_nand_type = NAND_TYPE_SLC4K;
+ *bus_width_16 = 1;
+ break;
+ case 0b01:
+ printf("2K page, 8bit bus\n");
+ *rda_nand_type = NAND_TYPE_2KL;
+ *bus_width_16 = 0;
+ break;
+ case 0b11:
+ printf("2K page, 16bit bus\n");
+ *rda_nand_type = NAND_TYPE_2KL;
+ *bus_width_16 = 1;
+ break;
+ default:
+ printf("NAND: type %d not support\n", hwcfg_nand_type);
+ *rda_nand_type = NAND_TYPE_INVALID;
+ return -ENODEV;
+ }
+ }
+ else if ((metal_id == 2) || (metal_id == 1)) {
+ hwcfg_nand_type = rda_hwcfg_get() & 0x03;
+ switch(hwcfg_nand_type) {
+ case 0b00:
+ printf("SLC 4K, 16bit bus\n");
+ *rda_nand_type = NAND_TYPE_SLC4K;
+ *bus_width_16 = 1;
+ break;
+ case 0b10:
+ printf("MLC 4K, 8bit bus\n");
+ *rda_nand_type = NAND_TYPE_MLC4K;
+ *bus_width_16 = 0;
+ break;
+ case 0b11:
+ printf("MLC 8K, 8bit bus\n");
+ *rda_nand_type = NAND_TYPE_MLC8K;
+ *bus_width_16 = 0;
+ break;
+ default:
+ printf("NAND: type %d not support\n", hwcfg_nand_type);
+ *rda_nand_type = NAND_TYPE_INVALID;
+ return -ENODEV;
+ }
+ }
+ else if (metal_id >= 3) {
+#define RDA_NAND_TYPE_BIT_TYPE (1 << 0) /* 1: MLC; 0: SLC */
+#define RDA_NAND_TYPE_BIT_BUS (1 << 1) /* 1: 8-bit; 0: 16-bit */
+#define RDA_NAND_TYPE_BIT_SIZE (1 << 7) /* 1: 4K SLC / 8K MLC; 0: 2K SLC / 4K MLC */
+ hwcfg_nand_type = rda_hwcfg_get() & 0x83;
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_BUS)
+ *bus_width_16 = 0;
+ else
+ *bus_width_16 = 1;
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_TYPE) {
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_SIZE) {
+ printf("MLC 8K, %s bus\n",
+ (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_type = NAND_TYPE_MLC8K;
+ } else {
+#if(__TGT_AP_FLASH_USE_PART_OF_PAGE__ == 1)
+ switch(id[0] & 0xff){
+ case NAND_MFR_HYNIX:
+ if(id[0] == 0xda94d7ad || id[0] == 0x9e14d7ad){//hynix for nanya
+ *rda_nand_type = NAND_TYPE_MLC8K;
+ }else{
+ switch((id[0] >> 24)&0x3){//MLC
+ case 0:
+ *rda_nand_type = NAND_TYPE_MLC4K;
+ break;
+ case 1:
+ *rda_nand_type = NAND_TYPE_MLC8K;
+ break;
+ case 2:
+ *rda_nand_type = NAND_TYPE_MLC16K;
+ break;
+ case 3:
+ *rda_nand_type = NAND_TYPE_MLC16K;//reserved for 32k
+ break;
+ }
+ }
+ break;
+ case NAND_MFR_TOSHIBA://mlc
+ case NAND_MFR_SAMSUNG:
+ switch((id[0] >> 24)&0x3){
+ case 0:
+ printf("nand:no 2k MLC nand.\n");
+ break;
+ case 1:
+ *rda_nand_type = NAND_TYPE_MLC4K;
+ break;
+ case 2:
+ *rda_nand_type = NAND_TYPE_MLC8K;
+ break;
+ case 3:
+ *rda_nand_type = NAND_TYPE_MLC16K;
+ break;
+ }
+ break;
+ default:
+ switch((id[0] >> 24)&0x3){
+ case 0:
+ printf("nand:not support 1k nand.\n");
+ break;
+ case 1:
+ printf("nand:no 2k MLC nand.\n");
+ break;
+ case 2:
+ *rda_nand_type = NAND_TYPE_MLC4K;
+ break;
+ case 3:
+ *rda_nand_type = NAND_TYPE_MLC8K;
+ break;
+ }
+ break;
+ }
+ nand_page_type_setting(info, bus_width_16);
+#else
+ printf("MLC 4K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_type = NAND_TYPE_MLC4K;
+#endif
+ }
+ } else {
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_SIZE) {
+ printf("SLC 4K, %s bus\n", (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_type = NAND_TYPE_SLC4K;
+ } else {
+ printf("SLC 2KL, %s bus\n",
+ (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_type = NAND_TYPE_2KL;
+ }
+ }
+ }
+ else {
+ printf("NAND: invalid metal id %d\n", metal_id);
+ return -ENODEV;
+ }
+#elif defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8820)
+#error "RDA8810E/RDA8820 does not match RDA NAND Controller V1"
+#else
+#error "unknown MACH"
+#endif
+
+ printf("SPL adjust ratio is %d. \n", info->spl_adjust_ratio);
+ return 0;
+}
+
+static int nand_rda_init(struct nand_chip *this, struct rda_nand_info *info)
+{
+ unsigned int id[2];
+ int ret = 0;
+ int rda_nand_type = NAND_TYPE_INVALID;
+ int bus_width_16 = 0;
+
+ ret = nand_reset_flash();
+ if (ret)
+ return ret;
+ ret = nand_read_id(id);
+ if (ret)
+ return ret;
+ ret = nand_get_type(info, id, &rda_nand_type, &bus_width_16);
+ if (ret)
+ return ret;
+ ret = init_nand_info(info, rda_nand_type, bus_width_16);
+ if (ret)
+ return ret;
+ if (info->bus_width_16)
+ this->options |= NAND_BUSWIDTH_16;
+
+ return hal_init(info);
+}
+
+static int nand_rda_init_size(struct mtd_info *mtd, struct nand_chip *this,
+ u8 *id_data)
+{
+ struct rda_nand_info *info = this->priv;
+
+ mtd->erasesize = info->vir_erase_size;
+ mtd->writesize = info->vir_page_size;
+ mtd->oobsize = info->vir_oob_size;
+
+ return (info->bus_width_16) ? NAND_BUSWIDTH_16 : 0;
+}
+
+int rda_nand_init(struct nand_chip *nand)
+{
+ struct rda_nand_info *info;
+ static struct rda_nand_info rda_nand_info;
+
+ info = &rda_nand_info;
+
+ nand->chip_delay = 0;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->options |= NAND_USE_FLASH_BBT;
+#endif
+ /*
+ * in fact, nand controler we do hardware ECC silently, and
+ * we don't need tell mtd layer, and will simple nand operations
+ */
+ nand->ecc.mode = NAND_ECC_NONE;
+ /* Set address of hardware control function */
+ nand->cmd_ctrl = nand_rda_hwcontrol;
+ nand->init_size = nand_rda_init_size;
+ nand->cmdfunc = nand_rda_cmdfunc;
+
+ nand->read_byte = nand_rda_read_byte;
+ nand->read_word = nand_rda_read_word;
+ nand->read_buf = nand_rda_read_buf;
+ nand->write_buf = nand_rda_write_buf;
+ nand->read_nand_ID = read_ID;
+ nand->dev_ready = nand_rda_dev_ready;
+
+ nand->priv = (void *)info;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)NANDFC_DATA_BUF;
+#ifdef CONFIG_NAND_RDA_DMA
+ info->nand_data_phys = (void __iomem *)NANDFC_DATA_BUF;
+#endif /* CONFIG_NAND_RDA_DMA */
+ info->index = 0;
+ info->write_ptr = 0;
+ info->read_ptr = 0;
+ info->cmd_flag = NAND_CMD_NONE;
+ info->master_clk = get_master_clk_rate(_TGT_AP_CLK_APB2);
+ info->clk=_TGT_AP_NAND_CLOCK;
+
+#ifdef CONFIG_NAND_RDA_DMA
+ rda_request_dma(&info->dma_ch);
+#endif /* CONFIG_NAND_RDA_DMA */
+
+ nand_rda_init(nand, info);
+
+ if (!nand->ecc.layout && (nand->ecc.mode != NAND_ECC_SOFT_BCH)) {
+ switch (info->vir_oob_size) {
+ case 64:
+ nand->ecc.layout = &rda_nand_oob_64;
+ break;
+ case 128:
+ nand->ecc.layout = &rda_nand_oob_128;
+ break;
+ case 256:
+ nand->ecc.layout = &rda_nand_oob_256;
+ break;
+ case 512:
+ nand->ecc.layout = &rda_nand_oob_512;
+ break;
+ default:
+ printf("error oob size: %d \n", info->vir_oob_size);
+ }
+ }
+
+ return 0;
+}
+
+#else
+int rda_nand_init(struct nand_chip *nand) {return 0;}
+#endif
+
+#if 0
+/* move to rda_nand_base.c */
+int board_nand_init(struct nand_chip *chip) __attribute__ ((weak));
+
+int board_nand_init(struct nand_chip *chip)
+{
+ return rda_nand_init(chip);
+}
+#endif
diff --git a/drivers/mtd/nand/rda_nand_v2.c b/drivers/mtd/nand/rda_nand_v2.c
new file mode 100644
index 0000000000..925a1c81c8
--- /dev/null
+++ b/drivers/mtd/nand/rda_nand_v2.c
@@ -0,0 +1,941 @@
+#include <common.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <nand.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_nand.h>
+#include <asm/arch/reg_sysctrl.h>
+#include <asm/arch/hwcfg.h>
+#include <mtd/nand/rda_nand.h>
+
+#include "tgt_ap_board_config.h"
+#include "tgt_ap_clock_config.h"
+
+#ifdef CONFIG_SPL_BUILD
+#undef CONFIG_NAND_RDA_DMA
+#endif
+
+#ifdef CONFIG_NAND_RDA_DMA
+#include <asm/dma-mapping.h>
+#include <asm/arch/dma.h>
+#endif
+
+//#define NAND_DEBUG
+//#define NAND_DEBUG_VERBOSE
+
+#define NAND_NEED_OOB_OFF_FIX
+#ifdef NAND_NEED_OOB_OFF_FIX
+static uint8_t oob_ff_pattern[] = { 0xff, 0xff };
+#define NAND_4K_86_OOB_OFF 0x10B8
+#define NAND_8K_86_OOB_OFF 0x2170
+#define NAND_8K_83_OOB_OFF 0x23C0
+#endif
+
+#define hal_gettime get_ticks
+#define SECOND * CONFIG_SYS_HZ_CLOCK
+#define NAND_TIMEOUT ( 2 SECOND )
+#define PLL_BUS_FREQ (_TGT_AP_PLL_BUS_FREQ * 1000000)
+
+/* clock division value map */
+const static u8 clk_div_map[] = {
+ 4*60, /* 0 */
+ 4*60, /* 1 */
+ 4*60, /* 2 */
+ 4*60, /* 3 */
+ 4*60, /* 4 */
+ 4*60, /* 5 */
+ 4*60, /* 6 */
+ 4*60, /* 7 */
+ 4*40, /* 8 */
+ 4*30, /* 9 */
+ 4*24, /* 10 */
+ 4*20, /* 11 */
+ 4*17, /* 12 */
+ 4*15, /* 13 */
+ 4*13, /* 14 */
+ 4*12, /* 15 */
+ 4*11, /* 16 */
+ 4*10, /* 17 */
+ 4*9, /* 18 */
+ 4*8, /* 19 */
+ 4*7, /* 20 */
+ 4*13/2, /* 21 */
+ 4*6, /* 22 */
+ 4*11/2, /* 23 */
+ 4*5, /* 24 */
+ 4*9/2, /* 25 */
+ 4*4, /* 26 */
+ 4*7/2, /* 27 */
+ 4*3, /* 28 */
+ 4*5/2, /* 29 */
+ 4*2, /* 30 */
+ 4*1, /* 31 */
+};
+
+static struct nand_ecclayout rda_nand_oob_40 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 38} }
+};
+
+static struct nand_ecclayout rda_nand_oob_64 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 62} }
+};
+
+static struct nand_ecclayout rda_nand_oob_128 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 126} }
+};
+
+static struct nand_ecclayout rda_nand_oob_256 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 254} }
+};
+
+static struct nand_ecclayout rda_nand_oob_512 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 510} }
+};
+
+extern void rda_dump_buf(char *data, size_t len);
+
+static void hal_send_cmd(unsigned char cmd, unsigned int page_addr)
+{
+ unsigned long cmd_reg;
+
+ cmd_reg = NANDFC_DCMD(cmd) | NANDFC_PAGE_ADDR(page_addr);
+#ifdef NAND_DEBUG
+ printf(" hal_send_cmd 0x%08lx\n", cmd_reg);
+#endif
+ __raw_writel(cmd_reg, NANDFC_REG_DCMD_ADDR);
+}
+
+static void hal_set_col_addr(unsigned int col_addr)
+{
+ __raw_writel(col_addr, NANDFC_REG_COL_ADDR);
+}
+
+static void hal_flush_buf(struct mtd_info *mtd)
+{
+ /*
+ there is no reg NANDFC_REG_BUF_CTRL
+ */
+ //__raw_writel(0x7, NANDFC_REG_BUF_CTRL);
+}
+
+static unsigned long hal_wait_cmd_complete(void)
+{
+ unsigned long int_stat;
+ unsigned long long wait_time = NAND_TIMEOUT;
+ unsigned long long start_time = hal_gettime();
+ int timeout = 0;
+
+ /* wait done */
+ /*
+ * Use NANDFC_INT_STAT_IDLE instead of NANDFC_INT_DONE
+ * this is HW bug, NANDFC_INT_DONE comes too early
+ * however, NANDFC_INT_STAT_IDLE is not an interrupt source
+ * need HW fix anyway
+ */
+ do {
+ int_stat = __raw_readl(NANDFC_REG_INT_STAT);
+ if (hal_gettime() - start_time >= wait_time) {
+ timeout = 1;
+ }
+#ifdef CONFIG_MACH_RDA8810E
+ } while (!(int_stat & NANDFC_INT_STAT_IDLE) && !timeout);
+#else
+ } while (!(int_stat & NANDFC_INT_DONE) && !timeout);
+#endif
+
+ /* to clear */
+ __raw_writel(int_stat & NANDFC_INT_CLR_MASK, NANDFC_REG_INT_STAT);
+
+ if (timeout) {
+ printf("nand error, cmd timeout\n");
+ return -ETIME;
+ }
+
+ if (int_stat & NANDFC_INT_ERR_ALL) {
+ printf("nand error, int_stat = %lx\n", int_stat);
+ return (int_stat & NANDFC_INT_ERR_ALL);
+ }
+ return 0;
+}
+
+static int init_nand_info(struct rda_nand_info *info,
+ int nand_type, int nand_ecc, int bus_width_16)
+{
+ info->type = nand_type;
+ info->ecc_mode = nand_ecc;
+ info->bus_width_16 = bus_width_16;
+ switch (nand_type) {
+ case NAND_TYPE_2K:
+ info->page_size = 2048;
+ info->page_shift = 11;
+ info->oob_size = 64;
+ break;
+ case NAND_TYPE_4K:
+ info->page_size = 4096;
+ info->page_shift = 12;
+ info->oob_size = 224;
+ info->vir_oob_size = 40;
+ break;
+ case NAND_TYPE_8K:
+ info->page_size = 8192;
+ info->page_shift = 13;
+ info->oob_size = 744;
+ break;
+ default:
+ printf("invalid nand type %d\n", nand_type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static u32 cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2)
+{
+ u32 newfreq;
+
+ if (reg >= ARRAY_SIZE(clk_div_map)) {
+ printf("nand:Invalid div reg: %u\n", reg);
+ reg = ARRAY_SIZE(clk_div_map) - 1;
+ }
+ /* Assuming basefreq is smaller than 2^31 (2.147G Hz) */
+ newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1);
+ return newfreq;
+}
+
+unsigned long get_master_clk_rate(u32 reg)
+{
+ u32 div2;
+ unsigned long rate;
+
+ div2 = reg & SYS_CTRL_AP_AP_APB2_SRC_SEL;
+ reg = GET_BITFIELD(reg, SYS_CTRL_AP_AP_APB2_FREQ);
+ rate = cal_freq_by_divreg(PLL_BUS_FREQ, reg, div2);
+
+ return rate;
+}
+
+static unsigned long rda_nand_cld_div_tbl[16] = {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 12, 14, 16, 18, 20, 22, 24, 28
+};
+
+static unsigned long hal_calc_divider(struct rda_nand_info *info)
+{
+ unsigned long mclk = info->master_clk;
+ unsigned long div, clk_div;
+ int i;
+
+ div = mclk / info->clk;
+ if (mclk % info->clk)
+ div += 1;
+
+ if (div < 5) {
+ /* 5 is minimal divider by V2 hardware */
+ div = 5;
+ }
+
+ for (i=0;i<16;i++) {
+ if(div <= rda_nand_cld_div_tbl[i]) {
+ clk_div = i;
+ break;
+ }
+ }
+
+ if (i>=16) {
+ clk_div = 15;
+ }
+
+ printf("NAND: max clk: %ld, bus clk: %ld\n", info->clk, mclk);
+ printf("NAND: div: %ld, clk_div: %ld\n", div, clk_div);
+ return clk_div;
+}
+
+static int hal_init(struct rda_nand_info *info)
+{
+ unsigned long config_a, config_b;
+#ifdef CONFIG_RDA_FPGA
+ unsigned long clk_div = 4;
+#else
+ unsigned long clk_div = hal_calc_divider(info);
+#endif
+
+ /* setup config_a and config_b */
+ config_a = NANDFC_CYCLE(clk_div);
+ config_a |= NANDFC_CHIP_SEL(0x0e);
+ config_a |= NANDFC_POLARITY_IO(0); // set 0 invert IO
+ config_a |= NANDFC_POLARITY_MEM(1); // set 1 invert MEM
+
+ switch (info->type) {
+ case NAND_TYPE_2K:
+ config_a |= NANDFC_TIMING(0x8B);
+ break;
+ case NAND_TYPE_4K:
+ config_a |= NANDFC_TIMING(0x8C);
+ break;
+ case NAND_TYPE_8K:
+ config_a |= NANDFC_TIMING(0x8D);
+ break;
+ default:
+ printf("invalid nand type %d\n", info->type);
+ return -EINVAL;
+ }
+
+ /* enable the 16bit mode; */
+ if (info->bus_width_16)
+ config_a |= NANDFC_WDITH_16BIT(1);
+
+ config_b = NANDFC_HWECC(1) | NANDFC_ECC_MODE(info->ecc_mode);
+
+ __raw_writel(config_a, NANDFC_REG_CONFIG_A);
+ __raw_writel(config_b, NANDFC_REG_CONFIG_B);
+
+#if 1
+ /* Set an interval of filter for erasing operation. */
+ unsigned long delay;
+ delay = __raw_readl(NANDFC_REG_DELAY);
+ delay |= 0x10;
+ __raw_writel(delay, NANDFC_REG_DELAY);
+#endif /* #if 0 */
+
+ printf("NAND: nand init done, %08lx %08lx\n", config_a, config_b);
+
+ return 0;
+}
+
+static u8 nand_rda_read_byte(struct mtd_info *mtd)
+{
+ u8 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *((u8 *) info->byte_buf + info->index);
+ info->index++;
+#ifdef NAND_DEBUG
+ printf("nand_read_byte, ret = %02x\n", ret);
+#endif
+ return ret;
+}
+
+static u16 nand_rda_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *(u16 *) ((uint8_t *) info->byte_buf + info->index);
+ info->index += 2;
+#ifdef NAND_DEBUG
+ printf("nand_read_word, ret = %04x\n", ret);
+#endif
+ return ret;
+}
+
+static void nand_rda_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_R + info->read_ptr);
+
+#ifdef NAND_DEBUG
+ printf("read : buf addr = 0x%p, len = 0x%x, read_ptr = 0x%x \n", buf, len,
+ info->read_ptr);
+#endif /* NAND_DEBUG */
+
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)buf, (void *)nand_ptr, len);
+ if(info->ecc_mode == NAND_ECC_1K24BIT)
+ info->read_ptr += 184;
+ info->read_ptr += len;
+
+#ifdef NAND_DEBUG_VERBOSE
+ rda_dump_buf((char *)buf, 2);
+#endif
+
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy(buf, (void *)nand_ptr, len);
+ info->read_ptr = 0;
+ return;
+ }
+
+ if (((u32)addr & 0x7) != 0) {
+ printf("ERROR, nand dma read buffer %p is not 8bytes aligned\n",
+ addr);
+ return;
+ }
+
+ phys_addr = dma_map_single(addr, len, DMA_FROM_DEVICE);
+
+ dma_param.src_addr = (u32) info->nand_data_phys;
+ dma_param.dst_addr = phys_addr;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FR_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_FROM_DEVICE);
+ return;
+ }
+
+ /* use flush to avoid annoying unaligned warning */
+ /* however, invalidate after the dma it the right thing to do */
+ flush_dcache_range((u32)addr, (u32)(addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* use flush to avoid annoying unaligned warning */
+ //invalidate_dcache_range((u32)addr, (u32)(addr + len));
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_FROM_DEVICE);
+ info->read_ptr += len;
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void nand_rda_write_buf(struct mtd_info *mtd, const uint8_t * buf,
+ int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_W + info->write_ptr);
+
+#ifdef NAND_DEBUG
+ printf("write : buf addr = 0x%p, len = %d, write_ptr = %d\n", buf, len,
+ info->write_ptr);
+#endif /* NAND_DEBUG */
+
+#ifdef NAND_DEBUG_VERBOSE
+ rda_dump_buf((char *)buf, len);
+#endif
+
+#ifdef NAND_NEED_OOB_OFF_FIX
+ u8 *nand_oob_ptr;
+ switch (info->type) {
+ case NAND_TYPE_2K:
+ break;
+ case NAND_TYPE_4K:
+ if(info->write_ptr > 0 && info->ecc_mode == NAND_ECC_1K24BIT){
+ nand_ptr = (u8 *)(chip->IO_ADDR_W + NAND_4K_86_OOB_OFF);
+ len = 40;
+ }
+ break;
+ case NAND_TYPE_8K:
+ if (info->ecc_mode == NAND_ECC_1K24BIT)
+ nand_oob_ptr = (u8 *)(chip->IO_ADDR_W +
+ NAND_8K_86_OOB_OFF);
+ else
+ nand_oob_ptr = (u8 *)(chip->IO_ADDR_W +
+ NAND_8K_83_OOB_OFF);
+ memcpy((void *)nand_oob_ptr, (void *)oob_ff_pattern, 2);
+ break;
+ default:
+ break;
+ }
+#endif
+
+#ifndef CONFIG_NAND_RDA_DMA
+ memcpy((void *)nand_ptr, (void *)buf, len);
+ info->write_ptr += len;
+
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy((void *)nand_ptr, (void *)buf, len);
+ info->write_ptr += len;
+ return;
+ }
+
+ if (((u32)addr & 0x7) != 0) {
+ printf("ERROR, nand dma write %p buffer is not 8bytes aligned\n",
+ addr);
+ return;
+ }
+ phys_addr = dma_map_single(addr, len, DMA_TO_DEVICE);
+
+ dma_param.src_addr = phys_addr;
+ dma_param.dst_addr = (u32) info->nand_data_phys;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FW_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_TO_DEVICE);
+ return;
+ }
+
+ flush_dcache_range((u32)addr, (u32)(addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_TO_DEVICE);
+ info->write_ptr += len;
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void nand_rda_do_cmd_pre(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+
+ __raw_writel(0xfff, NANDFC_REG_INT_STAT);
+ switch (info->cmd) {
+ case NAND_CMD_SEQIN:
+ hal_flush_buf(mtd);
+ info->write_ptr = 0;
+ break;
+ case NAND_CMD_READ0:
+ hal_flush_buf(mtd);
+ //info->read_ptr = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static void nand_rda_do_cmd_post(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+ u32 temp;
+
+ switch (info->cmd) {
+ case NAND_CMD_RESET:
+ return;
+ case NAND_CMD_READID:
+ temp = __raw_readl(NANDFC_REG_IDCODE_A);
+ info->byte_buf[0] = temp;
+ temp = __raw_readl(NANDFC_REG_IDCODE_B);
+ info->byte_buf[1] = temp;
+ info->index = 0;
+ break;
+ case NAND_CMD_STATUS:
+ temp = __raw_readl(NANDFC_REG_OP_STATUS);
+ info->byte_buf[0] = (temp & 0xFF);
+ info->index = 0;
+ if (info->byte_buf[0] != 0xe0) {
+ printf("nand error in op status %x\n",
+ info->byte_buf[0]);
+ }
+#ifdef NAND_DEBUG
+ printf("post_cmd read status %x\n", info->byte_buf[0]);
+#endif
+ break;
+ case NAND_CMD_READ0: // NAND_CMD_READOOB goes here too
+ break;
+#if 0
+/* read back after write, for err checking */
+ case NAND_CMD_SEQIN:
+ {
+ int cmd_ret;
+ /* read back instantly and check fcs/crc result */
+ hal_send_cmd((unsigned char)NAND_CMD_READ0,
+ info->page_addr);
+ cmd_ret = hal_wait_cmd_complete();
+ if (cmd_ret) {
+ printf
+ ("readback cmd fail, page_addr = %x, ret = %x\n",
+ info->page_addr, cmd_ret);
+ }
+ }
+ break;
+#endif
+#if 0
+/* delay 10ms, for erase to complete */
+ case NAND_CMD_ERASE1:
+ udelay(10000);
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static void nand_rda_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+#ifdef NAND_DEBUG
+ printf("nand_rda_hwcontrol, cmd = %x, ctrl = %x, not use anymore\n",
+ cmd, ctrl);
+#endif
+}
+
+static void nand_rda_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ unsigned long cmd_ret;
+
+#ifdef NAND_DEBUG
+ printf("nand_rda_cmdfunc, cmd = %x, page_addr = %x, col_addr = %x\n",
+ command, page_addr, column);
+#endif
+
+ /* remap command */
+ switch (command) {
+ case NAND_CMD_SEQIN: /* 0x80 do nothing, wait for 0x10 */
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = NAND_CMD_NONE;
+ info->write_ptr= column;
+ break;
+ case NAND_CMD_READSTART: /* hw auto gen 0x30 for read */
+ case NAND_CMD_ERASE2: /* hw auto gen 0xd0 for erase */
+#ifdef NAND_DEBUG
+ printf("erase block, erase_size = %x\n", mtd->erasesize);
+#endif
+ info->cmd = NAND_CMD_NONE;
+ break;
+ case NAND_CMD_PAGEPROG: /* 0x10 do the real program */
+ info->cmd = NAND_CMD_SEQIN;
+ info->col_addr = 0;
+ break;
+ case NAND_CMD_READOOB: /* Emulate NAND_CMD_READOOB */
+ info->page_addr = page_addr;
+ info->cmd = NAND_CMD_READ0;
+ /* Offset of oob */
+ info->col_addr = column;
+ info->read_ptr = mtd->writesize;
+#ifdef NAND_NEED_OOB_OFF_FIX
+ switch (info->type) {
+ case NAND_TYPE_2K:
+ break;
+ case NAND_TYPE_4K:
+ //info->col_addr = NAND_4K_86_OOB_OFF;
+ info->read_ptr = NAND_4K_86_OOB_OFF;
+ break;
+ case NAND_TYPE_8K:
+ if (info->ecc_mode == NAND_ECC_1K24BIT) {
+ info->col_addr = NAND_8K_86_OOB_OFF;
+ info->read_ptr = NAND_8K_86_OOB_OFF;
+ }
+ else {
+ info->col_addr = NAND_8K_83_OOB_OFF;
+ info->read_ptr = NAND_8K_83_OOB_OFF;
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+ break;
+ case NAND_CMD_READ0: /* Emulate NAND_CMD_READOOB */
+ info->read_ptr = 0;
+ /*fall though, don't need break;*/
+ default:
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = command;
+ break;
+ }
+ if (info->cmd == NAND_CMD_NONE) {
+ return;
+ }
+ //info->col_addr >>= 1;
+#ifdef NAND_DEBUG
+ printf("after cmd remap, cmd = %x, page_addr = %x, col_addr = %x\n",
+ info->cmd, info->page_addr, info->col_addr);
+#endif
+
+ if (info->col_addr != -1) {
+ hal_set_col_addr(info->col_addr);
+ }
+ if (info->page_addr == -1)
+ info->page_addr = 0;
+
+ nand_rda_do_cmd_pre(mtd);
+
+ hal_send_cmd((unsigned char)info->cmd, info->page_addr);
+ cmd_ret = hal_wait_cmd_complete();
+ if (cmd_ret) {
+ printf("cmd fail, cmd = %x, page_addr = %x, col_addr = %x\n",
+ info->cmd, info->page_addr, info->col_addr);
+ }
+
+ nand_rda_do_cmd_post(mtd);
+
+ nand_wait_ready(mtd);
+}
+
+static int nand_rda_dev_ready(struct mtd_info *mtd)
+{
+ return 1;
+}
+
+static int nand_reset_flash(void)
+{
+ int ret = 0;
+
+ hal_send_cmd(NAND_CMD_RESET, 0);
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf("nand_reset_flash failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int nand_read_id(unsigned int id[2])
+{
+ int ret = 0;
+
+ hal_send_cmd(NAND_CMD_READID, 0);
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf("nand_read_id failed\n");
+ return ret;
+ }
+
+ id[0] = __raw_readl(NANDFC_REG_IDCODE_A);
+ id[1] = __raw_readl(NANDFC_REG_IDCODE_B);
+
+ printf("NAND: Nand ID: %08x %08x\n", id[0], id[1]);
+
+ return ret;
+}
+
+static void read_ID(struct mtd_info *mtd)
+{
+ unsigned int id[2];
+
+ nand_read_id(id);
+}
+
+static int nand_get_type(struct rda_nand_info *info, unsigned int id[2],
+ int *rda_nand_type, int *rda_nand_ecc, int *bus_width_16)
+{
+ u16 hwcfg_nand_type;
+ int metal_id;
+
+ info->spl_adjust_ratio = 2;
+ metal_id = rda_metal_id_get();
+
+#if defined(CONFIG_MACH_RDA8810) || defined(CONFIG_MACH_RDA8850)
+#error "RDA8810/RDA8850 does not match RDA NAND Controller V2"
+#elif defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8820)
+ printf("NAND: RDA8810E/RDA8820 get type, metal %d\n", metal_id);
+ if (1) {
+#define RDA_NAND_TYPE_BIT_7 (1 << 7) /* 1: 8K NAND, 0 - 2K/4K NAND */
+#define RDA_NAND_TYPE_BIT_BUS (1 << 1) /* 1: 8-bit; 0: 16-bit */
+#define RDA_NAND_TYPE_BIT_0 (1 << 0) /* if BIT_7 == 1 (8K NAND) : 1 - 1K64bit ECC, 0 - 1K24bit ECC */
+ /* if BIT_7 == 0 (4K/2K) : 1 - 4K(1K24bit), 0 - 2K(2K24bit) */
+ hwcfg_nand_type = rda_hwcfg_get() & 0x83;
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_BUS)
+ *bus_width_16 = 0;
+ else
+ *bus_width_16 = 1;
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_7) {
+ /* 8K */
+ *rda_nand_type = NAND_TYPE_8K;
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_0) {
+ printf("NAND: 8K page, 1K64BIT ECC, %s bus\n",
+ (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_ecc = NAND_ECC_1K64BIT;
+ } else {
+ printf("NAND: 8K page, 1K24BIT ECC, %s bus\n",
+ (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_ecc = NAND_ECC_1K24BIT;
+ }
+ } else {
+ /* 4K/2K */
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_0) {
+ printf("NAND: 4K page, 1K24BIT ECC, %s bus\n",
+ (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_type = NAND_TYPE_4K;
+ *rda_nand_ecc = NAND_ECC_1K24BIT;
+ } else {
+ printf("NAND: 2K page, 2K24BIT ECC, %s bus\n",
+ (*bus_width_16)?"16bit":"8bit");
+ *rda_nand_type = NAND_TYPE_2K;
+ *rda_nand_ecc = NAND_ECC_2K24BIT;
+ }
+ }
+ }
+#else
+#error "unknown MACH"
+#endif
+
+ printf("NAND: actually ratio * 2, SPL adjust ratio is %d. \n",
+ info->spl_adjust_ratio);
+ return 0;
+}
+
+static int nand_rda_init(struct nand_chip *this, struct rda_nand_info *info)
+{
+ unsigned int id[2];
+ int ret = 0;
+ int rda_nand_type = NAND_TYPE_INVALID;
+ int rda_nand_ecc = NAND_ECC_INVALID;
+ int bus_width_16 = 0;
+
+ ret = nand_reset_flash();
+ if (ret)
+ return ret;
+ ret = nand_read_id(id);
+ if (ret)
+ return ret;
+ ret = nand_get_type(info, id, &rda_nand_type, &rda_nand_ecc, &bus_width_16);
+ if (ret)
+ return ret;
+ ret = init_nand_info(info, rda_nand_type, rda_nand_ecc, bus_width_16);
+ if (ret)
+ return ret;
+ if (info->bus_width_16)
+ this->options |= NAND_BUSWIDTH_16;
+
+ return hal_init(info);
+}
+
+#if defined(CONFIG_SPL_BUILD)
+/* this is for u-boot-spl only */
+static int nand_rda_init_size(struct mtd_info *mtd, struct nand_chip *this,
+ u8 *id_data)
+{
+ struct rda_nand_info *info = this->priv;
+
+ /* TODO: this is not always right
+ * assuming nand is 2k/4k SLC for now
+ * for other types of nand, 64 pages per block is not always true
+ */
+ mtd->erasesize = info->page_size * 64;
+ mtd->writesize = info->page_size;
+ mtd->oobsize = (info->oob_size > 256)?256:info->oob_size;
+
+ return (info->bus_width_16) ? NAND_BUSWIDTH_16 : 0;
+}
+#endif
+
+int rda_nand_init(struct nand_chip *nand)
+{
+ struct rda_nand_info *info;
+ static struct rda_nand_info rda_nand_info;
+
+ info = &rda_nand_info;
+
+ nand->chip_delay = 0;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->options |= NAND_USE_FLASH_BBT;
+#endif
+ /*
+ * in fact, nand controler we do hardware ECC silently, and
+ * we don't need tell mtd layer, and will simple nand operations
+ */
+ nand->ecc.mode = NAND_ECC_NONE;
+ /* Set address of hardware control function */
+ nand->cmd_ctrl = nand_rda_hwcontrol;
+#if defined(CONFIG_SPL_BUILD)
+ nand->init_size = nand_rda_init_size;
+#else
+ nand->init_size = NULL;
+#endif
+ nand->cmdfunc = nand_rda_cmdfunc;
+
+ nand->read_nand_ID = read_ID;
+ nand->read_byte = nand_rda_read_byte;
+ nand->read_word = nand_rda_read_word;
+ nand->read_buf = nand_rda_read_buf;
+ nand->write_buf = nand_rda_write_buf;
+
+ nand->dev_ready = nand_rda_dev_ready;
+
+ nand->priv = (void *)info;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)NANDFC_DATA_BUF;
+#ifdef CONFIG_NAND_RDA_DMA
+ info->nand_data_phys = (void __iomem *)NANDFC_DATA_BUF;
+#endif /* CONFIG_NAND_RDA_DMA */
+ info->index = 0;
+ info->write_ptr = 0;
+ info->read_ptr = 0;
+ info->master_clk = get_master_clk_rate(_TGT_AP_CLK_APB2);
+ info->clk=_TGT_AP_NAND_CLOCK;
+
+#ifdef CONFIG_NAND_RDA_DMA
+ rda_request_dma(&info->dma_ch);
+#endif /* CONFIG_NAND_RDA_DMA */
+
+ nand_rda_init(nand, info);
+
+ if (!nand->ecc.layout && (nand->ecc.mode != NAND_ECC_SOFT_BCH)) {
+ switch (info->vir_oob_size) {
+ case 40:
+ nand->ecc.layout = &rda_nand_oob_40;
+ break;
+ case 64:
+ nand->ecc.layout = &rda_nand_oob_64;
+ break;
+ case 128:
+ nand->ecc.layout = &rda_nand_oob_128;
+ break;
+ case 256:
+ nand->ecc.layout = &rda_nand_oob_256;
+ break;
+ case 512:
+ nand->ecc.layout = &rda_nand_oob_512;
+ break;
+ default:
+ printf("error oob size: %d \n", info->vir_oob_size);
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+/* move to rda_nand_base.c */
+int board_nand_init(struct nand_chip *chip) __attribute__ ((weak));
+
+int board_nand_init(struct nand_chip *chip)
+{
+ return rda_nand_init(chip);
+}
+#endif
diff --git a/drivers/mtd/nand/rda_nand_v3.c b/drivers/mtd/nand/rda_nand_v3.c
new file mode 100644
index 0000000000..f603bc143a
--- /dev/null
+++ b/drivers/mtd/nand/rda_nand_v3.c
@@ -0,0 +1,2050 @@
+#include <common.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <nand.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_nand.h>
+#include <asm/arch/reg_sysctrl.h>
+#include <asm/arch/hwcfg.h>
+#include <asm/arch-rda/ispi.h>
+#include <mtd/nand/rda_nand.h>
+
+#include "tgt_ap_board_config.h"
+#include "tgt_ap_clock_config.h"
+#include "tgt_ap_flash_parts.h"
+
+#ifdef CONFIG_SPL_BUILD
+#undef CONFIG_NAND_RDA_DMA
+#endif
+
+#ifdef CONFIG_NAND_RDA_DMA
+#include <asm/dma-mapping.h>
+#include <asm/arch/dma.h>
+#endif
+
+/* FOR NAND TEST */
+#ifdef TGT_AP_DO_NAND_TEST
+
+unsigned int gbl_nand_test_id_val[2];
+unsigned char g_nand_test_buf_write[4096] = {0};
+unsigned char g_nand_test_buf_read[4096] = {0};
+#endif /* TGT_AP_DO_NAND_TEST */
+
+//#define NAND_DEBUG
+//#define NAND_DEBUG_VERBOSE
+#define NAND_CONTROLLER_BUFFER_LEN 9216
+
+#define hal_gettime get_ticks
+#define SECOND * CONFIG_SYS_HZ_CLOCK
+#define NAND_TIMEOUT ( 2 SECOND )
+#define PLL_BUS_FREQ (_TGT_AP_PLL_BUS_FREQ * 1000000)
+
+#ifdef CONFIG_SPL_BUILD
+/*sram space is not enough, so use sdram space as middle data buffer*/
+static u8 *g_nand_flash_temp_buffer = (u8 *)CONFIG_SPL_NAND_MIDDLE_DATA_BUFFER;
+#else
+static u8 g_nand_flash_temp_buffer[32*1024];
+#endif
+
+#define ECCSIZE(n) ((n)*15/8)
+static const u8 g_nand_ecc_size[] = {
+ ECCSIZE(24),
+ ECCSIZE(96),
+ ECCSIZE(96),
+ ECCSIZE(64),
+ ECCSIZE(56),
+ ECCSIZE(40),
+ ECCSIZE(24),
+ ECCSIZE(48),
+ ECCSIZE(32),
+ ECCSIZE(16),
+ ECCSIZE(8),
+ ECCSIZE(72),
+ ECCSIZE(80),
+ ECCSIZE(88),
+};
+
+struct flash_nand_parameter {
+ unsigned int page_size;
+ unsigned int ecc_msg_len;
+ unsigned int spare_space_size;
+ unsigned int oob_size;
+ unsigned int ecc_bits;
+ unsigned int spl_offset;
+ unsigned int crc;
+};
+
+struct flash_nand_parameter nand_parameter;
+
+/* clock division value map */
+const static u8 clk_div_map[] = {
+ 4*60, /* 0 */
+ 4*60, /* 1 */
+ 4*60, /* 2 */
+ 4*60, /* 3 */
+ 4*60, /* 4 */
+ 4*60, /* 5 */
+ 4*60, /* 6 */
+ 4*60, /* 7 */
+ 4*40, /* 8 */
+ 4*30, /* 9 */
+ 4*24, /* 10 */
+ 4*20, /* 11 */
+ 4*17, /* 12 */
+ 4*15, /* 13 */
+ 4*13, /* 14 */
+ 4*12, /* 15 */
+ 4*11, /* 16 */
+ 4*10, /* 17 */
+ 4*9, /* 18 */
+ 4*8, /* 19 */
+ 4*7, /* 20 */
+ 4*13/2, /* 21 */
+ 4*6, /* 22 */
+ 4*11/2, /* 23 */
+ 4*5, /* 24 */
+ 4*9/2, /* 25 */
+ 4*4, /* 26 */
+ 4*7/2, /* 27 */
+ 4*3, /* 28 */
+ 4*5/2, /* 29 */
+ 4*2, /* 30 */
+ 4*1, /* 31 */
+};
+
+static struct nand_ecclayout rda_nand_oob_2k = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 30} }
+};
+
+static struct nand_ecclayout rda_nand_oob_4k = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 30} }
+};
+
+static struct nand_ecclayout rda_nand_oob_8k = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 30} }
+};
+
+static struct nand_ecclayout rda_nand_oob_16k = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 30} }
+};
+
+extern void rda_dump_buf(char *data, size_t len);
+
+static unsigned int checksum32( unsigned char* data, unsigned int data_size)
+{
+ unsigned int checksum = 0;
+ unsigned int i;
+
+ for(i = 0; i < data_size; i++)
+ checksum = ((checksum << 31) | (checksum >> 1)) + (unsigned int)data[i];
+
+ return checksum;
+}
+
+static void fill_nand_parameter(void)
+{
+ nand_parameter.page_size = NAND_PAGE_SIZE;
+ nand_parameter.spare_space_size = NAND_SPARE_SIZE;
+ nand_parameter.ecc_bits = NAND_ECCBITS;
+ nand_parameter.ecc_msg_len = NAND_ECCMSGLEN;
+ nand_parameter.oob_size = NAND_OOBSIZE;
+ nand_parameter.spl_offset = NAND_PAGE_SIZE;
+ nand_parameter.crc = checksum32((unsigned char *)&nand_parameter,
+ sizeof(struct flash_nand_parameter) - 4);
+}
+
+static void hal_send_cmd_brick(unsigned char data, unsigned char io16,
+ unsigned short num, unsigned char next_act)
+{
+ unsigned long cmd_reg;
+
+ cmd_reg = NANDFC_BRICK_DATA(data)
+ | NANDFC_BRICK_W_WIDTH(io16)
+ | NANDFC_BRICK_R_WIDTH(io16)
+ | NANDFC_BRICK_DATA_NUM(num)
+ | NANDFC_BRICK_NEXT_ACT(next_act);
+ __raw_writel(cmd_reg, NANDFC_REG_BRICK_FIFO_WRITE_POINTER);
+#ifdef NAND_DEBUG
+ printf(" hal_send_cmd_brick 0x%08lx\n", cmd_reg);
+#endif
+}
+
+static void hal_start_cmd_data(void)
+{
+ unsigned long cmd_reg;
+
+ cmd_reg = __raw_readl(NANDFC_REG_CONFIG_A);
+ cmd_reg |= NANDFC_CMDFULL_STA(1);
+
+ __raw_writel(cmd_reg, NANDFC_REG_CONFIG_A);
+}
+
+#ifdef __SCRAMBLE_ENABLE__
+static void hal_set_scramble_func(BOOL enable)
+{
+ unsigned long cmd_reg;
+
+ cmd_reg = __raw_readl(NANDFC_REG_CONFIG_A);
+ cmd_reg &= ~ NANDFC_SCRAMBLE_ENABLE(1);
+ cmd_reg |= NANDFC_SCRAMBLE_ENABLE(enable);
+
+ __raw_writel(cmd_reg, NANDFC_REG_CONFIG_A);
+}
+#endif
+
+/*
+static void hal_set_col_addr(unsigned int col_addr)
+{
+ __raw_writel(col_addr, NANDFC_REG_COL_ADDR);
+}
+*/
+
+static void hal_flush_buf(struct mtd_info *mtd)
+{
+ /*
+ there is no reg NANDFC_REG_BUF_CTRL
+ */
+ //__raw_writel(0x7, NANDFC_REG_BUF_CTRL);
+}
+
+static unsigned long hal_wait_cmd_complete(void)
+{
+ unsigned long int_stat;
+ unsigned long long wait_time = NAND_TIMEOUT;
+ unsigned long long start_time = hal_gettime();
+ int timeout = 0;
+
+ /* wait done */
+ /*
+ * Use NANDFC_INT_STAT_IDLE instead of NANDFC_INT_DONE
+ * this is HW bug, NANDFC_INT_DONE comes too early
+ * however, NANDFC_INT_STAT_IDLE is not an interrupt source
+ * need HW fix anyway
+ */
+ do {
+ int_stat = __raw_readl(NANDFC_REG_INT_STAT);
+ if (hal_gettime() - start_time >= wait_time) {
+ timeout = 1;
+ }
+ } while (!(int_stat & NANDFC_INT_DONE) && !timeout);
+
+ /* to clear */
+ __raw_writel(int_stat & NANDFC_INT_CLR_MASK, NANDFC_REG_INT_STAT);
+
+ if (timeout) {
+ printf("nand error, cmd timeout\n");
+ return -ETIME;
+ }
+
+ if (int_stat & NANDFC_INT_ERR_ALL) {
+ printf("nand error, int_stat = %lx\n", int_stat);
+ return (int_stat & NANDFC_INT_ERR_ALL);
+ }
+ return 0;
+}
+
+static u16 nand_cal_message_num_size(struct rda_nand_info *info,
+ u16 * message_num, u16* msg_ecc_size)
+{
+ u16 num, mod, ecc_size, page_size, oob_size, message_len, ecc_mode;
+
+ page_size = info->vir_page_size;
+ oob_size = info->oob_size;
+ message_len = info->message_len;
+ ecc_mode = info->ecc_mode;
+
+ num = page_size / message_len;
+ mod = page_size % message_len;
+ if (mod)
+ num += 1;
+ else
+ mod = message_len;
+
+ ecc_size = g_nand_ecc_size[ecc_mode];
+ if (ecc_size % 2)
+ ecc_size = ecc_size + 1;
+
+ if (ecc_size * num > (oob_size - info->vir_oob_size)){
+ printf("Error:ecc used bytes has passed the oob size\n");
+ //return 0;
+ }
+
+ *message_num = num;
+ *msg_ecc_size = ecc_size;
+ return mod;
+}
+
+static unsigned char get_nand_eccmode(unsigned short ecc_bits)
+{
+ unsigned char i;
+
+ if(ecc_bits < 8 || ecc_bits > 96 || (ecc_bits % 8) != 0) {
+ printf("wrong ecc_bits, nand controller not support! \n");
+ return 0xff;
+ }
+
+ for(i = 0; i < ARRAY_SIZE(g_nand_ecc_size); i++) {
+ if(ECCSIZE(ecc_bits) == g_nand_ecc_size[i]) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static uint8_t nand_eccmode_autoselect(struct rda_nand_info *info)
+{
+ u16 num, mod, ecc_size, ecc_bits, max_ecc_parity_num;
+
+ ecc_bits = 96;
+ max_ecc_parity_num = info->oob_size - info->vir_oob_size;
+
+ num = info->vir_page_size / info->message_len;
+ mod = info->vir_page_size % info->message_len;
+ if (mod)
+ num += 1;
+
+ while(ecc_bits >= 8) {
+ ecc_size = ECCSIZE(ecc_bits);
+ if (ecc_size % 2)
+ ecc_size = ecc_size + 1;
+
+ if (ecc_size * num > max_ecc_parity_num){
+ ecc_bits -= 8;
+ } else
+ break;
+ }
+
+ return get_nand_eccmode(ecc_bits);
+}
+
+static int init_nand_info(struct rda_nand_info *info)
+{
+ u16 message_num = 0, message_mod;
+ u16 msg_ecc_size = 0, page_ecc_size;
+ u16 bch_kk = 0, bch_nn = 0, bch_oob_kk = 0, bch_oob_nn = 0;
+
+ info->hard_ecc_hec = 0;
+ info->nand_use_type = 1;
+ info->oob_size = NAND_SPARE_SIZE;
+ info->vir_erase_size = NAND_BLOCK_SIZE;
+
+ if (info->parameter_mode_select == NAND_PARAMETER_BY_GPIO) {
+ switch (info->type) {
+ case NAND_TYPE_2K:
+ info->vir_page_size = 2048;
+ info->vir_page_shift = 11;
+ break;
+ case NAND_TYPE_4K:
+ info->vir_page_size = 4096;
+ info->vir_page_shift = 12;
+ break;
+ case NAND_TYPE_8K:
+ info->vir_page_size = 8192;
+ info->vir_page_shift = 13;
+ break;
+ case NAND_TYPE_16K:
+ info->vir_page_size = 16384;
+ info->vir_page_shift = 14;
+ break;
+ default:
+ printf("invalid nand type %d\n", info->type);
+ return -EINVAL;
+ }
+
+ info->max_eccmode_support = nand_eccmode_autoselect(info);
+ } else {
+ info->vir_page_size = nand_parameter.page_size;
+ if (is_power_of_2(info->vir_page_size))
+ info->vir_page_shift = ffs(info->vir_page_size) - 1;
+ else
+ info->vir_page_shift = 0;
+
+ if (nand_parameter.spare_space_size) {
+ info->ecc_mode = nand_eccmode_autoselect(info);
+ } else {
+ info->ecc_mode = get_nand_eccmode(nand_parameter.ecc_bits);
+ }
+ }
+ info->ecc_mode_bak = info->ecc_mode;
+ info->type_bak = info->type;
+ info->cmd_flag = 0;
+ info->dump_debug_flag = 0;
+ message_mod = nand_cal_message_num_size(info, &message_num, &msg_ecc_size);
+ page_ecc_size = msg_ecc_size * message_num;
+
+ info->page_total_num = info->vir_page_size + info->vir_oob_size + page_ecc_size;
+ info->flash_oob_off = info->vir_page_size + page_ecc_size - msg_ecc_size;
+
+ bch_kk = info->message_len;
+ bch_nn = bch_kk + g_nand_ecc_size[info->ecc_mode];
+ info->bch_data= NANDFC_BCH_KK_DATA(bch_kk) | NANDFC_BCH_NN_DATA(bch_nn);
+
+ bch_oob_kk = message_mod + info->vir_oob_size;
+ bch_oob_nn = bch_oob_kk + g_nand_ecc_size[info->ecc_mode];
+ info->bch_oob = NANDFC_BCH_KK_OOB(bch_oob_kk) | NANDFC_BCH_NN_OOB(bch_oob_nn) ;
+
+ return 0;
+}
+
+static u32 cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2)
+{
+ u32 newfreq;
+
+ if (reg >= ARRAY_SIZE(clk_div_map)) {
+ printf("nand:Invalid div reg: %u\n", reg);
+ reg = ARRAY_SIZE(clk_div_map) - 1;
+ }
+ /* Assuming basefreq is smaller than 2^31 (2.147G Hz) */
+ newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1);
+ return newfreq;
+}
+
+unsigned long get_master_clk_rate(u32 reg)
+{
+ u32 div2;
+ unsigned long rate;
+
+ div2 = reg & SYS_CTRL_AP_AP_APB2_SRC_SEL;
+ reg = GET_BITFIELD(reg, SYS_CTRL_AP_AP_APB2_FREQ);
+ rate = cal_freq_by_divreg(PLL_BUS_FREQ, reg, div2);
+
+ return rate;
+}
+
+#ifndef CONFIG_RDA_FPGA
+static unsigned long rda_nand_clk_div_tbl[] = {
+ 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 12, 14, 16, 18, 20, 22, 24,26, 28, 30, 32, 40
+};
+#endif
+
+static unsigned long hal_calc_divider(struct rda_nand_info *info)
+{
+#ifdef CONFIG_RDA_FPGA
+ return 7;
+#else
+ unsigned long mclk = info->master_clk;
+ unsigned long div, clk_div;
+ int i;
+ int size = ARRAY_SIZE(rda_nand_clk_div_tbl);
+
+ div = mclk / info->clk;
+ if (mclk % info->clk)
+ div += 1;
+
+ for (i = 0; i < size; i++) {
+ if(div <= rda_nand_clk_div_tbl[i]) {
+ clk_div = i;
+ break;
+ }
+ }
+
+ if (i >= size) {
+ clk_div = size - 1;
+ }
+
+ printf("NAND: max clk: %ld, bus clk: %ld\n", info->clk, mclk);
+ printf("NAND: div: %ld, clk_div: %ld\n", div, clk_div);
+ return clk_div;
+#endif
+}
+
+static int hal_init(struct rda_nand_info *info)
+{
+ unsigned long config_a, config_b,page_para,message_oob;
+ unsigned long clk_div = hal_calc_divider(info);
+ uint16_t msg_package_num, msg_package_mod;
+
+ __raw_writel((1 << 23), NANDFC_REG_CONFIG_A);
+ __raw_writel(0, NANDFC_REG_CONFIG_A);
+
+ /* setup config_a and config_b */
+ config_a = NANDFC_CYCLE(clk_div);
+ config_a |= NANDFC_CHIP_SEL(0x0e);
+ config_a |= NANDFC_POLARITY_IO(0); // set 0 invert IO
+ config_a |= NANDFC_POLARITY_MEM(1); // set 1 invert MEM
+ config_a |= NANDFC_BRICK_COMMAND(1);
+
+ /* enable the 16bit mode; */
+ if (info->bus_width_16)
+ config_a |= NANDFC_WDITH_16BIT(1);
+
+ config_a |= (1 << 20);
+
+ config_b = NANDFC_HWECC(1) | NANDFC_ECC_MODE(info->ecc_mode);
+
+ msg_package_num = info->vir_page_size/info->message_len;
+ msg_package_mod = info->vir_page_size%info->message_len;
+ if(msg_package_mod)
+ msg_package_num += 1;
+ else
+ msg_package_mod = info->vir_page_size;
+ /*16k page nand need program twice one page*/
+ /*package num need change when program if not time of 2*/
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K)
+ msg_package_num >>= 1;
+
+ message_oob = NANDFC_OOB_SIZE(info->vir_oob_size) |
+ NANDFC_MESSAGE_SIZE(msg_package_num * info->message_len);
+ /*page size is not used, but package num is used*/
+ page_para = NANDFC_PAGE_SIZE(info->page_total_num) |
+ NANDFC_PACKAGE_NUM(msg_package_num);
+
+ __raw_writel(config_a, NANDFC_REG_CONFIG_A);
+ __raw_writel(config_b, NANDFC_REG_CONFIG_B);
+ __raw_writel(page_para, NANDFC_REG_PAGE_PARA);
+ if (msg_package_num == 1)
+ info->bch_data = info->bch_oob;
+ __raw_writel(info->bch_data, NANDFC_REG_BCH_DATA);
+ __raw_writel(info->bch_oob, NANDFC_REG_BCH_OOB);
+ __raw_writel(message_oob, NANDFC_REG_MESSAGE_OOB_SIZE);
+ __raw_writel(0x80008000, NANDFC_REG_IRBN_COUNT);
+ __raw_writel(0x20202020, NANDFC_REG_BRICK_FSM_TIME0);
+ __raw_writel(0x00006020, NANDFC_REG_BRICK_FSM_TIME1);
+ __raw_writel(0x80000000, NANDFC_REG_BRICK_FSM_TIME2);
+ printf("bch_data=0x%x, bch_oob=0x%x, page_total=0x%x, oob=0x%x \n",
+ info->bch_data, info->bch_oob, info->page_total_num, info->vir_oob_size);
+ printf("page_para=0x%08lx, message_oob=0x%08lx\n", page_para, message_oob);
+ printf("NAND: nand init done, config=0x%08lx configb=0x%08lx\n", config_a, config_b);
+
+ return 0;
+}
+
+static int nand_eccmode_setting(struct rda_nand_info *info,
+ NAND_BCHMODE_TYPE eccmode)
+{
+ u16 message_num = 0, message_mod;
+ u16 msg_ecc_size = 0, page_ecc_size;
+ u16 bch_kk = 0, bch_nn = 0, bch_oob_kk = 0, bch_oob_nn = 0;
+ unsigned long config_b,page_para,message_oob;
+
+ info->ecc_mode = eccmode;
+
+ message_mod = nand_cal_message_num_size(info, &message_num, &msg_ecc_size);
+ page_ecc_size = msg_ecc_size * message_num;
+
+ info->page_total_num = info->vir_page_size + info->vir_oob_size + page_ecc_size;
+ info->flash_oob_off = info->vir_page_size + page_ecc_size - msg_ecc_size;
+
+ bch_kk = info->message_len;
+ bch_nn = bch_kk + g_nand_ecc_size[eccmode];
+ info->bch_data= NANDFC_BCH_KK_DATA(bch_kk) | NANDFC_BCH_NN_DATA(bch_nn);
+
+ bch_oob_kk = message_mod + info->vir_oob_size;
+ bch_oob_nn = bch_oob_kk + g_nand_ecc_size[eccmode];
+ info->bch_oob = NANDFC_BCH_KK_OOB(bch_oob_kk) | NANDFC_BCH_NN_OOB(bch_oob_nn) ;
+
+ config_b = NANDFC_HWECC(1) | NANDFC_ECC_MODE(info->ecc_mode);
+
+ /*16k page nand need program twice one page*/
+ /*package num need change when program if not time of 2*/
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K)
+ message_num >>= 1;
+
+ message_oob = NANDFC_OOB_SIZE(info->vir_oob_size) |
+ NANDFC_MESSAGE_SIZE(info->message_len * message_num);
+ /*page size is not used, but package num is used*/
+ page_para = NANDFC_PAGE_SIZE(info->page_total_num) |
+ NANDFC_PACKAGE_NUM(message_num);
+
+ __raw_writel(config_b, NANDFC_REG_CONFIG_B);
+ __raw_writel(page_para, NANDFC_REG_PAGE_PARA);
+ if (message_num == 1)
+ info->bch_data = info->bch_oob;
+ __raw_writel(info->bch_data, NANDFC_REG_BCH_DATA);
+ __raw_writel(info->bch_oob, NANDFC_REG_BCH_OOB);
+ __raw_writel(message_oob, NANDFC_REG_MESSAGE_OOB_SIZE);
+
+#ifdef NAND_DEBUG
+ printf("nand_eccmode_setting:bch_data=0x%x, bch_oob=0x%x, page_total=0x%x, oob=0x%x\n",
+ info->bch_data, info->bch_oob, info->page_total_num, info->vir_oob_size);
+ printf("nand_eccmode_setting:page_para=0x%08lx, message_oob=0x%08lx\n",
+ page_para, message_oob);
+#endif
+ printf("nand ecc setting done, configA=0x%x, configb=0x%08lx, type=%d\n",
+ __raw_readl(NANDFC_REG_CONFIG_A), config_b, info->type);
+ return 0;
+}
+
+static void nand_eccmode_convert(unsigned int page_addr, struct mtd_info *mtd)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 ecc_mode;
+
+ if(page_addr < info->boot_logic_end_pageaddr) {
+ ecc_mode = info->ecc_mode_bak;
+ } else {
+ ecc_mode = info->max_eccmode_support;
+ }
+
+ if (ecc_mode != info->ecc_mode)
+ nand_eccmode_setting(info, ecc_mode);
+}
+
+static u8 nand_rda_read_byte(struct mtd_info *mtd)
+{
+ u8 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *((u8 *) info->byte_buf + info->index);
+ info->index++;
+#ifdef NAND_DEBUG
+ printf("nand_read_byte, ret = %02x\n", ret);
+#endif
+ return ret;
+}
+
+static u16 nand_rda_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *(u16 *) ((uint8_t *) info->byte_buf + info->index);
+ info->index += 2;
+#ifdef NAND_DEBUG
+ printf("nand_read_word, ret = %04x\n", ret);
+#endif
+ return ret;
+}
+
+static void nand_rda_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_R + info->read_ptr);
+ u8 * u_nand_temp_buffer = &g_nand_flash_temp_buffer[info->read_ptr];
+
+#ifdef NAND_DEBUG
+ printf("read : buf addr = 0x%x, len = %d, read_ptr = %d, u_nand_temp_buffer = 0x%x \n",
+ (u32) buf, (u32)len,
+ (u32) info->read_ptr, (u32)u_nand_temp_buffer);
+#endif /* NAND_DEBUG */
+
+#ifndef CONFIG_NAND_RDA_DMA
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K) {
+ memcpy((void *)buf, (void *)u_nand_temp_buffer, len);
+ } else {
+ if (info->page_addr == 0 && info->read_ptr == 0 &&
+ info->parameter_mode_select == NAND_PARAMETER_BY_NAND) {
+ /* specially do for nand when read 1KB of first page. */
+ if (info->dump_debug_flag == 0)
+ len = info->vir_page_size;/*no ecc bytes*/
+ else
+ len = 2048;/*ecc bytes needed*/
+ memset(buf, 0xff, info->page_size);
+ }
+ memcpy((void *)buf, (void *)nand_ptr, len);
+ }
+
+ info->read_ptr += len;
+
+#ifdef NAND_DEBUG_VERBOSE
+ rda_dump_buf((char *)buf, len);
+#endif
+
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy(buf, (void *)nand_ptr, len);
+ info->read_ptr = 0;
+ return;
+ }
+
+ if (((u32)addr & 0x7) != 0) {
+ printf("ERROR, nand dma read buffer %p is not 8bytes aligned\n",
+ addr);
+ return;
+ }
+
+ phys_addr = dma_map_single(addr, len, DMA_FROM_DEVICE);
+
+ dma_param.src_addr = (u32) info->nand_data_phys;
+ dma_param.dst_addr = phys_addr;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FR_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_FROM_DEVICE);
+ return;
+ }
+
+ /* use flush to avoid annoying unaligned warning */
+ /* however, invalidate after the dma it the right thing to do */
+ flush_dcache_range((u32)addr, (u32)(addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* use flush to avoid annoying unaligned warning */
+ //invalidate_dcache_range((u32)addr, (u32)(addr + len));
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_FROM_DEVICE);
+ info->read_ptr += len;
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void nand_rda_write_buf(struct mtd_info *mtd, const uint8_t * buf,
+ int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_W + info->write_ptr);
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[info->write_ptr];
+
+#ifdef NAND_DEBUG
+ printf("write : buf addr = 0x%p, len = %d, write_ptr = %d\n", buf, len,
+ info->write_ptr);
+#endif /* NAND_DEBUG */
+
+#ifdef NAND_DEBUG_VERBOSE
+ rda_dump_buf((char *)buf, len);
+#endif
+
+#ifndef CONFIG_NAND_RDA_DMA
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K)
+ memcpy((void *)u_nand_temp_buffer, (void *)buf, len);
+ else
+ memcpy((void *)nand_ptr, (void *)buf, len);
+
+ info->write_ptr += len;
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy((void *)nand_ptr, (void *)buf, len);
+ info->write_ptr += len;
+ return;
+ }
+
+ if (((u32)addr & 0x7) != 0) {
+ printf("ERROR, nand dma write %p buffer is not 8bytes aligned\n",
+ addr);
+ return;
+ }
+ phys_addr = dma_map_single(addr, len, DMA_TO_DEVICE);
+
+ dma_param.src_addr = phys_addr;
+ dma_param.dst_addr = (u32) info->nand_data_phys;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FW_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_TO_DEVICE);
+ return;
+ }
+
+ flush_dcache_range((u32)addr, (u32)(addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_TO_DEVICE);
+ info->write_ptr += len;
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void nand_rda_do_cmd_pre(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+ unsigned int page_addr = info->page_addr;
+
+ __raw_writel(0xfff, NANDFC_REG_INT_STAT);
+ switch (info->cmd) {
+ case NAND_CMD_SEQIN:
+ hal_flush_buf(mtd);
+ info->write_ptr = 0;
+ break;
+ case NAND_CMD_READ0:
+ hal_flush_buf(mtd);
+ //info->read_ptr = 0;
+ if(info->parameter_mode_select == NAND_PARAMETER_BY_NAND &&
+ info->dump_debug_flag == 0) {
+ if (page_addr < info->boot_logic_end_pageaddr) {
+ page_addr += info->spl_logic_pageaddr;
+ info->page_addr = page_addr;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void nand_rda_do_cmd_post(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+ u32 temp;
+
+ switch (info->cmd) {
+ case NAND_CMD_RESET:
+ return;
+ case NAND_CMD_READID:
+ temp = __raw_readl(NANDFC_REG_IDCODE_A);
+ info->byte_buf[0] = temp;
+ temp = __raw_readl(NANDFC_REG_IDCODE_B);
+ info->byte_buf[1] = temp;
+ info->index = 0;
+ break;
+ case NAND_CMD_STATUS:
+ temp = __raw_readl(NANDFC_REG_OP_STATUS);
+ info->byte_buf[0] = (temp & 0xFF);
+ info->index = 0;
+ if (info->byte_buf[0] != 0xe0) {
+ printf("nand error in op status %x\n",
+ info->byte_buf[0]);
+ }
+#ifdef NAND_DEBUG
+ printf("post_cmd read status 0x3c = %x\n", info->byte_buf[0]);
+#endif
+ break;
+ case NAND_CMD_READ0: // NAND_CMD_READOOB goes here too
+ break;
+#if 0
+/* read back after write, for err checking */
+ case NAND_CMD_SEQIN:
+ {
+ int cmd_ret;
+ /* read back instantly and check fcs/crc result */
+ hal_send_cmd((unsigned char)NAND_CMD_READ0,
+ info->page_addr);
+ cmd_ret = hal_wait_cmd_complete();
+ if (cmd_ret) {
+ printf
+ ("readback cmd fail, page_addr = %x, ret = %x\n",
+ info->page_addr, cmd_ret);
+ }
+ }
+ break;
+#endif
+#if 0
+/* delay 10ms, for erase to complete */
+ case NAND_CMD_ERASE1:
+ udelay(10000);
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static void nand_rda_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+#ifdef NAND_DEBUG
+ printf("nand_rda_hwcontrol, cmd = %x, ctrl = %x, not use anymore\n",
+ cmd, ctrl);
+#endif
+}
+
+static int nand_rda_dev_ready(struct mtd_info *mtd)
+{
+ return 1;
+}
+
+int nand_cmd_reset_flash(void)
+{
+ int ret = 0;
+
+ hal_send_cmd_brick(NAND_CMD_RESET,0,0,5);
+ hal_send_cmd_brick(0,0,0,0);
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret)
+ printf("nand_reset_flash failed\n");
+
+ return ret;
+}
+
+int nand_cmd_read_id(void)
+{
+ int ret = 0;
+
+ hal_send_cmd_brick(NAND_CMD_READID,0,0,2);
+ hal_send_cmd_brick(0,0,0,6);
+ hal_send_cmd_brick(0,0,0,0xd);
+ hal_send_cmd_brick(0,0,7,0);
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret)
+ printf("nand_cmd_read_id failed\n");
+
+ return ret;
+}
+
+void nand_cmd_write_page(struct rda_nand_info *info, unsigned int page_addr,
+ u16 page_size, u16 col_addr)
+{
+ u8 addr_temp = 0;
+
+ hal_send_cmd_brick(NAND_CMD_SEQIN,0,0,2);
+
+ addr_temp = col_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (col_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+
+ if (NAND_BLOCK_SIZE == 0x300000) {
+ /*page 0x180 ~ 0x1ff are invalid, so need skip to next block.*/
+ page_addr += 0x80 * (page_addr / 0x180);
+ /*block 0xab0 ~ 0xfff are invalid, so need skip to next LUN.*/
+ page_addr += (0x550 << 9) * ((page_addr >> 9) / 0xab0);
+ }
+
+ addr_temp = page_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 16) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,7);
+
+ hal_send_cmd_brick(0,0,page_size,3);
+
+ hal_send_cmd_brick(0,info->bus_width_16,page_size, 1);
+
+ hal_send_cmd_brick(NAND_CMD_PAGEPROG,0,0,5);
+
+ hal_send_cmd_brick(0,0,0,0);
+
+ return;
+}
+
+void nand_cmd_read_page(struct rda_nand_info *info, unsigned int page_addr,
+ unsigned short num, unsigned short col_addr)
+{
+ u8 addr_temp = 0;
+
+ hal_send_cmd_brick(NAND_CMD_READ0,0,0,2);
+
+ addr_temp = col_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (col_addr >> 8)& 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+
+ if (NAND_BLOCK_SIZE == 0x300000) {
+ /*page 0x180 ~ 0x1ff are invalid, so need skip to next block.*/
+ page_addr += 0x80 * (page_addr / 0x180);
+ /*block 0xab0 ~ 0xfff are invalid, so need skip to next LUN.*/
+ page_addr += (0x550 << 9) * ((page_addr >> 9) / 0xab0);
+ }
+
+ addr_temp = page_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 16) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,1);
+
+ hal_send_cmd_brick(NAND_CMD_READSTART,0,0,5);
+ hal_send_cmd_brick(0,0,0,0xa);
+ hal_send_cmd_brick(0,0,0,0x4);
+
+ hal_send_cmd_brick(0,info->bus_width_16,num, 0);
+
+ return;
+}
+
+void nand_cmd_read_random(struct rda_nand_info *info, unsigned short num,
+ unsigned short col_addr)
+{
+ u8 addr_temp = 0;
+
+ hal_send_cmd_brick(NAND_CMD_RNDOUT,0,0,2);
+
+ addr_temp = col_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (col_addr >> 8)& 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,1);
+ hal_send_cmd_brick(NAND_CMD_RNDOUTSTART,0,0,9);
+ hal_send_cmd_brick(0,0,0,0x4);
+
+ hal_send_cmd_brick(0,info->bus_width_16,num, 0);
+
+ return;
+}
+void nand_cmd_block_erase(unsigned int page_addr)
+{
+ u8 addr_temp = 0;
+ int ret = 0;
+
+ hal_send_cmd_brick(NAND_CMD_ERASE1,0,0,2);
+
+ if (NAND_BLOCK_SIZE == 0x300000) {
+ /*page 0x180 ~ 0x1ff are invalid, so need skip to next block.*/
+ page_addr += 0x80 * (page_addr / 0x180);
+ /*block 0xab0 ~ 0xfff are invalid, so need skip to next LUN.*/
+ page_addr += (0x550 << 9) * ((page_addr >> 9) / 0xab0);
+ }
+
+ addr_temp = page_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 16) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,1);
+
+ hal_send_cmd_brick(NAND_CMD_ERASE2,0,0,5);
+ hal_send_cmd_brick(0,0,0,0);
+
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf("nand_cmd_block_erase fail\n");
+ return;
+ }
+}
+
+void nand_cmd_read_status(void)
+{
+ int ret = 0;
+
+ hal_send_cmd_brick(NAND_CMD_STATUS,0,0,6);
+ hal_send_cmd_brick(0,0,0,0xd);
+ hal_send_cmd_brick(0,0,1,0);
+
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf("nand_cmd_read_status failed\n");
+ return;
+ }
+}
+
+void nandfc_page_write_first(struct rda_nand_info *info, unsigned int page_addr,
+ u16 page_size, u16 col_addr)
+{
+ u8 addr_temp = 0;
+
+ hal_send_cmd_brick(NAND_CMD_SEQIN,0,0,2);
+
+ addr_temp = col_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (col_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+
+ if (NAND_BLOCK_SIZE == 0x300000) {
+ /*page 0x180 ~ 0x1ff are invalid, so need skip to next block.*/
+ page_addr += 0x80 * (page_addr / 0x180);
+ /*block 0xab0 ~ 0xfff are invalid, so need skip to next LUN.*/
+ page_addr += (0x550 << 9) * ((page_addr >> 9) / 0xab0);
+ }
+
+ addr_temp = page_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (page_addr >> 16) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,7);
+
+ hal_send_cmd_brick(0,0,0,3);
+
+ hal_send_cmd_brick(0,info->bus_width_16,page_size, 0);
+
+ return;
+}
+
+void nandfc_page_write_random(struct rda_nand_info *info, u16 col_addr,
+ int data_num, char cmd_2nd)
+{
+ u8 addr_temp = 0;
+
+ hal_send_cmd_brick(NAND_CMD_RNDIN,0,0,2);
+
+ addr_temp = col_addr & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,2);
+ addr_temp = (col_addr >> 8) & 0xff;
+ hal_send_cmd_brick(addr_temp,0,0,9);
+
+ hal_send_cmd_brick(0,0,data_num, 3);
+ hal_send_cmd_brick(0,info->bus_width_16,data_num, 1);
+
+ hal_send_cmd_brick(cmd_2nd,0,0,5);
+
+ hal_send_cmd_brick(0,0,0,0);
+}
+
+int nand_page_write_all(unsigned int page_addr, struct mtd_info *mtd)
+{
+ int ret = 0;
+ u16 page_size = 0;
+ u16 col_addr = 0;
+ u32 message_oob = 0, page_para;
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) chip->IO_ADDR_W;
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[0];
+ u16 message_num = 0, message_mod = 0;
+ u16 first_wr_ecc_len = 0, first_wr_data_len = 0;
+
+ if(info->parameter_mode_select == NAND_PARAMETER_BY_GPIO)
+ nand_eccmode_convert(page_addr, mtd);
+
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K) {
+ message_num = info->vir_page_size / info->message_len;
+ message_mod = info->vir_page_size % info->message_len;
+ if(message_mod)
+ message_num += 1;
+ else
+ message_mod = info->message_len;
+
+ first_wr_data_len = (message_num >> 1) * info->message_len;
+
+ message_oob = NANDFC_OOB_SIZE(0) |
+ NANDFC_MESSAGE_SIZE(first_wr_data_len);
+
+ __raw_writel(info->bch_data, NANDFC_REG_BCH_OOB);
+ __raw_writel(message_oob, NANDFC_REG_MESSAGE_OOB_SIZE);
+
+ first_wr_ecc_len = (info->page_total_num - info->vir_page_size - \
+ info->vir_oob_size) / message_num * (message_num >> 1);
+ page_size = first_wr_data_len + first_wr_ecc_len;
+ col_addr = page_size;
+
+ if (info->bus_width_16)
+ page_size = page_size - 2;
+ else
+ page_size = page_size - 1;
+
+ page_para = NANDFC_PAGE_SIZE(page_size) |
+ NANDFC_PACKAGE_NUM(message_num >> 1);
+ __raw_writel(page_para, NANDFC_REG_PAGE_PARA);
+
+ hal_flush_buf(mtd);
+ memcpy((void *)nand_ptr, (void *)u_nand_temp_buffer, first_wr_data_len);
+ }else{
+ if (info->bus_width_16)
+ page_size = info->page_total_num - 2;
+ else
+ page_size = info->page_total_num - 1;
+ }
+
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K)
+ nandfc_page_write_first(info, page_addr, page_size, 0);
+ else
+ nand_cmd_write_page(info, page_addr, page_size, 0);
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K)
+ printf ("nandfc_page_write 16k first half page failed: page_addr = 0x%x\n",
+ page_addr);
+ else
+ printf ("nandfc_page_write failed: page_addr = 0x%x\n", page_addr);
+ return ret;
+ }
+
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K) {
+ message_oob = NANDFC_OOB_SIZE(info->vir_oob_size) |
+ NANDFC_MESSAGE_SIZE(info->vir_page_size - first_wr_data_len);
+
+ __raw_writel(info->bch_oob, NANDFC_REG_BCH_OOB);
+ __raw_writel(message_oob, NANDFC_REG_MESSAGE_OOB_SIZE);
+ page_size = info->page_total_num - col_addr;
+
+ if (info->bus_width_16){
+ page_size = page_size - 2;
+ col_addr = col_addr >> 1;
+ } else
+ page_size = page_size - 1;
+
+ page_para = NANDFC_PAGE_SIZE(page_size) |
+ NANDFC_PACKAGE_NUM(message_num - (message_num >> 1));
+ __raw_writel(page_para, NANDFC_REG_PAGE_PARA);
+
+ hal_flush_buf(mtd);
+ memcpy((void *)nand_ptr, (void *)(u_nand_temp_buffer + first_wr_data_len),
+ info->vir_page_size + info->vir_oob_size - first_wr_data_len);
+
+ //nand_cmd_write_page(page_addr, page_size, col_addr);
+ nandfc_page_write_random(info, col_addr, page_size, NAND_CMD_PAGEPROG);
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ printf ("nandfc_page_write 16k second half page failed: page_addr = 0x%x\n",
+ page_addr);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+int nand_page_read_all(unsigned int page_addr, struct mtd_info *mtd)
+{
+ int ret = 0;
+ unsigned short num = 0,col_addr = 0;
+ u32 message_oob = 0, page_para;
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) chip->IO_ADDR_R;
+ u8 *u_nand_temp_buffer = &g_nand_flash_temp_buffer[0];
+ u16 message_num = 0, message_mod = 0;
+ u16 first_rd_ecc_len = 0, first_rd_data_len = 0;
+
+ if (info->parameter_mode_select == NAND_PARAMETER_BY_GPIO)
+ nand_eccmode_convert(page_addr, mtd);
+
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K){
+ message_num = info->vir_page_size / info->message_len;
+ message_mod = info->vir_page_size % info->message_len;
+ if(message_mod)
+ message_num += 1;
+ else
+ message_mod = info->message_len;
+
+ first_rd_data_len = info->message_len * (message_num >> 1);
+ message_oob = NANDFC_OOB_SIZE(0) | NANDFC_MESSAGE_SIZE(first_rd_data_len);
+
+ __raw_writel(info->bch_data, NANDFC_REG_BCH_OOB);
+ __raw_writel(message_oob, NANDFC_REG_MESSAGE_OOB_SIZE);
+
+ first_rd_ecc_len = (info->page_total_num - info->vir_page_size - \
+ info->vir_oob_size) / message_num * (message_num >> 1);
+ num = first_rd_data_len+ first_rd_ecc_len;
+
+ page_para = NANDFC_PAGE_SIZE(num) |
+ NANDFC_PACKAGE_NUM(message_num >> 1);
+ __raw_writel(page_para, NANDFC_REG_PAGE_PARA);
+ }else
+ num = info->page_total_num;
+
+ nand_cmd_read_page(info, page_addr, num, col_addr);
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ if (ret) {
+ if (info->type != NAND_TYPE_16K && info->type != NAND_TYPE_14K) {
+ printf ("nandfc_page_read failed : page_addr = 0x%x\n", page_addr);
+ return ret;
+ } else
+ printf ("nandfc_page_read 16k first half page failed: page_addr = 0x%x\n",
+ page_addr);
+ }
+
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K)
+ memcpy((void *)u_nand_temp_buffer, (void *)nand_ptr, first_rd_data_len);
+
+ if (info->type == NAND_TYPE_16K || info->type == NAND_TYPE_14K) {
+ message_oob = NANDFC_OOB_SIZE(info->vir_oob_size) |
+ NANDFC_MESSAGE_SIZE(info->vir_page_size - first_rd_data_len);
+
+ __raw_writel(info->bch_oob, NANDFC_REG_BCH_OOB);
+ __raw_writel(message_oob, NANDFC_REG_MESSAGE_OOB_SIZE);
+
+ col_addr = num;
+ num = info->page_total_num - col_addr;
+
+ page_para = NANDFC_PAGE_SIZE(num) |
+ NANDFC_PACKAGE_NUM(message_num - (message_num >> 1));
+ __raw_writel(page_para, NANDFC_REG_PAGE_PARA);
+
+ nand_cmd_read_random(info, num, col_addr);
+ hal_start_cmd_data();
+ ret = hal_wait_cmd_complete();
+ memcpy((void *)(u_nand_temp_buffer + first_rd_data_len), (void *)nand_ptr,
+ (info->vir_page_size + info->vir_oob_size - first_rd_data_len));
+ if (ret) {
+ printf ("nandfc_page_read 16k second half page failed: page_addr = 0x%x\n",
+ page_addr);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static void nand_parameter_info_wr(unsigned int page_addr, struct mtd_info *mtd)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (chip->IO_ADDR_W);
+ u8 ecc_mode;
+
+ if(page_addr < info->boot_logic_end_pageaddr && info->cmd_flag == 0) {
+ ecc_mode = NAND_ECC_96BIT_WITHOUT_CRC;
+ info->vir_page_size = 1024;
+ info->vir_page_shift = 10;
+ info->vir_oob_size = 32;
+ info->message_len = 1024;
+ info->type = NAND_TYPE_2K;
+ info->cmd_flag = 1;
+ nand_eccmode_setting(info, ecc_mode);
+
+ memset(nand_ptr, 0xff, 2048);
+ memcpy(nand_ptr, (unsigned char*)&nand_parameter,
+ sizeof(struct flash_nand_parameter));
+ nand_page_write_all(0, mtd);
+
+ ecc_mode = info->ecc_mode_bak;
+ info->vir_page_size = nand_parameter.page_size;
+ if (is_power_of_2(info->vir_page_size))
+ info->vir_page_shift = ffs(info->vir_page_size) - 1;
+ else
+ info->vir_page_shift = 0;
+ info->vir_oob_size = nand_parameter.oob_size;
+ info->message_len = nand_parameter.ecc_msg_len;
+ info->type = info->type_bak;
+ nand_eccmode_setting(info, ecc_mode);
+ }
+}
+
+static void nand_parameter_info_rd(unsigned int page_addr, struct mtd_info *mtd)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 ecc_mode;
+
+ if(page_addr < info->spl_logic_pageaddr) {
+ ecc_mode = NAND_ECC_96BIT_WITHOUT_CRC;
+ if (ecc_mode != info->ecc_mode) {
+ info->vir_page_size = 1024;
+ info->vir_page_shift = 10;
+ info->vir_oob_size = 32;
+ info->message_len = 1024;
+ info->type = NAND_TYPE_2K;
+ }
+ } else {
+ ecc_mode = info->ecc_mode_bak;
+ if (ecc_mode != info->ecc_mode) {
+ info->vir_page_size = nand_parameter.page_size;
+ if (is_power_of_2(info->vir_page_size))
+ info->vir_page_shift = ffs(info->vir_page_size) - 1;
+ else
+ info->vir_page_shift = 0;
+ info->vir_oob_size = nand_parameter.oob_size;
+ info->message_len = nand_parameter.ecc_msg_len;
+ info->type = info->type_bak;
+ }
+ }
+
+ if (ecc_mode != info->ecc_mode) {
+ nand_eccmode_setting(info, ecc_mode);
+ }
+}
+
+static void nand_send_cmd_all(struct mtd_info *mtd)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ unsigned int page_addr = info->page_addr;
+
+ switch(info->cmd){
+ case NAND_CMD_READ0:
+ if(info->parameter_mode_select == NAND_PARAMETER_BY_NAND)
+ nand_parameter_info_rd(page_addr, mtd);
+ nand_page_read_all(page_addr, mtd);
+ break;
+ case NAND_CMD_RESET:
+ nand_cmd_reset_flash();
+ break;
+ case NAND_CMD_READID:
+ nand_cmd_read_id();
+ break;
+ case NAND_CMD_ERASE1:
+ nand_cmd_block_erase(page_addr);
+ if (info->parameter_mode_select == NAND_PARAMETER_BY_NAND) {
+ if (page_addr < info->boot_logic_end_pageaddr) {
+ info->cmd_flag = 0;
+ //nand_cmd_block_erase(0);
+ }
+ }
+ break;
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_SEQIN:
+ nand_page_write_all(page_addr, mtd);
+ if(info->parameter_mode_select == NAND_PARAMETER_BY_NAND)
+ nand_parameter_info_wr(page_addr, mtd);
+ break;
+ case NAND_CMD_STATUS:
+ nand_cmd_read_status();
+ break;
+ default:
+ return;
+ }
+
+ //hal_start_cmd_data();
+}
+
+static void nand_rda_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+
+#ifdef NAND_DEBUG
+ printf("nand_rda_cmdfunc, cmd = %x, page_addr = %x, col_addr = %x\n",
+ command, page_addr, column);
+#endif
+
+ /* remap command */
+ switch (command) {
+ case NAND_CMD_SEQIN: /* 0x80 do nothing, wait for 0x10 */
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = NAND_CMD_NONE;
+ info->write_ptr= column;
+ if(info->parameter_mode_select == NAND_PARAMETER_BY_NAND) {
+ if (page_addr < info->boot_logic_end_pageaddr) {
+ page_addr += info->spl_logic_pageaddr;
+ info->page_addr = page_addr;
+ }
+ nand_parameter_info_rd(page_addr, mtd);
+ }
+ break;
+ case NAND_CMD_READSTART: /* hw auto gen 0x30 for read */
+ case NAND_CMD_ERASE2: /* hw auto gen 0xd0 for erase */
+#ifdef NAND_DEBUG
+ printf("erase block, erase_size = %x\n", mtd->erasesize);
+#endif
+ info->cmd = NAND_CMD_NONE;
+ break;
+ case NAND_CMD_PAGEPROG: /* 0x10 do the real program */
+ info->cmd = NAND_CMD_SEQIN;
+ info->col_addr = 0;
+ break;
+ case NAND_CMD_READOOB: /* Emulate NAND_CMD_READOOB */
+ info->page_addr = page_addr;
+ info->cmd = NAND_CMD_READ0;
+ /* Offset of oob */
+ info->col_addr = column;
+ info->read_ptr = mtd->writesize;
+ break;
+ case NAND_CMD_READ0: /* Emulate NAND_CMD_READOOB */
+ info->read_ptr = 0;
+ /*fall though, don't need break;*/
+ default:
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = command;
+ break;
+ }
+ if (info->cmd == NAND_CMD_NONE) {
+ return;
+ }
+
+#ifdef NAND_DEBUG
+ printf("after cmd remap, cmd = %x, page_addr = %x, col_addr = %x\n",
+ info->cmd, info->page_addr, info->col_addr);
+#endif
+/*
+ if (info->col_addr != -1) {
+ hal_set_col_addr(info->col_addr);
+ }
+*/
+ if (info->page_addr == -1)
+ info->page_addr = 0;
+
+ nand_rda_do_cmd_pre(mtd);
+
+ nand_send_cmd_all(mtd);
+
+ nand_rda_do_cmd_post(mtd);
+
+ nand_wait_ready(mtd);
+}
+
+static int nand_reset_flash(void)
+{
+ int ret = 0;
+#ifdef NAND_DEBUG
+ printf("Enter function nand_rda_init\n");
+#endif
+ nand_cmd_reset_flash();
+
+ return ret;
+}
+
+static int nand_read_id(unsigned int id[2])
+{
+ int ret = 0;
+
+ nand_cmd_read_id();
+
+ id[0] = __raw_readl(NANDFC_REG_IDCODE_A);
+ id[1] = __raw_readl(NANDFC_REG_IDCODE_B);
+
+ printf("NAND: Nand ID: %08x %08x\n", id[0], id[1]);
+
+#ifdef TGT_AP_DO_NAND_TEST
+ gbl_nand_test_id_val[0] = id[0];
+ gbl_nand_test_id_val[1] = id[1];
+#endif /* TGT_AP_DO_NAND_TEST */
+ return ret;
+}
+
+static void read_ID(struct mtd_info *mtd)
+{
+ unsigned int id[2];
+
+ nand_read_id(id);
+}
+
+static void nand_rda_select_chip(struct mtd_info *mtd, int chip)
+{
+ unsigned long config_a = __raw_readl(NANDFC_REG_CONFIG_A);
+
+ if ((chip == -1) || (chip > 3))
+ return;
+
+ config_a |= NANDFC_CHIP_SEL(0x0f);
+ config_a &= ~ NANDFC_CHIP_SEL(1 << chip);
+
+ //printf("**********config_a = 0x%lx", config_a);
+ __raw_writel(config_a, NANDFC_REG_CONFIG_A);
+}
+
+static int nand_get_type(struct rda_nand_info *info, unsigned int id[2])
+{
+ u16 hwcfg_nand_type;
+ int metal_id;
+
+ info->spl_adjust_ratio = 2;
+ metal_id = rda_metal_id_get();
+
+#if defined(CONFIG_MACH_RDA8850E)
+#define RDA_NAND_TYPE_BIT_BUS (1 << 1) /* 1: 8-bit; 0: 16-bit */
+/*bit7 bit0 00 menas 2k, 01 means 4k, 10 means 8k, 11 means 16k*/
+#define RDA_NAND_TYPE_BIT_7 (1 << 7)
+#define RDA_NAND_TYPE_BIT_0 (1 << 0)
+ printf("NAND: RDA8850E chip, metal %d\n", metal_id);
+ hwcfg_nand_type = rda_hwcfg_get() & 0x83;
+ if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_BUS)
+ info->bus_width_16 = 0;
+ else
+ info->bus_width_16 = 1;
+
+ info->vir_oob_size = NAND_OOBSIZE;
+ info->parameter_mode_select = NAND_PARAMETER_BY_GPIO;
+
+ u16 nand_type = ((hwcfg_nand_type & RDA_NAND_TYPE_BIT_7) >> 6)
+ | (hwcfg_nand_type & RDA_NAND_TYPE_BIT_0);
+ if(nand_type == 0) {
+ info->type = NAND_TYPE_2K;
+ info->parameter_mode_select = NAND_PARAMETER_BY_NAND;
+ }
+ else if(nand_type == 1)
+ info->type = NAND_TYPE_4K;
+ else
+ info->type = nand_type;
+
+ if (info->parameter_mode_select == NAND_PARAMETER_BY_GPIO) {
+ switch(info->type){
+ case NAND_TYPE_2K:
+ info->page_size = 2048;
+ info->page_shift = 11;
+ info->ecc_mode = NAND_ECC_16BIT;
+ info->message_len = 2048;
+ break;
+ case NAND_TYPE_4K:
+ info->page_size = 4096;
+ info->page_shift = 12;
+ info->ecc_mode = NAND_ECC_24BIT;
+ info->message_len = 1024;
+ break;
+ case NAND_TYPE_8K:
+ info->page_size = 8192;
+ info->page_shift = 13;
+ info->ecc_mode = NAND_ECC_24BIT;
+ info->message_len = 1024;
+ break;
+ case NAND_TYPE_16K:
+ info->page_size = 16384;
+ info->page_shift = 14;
+ info->ecc_mode = NAND_ECC_32BIT;
+ info->message_len = 1024;
+ break;
+ default:
+ printf("Error nand type \n");
+ break;
+ }
+ }else {
+ fill_nand_parameter();
+ info->page_size = nand_parameter.page_size;
+ if (is_power_of_2(info->page_size))
+ info->page_shift = ffs(info->page_size) - 1;
+ else
+ info->page_shift = 0;
+ info->message_len = nand_parameter.ecc_msg_len;
+ info->vir_oob_size = nand_parameter.oob_size;
+ switch(info->page_size) {
+ case 2048:
+ info->type = NAND_TYPE_2K;
+ break;
+ case 4096:
+ info->type = NAND_TYPE_4K;
+ break;
+ case 8192:
+ info->type = NAND_TYPE_8K;
+ break;
+ case 16384:
+ info->type = NAND_TYPE_16K;
+ break;
+ case 3072:
+ info->type = NAND_TYPE_3K;
+ break;
+ case 7168:
+ info->type = NAND_TYPE_7K;
+ break;
+ case 14336:
+ info->type = NAND_TYPE_14K;
+ break;
+ default:
+ printf("Error nand type \n");
+ break;
+ }
+ }
+#else
+#if defined(CONFIG_MACH_RDA8810H)
+ printf("NAND: RDA8810H chip, metal %d\n", metal_id);
+ hwcfg_nand_type = rda_hwcfg_get();
+
+ if (RDA_HW_CFG_GET_BM_IDX(hwcfg_nand_type) == RDA_MODE_NAND_8BIT)
+ info->bus_width_16 = 0;
+ else
+ if (RDA_HW_CFG_GET_BM_IDX(hwcfg_nand_type) == RDA_MODE_NAND_16BIT)
+ info->bus_width_16 = 1;
+ else
+ printf("nand: invalid nand type \n");
+
+ info->parameter_mode_select = NAND_PARAMETER_BY_NAND;
+
+ fill_nand_parameter();
+ info->page_size = nand_parameter.page_size;
+ if (is_power_of_2(info->page_size))
+ info->page_shift = ffs(info->page_size) - 1;
+ else
+ info->page_shift = 0;
+ info->message_len = nand_parameter.ecc_msg_len;
+ info->vir_oob_size = nand_parameter.oob_size;
+ switch(info->page_size) {
+ case 2048:
+ info->type = NAND_TYPE_2K;
+ break;
+ case 4096:
+ info->type = NAND_TYPE_4K;
+ break;
+ case 8192:
+ info->type = NAND_TYPE_8K;
+ break;
+ case 16384:
+ info->type = NAND_TYPE_16K;
+ break;
+ case 3072:
+ info->type = NAND_TYPE_3K;
+ break;
+ case 7168:
+ info->type = NAND_TYPE_7K;
+ break;
+ case 14336:
+ info->type = NAND_TYPE_14K;
+ break;
+ default:
+ printf("Error nand type \n");
+ break;
+ }
+#else
+#error "WRONG MACH"
+#endif
+#endif
+
+ printf("NAND: actually ratio * 2, SPL adjust ratio is %d. \n",
+ info->spl_adjust_ratio);
+ return 0;
+}
+
+static int nand_rda_init(struct nand_chip *this, struct rda_nand_info *info)
+{
+ unsigned int id[2];
+ int ret = 0;
+
+#ifdef NAND_DEBUG
+ printf("Enter function nand_rda_init\n");
+#endif
+
+ ret = nand_get_type(info, id);
+ if (ret)
+ return ret;
+
+ ret = init_nand_info(info);
+ if (ret)
+ return ret;
+ if (info->bus_width_16)
+ this->options |= NAND_BUSWIDTH_16;
+
+ (void)hal_init(info);
+
+ ret = nand_reset_flash();
+ if (ret)
+ return ret;
+
+ ret = nand_read_id(id);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int nand_rda_init_size(struct mtd_info *mtd, struct nand_chip *this,
+ u8 *id_data)
+{
+ struct rda_nand_info *info = this->priv;
+
+ /* TODO: this is not always right
+ * assuming nand is 2k/4k SLC for now
+ * for other types of nand, 64 pages per block is not always true
+ */
+ mtd->erasesize = info->vir_erase_size;
+ mtd->writesize = info->vir_page_size;
+ mtd->oobsize = info->vir_oob_size;
+
+ return (info->bus_width_16) ? NAND_BUSWIDTH_16 : 0;
+}
+
+int rda_nand_init(struct nand_chip *nand)
+{
+ struct rda_nand_info *info;
+ static struct rda_nand_info rda_nand_info;
+ int i;
+ u32 bootloader_end_addr = 0x400000;//4Mbytes
+
+ info = &rda_nand_info;
+#if 0
+ if (g_nand_flash_temp_buffer)
+ g_nand_flash_temp_buffer = (u8 *)malloc(18432);
+#endif
+ nand->chip_delay = 0;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->options |= NAND_USE_FLASH_BBT;
+#endif
+ /*
+ * in fact, nand controler we do hardware ECC silently, and
+ * we don't need tell mtd layer, and will simple nand operations
+ */
+ nand->ecc.mode = NAND_ECC_NONE;
+ /* Set address of hardware control function */
+ nand->cmd_ctrl = nand_rda_hwcontrol;
+ nand->init_size = nand_rda_init_size;
+ nand->cmdfunc = nand_rda_cmdfunc;
+ nand->read_byte = nand_rda_read_byte;
+ nand->read_word = nand_rda_read_word;
+ nand->read_buf = nand_rda_read_buf;
+ nand->write_buf = nand_rda_write_buf;
+ nand->dev_ready = nand_rda_dev_ready;
+ nand->select_chip = nand_rda_select_chip;
+ nand->read_nand_ID = read_ID;
+ nand->priv = (void *)info;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)NANDFC_DATA_BUF;
+#ifdef CONFIG_NAND_RDA_DMA
+ info->nand_data_phys = (void __iomem *)NANDFC_DATA_BUF;
+#endif /* CONFIG_NAND_RDA_DMA */
+ info->index = 0;
+ info->write_ptr = 0;
+ info->read_ptr = 0;
+ info->master_clk = get_master_clk_rate(_TGT_AP_CLK_APB2);
+ info->clk=_TGT_AP_NAND_CLOCK;
+
+#ifdef CONFIG_NAND_RDA_DMA
+ rda_request_dma(&info->dma_ch);
+#endif /* CONFIG_NAND_RDA_DMA */
+
+ nand_rda_init(nand, info);
+
+ if (info->parameter_mode_select == NAND_PARAMETER_BY_NAND) {
+ switch (info->type) {
+ case NAND_TYPE_2K:
+ bootloader_end_addr = 0x200000;//2Mbytes
+ break;
+ case NAND_TYPE_4K:
+ bootloader_end_addr = 0x400000;//4Mbytes
+ break;
+ case NAND_TYPE_8K:
+ if (is_power_of_2(info->vir_erase_size))
+ bootloader_end_addr = 0x400000;//4Mbytes
+ else
+ bootloader_end_addr = 0x300000;//3Mbytes
+ break;
+ case NAND_TYPE_16K:
+ bootloader_end_addr = 0x400000;//4Mbytes
+ break;
+ case NAND_TYPE_3K:
+ bootloader_end_addr = 0x300000;//3Mbytes
+ break;
+ case NAND_TYPE_7K:
+ bootloader_end_addr = 0x700000;//7Mbytes
+ break;
+ case NAND_TYPE_14K:
+ bootloader_end_addr = 0x700000;//7Mbytes
+ break;
+ default:
+ printf("error nand type: %d \n", info->type);
+ }
+
+ bootloader_end_addr -= nand_parameter.spl_offset;
+
+ if (info->page_shift) {
+ info->boot_logic_end_pageaddr = bootloader_end_addr >> info->page_shift;
+ info->spl_logic_pageaddr = nand_parameter.spl_offset >> info->page_shift;
+ } else {
+ info->boot_logic_end_pageaddr = bootloader_end_addr / info->page_size;
+ info->spl_logic_pageaddr = nand_parameter.spl_offset / info->page_size;
+ }
+ }
+
+ if (!nand->ecc.layout && (nand->ecc.mode != NAND_ECC_SOFT_BCH)) {
+ switch (info->type) {
+ case NAND_TYPE_2K:
+ nand->ecc.layout = &rda_nand_oob_2k;
+ break;
+ case NAND_TYPE_4K:
+ nand->ecc.layout = &rda_nand_oob_4k;
+ break;
+ case NAND_TYPE_8K:
+ nand->ecc.layout = &rda_nand_oob_8k;
+ break;
+ case NAND_TYPE_16K:
+ nand->ecc.layout = &rda_nand_oob_16k;
+ break;
+ case NAND_TYPE_3K:
+ nand->ecc.layout = &rda_nand_oob_4k;
+ break;
+ case NAND_TYPE_7K:
+ nand->ecc.layout = &rda_nand_oob_8k;
+ break;
+ case NAND_TYPE_14K:
+ nand->ecc.layout = &rda_nand_oob_16k;
+ break;
+ default:
+ printf("error oob size: %d \n", info->vir_oob_size);
+ }
+ nand->ecc.layout->oobfree[0].length = info->vir_oob_size - 2;
+ for(i=1; i<MTD_MAX_OOBFREE_ENTRIES; i++)
+ nand->ecc.layout->oobfree[i].length = 0;
+ }
+
+ return 0;
+}
+
+#if 0
+/* move to rda_nand_base.c */
+int board_nand_init(struct nand_chip *chip) __attribute__ ((weak));
+
+int board_nand_init(struct nand_chip *chip)
+{
+ return rda_nand_init(chip);
+}
+#endif
+
+#ifdef TGT_AP_DO_NAND_TEST
+
+#define NAND_TEST_DATA_BASE 0x11c01080
+#define NAND_RESULT_DATA_ADDR (NAND_TEST_DATA_BASE+0x00)
+#define NAND_TYPE_DATA_ADDR (NAND_TEST_DATA_BASE+0x04)
+#define NAND_PAGE_SIZE_ADDR (NAND_TEST_DATA_BASE+0x08)
+#define NAND_VID_B1_ADDR (NAND_TEST_DATA_BASE+0x0C)
+#define NAND_VID_B2_ADDR (NAND_TEST_DATA_BASE+0x10)
+#define NAND_NAME_ADDR (NAND_TEST_DATA_BASE+0x14)
+#define NAND_NAME_SIZE 0x08
+#define NAND_BUS_WIDTH_ADDR (NAND_TEST_DATA_BASE+0x1C)
+
+#define NAND_TEST_OK_VAL 0xA55A6666
+#define NAND_TEST_ERROR_VAL 0xDEADDEAD
+
+static void print_nand_info(int dev_idx)
+{
+ nand_info_t *nand = &nand_info[dev_idx];
+ struct nand_chip *chip = nand->priv;
+ struct rda_nand_info *info = chip->priv;
+ unsigned int id[2],type_val = 0,bus_width = 8;
+ int man_id,j,k;
+ char *pname ;
+
+ id[0] = gbl_nand_test_id_val[0];
+ id[1] = gbl_nand_test_id_val[1];
+ man_id = id[0] & 0xFF;
+ pname = NULL;
+ for(j=0; nand_manuf_ids[j].id != 0x0;j++) {
+ if(nand_manuf_ids[j].id == man_id) {
+ pname = nand_manuf_ids[j].name;
+ break;
+ }
+ }
+ printf("nand device info:\n");
+ if(pname != NULL)
+ printf("Nand name: %s\n",pname);
+ switch(info->type) {
+ case NAND_TYPE_4K: printf("Nand type: 4K\n"); type_val = 4;break;
+ case NAND_TYPE_2K: printf("Nand type: 2K\n"); type_val = 2;break;
+ case NAND_TYPE_8K: printf("Nand type: 8K\n"); type_val = 8;break;
+ case NAND_TYPE_16K: printf("Nand type: 16K\n"); type_val = 16;break;
+ default: printf("Nand type error\n"); type_val = 0;break;
+ }
+ //intf("Nand type: 0x%x\n",info->type);
+ //printf("Nand_used_type: 0x%x\n",info->nand_use_type);
+ printf("Nand page size: 0x%x\n",info->page_size);
+ printf("Nand ID: %08x %08x\n", id[0], id[1]);
+ printf("Nand page total num: 0x%x\n",info->page_total_num);
+
+ switch(info->bus_width_16) {
+ case 0:printf("Nand bus width: 8 bit\n"); bus_width = 8;break;
+ case 1:printf("Nand bus width: 16 bit\n"); bus_width = 16;break;
+ default:printf("Nand bus width error\n"); bus_width = 0;break;
+ }
+ *(unsigned int *)NAND_TYPE_DATA_ADDR = type_val;
+ *(unsigned int *)NAND_PAGE_SIZE_ADDR = info->page_size;
+ *(unsigned int *)NAND_VID_B1_ADDR = id[0];
+ *(unsigned int *)NAND_VID_B2_ADDR = id[1];
+ if(pname != NULL) {
+ for(k = 0;k<8;k++) {
+ *(unsigned char *)(NAND_NAME_ADDR + k) = '\0';
+ }
+ for(k = 0;k<8;k++) {
+ *(unsigned char *)(NAND_NAME_ADDR + k) = *(pname+k);
+ }
+ }
+ *(unsigned int *)NAND_BUS_WIDTH_ADDR = bus_width;
+}
+
+void test_nand_easily(void)
+{
+ nand_info_t *nand;
+ unsigned int i = 0,j = 0;
+ unsigned long addr = 0x1000000;
+ size_t len = 0;
+ int ret = 0;
+ nand_erase_options_t opts;
+
+ j = j;
+ ret = ret;
+ nand = &nand_info[0];
+
+ /* check test result */
+ if(*(unsigned int *)NAND_RESULT_DATA_ADDR == NAND_TEST_ERROR_VAL) {
+ printf("nand error!!!\n");
+ return;
+ }
+
+ printf("\n%s: current ticks = %llu\n",__func__,get_ticks());
+ printf("-------------Begin to test internal nand flash memory.............. \n");
+ len = nand->writesize;
+ for (i = 0; i < len; i++){
+ g_nand_test_buf_write[i] = i & 0xff;
+ g_nand_test_buf_read[i] =0;
+ }
+
+ /* set nand erasing parameters */
+ memset(&opts,0,sizeof(opts));
+ opts.offset = addr;
+ opts.length = len;
+ opts.jffs2 = 0;
+ opts.quiet = 0;
+ opts.spread = 0;
+
+ /* step0: read nand flash information data */
+ print_nand_info(0);
+ /* step1: erase flash */
+ printf("erase flash memory,addr = 0x%x,len = %d\n",(int)addr,(int)len);
+ //nand_erase(nand,addr,len);
+ ret = nand_erase_opts(nand, &opts);
+ if(ret != 0) {
+ printf("erase flash error, returned value: ret = %d\n",ret);
+ goto L_ERROR_END;
+ }
+ /* step2: write test data to flash */
+ printf("write test data to flash memory, addr = 0x%x, len = %d\n",(int)addr, (int)len);
+ /*
+ nand_write_skip_bad(nand,
+ addr,
+ &len,
+ g_nand_test_buf_write,
+ 0);
+ */
+ nand_write(nand, addr, &len,g_nand_test_buf_write);
+ printf("real writing test data len = %d\n",(int)len);
+ /* step3: read test data back */
+ printf("read test data from flash memory,addr = 0x%x, len = %d\n",(int)addr,(int)len);
+ //nand_read_skip_bad(nand,addr,&len,g_nand_test_buf_read);
+ nand_read(nand, addr,&len,g_nand_test_buf_read);
+ printf("real reading test data len = %d\n",(int)len);
+ #if 0
+ printf("display rx data\n");
+ for(i = 0; i < len; i++) {
+ printf("%x\t",g_nand_test_buf_read[i]);
+ j++;
+ if(j > 15) {
+ j = 0;
+ printf("\n");
+ }
+ }
+ #endif
+
+ /* step4: check test data for testing result */
+ printf("compare data\n");
+ for (i = 0; i < len; i++) {
+ if (g_nand_test_buf_write[i] != g_nand_test_buf_read[i]){
+ printf("nand operate error!!\n");
+ *(unsigned int *)NAND_RESULT_DATA_ADDR = NAND_TEST_ERROR_VAL;
+ return;
+ }
+ }
+//L_OK_END:
+
+ *(unsigned int *)NAND_RESULT_DATA_ADDR = NAND_TEST_OK_VAL;
+ printf("Write result data: addr = 0x%x,value = 0x%x\n",(int)NAND_RESULT_DATA_ADDR, NAND_TEST_OK_VAL);
+ printf("-------------Test internal nand flash memory OK.............. \n");
+ printf("\n%s: current ticks = %llu\n",__func__,get_ticks());
+ return;
+L_ERROR_END:
+ printf("-------------Test internal nand flash memory error.............. \n");
+ printf("\n%s: current ticks = %llu\n",__func__,get_ticks());
+ return;
+}
+#endif /* TGT_AP_DO_NAND_TEST */
diff --git a/drivers/mtd/nand/rda_spi_nand.c b/drivers/mtd/nand/rda_spi_nand.c
new file mode 100644
index 0000000000..0e78ec25d0
--- /dev/null
+++ b/drivers/mtd/nand/rda_spi_nand.c
@@ -0,0 +1,1654 @@
+#include <common.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <nand.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/reg_sysctrl.h>
+#include <asm/arch/hwcfg.h>
+#include <mtd/nand/rda_nand.h>
+
+#include <rda/tgt_ap_board_config.h>
+#include <rda/tgt_ap_clock_config.h>
+
+#ifdef CONFIG_SPL_BUILD
+#undef CONFIG_NAND_RDA_DMA
+#endif
+
+#ifdef CONFIG_NAND_RDA_DMA
+#include <asm/dma-mapping.h>
+#include <asm/arch/dma.h>
+#endif
+
+#if (_TGT_NAND_TYPE_ == _SPI_NAND_USED_)
+/*******************************************************************/
+/*******************spi flash MACRO define******************************/
+#define SPI_NAND_READ_DATA_BUFFER_BASE 0xc0000000
+#define SPI_NAND_WRITE_DATA_BUFFER_BASE (RDA_SPIFLASH_BASE + 0x8)
+
+/*******************spi controller register define**************************/
+#define SPI_FLASH_REG_BASE RDA_SPIFLASH_BASE
+#define CMD_ADDR_OFFSET 0x0
+#define MODE_BLOCKSIZE_OFFSET 0x4
+#define DATA_FIFO_OFFSET 0x8
+#define STATUS_OFFSET 0xc
+#define READBACK_REG_OFFSET 0x10
+#define FLASH_CONFIG_OFFSET 0x14
+#define FIFO_CTRL_OFFSET 0x18
+#define DUAL_SPI_OFFSET 0x1c
+#define READ_CMD_OFFSET 0x20
+#define NAND_SPI_CONFIG_OFFSET 0x24
+#define NAND_SPI_CONFIG2_OFFSET 0x28
+
+#define SPI_CMD_ADDR (SPI_FLASH_REG_BASE+CMD_ADDR_OFFSET)
+#define SPI_BLOCK_SIZE (SPI_FLASH_REG_BASE+MODE_BLOCKSIZE_OFFSET)
+#define SPI_DATA_FIFO (SPI_FLASH_REG_BASE+DATA_FIFO_OFFSET)
+#define SPI_STATUS (SPI_FLASH_REG_BASE+STATUS_OFFSET)
+#define SPI_READ_BACK (SPI_FLASH_REG_BASE+READBACK_REG_OFFSET)
+#define SPI_CONFIG (SPI_FLASH_REG_BASE+FLASH_CONFIG_OFFSET)
+#define SPI_FIFO_CTRL (SPI_FLASH_REG_BASE+FIFO_CTRL_OFFSET)
+#define SPI_CS_SIZE (SPI_FLASH_REG_BASE+DUAL_SPI_OFFSET)
+#define SPI_READ_CMD (SPI_FLASH_REG_BASE+READ_CMD_OFFSET)
+#define SPI_NAND_CMD (SPI_FLASH_REG_BASE+NAND_SPI_CONFIG_OFFSET)
+#define SPI_NAND_CMD2 (SPI_FLASH_REG_BASE+NAND_SPI_CONFIG2_OFFSET)
+
+/*******************spi flash command define**************************/
+#define OPCODE_WRITE_ENABLE 0x06
+#define OPCODE_WRITE_DISABLE 0x04
+#define OPCODE_GET_FEATURE 0x0f
+#define OPCODE_SET_FEATURE 0x1f
+#define OPCODE_PAGE_READ_2_CACHE 0x13
+#define OPCODE_READ_FROM_CACHE 0x03
+#define OPCODE_READ_FROM_CACHEX4 0x6b
+#define OPCODE_READ_FROM_CACHE_QUADIO 0xeb
+#define OPCODE_READ_ID 0x9f
+#define OPCODE_PROGRAM_LOAD_RANDOM_DATA 0x84
+#define OPCODE_PROGRAM_LOAD_RANDOM_DATAX4 0xc4
+#define OPCODE_PROGRAM_LOAD_RANDOM_DATA_QUADIO 0x72
+#define OPCODE_PROGRAM_EXECUTE 0x10
+#define OPCODE_BLOCK_ERASE 0xd8
+#define OPCODE_FLASH_RESET 0Xff
+
+//spi nand state
+#define NANDFC_SPI_PROG_FAIL (1 << 3)
+#define NANDFC_SPI_ERASE_FAIL (1 << 2)
+#define NANDFC_SPI_OPERATE_ERROR (1 << 0)
+
+typedef enum {
+ SPINAND_TYPE_SPI2K = 16, // SPI, 2048+64, 2Gb
+ SPINAND_TYPE_SPI4K = 17, // SPI, 4096+128, 4Gb
+ SPINAND_TYPE_INVALID = 0xFF,
+} SPINAND_FLASH_TYPE;
+
+#define PLL_BUS_FREQ (_TGT_AP_PLL_BUS_FREQ * 1000000)
+/*******************MACRO define***********************************/
+#define REG_READ_UINT32( _reg_ ) (*(volatile unsigned long*)(_reg_))
+#define REG_WRITE_UINT32( _reg_, _val_) ((*(volatile unsigned long*)(_reg_)) = (unsigned long)(_val_))
+
+/*******************spi nand global variables define **********************/
+//static u8 g_spi_flash_read_temp_buffer[4352] __attribute__ ((__aligned__(64)));
+/*sram space is not enough, so use sdram space as middle data buffer*/
+static u8 *g_spi_flash_read_temp_buffer = (u8 *)CONFIG_SPL_NAND_MIDDLE_DATA_BUFFER;
+/*******************************************************************/
+
+//#define NAND_DEBUG
+//#define NAND_DEBUG_VERBOSE
+/*read page data with 4k middle buffer: g_spi_flash_read_temp_buffer[4352],
+ OOB always use middle buffer.*/
+//#define READ_PAGE_DATA_USE_MIDDLE_BUFFER
+#define READ_PAGE_DATA_WITH_SW_MODE
+
+#define hal_gettime get_ticks
+#define SECOND * CONFIG_SYS_HZ_CLOCK
+#define NAND_TIMEOUT ( 2 SECOND )
+
+/* clock division value map */
+const static u8 clk_div_map[] = {
+ 4*60, /* 0 */
+ 4*60, /* 1 */
+ 4*60, /* 2 */
+ 4*60, /* 3 */
+ 4*60, /* 4 */
+ 4*60, /* 5 */
+ 4*60, /* 6 */
+ 4*60, /* 7 */
+ 4*40, /* 8 */
+ 4*30, /* 9 */
+ 4*24, /* 10 */
+ 4*20, /* 11 */
+ 4*17, /* 12 */
+ 4*15, /* 13 */
+ 4*13, /* 14 */
+ 4*12, /* 15 */
+ 4*11, /* 16 */
+ 4*10, /* 17 */
+ 4*9, /* 18 */
+ 4*8, /* 19 */
+ 4*7, /* 20 */
+ 4*13/2, /* 21 */
+ 4*6, /* 22 */
+ 4*11/2, /* 23 */
+ 4*5, /* 24 */
+ 4*9/2, /* 25 */
+ 4*4, /* 26 */
+ 4*7/2, /* 27 */
+ 4*3, /* 28 */
+ 4*5/2, /* 29 */
+ 4*2, /* 30 */
+ 4*1, /* 31 */
+};
+
+static struct nand_ecclayout spi_nand_oob_64 = {
+ .eccbytes = 16,
+ .eccpos = {
+ 12, 13, 14, 15, 28, 29, 30, 31,
+ 44, 45, 46, 47, 60, 61, 62, 63},
+ .oobfree = {
+ {.offset = 2,
+ .length = 10},
+ {.offset = 16,
+ .length = 12},
+ {.offset = 32,
+ .length = 12},
+ {.offset = 48,
+ .length = 12}}
+};
+
+static struct nand_ecclayout spi_nand_oob_128 = {
+ .eccbytes = 0,
+ .eccpos = {},
+ .oobfree = {
+ {.offset = 2,
+ .length = 126}}
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr spi_bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 16,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr spi_bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 16,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+void wait_spi_busy(void)
+{ /*spi is busy,wait until idle */
+ u32 data_tmp_32;
+
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x1) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ }
+}
+
+void wait_spi_tx_fifo_empty(void)
+{ /*spi tx fifo not empty, wait until empty */
+ u32 data_tmp_32;
+
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (!(data_tmp_32 & 0x2)) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ }
+}
+
+void wait_spi_rx_fifo_empty(void)
+{ /*spi rx fifo not empty, wait until empty */
+ u32 data_tmp_32;
+
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (!(data_tmp_32 & 0x8)) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ }
+}
+
+void spi_tx_fifo_clear(void)
+{ /*clear spi tx fifo */
+ REG_WRITE_UINT32(SPI_FIFO_CTRL, 0x2);
+ wait_spi_tx_fifo_empty();
+}
+
+void spi_rx_fifo_clear(void)
+{ /*clear spi rx fifo */
+ REG_WRITE_UINT32(SPI_FIFO_CTRL, 0x1);
+ wait_spi_rx_fifo_empty();
+}
+
+static void push_fifo_data(u8 data_array[], u32 data_cnt, BOOL quard_flag,
+ BOOL clr_flag)
+{
+ u32 data_tmp_32;
+ u16 i = 0;
+
+ if (clr_flag)
+ spi_tx_fifo_clear();
+
+ /*if tx fifo full, wait */
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x4)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ /*put data into fifo */
+ for (i = 0; i < data_cnt; i++) {
+ data_tmp_32 = (u32) data_array[i];
+ if (quard_flag)
+ data_tmp_32 = data_tmp_32 | 0x100;
+ REG_WRITE_UINT32(SPI_DATA_FIFO, data_tmp_32);
+ }
+}
+
+/********************************************************************/
+/* opmode = 0: normal operation */
+/* opmode = 1: extended serial read */
+/* opmode = 2: extended quard read */
+/* opmode = 3: extended write (OPCODE_WRITE_ENABLE, flash_addr, opmode, 0); */
+/********************************************************************/
+static void start_flash_operation(u8 cmd, u32 addr, u8 opmode, u16 blocksize)
+{
+ u32 data_tmp_32;
+
+ wait_spi_busy();
+
+ if (blocksize != 0) {
+ data_tmp_32 = REG_READ_UINT32(SPI_BLOCK_SIZE);
+ data_tmp_32 = (data_tmp_32 & (~0x1ff00)) | (blocksize << 8);
+ REG_WRITE_UINT32(SPI_BLOCK_SIZE, data_tmp_32);
+ wait_spi_busy();
+ }
+
+ if (opmode == 0) {
+ data_tmp_32 = ((addr << 8) & 0xffffff00) | cmd;
+ REG_WRITE_UINT32(SPI_CMD_ADDR, data_tmp_32);
+ } else if (opmode == 1) {
+ data_tmp_32 = (((cmd << 8) | 0x10000) & 0x000fff00);
+ REG_WRITE_UINT32(SPI_CMD_ADDR, data_tmp_32);
+ } else if (opmode == 2) {
+ data_tmp_32 = (((cmd << 8) | 0x30000) & 0x000fff00);
+ REG_WRITE_UINT32(SPI_CMD_ADDR, data_tmp_32);
+ } else if (opmode == 3) {
+ data_tmp_32 = ((cmd << 8) & 0x000fff00);
+ REG_WRITE_UINT32(SPI_CMD_ADDR, data_tmp_32);
+ } else {
+ data_tmp_32 = ((cmd << 8) & 0x000fff00);
+ REG_WRITE_UINT32(SPI_CMD_ADDR, data_tmp_32);
+ }
+}
+
+u8 get_flash_feature(u32 flash_addr)
+{
+ u32 data_tmp_32;
+ u8 opmode;
+ u8 data_array[2];
+ u8 data_size;
+ u8 quard_flag;
+
+ quard_flag = FALSE;
+ opmode = 1;
+ data_array[0] = 0xb0;
+ data_size = 1;
+
+ wait_spi_busy();
+ /*clear spi rx fifo */
+ spi_rx_fifo_clear();
+
+ /*put addr into spi tx fifo */
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ /*send command */
+ start_flash_operation(OPCODE_GET_FEATURE, flash_addr, opmode, 1);
+ wait_spi_busy();
+
+ /*wait until rx fifo not empty */
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ return (REG_READ_UINT32(SPI_READ_BACK) & 0xff);
+}
+
+void set_flash_feature(u8 data)
+{
+ u8 opmode;
+ u8 data_array[2];
+ u8 data_size;
+ BOOL quard_flag;
+
+ quard_flag = FALSE;
+ opmode = 3;
+ data_array[1] = data;
+ data_array[0] = 0xb0;
+ data_size = 2;
+
+ wait_spi_busy();
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(OPCODE_SET_FEATURE, 0, opmode, 0);
+ wait_spi_busy();
+ wait_spi_tx_fifo_empty();
+}
+
+static BOOL spi_nand_flash_quad_enable = FALSE;
+void enable_spi_nand_flash_quad_mode(void)
+{
+ u8 data;
+
+ data = get_flash_feature(0);
+ data = data | 0x1;
+ set_flash_feature(data);
+ spi_nand_flash_quad_enable = TRUE;
+}
+
+void disable_spi_nand_flash_quad_mode(void)
+{
+ u8 data;
+
+ data = get_flash_feature(0);
+ data = data & 0xfe;
+ set_flash_feature(data);
+ spi_nand_flash_quad_enable = FALSE;
+}
+
+void enable_spi_nand_flash_ecc_mode(void)
+{
+ u8 data;
+
+ data = get_flash_feature(0);
+ data = data | 0x10;
+ set_flash_feature(data);
+}
+
+void disable_spi_nand_flash_ecc_mode(void)
+{
+ u8 data;
+
+ data = get_flash_feature(0);
+ data = data & 0xef;
+ set_flash_feature(data);
+}
+
+u8 get_flash_status(u32 flash_addr)
+{
+ u32 data_tmp_32;
+ u8 opmode;
+ u8 data_array[2];
+ u8 data_size;
+ BOOL quard_flag;
+
+ quard_flag = FALSE;
+ opmode = 1;
+ data_array[0] = 0xc0;
+ data_size = 1;
+
+ wait_spi_busy();
+ spi_rx_fifo_clear();
+
+ wait_spi_busy();
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(OPCODE_GET_FEATURE, flash_addr, opmode, 1);
+ wait_spi_busy();
+
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ return (REG_READ_UINT32(SPI_READ_BACK) & 0xff);
+}
+
+void disable_flash_protection(u32 flash_addr)
+{
+ u8 opmode;
+ u8 data_array[2];
+ u8 data_size;
+
+ opmode = 3;
+ data_array[1] = 0;
+ data_array[0] = 0xa0;
+ data_size = 2;
+
+ wait_spi_busy();
+ push_fifo_data(data_array, data_size, FALSE, TRUE);
+ start_flash_operation(OPCODE_SET_FEATURE, flash_addr, opmode, 0);
+ wait_spi_busy();
+ wait_spi_tx_fifo_empty();
+}
+
+void spi_nand_flash_operate_mode_setting(u8 mode)
+{
+ u32 data_tmp_32;
+
+ data_tmp_32 = REG_READ_UINT32(SPI_NAND_CMD);
+ if (mode == 1) // 1 command-1 addr- 1 data
+ {
+ data_tmp_32 |= OPCODE_READ_FROM_CACHE << 24;
+ } else if (mode == 2) // 1 command- 1 addr- 4 data
+ {
+ data_tmp_32 |= OPCODE_READ_FROM_CACHEX4 << 24;
+ } else if (mode == 4) // 1 command- 4 addr- 4 data
+ {
+ data_tmp_32 |= OPCODE_READ_FROM_CACHE_QUADIO << 24;
+ } else // default:1 command-1 addr- 1 data
+ {
+ data_tmp_32 |= OPCODE_READ_FROM_CACHE << 24;
+ }
+
+ REG_WRITE_UINT32(SPI_NAND_CMD, data_tmp_32);
+}
+
+u16 get_flash_ID(u32 flash_addr)
+{
+ u32 data_tmp_32;
+ u8 opmode;
+ BOOL quard_flag;
+ u8 data_array[2];
+ u8 data_size;
+ u8 manufacturerID, device_memory_type_ID;
+
+ opmode = 1;
+ data_array[0] = 0x00;
+ data_size = 1;
+ quard_flag = FALSE;
+
+ wait_spi_busy();
+ spi_rx_fifo_clear();
+
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(OPCODE_READ_ID, flash_addr, opmode, 2);
+ wait_spi_busy();
+
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ manufacturerID = (u8) (REG_READ_UINT32(SPI_READ_BACK) & 0xff);
+ device_memory_type_ID = (u8) (REG_READ_UINT32(SPI_READ_BACK) & 0xff);
+
+ return (manufacturerID | (device_memory_type_ID << 8));
+}
+
+void nand_spi_init(BOOL quard_flag, u8 clk_offset_val, u8 clkdiv_val)
+{
+ u32 data_tmp_32;
+
+ wait_spi_busy();
+ data_tmp_32 = ((clk_offset_val << 4) | (clkdiv_val << 8)) & 0xff70;
+ REG_WRITE_UINT32(SPI_CONFIG, data_tmp_32);
+ wait_spi_busy();
+
+ if (quard_flag) {
+ enable_spi_nand_flash_quad_mode();
+ spi_nand_flash_operate_mode_setting(4);
+ }else{
+ disable_spi_nand_flash_quad_mode();
+ spi_nand_flash_operate_mode_setting(1);
+ }
+
+ enable_spi_nand_flash_ecc_mode();
+}
+
+static void nand_spi_fifo_word_read_set(BOOL enable)
+{
+ u32 data_tmp_32;
+
+ wait_spi_busy();
+ data_tmp_32 = REG_READ_UINT32(SPI_CONFIG);
+ if(enable)
+ data_tmp_32 = (data_tmp_32 & (~(3 << 17))) |(2 << 17);
+ else
+ data_tmp_32 = data_tmp_32 & (~(3 << 17));
+ REG_WRITE_UINT32(SPI_CONFIG, data_tmp_32);
+ wait_spi_busy();
+}
+
+static int init_spi_nand_info(struct rda_nand_info *info,
+ int nand_type, int bus_width_16)
+{
+ info->type = nand_type;
+ switch (nand_type) {
+ case SPINAND_TYPE_SPI2K:
+ info->hard_ecc_hec = 0;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 11;
+ info->oob_size = 64;
+ info->page_num_per_block = 64;
+ info->vir_page_size = 2048;
+ info->vir_page_shift = 11;
+ info->vir_oob_size = 64;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ break;
+ case SPINAND_TYPE_SPI4K:
+ info->hard_ecc_hec = 0;
+ info->bus_width_16 = bus_width_16;
+ info->page_shift = 12;
+ info->oob_size = 128;
+ info->page_num_per_block = 64;
+ info->vir_page_size = 4096;
+ info->vir_page_shift = 12;
+ info->vir_oob_size = 128;
+ info->vir_erase_size =
+ info->vir_page_size * info->page_num_per_block;
+ break;
+ default:
+ printf("invalid nand type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static u8 spi_nand_rda_read_byte(struct mtd_info *mtd)
+{
+ u8 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *((u8 *) info->byte_buf + info->index);
+ info->index++;
+#ifdef NAND_DEBUG
+ printf("spi_nand_read_byte, ret = %02x\n", ret);
+#endif
+ return ret;
+}
+
+static u16 spi_nand_rda_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ ret = *(u16 *) ((uint8_t *) info->byte_buf + info->index);
+ info->index += 2;
+#ifdef NAND_DEBUG
+ printf("spi_nand_read_word, ret = %04x\n", ret);
+#endif
+ return ret;
+}
+
+#ifdef READ_PAGE_DATA_WITH_SW_MODE
+static void spi_nand_flash_page_read2cache(u32 page_addr)
+{
+ u32 data_tmp_32;
+ u8 opmode;
+ u8 data_array[3];
+ u8 data_size;
+
+#ifdef NAND_DEBUG
+ printf("spi nand: OOB read to cache!page addr = %x\n", page_addr);
+#endif
+ opmode = 3;
+ data_array[2] = (u8)(page_addr & 0xff);
+ data_array[1] = (u8)((page_addr >> 8) & 0xff);
+ data_array[0] = (u8)((page_addr >> 16) & 0xff);
+ data_size = 3;
+
+ wait_spi_busy();
+ push_fifo_data(data_array, data_size, FALSE, TRUE);
+ start_flash_operation(OPCODE_PAGE_READ_2_CACHE, 0, opmode, 0);
+ wait_spi_busy();
+
+ data_tmp_32 = get_flash_status(0);
+ while (1)
+ {
+ if ((data_tmp_32 & 0x1) == 0)
+ {
+ break;
+ }
+ else
+ {
+ data_tmp_32 = get_flash_status(0);
+ }
+ }
+}
+#endif
+
+#define __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__ 64 //bytes, 16 words
+static void spi_nand_flash_read_from_cache(u8 * buffer, u32 page_column_addr,
+ u16 length)
+{
+ u32 data_tmp_32;
+ u8 opmode, i;
+ u8 data_array[3];
+ u8 data_size;
+ u32 data_count, data_value;
+ u32 loop_div_count, loop_mod_count;
+ BOOL quard_flag = FALSE;
+ u8 command = OPCODE_READ_FROM_CACHE;
+ u32 temp_column_addr;
+
+#ifdef NAND_DEBUG
+ printf
+ ("spi nand: OOB read from cache to buffer!buffer_addr = %x, column = %x, length = %d\n",
+ (u32) buffer, page_column_addr, length);
+#endif
+ opmode = 1;
+ data_array[2] = 0;
+ data_size = 3;
+
+ if (spi_nand_flash_quad_enable) {
+ command = OPCODE_READ_FROM_CACHE_QUADIO;
+ quard_flag = TRUE;
+ opmode = 0;
+ }
+#if 0
+ for (data_count = 0; data_count < length / 16; data_count++) {
+ temp_column_addr = page_column_addr + 16 * data_count;
+ data_array[1] = (u8) (temp_column_addr & 0xff);
+ data_array[0] = ((temp_column_addr >> 8) & 0x0f);
+
+ wait_spi_busy();
+ spi_rx_fifo_clear();
+
+ wait_spi_busy();
+ if (spi_nand_flash_quad_enable) {
+ start_flash_operation(command, temp_column_addr, opmode,
+ 16);
+ } else {
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(command, 0, opmode, 16);
+ }
+ wait_spi_busy();
+#ifdef NAND_DEBUG
+ printf
+ ("read OOB: data [0] = %x, data{1] = %x, data[2] = %x, quard_flag = %d, opmode = %d\n",
+ data_array[0], data_array[1], data_array[2], quard_flag,
+ opmode);
+#endif
+ for (i = 0; i < 16; i++) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ buffer[data_count * 16 + i] =
+ (u8) (REG_READ_UINT32(SPI_READ_BACK) & 0xff);
+#ifdef NAND_DEBUG
+ printf("buffer[%d] = %x \n", data_count * 16 + i,
+ buffer[data_count * 16 + i]);
+#endif
+ }
+ }
+
+ if ((length % 16) > 0) {
+ temp_column_addr = page_column_addr + 16 * data_count;
+ data_array[1] = (u8) (temp_column_addr & 0xff);
+ data_array[0] = ((temp_column_addr >> 8) & 0x0f);
+
+ wait_spi_busy();
+ spi_rx_fifo_clear();
+
+ wait_spi_busy();
+ if (spi_nand_flash_quad_enable) {
+ start_flash_operation(command, temp_column_addr, opmode,
+ length % 16);
+ } else {
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(command, 0, opmode, length % 16);
+ }
+ wait_spi_busy();
+
+ for (i = 0; i < (length % 16); i++) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ buffer[data_count * 16 + i] =
+ (u8) (REG_READ_UINT32(SPI_READ_BACK) & 0xff);
+#ifdef NAND_DEBUG
+ printf("buffer[%d] = %x \n", data_count * 16 + i,
+ buffer[data_count * 16 + i]);
+#endif
+ }
+ }
+#else
+ nand_spi_fifo_word_read_set(TRUE);
+ if (length > 0) {
+ loop_div_count = length / __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__;
+ loop_mod_count = length % __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__;
+ for (data_count = 0; data_count < loop_div_count; data_count++) {
+ temp_column_addr = page_column_addr + __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__ * data_count;
+ data_array[1] = (u8) (temp_column_addr & 0xff);
+ data_array[0] = ((temp_column_addr >> 8) & 0x0f);
+
+ wait_spi_busy();
+ spi_rx_fifo_clear();
+
+ wait_spi_busy();
+ if (spi_nand_flash_quad_enable) {
+ start_flash_operation(command, temp_column_addr, opmode,
+ __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__);
+ } else {
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(command, 0, opmode, __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__);
+ }
+ wait_spi_busy();
+
+ for (i = 0; i < __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__; i += 4) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ data_value = REG_READ_UINT32(SPI_READ_BACK);
+
+ buffer[i + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) (data_value & 0xff);
+ buffer[i + 1 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) ((data_value >> 8) & 0xff);
+ buffer[i + 2 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) ((data_value >> 16) & 0xff);
+ buffer[i + 3 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) ((data_value >> 24) & 0xff);
+#ifdef NAND_DEBUG
+ printf("buffer[%d] = %x \n", i + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+ printf("buffer[%d] = %x \n", i + 1 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + 1 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+ printf("buffer[%d] = %x \n", i + 2 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + 2 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+ printf("buffer[%d] = %x \n", i + 3 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + 3 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+#endif
+ }
+ }
+
+ if(loop_mod_count){
+ temp_column_addr = page_column_addr + __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__ * data_count;
+ data_array[1] = (u8) (temp_column_addr & 0xff);
+ data_array[0] = ((temp_column_addr >> 8) & 0x0f);
+ wait_spi_busy();
+ spi_rx_fifo_clear();
+ wait_spi_busy();
+ if (spi_nand_flash_quad_enable) {
+ start_flash_operation(command, temp_column_addr, opmode, loop_mod_count);
+ } else {
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ start_flash_operation(command, 0, opmode, loop_mod_count);
+ }
+ wait_spi_busy();
+
+ for (i = 0; i < loop_mod_count; i += 4) {
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+ while (data_tmp_32 & 0x8)
+ data_tmp_32 = REG_READ_UINT32(SPI_STATUS);
+
+ data_value = REG_READ_UINT32(SPI_READ_BACK);
+
+ buffer[i + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) (data_value & 0xff);
+ buffer[i + 1 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) ((data_value >> 8) & 0xff);
+ buffer[i + 2 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) ((data_value >> 16) & 0xff);
+ buffer[i + 3 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__] = (u8) ((data_value >> 24) & 0xff);
+#ifdef NAND_DEBUG
+ printf("buffer[%d] = %x \n", i + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+ printf("buffer[%d] = %x \n", i + 1 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + 1 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+ printf("buffer[%d] = %x \n", i + 2 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + 2 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+ printf("buffer[%d] = %x \n", i + 3 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__,
+ buffer[i + 3 + data_count * __SPI_NAND_CONTROLLER_RCV_FIFO_LEVEL__]);
+#endif
+ }
+ }
+ }
+ nand_spi_fifo_word_read_set(FALSE);
+#endif
+}
+
+static void spi_nand_read_flash_data2buffer(struct mtd_info *mtd, u32 page_addr)
+{
+#ifndef READ_PAGE_DATA_WITH_SW_MODE
+ u32 flash_addr =
+ SPI_NAND_READ_DATA_BUFFER_BASE + page_addr * mtd->writesize;
+#endif
+
+#ifdef NAND_DEBUG
+ printf("read flash : page addr = %x, flash_addr = %x\n", page_addr,
+ flash_addr);
+#endif /* NAND_DEBUG */
+
+#if 1// ndef CONFIG_NAND_RDA_DMA
+#ifdef READ_PAGE_DATA_WITH_SW_MODE
+ spi_nand_flash_page_read2cache(page_addr);
+ spi_nand_flash_read_from_cache(&g_spi_flash_read_temp_buffer[0],
+ 0, mtd->writesize);
+#else
+ memcpy((void *)(&g_spi_flash_read_temp_buffer[0]), (void *)flash_addr,
+ mtd->writesize);
+#endif
+#ifdef NAND_DEBUG
+ printf("read flash: dst_addr = %x, src_addr = %x, length = %x\n",
+ (u32) (&g_spi_flash_read_temp_buffer[0]), flash_addr,
+ mtd->writesize);
+#endif /* NAND_DEBUG */
+#else
+ struct nand_chip *chip = mtd->priv;
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)(&g_spi_flash_read_temp_buffer[0]);
+ int ret = 0;
+ struct rda_nand_info *info = chip->priv;
+
+ phys_addr = dma_map_single(addr, mtd->writesize, DMA_FROM_DEVICE);
+
+ dma_param.src_addr = flash_addr;
+ dma_param.dst_addr = phys_addr;
+ dma_param.xfer_size = mtd->writesize;
+ //dma_param.dma_mode = RDA_DMA_FR_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda spi nand : Failed to set parameter\n");
+ dma_unmap_single(addr, mtd->writesize, DMA_FROM_DEVICE);
+ return;
+ }
+
+ /* use flush to avoid annoying unaligned warning */
+ /* however, invalidate after the dma it the right thing to do */
+ flush_dcache_range((u32) addr, (u32) (addr + mtd->writesize));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* use flush to avoid annoying unaligned warning */
+ //invalidate_dcache_range((u32)addr, (u32)(addr + len));
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, mtd->writesize, DMA_FROM_DEVICE);
+#ifdef NAND_DEBUG
+ printf("read flash, middle buf-data:\n");
+ rda_dump_buf((char *)addr, mtd->writesize);
+#endif
+#endif
+}
+
+static void spi_nand_rda_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *nand_ptr = (u8 *) (g_spi_flash_read_temp_buffer + info->read_ptr);
+#ifndef READ_PAGE_DATA_USE_MIDDLE_BUFFER
+#ifndef READ_PAGE_DATA_WITH_SW_MODE
+ u32 flash_addr =
+ SPI_NAND_READ_DATA_BUFFER_BASE + info->page_addr * mtd->writesize;
+#endif/*READ_PAGE_DATA_WITH_SW_MODE*/
+#endif/*READ_PAGE_DATA_USE_MIDDLE_BUFFER*/
+
+#ifdef NAND_DEBUG
+ printf
+ ("read buf: buf addr = %x, len = %d, nand_ptr = %x, read_ptr = %d\n",
+ (u32) buf, len, (u32) nand_ptr, info->read_ptr);
+#endif /* NAND_DEBUG */
+
+#if 1//ndef CONFIG_NAND_RDA_DMA
+#ifdef READ_PAGE_DATA_USE_MIDDLE_BUFFER
+ memcpy((void *)buf, (void *)nand_ptr, len);
+#else
+ if(info->read_ptr == 0){
+#ifndef READ_PAGE_DATA_WITH_SW_MODE
+ memcpy((void *)buf, (void *)flash_addr, mtd->writesize);
+#else
+ spi_nand_flash_page_read2cache(info->page_addr);
+ spi_nand_flash_read_from_cache(buf, 0, mtd->writesize);
+#endif
+ spi_nand_flash_read_from_cache(&g_spi_flash_read_temp_buffer[mtd->writesize],
+ mtd->writesize, mtd->oobsize);
+ /*gigadevice flash hardware reserved block, add to BBT, 0xffe0000, 0x1ffc0000 */
+ if ((0xffe0000 == info->page_addr * mtd->writesize && info->type == SPINAND_TYPE_SPI2K)
+ || (0x1ffc0000 == info->page_addr * mtd->writesize && info->type == SPINAND_TYPE_SPI4K)){
+ memset((void *)buf, 0, len);
+ memset(&g_spi_flash_read_temp_buffer[0], 0,
+ mtd->writesize + mtd->oobsize);
+ }
+ }else{
+ memcpy((void *)buf, (void *)nand_ptr, len);
+ }
+#endif/*READ_PAGE_DATA_USE_MIDDLE_BUFFER*/
+
+ info->read_ptr += len;
+
+#ifdef NAND_DEBUG
+ printf("read buf, dst buf:\n");
+ rda_dump_buf((char *)buf, len);
+#endif
+
+#else
+ struct rda_dma_chan_params dma_param;
+ dma_addr_t phys_addr;
+ void *addr = (void *)buf;
+ int ret = 0;
+
+ /*
+ * If size is less than the size of oob,
+ * we copy directly them to mapping buffer.
+ */
+ if (len <= mtd->oobsize) {
+ memcpy(buf, (void *)nand_ptr, len);
+ info->read_ptr = 0;
+#ifdef NAND_DEBUG
+ printf("read buf, dst buf-oob:\n");
+ rda_dump_buf((char *)buf, len);
+#endif
+ return;
+ }
+
+ phys_addr = dma_map_single(addr, len, DMA_BIDIRECTIONAL);
+
+ dma_param.src_addr = (u32) nand_ptr;
+ dma_param.dst_addr = phys_addr;
+ dma_param.xfer_size = len;
+ //dma_param.dma_mode = RDA_DMA_FR_MODE;
+ dma_param.dma_mode = RDA_DMA_NOR_MODE;
+
+ ret = rda_set_dma_params(info->dma_ch, &dma_param);
+ if (ret < 0) {
+ printf("rda spi nand : Failed to set parameter\n");
+ dma_unmap_single(addr, len, DMA_BIDIRECTIONAL);
+ return;
+ }
+
+ /* use flush to avoid annoying unaligned warning */
+ /* however, invalidate after the dma it the right thing to do */
+ flush_dcache_range((u32) addr, (u32) (addr + len));
+
+ rda_start_dma(info->dma_ch);
+ rda_poll_dma(info->dma_ch);
+ rda_stop_dma(info->dma_ch);
+
+ /* use flush to avoid annoying unaligned warning */
+ //invalidate_dcache_range((u32)addr, (u32)(addr + len));
+
+ /* Free the specified physical address */
+ dma_unmap_single(addr, len, DMA_BIDIRECTIONAL);
+ info->read_ptr += len;
+
+#ifdef NAND_DEBUG
+ printf("read buf, dst buf-data:\n");
+ rda_dump_buf((char *)addr, len);
+#endif
+
+ return;
+#endif /* CONFIG_NAND_RDA_DMA */
+}
+
+static void spi_nand_flash_write_data2cache_random(u8 * buffer,
+ u32 page_column_addr,
+ u32 length,
+ BOOL bug_mode)
+{
+ u8 opmode;
+ u8 data_array[3];
+ u8 data_size;
+ BOOL quard_flag;
+ u8 opcommand;
+
+ if(bug_mode == FALSE){
+ if (spi_nand_flash_quad_enable) {
+ quard_flag = TRUE;
+ opcommand = OPCODE_PROGRAM_LOAD_RANDOM_DATA_QUADIO;
+ } else {
+ quard_flag = FALSE;
+ opcommand = OPCODE_PROGRAM_LOAD_RANDOM_DATA;
+ }
+
+ opmode = 3;
+ data_array[1] = (u8) (page_column_addr & 0xff);
+ data_array[0] = ((page_column_addr >> 8) & 0x0f);
+ data_size = 2;
+
+ wait_spi_busy();
+ push_fifo_data(data_array, data_size, quard_flag, TRUE);
+ push_fifo_data(buffer, length, quard_flag, FALSE);
+ start_flash_operation(opcommand, 0, opmode, 0);
+ }
+ else{/*0x84 standard mode for spi controller bug*/
+ wait_spi_busy();
+ push_fifo_data(buffer, length, FALSE, TRUE);
+ start_flash_operation(OPCODE_PROGRAM_LOAD_RANDOM_DATA, page_column_addr,
+ 0, 0);
+ }
+
+ wait_spi_busy();
+ wait_spi_tx_fifo_empty();
+ wait_spi_busy();
+}
+
+void spi_nand_flash_write_one_page_cache_random(u8 * buffer, u32 length,
+ u32 write_pos)
+{
+ u32 i = 0, m, n;
+ u8 *w_buffer = buffer;
+
+#define SPI_NAND_OPERATE_LENGTH_ONCE 200
+
+// if (write_pos == 0) {
+ m = length / SPI_NAND_OPERATE_LENGTH_ONCE;
+ n = length % SPI_NAND_OPERATE_LENGTH_ONCE;
+
+ if(n == 0){
+ m = m - 1;
+ n = SPI_NAND_OPERATE_LENGTH_ONCE;
+ }
+
+ if (m > 0) {
+ for (i = 0; i < m; i++) {
+ spi_nand_flash_write_data2cache_random(w_buffer
+ +
+ SPI_NAND_OPERATE_LENGTH_ONCE * i,
+ SPI_NAND_OPERATE_LENGTH_ONCE * i + write_pos,
+ SPI_NAND_OPERATE_LENGTH_ONCE,
+ FALSE);
+ }
+ }
+
+ if (n > 0)
+ spi_nand_flash_write_data2cache_random(w_buffer +
+ SPI_NAND_OPERATE_LENGTH_ONCE * i,
+ SPI_NAND_OPERATE_LENGTH_ONCE * i + write_pos,
+ n,
+ TRUE);
+// } else {
+// spi_nand_flash_write_data2cache_random(w_buffer, write_pos,
+// length, TRUE);
+// }
+}
+
+static int spi_nand_flash_program_execute(u32 block_page_addr)
+{
+ u32 data_tmp_32;
+ u8 opmode;
+ u8 data_array[5];
+ u8 data_size;
+
+ start_flash_operation(OPCODE_WRITE_ENABLE, 0, 0, 0);
+ wait_spi_busy();
+
+ opmode = 3;
+ data_array[2] = (u8) (block_page_addr & 0xff);
+ data_array[1] = (u8) ((block_page_addr >> 8) & 0xff);
+ data_array[0] = (u8) ((block_page_addr >> 16) & 0xff);
+ data_size = 3;
+
+ push_fifo_data(data_array, data_size, FALSE, TRUE);
+ start_flash_operation(OPCODE_PROGRAM_EXECUTE, 0, opmode, 0);
+ wait_spi_busy();
+
+ data_tmp_32 = get_flash_status(0);
+ while (1) {
+ if ((data_tmp_32 & 0x1) == 0) {
+ if ((data_tmp_32 & 0x8) == 0x8) {
+ printf("!!program failed page addr = %x\n ",
+ block_page_addr);
+ return NANDFC_SPI_PROG_FAIL;
+ }
+ break;
+ } else {
+ data_tmp_32 = get_flash_status(0);
+ }
+ }
+
+ return 0;
+}
+
+static void spi_nand_rda_write_buf(struct mtd_info *mtd, const uint8_t * buf,
+ int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+ u8 *pbuf = (u8 *) buf;
+
+#ifdef NAND_DEBUG
+ printf("spi_nand_rda_write_buf : buf addr = %x, len = %d, write_pos = %d\n", (u32) pbuf,
+ len, info->write_ptr);
+#endif /* NAND_DEBUG */
+
+#ifdef NAND_DEBUG
+ rda_dump_buf((char *)pbuf, len);
+#endif
+
+ if (len > mtd->writesize) {
+ printf("error: write size is out of page size!\n");
+ }
+
+ spi_nand_flash_write_one_page_cache_random(pbuf, len, info->write_ptr);
+ info->write_ptr += len;
+}
+
+u32 spi_nand_flash_block_erase(u32 block_page_addr)
+{
+ u32 data_tmp_32;
+ u8 opmode;
+ u8 data_array[3];
+ u8 data_size;
+
+ start_flash_operation(OPCODE_WRITE_ENABLE, 0, 0, 0);
+
+ opmode = 3;
+ data_array[2] = (u8) (block_page_addr & 0xff);
+ data_array[1] = ((block_page_addr >> 8) & 0xff);
+ data_array[0] = ((block_page_addr >> 16) & 0x0f);
+ data_size = 3;
+
+ wait_spi_busy();
+ push_fifo_data(data_array, data_size, 0, TRUE);
+ start_flash_operation(OPCODE_BLOCK_ERASE, 0, opmode, 0);
+ wait_spi_busy();
+
+ data_tmp_32 = get_flash_status(0);
+ while (1) {
+ if ((data_tmp_32 & 0x1) == 0) {
+ if ((data_tmp_32 & 0x4) == 0x4) {
+ printf("!!earse failed page addr = %x \n",
+ block_page_addr);
+ return NANDFC_SPI_ERASE_FAIL;
+ }
+ break;
+ } else {
+ data_tmp_32 = get_flash_status(0);
+ }
+ }
+ return 0;
+}
+
+static void nand_rda_do_cmd_post(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct rda_nand_info *info = this->priv;
+ u8 temp;
+
+ switch (info->cmd) {
+ case NAND_CMD_READID:
+ info->index = 0;
+ break;
+ case NAND_CMD_STATUS:
+ temp = get_flash_status(0);
+#ifdef NAND_DEBUG
+ printf("nand_rda_do_cmd_post: flash status = %x\n ", temp);
+#endif
+ if ((temp & NANDFC_SPI_ERASE_FAIL)
+ && (info->cmd_flag == NAND_CMD_ERASE2))
+ info->byte_buf[0] = 0xE0 | NAND_STATUS_FAIL;
+ else if ((temp & NANDFC_SPI_PROG_FAIL)
+ && (info->cmd_flag == NAND_CMD_PAGEPROG))
+ info->byte_buf[0] = 0xE0 | NAND_STATUS_FAIL;
+ else if (temp & NANDFC_SPI_OPERATE_ERROR)
+ info->byte_buf[0] = 0xE0 | NAND_STATUS_FAIL;
+ else
+ info->byte_buf[0] = 0xE0;
+
+ info->index = 0;
+ info->cmd_flag = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static void spi_nand_rda_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+
+}
+
+static int spi_nand_reset_flash(void)
+{
+ u32 data_tmp_32;
+
+ start_flash_operation(OPCODE_FLASH_RESET, 0, 0, 0);
+ /*delay 10us for reset process */
+ udelay(10);
+
+ data_tmp_32 = get_flash_status(0);
+ while (1) {
+ if ((data_tmp_32 & 0x1) == 0) {
+ break;
+ } else {
+ data_tmp_32 = get_flash_status(0);
+ }
+ }
+ return 0;
+}
+
+static void spi_nand_rda_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct rda_nand_info *info = chip->priv;
+
+#ifdef NAND_DEBUG
+ printf("nand_rda_cmdfunc, cmd = %x, page_addr = %x, col_addr = %x\n",
+ command, page_addr, column);
+#endif
+
+ /* remap command */
+ switch (command) {
+ case NAND_CMD_RESET:
+ spi_nand_reset_flash();
+ return;
+
+ case NAND_CMD_READID:
+ info->byte_buf[0] = get_flash_ID(0);
+ info->byte_buf[1] = get_flash_ID(1);
+ info->index = 0;
+#ifdef NAND_DEBUG
+ printf("READID: id = %x \n", info->byte_buf[0]);
+#endif
+ break;
+
+ case NAND_CMD_SEQIN: /* 0x80 do nothing, just only transfer write addr */
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = NAND_CMD_NONE;
+ info->write_ptr = column;
+ break;
+ case NAND_CMD_READSTART: /* hw auto gen 0x30 for read */
+ break;
+
+ case NAND_CMD_ERASE1:
+ disable_flash_protection(0);
+ if (NANDFC_SPI_ERASE_FAIL ==
+ spi_nand_flash_block_erase(page_addr))
+ printf("spi nand: erase failed!page addr = %x\n",
+ page_addr);
+ break;
+
+ case NAND_CMD_ERASE2:
+#ifdef NAND_DEBUG
+ printf("erase block complete!\n");
+#endif
+ info->cmd_flag = NAND_CMD_ERASE2;
+ info->cmd = NAND_CMD_NONE;
+ break;
+
+ case NAND_CMD_PAGEPROG: /* 0x10 do the real program */
+ disable_flash_protection(0);
+ if (NANDFC_SPI_PROG_FAIL ==
+ spi_nand_flash_program_execute(info->page_addr))
+ printf("spi nand: program failed!page addr = %x\n",
+ page_addr);
+ info->cmd_flag = NAND_CMD_PAGEPROG;
+ info->cmd = NAND_CMD_NONE;
+ info->col_addr = 0;
+ info->write_ptr = 0;
+
+ //spi_nand_flash_cache_flush(mtd, info->page_addr);
+ break;
+
+ case NAND_CMD_READOOB: /* Emulate NAND_CMD_READOOB */
+ info->page_addr = page_addr;
+ info->col_addr = column + mtd->writesize;
+ info->cmd = NAND_CMD_READ0;
+ /* Offset of oob */
+ info->read_ptr = info->col_addr;
+ /*read page data to cache */
+ spi_nand_read_flash_data2buffer(mtd, page_addr);
+ //spi_nand_flash_page_read2cache(page_addr);
+ spi_nand_flash_read_from_cache(&g_spi_flash_read_temp_buffer
+ [mtd->writesize], mtd->writesize,
+ mtd->oobsize);
+
+ /*gigadevice flash hardware reserved block, add to BBT, 0xffe0000, 0x1ffc0000 */
+ if ((0xffe0000 == page_addr * mtd->writesize && info->type == SPINAND_TYPE_SPI2K)
+ || (0x1ffc0000 == info->page_addr * mtd->writesize && info->type == SPINAND_TYPE_SPI4K))
+ memset(&g_spi_flash_read_temp_buffer[0], 0,
+ mtd->writesize + mtd->oobsize);
+
+ info->byte_buf[0] =
+ g_spi_flash_read_temp_buffer[mtd->writesize];
+ info->index = 0;
+
+#ifdef NAND_DEBUG
+ printf("read flash, middle buf-oob0:\n");
+ rda_dump_buf((char *)
+ &g_spi_flash_read_temp_buffer[mtd->writesize],
+ mtd->oobsize);
+#endif
+ break;
+
+ case NAND_CMD_READ0: /* Emulate NAND_CMD_READOOB */
+ info->read_ptr = 0;
+ info->page_addr = page_addr;
+#ifdef READ_PAGE_DATA_USE_MIDDLE_BUFFER
+ /*read page data to cache */
+ spi_nand_read_flash_data2buffer(mtd, page_addr);
+ /*read OOB to cache */
+ //spi_nand_flash_page_read2cache(page_addr);
+ spi_nand_flash_read_from_cache(&g_spi_flash_read_temp_buffer
+ [mtd->writesize], mtd->writesize,
+ mtd->oobsize);
+
+ /*gigadevice flash hardware reserved block, add to BBT, 0xffe0000, 0x1ffc0000 */
+ if ((0xffe0000 == page_addr * mtd->writesize && info->type == SPINAND_TYPE_SPI2K)
+ || (0x1ffc0000 == info->page_addr * mtd->writesize && info->type == SPINAND_TYPE_SPI4K))
+ memset(&g_spi_flash_read_temp_buffer[0], 0,
+ mtd->writesize + mtd->oobsize);
+#endif/*READ_PAGE_DATA_USE_MIDDLE_BUFFER*/
+
+#ifdef NAND_DEBUG
+ printf("read flash, middle buf-oob1:\n");
+ rda_dump_buf((char *)
+ &g_spi_flash_read_temp_buffer[mtd->writesize],
+ mtd->oobsize);
+#endif
+ break;
+
+ default:
+ info->page_addr = page_addr;
+ info->col_addr = column;
+ info->cmd = command;
+ break;
+ }
+
+ if (info->cmd == NAND_CMD_NONE) {
+ return;
+ }
+
+ nand_rda_do_cmd_post(mtd);
+}
+
+static int spi_nand_rda_dev_ready(struct mtd_info *mtd)
+{
+ return 1;
+}
+
+static void nand_rda_select_chip(struct mtd_info *mtd, int chip){}
+
+static int spi_nand_rda_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+#ifdef NAND_DEBUG
+ printf("function: spi_nand_rda_wait\n");
+#endif
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+
+ return (int)chip->read_byte(mtd);
+}
+
+static int spi_nand_read_id(unsigned int id[2])
+{
+ id[0] = get_flash_ID(0);
+ id[1] = get_flash_ID(0);
+
+ printf("Nand ID: %08x %08x\n", id[0], id[1]);
+
+ return 0;
+}
+
+static void read_ID(struct mtd_info *mtd)
+{
+ unsigned int id[2];
+
+ spi_nand_read_id(id);
+}
+
+#define RDA_HW_CFG_BIT_7 (1 << 7)
+#define RDA_HW_CFG_BIT_4 (1 << 4)
+#define RDA_HW_CFG_BIT_3 (1 << 3)
+
+#if defined(CONFIG_MACH_RDA8810)
+static int spi_nand_get_type(struct rda_nand_info *info, unsigned int id[2],
+ int *rda_nand_type, int *bus_width_16)
+{
+ int metal_id;
+
+ info->spl_adjust_ratio = 2;
+ metal_id = rda_metal_id_get();
+ printf("get type, metal %d\n", metal_id);
+
+ if (metal_id >= 7) {
+ if(id[0] == 0xc4c8 || id[0] == 0xd4c8){
+ *rda_nand_type = SPINAND_TYPE_SPI4K;
+ info->spl_adjust_ratio = 4; //actually ratio * 2
+ }else{
+ *rda_nand_type = SPINAND_TYPE_SPI2K;
+ }
+
+ *bus_width_16 = 0;
+ } else {
+ printf("invalid metal id %d\n", metal_id);
+ return -ENODEV;
+ }
+
+ printf("SPL adjust ratio is %d. \n", info->spl_adjust_ratio);
+ return 0;
+}
+#else /* 8810E, 8820, 8850 */
+static int spi_nand_get_type(struct rda_nand_info *info, unsigned int id[2],
+ int *rda_nand_type, int *bus_width_16)
+{
+ int metal_id;
+
+ info->spl_adjust_ratio = 2;
+ metal_id = rda_metal_id_get();
+ printf("get type, metal %d\n", metal_id);
+
+ if(id[0] == 0xc4c8 || id[0] == 0xd4c8){
+ *rda_nand_type = SPINAND_TYPE_SPI4K;
+ info->spl_adjust_ratio = 4; //actually ratio * 2
+ }else{
+ *rda_nand_type = SPINAND_TYPE_SPI2K;
+ }
+
+ *bus_width_16 = 0;
+
+ printf("SPL adjust ratio is %d. \n", info->spl_adjust_ratio);
+ return 0;
+}
+#endif
+
+static u32 spi_nand_cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2)
+{
+ u32 newfreq;
+
+ if (reg >= ARRAY_SIZE(clk_div_map)) {
+ printf("nand:Invalid div reg: %u\n", reg);
+ reg = ARRAY_SIZE(clk_div_map) - 1;
+ }
+ /* Assuming basefreq is smaller than 2^31 (2.147G Hz) */
+ newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1);
+ return newfreq;
+}
+
+static unsigned long spi_nand_get_master_clk_rate(u32 reg)
+{
+ u32 div2;
+ unsigned long rate;
+
+ div2 = reg & SYS_CTRL_AP_AP_SFLSH_SRC_DIV2;
+ reg = GET_BITFIELD(reg, SYS_CTRL_AP_AP_SFLSH_FREQ);
+ rate = spi_nand_cal_freq_by_divreg(PLL_BUS_FREQ, reg, div2);
+
+ return rate;
+}
+
+static unsigned char spi_nand_calc_divider(struct rda_nand_info *info)
+{
+ unsigned long mclk = info->master_clk;
+ unsigned char div;
+ unsigned long real_clk;
+
+ div = mclk / info->clk;
+ if (mclk % info->clk)
+ div += 1;
+
+ if (div < 2) {
+ /* 2 is minimal divider by hardware */
+ div = 2;
+ }
+
+ if (div > 255) {
+ /* 255 is max divider by hardware */
+ div = 255;
+ }
+
+ real_clk = mclk / div;
+ printf("Spi nand real clock is %ld\n", real_clk);
+ return div;
+}
+
+static unsigned char spi_nand_calc_delay(struct rda_nand_info *info)
+{/*the value need check in future!*/
+ unsigned char delay;
+
+#ifndef _TGT_AP_SPI_NAND_READDELAY
+ unsigned long mclk = info->master_clk;
+ unsigned short clk_val;
+
+ clk_val = mclk / 1000000;
+ if (clk_val <= 220)
+ delay = 2;
+ else if (clk_val <= 280)
+ delay = 3;
+ else if (clk_val <= 350)
+ delay = 4;
+ else if (clk_val <= 420)
+ delay = 5;
+ else
+ delay = 6;
+#else
+ delay = _TGT_AP_SPI_NAND_READDELAY;
+#endif
+
+ printf("read delay:%d\n", delay);
+ return delay;
+}
+
+static int spi_nand_rda_init(struct nand_chip *this, struct rda_nand_info *info)
+{
+ unsigned int id[2];
+ int ret = 0;
+ int rda_nand_type = SPINAND_TYPE_INVALID;
+ int bus_width_16 = 0;
+ BOOL spi_nand_quad_mode = TRUE;
+ unsigned char spi_read_delay = 2; /*AP_CLK_SFLSH clock cycles*/
+ unsigned char spi_controller_div = 4; /*2~8*/
+
+ ret = spi_nand_reset_flash();
+ if (ret)
+ return ret;
+ ret = spi_nand_read_id(id);
+ if (ret)
+ return ret;
+ ret = spi_nand_get_type(info, id, &rda_nand_type, &bus_width_16);
+ if (ret)
+ return ret;
+ ret = init_spi_nand_info(info, rda_nand_type, bus_width_16);
+ if (ret)
+ return ret;
+ if (info->bus_width_16)
+ this->options |= NAND_BUSWIDTH_16;
+
+ spi_controller_div = spi_nand_calc_divider(info);
+ spi_read_delay = spi_nand_calc_delay(info);
+ //nand_spi_init(1, 4, 3);
+ //nand_spi_init(1, 2, 4);
+ nand_spi_init(spi_nand_quad_mode, spi_read_delay, spi_controller_div);
+
+
+ return 0;
+}
+
+static int spi_nand_rda_init_size(struct mtd_info *mtd, struct nand_chip *this,
+ u8 * id_data)
+{
+ struct rda_nand_info *info = this->priv;
+
+ mtd->erasesize = info->vir_erase_size;
+ mtd->writesize = info->vir_page_size;
+ mtd->oobsize = info->vir_oob_size;
+
+ return (info->bus_width_16) ? NAND_BUSWIDTH_16 : 0;
+}
+
+int rda_spi_nand_init(struct nand_chip *nand)
+{
+ struct rda_nand_info *info;
+ static struct rda_nand_info rda_nand_info;
+
+ info = &rda_nand_info;
+
+ nand->chip_delay = 0;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->options |= NAND_USE_FLASH_BBT;
+#endif
+#ifdef NAND_DEBUG
+ printf("SPINAND: nand_options = %x\n", nand->options);
+#endif
+ /*
+ * in fact, nand controler we do hardware ECC silently, and
+ * we don't need tell mtd layer, and will simple nand operations
+ */
+ nand->ecc.mode = NAND_ECC_NONE;
+ /* Set address of hardware control function */
+ nand->cmd_ctrl = spi_nand_rda_hwcontrol;
+ nand->init_size = spi_nand_rda_init_size;
+ nand->cmdfunc = spi_nand_rda_cmdfunc;
+ nand->read_byte = spi_nand_rda_read_byte;
+ nand->read_word = spi_nand_rda_read_word;
+ nand->read_buf = spi_nand_rda_read_buf;
+ nand->write_buf = spi_nand_rda_write_buf;
+ nand->dev_ready = spi_nand_rda_dev_ready;
+ nand->waitfunc = spi_nand_rda_wait;
+ nand->select_chip = nand_rda_select_chip;
+ nand->read_nand_ID = read_ID;
+ nand->priv = (void *)info;
+ nand->IO_ADDR_R = (void __iomem *)SPI_NAND_READ_DATA_BUFFER_BASE;
+ nand->IO_ADDR_W = (void __iomem *)SPI_NAND_WRITE_DATA_BUFFER_BASE;
+ info->index = 0;
+ info->write_ptr = 0;
+ info->read_ptr = 0;
+ info->cmd_flag = NAND_CMD_NONE;
+ info->master_clk = spi_nand_get_master_clk_rate(_TGT_AP_CLK_SFLSH);
+ info->clk = _TGT_AP_SPI_NAND_CLOCK;
+
+#ifdef CONFIG_NAND_RDA_DMA
+ rda_request_dma(&info->dma_ch);
+#endif /* CONFIG_NAND_RDA_DMA */
+
+ spi_nand_rda_init(nand, info);
+
+ if (!nand->ecc.layout && (nand->ecc.mode != NAND_ECC_SOFT_BCH)) {
+ switch (info->vir_oob_size) {
+ case 64:
+ nand->ecc.layout = &spi_nand_oob_64;
+ break;
+ case 128:
+ nand->ecc.layout = &spi_nand_oob_128;
+ break;
+ default:
+ printf("error oobsize: %d\n",
+ info->vir_oob_size);
+ }
+ }
+
+ if (nand->options & NAND_USE_FLASH_BBT) {
+ /* Use the default pattern descriptors */
+ if (!nand->bbt_td) {
+ nand->bbt_td = &spi_bbt_main_descr;
+ nand->bbt_md = &spi_bbt_mirror_descr;
+ }
+ }
+
+ printf("SPINAND: Nand Init Done\n");
+ return 0;
+}
+
+#else
+int rda_spi_nand_init(struct nand_chip *nand){return 0;}
+#endif
+
+#if 0
+/* move to rda_nand_base.c */
+int board_nand_init(struct nand_chip *chip) __attribute__ ((weak));
+
+int board_nand_init(struct nand_chip *chip)
+{
+ return rda_spi_nand_init(chip);
+}
+#endif
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 1d0f196c12..1a7b40eaa3 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -20,7 +20,7 @@
*/
#include <common.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index 1354877729..9d5da54708 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -15,7 +15,7 @@
*/
#include <common.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <malloc.h>
diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c
index c642016c2d..ae60c3bb71 100644
--- a/drivers/mtd/onenand/onenand_uboot.c
+++ b/drivers/mtd/onenand/onenand_uboot.c
@@ -14,7 +14,7 @@
*/
#include <common.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index c9d33ec825..0d94ea5b1f 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -28,7 +28,7 @@
#include <common.h>
#include <malloc.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <linux/mtd/samsung_onenand.h>
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index d144ac29bc..ca169a83e0 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -476,21 +476,21 @@ static int attach_by_scanning(struct ubi_device *ubi)
if (err)
goto out_si;
- err = ubi_eba_init_scan(ubi, si);
- if (err)
- goto out_wl;
-
err = ubi_wl_init_scan(ubi, si);
if (err)
goto out_vtbl;
+ err = ubi_eba_init_scan(ubi, si);
+ if (err)
+ goto out_wl;
+
ubi_scan_destroy_si(si);
return 0;
-out_vtbl:
- vfree(ubi->vtbl);
out_wl:
ubi_wl_close(ubi);
+out_vtbl:
+ vfree(ubi->vtbl);
out_si:
ubi_scan_destroy_si(si);
return err;
@@ -550,19 +550,18 @@ static int io_init(struct ubi_device *ubi)
* fundamental reason for this assumption. It is just an optimization
* which allows us to avoid costly division operations.
*/
- if (!is_power_of_2(ubi->min_io_size)) {
- ubi_err("min. I/O unit (%d) is not power of 2",
- ubi->min_io_size);
- return -EINVAL;
- }
-
ubi_assert(ubi->hdrs_min_io_size > 0);
ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size);
ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0);
/* Calculate default aligned sizes of EC and VID headers */
- ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
- ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
+ if (is_power_of_2(ubi->hdrs_min_io_size)) {
+ ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
+ ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
+ } else {
+ ubi->ec_hdr_alsize = roundup(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
+ ubi->vid_hdr_alsize = roundup(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
+ }
dbg_msg("min_io_size %d", ubi->min_io_size);
dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size);
@@ -574,15 +573,22 @@ static int io_init(struct ubi_device *ubi)
ubi->vid_hdr_offset = ubi->vid_hdr_aloffset =
ubi->ec_hdr_alsize;
else {
- ubi->vid_hdr_aloffset = ubi->vid_hdr_offset &
+ if (is_power_of_2(ubi->hdrs_min_io_size))
+ ubi->vid_hdr_aloffset = ubi->vid_hdr_offset &
~(ubi->hdrs_min_io_size - 1);
+ else
+ ubi->vid_hdr_aloffset = (ubi->vid_hdr_offset / ubi->hdrs_min_io_size) *
+ ubi->hdrs_min_io_size;
ubi->vid_hdr_shift = ubi->vid_hdr_offset -
ubi->vid_hdr_aloffset;
}
/* Similar for the data offset */
ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE;
- ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
+ if (is_power_of_2(ubi->min_io_size))
+ ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
+ else
+ ubi->leb_start = roundup(ubi->leb_start, ubi->min_io_size);
dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset);
dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset);
@@ -597,13 +603,24 @@ static int io_init(struct ubi_device *ubi)
}
/* Check sanity */
- if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
- ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
- ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
- ubi->leb_start & (ubi->min_io_size - 1)) {
- ubi_err("bad VID header (%d) or data offsets (%d)",
- ubi->vid_hdr_offset, ubi->leb_start);
- return -EINVAL;
+ if (is_power_of_2(ubi->min_io_size)) {
+ if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
+ ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
+ ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
+ ubi->leb_start & (ubi->min_io_size - 1)) {
+ ubi_err("bad VID header (%d) or data offsets (%d)",
+ ubi->vid_hdr_offset, ubi->leb_start);
+ return -EINVAL;
+ }
+ } else {
+ if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
+ ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
+ ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
+ (ubi->leb_start % ubi->min_io_size)) {
+ ubi_err("bad VID header (%d) or data offsets (%d)",
+ ubi->vid_hdr_offset, ubi->leb_start);
+ return -EINVAL;
+ }
}
/*
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index d523c94b12..a58b9de06d 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -752,11 +752,19 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
if (ubi->ro_mode)
return -EROFS;
- if (lnum == used_ebs - 1)
- /* If this is the last LEB @len may be unaligned */
- len = ALIGN(data_size, ubi->min_io_size);
- else
- ubi_assert(!(len & (ubi->min_io_size - 1)));
+ if (is_power_of_2(ubi->min_io_size)) {
+ if (lnum == used_ebs - 1)
+ /* If this is the last LEB @len may be unaligned */
+ len = ALIGN(data_size, ubi->min_io_size);
+ else
+ ubi_assert(!(len & (ubi->min_io_size - 1)));
+ } else {
+ if (lnum == used_ebs - 1)
+ /* If this is the last LEB @len may be unaligned */
+ len = roundup(data_size, ubi->min_io_size);
+ else
+ ubi_assert(!(len % ubi->min_io_size));
+ }
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
if (!vid_hdr)
@@ -988,7 +996,10 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
if (vid_hdr->vol_type == UBI_VID_STATIC) {
data_size = be32_to_cpu(vid_hdr->data_size);
- aldata_size = ALIGN(data_size, ubi->min_io_size);
+ if (is_power_of_2(ubi->min_io_size))
+ aldata_size = ALIGN(data_size, ubi->min_io_size);
+ else
+ aldata_size = roundup(data_size, ubi->min_io_size);
} else
data_size = aldata_size =
ubi->leb_size - be32_to_cpu(vid_hdr->data_pad);
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index 423d479152..df67cde5af 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -400,10 +400,17 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
- offset + len > vol->usable_leb_size ||
- offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
+ if (is_power_of_2(ubi->min_io_size)) {
+ if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
+ offset + len > vol->usable_leb_size ||
+ offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
+ return -EINVAL;
+ } else {
+ if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
+ offset + len > vol->usable_leb_size ||
+ (offset % ubi->min_io_size) || (len % ubi->min_io_size))
return -EINVAL;
+ }
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
dtype != UBI_UNKNOWN)
@@ -450,9 +457,17 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
- if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
- len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
- return -EINVAL;
+ if (is_power_of_2(ubi->min_io_size)) {
+ if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
+ len > vol->usable_leb_size ||
+ len & (ubi->min_io_size - 1))
+ return -EINVAL;
+ } else {
+ if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
+ len > vol->usable_leb_size ||
+ (len % ubi->min_io_size))
+ return -EINVAL;
+ }
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
dtype != UBI_UNKNOWN)
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
index a6410bfb6b..97d806b924 100644
--- a/drivers/mtd/ubi/misc.c
+++ b/drivers/mtd/ubi/misc.c
@@ -38,14 +38,20 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
{
int i;
- ubi_assert(!(length & (ubi->min_io_size - 1)));
+ if (is_power_of_2(ubi->min_io_size))
+ ubi_assert(!(length & (ubi->min_io_size - 1)));
+ else
+ ubi_assert(!(length % ubi->min_io_size));
for (i = length - 1; i >= 0; i--)
if (((const uint8_t *)buf)[i] != 0xFF)
break;
/* The resulting length must be aligned to the minimum flash I/O size */
- length = ALIGN(i + 1, ubi->min_io_size);
+ if (is_power_of_2(ubi->min_io_size))
+ length = ALIGN(i + 1, ubi->min_io_size);
+ else
+ length = roundup(i + 1, ubi->min_io_size);
return length;
}
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
index 5f7ed7b2ef..0c201ceca1 100644
--- a/drivers/mtd/ubi/upd.c
+++ b/drivers/mtd/ubi/upd.c
@@ -241,7 +241,12 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int err;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
- int l = ALIGN(len, ubi->min_io_size);
+ int l;
+
+ if (is_power_of_2(ubi->min_io_size))
+ l = ALIGN(len, ubi->min_io_size);
+ else
+ l = roundup(len, ubi->min_io_size);
memset(buf + len, 0xFF, l - len);
len = ubi_calc_data_len(ubi, buf, l);
@@ -420,7 +425,11 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
vol->upd_received += count;
if (vol->upd_received == vol->upd_bytes) {
- int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size);
+ int len;
+ if (is_power_of_2(ubi->min_io_size))
+ len = ALIGN((int)vol->upd_bytes, ubi->min_io_size);
+ else
+ len = roundup((int)vol->upd_bytes, ubi->min_io_size);
memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes);
len = ubi_calc_data_len(ubi, vol->upd_buf, len);
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index ce9fec7fc9..f63730b9fd 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -732,8 +732,10 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
ubi_err("bad alignment");
goto fail;
}
-
- n = vol->alignment & (ubi->min_io_size - 1);
+ if (is_power_of_2(ubi->min_io_size))
+ n = vol->alignment & (ubi->min_io_size - 1);
+ else
+ n = vol->alignment % ubi->min_io_size;
if (vol->alignment != 1 && n) {
ubi_err("alignment is not multiple of min I/O unit");
goto fail;
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index f679f06494..48cb62786c 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -174,7 +174,10 @@ static int vtbl_check(const struct ubi_device *ubi,
goto bad;
}
- n = alignment & (ubi->min_io_size - 1);
+ if (is_power_of_2(ubi->min_io_size))
+ n = alignment & (ubi->min_io_size - 1);
+ else
+ n = alignment % ubi->min_io_size;
if (alignment != 1 && n) {
err = 5;
goto bad;
@@ -758,7 +761,10 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
ubi->vtbl_slots = UBI_MAX_VOLUMES;
ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE;
- ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size);
+ if (is_power_of_2(ubi->min_io_size))
+ ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size);
+ else
+ ubi->vtbl_size = roundup(ubi->vtbl_size, ubi->min_io_size);
sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
if (!sv) {
diff --git a/drivers/net/4xx_enet.c b/drivers/net/4xx_enet.c
index 73700dd0df..7c6e362f0e 100644
--- a/drivers/net/4xx_enet.c
+++ b/drivers/net/4xx_enet.c
@@ -1557,8 +1557,7 @@ get_speed:
}
-static int ppc_4xx_eth_send (struct eth_device *dev, volatile void *ptr,
- int len)
+static int ppc_4xx_eth_send(struct eth_device *dev, void *ptr, int len)
{
struct enet_frame *ef_ptr;
ulong time_start, time_now;
diff --git a/drivers/net/altera_tse.c b/drivers/net/altera_tse.c
index 0e6aac7173..de517f8dab 100644
--- a/drivers/net/altera_tse.c
+++ b/drivers/net/altera_tse.c
@@ -257,8 +257,7 @@ static int tse_adjust_link(struct altera_tse_priv *priv)
return 0;
}
-static int tse_eth_send(struct eth_device *dev,
- volatile void *packet, int length)
+static int tse_eth_send(struct eth_device *dev, void *packet, int length)
{
struct altera_tse_priv *priv = dev->priv;
volatile struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx;
diff --git a/drivers/net/armada100_fec.c b/drivers/net/armada100_fec.c
index 52be3a7195..d318a36398 100644
--- a/drivers/net/armada100_fec.c
+++ b/drivers/net/armada100_fec.c
@@ -558,8 +558,7 @@ static void armdfec_halt(struct eth_device *dev)
clrbits_le32(&regs->pconf, PCR_EN);
}
-static int armdfec_send(struct eth_device *dev, volatile void *dataptr,
- int datasize)
+static int armdfec_send(struct eth_device *dev, void *dataptr, int datasize)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
diff --git a/drivers/net/at91_emac.c b/drivers/net/at91_emac.c
index 483c831c05..2fa6b686da 100644
--- a/drivers/net/at91_emac.c
+++ b/drivers/net/at91_emac.c
@@ -415,8 +415,7 @@ static void at91emac_halt(struct eth_device *netdev)
debug_cond(DEBUG_AT91EMAC, "halt MAC\n");
}
-static int at91emac_send(struct eth_device *netdev, volatile void *packet,
- int length)
+static int at91emac_send(struct eth_device *netdev, void *packet, int length)
{
at91_emac_t *emac;
diff --git a/drivers/net/ax88180.c b/drivers/net/ax88180.c
index bc3e6ad58a..f5017682d4 100644
--- a/drivers/net/ax88180.c
+++ b/drivers/net/ax88180.c
@@ -604,8 +604,7 @@ static int ax88180_recv (struct eth_device *dev)
}
/* Send a data block via Ethernet. */
-static int
-ax88180_send (struct eth_device *dev, volatile void *packet, int length)
+static int ax88180_send(struct eth_device *dev, void *packet, int length)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned short TXDES_addr;
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index dcc781a46a..e24ac4a174 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -118,8 +118,7 @@ int bfin_EMAC_initialize(bd_t *bis)
return 0;
}
-static int bfin_EMAC_send(struct eth_device *dev, volatile void *packet,
- int length)
+static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length)
{
int i;
int result = 0;
@@ -471,7 +470,7 @@ int ether_post_test(int flags)
for (i = 0; i < 42; i++)
buf[i + 22] = i;
printf("--------Send 64 bytes......\n");
- bfin_EMAC_send(NULL, (volatile void *)buf, 64);
+ bfin_EMAC_send(NULL, buf, 64);
for (i = 0; i < 100; i++) {
udelay(10000);
if ((rxbuf[rxIdx]->StatusWord & RX_COMP) != 0) {
diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h
index c731c179b5..54ffb3830e 100644
--- a/drivers/net/bfin_mac.h
+++ b/drivers/net/bfin_mac.h
@@ -58,7 +58,7 @@ static ADI_ETHER_BUFFER *SetupTxBuffer(int no);
static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd);
static void bfin_EMAC_halt(struct eth_device *dev);
-static int bfin_EMAC_send(struct eth_device *dev, volatile void *packet, int length);
+static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length);
static int bfin_EMAC_recv(struct eth_device *dev);
static int bfin_EMAC_setup_addr(struct eth_device *dev);
diff --git a/drivers/net/calxedaxgmac.c b/drivers/net/calxedaxgmac.c
index 00e26c2adc..e3553d68d8 100644
--- a/drivers/net/calxedaxgmac.c
+++ b/drivers/net/calxedaxgmac.c
@@ -434,7 +434,7 @@ static int xgmac_init(struct eth_device *dev, bd_t * bis)
return 0;
}
-static int xgmac_tx(struct eth_device *dev, volatile void *packet, int length)
+static int xgmac_tx(struct eth_device *dev, void *packet, int length)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
struct calxeda_eth_dev *priv = dev->priv;
@@ -442,7 +442,7 @@ static int xgmac_tx(struct eth_device *dev, volatile void *packet, int length)
struct xgmac_dma_desc *txdesc = &priv->tx_chain[currdesc];
int timeout;
- desc_set_buf_addr_and_size(txdesc, (void *)packet, length);
+ desc_set_buf_addr_and_size(txdesc, packet, length);
desc_set_tx_owner(txdesc, TXDESC_FIRST_SEG |
TXDESC_LAST_SEG | TXDESC_CRC_EN_APPEND);
@@ -476,7 +476,7 @@ static int xgmac_rx(struct eth_device *dev)
length = desc_get_rx_frame_len(rxdesc);
- NetReceive((volatile unsigned char *)desc_get_buf_addr(rxdesc), length);
+ NetReceive(desc_get_buf_addr(rxdesc), length);
/* set descriptor back to owned by XGMAC */
desc_set_rx_owner(rxdesc);
diff --git a/drivers/net/cs8900.c b/drivers/net/cs8900.c
index e04a784a4b..6aaa0cf49f 100644
--- a/drivers/net/cs8900.c
+++ b/drivers/net/cs8900.c
@@ -215,8 +215,7 @@ static int cs8900_recv(struct eth_device *dev)
}
/* Send a data block via Ethernet. */
-static int cs8900_send(struct eth_device *dev,
- volatile void *packet, int length)
+static int cs8900_send(struct eth_device *dev, void *packet, int length)
{
volatile u16 *addr;
int tmo;
diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index fbd0f1b7b5..e471d2ce33 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -637,7 +637,7 @@ static int tx_send_loop = 0;
* positive number (number of bytes transmitted) or negative for error
*/
static int davinci_eth_send_packet (struct eth_device *dev,
- volatile void *packet, int length)
+ void *packet, int length)
{
int ret_status = -1;
int index;
diff --git a/drivers/net/dc2114x.c b/drivers/net/dc2114x.c
index 0fb616ac20..d9fcb5a13e 100644
--- a/drivers/net/dc2114x.c
+++ b/drivers/net/dc2114x.c
@@ -175,7 +175,7 @@ static void read_hw_addr(struct eth_device* dev, bd_t * bis);
static void send_setup_frame(struct eth_device* dev, bd_t * bis);
static int dc21x4x_init(struct eth_device* dev, bd_t* bis);
-static int dc21x4x_send(struct eth_device* dev, volatile void *packet, int length);
+static int dc21x4x_send(struct eth_device *dev, void *packet, int length);
static int dc21x4x_recv(struct eth_device* dev);
static void dc21x4x_halt(struct eth_device* dev);
#ifdef CONFIG_TULIP_SELECT_MEDIA
@@ -390,7 +390,7 @@ static int dc21x4x_init(struct eth_device* dev, bd_t* bis)
return 0;
}
-static int dc21x4x_send(struct eth_device* dev, volatile void *packet, int length)
+static int dc21x4x_send(struct eth_device *dev, void *packet, int length)
{
int status = -1;
int i;
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index e8e669bd90..9b17db41f6 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -189,8 +189,7 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis)
return 0;
}
-static int dw_eth_send(struct eth_device *dev, volatile void *packet,
- int length)
+static int dw_eth_send(struct eth_device *dev, void *packet, int length)
{
struct dw_eth_dev *priv = dev->priv;
struct eth_dma_regs *dma_p = priv->dma_regs_p;
@@ -203,7 +202,7 @@ static int dw_eth_send(struct eth_device *dev, volatile void *packet,
return -1;
}
- memcpy((void *)desc_p->dmamac_addr, (void *)packet, length);
+ memcpy((void *)desc_p->dmamac_addr, packet, length);
#if defined(CONFIG_DW_ALTDESCRIPTOR)
desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST;
diff --git a/drivers/net/dm9000x.c b/drivers/net/dm9000x.c
index 04241109cc..f0c4499edb 100644
--- a/drivers/net/dm9000x.c
+++ b/drivers/net/dm9000x.c
@@ -406,8 +406,7 @@ static int dm9000_init(struct eth_device *dev, bd_t *bd)
Hardware start transmission.
Send a packet to media from the upper layer.
*/
-static int dm9000_send(struct eth_device *netdev, volatile void *packet,
- int length)
+static int dm9000_send(struct eth_device *netdev, void *packet, int length)
{
int tmo;
struct board_info *db = &dm9000_info;
diff --git a/drivers/net/dnet.c b/drivers/net/dnet.c
index 15d0a6e741..944a0c046f 100644
--- a/drivers/net/dnet.c
+++ b/drivers/net/dnet.c
@@ -130,8 +130,7 @@ static u16 dnet_mdio_read(struct dnet_device *dnet, u8 reg)
return value;
}
-static int dnet_send(struct eth_device *netdev, volatile void *packet,
- int length)
+static int dnet_send(struct eth_device *netdev, void *packet, int length)
{
struct dnet_device *dnet = to_dnet(netdev);
int i, wrsz;
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c
index e726f398d4..94b2a41e14 100644
--- a/drivers/net/e1000.c
+++ b/drivers/net/e1000.c
@@ -5031,10 +5031,9 @@ e1000_poll(struct eth_device *nic)
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
-static int
-e1000_transmit(struct eth_device *nic, volatile void *packet, int length)
+static int e1000_transmit(struct eth_device *nic, void *packet, int length)
{
- void * nv_packet = (void *)packet;
+ void *nv_packet = (void *)packet;
struct e1000_hw *hw = nic->priv;
struct e1000_tx_desc *txp;
int i = 0;
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index 07ec34cbba..d2c8277de9 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -242,8 +242,7 @@ static void purge_tx_ring (struct eth_device *dev);
static void read_hw_addr (struct eth_device *dev, bd_t * bis);
static int eepro100_init (struct eth_device *dev, bd_t * bis);
-static int eepro100_send (struct eth_device *dev, volatile void *packet,
- int length);
+static int eepro100_send(struct eth_device *dev, void *packet, int length);
static int eepro100_recv (struct eth_device *dev);
static void eepro100_halt (struct eth_device *dev);
@@ -608,7 +607,7 @@ static int eepro100_init (struct eth_device *dev, bd_t * bis)
return status;
}
-static int eepro100_send (struct eth_device *dev, volatile void *packet, int length)
+static int eepro100_send(struct eth_device *dev, void *packet, int length)
{
int i, status = -1;
int tx_cur;
@@ -691,7 +690,7 @@ static int eepro100_recv (struct eth_device *dev)
/* Pass the packet up to the protocol
* layers.
*/
- NetReceive (rx_ring[rx_next].data, length);
+ NetReceive((u8 *)rx_ring[rx_next].data, length);
} else {
/* There was an error.
*/
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index e2011aef4a..c55667c931 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -875,7 +875,7 @@ static int enc_recv(struct eth_device *dev)
*/
static int enc_send(
struct eth_device *dev,
- volatile void *packet,
+ void *packet,
int length)
{
enc_dev_t *enc = dev->priv;
diff --git a/drivers/net/ep93xx_eth.c b/drivers/net/ep93xx_eth.c
index c09384c632..245ad1c25d 100644
--- a/drivers/net/ep93xx_eth.c
+++ b/drivers/net/ep93xx_eth.c
@@ -380,7 +380,7 @@ static int ep93xx_eth_rcv_packet(struct eth_device *dev)
* Send a block of data via ethernet.
*/
static int ep93xx_eth_send_packet(struct eth_device *dev,
- volatile void * const packet, int const length)
+ void * const packet, int const length)
{
struct mac_regs *mac = GET_REGS(dev);
struct ep93xx_priv *priv = GET_PRIV(dev);
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index 5824fd6827..af06d4fb82 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -414,7 +414,7 @@ static void ethoc_tx(struct eth_device *dev)
(void)ethoc_update_tx_stats(&bd);
}
-static int ethoc_send(struct eth_device *dev, volatile void *packet, int length)
+static int ethoc_send(struct eth_device *dev, void *packet, int length)
{
struct ethoc *priv = (struct ethoc *)dev->priv;
struct ethoc_bd bd;
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
index d8db9f0c6e..eee41d7c8b 100644
--- a/drivers/net/fec_mxc.c
+++ b/drivers/net/fec_mxc.c
@@ -187,9 +187,10 @@ int fec_phy_write(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr,
#ifndef CONFIG_PHYLIB
static int miiphy_restart_aneg(struct eth_device *dev)
{
+ int ret = 0;
+#if !defined(CONFIG_FEC_MXC_NO_ANEG)
struct fec_priv *fec = (struct fec_priv *)dev->priv;
struct ethernet_regs *eth = fec->bus->priv;
- int ret = 0;
/*
* Wake up from sleep if necessary
@@ -213,6 +214,7 @@ static int miiphy_restart_aneg(struct eth_device *dev)
if (fec->mii_postcall)
ret = fec->mii_postcall(fec->phy_id);
+#endif
return ret;
}
@@ -398,6 +400,42 @@ static void fec_eth_phy_config(struct eth_device *dev)
#endif
}
+/*
+ * Do initial configuration of the FEC registers
+ */
+static void fec_reg_setup(struct fec_priv *fec)
+{
+ uint32_t rcntrl;
+
+ /*
+ * Set interrupt mask register
+ */
+ writel(0x00000000, &fec->eth->imask);
+
+ /*
+ * Clear FEC-Lite interrupt event register(IEVENT)
+ */
+ writel(0xffffffff, &fec->eth->ievent);
+
+
+ /*
+ * Set FEC-Lite receive control register(R_CNTRL):
+ */
+
+ /* Start with frame length = 1518, common for all modes. */
+ rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT;
+ if (fec->xcv_type == SEVENWIRE)
+ rcntrl |= FEC_RCNTRL_FCE;
+ else if (fec->xcv_type == RGMII)
+ rcntrl |= FEC_RCNTRL_RGMII;
+ else if (fec->xcv_type == RMII)
+ rcntrl |= FEC_RCNTRL_RMII;
+ else /* MII mode */
+ rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE;
+
+ writel(rcntrl, &fec->eth->r_cntrl);
+}
+
/**
* Start the FEC engine
* @param[in] dev Our device to handle
@@ -512,7 +550,6 @@ static int fec_init(struct eth_device *dev, bd_t* bd)
{
struct fec_priv *fec = (struct fec_priv *)dev->priv;
uint32_t mib_ptr = (uint32_t)&fec->eth->rmon_t_drop;
- uint32_t rcntrl;
uint32_t size;
int i, ret;
@@ -560,33 +597,7 @@ static int fec_init(struct eth_device *dev, bd_t* bd)
(unsigned)fec->rbd_base + size);
}
- /*
- * Set interrupt mask register
- */
- writel(0x00000000, &fec->eth->imask);
-
- /*
- * Clear FEC-Lite interrupt event register(IEVENT)
- */
- writel(0xffffffff, &fec->eth->ievent);
-
-
- /*
- * Set FEC-Lite receive control register(R_CNTRL):
- */
-
- /* Start with frame length = 1518, common for all modes. */
- rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT;
- if (fec->xcv_type == SEVENWIRE)
- rcntrl |= FEC_RCNTRL_FCE;
- else if (fec->xcv_type == RGMII)
- rcntrl |= FEC_RCNTRL_RGMII;
- else if (fec->xcv_type == RMII)
- rcntrl |= FEC_RCNTRL_RMII;
- else /* MII mode */
- rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE;
-
- writel(rcntrl, &fec->eth->r_cntrl);
+ fec_reg_setup(fec);
if (fec->xcv_type == MII10 || fec->xcv_type == MII100)
fec_mii_setspeed(fec);
@@ -676,7 +687,7 @@ static void fec_halt(struct eth_device *dev)
* @param[in] length Data count in bytes
* @return 0 on success
*/
-static int fec_send(struct eth_device *dev, volatile void *packet, int length)
+static int fec_send(struct eth_device *dev, void *packet, int length)
{
unsigned int status;
uint32_t size;
@@ -933,24 +944,7 @@ static int fec_probe(bd_t *bd, int dev_id, int phy_id, uint32_t base_addr)
udelay(10);
}
- /*
- * Set interrupt mask register
- */
- writel(0x00000000, &fec->eth->imask);
-
- /*
- * Clear FEC-Lite interrupt event register(IEVENT)
- */
- writel(0xffffffff, &fec->eth->ievent);
-
- /*
- * Set FEC-Lite receive control register(R_CNTRL):
- */
- /*
- * Frame length=1518; MII mode;
- */
- writel((PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT) | FEC_RCNTRL_FCE |
- FEC_RCNTRL_MII_MODE, &fec->eth->r_cntrl);
+ fec_reg_setup(fec);
fec_mii_setspeed(fec);
if (dev_id == -1) {
diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c
index f7ed850c91..f34f4db6b6 100644
--- a/drivers/net/fm/eth.c
+++ b/drivers/net/fm/eth.c
@@ -420,7 +420,7 @@ static void fm_eth_halt(struct eth_device *dev)
phy_shutdown(fm_eth->phydev);
}
-static int fm_eth_send(struct eth_device *dev, volatile void *buf, int len)
+static int fm_eth_send(struct eth_device *dev, void *buf, int len)
{
struct fm_eth *fm_eth;
struct fm_port_global_pram *pram;
diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c
index 0b8c33fb7a..49c74c278a 100644
--- a/drivers/net/fm/fm.c
+++ b/drivers/net/fm/fm.c
@@ -408,6 +408,8 @@ int fm_init_common(int index, struct ccsr_fman *reg)
/* flush cache after read */
flush_cache((ulong)addr, cnt * 512);
}
+#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_REMOTE)
+ void *addr = (void *)CONFIG_SYS_QE_FMAN_FW_ADDR;
#endif
/* Upload the Fman microcode if it's present */
diff --git a/drivers/net/fsl_mcdmafec.c b/drivers/net/fsl_mcdmafec.c
index 5330dbc79f..63842cd466 100644
--- a/drivers/net/fsl_mcdmafec.c
+++ b/drivers/net/fsl_mcdmafec.c
@@ -116,7 +116,7 @@ struct fec_info_dma fec_info[] = {
#endif
};
-static int fec_send(struct eth_device *dev, volatile void *packet, int length);
+static int fec_send(struct eth_device *dev, void *packet, int length);
static int fec_recv(struct eth_device *dev);
static int fec_init(struct eth_device *dev, bd_t * bd);
static void fec_halt(struct eth_device *dev);
@@ -194,7 +194,7 @@ static void set_fec_duplex_speed(volatile fecdma_t * fecp, bd_t * bd,
}
}
-static int fec_send(struct eth_device *dev, volatile void *packet, int length)
+static int fec_send(struct eth_device *dev, void *packet, int length)
{
struct fec_info_dma *info = dev->priv;
cbd_t *pTbd, *pUsedTbd;
@@ -301,8 +301,7 @@ static int fec_recv(struct eth_device *dev)
frame_length = pRbd->cbd_datlen - 4;
/* Fill the buffer and pass it to upper layers */
- NetReceive((volatile uchar *)pRbd->cbd_bufaddr,
- frame_length);
+ NetReceive((uchar *)pRbd->cbd_bufaddr, frame_length);
len = frame_length;
}
diff --git a/drivers/net/ftgmac100.c b/drivers/net/ftgmac100.c
index 0af0c3450f..69ba57d3d0 100644
--- a/drivers/net/ftgmac100.c
+++ b/drivers/net/ftgmac100.c
@@ -480,8 +480,7 @@ static int ftgmac100_recv(struct eth_device *dev)
/*
* Send a data block via Ethernet
*/
-static int
-ftgmac100_send(struct eth_device *dev, void *packet, int length)
+static int ftgmac100_send(struct eth_device *dev, void *packet, int length)
{
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
struct ftgmac100_data *priv = dev->priv;
diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
index 94dc6d99ba..fdd3ec0ac2 100644
--- a/drivers/net/ftmac100.c
+++ b/drivers/net/ftmac100.c
@@ -190,8 +190,7 @@ static int ftmac100_recv (struct eth_device *dev)
/*
* Send a data block via Ethernet
*/
-static int
-ftmac100_send (struct eth_device *dev, volatile void *packet, int length)
+static int ftmac100_send(struct eth_device *dev, void *packet, int length)
{
struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase;
struct ftmac100_data *priv = dev->priv;
diff --git a/drivers/net/greth.c b/drivers/net/greth.c
index 2aab52f378..08206c8711 100644
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -403,7 +403,7 @@ void greth_halt(struct eth_device *dev)
}
}
-int greth_send(struct eth_device *dev, volatile void *eth_data, int data_length)
+int greth_send(struct eth_device *dev, void *eth_data, int data_length)
{
greth_priv *greth = dev->priv;
greth_regs *regs = greth->regs;
diff --git a/drivers/net/inca-ip_sw.c b/drivers/net/inca-ip_sw.c
index c45389e0cd..f0f62deaf8 100644
--- a/drivers/net/inca-ip_sw.c
+++ b/drivers/net/inca-ip_sw.c
@@ -158,7 +158,7 @@ static int initialized = 0;
static int inca_switch_init(struct eth_device *dev, bd_t * bis);
-static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length);
+static int inca_switch_send(struct eth_device *dev, void *packet, int length);
static int inca_switch_recv(struct eth_device *dev);
static void inca_switch_halt(struct eth_device *dev);
static void inca_init_switch_chip(void);
@@ -334,7 +334,7 @@ static int inca_switch_init(struct eth_device *dev, bd_t * bis)
}
-static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length)
+static int inca_switch_send(struct eth_device *dev, void *packet, int length)
{
int i;
int res = -1;
diff --git a/drivers/net/ks8695eth.c b/drivers/net/ks8695eth.c
index 8e988d1423..b4904b68e5 100644
--- a/drivers/net/ks8695eth.c
+++ b/drivers/net/ks8695eth.c
@@ -190,8 +190,7 @@ static int ks8695_eth_recv(struct eth_device *dev)
/****************************************************************************/
-static int ks8695_eth_send(struct eth_device *dev, volatile void *packet,
- int len)
+static int ks8695_eth_send(struct eth_device *dev, void *packet, int len)
{
volatile struct ks8695_txdesc *dp;
static int next = 0;
diff --git a/drivers/net/lan91c96.c b/drivers/net/lan91c96.c
index 24b28da249..11d350eb8d 100644
--- a/drivers/net/lan91c96.c
+++ b/drivers/net/lan91c96.c
@@ -314,7 +314,7 @@ static void smc_shutdown(struct eth_device *dev)
* Enable the transmit interrupt, so I know if it failed
* Free the kernel data if I actually sent it.
*/
-static int smc_send_packet(struct eth_device *dev, volatile void *packet,
+static int smc_send_packet(struct eth_device *dev, void *packet,
int packet_length)
{
byte packet_no;
@@ -700,7 +700,7 @@ static int lan91c96_recv(struct eth_device *dev)
return smc_rcv(dev);
}
-static int lan91c96_send(struct eth_device *dev, volatile void *packet,
+static int lan91c96_send(struct eth_device *dev, void *packet,
int length)
{
return smc_send_packet(dev, packet, length);
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index c63eea966a..45784678ba 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -197,8 +197,7 @@ int macb_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value)
#if defined(CONFIG_CMD_NET)
-static int macb_send(struct eth_device *netdev, volatile void *packet,
- int length)
+static int macb_send(struct eth_device *netdev, void *packet, int length)
{
struct macb_device *macb = to_macb(netdev);
unsigned long paddr, ctrl;
diff --git a/drivers/net/mcffec.c b/drivers/net/mcffec.c
index a08ff278bf..ed7459c647 100644
--- a/drivers/net/mcffec.c
+++ b/drivers/net/mcffec.c
@@ -95,7 +95,6 @@ struct fec_info_s fec_info[] = {
#endif
};
-int fec_send(struct eth_device *dev, volatile void *packet, int length);
int fec_recv(struct eth_device *dev);
int fec_init(struct eth_device *dev, bd_t * bd);
void fec_halt(struct eth_device *dev);
@@ -134,7 +133,7 @@ void setFecDuplexSpeed(volatile fec_t * fecp, bd_t * bd, int dup_spd)
}
}
-int fec_send(struct eth_device *dev, volatile void *packet, int length)
+static int fec_send(struct eth_device *dev, void *packet, int length)
{
struct fec_info_s *info = dev->priv;
volatile fec_t *fecp = (fec_t *) (info->iobase);
diff --git a/drivers/net/mpc512x_fec.c b/drivers/net/mpc512x_fec.c
index 0d5efd56ea..ad57d566be 100644
--- a/drivers/net/mpc512x_fec.c
+++ b/drivers/net/mpc512x_fec.c
@@ -452,8 +452,8 @@ static void mpc512x_fec_halt (struct eth_device *dev)
/********************************************************************/
-static int mpc512x_fec_send (struct eth_device *dev, volatile void *eth_data,
- int data_length)
+static int mpc512x_fec_send(struct eth_device *dev, void *eth_data,
+ int data_length)
{
/*
* This routine transmits one frame. This routine only accepts
diff --git a/drivers/net/mpc5xxx_fec.c b/drivers/net/mpc5xxx_fec.c
index bc8c9222dc..3d180db749 100644
--- a/drivers/net/mpc5xxx_fec.c
+++ b/drivers/net/mpc5xxx_fec.c
@@ -707,7 +707,7 @@ static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec)
/********************************************************************/
-static int mpc5xxx_fec_send(struct eth_device *dev, volatile void *eth_data,
+static int mpc5xxx_fec_send(struct eth_device *dev, void *eth_data,
int data_length)
{
/*
diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c
index dcc1547c1c..47bf27c8ba 100644
--- a/drivers/net/mvgbe.c
+++ b/drivers/net/mvgbe.c
@@ -525,8 +525,7 @@ static int mvgbe_write_hwaddr(struct eth_device *dev)
return 0;
}
-static int mvgbe_send(struct eth_device *dev, void *dataptr,
- int datasize)
+static int mvgbe_send(struct eth_device *dev, void *dataptr, int datasize)
{
struct mvgbe_device *dmvgbe = to_mvgbe(dev);
struct mvgbe_registers *regs = dmvgbe->regs;
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 9386adf87f..04743bd2b3 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -237,8 +237,7 @@ static void natsemi_init_txd(struct eth_device *dev);
static void natsemi_init_rxd(struct eth_device *dev);
static void natsemi_set_rx_mode(struct eth_device *dev);
static void natsemi_check_duplex(struct eth_device *dev);
-static int natsemi_send(struct eth_device *dev, volatile void *packet,
- int length);
+static int natsemi_send(struct eth_device *dev, void *packet, int length);
static int natsemi_poll(struct eth_device *dev);
static void natsemi_disable(struct eth_device *dev);
@@ -754,8 +753,7 @@ natsemi_check_duplex(struct eth_device *dev)
* Description: transmits a packet and waits for completion or timeout.
*
* Returns: void. */
-static int
-natsemi_send(struct eth_device *dev, volatile void *packet, int length)
+static int natsemi_send(struct eth_device *dev, void *packet, int length)
{
u32 i, status = 0;
u32 tx_status = 0;
diff --git a/drivers/net/ne2000_base.c b/drivers/net/ne2000_base.c
index 8275091871..ef35922042 100644
--- a/drivers/net/ne2000_base.c
+++ b/drivers/net/ne2000_base.c
@@ -749,7 +749,7 @@ static int ne2k_recv(struct eth_device *dev)
return 1;
}
-static int ne2k_send(struct eth_device *dev, volatile void *packet, int length)
+static int ne2k_send(struct eth_device *dev, void *packet, int length)
{
int tmo;
diff --git a/drivers/net/netarm_eth.c b/drivers/net/netarm_eth.c
index f54817e58d..325f16c3a8 100644
--- a/drivers/net/netarm_eth.c
+++ b/drivers/net/netarm_eth.c
@@ -300,7 +300,7 @@ extern int eth_rx (void)
}
/* Send a data block via Ethernet. */
-extern int eth_send (volatile void *packet, int length)
+extern int eth_send(void *packet, int length)
{
int i, length32;
char *pa;
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 56ba64fbb0..14243b8a92 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -29,81 +29,84 @@
DECLARE_GLOBAL_DATA_PTR;
static char input_buffer[512];
-static int input_size = 0; /* char count in input buffer */
-static int input_offset = 0; /* offset to valid chars in input buffer */
-static int input_recursion = 0;
-static int output_recursion = 0;
+static int input_size; /* char count in input buffer */
+static int input_offset; /* offset to valid chars in input buffer */
+static int input_recursion;
+static int output_recursion;
static int net_timeout;
-static uchar nc_ether[6]; /* server enet address */
-static IPaddr_t nc_ip; /* server ip */
-static short nc_port; /* source/target port */
-static const char *output_packet; /* used by first send udp */
-static int output_packet_len = 0;
+static uchar nc_ether[6]; /* server enet address */
+static IPaddr_t nc_ip; /* server ip */
+static short nc_port; /* source/target port */
+static const char *output_packet; /* used by first send udp */
+static int output_packet_len;
static void nc_wait_arp_handler(uchar *pkt, unsigned dest,
IPaddr_t sip, unsigned src,
unsigned len)
{
- NetState = NETLOOP_SUCCESS; /* got arp reply - quit net loop */
+ net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */
}
static void nc_handler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
unsigned len)
{
if (input_size)
- NetState = NETLOOP_SUCCESS; /* got input - quit net loop */
+ net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */
}
-static void nc_timeout (void)
+static void nc_timeout(void)
{
- NetState = NETLOOP_SUCCESS;
+ net_set_state(NETLOOP_SUCCESS);
}
-void NcStart (void)
+void NcStart(void)
{
- if (!output_packet_len || memcmp (nc_ether, NetEtherNullAddr, 6)) {
+ if (!output_packet_len || memcmp(nc_ether, NetEtherNullAddr, 6)) {
/* going to check for input packet */
- NetSetHandler (nc_handler);
- NetSetTimeout (net_timeout, nc_timeout);
+ net_set_udp_handler(nc_handler);
+ NetSetTimeout(net_timeout, nc_timeout);
} else {
/* send arp request */
uchar *pkt;
- NetSetHandler (nc_wait_arp_handler);
- pkt = (uchar *) NetTxPacket + NetEthHdrSize () + IP_HDR_SIZE;
- memcpy (pkt, output_packet, output_packet_len);
- NetSendUDPPacket (nc_ether, nc_ip, nc_port, nc_port, output_packet_len);
+ net_set_arp_handler(nc_wait_arp_handler);
+ pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
+ memcpy(pkt, output_packet, output_packet_len);
+ NetSendUDPPacket(nc_ether, nc_ip, nc_port, nc_port,
+ output_packet_len);
}
}
-int nc_input_packet (uchar * pkt, unsigned dest, unsigned src, unsigned len)
+int nc_input_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len)
{
int end, chunk;
if (dest != nc_port || !len)
- return 0; /* not for us */
+ return 0; /* not for us */
- if (input_size == sizeof input_buffer)
- return 1; /* no space */
- if (len > sizeof input_buffer - input_size)
- len = sizeof input_buffer - input_size;
+ debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt);
+
+ if (input_size == sizeof(input_buffer))
+ return 1; /* no space */
+ if (len > sizeof(input_buffer) - input_size)
+ len = sizeof(input_buffer) - input_size;
end = input_offset + input_size;
- if (end > sizeof input_buffer)
- end -= sizeof input_buffer;
+ if (end > sizeof(input_buffer))
+ end -= sizeof(input_buffer);
chunk = len;
- if (end + len > sizeof input_buffer) {
- chunk = sizeof input_buffer - end;
+ if (end + len > sizeof(input_buffer)) {
+ chunk = sizeof(input_buffer) - end;
memcpy(input_buffer, pkt + chunk, len - chunk);
}
- memcpy (input_buffer + end, pkt, chunk);
+ memcpy(input_buffer + end, pkt, chunk);
input_size += len;
return 1;
}
-static void nc_send_packet (const char *buf, int len)
+static void nc_send_packet(const char *buf, int len)
{
struct eth_device *eth;
int inited = 0;
@@ -111,33 +114,35 @@ static void nc_send_packet (const char *buf, int len)
uchar *ether;
IPaddr_t ip;
- if ((eth = eth_get_dev ()) == NULL) {
+ debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf);
+
+ eth = eth_get_dev();
+ if (eth == NULL)
return;
- }
- if (!memcmp (nc_ether, NetEtherNullAddr, 6)) {
+ if (!memcmp(nc_ether, NetEtherNullAddr, 6)) {
if (eth->state == ETH_STATE_ACTIVE)
return; /* inside net loop */
output_packet = buf;
output_packet_len = len;
- NetLoop (NETCONS); /* wait for arp reply and send packet */
+ NetLoop(NETCONS); /* wait for arp reply and send packet */
output_packet_len = 0;
return;
}
if (eth->state != ETH_STATE_ACTIVE) {
- if (eth_init (gd->bd) < 0)
+ if (eth_init(gd->bd) < 0)
return;
inited = 1;
}
- pkt = (uchar *) NetTxPacket + NetEthHdrSize () + IP_HDR_SIZE;
- memcpy (pkt, buf, len);
+ pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
+ memcpy(pkt, buf, len);
ether = nc_ether;
ip = nc_ip;
- NetSendUDPPacket (ether, ip, nc_port, nc_port, len);
+ NetSendUDPPacket(ether, ip, nc_port, nc_port, len);
if (inited)
- eth_halt ();
+ eth_halt();
}
static int nc_start(void)
@@ -146,26 +151,33 @@ static int nc_start(void)
nc_port = 6666; /* default port */
- if (getenv ("ncip")) {
+ if (getenv("ncip")) {
char *p;
- nc_ip = getenv_IPaddr ("ncip");
+ nc_ip = getenv_IPaddr("ncip");
if (!nc_ip)
return -1; /* ncip is 0.0.0.0 */
- if ((p = strchr (getenv ("ncip"), ':')) != NULL)
- nc_port = simple_strtoul (p + 1, NULL, 10);
+ p = strchr(getenv("ncip"), ':');
+ if (p != NULL)
+ nc_port = simple_strtoul(p + 1, NULL, 10);
} else
nc_ip = ~0; /* ncip is not set */
- our_ip = getenv_IPaddr ("ipaddr");
- netmask = getenv_IPaddr ("netmask");
+ our_ip = getenv_IPaddr("ipaddr");
+ netmask = getenv_IPaddr("netmask");
if (nc_ip == ~0 || /* 255.255.255.255 */
((netmask & our_ip) == (netmask & nc_ip) && /* on the same net */
- (netmask | nc_ip) == ~0)) /* broadcast to our net */
- memset (nc_ether, 0xff, sizeof nc_ether);
+ (netmask | nc_ip) == ~0)) /* broadcast to our net */
+ memset(nc_ether, 0xff, sizeof(nc_ether));
else
- memset (nc_ether, 0, sizeof nc_ether); /* force arp request */
+ memset(nc_ether, 0, sizeof(nc_ether)); /* force arp request */
+
+ /*
+ * Initialize the static IP settings and buffer pointers
+ * incase we call NetSendUDPPacket before NetLoop
+ */
+ net_init();
return 0;
}
@@ -176,7 +188,7 @@ static void nc_putc(char c)
return;
output_recursion = 1;
- nc_send_packet (&c, 1);
+ nc_send_packet(&c, 1);
output_recursion = 0;
}
@@ -208,14 +220,14 @@ static int nc_getc(void)
net_timeout = 0; /* no timeout */
while (!input_size)
- NetLoop (NETCONS);
+ NetLoop(NETCONS);
input_recursion = 0;
c = input_buffer[input_offset++];
- if (input_offset >= sizeof input_buffer)
- input_offset -= sizeof input_buffer;
+ if (input_offset >= sizeof(input_buffer))
+ input_offset -= sizeof(input_buffer);
input_size--;
return c;
@@ -231,28 +243,28 @@ static int nc_tstc(void)
if (input_size)
return 1;
- eth = eth_get_dev ();
+ eth = eth_get_dev();
if (eth && eth->state == ETH_STATE_ACTIVE)
return 0; /* inside net loop */
input_recursion = 1;
net_timeout = 1;
- NetLoop (NETCONS); /* kind of poll */
+ NetLoop(NETCONS); /* kind of poll */
input_recursion = 0;
return input_size != 0;
}
-int drv_nc_init (void)
+int drv_nc_init(void)
{
struct stdio_dev dev;
int rc;
- memset (&dev, 0, sizeof (dev));
+ memset(&dev, 0, sizeof(dev));
- strcpy (dev.name, "nc");
+ strcpy(dev.name, "nc");
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
dev.start = nc_start;
dev.putc = nc_putc;
@@ -260,7 +272,7 @@ int drv_nc_init (void)
dev.getc = nc_getc;
dev.tstc = nc_tstc;
- rc = stdio_register (&dev);
+ rc = stdio_register(&dev);
return (rc == 0) ? 1 : rc;
}
diff --git a/drivers/net/ns8382x.c b/drivers/net/ns8382x.c
index 11863feba3..cfe1f349db 100644
--- a/drivers/net/ns8382x.c
+++ b/drivers/net/ns8382x.c
@@ -258,8 +258,7 @@ static void ns8382x_init_txd(struct eth_device *dev);
static void ns8382x_init_rxd(struct eth_device *dev);
static void ns8382x_set_rx_mode(struct eth_device *dev);
static void ns8382x_check_duplex(struct eth_device *dev);
-static int ns8382x_send(struct eth_device *dev, volatile void *packet,
- int length);
+static int ns8382x_send(struct eth_device *dev, void *packet, int length);
static int ns8382x_poll(struct eth_device *dev);
static void ns8382x_disable(struct eth_device *dev);
@@ -735,8 +734,7 @@ ns8382x_check_duplex(struct eth_device *dev)
/* Function: ns8382x_send
* Description: transmits a packet and waits for completion or timeout.
* Returns: void. */
-static int
-ns8382x_send(struct eth_device *dev, volatile void *packet, int length)
+static int ns8382x_send(struct eth_device *dev, void *packet, int length)
{
u32 i, status = 0;
vu_long tx_stat = 0;
diff --git a/drivers/net/pcnet.c b/drivers/net/pcnet.c
index 45066c8fea..c028a44a9e 100644
--- a/drivers/net/pcnet.c
+++ b/drivers/net/pcnet.c
@@ -141,8 +141,7 @@ static int pcnet_check (struct eth_device *dev)
}
static int pcnet_init (struct eth_device *dev, bd_t * bis);
-static int pcnet_send (struct eth_device *dev, volatile void *packet,
- int length);
+static int pcnet_send(struct eth_device *dev, void *packet, int length);
static int pcnet_recv (struct eth_device *dev);
static void pcnet_halt (struct eth_device *dev);
static int pcnet_probe (struct eth_device *dev, bd_t * bis, int dev_num);
@@ -415,8 +414,7 @@ static int pcnet_init (struct eth_device *dev, bd_t * bis)
return 0;
}
-static int pcnet_send (struct eth_device *dev, volatile void *packet,
- int pkt_len)
+static int pcnet_send(struct eth_device *dev, void *packet, int pkt_len)
{
int i, status;
struct pcnet_tx_head *entry = &lp->tx_ring[lp->cur_tx];
diff --git a/drivers/net/plb2800_eth.c b/drivers/net/plb2800_eth.c
index d799c73823..93782c23ff 100644
--- a/drivers/net/plb2800_eth.c
+++ b/drivers/net/plb2800_eth.c
@@ -82,8 +82,7 @@ typedef volatile struct {
static int plb2800_eth_init(struct eth_device *dev, bd_t * bis);
-static int plb2800_eth_send(struct eth_device *dev, volatile void *packet,
- int length);
+static int plb2800_eth_send(struct eth_device *dev, void *packet, int length);
static int plb2800_eth_recv(struct eth_device *dev);
static void plb2800_eth_halt(struct eth_device *dev);
@@ -162,8 +161,7 @@ static int plb2800_eth_init(struct eth_device *dev, bd_t * bis)
}
-static int plb2800_eth_send(struct eth_device *dev, volatile void *packet,
- int length)
+static int plb2800_eth_send(struct eth_device *dev, void *packet, int length)
{
int i;
int res = -1;
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
index e3feef849c..4186699ff9 100644
--- a/drivers/net/rtl8139.c
+++ b/drivers/net/rtl8139.c
@@ -184,7 +184,7 @@ static unsigned char rx_ring[RX_BUF_LEN+16] __attribute__((aligned(4)));
static int rtl8139_probe(struct eth_device *dev, bd_t *bis);
static int read_eeprom(int location, int addr_len);
static void rtl_reset(struct eth_device *dev);
-static int rtl_transmit(struct eth_device *dev, volatile void *packet, int length);
+static int rtl_transmit(struct eth_device *dev, void *packet, int length);
static int rtl_poll(struct eth_device *dev);
static void rtl_disable(struct eth_device *dev);
#ifdef CONFIG_MCAST_TFTP/* This driver already accepts all b/mcast */
@@ -407,7 +407,7 @@ static void rtl_reset(struct eth_device *dev)
outw(0, ioaddr + IntrMask);
}
-static int rtl_transmit(struct eth_device *dev, volatile void *packet, int length)
+static int rtl_transmit(struct eth_device *dev, void *packet, int length)
{
unsigned int status;
unsigned long txstatus;
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c
index 1ad13bddd5..9864fd7b5c 100644
--- a/drivers/net/rtl8169.c
+++ b/drivers/net/rtl8169.c
@@ -464,7 +464,7 @@ static int rtl_recv(struct eth_device *dev)
/**************************************************************************
SEND - Transmit a frame
***************************************************************************/
-static int rtl_send(struct eth_device *dev, volatile void *packet, int length)
+static int rtl_send(struct eth_device *dev, void *packet, int length)
{
/* send the packet to destination */
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index 8d3dac20af..bb57e4d53a 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -46,7 +46,7 @@
#define TIMEOUT_CNT 1000
-int sh_eth_send(struct eth_device *dev, volatile void *packet, int len)
+int sh_eth_send(struct eth_device *dev, void *packet, int len)
{
struct sh_eth_dev *eth = dev->priv;
int port = eth->port, ret = 0, timeout;
@@ -59,7 +59,7 @@ int sh_eth_send(struct eth_device *dev, volatile void *packet, int len)
}
/* packet must be a 4 byte boundary */
- if ((int)packet & (4 - 1)) {
+ if ((int)packet & 3) {
printf(SHETHER_NAME ": %s: packet not 4 byte alligned\n", __func__);
ret = -EFAULT;
goto err;
@@ -103,15 +103,15 @@ int sh_eth_recv(struct eth_device *dev)
struct sh_eth_dev *eth = dev->priv;
int port = eth->port, len = 0;
struct sh_eth_info *port_info = &eth->port_info[port];
- volatile u8 *packet;
+ uchar *packet;
/* Check if the rx descriptor is ready */
if (!(port_info->rx_desc_cur->rd0 & RD_RACT)) {
/* Check for errors */
if (!(port_info->rx_desc_cur->rd0 & RD_RFE)) {
len = port_info->rx_desc_cur->rd1 & 0xffff;
- packet = (volatile u8 *)
- ADDR_TO_P2(port_info->rx_desc_cur->rd2);
+ packet = (uchar *)
+ ADDR_TO_P2(port_info->rx_desc_cur->rd2);
NetReceive(packet, len);
}
@@ -138,7 +138,7 @@ int sh_eth_recv(struct eth_device *dev)
static int sh_eth_reset(struct sh_eth_dev *eth)
{
int port = eth->port;
-#if defined(CONFIG_CPU_SH7763)
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
int ret = 0, i;
/* Start e-dmac transmitter and receiver */
@@ -208,7 +208,7 @@ static int sh_eth_tx_desc_init(struct sh_eth_dev *eth)
/* Point the controller to the tx descriptor list. Must use physical
addresses */
outl(ADDR_TO_PHY(port_info->tx_desc_base), TDLAR(port));
-#if defined(CONFIG_CPU_SH7763)
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
outl(ADDR_TO_PHY(port_info->tx_desc_base), TDFAR(port));
outl(ADDR_TO_PHY(cur_tx_desc), TDFXR(port));
outl(0x01, TDFFR(port));/* Last discriptor bit */
@@ -276,7 +276,7 @@ static int sh_eth_rx_desc_init(struct sh_eth_dev *eth)
/* Point the controller to the rx descriptor list */
outl(ADDR_TO_PHY(port_info->rx_desc_base), RDLAR(port));
-#if defined(CONFIG_CPU_SH7763)
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
outl(ADDR_TO_PHY(port_info->rx_desc_base), RDFAR(port));
outl(ADDR_TO_PHY(cur_rx_desc), RDFXR(port));
outl(RDFFR_RDLF, RDFFR(port));
@@ -346,8 +346,9 @@ static int sh_eth_phy_config(struct sh_eth_dev *eth)
struct eth_device *dev = port_info->dev;
struct phy_device *phydev;
- phydev = phy_connect(miiphy_get_dev_by_name(dev->name),
- port_info->phy_addr, dev, PHY_INTERFACE_MODE_MII);
+ phydev = phy_connect(
+ miiphy_get_dev_by_name(dev->name),
+ port_info->phy_addr, dev, CONFIG_SH_ETHER_PHY_MODE);
port_info->phydev = phydev;
phy_config(phydev);
@@ -398,12 +399,15 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd)
outl(APR_AP, APR(port));
outl(MPR_MP, MPR(port));
#endif
-#if defined(CONFIG_CPU_SH7763)
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
outl(TPAUSER_TPAUSE, TPAUSER(port));
#elif defined(CONFIG_CPU_SH7757)
outl(TPAUSER_UNLIMITED, TPAUSER(port));
#endif
+#if defined(CONFIG_CPU_SH7734)
+ outl(CONFIG_SH_ETHER_SH7734_MII, RMII_MII(port));
+#endif
/* Configure phy */
ret = sh_eth_phy_config(eth);
if (ret) {
@@ -418,7 +422,7 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd)
/* Set the transfer speed */
if (phy->speed == 100) {
printf(SHETHER_NAME ": 100Base/");
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
outl(GECMR_100B, GECMR(port));
#elif defined(CONFIG_CPU_SH7757)
outl(1, RTRATE(port));
@@ -427,12 +431,18 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd)
#endif
} else if (phy->speed == 10) {
printf(SHETHER_NAME ": 10Base/");
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
outl(GECMR_10B, GECMR(port));
#elif defined(CONFIG_CPU_SH7757)
outl(0, RTRATE(port));
#endif
}
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
+ else if (phy->speed == 1000) {
+ printf(SHETHER_NAME ": 1000Base/");
+ outl(GECMR_1000B, GECMR(port));
+ }
+#endif
/* Check if full duplex mode is supported by the phy */
if (phy->duplex) {
diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h
index 27fde05bd6..a1ba68b1b4 100644
--- a/drivers/net/sh_eth.h
+++ b/drivers/net/sh_eth.h
@@ -188,13 +188,52 @@ struct sh_eth_dev {
#define TPAUSER(port) (BASE_IO_ADDR + 0x0164)
#define MAHR(port) (BASE_IO_ADDR + 0x01c0)
#define MALR(port) (BASE_IO_ADDR + 0x01c8)
+
+#elif defined(CONFIG_CPU_SH7734)
+#define BASE_IO_ADDR 0xFEE00000
+
+#define EDSR(port) (BASE_IO_ADDR)
+
+#define TDLAR(port) (BASE_IO_ADDR + 0x0010)
+#define TDFAR(port) (BASE_IO_ADDR + 0x0014)
+#define TDFXR(port) (BASE_IO_ADDR + 0x0018)
+#define TDFFR(port) (BASE_IO_ADDR + 0x001c)
+#define RDLAR(port) (BASE_IO_ADDR + 0x0030)
+#define RDFAR(port) (BASE_IO_ADDR + 0x0034)
+#define RDFXR(port) (BASE_IO_ADDR + 0x0038)
+#define RDFFR(port) (BASE_IO_ADDR + 0x003c)
+
+#define EDMR(port) (BASE_IO_ADDR + 0x0400)
+#define EDTRR(port) (BASE_IO_ADDR + 0x0408)
+#define EDRRR(port) (BASE_IO_ADDR + 0x0410)
+#define EESR(port) (BASE_IO_ADDR + 0x0428)
+#define EESIPR(port) (BASE_IO_ADDR + 0x0430)
+#define TRSCER(port) (BASE_IO_ADDR + 0x0438)
+#define TFTR(port) (BASE_IO_ADDR + 0x0448)
+#define FDR(port) (BASE_IO_ADDR + 0x0450)
+#define RMCR(port) (BASE_IO_ADDR + 0x0458)
+#define RPADIR(port) (BASE_IO_ADDR + 0x0460)
+#define FCFTR(port) (BASE_IO_ADDR + 0x0468)
+#define ECMR(port) (BASE_IO_ADDR + 0x0500)
+#define RFLR(port) (BASE_IO_ADDR + 0x0508)
+#define ECSIPR(port) (BASE_IO_ADDR + 0x0518)
+#define PIR(port) (BASE_IO_ADDR + 0x0520)
+#define PIPR(port) (BASE_IO_ADDR + 0x052c)
+#define APR(port) (BASE_IO_ADDR + 0x0554)
+#define MPR(port) (BASE_IO_ADDR + 0x0558)
+#define TPAUSER(port) (BASE_IO_ADDR + 0x0564)
+#define GECMR(port) (BASE_IO_ADDR + 0x05b0)
+#define MAHR(port) (BASE_IO_ADDR + 0x05C0)
+#define MALR(port) (BASE_IO_ADDR + 0x05C8)
+#define RMII_MII(port) (BASE_IO_ADDR + 0x0790)
+
#endif
/*
* Register's bits
* Copy from Linux driver source code
*/
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
/* EDSR */
enum EDSR_BIT {
EDSR_ENT = 0x01, EDSR_ENR = 0x02,
@@ -205,11 +244,11 @@ enum EDSR_BIT {
/* EDMR */
enum DMAC_M_BIT {
EDMR_DL1 = 0x20, EDMR_DL0 = 0x10,
-#ifdef CONFIG_CPU_SH7763
- EDMR_SRST = 0x03,
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
+ EDMR_SRST = 0x03, /* Receive/Send reset */
EMDR_DESC_R = 0x30, /* Descriptor reserve size */
EDMR_EL = 0x40, /* Litte endian */
-#elif defined(CONFIG_CPU_SH7757) ||defined (CONFIG_CPU_SH7724)
+#elif defined(CONFIG_CPU_SH7757) || defined(CONFIG_CPU_SH7724)
EDMR_SRST = 0x01,
EMDR_DESC_R = 0x30, /* Descriptor reserve size */
EDMR_EL = 0x40, /* Litte endian */
@@ -223,7 +262,7 @@ enum DMAC_M_BIT {
/* EDTRR */
enum DMAC_T_BIT {
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
EDTRR_TRNS = 0x03,
#else
EDTRR_TRNS = 0x01,
@@ -262,7 +301,8 @@ enum PHY_STATUS_BIT { PHY_ST_LINK = 0x01, };
/* EESR */
enum EESR_BIT {
-#ifndef CONFIG_CPU_SH7763
+
+#if defined(CONFIG_CPU_SH7724) || defined(CONFIG_CPU_SH7757)
EESR_TWB = 0x40000000,
#else
EESR_TWB = 0xC0000000,
@@ -272,14 +312,14 @@ enum EESR_BIT {
#endif
EESR_TABT = 0x04000000,
EESR_RABT = 0x02000000, EESR_RFRMER = 0x01000000,
-#ifndef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7724) || defined(CONFIG_CPU_SH7757)
EESR_ADE = 0x00800000,
#endif
EESR_ECI = 0x00400000,
EESR_FTC = 0x00200000, EESR_TDE = 0x00100000,
EESR_TFE = 0x00080000, EESR_FRC = 0x00040000,
EESR_RDE = 0x00020000, EESR_RFE = 0x00010000,
-#ifndef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7724) && !defined(CONFIG_CPU_SH7757)
EESR_CND = 0x00000800,
#endif
EESR_DLC = 0x00000400,
@@ -291,7 +331,7 @@ enum EESR_BIT {
};
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
# define TX_CHECK (EESR_TC1 | EESR_FTC)
# define EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RDE \
| EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI)
@@ -352,7 +392,7 @@ enum FCFTR_BIT {
/* Transfer descriptor bit */
enum TD_STS_BIT {
#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7757) \
- || defined(CONFIG_CPU_SH7724)
+ || defined(CONFIG_CPU_SH7724) || defined(CONFIG_CPU_SH7734)
TD_TACT = 0x80000000,
#else
TD_TACT = 0x7fffffff,
@@ -368,7 +408,7 @@ enum TD_STS_BIT {
enum RECV_RST_BIT { RMCR_RST = 0x01, };
/* ECMR */
enum FELIC_MODE_BIT {
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
ECMR_TRCCM=0x04000000, ECMR_RCSC= 0x00800000, ECMR_DPAD= 0x00200000,
ECMR_RZPF = 0x00100000,
#endif
@@ -383,7 +423,7 @@ enum FELIC_MODE_BIT {
};
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
#define ECMR_CHG_DM (ECMR_TRCCM | ECMR_RZPF | ECMR_ZPF | ECMR_PFR | ECMR_RXF | \
ECMR_TXF | ECMR_MCT)
#elif CONFIG_CPU_SH7757
@@ -396,14 +436,14 @@ enum FELIC_MODE_BIT {
/* ECSR */
enum ECSR_STATUS_BIT {
-#ifndef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7724) || defined(CONFIG_CPU_SH7757)
ECSR_BRCRX = 0x20, ECSR_PSRTO = 0x10,
#endif
ECSR_LCHNG = 0x04,
ECSR_MPD = 0x02, ECSR_ICD = 0x01,
};
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
# define ECSR_INIT (ECSR_ICD | ECSIPR_MPDIP)
#else
# define ECSR_INIT (ECSR_BRCRX | ECSR_PSRTO | \
@@ -412,14 +452,20 @@ enum ECSR_STATUS_BIT {
/* ECSIPR */
enum ECSIPR_STATUS_MASK_BIT {
-#ifndef CONFIG_CPU_SH7763
- ECSIPR_BRCRXIP = 0x20, ECSIPR_PSRTOIP = 0x10,
-#endif
+#if defined(CONFIG_CPU_SH7724)
+ ECSIPR_PSRTOIP = 0x10,
+ ECSIPR_LCHNGIP = 0x04,
+ ECSIPR_ICDIP = 0x01,
+#elif defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
+ ECSIPR_PSRTOIP = 0x10,
+ ECSIPR_PHYIP = 0x08,
ECSIPR_LCHNGIP = 0x04,
- ECSIPR_MPDIP = 0x02, ECSIPR_ICDIP = 0x01,
+ ECSIPR_MPDIP = 0x02,
+ ECSIPR_ICDIP = 0x01,
+#endif
};
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
# define ECSIPR_INIT (ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP)
#else
# define ECSIPR_INIT (ECSIPR_BRCRXIP | ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | \
@@ -458,7 +504,7 @@ enum RPADIR_BIT {
RPADIR_PADR = 0x0003f,
};
-#ifdef CONFIG_CPU_SH7763
+#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734)
# define RPADIR_INIT (0x00)
#else
# define RPADIR_INIT (RPADIR_PADS1)
diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c
index 5cfef4dd7b..6dc7ad52e4 100644
--- a/drivers/net/smc91111.c
+++ b/drivers/net/smc91111.c
@@ -426,8 +426,7 @@ static void smc_halt(struct eth_device *dev)
. Enable the transmit interrupt, so I know if it failed
. Free the kernel data if I actually sent it.
*/
-static int smc_send(struct eth_device *dev, volatile void *packet,
- int packet_length)
+static int smc_send(struct eth_device *dev, void *packet, int packet_length)
{
byte packet_no;
byte *buf;
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
index e34b4879d0..b2aed7e14a 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/smc911x.c
@@ -161,8 +161,7 @@ static int smc911x_init(struct eth_device *dev, bd_t * bd)
return 0;
}
-static int smc911x_send(struct eth_device *dev,
- volatile void *packet, int length)
+static int smc911x_send(struct eth_device *dev, void *packet, int length)
{
u32 *data = (u32*)packet;
u32 tmplen;
diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c
index 160bc0597d..3c1c8f0799 100644
--- a/drivers/net/tsec.c
+++ b/drivers/net/tsec.c
@@ -44,8 +44,7 @@ static RTXBD rtx __attribute__ ((aligned(8)));
#error "rtx must be 64-bit aligned"
#endif
-static int tsec_send(struct eth_device *dev,
- volatile void *packet, int length);
+static int tsec_send(struct eth_device *dev, void *packet, int length);
/* Default initializations for TSEC controllers. */
@@ -377,7 +376,7 @@ static void startup_tsec(struct eth_device *dev)
* do the same. Presumably, this would be zero if there were no
* errors
*/
-static int tsec_send(struct eth_device *dev, volatile void *packet, int length)
+static int tsec_send(struct eth_device *dev, void *packet, int length)
{
int i;
int result = 0;
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
index f100ec1a50..b2c1be54fe 100644
--- a/drivers/net/tsi108_eth.c
+++ b/drivers/net/tsi108_eth.c
@@ -433,8 +433,7 @@ static struct dma_descriptor rx_descr_array[NUM_RX_DESC]
static struct dma_descriptor *rx_descr_current;
static int tsi108_eth_probe (struct eth_device *dev, bd_t * bis);
-static int tsi108_eth_send (struct eth_device *dev,
- volatile void *packet, int length);
+static int tsi108_eth_send(struct eth_device *dev, void *packet, int length);
static int tsi108_eth_recv (struct eth_device *dev);
static void tsi108_eth_halt (struct eth_device *dev);
static unsigned int read_phy (unsigned int base,
@@ -872,8 +871,7 @@ static int tsi108_eth_probe (struct eth_device *dev, bd_t * bis)
/*
* send a packet
*/
-static int tsi108_eth_send (struct eth_device *dev,
- volatile void *packet, int length)
+static int tsi108_eth_send(struct eth_device *dev, void *packet, int length)
{
unsigned long base;
int timeout;
@@ -948,7 +946,7 @@ static int tsi108_eth_recv (struct eth_device *dev)
unsigned long base;
int length = 0;
unsigned long status;
- volatile uchar *buffer;
+ uchar *buffer;
base = dev->iobase;
@@ -983,10 +981,8 @@ static int tsi108_eth_recv (struct eth_device *dev)
le32_to_cpu(rx_descr->vlan_byte_count) & 0xFFFF;
/*** process packet ***/
- buffer =
- (volatile uchar
- *)(le32_to_cpu (rx_descr->start_addr0));
- NetReceive (buffer, length);
+ buffer = (uchar *)(le32_to_cpu(rx_descr->start_addr0));
+ NetReceive(buffer, length);
invalidate_dcache_range ((unsigned long)buffer,
(unsigned long)buffer +
diff --git a/drivers/net/uli526x.c b/drivers/net/uli526x.c
index 5933bddce5..9648901925 100644
--- a/drivers/net/uli526x.c
+++ b/drivers/net/uli526x.c
@@ -168,8 +168,7 @@ static char buf_pool[TX_BUF_ALLOC * TX_DESC_CNT + 4];
static int mode = 8;
/* function declaration -- */
-static int uli526x_start_xmit(struct eth_device *dev,
- volatile void *packet, int length);
+static int uli526x_start_xmit(struct eth_device *dev, void *packet, int length);
static const struct ethtool_ops netdev_ethtool_ops;
static u16 read_srom_word(long, int);
static void uli526x_descriptor_init(struct uli526x_board_info *, unsigned long);
@@ -443,8 +442,7 @@ static void uli526x_init(struct eth_device *dev)
* Send a packet to media from the upper layer.
*/
-static int uli526x_start_xmit(struct eth_device *dev,
- volatile void *packet, int length)
+static int uli526x_start_xmit(struct eth_device *dev, void *packet, int length)
{
struct uli526x_board_info *db = dev->priv;
struct tx_desc *txptr;
diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c
index 82e254b529..7854a04cae 100644
--- a/drivers/net/xilinx_axi_emac.c
+++ b/drivers/net/xilinx_axi_emac.c
@@ -468,7 +468,7 @@ static int axiemac_init(struct eth_device *dev, bd_t * bis)
return 0;
}
-static int axiemac_send(struct eth_device *dev, volatile void *ptr, int len)
+static int axiemac_send(struct eth_device *dev, void *ptr, int len)
{
struct axidma_priv *priv = dev->priv;
u32 timeout;
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index 9791b9a534..d5bd737827 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -199,7 +199,7 @@ static int xemaclite_txbufferavailable(struct eth_device *dev)
return !(txpingbusy && txpongbusy);
}
-static int emaclite_send(struct eth_device *dev, volatile void *ptr, int len)
+static int emaclite_send(struct eth_device *dev, void *ptr, int len)
{
u32 reg;
u32 baseaddress;
@@ -240,7 +240,7 @@ static int emaclite_send(struct eth_device *dev, volatile void *ptr, int len)
debug("Send packet from 0x%x\n", baseaddress);
/* Write the frame to the buffer */
- xemaclite_alignedwrite((void *) ptr, baseaddress, len);
+ xemaclite_alignedwrite(ptr, baseaddress, len);
out_be32 (baseaddress + XEL_TPLR_OFFSET,(len &
(XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO)));
reg = in_be32 (baseaddress + XEL_TSR_OFFSET);
@@ -261,7 +261,7 @@ static int emaclite_send(struct eth_device *dev, volatile void *ptr, int len)
& XEL_TSR_XMIT_ACTIVE_MASK) == 0)) {
debug("Send packet from 0x%x\n", baseaddress);
/* Write the frame to the buffer */
- xemaclite_alignedwrite((void *) ptr, baseaddress, len);
+ xemaclite_alignedwrite(ptr, baseaddress, len);
out_be32 (baseaddress + XEL_TPLR_OFFSET, (len &
(XEL_TPLR_LENGTH_MASK_HI |
XEL_TPLR_LENGTH_MASK_LO)));
diff --git a/drivers/net/xilinx_ll_temac_fifo.c b/drivers/net/xilinx_ll_temac_fifo.c
index 3ff0f347ed..d7fd989cd4 100644
--- a/drivers/net/xilinx_ll_temac_fifo.c
+++ b/drivers/net/xilinx_ll_temac_fifo.c
@@ -102,8 +102,7 @@ int ll_temac_recv_fifo(struct eth_device *dev)
return 0;
}
-int ll_temac_send_fifo(struct eth_device *dev, volatile void *packet,
- int length)
+int ll_temac_send_fifo(struct eth_device *dev, void *packet, int length)
{
int i;
u32 *buf = (u32 *)packet;
diff --git a/drivers/net/xilinx_ll_temac_fifo.h b/drivers/net/xilinx_ll_temac_fifo.h
index f0d6e6823c..e5b4be9e6c 100644
--- a/drivers/net/xilinx_ll_temac_fifo.h
+++ b/drivers/net/xilinx_ll_temac_fifo.h
@@ -116,7 +116,6 @@ int ll_temac_reset_fifo(struct eth_device *dev);
int ll_temac_recv_fifo(struct eth_device *dev);
/* send buffered data to FIFO */
-int ll_temac_send_fifo(struct eth_device *dev, volatile void *packet,
- int length);
+int ll_temac_send_fifo(struct eth_device *dev, void *packet, int length);
#endif /* _XILINX_LL_TEMAC_FIFO_ */
diff --git a/drivers/net/xilinx_ll_temac_sdma.c b/drivers/net/xilinx_ll_temac_sdma.c
index 621d100f29..8637a6b1fc 100644
--- a/drivers/net/xilinx_ll_temac_sdma.c
+++ b/drivers/net/xilinx_ll_temac_sdma.c
@@ -324,8 +324,7 @@ int ll_temac_recv_sdma(struct eth_device *dev)
return 0;
}
-int ll_temac_send_sdma(struct eth_device *dev, volatile void *packet,
- int length)
+int ll_temac_send_sdma(struct eth_device *dev, void *packet, int length)
{
unsigned timeout = 50; /* 1usec * 50 = 50usec */
struct cdmac_bd *tx_dp = &cdmac_bd.tx[tx_idx];
diff --git a/drivers/net/xilinx_ll_temac_sdma.h b/drivers/net/xilinx_ll_temac_sdma.h
index 51e258d7f5..db00a57a16 100644
--- a/drivers/net/xilinx_ll_temac_sdma.h
+++ b/drivers/net/xilinx_ll_temac_sdma.h
@@ -275,7 +275,6 @@ int ll_temac_reset_sdma(struct eth_device *dev);
int ll_temac_recv_sdma(struct eth_device *dev);
/* send buffered data to SDMA */
-int ll_temac_send_sdma(struct eth_device *dev, volatile void *packet,
- int length);
+int ll_temac_send_sdma(struct eth_device *dev, void *packet, int length);
#endif /* _XILINX_LL_TEMAC_SDMA_ */
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ead00f8dae..6bf388cb7d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -26,8 +26,10 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libpower.o
COBJS-$(CONFIG_FTPMU010_POWER) += ftpmu010.o
+COBJS-$(CONFIG_TPS6586X_POWER) += tps6586x.o
COBJS-$(CONFIG_TWL4030_POWER) += twl4030.o
COBJS-$(CONFIG_TWL6030_POWER) += twl6030.o
+COBJS-$(CONFIG_TWL6035_POWER) += twl6035.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/power/tps6586x.c b/drivers/power/tps6586x.c
new file mode 100644
index 0000000000..f3f2ec6e58
--- /dev/null
+++ b/drivers/power/tps6586x.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <tps6586x.h>
+#include <asm/io.h>
+#include <i2c.h>
+
+static int bus_num; /* I2C bus we are on */
+#define I2C_ADDRESS 0x34 /* chip requires this address */
+static char inited; /* 1 if we have been inited */
+
+enum {
+ /* Registers that we access */
+ SUPPLY_CONTROL1 = 0x20,
+ SUPPLY_CONTROL2,
+ SM1_VOLTAGE_V1 = 0x23,
+ SM1_VOLTAGE_V2,
+ SM0_VOLTAGE_V1 = 0x26,
+ SM0_VOLTAGE_V2,
+ PFM_MODE = 0x47,
+
+ /* Bits in the supply control registers */
+ CTRL_SM1_RAMP = 0x01,
+ CTRL_SM1_SUPPLY2 = 0x02,
+ CTRL_SM0_RAMP = 0x04,
+ CTRL_SM0_SUPPLY2 = 0x08,
+};
+
+#define MAX_I2C_RETRY 3
+int tps6586x_read(int reg)
+{
+ int i;
+ uchar data;
+ int retval = -1;
+ int old_bus_num;
+
+ old_bus_num = i2c_get_bus_num();
+ i2c_set_bus_num(bus_num);
+
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (!i2c_read(I2C_ADDRESS, reg, 1, &data, 1)) {
+ retval = (int)data;
+ goto exit;
+ }
+
+ /* i2c access failed, retry */
+ udelay(100);
+ }
+
+exit:
+ i2c_set_bus_num(old_bus_num);
+ debug("pmu_read %x=%x\n", reg, retval);
+ if (retval < 0)
+ debug("%s: failed to read register %#x: %d\n", __func__, reg,
+ retval);
+ return retval;
+}
+
+int tps6586x_write(int reg, uchar *data, uint len)
+{
+ int i;
+ int retval = -1;
+ int old_bus_num;
+
+ old_bus_num = i2c_get_bus_num();
+ i2c_set_bus_num(bus_num);
+
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (!i2c_write(I2C_ADDRESS, reg, 1, data, len)) {
+ retval = 0;
+ goto exit;
+ }
+
+ /* i2c access failed, retry */
+ udelay(100);
+ }
+
+exit:
+ i2c_set_bus_num(old_bus_num);
+ debug("pmu_write %x=%x: ", reg, retval);
+ for (i = 0; i < len; i++)
+ debug("%x ", data[i]);
+ if (retval)
+ debug("%s: failed to write register %#x\n", __func__, reg);
+ return retval;
+}
+
+/*
+ * Get current voltage of SM0 and SM1
+ *
+ * @param sm0 Place to put SM0 voltage
+ * @param sm1 Place to put SM1 voltage
+ * @return 0 if ok, -1 on error
+ */
+static int read_voltages(int *sm0, int *sm1)
+{
+ int ctrl1, ctrl2;
+ int is_v2;
+
+ /*
+ * Each vdd has two supply sources, ie, v1 and v2.
+ * The supply control reg1 and reg2 determine the current selection.
+ */
+ ctrl1 = tps6586x_read(SUPPLY_CONTROL1);
+ ctrl2 = tps6586x_read(SUPPLY_CONTROL2);
+ if (ctrl1 == -1 || ctrl2 == -1)
+ return -1;
+
+ /* Figure out whether V1 or V2 is selected */
+ is_v2 = (ctrl1 | ctrl2) & CTRL_SM0_SUPPLY2;
+ *sm0 = tps6586x_read(is_v2 ? SM0_VOLTAGE_V2 : SM0_VOLTAGE_V1);
+ *sm1 = tps6586x_read(is_v2 ? SM1_VOLTAGE_V2 : SM1_VOLTAGE_V1);
+ if (*sm0 == -1 || *sm1 == -1)
+ return -1;
+
+ return 0;
+}
+
+static int set_voltage(int reg, int data, int rate)
+{
+ uchar control_bit;
+ uchar buff[3];
+
+ control_bit = (reg == SM0_VOLTAGE_V1 ? CTRL_SM0_RAMP : CTRL_SM1_RAMP);
+
+ /*
+ * Only one supply is needed in u-boot. set both v1 and v2 to
+ * same value.
+ *
+ * When both v1 and v2 are set to same value, we just need to set
+ * control1 reg to trigger the supply selection.
+ */
+ buff[0] = buff[1] = (uchar)data;
+ buff[2] = rate;
+
+ /* write v1, v2 and rate, then trigger */
+ if (tps6586x_write(reg, buff, 3) ||
+ tps6586x_write(SUPPLY_CONTROL1, &control_bit, 1))
+ return -1;
+
+ return 0;
+}
+
+static int calculate_next_voltage(int voltage, int target, int step)
+{
+ int diff = voltage < target ? step : -step;
+
+ if (abs(target - voltage) > step)
+ voltage += diff;
+ else
+ voltage = target;
+
+ return voltage;
+}
+
+int tps6586x_set_pwm_mode(int mask)
+{
+ uchar val;
+ int ret;
+
+ assert(inited);
+ ret = tps6586x_read(PFM_MODE);
+ if (ret != -1) {
+ val = (uchar)ret;
+ val |= mask;
+
+ ret = tps6586x_write(PFM_MODE, &val, 1);
+ }
+
+ if (ret == -1)
+ debug("%s: Failed to read/write PWM mode reg\n", __func__);
+
+ return ret;
+}
+
+int tps6586x_adjust_sm0_sm1(int sm0_target, int sm1_target, int step, int rate,
+ int min_sm0_over_sm1)
+{
+ int sm0, sm1;
+ int bad;
+
+ assert(inited);
+
+ /* get current voltage settings */
+ if (read_voltages(&sm0, &sm1)) {
+ debug("%s: Cannot read voltage settings\n", __func__);
+ return -1;
+ }
+
+ /*
+ * if vdd_core < vdd_cpu + rel
+ * skip
+ *
+ * This condition may happen when system reboots due to kernel crash.
+ */
+ if (min_sm0_over_sm1 != -1 && sm0 < sm1 + min_sm0_over_sm1) {
+ debug("%s: SM0 is %d, SM1 is %d, but min_sm0_over_sm1 is %d\n",
+ __func__, sm0, sm1, min_sm0_over_sm1);
+ return -1;
+ }
+
+ /*
+ * Since vdd_core and vdd_cpu may both stand at either greater or less
+ * than their nominal voltage, the adjustment may go either directions.
+ *
+ * Make sure vdd_core is always higher than vdd_cpu with certain margin.
+ * So, find out which vdd to adjust first in each step.
+ *
+ * case 1: both sm0 and sm1 need to move up
+ * adjust sm0 before sm1
+ *
+ * case 2: both sm0 and sm1 need to move down
+ * adjust sm1 before sm0
+ *
+ * case 3: sm0 moves down and sm1 moves up
+ * adjusting either one first is fine.
+ *
+ * Adjust vdd_core and vdd_cpu one step at a time until they reach
+ * their nominal values.
+ */
+ bad = 0;
+ while (!bad && (sm0 != sm0_target || sm1 != sm1_target)) {
+ int adjust_sm0_late = 0; /* flag to adjust vdd_core later */
+
+ debug("%d-%d %d-%d ", sm0, sm0_target, sm1, sm1_target);
+
+ if (sm0 != sm0_target) {
+ /*
+ * if case 1 and case 3, set new sm0 first.
+ * otherwise, hold down until new sm1 is set.
+ */
+ sm0 = calculate_next_voltage(sm0, sm0_target, step);
+ if (sm1 < sm1_target)
+ bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate);
+ else
+ adjust_sm0_late = 1;
+ }
+
+ if (sm1 != sm1_target) {
+ sm1 = calculate_next_voltage(sm1, sm1_target, step);
+ bad |= set_voltage(SM1_VOLTAGE_V1, sm1, rate);
+ }
+
+ if (adjust_sm0_late)
+ bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate);
+ debug("%d\n", adjust_sm0_late);
+ }
+ debug("%d-%d %d-%d done\n", sm0, sm0_target, sm1, sm1_target);
+
+ return bad ? -1 : 0;
+}
+
+int tps6586x_init(int bus)
+{
+ bus_num = bus;
+ inited = 1;
+
+ return 0;
+}
diff --git a/drivers/power/twl6035.c b/drivers/power/twl6035.c
new file mode 100644
index 0000000000..624c09e85d
--- /dev/null
+++ b/drivers/power/twl6035.c
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 2012
+ * Texas Instruments, <www.ti.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <config.h>
+#include <twl6035.h>
+
+/* Functions to read and write from TWL6030 */
+int twl6035_i2c_write_u8(u8 chip_no, u8 val, u8 reg)
+{
+ return i2c_write(chip_no, reg, 1, &val, 1);
+}
+
+int twl6035_i2c_read_u8(u8 chip_no, u8 *val, u8 reg)
+{
+ return i2c_read(chip_no, reg, 1, val, 1);
+}
+
+/* To align with i2c mw/mr address, reg, val command syntax */
+static inline int palmas_write_u8(u8 chip_no, u8 reg, u8 val)
+{
+ return i2c_write(chip_no, reg, 1, &val, 1);
+}
+
+static inline int palmas_read_u8(u8 chip_no, u8 reg, u8 *val)
+{
+ return i2c_read(chip_no, reg, 1, val, 1);
+}
+
+void twl6035_init_settings(void)
+{
+ return;
+}
+
+void twl6035_mmc1_poweron_ldo(void)
+{
+ u8 val = 0;
+
+ /* set LDO9 TWL6035 to 3V */
+ val = 0x2b; /* (3 -.9)*28 +1 */
+ palmas_write_u8(0x48, LDO9_VOLTAGE, val);
+
+ /* TURN ON LDO9 */
+ val = LDO_ON | LDO_MODE_SLEEP | LDO_MODE_ACTIVE;
+ palmas_write_u8(0x48, LDO9_CTRL, val);
+ return;
+}
diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c
index 3e46e3515f..216c8982be 100644
--- a/drivers/qe/uec.c
+++ b/drivers/qe/uec.c
@@ -1288,7 +1288,7 @@ static void uec_halt(struct eth_device* dev)
uec_stop(uec, COMM_DIR_RX_AND_TX);
}
-static int uec_send(struct eth_device* dev, volatile void *buf, int len)
+static int uec_send(struct eth_device *dev, void *buf, int len)
{
uec_private_t *uec;
ucc_fast_private_t *uccf;
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 616b85703b..6249cc2e21 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -29,6 +29,7 @@ COBJS-$(CONFIG_ALTERA_UART) += altera_uart.o
COBJS-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o
COBJS-$(CONFIG_ARM_DCC) += arm_dcc.o
COBJS-$(CONFIG_ATMEL_USART) += atmel_usart.o
+COBJS-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o
COBJS-$(CONFIG_MCFUART) += mcfuart.o
COBJS-$(CONFIG_NS9750_UART) += ns9750_serial.o
COBJS-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o
@@ -56,10 +57,21 @@ COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o
COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o
COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o
+CFLAGS += -I $(TOPDIR)/pdl/include
+CPPFLAGS += -I $(TOPDIR)/pdl/include
+
ifndef CONFIG_SPL_BUILD
COBJS-$(CONFIG_USB_TTY) += usbtty.o
endif
+ifdef CONFIG_RDA_PDL
+COBJS-$(CONFIG_RDA_USB_SERIAL) += usb-serial.o
+endif
+
+ifndef CONFIG_SPL_BUILD
+COBJS-$(CONFIG_RDA_USB_SERIAL) += usb-serial.o
+endif
+
COBJS := $(sort $(COBJS-y))
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/drivers/serial/lpc32xx_hsuart.c b/drivers/serial/lpc32xx_hsuart.c
new file mode 100644
index 0000000000..8ce3382d86
--- /dev/null
+++ b/drivers/serial/lpc32xx_hsuart.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 Vladimir Zapolskiy <vz@mleia.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <common.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/uart.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct hsuart_regs *hsuart = (struct hsuart_regs *)HS_UART_BASE;
+
+static void lpc32xx_hsuart_set_baudrate(void)
+{
+ u32 div;
+
+ /* UART rate = PERIPH_CLK / ((HSU_RATE + 1) x 14) */
+ div = (get_serial_clock() / 14 + gd->baudrate / 2) / gd->baudrate - 1;
+ if (div > 255)
+ div = 255;
+
+ writel(div, &hsuart->rate);
+}
+
+static int lpc32xx_hsuart_getc(void)
+{
+ while (!(readl(&hsuart->level) & HSUART_LEVEL_RX))
+ /* NOP */;
+
+ return readl(&hsuart->rx) & HSUART_RX_DATA;
+}
+
+static void lpc32xx_hsuart_putc(const char c)
+{
+ writel(c, &hsuart->tx);
+
+ /* Wait for character to be sent */
+ while (readl(&hsuart->level) & HSUART_LEVEL_TX)
+ /* NOP */;
+}
+
+static int lpc32xx_hsuart_tstc(void)
+{
+ if (readl(&hsuart->level) & HSUART_LEVEL_RX)
+ return 1;
+
+ return 0;
+}
+
+static void lpc32xx_hsuart_init(void)
+{
+ lpc32xx_hsuart_set_baudrate();
+
+ /* Disable hardware RTS and CTS flow control, set up RX and TX FIFO */
+ writel(HSUART_CTRL_TMO_16 | HSUART_CTRL_HSU_OFFSET(20) |
+ HSUART_CTRL_HSU_RX_TRIG_32 | HSUART_CTRL_HSU_TX_TRIG_0,
+ &hsuart->ctrl);
+}
+
+void serial_setbrg(void)
+{
+ return lpc32xx_hsuart_set_baudrate();
+}
+
+void serial_putc(const char c)
+{
+ lpc32xx_hsuart_putc(c);
+
+ /* If \n, also do \r */
+ if (c == '\n')
+ lpc32xx_hsuart_putc('\r');
+}
+
+int serial_getc(void)
+{
+ return lpc32xx_hsuart_getc();
+}
+
+void serial_puts(const char *s)
+{
+ while (*s)
+ serial_putc(*s++);
+}
+
+int serial_tstc(void)
+{
+ return lpc32xx_hsuart_tstc();
+}
+
+int serial_init(void)
+{
+ lpc32xx_hsuart_init();
+
+ return 0;
+}
diff --git a/drivers/serial/usb-serial.c b/drivers/serial/usb-serial.c
new file mode 100644
index 0000000000..4138971967
--- /dev/null
+++ b/drivers/serial/usb-serial.c
@@ -0,0 +1,871 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, bodonoghue@codehermit.ie
+ *
+ * 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 <common.h>
+#include <config.h>
+#include <asm/unaligned.h>
+#include <usb/usbserial.h>
+#include "usbser.h"
+#include "usb_cdc_acm.h"
+#include "usbdescriptors.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt,args...)\
+ serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
+#else
+#define DBG(fmt,args...) do{}while(0)
+#endif
+
+#if 1
+#define ERR(fmt,args...)\
+ serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\
+ __LINE__,##args)
+#else
+#define ERR(fmt,args...) do{}while(0)
+#endif
+
+/*
+ * Defines
+ */
+#define NUM_CONFIGS 1
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+#define MAX_INTERFACES 4
+#define NUM_ENDPOINTS 6
+#define NUM_ACM_INTERFACES 4
+#else
+#define MAX_INTERFACES 2
+#define NUM_ENDPOINTS 3
+#define NUM_ACM_INTERFACES 2
+#endif
+
+/*
+ * Buffers to hold input and output data
+ */
+
+
+/*
+ * Instance variables
+ */
+static struct usb_device_instance device_instance[1];
+static struct usb_bus_instance bus_instance[1];
+static struct usb_configuration_instance config_instance[NUM_CONFIGS];
+static struct usb_interface_instance interface_instance[MAX_INTERFACES];
+static struct usb_alternate_instance alternate_instance[MAX_INTERFACES];
+/* one extra for control endpoint */
+static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1];
+
+/*
+ * Global flag
+ */
+int usbser_configured_flag = 0;
+
+
+/* Indicies, References */
+static unsigned short rx_endpoint[2] = {0, 0};
+static unsigned short tx_endpoint[2] = {0, 0};
+
+/* Standard USB Data Structures */
+static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES];
+static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];
+static struct usb_configuration_descriptor *configuration_descriptor = 0;
+static struct usb_device_descriptor device_descriptor = {
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(USB_BCD_VERSION),
+#ifdef CONFIG_USB_ACM_TWO_CHANS /* enable interface association */
+ .bDeviceClass = 0xEF,
+ .bDeviceSubClass = 0x02,
+ .bDeviceProtocol = 0x01,
+#else
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+#endif
+ .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
+ .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID),
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ .idProduct = cpu_to_le16(CONFIG_USBD_PRODUCTID_RDAACM),
+#else
+ .idProduct = cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM),
+#endif
+ .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE),
+ .iManufacturer = 0,
+ .iProduct = 0,
+ .iSerialNumber = 0,
+ .bNumConfigurations = NUM_CONFIGS
+};
+
+
+#if defined(CONFIG_USBD_HS)
+static struct usb_qualifier_descriptor qualifier_descriptor = {
+ .bLength = sizeof(struct usb_qualifier_descriptor),
+ .bDescriptorType = USB_DT_QUAL,
+ .bcdUSB = cpu_to_le16(USB_BCD_VERSION),
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
+ .bNumConfigurations = NUM_CONFIGS
+};
+#endif
+
+/*
+ * Static CDC ACM specific descriptors
+ */
+
+struct acm_config_desc {
+ struct usb_configuration_descriptor configuration_desc;
+
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ /* function 1 */
+ struct usb_function_descriptor function_desc;
+#endif
+
+ /* Master Interface */
+ struct usb_interface_descriptor interface_desc;
+
+ struct usb_class_header_function_descriptor usb_class_header;
+ struct usb_class_call_management_descriptor usb_class_call_mgt;
+ struct usb_class_abstract_control_descriptor usb_class_acm;
+ struct usb_class_union_function_descriptor usb_class_union;
+ struct usb_endpoint_descriptor notification_endpoint;
+
+ /* Slave Interface */
+ struct usb_interface_descriptor data_class_interface;
+ struct usb_endpoint_descriptor data_endpoints[2];
+
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ /* function 2 */
+ struct usb_function_descriptor function_desc2;
+
+ /* Master Interface 2 */
+ struct usb_interface_descriptor interface_desc2;
+
+ struct usb_class_header_function_descriptor usb_class_header2;
+ struct usb_class_call_management_descriptor usb_class_call_mgt2;
+ struct usb_class_abstract_control_descriptor usb_class_acm2;
+ struct usb_class_union_function_descriptor usb_class_union2;
+ struct usb_endpoint_descriptor notification_endpoint2;
+
+ /* Slave Interface 2 */
+ struct usb_interface_descriptor data_class_interface2;
+ struct usb_endpoint_descriptor data_endpoints2[2];
+#endif
+} __attribute__((packed));
+
+static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
+ {
+ .configuration_desc ={
+ .bLength =
+ sizeof(struct usb_configuration_descriptor),
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength =
+ cpu_to_le16(sizeof(struct acm_config_desc)),
+ .bNumInterfaces = NUM_ACM_INTERFACES,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes =
+ BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
+ .bMaxPower = USBTTY_MAXPOWER
+ },
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ /* function 1 */
+ .function_desc = {
+ .bLength = sizeof(struct usb_function_descriptor),
+ .bDescriptorType = 0x0B,
+ .bFirstInterface = 0,
+ .bInterfaceCount = 2,
+ .bFunctionClass = COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
+ .bFunctionSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
+ .iFunction = 0,
+ },
+#endif
+ /* Interface 1 */
+ .interface_desc = {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0x01,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
+ .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
+ .iInterface = 0,
+ },
+ .usb_class_header = {
+ .bFunctionLength =
+ sizeof(struct usb_class_header_function_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_HEADER,
+ .bcdCDC = cpu_to_le16(110),
+ },
+ .usb_class_call_mgt = {
+ .bFunctionLength =
+ sizeof(struct usb_class_call_management_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_CMF,
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
+ },
+ .usb_class_acm = {
+ .bFunctionLength =
+ sizeof(struct usb_class_abstract_control_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_ACMF,
+ .bmCapabilities = 0x00,
+ },
+ .usb_class_union = {
+ .bFunctionLength =
+ sizeof(struct usb_class_union_function_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_UF,
+ .bMasterInterface = 0x00,
+ .bSlaveInterface0 = 0x01,
+ },
+ .notification_endpoint = {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize
+ = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE),
+ .bInterval = 0xb,
+ },
+
+ /* Interface 2 */
+ .data_class_interface = {
+ .bLength =
+ sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x01,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_DATA,
+ .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE,
+ .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE,
+ .iInterface = 0,
+ },
+ .data_endpoints = {
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT,
+ .bmAttributes =
+ USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN,
+ .bmAttributes =
+ USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ },
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ /* function 2 */
+ .function_desc2 = {
+ .bLength = sizeof(struct usb_function_descriptor),
+ .bDescriptorType = 0x0B,
+ .bFirstInterface = 2,
+ .bInterfaceCount = 2,
+ .bFunctionClass = COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
+ .bFunctionSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
+ .iFunction = 0,
+ },
+ /* Interface 3 */
+ .interface_desc2 = {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x02,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0x01,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
+ .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
+ .iInterface = 0,
+ },
+ .usb_class_header2 = {
+ .bFunctionLength =
+ sizeof(struct usb_class_header_function_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_HEADER,
+ .bcdCDC = cpu_to_le16(110),
+ },
+ .usb_class_call_mgt2 = {
+ .bFunctionLength =
+ sizeof(struct usb_class_call_management_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_CMF,
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x03,
+ },
+ .usb_class_acm2 = {
+ .bFunctionLength =
+ sizeof(struct usb_class_abstract_control_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_ACMF,
+ .bmCapabilities = 0x00,
+ },
+ .usb_class_union2 = {
+ .bFunctionLength =
+ sizeof(struct usb_class_union_function_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_UF,
+ .bMasterInterface = 0x02,
+ .bSlaveInterface0 = 0x03,
+ },
+ .notification_endpoint2 = {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_INT_ENDPOINT2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize
+ = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE),
+ .bInterval = 0xb,
+ },
+
+ /* Interface 4 */
+ .data_class_interface2 = {
+ .bLength =
+ sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x03,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_DATA,
+ .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE,
+ .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE,
+ .iInterface = 0,
+ },
+ .data_endpoints2 = {
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_OUT_ENDPOINT2 | USB_DIR_OUT,
+ .bmAttributes =
+ USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_IN_ENDPOINT2 | USB_DIR_IN,
+ .bmAttributes =
+ USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ },
+#endif //CONFIG_USB_ACM_TWO_CHANS
+ },
+};
+
+static struct rs232_emu rs232_desc={
+ .dter = 115200,
+ .stop_bits = 0x00,
+ .parity = 0x00,
+ .data_bits = 0x08
+};
+
+/*
+ * Static Function Prototypes
+ */
+
+static void usbser_init_instances (void);
+static void usbser_init_endpoints (void);
+static void usbser_init_terminal_type(void);
+static void usbser_event_handler (struct usb_device_instance *device,
+ usb_device_event_t event, int data);
+static int usbser_cdc_setup(struct usb_device_request *request,
+ struct urb *urb);
+static int usbser_configured (void);
+
+
+/*
+ * Test whether a character is in the RX buffer
+ */
+
+int usbser_tstc (int chan)
+{
+ udc_irq();
+
+ return udc_chars_in_rxfifo(rx_endpoint[chan]);
+}
+
+/*
+ * Read a single byte from the usb client port. Returns 1 on success, 0
+ * otherwise. When the function is succesfull, the character read is
+ * written into its argument c.
+ */
+int usbser_getc (int chan)
+{
+ unsigned char c = 0;
+
+ usbser_read(chan, &c, 1);
+ return c;
+}
+
+/*
+ * Output a single byte to the usb client port.
+ */
+void usbser_putc (int chan, const char c)
+{
+ usbser_write(chan, (u8 *)&c, 1);
+}
+
+int usbser_read(int chan, unsigned char *_buf, unsigned int len)
+{
+ int count = 0, i;
+ struct usb_endpoint_instance *ep_out = NULL;
+ struct urb *urb = NULL;
+ unsigned char *buf = _buf;
+ unsigned short ep = rx_endpoint[chan];
+ u32 rx_avail;
+
+ for(i = 1; i <= NUM_ENDPOINTS; i++) {
+ if(endpoint_instance[i].endpoint_address == (ep | USB_DIR_OUT))
+ ep_out = &endpoint_instance[i];
+ }
+ if(!ep_out)
+ return -1;
+ urb = ep_out->rcv_urb;
+
+ DBG(" buf len: %d\n", len);
+
+ rx_avail = udc_chars_in_rxfifo(ep);
+ if (rx_avail > 0) {
+ if (len <= rx_avail) {
+ usbd_setup_urb(urb, buf, len, 0);
+ udc_irq();
+ if(!urb->actual_length)
+ poll_rx_ep(ep);
+ usbd_free_urb(urb);
+ return len;
+ } else {
+ //first flush all chars in buffer
+ usbd_setup_urb(urb, buf, rx_avail, 0);
+ udc_irq();
+ if(!urb->actual_length)
+ poll_rx_ep(ep);
+ buf += rx_avail;
+ len -= rx_avail;
+ count += rx_avail;
+ usbd_free_urb(urb);
+ }
+ }
+ //now get remain data...
+ while (len > 0) {
+ int xfer;
+ int dma = 0;
+ int maxPktSize = ep_out->rcv_packetSize;
+
+ xfer = (len > maxPktSize) ? maxPktSize : len;
+ if (xfer == maxPktSize)
+ dma = 1;
+ usbd_setup_urb(urb, buf, xfer, dma);
+ udc_irq();
+ if(!urb->actual_length)
+ poll_rx_ep(ep);
+
+ DBG("urb actual_len :%d\n", urb->actual_length);
+ if (urb->actual_length) {
+ buf += urb->actual_length;
+ len -= urb->actual_length;
+ count += urb->actual_length;
+ usbd_free_urb(urb);
+ }
+ }
+
+ DBG("read : %d\n", count);
+ return count;
+}
+
+int usbser_write(int chan, const unsigned char *_buf, unsigned int len)
+{
+ int i;
+ struct usb_endpoint_instance *ep_in = NULL;
+ struct urb *current_urb = NULL;
+ unsigned char *buf = (unsigned char *)_buf;
+ int count = 0;
+ unsigned short ep = tx_endpoint[chan];
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ static int acm_ch1_opened = 1;
+ if(chan == USB_ACM_CHAN_1 && !acm_ch1_opened)
+ return -1;
+#endif
+
+ for(i = 1; i <= NUM_ENDPOINTS; i++) {
+ if(endpoint_instance[i].endpoint_address == (ep | USB_DIR_IN))
+ ep_in = &endpoint_instance[i];
+ }
+ if(!ep_in)
+ return -1;
+
+ if (!usbser_configured ()) {
+ return 0;
+ }
+ current_urb = ep_in->tx_urb;
+
+ while(len > 0) {
+ int xfer;
+ int maxPktSize = ep_in->rcv_packetSize;
+
+ xfer = (len > maxPktSize) ? maxPktSize : len;
+
+ current_urb->buffer = buf;
+ current_urb->actual_length = xfer;
+
+ if(udc_endpoint_write (ep_in))
+ goto oops;
+ count += xfer;
+ len -= xfer;
+ buf += xfer;
+ }
+
+ DBG("after write urb actual_len :%d count %d\n",
+ current_urb->actual_length, count);
+ return count;
+
+oops:
+ serial_puts("usb write error\n");
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ if(chan == USB_ACM_CHAN_1)
+ acm_ch1_opened = 0;
+#endif
+ return -1;
+}
+
+int drv_usbser_init (void)
+{
+ static int usb_init_flag = 0;
+
+ if (usb_init_flag)
+ return 0;
+ usb_init_flag = 1;
+
+ /* Decide on which type of UDC device to be.
+ */
+ usbser_init_terminal_type();
+
+ /* Now, set up USB controller and infrastructure */
+ if (udc_is_initialized()) {
+ printf("UDC software initializing...\n");
+ udc_soft_init (); /* Basic USB initialization */
+ } else {
+ printf("UDC hardware initializing...\n");
+ udc_init ();
+ }
+
+ usbser_init_instances ();
+ usbser_init_endpoints ();
+
+ if (!udc_is_initialized()) {
+ udc_startup_events (device_instance);/* Enable dev, init udc pointers */
+ udc_connect (); /* Enable pullup for host detection */
+ } else {
+ udc_hot_startup(device_instance);
+ }
+
+ return 0;
+}
+
+
+#define init_wMaxPacketSize(x) le16_to_cpu(get_unaligned(\
+ &ep_descriptor_ptrs[(x) - 1]->wMaxPacketSize));
+
+#define init_write_wMaxPacketSize(val, x) (put_unaligned(le16_to_cpu(val),\
+ &ep_descriptor_ptrs[(x) - 1]->wMaxPacketSize));
+
+static struct urb urb_pool[NUM_ENDPOINTS];
+static u8 int_ep_buffer[URB_BUF_SIZE] __attribute__((__aligned__(4)));
+
+static int endpoint_is_int(struct usb_endpoint_instance *instance)
+{
+ if (!instance)
+ return 0;
+
+ if (instance->tx_attributes == USB_ENDPOINT_XFER_INT)
+ return 1;
+ else
+ return 0;
+}
+static void usbser_init_instances (void)
+{
+ int i;
+
+ /* initialize device instance */
+ memset (device_instance, 0, sizeof (struct usb_device_instance));
+ if (!udc_is_initialized())
+ device_instance->device_state = STATE_INIT;
+ else
+ device_instance->device_state = STATE_CONFIGURED;
+ device_instance->device_descriptor = &device_descriptor;
+#if defined(CONFIG_USBD_HS)
+ device_instance->qualifier_descriptor = &qualifier_descriptor;
+#endif
+ device_instance->event = usbser_event_handler;
+ device_instance->cdc_recv_setup = usbser_cdc_setup;
+ device_instance->bus = bus_instance;
+ device_instance->configurations = NUM_CONFIGS;
+ device_instance->configuration_instance_array = config_instance;
+
+ /* initialize bus instance */
+ memset (bus_instance, 0, sizeof (struct usb_bus_instance));
+ bus_instance->device = device_instance;
+ bus_instance->endpoint_array = endpoint_instance;
+ bus_instance->max_endpoints = 1;
+ bus_instance->maxpacketsize = 64;
+
+ /* configuration instance */
+ memset (config_instance, 0,
+ sizeof (struct usb_configuration_instance));
+ config_instance->interfaces = NUM_ACM_INTERFACES;
+ config_instance->configuration_descriptor = configuration_descriptor;
+ config_instance->interface_instance_array = interface_instance;
+
+ /* interface instance */
+ memset (interface_instance, 0,
+ sizeof (struct usb_interface_instance));
+ interface_instance->alternates = 1;
+ interface_instance->alternates_instance_array = alternate_instance;
+
+ /* alternates instance */
+ memset (alternate_instance, 0,
+ sizeof (struct usb_alternate_instance));
+ alternate_instance->interface_descriptor = interface_descriptors;
+ alternate_instance->endpoints = NUM_ENDPOINTS;
+ alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs;
+
+ /* endpoint instances */
+ memset (&endpoint_instance[0], 0,
+ sizeof (struct usb_endpoint_instance));
+ endpoint_instance[0].endpoint_address = 0;
+ endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE;
+ endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL;
+ endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE;
+ endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL;
+ udc_setup_ep (device_instance, 0, &endpoint_instance[0]);
+
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ memset (&endpoint_instance[i], 0,
+ sizeof (struct usb_endpoint_instance));
+
+ endpoint_instance[i].endpoint_address =
+ ep_descriptor_ptrs[i - 1]->bEndpointAddress;
+
+ endpoint_instance[i].rcv_attributes =
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+ endpoint_instance[i].rcv_packetSize = init_wMaxPacketSize(i);
+
+ endpoint_instance[i].tx_attributes =
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+ endpoint_instance[i].tx_packetSize = init_wMaxPacketSize(i);
+
+
+ urb_link_init (&endpoint_instance[i].rcv);
+ urb_link_init (&endpoint_instance[i].rdy);
+ urb_link_init (&endpoint_instance[i].tx);
+ urb_link_init (&endpoint_instance[i].done);
+
+ if (endpoint_instance[i].endpoint_address & USB_DIR_IN) {
+ endpoint_instance[i].tx_urb = &urb_pool[i - 1];
+ /* function only has one interrupt endpoint */
+ if (endpoint_is_int(&endpoint_instance[i]))
+ usbd_init_urb (endpoint_instance[i].tx_urb, device_instance,
+ &endpoint_instance[i], int_ep_buffer, URB_BUF_SIZE);
+ else // bulk in&out re-use buffer with application driver
+ usbd_init_urb (endpoint_instance[i].tx_urb, device_instance,
+ &endpoint_instance[i], NULL, 0);
+ } else {
+ endpoint_instance[i].rcv_urb = &urb_pool[i - 1];
+ usbd_init_urb (endpoint_instance[i].rcv_urb, device_instance,
+ &endpoint_instance[i], NULL, 0);
+ }
+ }
+}
+
+static void usbser_init_endpoints (void)
+{
+ int i;
+
+ bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ if (((ep_descriptor_ptrs[i]->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)
+ && is_usbd_high_speed()) {
+
+ init_write_wMaxPacketSize(CONFIG_USBD_SERIAL_BULK_HS_PKTSIZE, i+1);
+ }
+
+ endpoint_instance[i + 1].tx_packetSize = init_wMaxPacketSize(i + 1);
+ endpoint_instance[i + 1].rcv_packetSize = init_wMaxPacketSize(i + 1);
+
+ udc_setup_ep (device_instance, i + 1, &endpoint_instance[i+1]);
+ }
+}
+
+/* usbser_init_terminal_type
+ *
+ * Do some late binding for our device type.
+ */
+static void usbser_init_terminal_type(void)
+{
+ /* CDC ACM */
+ /* Assign endpoint descriptors */
+ ep_descriptor_ptrs[0] =
+ &acm_configuration_descriptors[0].notification_endpoint;
+ ep_descriptor_ptrs[1] =
+ &acm_configuration_descriptors[0].data_endpoints[0];
+ ep_descriptor_ptrs[2] =
+ &acm_configuration_descriptors[0].data_endpoints[1];
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ ep_descriptor_ptrs[3] =
+ &acm_configuration_descriptors[0].notification_endpoint2;
+ ep_descriptor_ptrs[4] =
+ &acm_configuration_descriptors[0].data_endpoints2[0];
+ ep_descriptor_ptrs[5] =
+ &acm_configuration_descriptors[0].data_endpoints2[1];
+#endif
+
+#if defined(CONFIG_USBD_HS)
+ qualifier_descriptor.bDeviceClass =
+ COMMUNICATIONS_DEVICE_CLASS;
+#endif
+ /* Assign endpoint indices */
+ tx_endpoint[0] = UDC_IN_ENDPOINT;
+ rx_endpoint[0] = UDC_OUT_ENDPOINT;
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ tx_endpoint[1] = UDC_IN_ENDPOINT2;
+ rx_endpoint[1] = UDC_OUT_ENDPOINT2;
+#endif
+
+ /* Configuration Descriptor */
+ configuration_descriptor =
+ (struct usb_configuration_descriptor*)
+ &acm_configuration_descriptors;
+
+}
+
+static int usbser_configured (void)
+{
+ return usbser_configured_flag;
+}
+
+/******************************************************************************/
+
+static void usbser_event_handler (struct usb_device_instance *device,
+ usb_device_event_t event, int data)
+{
+ switch (event) {
+ case DEVICE_RESET:
+ case DEVICE_BUS_INACTIVE:
+ usbser_configured_flag = 0;
+ break;
+ case DEVICE_CONFIGURED:
+ usbser_configured_flag = 1;
+ break;
+
+ case DEVICE_ADDRESS_ASSIGNED:
+ usbser_init_endpoints ();
+
+ default:
+ break;
+ }
+}
+
+/******************************************************************************/
+
+int usbser_cdc_setup(struct usb_device_request *request, struct urb *urb)
+{
+ switch (request->bRequest){
+
+ case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */
+ break;
+ case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */
+ break;
+ case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits
+ * per character */
+ break;
+ case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */
+ break;
+ case ACM_GET_LINE_ENCODING : /* request DTE rate,
+ * stop/parity bits */
+ memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc));
+ urb->actual_length = sizeof(rs232_desc);
+
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+#include <command.h>
+int usbser_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
+{
+ char c;
+
+ drv_usbser_init();
+ serial_puts("usb tty test\n");
+ while (1) {
+ c = usbser_getc(USB_ACM_CHAN_0);
+ serial_putc(c);
+ //usbser_puts("from usb ");
+ usbser_putc(USB_ACM_CHAN_0, c);
+ //usbser_puts("\n");
+ if (c == 'Q' || c == 'q')
+ break;
+ }
+ return 0;
+}
+
+U_BOOT_CMD(usbser_test, 1, 1, usbser_test,
+ "rda usbserial test", "loopback everything to user");
diff --git a/drivers/serial/usbser.h b/drivers/serial/usbser.h
new file mode 100644
index 0000000000..7c244d3641
--- /dev/null
+++ b/drivers/serial/usbser.h
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit
+ *
+ * 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
+ *
+ */
+
+#ifndef __USB_SER_H__
+#define __USB_SER_H__
+
+#include <usbdevice.h>
+#include <usb/musb_udc.h>
+
+
+
+/* If no VendorID/ProductID is defined in config.h, pretend to be Linux
+ * DO NOT Reuse this Vendor/Product setup with protocol incompatible devices */
+#ifndef CONFIG_USBD_VENDORID
+#define CONFIG_USBD_VENDORID 0x0525 /* Linux/NetChip */
+#endif
+#ifndef CONFIG_USBD_PRODUCTID_GSERIAL
+#define CONFIG_USBD_PRODUCTID_GSERIAL 0xa4a6 /* gserial */
+#endif
+#ifndef CONFIG_USBD_PRODUCTID_CDCACM
+#define CONFIG_USBD_PRODUCTID_CDCACM 0xa4a7 /* CDC ACM */
+#endif
+#ifndef CONFIG_USBD_PRODUCTID_RDAACM
+#define CONFIG_USBD_PRODUCTID_RDAACM 0xa4a9 /* for two acm channels */
+#endif
+
+#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT
+#define CONFIG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT
+#define CONFIG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT
+#define CONFIG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE
+
+#if defined(CONFIG_USBD_HS)
+#define CONFIG_USBD_SERIAL_BULK_HS_PKTSIZE UDC_BULK_HS_PACKET_SIZE
+#endif
+
+#define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS
+
+#define USBTTY_BCD_DEVICE 0x00
+#define USBTTY_MAXPOWER 0x00
+
+#endif
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 4e6f14ee07..7859536a67 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -34,6 +34,8 @@
#define MXS_SPI_MAX_TIMEOUT 1000000
#define MXS_SPI_PORT_OFFSET 0x2000
+#define MXS_SSP_CHIPSELECT_MASK 0x00300000
+#define MXS_SSP_CHIPSELECT_SHIFT 20
struct mxs_spi_slave {
struct spi_slave slave;
@@ -51,14 +53,25 @@ void spi_init(void)
{
}
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ /* MXS SPI: 4 ports and 3 chip selects maximum */
+ if (bus > 3 || cs > 2)
+ return 0;
+ else
+ return 1;
+}
+
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct mxs_spi_slave *mxs_slave;
uint32_t addr;
+ struct mx28_ssp_regs *ssp_regs;
+ int reg;
- if (bus > 3) {
- printf("MXS SPI: Max bus number is 3\n");
+ if (!spi_cs_is_valid(bus, cs)) {
+ printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
return NULL;
}
@@ -73,7 +86,13 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
mxs_slave->regs = (struct mx28_ssp_regs *)addr;
+ ssp_regs = mxs_slave->regs;
+
+ reg = readl(&ssp_regs->hw_ssp_ctrl0);
+ reg &= ~(MXS_SSP_CHIPSELECT_MASK);
+ reg |= cs << MXS_SSP_CHIPSELECT_SHIFT;
+ writel(reg, &ssp_regs->hw_ssp_ctrl0);
return &mxs_slave->slave;
}
diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c
index 2f116c0cb6..a3bf51a64d 100644
--- a/drivers/usb/eth/asix.c
+++ b/drivers/usb/eth/asix.c
@@ -419,7 +419,7 @@ out_err:
return -1;
}
-static int asix_send(struct eth_device *eth, volatile void *packet, int length)
+static int asix_send(struct eth_device *eth, void *packet, int length)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
int err;
diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c
index 2f63340fdb..c7aebea4e3 100644
--- a/drivers/usb/eth/smsc95xx.c
+++ b/drivers/usb/eth/smsc95xx.c
@@ -657,8 +657,7 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
return 0;
}
-static int smsc95xx_send(struct eth_device *eth, volatile void* packet,
- int length)
+static int smsc95xx_send(struct eth_device *eth, void* packet, int length)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
int err;
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 87d1918cb9..c3eca4b9fe 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -47,6 +47,10 @@ COBJS-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o
endif
endif
+ifndef CONFIG_SPL_BUILD
+COBJS-$(CONFIG_USB_FASTBOOT) += f_fastboot.o fastboot.o
+endif
+
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
new file mode 100644
index 0000000000..ebb5131a9c
--- /dev/null
+++ b/drivers/usb/gadget/composite.c
@@ -0,0 +1,1082 @@
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ * U-boot porting: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * 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
+ */
+#undef DEBUG
+
+#include <linux/bitops.h>
+#include <linux/usb/composite.h>
+
+#define USB_BUFSIZ 4096
+
+static struct usb_composite_driver *composite;
+
+/**
+ * usb_add_function() - add a function to a configuration
+ * @config: the configuration
+ * @function: the function being added
+ * Context: single threaded during gadget setup
+ *
+ * After initialization, each configuration must have one or more
+ * functions added to it. Adding a function involves calling its @bind()
+ * method to allocate resources such as interface and string identifiers
+ * and endpoints.
+ *
+ * This function returns the value of the function's bind(), which is
+ * zero for success else a negative errno value.
+ */
+int usb_add_function(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ int value = -EINVAL;
+
+ debug("adding '%s'/%p to config '%s'/%p\n",
+ function->name, function,
+ config->label, config);
+
+ if (!function->set_alt || !function->disable)
+ goto done;
+
+ function->config = config;
+ list_add_tail(&function->list, &config->functions);
+
+ if (function->bind) {
+ value = function->bind(config, function);
+ if (value < 0) {
+ list_del(&function->list);
+ function->config = NULL;
+ }
+ } else
+ value = 0;
+
+ if (!config->fullspeed && function->descriptors)
+ config->fullspeed = 1;
+ if (!config->highspeed && function->hs_descriptors)
+ config->highspeed = 1;
+
+done:
+ if (value)
+ debug("adding '%s'/%p --> %d\n",
+ function->name, function, value);
+ return value;
+}
+
+/**
+ * usb_function_deactivate - prevent function and gadget enumeration
+ * @function: the function that isn't yet ready to respond
+ *
+ * Blocks response of the gadget driver to host enumeration by
+ * preventing the data line pullup from being activated. This is
+ * normally called during @bind() processing to change from the
+ * initial "ready to respond" state, or when a required resource
+ * becomes available.
+ *
+ * For example, drivers that serve as a passthrough to a userspace
+ * daemon can block enumeration unless that daemon (such as an OBEX,
+ * MTP, or print server) is ready to handle host requests.
+ *
+ * Not all systems support software control of their USB peripheral
+ * data pullups.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_deactivate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ int status = 0;
+
+ if (cdev->deactivations == 0)
+ status = usb_gadget_disconnect(cdev->gadget);
+ if (status == 0)
+ cdev->deactivations++;
+
+ return status;
+}
+
+/**
+ * usb_function_activate - allow function and gadget enumeration
+ * @function: function on which usb_function_activate() was called
+ *
+ * Reverses effect of usb_function_deactivate(). If no more functions
+ * are delaying their activation, the gadget driver will respond to
+ * host enumeration procedures.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_activate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ int status = 0;
+
+ if (cdev->deactivations == 0)
+ status = -EINVAL;
+ else {
+ cdev->deactivations--;
+ if (cdev->deactivations == 0)
+ status = usb_gadget_connect(cdev->gadget);
+ }
+
+ return status;
+}
+
+/**
+ * usb_interface_id() - allocate an unused interface ID
+ * @config: configuration associated with the interface
+ * @function: function handling the interface
+ * Context: single threaded during gadget setup
+ *
+ * usb_interface_id() is called from usb_function.bind() callbacks to
+ * allocate new interface IDs. The function driver will then store that
+ * ID in interface, association, CDC union, and other descriptors. It
+ * will also handle any control requests targetted at that interface,
+ * particularly changing its altsetting via set_alt(). There may
+ * also be class-specific or vendor-specific requests to handle.
+ *
+ * All interface identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier. Note that since interface
+ * identifers are configuration-specific, functions used in more than
+ * one configuration (or more than once in a given configuration) need
+ * multiple versions of the relevant descriptors.
+ *
+ * Returns the interface ID which was allocated; or -ENODEV if no
+ * more interface IDs can be allocated.
+ */
+int usb_interface_id(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ unsigned char id = config->next_interface_id;
+
+ if (id < MAX_CONFIG_INTERFACES) {
+ config->interface[id] = function;
+ config->next_interface_id = id + 1;
+ return id;
+ }
+ return -ENODEV;
+}
+
+static int config_buf(struct usb_configuration *config,
+ enum usb_device_speed speed, void *buf, u8 type)
+{
+ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ void *next = buf + USB_DT_CONFIG_SIZE;
+ struct usb_descriptor_header **descriptors;
+ struct usb_config_descriptor *c = buf;
+ int status;
+ struct usb_function *f;
+
+ /* write the config descriptor */
+ c = buf;
+ c->bLength = USB_DT_CONFIG_SIZE;
+ c->bDescriptorType = type;
+
+ c->bNumInterfaces = config->next_interface_id;
+ c->bConfigurationValue = config->bConfigurationValue;
+ c->iConfiguration = config->iConfiguration;
+ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
+ c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);
+
+ /* There may be e.g. OTG descriptors */
+ if (config->descriptors) {
+ status = usb_descriptor_fillbuf(next, len,
+ config->descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ /* add each function's descriptors */
+ list_for_each_entry(f, &config->functions, list) {
+ if (speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+ if (!descriptors)
+ continue;
+ status = usb_descriptor_fillbuf(next, len,
+ (const struct usb_descriptor_header **) descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ len = next - buf;
+ c->wTotalLength = cpu_to_le16(len);
+ return len;
+}
+
+static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
+{
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+ struct usb_gadget *gadget = cdev->gadget;
+ u8 type = w_value >> 8;
+ int hs = 0;
+ struct usb_configuration *c;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ if (hs)
+ speed = USB_SPEED_HIGH;
+ }
+
+ w_value &= 0xff;
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (speed == USB_SPEED_HIGH) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ if (w_value == 0)
+ return config_buf(c, speed, cdev->req->buf, type);
+ w_value--;
+ }
+ return -EINVAL;
+}
+
+static int count_configs(struct usb_composite_dev *cdev, unsigned type)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ unsigned count = 0;
+ int hs = 0;
+ struct usb_configuration *c;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_DEVICE_QUALIFIER)
+ hs = !hs;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ if (hs) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ count++;
+ }
+ return count;
+}
+
+static void device_qual(struct usb_composite_dev *cdev)
+{
+ struct usb_qualifier_descriptor *qual = cdev->req->buf;
+
+ qual->bLength = sizeof(*qual);
+ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ /* POLICY: same bcdUSB and device type info at both speeds */
+ qual->bcdUSB = cdev->desc.bcdUSB;
+ qual->bDeviceClass = cdev->desc.bDeviceClass;
+ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
+ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
+ /* ASSUME same EP0 fifo size at both speeds */
+ qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0;
+ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
+ qual->bRESERVED = 0;
+}
+
+static void reset_config(struct usb_composite_dev *cdev)
+{
+ struct usb_function *f;
+
+ debug("%s:\n", __func__);
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->disable)
+ f->disable(f);
+
+ bitmap_zero(f->endpoints, 32);
+ }
+ cdev->config = NULL;
+}
+
+static int set_config(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ unsigned power = gadget_is_otg(gadget) ? 8 : 100;
+ struct usb_descriptor_header **descriptors;
+ int result = -EINVAL;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_configuration *c = NULL;
+ int addr;
+ int tmp;
+ struct usb_function *f;
+
+ if (cdev->config)
+ reset_config(cdev);
+
+ if (number) {
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == number) {
+ result = 0;
+ break;
+ }
+ }
+ if (result < 0)
+ goto done;
+ } else
+ result = 0;
+
+ debug("%s: %s speed config #%d: %s\n", __func__,
+ ({ char *speed;
+ switch (gadget->speed) {
+ case USB_SPEED_LOW:
+ speed = "low";
+ break;
+ case USB_SPEED_FULL:
+ speed = "full";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "high";
+ break;
+ default:
+ speed = "?";
+ break;
+ };
+ speed;
+ }), number, c ? c->label : "unconfigured");
+
+ if (!c)
+ goto done;
+
+ cdev->config = c;
+
+ /* Initialize all interfaces by setting them to altsetting zero. */
+ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
+ f = c->interface[tmp];
+ if (!f)
+ break;
+
+ /*
+ * Record which endpoints are used by the function. This is used
+ * to dispatch control requests targeted at that endpoint to the
+ * function's setup callback instead of the current
+ * configuration's setup callback.
+ */
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)*descriptors;
+ addr = ((ep->bEndpointAddress & 0x80) >> 3)
+ | (ep->bEndpointAddress & 0x0f);
+ __set_bit(addr, f->endpoints);
+ }
+
+ result = f->set_alt(f, tmp, 0);
+ if (result < 0) {
+ debug("interface %d (%s/%p) alt 0 --> %d\n",
+ tmp, f->name, f, result);
+
+ reset_config(cdev);
+ goto done;
+ }
+ }
+
+ /* when we return, be sure our power usage is valid */
+ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
+done:
+ usb_gadget_vbus_draw(gadget, power);
+ return result;
+}
+
+/**
+ * usb_add_config() - add a configuration to a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration, with bConfigurationValue assigned
+ * Context: single threaded during gadget setup
+ *
+ * One of the main tasks of a composite driver's bind() routine is to
+ * add each of the configurations it supports, using this routine.
+ *
+ * This function returns the value of the configuration's bind(), which
+ * is zero for success else a negative errno value. Binding configurations
+ * assigns global resources including string IDs, and per-configuration
+ * resources such as interface IDs and endpoints.
+ */
+int usb_add_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ int status = -EINVAL;
+ struct usb_configuration *c;
+ struct usb_function *f;
+ unsigned int i;
+
+ debug("%s: adding config #%u '%s'/%p\n", __func__,
+ config->bConfigurationValue,
+ config->label, config);
+
+ if (!config->bConfigurationValue || !config->bind)
+ goto done;
+
+ /* Prevent duplicate configuration identifiers */
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == config->bConfigurationValue) {
+ status = -EBUSY;
+ goto done;
+ }
+ }
+
+ config->cdev = cdev;
+ list_add_tail(&config->list, &cdev->configs);
+
+ INIT_LIST_HEAD(&config->functions);
+ config->next_interface_id = 0;
+
+ status = config->bind(config);
+ if (status < 0) {
+ list_del(&config->list);
+ config->cdev = NULL;
+ } else {
+ debug("cfg %d/%p speeds:%s%s\n",
+ config->bConfigurationValue, config,
+ config->highspeed ? " high" : "",
+ config->fullspeed
+ ? (gadget_is_dualspeed(cdev->gadget)
+ ? " full"
+ : " full/low")
+ : "");
+
+ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
+ f = config->interface[i];
+ if (!f)
+ continue;
+ debug("%s: interface %d = %s/%p\n",
+ __func__, i, f->name, f);
+ }
+ }
+
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+done:
+ if (status)
+ debug("added config '%s'/%u --> %d\n", config->label,
+ config->bConfigurationValue, status);
+ return status;
+}
+
+/*
+ * We support strings in multiple languages ... string descriptor zero
+ * says which languages are supported. The typical case will be that
+ * only one language (probably English) is used, with I18N handled on
+ * the host side.
+ */
+
+static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+ const struct usb_gadget_strings *s;
+ u16 language;
+ __le16 *tmp;
+
+ while (*sp) {
+ s = *sp;
+ language = cpu_to_le16(s->language);
+ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+ if (*tmp == language)
+ goto repeat;
+ }
+ *tmp++ = language;
+repeat:
+ sp++;
+ }
+}
+
+static int lookup_string(
+ struct usb_gadget_strings **sp,
+ void *buf,
+ u16 language,
+ int id
+)
+{
+ int value;
+ struct usb_gadget_strings *s;
+
+ while (*sp) {
+ s = *sp++;
+ if (s->language != language)
+ continue;
+ value = usb_gadget_get_string(s, id, buf);
+ if (value > 0)
+ return value;
+ }
+ return -EINVAL;
+}
+
+static int get_string(struct usb_composite_dev *cdev,
+ void *buf, u16 language, int id)
+{
+ struct usb_string_descriptor *s = buf;
+ struct usb_gadget_strings **sp;
+ int len;
+ struct usb_configuration *c;
+ struct usb_function *f;
+
+ /*
+ * Yes, not only is USB's I18N support probably more than most
+ * folk will ever care about ... also, it's all supported here.
+ * (Except for UTF8 support for Unicode's "Astral Planes".)
+ */
+
+ /* 0 == report all available language codes */
+ if (id == 0) {
+ memset(s, 0, 256);
+ s->bDescriptorType = USB_DT_STRING;
+
+ sp = composite->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(c, &cdev->configs, list) {
+ sp = c->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(f, &c->functions, list) {
+ sp = f->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+ }
+ }
+
+ for (len = 0; len <= 126 && s->wData[len]; len++)
+ continue;
+ if (!len)
+ return -EINVAL;
+
+ s->bLength = 2 * (len + 1);
+ return s->bLength;
+ }
+
+ /*
+ * Otherwise, look up and return a specified string. String IDs
+ * are device-scoped, so we look up each string table we're told
+ * about. These lookups are infrequent; simpler-is-better here.
+ */
+ if (composite->strings) {
+ len = lookup_string(composite->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->strings) {
+ len = lookup_string(c->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(f, &c->functions, list) {
+ if (!f->strings)
+ continue;
+ len = lookup_string(f->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * usb_string_id() - allocate an unused string ID
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_id() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
+ */
+int usb_string_id(struct usb_composite_dev *cdev)
+{
+ if (cdev->next_string_id < 254) {
+ /*
+ * string id 0 is reserved by USB spec for list of
+ * supported languages
+ * 255 reserved as well? -- mina86
+ */
+ cdev->next_string_id++;
+ return cdev->next_string_id;
+ }
+ return -ENODEV;
+}
+
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+ u8 next = cdev->next_string_id;
+
+ for (; str->s; ++str) {
+ if (next >= 254)
+ return -ENODEV;
+ str->id = ++next;
+ }
+
+ cdev->next_string_id = next;
+
+ return 0;
+}
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @c: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID. This ID and next @n-1 IDs are now
+ * valid IDs. At least provided that @n is non-zero because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+ u8 next = c->next_string_id;
+
+ if (n > 254 || next + n > 254)
+ return -ENODEV;
+
+ c->next_string_id += n;
+ return next + 1;
+}
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ debug("%s: setup complete --> %d, %d/%d\n", __func__,
+ req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config and function specific setup.
+ */
+static int
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ u8 intf = w_index & 0xFF;
+ int value = -EOPNOTSUPP;
+ struct usb_request *req = cdev->req;
+ struct usb_function *f = NULL;
+ int standard;
+ u8 endp;
+ struct usb_configuration *c;
+
+ /*
+ * partial re-init of the response message; the function or the
+ * gadget might need to intercept e.g. a control-OUT completion
+ * when we delegate to it.
+ */
+ req->zero = 0;
+ req->complete = composite_setup_complete;
+ req->length = USB_BUFSIZ;
+ gadget->ep0->driver_data = cdev;
+ standard = (ctrl->bRequestType & USB_TYPE_MASK)
+ == USB_TYPE_STANDARD;
+ if (!standard)
+ goto unknown;
+
+ switch (ctrl->bRequest) {
+
+ /* we handle all standard USB descriptors */
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ cdev->desc.bNumConfigurations =
+ count_configs(cdev, USB_DT_DEVICE);
+ value = min(w_length, (u16) sizeof cdev->desc);
+ memcpy(req->buf, &cdev->desc, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ device_qual(cdev);
+ value = min(w_length,
+ sizeof(struct usb_qualifier_descriptor));
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+
+ case USB_DT_CONFIG:
+ value = config_desc(cdev, w_value);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_STRING:
+ value = get_string(cdev, req->buf,
+ w_index, w_value & 0xff);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ default:
+ goto unknown;
+ }
+ break;
+
+ /* any number of configs can work */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ goto unknown;
+ if (gadget_is_otg(gadget)) {
+ if (gadget->a_hnp_support)
+ debug("HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ debug("HNP on another port\n");
+ else
+ debug("HNP inactive\n");
+ }
+
+ value = set_config(cdev, ctrl, w_value);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ if (cdev->config)
+ *(u8 *)req->buf = cdev->config->bConfigurationValue;
+ else
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /*
+ * function drivers must handle get/set altsetting; if there's
+ * no get() method, we know only altsetting zero works.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+ goto unknown;
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ if (w_value && !f->set_alt)
+ break;
+ value = f->set_alt(f, w_index, w_value);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+ goto unknown;
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ /* lots of interfaces only need altsetting zero... */
+ value = f->get_alt ? f->get_alt(f, w_index) : 0;
+ if (value < 0)
+ break;
+ *((u8 *)req->buf) = value;
+ value = min(w_length, (u16) 1);
+ break;
+ default:
+unknown:
+ debug("non-core control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /*
+ * functions always handle their interfaces and endpoints...
+ * punt other recipients (other, WUSB, ...) to the current
+ * configuration code.
+ */
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ f = cdev->config->interface[intf];
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (test_bit(endp, f->endpoints))
+ break;
+ }
+ if (&f->list == &cdev->config->functions)
+ f = NULL;
+ break;
+ }
+
+ if (f && f->setup)
+ value = f->setup(f, ctrl);
+ else {
+ c = cdev->config;
+ if (c && c->setup)
+ value = c->setup(c, ctrl);
+ }
+
+ goto done;
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(gadget->ep0, req);
+ }
+ }
+
+done:
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void composite_disconnect(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ if (cdev->config)
+ reset_config(cdev);
+ if (composite->disconnect)
+ composite->disconnect(cdev);
+}
+
+static void composite_unbind(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_configuration *c;
+ struct usb_function *f;
+
+ /*
+ * composite_disconnect() must already have been called
+ * by the underlying peripheral controller driver!
+ * so there's no i/o concurrency that could affect the
+ * state protected by cdev->lock.
+ */
+ BUG_ON(cdev->config);
+
+ while (!list_empty(&cdev->configs)) {
+ c = list_first_entry(&cdev->configs,
+ struct usb_configuration, list);
+ while (!list_empty(&c->functions)) {
+ f = list_first_entry(&c->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ debug("unbind function '%s'/%p\n",
+ f->name, f);
+ f->unbind(c, f);
+ }
+ }
+ list_del(&c->list);
+ if (c->unbind) {
+ debug("unbind config '%s'/%p\n", c->label, c);
+ c->unbind(c);
+ }
+ }
+ if (composite->unbind)
+ composite->unbind(cdev);
+
+ if (cdev->req) {
+ kfree(cdev->req->buf);
+ usb_ep_free_request(gadget->ep0, cdev->req);
+ }
+ kfree(cdev);
+ set_gadget_data(gadget, NULL);
+
+ composite = NULL;
+}
+
+static int composite_bind(struct usb_gadget *gadget)
+{
+ int status = -ENOMEM;
+ struct usb_composite_dev *cdev;
+
+ cdev = calloc(sizeof *cdev, 1);
+ if (!cdev)
+ return status;
+
+ cdev->gadget = gadget;
+ set_gadget_data(gadget, cdev);
+ INIT_LIST_HEAD(&cdev->configs);
+
+ /* preallocate control response and buffer */
+ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!cdev->req)
+ goto fail;
+ cdev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, USB_BUFSIZ);
+ if (!cdev->req->buf)
+ goto fail;
+ cdev->req->complete = composite_setup_complete;
+ gadget->ep0->driver_data = cdev;
+
+ cdev->bufsiz = USB_BUFSIZ;
+ cdev->driver = composite;
+
+ usb_gadget_set_selfpowered(gadget);
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+ status = composite->bind(cdev);
+ if (status < 0)
+ goto fail;
+
+ cdev->desc = *composite->dev;
+ cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+ debug("%s: ready\n", composite->name);
+ return 0;
+
+fail:
+ composite_unbind(gadget);
+ return status;
+}
+
+static void
+composite_suspend(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ debug("%s: suspend\n", __func__);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->suspend)
+ f->suspend(f);
+ }
+ }
+ if (composite->suspend)
+ composite->suspend(cdev);
+
+ cdev->suspended = 1;
+}
+
+static void
+composite_resume(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ debug("%s: resume\n", __func__);
+ if (composite->resume)
+ composite->resume(cdev);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->resume)
+ f->resume(f);
+ }
+ }
+
+ cdev->suspended = 0;
+}
+
+static struct usb_gadget_driver composite_driver = {
+ .speed = USB_SPEED_HIGH,
+
+ .bind = composite_bind,
+ .unbind = composite_unbind,
+
+ .setup = composite_setup,
+ .disconnect = composite_disconnect,
+
+ .suspend = composite_suspend,
+ .resume = composite_resume,
+};
+
+/**
+ * usb_composite_register() - register a composite driver
+ * @driver: the driver to register
+ * Context: single threaded during gadget setup
+ *
+ * This function is used to register drivers using the composite driver
+ * framework. The return value is zero, or a negative errno value.
+ * Those values normally come from the driver's @bind method, which does
+ * all the work of setting up the driver to match the hardware.
+ *
+ * On successful return, the gadget is ready to respond to requests from
+ * the host, unless one of its components invokes usb_gadget_disconnect()
+ * while it was binding. That would usually be done in order to wait for
+ * some userspace participation.
+ */
+int usb_composite_register(struct usb_composite_driver *driver)
+{
+ if (!driver || !driver->dev || !driver->bind || composite)
+ return -EINVAL;
+
+ if (!driver->name)
+ driver->name = "composite";
+ composite = driver;
+
+ return usb_gadget_register_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_unregister() - unregister a composite driver
+ * @driver: the driver to unregister
+ *
+ * This function is used to unregister drivers using the composite
+ * driver framework.
+ */
+void usb_composite_unregister(struct usb_composite_driver *driver)
+{
+ if (composite != driver)
+ return;
+ usb_gadget_unregister_driver(&composite_driver);
+}
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index f9163a80ed..f88d0c190c 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -27,6 +27,7 @@
#include <linux/string.h>
#include <linux/usb/ch9.h>
+#include <usbdescriptors.h>
#include <linux/usb/gadget.h>
diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c
index 46ab3f67f8..1ab14eb3b4 100644
--- a/drivers/usb/gadget/core.c
+++ b/drivers/usb/gadget/core.c
@@ -364,11 +364,12 @@ void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_
/*rcv_urb->actual_length, rcv_urb->buffer_length); */
/* check the urb is ok, are we adding data less than the packetsize */
- if (!urb_bad && (len <= endpoint->rcv_packetSize)) {
+ if (!urb_bad) {
/*usbdbg("updating actual_length by %d\n",len); */
/* increment the received data size */
rcv_urb->actual_length += len;
+ rcv_urb->req_length = 0;
} else {
usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n",
@@ -560,14 +561,55 @@ struct urb *usbd_alloc_urb (struct usb_device_instance *device,
memset (urb, 0, sizeof (struct urb));
urb->endpoint = endpoint;
urb->device = device;
- urb->buffer = (u8 *) urb->buffer_data;
- urb->buffer_length = sizeof (urb->buffer_data);
+ urb->buffer = malloc(URB_BUF_SIZE);
+ if (!urb->buffer) {
+ usberr (" F A T A L: mallocFAILED!!!!");
+ return NULL;
+ }
+ urb->buffer_length = URB_BUF_SIZE;
+ urb->buffer_data = urb->buffer;
urb_link_init (&urb->link);
return urb;
}
+int usbd_init_urb (struct urb *urb,
+ struct usb_device_instance *device,
+ struct usb_endpoint_instance *endpoint,
+ u8 *buf, unsigned int len)
+{
+ /* Fill in known fields */
+ memset (urb, 0, sizeof (struct urb));
+ urb->endpoint = endpoint;
+ urb->device = device;
+ urb->buffer = buf;
+ urb->buffer_length = len;
+ urb->buffer_data = buf;
+
+ urb_link_init (&urb->link);
+
+ return 0;
+}
+
+int usbd_setup_urb(struct urb *urb, u8 *buf, u32 len, int dma)
+{
+ if (urb) {
+ urb->req_length = len;
+ urb->buffer = buf;
+ urb->use_dma = dma;
+ }
+ return 0;
+}
+
+void usbd_free_urb(struct urb *urb)
+{
+ if (urb) {
+ urb->req_length = 0;
+ urb->actual_length = 0;
+ urb->buffer = NULL;
+ }
+}
/**
* usbd_dealloc_urb - deallocate an URB and associated buffer
* @urb: pointer to an urb structure
@@ -577,6 +619,8 @@ struct urb *usbd_alloc_urb (struct usb_device_instance *device,
void usbd_dealloc_urb (struct urb *urb)
{
if (urb) {
+ if (urb->buffer)
+ free (urb->buffer);
free (urb);
}
}
diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c
index aa8f91600d..40098c5fe9 100644
--- a/drivers/usb/gadget/ep0.c
+++ b/drivers/usb/gadget/ep0.c
@@ -388,6 +388,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
* or failure (STALL).
*
*/
+void udc_clear_halt(unsigned int ep, int is_in);
int ep0_recv_setup (struct urb *urb)
{
/*struct usb_device_request *request = urb->buffer; */
@@ -540,14 +541,17 @@ int ep0_recv_setup (struct urb *urb)
return -1;
case USB_REQ_RECIPIENT_ENDPOINT:
- dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue));
+ serial_printf ("ENDPOINT: %x \n", le16_to_cpu (request->wIndex) & 0x0f);
if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) {
+ u8 epnum = request->wIndex & 0xf;
+ int is_in = request->wIndex & USB_DIR_IN;
/*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */
/* request->bRequest == USB_REQ_SET_FEATURE); */
/* NEED TO IMPLEMENT THIS!!! */
- return -1;
+ udc_clear_halt(epnum, is_in);
+ return 0;
} else {
- dbg_ep0 (1, "request %s bad wValue: %04x",
+ serial_printf ("request %s bad wValue: %04x",
USBD_DEVICE_REQUESTS
(request->bRequest),
le16_to_cpu (request->wValue));
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 5b8776e0b7..b656c8b9f4 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -23,6 +23,7 @@
#include <common.h>
#include <linux/usb/ch9.h>
+#include <usbdescriptors.h>
#include <asm/errno.h>
#include <linux/usb/gadget.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 9fb0e80ad1..d975fb680e 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -24,6 +24,7 @@
#include <asm/errno.h>
#include <linux/netdevice.h>
#include <linux/usb/ch9.h>
+#include <usbdescriptors.h>
#include <linux/usb/cdc.h>
#include <linux/usb/gadget.h>
#include <net.h>
@@ -2396,8 +2397,7 @@ fail:
return -1;
}
-static int usb_eth_send(struct eth_device *netdev,
- volatile void *packet, int length)
+static int usb_eth_send(struct eth_device *netdev, void *packet, int length)
{
int retval;
void *rndis_pkt = NULL;
@@ -2418,11 +2418,11 @@ static int usb_eth_send(struct eth_device *netdev,
}
rndis_add_hdr(rndis_pkt, length);
memcpy(rndis_pkt + sizeof(struct rndis_packet_msg_type),
- (void *)packet, length);
+ packet, length);
packet = rndis_pkt;
length += sizeof(struct rndis_packet_msg_type);
}
- req->buf = (void *)packet;
+ req->buf = packet;
req->context = NULL;
req->complete = tx_complete;
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
new file mode 100644
index 0000000000..67e5b76911
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,473 @@
+/*
+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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
+ */
+#define DEBUG
+
+#include <common.h>
+#include <config.h>
+#include <asm/unaligned.h>
+#include "usbdescriptors.h"
+#include <usbdevice.h>
+
+#if defined(CONFIG_MUSB_UDC)
+#include <usb/musb_udc.h>
+#else
+#error "no usb device controller"
+#endif
+
+#define DEV_CONFIG_CDC 1
+
+/* defined and used by gadget/ep0.c */
+extern struct usb_string_descriptor **usb_strings;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define FASTBOOT_VENDOR_NUM 0x18D1 /* Goolge VID */
+#define FASTBOOT_PRODUCT_NUM 0x4EE0 /* Bootloader Product ID */
+
+#define FASTBOOT_MANUFACTURER "RDAmicro"
+#define FASTBOOT_PRODUCT_NAME "RDA Droid"
+#define FASTBOOT_CONFIGURATION_STR "fastboot"
+#define FASTBOOT_DATA_INTERFACE_STR "fastboot data intf"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand. For now we do either full CDC, or
+ * our simple subset.
+ */
+
+#define STRING_MANUFACTURER 1
+#define STRING_PRODUCT 2
+#define STRING_DATA 3
+#define STRING_CFG 4
+#define STRING_SERIALNUMBER 5
+
+#define NUM_ENDPOINTS 2
+
+static struct usb_device_instance fastboot_dev_instance[1];
+static struct usb_bus_instance fastboot_bus_instance[1];
+static struct usb_configuration_instance fastboot_cfg_instance;
+static struct usb_interface_instance fastboot_intf_instance[1];
+static struct usb_alternate_instance fastboot_alternate_instance[1];
+/* one extra for control endpoint */
+static struct usb_endpoint_instance fastboot_ep_instance[NUM_ENDPOINTS + 1];
+
+static char serial_number[] = "dragon2012";
+
+static struct usb_endpoint_descriptor *ep_desc_ptrs[NUM_ENDPOINTS];
+
+static struct usb_device_descriptor
+ device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = __constant_cpu_to_le16(FASTBOOT_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(FASTBOOT_PRODUCT_NUM),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .iSerialNumber = STRING_SERIALNUMBER,
+ .bNumConfigurations = 1,
+};
+
+static struct usb_interface_descriptor
+ data_intf = {
+ .bLength = sizeof data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0x42,
+ .bInterfaceProtocol = 3,
+ .iInterface = STRING_DATA,
+};
+
+static struct usb_qualifier_descriptor
+ dev_qualifier = {
+ .bLength = sizeof dev_qualifier,
+ .bDescriptorType = USB_DT_QUAL,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+};
+
+struct fastboot_config_desc {
+ struct usb_configuration_descriptor configuration_desc;
+ struct usb_interface_descriptor interface_desc[1];
+ struct usb_endpoint_descriptor data_eps[NUM_ENDPOINTS];
+} __attribute__ ((packed));
+
+static struct fastboot_config_desc
+fastboot_cfg_desc = {
+ .configuration_desc = {
+ .bLength =
+ sizeof(struct usb_configuration_descriptor),
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength =
+ cpu_to_le16(sizeof
+ (struct fastboot_config_desc)),
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STRING_CFG,
+ .bmAttributes =
+ BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED,
+ .bMaxPower = 0xfa
+ },
+ .interface_desc = {
+ {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = NUM_ENDPOINTS,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0x42,
+ .bInterfaceProtocol = 3,
+ .iInterface = STRING_DATA
+ },
+ },
+ .data_eps = {
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = (1) | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+ .bInterval = 0xFF,
+ },
+ {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = (2) | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+ .bInterval = 0xFF,
+ },
+ },
+};
+
+/*-------------------------------------------------------------------------*/
+
+
+
+static int fastboot_configured_flag = 0;
+
+static void fastboot_init_endpoints(void);
+static void fastboot_event_handler(struct usb_device_instance *device,
+ usb_device_event_t event, int data)
+{
+#if defined(CONFIG_USBD_HS)
+ int i;
+#endif
+ switch (event) {
+ case DEVICE_RESET:
+ case DEVICE_BUS_INACTIVE:
+ fastboot_configured_flag = 0;
+
+ break;
+ case DEVICE_CONFIGURED:
+ fastboot_configured_flag = 1;
+ break;
+
+ case DEVICE_ADDRESS_ASSIGNED:
+#if defined(CONFIG_USBD_HS)
+ /*
+ * is_usbd_high_speed routine needs to be defined by
+ * specific gadget driver
+ * It returns TRUE if device enumerates at High speed
+ * Retuns FALSE otherwise
+ */
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ if (((ep_desc_ptrs[i]->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)
+ && is_usbd_high_speed()) {
+
+ ep_desc_ptrs[i]->wMaxPacketSize = 512;
+ }
+
+ fastboot_ep_instance[i + 1].tx_packetSize =
+ ep_desc_ptrs[i]->wMaxPacketSize;
+ fastboot_ep_instance[i + 1].rcv_packetSize =
+ ep_desc_ptrs[i]->wMaxPacketSize;
+ }
+#endif
+ fastboot_init_endpoints();
+
+ default:
+ break;
+ }
+}
+
+/* utility function for converting char* to wide string used by USB */
+static void str2wide (char *str, u16 * wide)
+{
+ int i;
+ for (i = 0; i < strlen (str) && str[i]; i++){
+ #if defined(__LITTLE_ENDIAN)
+ wide[i] = (u16) str[i];
+ #elif defined(__BIG_ENDIAN)
+ wide[i] = ((u16)(str[i])<<8);
+ #else
+ #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined"
+ #endif
+ }
+}
+
+static struct usb_string_descriptor *fastboot_str_tab[10];
+
+static void fastboot_init_strings (void)
+{
+ struct usb_string_descriptor *string;
+
+ static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
+ static u8 wstrManufacturer[2 + 2*(sizeof(FASTBOOT_MANUFACTURER)-1)];
+ static u8 wstrProduct[2 + 2*(sizeof(FASTBOOT_PRODUCT_NAME)-1)];
+ static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)];
+ static u8 wstrConfiguration[2 + 2*(sizeof(FASTBOOT_CONFIGURATION_STR)-1)];
+ static u8 wstrDataInterface[2 + 2*(sizeof(FASTBOOT_DATA_INTERFACE_STR)-1)];
+
+
+
+ fastboot_str_tab[0] =
+ (struct usb_string_descriptor*)wstrLang;
+
+ string = (struct usb_string_descriptor *) wstrManufacturer;
+ string->bLength = sizeof(wstrManufacturer);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide (FASTBOOT_MANUFACTURER, string->wData);
+ fastboot_str_tab[STRING_MANUFACTURER]=string;
+
+
+ string = (struct usb_string_descriptor *) wstrProduct;
+ string->bLength = sizeof(wstrProduct);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide (FASTBOOT_PRODUCT_NAME, string->wData);
+ fastboot_str_tab[STRING_PRODUCT]=string;
+
+
+ string = (struct usb_string_descriptor *) wstrSerial;
+ string->bLength = sizeof(wstrSerial);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide (serial_number, string->wData);
+ fastboot_str_tab[STRING_SERIALNUMBER]=string;
+
+ string = (struct usb_string_descriptor *) wstrConfiguration;
+ string->bLength = sizeof(wstrConfiguration);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide (FASTBOOT_CONFIGURATION_STR, string->wData);
+ fastboot_str_tab[STRING_CFG]=string;
+
+ string = (struct usb_string_descriptor *) wstrDataInterface;
+ string->bLength = sizeof(wstrDataInterface);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide (FASTBOOT_DATA_INTERFACE_STR, string->wData);
+ fastboot_str_tab[STRING_DATA]=string;
+
+ /* Now, initialize the string table for ep0 handling */
+ usb_strings = fastboot_str_tab;
+}
+#define init_wMaxPacketSize(x) le16_to_cpu(get_unaligned(\
+ &ep_desc_ptrs[(x) - 1]->wMaxPacketSize));
+
+static void fastboot_init_instances(void)
+{
+ int i;
+
+ /* initialize device instance */
+ memset(fastboot_dev_instance, 0, sizeof(struct usb_device_instance));
+ fastboot_dev_instance->device_state = STATE_INIT;
+ fastboot_dev_instance->device_descriptor = &device_desc;
+#if defined(CONFIG_USBD_HS)
+ fastboot_dev_instance->qualifier_descriptor = &dev_qualifier;
+#endif
+ fastboot_dev_instance->event = fastboot_event_handler;
+ fastboot_dev_instance->bus = fastboot_bus_instance;
+ fastboot_dev_instance->configurations = 1;
+ fastboot_dev_instance->configuration_instance_array =
+ &fastboot_cfg_instance;
+
+ /* initialize bus instance */
+ memset(fastboot_bus_instance, 0, sizeof(struct usb_bus_instance));
+ fastboot_bus_instance->device = fastboot_dev_instance;
+ fastboot_bus_instance->endpoint_array = fastboot_ep_instance;
+ fastboot_bus_instance->max_endpoints = 1;
+ fastboot_bus_instance->maxpacketsize = 64;
+ fastboot_bus_instance->serial_number_str = serial_number;
+
+ /* configuration instance */
+ memset(&fastboot_cfg_instance, 0,
+ sizeof(struct usb_configuration_instance));
+ fastboot_cfg_instance.interfaces = 1;
+ fastboot_cfg_instance.configuration_descriptor =
+ (struct usb_configuration_descriptor *)
+ &fastboot_cfg_desc;
+ fastboot_cfg_instance.interface_instance_array =
+ fastboot_intf_instance;
+
+ /* interface instance */
+ memset(fastboot_intf_instance, 0,
+ sizeof(struct usb_interface_instance));
+ fastboot_intf_instance->alternates = 1;
+ fastboot_intf_instance->alternates_instance_array =
+ fastboot_alternate_instance;
+
+ /* alternates instance */
+ memset(fastboot_alternate_instance, 0,
+ sizeof(struct usb_alternate_instance));
+ fastboot_alternate_instance->interface_descriptor = &data_intf;
+ fastboot_alternate_instance->endpoints = NUM_ENDPOINTS;
+ fastboot_alternate_instance->endpoints_descriptor_array = ep_desc_ptrs;
+
+ /* endpoint instances */
+ memset(&fastboot_ep_instance[0], 0,
+ sizeof(struct usb_endpoint_instance));
+ fastboot_ep_instance[0].endpoint_address = 0;
+ fastboot_ep_instance[0].rcv_packetSize = 64;
+ fastboot_ep_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL;
+ fastboot_ep_instance[0].tx_packetSize = 64;
+ fastboot_ep_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL;
+ udc_setup_ep(fastboot_dev_instance, 0,
+ &fastboot_ep_instance[0]);
+
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ memset(&fastboot_ep_instance[i], 0,
+ sizeof(struct usb_endpoint_instance));
+
+ fastboot_ep_instance[i].endpoint_address =
+ ep_desc_ptrs[i - 1]->bEndpointAddress;
+
+ fastboot_ep_instance[i].rcv_attributes =
+ ep_desc_ptrs[i - 1]->bmAttributes;
+
+ fastboot_ep_instance[i].rcv_packetSize =
+ init_wMaxPacketSize(i);
+
+ fastboot_ep_instance[i].tx_attributes =
+ ep_desc_ptrs[i - 1]->bmAttributes;
+
+ fastboot_ep_instance[i].tx_packetSize =
+ init_wMaxPacketSize(i);
+
+ fastboot_ep_instance[i].tx_attributes =
+ ep_desc_ptrs[i - 1]->bmAttributes;
+
+ urb_link_init(&fastboot_ep_instance[i].rcv);
+ urb_link_init(&fastboot_ep_instance[i].rdy);
+ urb_link_init(&fastboot_ep_instance[i].tx);
+ urb_link_init(&fastboot_ep_instance[i].done);
+
+ if (fastboot_ep_instance[i].endpoint_address & USB_DIR_IN)
+ fastboot_ep_instance[i].tx_urb =
+ usbd_alloc_urb(fastboot_dev_instance,
+ &fastboot_ep_instance[i]);
+ else
+ fastboot_ep_instance[i].rcv_urb =
+ usbd_alloc_urb(fastboot_dev_instance,
+ &fastboot_ep_instance[i]);
+ }
+}
+
+static void fastboot_init_endpoints(void)
+{
+ int i;
+
+ fastboot_bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ udc_setup_ep(fastboot_dev_instance, i,
+ &fastboot_ep_instance[i]);
+ }
+}
+
+struct usb_endpoint_instance * fastboot_get_out_ep(void)
+{
+ int i;
+ int ep_addr;
+
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ ep_addr = fastboot_ep_instance[i].endpoint_address;
+ if ((ep_addr != 0) && ((ep_addr & 0x80) == 0))
+ return &fastboot_ep_instance[i];
+ }
+
+ serial_printf("no out endpoint for fastboot\n");
+ return NULL;
+}
+
+struct usb_endpoint_instance * fastboot_get_in_ep(void)
+{
+ int i;
+
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ if (fastboot_ep_instance[i].endpoint_address & USB_DIR_IN)
+ return &fastboot_ep_instance[i];
+ }
+
+ serial_printf("no in endpoint for fastboot\n");
+ return NULL;
+
+}
+
+int fastboot_configured (void)
+{
+ return fastboot_configured_flag;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Initialize the usb client port.
+ *
+ */
+int drv_fastboot_init(void)
+{
+ ep_desc_ptrs[0] = &fastboot_cfg_desc.data_eps[0];
+ ep_desc_ptrs[1] = &fastboot_cfg_desc.data_eps[1];
+
+ /* Now, set up USB controller and infrastructure */
+
+ fastboot_init_strings ();
+ fastboot_init_instances();
+
+ fastboot_init_endpoints();
+
+ udc_startup_events(fastboot_dev_instance); /* Enable dev, init udc pointers */
+ udc_connect(); /* Enable pullup for host detection */
+
+ return 0;
+}
+
diff --git a/drivers/usb/gadget/fastboot.c b/drivers/usb/gadget/fastboot.c
new file mode 100644
index 0000000000..21aaa0502b
--- /dev/null
+++ b/drivers/usb/gadget/fastboot.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <common.h>
+#include <asm/errno.h>
+#include <usbdescriptors.h>
+#include <usbdevice.h>
+#include <linux/ctype.h>
+#include <malloc.h>
+#include <command.h>
+#include <nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <jffs2/jffs2.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <android/android_boot.h>
+#include <android/android_bootimg.h>
+#include <android/boot_mode.h>
+#include <asm/arch/rda_sys.h>
+#include "fastboot.h"
+#include <mmc.h>
+#include <mmc/sparse.h>
+#include <mmc/mmcpart.h>
+#if defined(CONFIG_MUSB_UDC)
+#include <usb/musb_udc.h>
+#else
+#error "no usb device controller"
+#endif
+
+//#define DEBUG
+#ifdef DEBUG
+#define fb_dbg(fmt, args...) serial_printf(fmt, ##args)
+#else
+#define fb_dbg(fmt, args...) do {} while(0)
+#endif
+
+#define fb_info(fmt, args...) serial_printf(fmt, ##args)
+
+#ifdef FLASH_PAGE_SIZE
+#undef FLASH_PAGE_SIZE
+#endif
+#define FLASH_PAGE_SIZE 2048
+
+/** implemented in cmd_misc.c */
+extern int load_boot_from_nand(void);
+extern int load_boot_from_mmc(void);
+
+struct fastboot_cmd {
+ struct fastboot_cmd *next;
+ const char *prefix;
+ unsigned prefix_len;
+ void (*handle) (const char *arg, void *data, loff_t sz);
+};
+
+struct fastboot_var {
+ struct fastboot_var *next;
+ const char *name;
+ const char *value;
+};
+
+static struct fastboot_cmd *cmdlist;
+
+static void fastboot_register(const char *prefix,
+ void (*handle) (const char *arg, void *data,
+ loff_t sz))
+{
+ struct fastboot_cmd *cmd;
+ cmd = malloc(sizeof(*cmd));
+ if (cmd) {
+ cmd->prefix = prefix;
+ cmd->prefix_len = strlen(prefix);
+ cmd->handle = handle;
+ cmd->next = cmdlist;
+ cmdlist = cmd;
+ }
+}
+
+static struct fastboot_var *varlist;
+
+static void fastboot_publish(const char *name, const char *value)
+{
+ struct fastboot_var *var;
+ var = malloc(sizeof(*var));
+ if (var) {
+ var->name = name;
+ var->value = value;
+ var->next = varlist;
+ varlist = var;
+ }
+}
+
+static unsigned char buffer[4096];
+
+static void *download_base;
+static unsigned download_max;
+static unsigned download_size;
+
+enum {
+ STATE_OFFLINE,
+ STATE_COMMAND,
+ STATE_COMPLETE,
+ STATE_EXIT,
+ STATE_ERROR
+};
+
+static unsigned fastboot_state = STATE_OFFLINE;
+
+static void show_progress(unsigned img_size, unsigned xfer)
+{
+ static int step = 0;
+ static unsigned old_size = 0;
+
+ if (img_size != old_size) {
+ old_size = img_size;
+ step = 0;
+ }
+
+ if (step % 64 == 0)
+ fb_info(">");
+ step++;
+ if (step > 2560) {
+ printf("%3d%%\n", (u32) (((u64) xfer * 100) / img_size));
+ step = 0;
+ }
+ if (xfer >= img_size)
+ fb_info("100%% finised\n");
+}
+
+static int usb_read(void *_buf, unsigned len)
+{
+ int count = 0;
+ struct usb_endpoint_instance *ep_out = fastboot_get_out_ep();
+ struct urb *current_urb = NULL;
+ unsigned char *buf = _buf;
+ unsigned total = len;
+
+ if (!fastboot_configured()) {
+ return 0;
+ }
+
+ if (fastboot_state == STATE_ERROR)
+ goto oops;
+
+ if (!ep_out)
+ goto oops;
+
+ fb_dbg("%s, buf 0x%p, len :%d\n", __func__, _buf, len);
+ current_urb = ep_out->rcv_urb;
+ while (len > 0) {
+ int xfer;
+ int maxPktSize = ep_out->rcv_packetSize;
+ int dma = 0;
+
+ xfer = (len > maxPktSize) ? maxPktSize : len;
+ if (xfer == maxPktSize)
+ dma = 1;
+ usbd_setup_urb(current_urb, buf, xfer, dma);
+ udc_irq();
+ if (current_urb->actual_length) {
+ buf += current_urb->actual_length;
+ len -= current_urb->actual_length;
+ count += current_urb->actual_length;
+ if (xfer != current_urb->actual_length)
+ break;
+ show_progress(total, count);
+ current_urb->actual_length = 0;
+ }
+ }
+ current_urb->actual_length = 0;
+ fb_dbg("%s, read :%d\n", __func__, count);
+ return count;
+
+oops:
+ fb_info("fastboot usb read error\n");
+ fastboot_state = STATE_ERROR;
+ return -1;
+}
+
+static int usb_write(void *_buf, unsigned len)
+{
+ struct usb_endpoint_instance *ep_in = fastboot_get_in_ep();
+ struct urb *current_urb = NULL;
+ unsigned char *buf = _buf;
+ int count = 0;
+
+ if (!fastboot_configured()) {
+ return 0;
+ }
+
+ if (fastboot_state == STATE_ERROR)
+ goto oops;
+
+ if (!ep_in)
+ goto oops;
+
+ fb_dbg("%s, len :%d\n", __func__, len);
+ current_urb = ep_in->tx_urb;
+
+ while (len > 0) {
+ int xfer;
+ int maxPktSize = ep_in->tx_packetSize;
+
+ xfer = (len > maxPktSize) ? maxPktSize : len;
+ current_urb->buffer = buf;
+
+ current_urb->actual_length = xfer;
+ fb_dbg("urb actual_len :%d, ep sent, last %d\n",
+ current_urb->actual_length, ep_in->sent, ep_in->last);
+ if (udc_endpoint_write(ep_in))
+ goto oops;
+ count += xfer;
+ len -= xfer;
+ buf += xfer;
+ }
+ fb_dbg("after write urb actual_len :%d, ep sent, last %d, count:%d\n",
+ current_urb->actual_length, ep_in->sent, ep_in->last, count);
+
+ return count;
+
+oops:
+ fb_info("fastboot usb write error\n");
+ fastboot_state = STATE_ERROR;
+ return -1;
+}
+
+void fastboot_ack(const char *code, const char *reason)
+{
+ char response[64] = { 0 };
+
+ if (fastboot_state != STATE_COMMAND)
+ return;
+
+ if (reason == 0)
+ reason = "";
+
+ if (strlen(code) + strlen(reason) >= 64) {
+ fb_info("%s too long string\r\n", __func__);
+ }
+ sprintf(response, "%s%s", code, reason);
+ fastboot_state = STATE_COMPLETE;
+
+ usb_write(response, strlen(response));
+
+}
+
+void fastboot_fail(const char *reason)
+{
+ fastboot_ack("FAIL", reason);
+}
+
+void fastboot_okay(const char *info)
+{
+ fastboot_ack("OKAY", info);
+}
+
+void fastboot_info(const char *info)
+{
+ char response[64] = { 0 };
+ const char *code = "INFO";
+
+ if (info == 0)
+ info = "";
+
+ if (strlen(code) + strlen(info) >= 64) {
+ fb_info("%s too long string\r\n", __func__);
+ }
+ snprintf(response, 63, "%s%s", code, info);
+
+ usb_write(response, strlen(response));
+}
+
+static void cmd_getvar(const char *arg, void *data, loff_t sz)
+{
+ struct fastboot_var *var;
+ int value_len;
+ char str[60];
+
+ for (var = varlist; var; var = var->next) {
+ if (!strcmp(var->name, arg)) {
+ int index = 0;
+
+ value_len = strlen(var->value);
+ if (value_len < 60) {
+ fastboot_okay(var->value);
+ return;
+ }
+ /* value length > 60(+INFO > 64) */
+ while (value_len) {
+ int xfer = (value_len > 50) ? 50 : value_len;
+
+ memset(str, 0, sizeof(str));
+ strncpy(str, &var->value[index], xfer);
+ fastboot_info(str);
+ value_len -= xfer;
+ index += xfer;
+ }
+ fastboot_okay("");
+ return;
+ }
+ }
+ fastboot_okay("");
+}
+
+static void cmd_download(const char *arg, void *data, loff_t sz)
+{
+ char response[64];
+ unsigned len = simple_strtoul(arg, NULL, 16);
+ int r;
+
+ fb_dbg("%s\n", __func__);
+
+ fb_info("start downloading... length %d is to %p\n", len, data);
+ download_size = 0;
+ if (len > download_max) {
+ fastboot_fail("data too large");
+ return;
+ }
+
+ sprintf(response, "DATA%08x", len);
+ if (usb_write(response, strlen(response)) < 0)
+ return;
+
+ r = usb_read(download_base, len);
+ if ((r < 0) || (r != len)) {
+ fastboot_state = STATE_ERROR;
+ return;
+ }
+ download_size = len;
+ fb_info("download ok\n");
+ fastboot_okay("");
+ //dump_log(download_base, len);
+}
+
+extern int mtdparts_init_default(void);
+extern int mtdparts_save_ptbl(int need_erase);
+
+#ifdef MTDPARTS_UBI_DEF
+extern int ubi_part_scan(char *part_name);
+extern int ubi_check_default_vols(const char *ubi_default_str);
+extern int ubi_erase_vol(char *vol_name);
+extern int ubi_update_vol(char *vol_name, void *buf, size_t size);
+static int cmd_flash_ubi(const char *arg, void *data, loff_t sz)
+{
+ int ret = 0;
+
+ ret = ubi_part_scan(MTDPARTS_UBI_PART_NAME);
+ if(ret) {
+ fastboot_fail("ubi init failed.");
+ goto exit;
+ }
+
+ ret = ubi_check_default_vols(MTDPARTS_UBI_DEF);
+ if(ret) {
+ fastboot_fail("ubi volumes check failed.");
+ goto exit;
+ }
+
+ fb_info("ready to update ubi volume '%s'\n", arg);
+ ret = ubi_update_vol((char *)arg, data, sz);
+exit:
+ return ret;
+}
+#endif /* MTDPARTS_UBI_DEF */
+static void cmd_flash_nand(const char *arg, void *data, loff_t sz)
+{
+ struct mtd_info *nand;
+ struct mtd_device *dev;
+ struct part_info *part;
+ size_t size = 0;
+ u8 pnum;
+ nand_erase_options_t opts;
+ int ret;
+ unsigned addr = simple_strtoul(arg, NULL, 16);
+ loff_t max_limit = 0;
+ u32 bad_blocks = 0;
+ sparse_header_t *header = (void *)data;
+
+ fb_info("%s, addr: %s date: %p, sz: 0x%llx\n", __func__, arg, data, sz);
+
+ /* use "flash" command for downloading purpose */
+ if (addr && (addr >= PHYS_SDRAM_1) &&
+ (addr < (PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE))) {
+ fb_info("in memory, do copy to 0x%x\n", addr);
+ fastboot_okay("");
+ memcpy((void *)addr, download_base, sz);
+ fastboot_state = STATE_EXIT;
+ return;
+ }
+
+ data = download_base; //previous downloaded date to download_base
+
+#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */
+ if(strstr(MTDPARTS_UBI_DEF, arg)) {
+ ret = cmd_flash_ubi(arg, data, sz);
+ goto exit;
+ }
+#endif
+
+ ret = find_dev_and_part(arg, &dev, &pnum, &part);
+ if (ret) {
+ fastboot_fail("unknown partition name");
+ return;
+ } else if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ fastboot_fail("mtd dev type error");
+ return;
+ }
+ nand = &nand_info[dev->id->num];
+ fb_info("found part '%s' offset: 0x%llx length: 0x%llx id: %d\n",
+ part->name, part->offset, part->size, dev->id->num);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = (loff_t) part->offset;
+ opts.length = (loff_t) part->size;
+ opts.jffs2 = 0;
+ opts.quiet = 0;
+
+ fb_dbg("opts off 0x%08x\n", (uint32_t) opts.offset);
+ fb_dbg("opts size 0x%08x\n", (uint32_t) opts.length);
+ fb_dbg("nand write size 0x%08x\n", nand->writesize);
+ fb_info("erase 0x%llx bytes to '%s' offset: 0x%llx\n",
+ opts.length, part->name, opts.offset);
+ ret = nand_erase_opts(nand, &opts);
+ if (ret) {
+ fastboot_fail("nand erase error");
+ return;
+ }
+
+ if (!strcmp(part->name, "boot") || !strcmp(part->name, "recovery")) {
+ if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ fastboot_fail("image is not a boot image");
+ return;
+ }
+ }
+
+ if (is_power_of_2(nand->writesize))
+ sz = ROUND(sz, nand->writesize);
+ else
+ sz = roundup(sz, nand->writesize);
+ size = sz;
+ max_limit = part->offset + part->size;
+
+ fb_info("writing 0x%x bytes to '%s' offset: 0x%llx\n", size,
+ part->name, part->offset);
+ header = (void *)data;
+ if (header->magic != SPARSE_HEADER_MAGIC) {
+ ret =
+ nand_write_skip_bad_new(nand, part->offset, &size,
+ max_limit, (u_char *) data,
+ 0,
+ &bad_blocks);
+ } else {
+ ret = nand_write_unsparse(nand, part->offset, &size, max_limit,
+ (u_char *) data,
+ 0,
+ &bad_blocks);
+ }
+
+ /* the mtd partition table is saved in 'bootloader' partition,
+ * if 'bootloader' is updated, need to re-write the partition table. */
+ if(strcmp(part->name,"bootloader") == 0) {
+ mtdparts_save_ptbl(1);
+ }
+
+#ifdef MTDPARTS_UBI_DEF
+exit:
+#endif
+ if (!ret)
+ fastboot_okay("");
+ else
+ fastboot_fail("flash error");
+ fb_info("flash ok\n");
+}
+
+static block_dev_desc_t *mmc_blkdev;
+void cmd_flash_mmc_img(const char *name, void *data, loff_t sz)
+{
+ disk_partition_t *ptn = 0;
+ u64 size = 0;
+
+ fb_info("flash to mmc part %s\n", name);
+ ptn = partition_find_ptn(name);
+ if (ptn == 0) {
+ fastboot_fail("partition table doesn't exist");
+ return;
+ }
+
+ if (!strcmp(name, "boot") || !strcmp(name, "recovery")) {
+ if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ fastboot_fail("image is not a boot image");
+ return;
+ }
+ }
+
+ size = (u64) ptn->size * ptn->blksz;
+ fb_info("partition %s, type %s blocks " LBAFU ", size %llx\n",
+ ptn->name, ptn->type, ptn->size, size);
+ if (ROUND(sz, 512) > size) {
+ fastboot_fail("size too large");
+ return;
+ } else if (partition_write_bytes(mmc_blkdev, ptn, &sz, data)) {
+ fastboot_fail("flash write failure");
+ return;
+ }
+
+ fastboot_okay("");
+ return;
+}
+
+void cmd_flash_mmc_sparse_img(const char *part_name, void *data, loff_t sz)
+{
+ disk_partition_t *ptn;
+ unsigned long long size = 0;
+ int ret;
+
+ fb_info("unsparese and flash part %s\n", part_name);
+ ptn = partition_find_ptn(part_name);
+ if (ptn == 0) {
+ fastboot_fail("partition table doesn't exist");
+ return;
+ }
+
+ size = (u64) ptn->blksz * ptn->size;
+ if (ROUND(sz, 512) > size) {
+ fastboot_fail("size too large");
+ return;
+ }
+
+ ret = partition_unsparse(mmc_blkdev, ptn, data, ptn->start, ptn->size);
+ if (ret) {
+ fastboot_fail("partition cannot unsparse");
+ return;
+ }
+ fastboot_okay("");
+ return;
+}
+
+void cmd_flash_mmc(const char *arg, void *data, loff_t sz)
+{
+ sparse_header_t *sparse_header;
+
+ sparse_header = (sparse_header_t *) data;
+
+ /* Using to judge if ext4 file system */
+ if (sparse_header->magic != SPARSE_HEADER_MAGIC)
+ cmd_flash_mmc_img(arg, data, sz);
+ else
+ cmd_flash_mmc_sparse_img(arg, data, sz);
+
+ return;
+}
+
+static void cmd_flash(const char *arg, void *data, loff_t sz)
+{
+ if (rda_media_get() == MEDIA_MMC)
+ cmd_flash_mmc(arg, data, sz);
+ else
+ cmd_flash_nand(arg, data, sz);
+}
+
+static void cmd_erase_nand(const char *arg, void *data, unsigned sz)
+{
+ struct mtd_info *nand;
+ struct mtd_device *dev;
+ struct part_info *part;
+ u8 pnum;
+ nand_erase_options_t opts;
+ int ret;
+
+ fb_info("erase part: %s\n", arg);
+
+#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */
+ if(strstr(MTDPARTS_UBI_DEF, arg)) {
+ ubi_part_scan(MTDPARTS_UBI_PART_NAME);
+ ret = ubi_erase_vol((char *)arg);
+ if(ret)
+ fastboot_fail("ubi volume erase error");
+ else
+ fastboot_okay("");
+ return;
+ }
+#endif
+
+ ret = find_dev_and_part(arg, &dev, &pnum, &part);
+ if (ret) {
+ fastboot_fail("unknown partition name");
+ return;
+ } else if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ fastboot_fail("mtd dev type error");
+ return;
+ }
+ nand = &nand_info[dev->id->num];
+ fb_info("found part '%s' offset: 0x%llx length: 0x%llx id: %d\n",
+ part->name, part->offset, part->size, dev->id->num);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = (loff_t) part->offset;
+ opts.length = (loff_t) part->size;
+ opts.jffs2 = 0;
+ opts.quiet = 0;
+
+ fb_dbg("opts off 0x%08x\n", (uint32_t) opts.offset);
+ fb_dbg("opts size 0x%08x\n", (uint32_t) opts.length);
+ fb_dbg("nand write size 0x%08x\n", nand->writesize);
+ ret = nand_erase_opts(nand, &opts);
+ if (ret)
+ fastboot_fail("nand erase error");
+ else
+ fastboot_okay("");
+}
+
+static void cmd_erase_mmc(const char *arg, void *data, loff_t sz)
+{
+ int ret;
+ disk_partition_t *ptn;
+ lbaint_t blkcnt;
+
+ fb_info("erase part: %s\n", arg);
+ ptn = partition_find_ptn(arg);
+ if (!ptn) {
+ fastboot_fail("partition table doesn't exist");
+ return;
+ }
+
+ blkcnt = ptn->size;
+ fb_info("erase mmc from %#x, blocks %#x\n",
+ (uint32_t)ptn->start, (uint32_t)blkcnt);
+ ret = partition_erase_blks(mmc_blkdev, ptn, &blkcnt);
+ if (ret || blkcnt != ptn->size) {
+ fastboot_fail("partition erase failed");
+ return;
+ }
+ fastboot_okay("");
+}
+
+static void cmd_erase(const char *arg, void *data, loff_t sz)
+{
+ if (rda_media_get() == MEDIA_MMC)
+ return cmd_erase_mmc(arg, data, sz);
+ else
+ return cmd_erase_nand(arg, data, sz);
+}
+
+static unsigned char raw_header[2048];
+
+static void cmd_boot(const char *arg, void *data, loff_t sz)
+{
+ boot_img_hdr *hdr = (boot_img_hdr *) raw_header;
+ unsigned kernel_actual;
+ unsigned ramdisk_actual;
+ char *cmdline;
+
+ fb_info("%s, arg: %s, data: %p, sz: 0x%llx\n", __func__, arg, data, sz);
+ memcpy(raw_header, data, 2048);
+ if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ fb_info("boot image headr: %s\n", hdr->magic);
+ fastboot_fail("bad boot image header");
+ return;
+ }
+ kernel_actual = ROUND(hdr->kernel_size, FLASH_PAGE_SIZE);
+ if (kernel_actual <= 0) {
+ fastboot_fail("kernel image should not be zero");
+ return;
+ }
+ ramdisk_actual = ROUND(hdr->ramdisk_size, FLASH_PAGE_SIZE);
+ if (ramdisk_actual < 0) {
+ fastboot_fail("ramdisk size error");
+ return;
+ }
+
+ memcpy((void *)hdr->kernel_addr, (void *)data + FLASH_PAGE_SIZE,
+ kernel_actual);
+ memcpy((void *)hdr->ramdisk_addr,
+ (void *)data + FLASH_PAGE_SIZE + kernel_actual, ramdisk_actual);
+
+ fb_info("kernel @0x%08x (0x%08x bytes)\n", hdr->kernel_addr,
+ kernel_actual);
+ fb_info("ramdisk @0x%08x (0x%08x bytes)\n", hdr->ramdisk_addr,
+ ramdisk_actual);
+ //set boot environment
+ if (hdr->cmdline[0]) {
+ cmdline = (char *)hdr->cmdline;
+ } else {
+ cmdline = getenv("bootargs");
+ }
+
+ fb_info("cmdline %s\n", cmdline);
+ fastboot_okay("");
+ udc_power_off();
+ creat_atags(hdr->tags_addr, cmdline, hdr->ramdisk_addr,
+ hdr->ramdisk_size);
+
+ cleanup_before_linux();
+ boot_linux(hdr->kernel_addr, hdr->tags_addr);
+}
+
+extern int do_cboot(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
+
+static void cmd_continue(const char *arg, void *data, loff_t sz)
+{
+ fb_info("continue....\n");
+ fastboot_okay("");
+ udc_power_off();
+ fastboot_state = STATE_EXIT;
+ //do_cboot(NULL, 0, 1, NULL);
+ //normal_mode();
+}
+
+static void cmd_reboot(const char *arg, void *data, loff_t sz)
+{
+ fb_info("reboot....\n");
+ fastboot_okay("");
+ mdelay(10);
+ udc_power_off();
+ fastboot_state = STATE_EXIT;
+ rda_reboot(REBOOT_TO_NORMAL_MODE);
+}
+
+static void cmd_reboot_bootloader(const char *arg, void *data, loff_t sz)
+{
+ fb_info("reboot to bootloader....\n");
+ fastboot_okay("");
+ mdelay(10);
+ udc_power_off();
+ fastboot_state = STATE_EXIT;
+ rda_reboot(REBOOT_TO_FASTBOOT_MODE);
+}
+
+static void cmd_powerdown(const char *arg, void *data, loff_t sz)
+{
+ fb_info("power down....\n");
+ fastboot_okay("");
+ fastboot_state = STATE_EXIT;
+ //power_down_devices(0);
+}
+
+static void fastboot_command_loop(void)
+{
+ struct fastboot_cmd *cmd;
+ int r;
+ fb_dbg("fastboot: processing commands\n");
+
+again:
+ while ((fastboot_state != STATE_ERROR)
+ && (fastboot_state != STATE_EXIT)) {
+ memset(buffer, 0, 64);
+ r = usb_read(buffer, 64);
+ if (r < 0)
+ break;
+ buffer[r] = 0;
+ fb_dbg("fastboot: %s, r:%d\n", buffer, r);
+
+ for (cmd = cmdlist; cmd; cmd = cmd->next) {
+ fb_dbg("cmd list :%s \n", cmd->prefix);
+ if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
+ continue;
+ fastboot_state = STATE_COMMAND;
+ cmd->handle((const char *)buffer + cmd->prefix_len,
+ (void *)download_base, download_size);
+ if (fastboot_state == STATE_COMMAND)
+ fastboot_fail("unknown reason");
+ if (fastboot_state == STATE_EXIT)
+ goto fb_done;
+ goto again;
+ }
+
+ fastboot_fail("unknown command");
+ }
+ fastboot_state = STATE_OFFLINE;
+fb_done:
+ fb_dbg("fastboot: done\n");
+}
+
+static int fastboot_handler(void *arg)
+{
+ while (fastboot_state != STATE_EXIT) {
+ fastboot_command_loop();
+ }
+ return 0;
+}
+
+static int fastboot_start(void *base, unsigned size)
+{
+ char *parts;
+
+ fb_info("fastboot start\n");
+
+ download_base = base;
+ download_max = size;
+
+ fastboot_register("getvar:", cmd_getvar);
+ fastboot_register("download:", cmd_download);
+ fastboot_register("flash:", cmd_flash);
+ fastboot_register("erase:", cmd_erase);
+ fastboot_register("boot", cmd_boot);
+ fastboot_register("reboot", cmd_reboot);
+ fastboot_register("powerdown", cmd_powerdown);
+ fastboot_register("continue", cmd_continue);
+ fastboot_register("reboot-bootloader", cmd_reboot_bootloader);
+
+ fastboot_publish("version", "1.0");
+
+ parts = getenv("mtdparts");
+ fastboot_publish("mtd", parts);
+
+ fastboot_handler(0);
+ return 0;
+}
+
+/*
+ * Since interrupt handling has not yet been implemented, we use this function
+ * to handle polling. .
+ */
+static void fastboot_wait_for_configed(void)
+{
+ /* New interrupts? */
+ while (!fastboot_configured())
+ udc_irq();
+}
+
+void boot_linux_from_mem(void *data)
+{
+ boot_img_hdr *hdr = (boot_img_hdr *) data;
+ unsigned kernel_actual;
+ unsigned ramdisk_actual;
+ char *cmdline;
+
+ fb_info("%s, data: %p\n", __func__, data);
+ if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ fb_info("boot image headr: %s\n", hdr->magic);
+ fastboot_fail("bad boot image header");
+ return;
+ }
+ kernel_actual = ROUND(hdr->kernel_size, FLASH_PAGE_SIZE);
+ if (kernel_actual <= 0) {
+ fastboot_fail("kernel image should not be zero");
+ return;
+ }
+ ramdisk_actual = ROUND(hdr->ramdisk_size, FLASH_PAGE_SIZE);
+ if (ramdisk_actual < 0) {
+ fastboot_fail("ramdisk size error");
+ return;
+ }
+
+ memcpy((void *)hdr->kernel_addr, (void *)data + FLASH_PAGE_SIZE,
+ kernel_actual);
+ memcpy((void *)hdr->ramdisk_addr,
+ (void *)data + FLASH_PAGE_SIZE + kernel_actual, ramdisk_actual);
+
+ fb_info("kernel @0x%08x (0x%08x bytes)\n", hdr->kernel_addr,
+ kernel_actual);
+ fb_info("ramdisk @0x%08x (0x%08x bytes)\n", hdr->ramdisk_addr,
+ ramdisk_actual);
+ //set boot environment
+ if (hdr->cmdline[0]) {
+ cmdline = (char *)hdr->cmdline;
+ } else {
+ cmdline = getenv("bootargs");
+ }
+
+ fb_info("cmdline %s\n", cmdline);
+ creat_atags(hdr->tags_addr, cmdline, hdr->ramdisk_addr,
+ hdr->ramdisk_size);
+
+ cleanup_before_linux();
+ boot_linux(hdr->kernel_addr, hdr->tags_addr);
+}
+
+void boot_linux_from_nand(void)
+{
+ if(load_boot_from_nand() == CMD_RET_FAILURE) {
+ fb_info("fastboot load_boot_from_nand fail\n");
+ return;
+ }
+ /* boot from mem */
+ boot_linux_from_mem((void *)SCRATCH_ADDR);
+}
+
+void boot_linux_from_mmc(void)
+{
+ if(load_boot_from_mmc() == CMD_RET_FAILURE) {
+ fb_info("fastboot load_boot_from_mmc fail\n");
+ return;
+ }
+ /* boot from mem */
+ boot_linux_from_mem((void *)SCRATCH_ADDR);
+}
+
+void boot_linux_from_flash(void)
+{
+ enum media_type media = rda_media_get();
+
+ if (media == MEDIA_MMC)
+ boot_linux_from_mmc();
+ else if ((media == MEDIA_NAND) || (media == MEDIA_SPINAND))
+ boot_linux_from_nand();
+ else
+ fb_info("fastboot can't find media\n");
+}
+
+#if defined(CONFIG_CMD_FASTBOOT)
+extern int drv_fastboot_init(void);
+
+int do_fastboot(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret;
+ enum media_type media = rda_media_get();
+
+ udc_init();
+ drv_fastboot_init();
+
+ if ((media == MEDIA_NAND) || (media == MEDIA_SPINAND)) {
+ ret = mtdparts_init_default();
+ if(ret) {
+ fb_info("nand format partition fail %d\n", ret);
+ return -1;
+ }
+ }
+
+ if (media == MEDIA_MMC) {
+ ret = mmc_parts_format();
+ if (ret) {
+ fb_info("mmc format partition fail %d\n", ret);
+ return -1;
+ }
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ }
+ fastboot_wait_for_configed();
+
+ fastboot_start((void *)SCRATCH_ADDR, FB_DOWNLOAD_BUF_SIZE);
+ return 0;
+}
+
+U_BOOT_CMD(fastboot, 1, 1, do_fastboot,
+ "android fastboot protocol", "flash image to nand");
+
+int do_abootf(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ boot_linux_from_flash();
+ return 1;
+}
+
+U_BOOT_CMD(abootf, 1, 0, do_abootf,
+ "boot android boot.img from flash",
+ " - boot android boot.image from flash");
+
+int do_abootm(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ ulong offset = 0;
+ if (argc >= 2) {
+ offset = simple_strtoul(argv[1], NULL, 16);
+ }
+ boot_linux_from_mem((void *)offset);
+ return 1;
+}
+
+U_BOOT_CMD(abootm, 2, 0, do_abootm,
+ "boot android boot.img from memory",
+ "[ off ]\n"
+ " - boot android boot.image from memory with offset 'off'");
+
+#endif
diff --git a/drivers/usb/gadget/fastboot.h b/drivers/usb/gadget/fastboot.h
new file mode 100644
index 0000000000..123f523b90
--- /dev/null
+++ b/drivers/usb/gadget/fastboot.h
@@ -0,0 +1,8 @@
+#ifndef __FASTBOOT_H__
+#define __FASTBOOT_H__
+
+extern struct usb_endpoint_instance * fastboot_get_out_ep(void);
+extern struct usb_endpoint_instance * fastboot_get_in_ep(void);
+extern int fastboot_configured (void);
+
+#endif
diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c
index 1050a98b6c..3fdfdf7af0 100644
--- a/drivers/usb/gadget/s3c_udc_otg.c
+++ b/drivers/usb/gadget/s3c_udc_otg.c
@@ -30,13 +30,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-
+#undef DEBUG
#include <common.h>
#include <asm/errno.h>
#include <linux/list.h>
#include <malloc.h>
#include <linux/usb/ch9.h>
+#include <usbdescriptors.h>
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
@@ -53,19 +54,11 @@
#define OTG_DMA_MODE 1
-#undef DEBUG_S3C_UDC_SETUP
-#undef DEBUG_S3C_UDC_EP0
-#undef DEBUG_S3C_UDC_ISR
-#undef DEBUG_S3C_UDC_OUT_EP
-#undef DEBUG_S3C_UDC_IN_EP
-#undef DEBUG_S3C_UDC
-
-/* #define DEBUG_S3C_UDC_SETUP */
-/* #define DEBUG_S3C_UDC_EP0 */
-/* #define DEBUG_S3C_UDC_ISR */
-/* #define DEBUG_S3C_UDC_OUT_EP */
-/* #define DEBUG_S3C_UDC_IN_EP */
-/* #define DEBUG_S3C_UDC */
+#define DEBUG_SETUP 0
+#define DEBUG_EP0 0
+#define DEBUG_ISR 0
+#define DEBUG_OUT_EP 0
+#define DEBUG_IN_EP 0
#include <usb/s3c_udc.h>
@@ -132,6 +125,19 @@ static void nuke(struct s3c_ep *ep, int status);
static int s3c_udc_set_halt(struct usb_ep *_ep, int value);
static void s3c_udc_set_nak(struct s3c_ep *ep);
+void set_udc_gadget_private_data(void *p)
+{
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: the_controller: 0x%p, p: 0x%p\n", __func__,
+ the_controller, p);
+ the_controller->gadget.dev.device_data = p;
+}
+
+void *get_udc_gadget_private_data(struct usb_gadget *gadget)
+{
+ return gadget->dev.device_data;
+}
+
static struct usb_ep_ops s3c_ep_ops = {
.enable = s3c_ep_enable,
.disable = s3c_ep_disable,
@@ -216,7 +222,7 @@ void otg_phy_off(struct s3c_udc *dev)
*/
static void udc_disable(struct s3c_udc *dev)
{
- DEBUG_SETUP("%s: %p\n", __func__, dev);
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
udc_set_address(dev, 0);
@@ -234,7 +240,7 @@ static void udc_reinit(struct s3c_udc *dev)
{
unsigned int i;
- DEBUG_SETUP("%s: %p\n", __func__, dev);
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
/* device/ep0 records init */
INIT_LIST_HEAD(&dev->gadget.ep_list);
@@ -265,12 +271,13 @@ static void udc_reinit(struct s3c_udc *dev)
*/
static int udc_enable(struct s3c_udc *dev)
{
- DEBUG_SETUP("%s: %p\n", __func__, dev);
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
otg_phy_init(dev);
reconfig_usbd();
- DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n",
+ debug_cond(DEBUG_SETUP != 0,
+ "S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n",
readl(&reg->gintmsk));
dev->gadget.speed = USB_SPEED_UNKNOWN;
@@ -287,7 +294,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
int retval = 0;
unsigned long flags;
- DEBUG_SETUP("%s: %s\n", __func__, "no name");
+ debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
if (!driver
|| (driver->speed != USB_SPEED_FULL
@@ -311,7 +318,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
retval = driver->bind(&dev->gadget);
if (retval) {
- DEBUG_SETUP("%s: bind to driver --> error %d\n",
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: bind to driver --> error %d\n",
dev->gadget.name, retval);
dev->driver = 0;
return retval;
@@ -319,7 +327,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
enable_irq(IRQ_OTG);
- DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name);
+ debug_cond(DEBUG_SETUP != 0,
+ "Registered gadget driver %s\n", dev->gadget.name);
udc_enable(dev);
return 0;
@@ -377,7 +386,7 @@ static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
/* don't modify queue heads during completion callback */
ep->stopped = 1;
-#ifdef DEBUG_S3C_UDC
+#ifdef DEBUG
printf("calling complete callback\n");
{
int i, len = req->req.length;
@@ -671,7 +680,7 @@ static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
debug("%s: %s %p\n", __func__, ep->name, ep);
- req = kmalloc(sizeof *req, gfp_flags);
+ req = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*req));
if (!req)
return 0;
@@ -865,7 +874,8 @@ int s3c_udc_probe(struct s3c_plat_otg_data *pdata)
the_controller = dev;
for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) {
- dev->dma_buf[i] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL);
+ dev->dma_buf[i] = memalign(CONFIG_SYS_CACHELINE_SIZE,
+ DMA_BUFFER_SIZE);
dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i];
invalidate_dcache_range((unsigned long) dev->dma_buf[i],
(unsigned long) (dev->dma_buf[i]
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
index afd4931fa0..4be603783c 100644
--- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
+++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
@@ -53,7 +53,7 @@ static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev)
writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
&reg->in_endp[EP0_CON].diepctl);
- DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
__func__, readl(&reg->in_endp[EP0_CON].diepctl));
dev->ep0state = WAIT_FOR_IN_COMPLETE;
}
@@ -62,7 +62,8 @@ void s3c_udc_pre_setup(void)
{
u32 ep_ctrl;
- debug_cond(DEBUG_IN_EP, "%s : Prepare Setup packets.\n", __func__);
+ debug_cond(DEBUG_IN_EP,
+ "%s : Prepare Setup packets.\n", __func__);
invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr,
(unsigned long) usb_ctrl_dma_addr
@@ -75,9 +76,9 @@ void s3c_udc_pre_setup(void)
ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
writel(ep_ctrl|DEPCTL_EPENA, &reg->out_endp[EP0_CON].doepctl);
- DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
__func__, readl(&reg->in_endp[EP0_CON].diepctl));
- DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
__func__, readl(&reg->out_endp[EP0_CON].doepctl));
}
@@ -86,9 +87,9 @@ static inline void s3c_ep0_complete_out(void)
{
u32 ep_ctrl;
- DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
__func__, readl(&reg->in_endp[EP0_CON].diepctl));
- DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
__func__, readl(&reg->out_endp[EP0_CON].doepctl));
debug_cond(DEBUG_IN_EP,
@@ -106,9 +107,9 @@ static inline void s3c_ep0_complete_out(void)
writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
&reg->out_endp[EP0_CON].doepctl);
- DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
__func__, readl(&reg->in_endp[EP0_CON].diepctl));
- DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
__func__, readl(&reg->out_endp[EP0_CON].doepctl));
}
@@ -145,14 +146,15 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
&reg->out_endp[ep_num].doeptsiz);
writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, &reg->out_endp[ep_num].doepctl);
- DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x,"
- "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n"
- "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
- __func__, ep_num,
- readl(&reg->out_endp[ep_num].doepdma),
- readl(&reg->out_endp[ep_num].doeptsiz),
- readl(&reg->out_endp[ep_num].doepctl),
- buf, pktcnt, length);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: EP%d RX DMA start : DOEPDMA = 0x%x,"
+ "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n"
+ "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+ __func__, ep_num,
+ readl(&reg->out_endp[ep_num].doepdma),
+ readl(&reg->out_endp[ep_num].doeptsiz),
+ readl(&reg->out_endp[ep_num].doepctl),
+ buf, pktcnt, length);
return 0;
}
@@ -168,7 +170,7 @@ int setdma_tx(struct s3c_ep *ep, struct s3c_request *req)
length = req->req.length - req->req.actual;
if (ep_num == EP0_CON)
- length = min_t(length, (u32)ep_maxpacket(ep));
+ length = min(length, (u32)ep_maxpacket(ep));
ep->len = length;
ep->dma_buf = buf;
@@ -225,8 +227,9 @@ static void complete_rx(struct s3c_udc *dev, u8 ep_num)
u32 *p = the_controller->dma_buf[ep_index(ep)+1];
if (list_empty(&ep->queue)) {
- DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n",
- __func__, ep_num);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: RX DMA done : NULL REQ on OUT EP-%d\n",
+ __func__, ep_num);
return;
}
@@ -249,14 +252,15 @@ static void complete_rx(struct s3c_udc *dev, u8 ep_num)
req->req.actual += min(xfer_size, req->req.length - req->req.actual);
is_short = (xfer_size < ep->ep.maxpacket);
- DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, "
- "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n",
- __func__, ep_num, req->req.actual, req->req.length,
- is_short, ep_tsr, xfer_size);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: RX DMA done : ep = %d, rx bytes = %d/%d, "
+ "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n",
+ __func__, ep_num, req->req.actual, req->req.length,
+ is_short, ep_tsr, xfer_size);
if (is_short || req->req.actual == req->req.length) {
if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) {
- DEBUG_OUT_EP(" => Send ZLP\n");
+ debug_cond(DEBUG_OUT_EP != 0, " => Send ZLP\n");
s3c_udc_ep0_zlp(dev);
/* packet will be completed in complete_tx() */
dev->ep0state = WAIT_FOR_IN_COMPLETE;
@@ -266,8 +270,9 @@ static void complete_rx(struct s3c_udc *dev, u8 ep_num)
if (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next,
struct s3c_request, queue);
- DEBUG_OUT_EP("%s: Next Rx request start...\n",
- __func__);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: Next Rx request start...\n",
+ __func__);
setdma_rx(ep, req);
}
}
@@ -392,8 +397,9 @@ static void process_ep_in_intr(struct s3c_udc *dev)
while (ep_intr) {
if (ep_intr & DAINT_IN_EP_INT(1)) {
ep_intr_status = readl(&reg->in_endp[ep_num].diepint);
- debug_cond(DEBUG_IN_EP, "\tEP%d-IN : DIEPINT = 0x%x\n",
- ep_num, ep_intr_status);
+ debug_cond(DEBUG_IN_EP,
+ "\tEP%d-IN : DIEPINT = 0x%x\n",
+ ep_num, ep_intr_status);
/* Interrupt Clear */
writel(ep_intr_status, &reg->in_endp[ep_num].diepint);
@@ -430,16 +436,18 @@ static void process_ep_out_intr(struct s3c_udc *dev)
u8 ep_num = 0;
ep_intr = readl(&reg->daint);
- DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n",
- __func__, ep_intr);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "*** %s: EP OUT interrupt : DAINT = 0x%x\n",
+ __func__, ep_intr);
ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK;
while (ep_intr) {
if (ep_intr & 0x1) {
ep_intr_status = readl(&reg->out_endp[ep_num].doepint);
- DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n",
- ep_num, ep_intr_status);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "\tEP%d-OUT : DOEPINT = 0x%x\n",
+ ep_num, ep_intr_status);
/* Interrupt Clear */
writel(ep_intr_status, &reg->out_endp[ep_num].doepint);
@@ -457,7 +465,8 @@ static void process_ep_out_intr(struct s3c_udc *dev)
if (ep_intr_status &
CTRL_OUT_EP_SETUP_PHASE_DONE) {
- DEBUG_OUT_EP("SETUP packet arrived\n");
+ debug_cond(DEBUG_OUT_EP != 0,
+ "SETUP packet arrived\n");
s3c_handle_ep0(dev);
}
} else {
@@ -503,7 +512,8 @@ static int s3c_udc_irq(int irq, void *_dev)
usb_status = (readl(&reg->dsts) & 0x6);
if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) {
- debug_cond(DEBUG_ISR, "\t\tFull Speed Detection\n");
+ debug_cond(DEBUG_ISR,
+ "\t\tFull Speed Detection\n");
set_max_pktsize(dev, USB_SPEED_FULL);
} else {
@@ -571,7 +581,8 @@ static int s3c_udc_irq(int irq, void *_dev)
} else {
reset_available = 1;
- debug_cond(DEBUG_ISR, "\t\tRESET handling skipped\n");
+ debug_cond(DEBUG_ISR,
+ "\t\tRESET handling skipped\n");
}
}
@@ -635,7 +646,7 @@ static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req,
_req, _req->length, _req->buf,
list_empty(&ep->queue), ep->stopped);
-#ifdef DEBUG_S3C_UDC
+#ifdef DEBUG
{
int i, len = _req->length;
@@ -662,14 +673,15 @@ static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req,
} else if (ep_is_in(ep)) {
gintsts = readl(&reg->gintsts);
debug_cond(DEBUG_IN_EP,
- "%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n",
- __func__, gintsts);
+ "%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
setdma_tx(ep, req);
} else {
gintsts = readl(&reg->gintsts);
- DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n",
- __func__, gintsts);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
setdma_rx(ep, req);
}
@@ -697,7 +709,7 @@ static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
max = ep_maxpacket(ep);
- DEBUG_EP0("%s: max = %d\n", __func__, max);
+ debug_cond(DEBUG_EP0 != 0, "%s: max = %d\n", __func__, max);
count = setdma_tx(ep, req);
@@ -712,10 +724,11 @@ static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
is_last = 1;
}
- DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__,
- ep->ep.name, count,
- is_last ? "/L" : "",
- req->req.length - req->req.actual - count, req);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: wrote %s %d bytes%s %d left %p\n", __func__,
+ ep->ep.name, count,
+ is_last ? "/L" : "",
+ req->req.length - req->req.actual - count, req);
/* requests complete when all IN data is in the FIFO */
if (is_last) {
@@ -736,8 +749,9 @@ int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max)
(unsigned long) ep->dev->dma_buf[ep_index(ep)]
+ DMA_BUFFER_SIZE);
- DEBUG_EP0("%s: bytes=%d, ep_index=%d %p\n", __func__,
- bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: bytes=%d, ep_index=%d %p\n", __func__,
+ bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]);
return bytes;
}
@@ -756,8 +770,9 @@ static void udc_set_address(struct s3c_udc *dev, unsigned char address)
s3c_udc_ep0_zlp(dev);
- DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n",
- __func__, address, readl(&reg->dcfg));
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n",
+ __func__, address, readl(&reg->dcfg));
dev->usb_address = address;
}
@@ -778,8 +793,9 @@ static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep)
writel(ep_ctrl, &reg->in_endp[EP0_CON].diepctl);
- DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n",
- __func__, ep_index(ep), &reg->in_endp[EP0_CON].diepctl);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: set ep%d stall, DIEPCTL0 = 0x%p\n",
+ __func__, ep_index(ep), &reg->in_endp[EP0_CON].diepctl);
/*
* The application can only set this bit, and the core clears it,
* when a SETUP token is received for this endpoint
@@ -803,8 +819,9 @@ static void s3c_ep0_read(struct s3c_udc *dev)
return;
}
- DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
- __func__, req, req->req.length, req->req.actual);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+ __func__, req, req->req.length, req->req.actual);
if (req->req.length == 0) {
/* zlp for Set_configuration, Set_interface,
@@ -813,8 +830,9 @@ static void s3c_ep0_read(struct s3c_udc *dev)
ep->len = 0;
s3c_udc_ep0_zlp(dev);
- DEBUG_EP0("%s: req.length = 0, bRequest = %d\n",
- __func__, usb_ctrl->bRequest);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: req.length = 0, bRequest = %d\n",
+ __func__, usb_ctrl->bRequest);
return;
}
@@ -836,12 +854,13 @@ static int s3c_ep0_write(struct s3c_udc *dev)
req = list_entry(ep->queue.next, struct s3c_request, queue);
if (!req) {
- DEBUG_EP0("%s: NULL REQ\n", __func__);
+ debug_cond(DEBUG_EP0 != 0, "%s: NULL REQ\n", __func__);
return 0;
}
- DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
- __func__, req, req->req.length, req->req.actual);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+ __func__, req, req->req.length, req->req.actual);
if (req->req.length - req->req.actual == ep0_fifo_size) {
/* Next write will end with the packet size, */
@@ -854,11 +873,13 @@ static int s3c_ep0_write(struct s3c_udc *dev)
if ((ret == 1) && !need_zlp) {
/* Last packet */
dev->ep0state = WAIT_FOR_COMPLETE;
- DEBUG_EP0("%s: finished, waiting for status\n", __func__);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: finished, waiting for status\n", __func__);
} else {
dev->ep0state = DATA_STATE_XMIT;
- DEBUG_EP0("%s: not finished\n", __func__);
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: not finished\n", __func__);
}
return 1;
@@ -873,30 +894,35 @@ int s3c_udc_get_status(struct s3c_udc *dev,
u32 ep_ctrl;
u32 *p = the_controller->dma_buf[1];
- DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_GET_STATUS\n", __func__);
printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK);
switch (crq->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
g_status = 0;
- DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n",
- g_status);
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n",
+ g_status);
break;
case USB_RECIP_DEVICE:
g_status = 0x1; /* Self powered */
- DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n",
- g_status);
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n",
+ g_status);
break;
case USB_RECIP_ENDPOINT:
if (crq->wLength > 2) {
- DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS:Not support EP or wLength\n");
return 1;
}
g_status = dev->ep[ep_num].stopped;
- DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n",
- g_status);
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n",
+ g_status);
break;
@@ -1134,11 +1160,13 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep)
ep_num = ep_index(ep);
dev = ep->dev;
- DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n",
- __func__, ep_num, ep_is_in(ep), clear_feature_flag);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n",
+ __func__, ep_num, ep_is_in(ep), clear_feature_flag);
if (usb_ctrl->wLength != 0) {
- DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tCLEAR_FEATURE: wLength is not zero.....\n");
return 1;
}
@@ -1146,11 +1174,13 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep)
case USB_RECIP_DEVICE:
switch (usb_ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
- DEBUG_SETUP("\tOFF:USB_DEVICE_REMOTE_WAKEUP\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tOFF:USB_DEVICE_REMOTE_WAKEUP\n");
break;
case USB_DEVICE_TEST_MODE:
- DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n");
/** @todo Add CLEAR_FEATURE for TEST modes. */
break;
}
@@ -1159,8 +1189,9 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep)
break;
case USB_RECIP_ENDPOINT:
- DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n",
- usb_ctrl->wValue);
+ debug_cond(DEBUG_SETUP != 0,
+ "\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n",
+ usb_ctrl->wValue);
if (usb_ctrl->wValue == USB_ENDPOINT_HALT) {
if (ep_num == 0) {
@@ -1193,11 +1224,13 @@ static int s3c_udc_set_feature(struct usb_ep *_ep)
ep_num = ep_index(ep);
dev = ep->dev;
- DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n",
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n",
__func__, ep_num);
if (usb_ctrl->wLength != 0) {
- DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: wLength is not zero.....\n");
return 1;
}
@@ -1205,20 +1238,24 @@ static int s3c_udc_set_feature(struct usb_ep *_ep)
case USB_RECIP_DEVICE:
switch (usb_ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
- DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n");
break;
case USB_DEVICE_B_HNP_ENABLE:
- DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
break;
case USB_DEVICE_A_HNP_SUPPORT:
/* RH port supports HNP */
- DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n");
break;
case USB_DEVICE_A_ALT_HNP_SUPPORT:
/* other RH port does */
- DEBUG_SETUP("\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
break;
}
@@ -1226,11 +1263,13 @@ static int s3c_udc_set_feature(struct usb_ep *_ep)
return 0;
case USB_RECIP_INTERFACE:
- DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: USB_RECIP_INTERFACE\n");
break;
case USB_RECIP_ENDPOINT:
- DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: USB_RECIP_ENDPOINT\n");
if (usb_ctrl->wValue == USB_ENDPOINT_HALT) {
if (ep_num == 0) {
s3c_udc_ep0_set_stall(ep);
@@ -1262,14 +1301,15 @@ void s3c_ep0_setup(struct s3c_udc *dev)
/* read control req from fifo (8 bytes) */
s3c_fifo_read(ep, (u32 *)usb_ctrl, 8);
- DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x"
- "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n",
- __func__, usb_ctrl->bRequestType,
- (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT",
- usb_ctrl->bRequest,
- usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: bRequestType = 0x%x(%s), bRequest = 0x%x"
+ "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n",
+ __func__, usb_ctrl->bRequestType,
+ (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT",
+ usb_ctrl->bRequest,
+ usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex);
-#ifdef DEBUG_S3C_UDC
+#ifdef DEBUG
{
int i, len = sizeof(*usb_ctrl);
char *p = (char *)usb_ctrl;
@@ -1286,10 +1326,12 @@ void s3c_ep0_setup(struct s3c_udc *dev)
if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST &&
usb_ctrl->wLength != 1) {
- DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid",
- __func__);
- DEBUG_SETUP("wLength = %d, setup returned\n",
- usb_ctrl->wLength);
+ debug_cond(DEBUG_SETUP != 0,
+ "\t%s:GET_MAX_LUN_REQUEST:invalid",
+ __func__);
+ debug_cond(DEBUG_SETUP != 0,
+ "wLength = %d, setup returned\n",
+ usb_ctrl->wLength);
s3c_udc_ep0_set_stall(ep);
dev->ep0state = WAIT_FOR_SETUP;
@@ -1298,8 +1340,9 @@ void s3c_ep0_setup(struct s3c_udc *dev)
} else if (usb_ctrl->bRequest == BOT_RESET_REQUEST &&
usb_ctrl->wLength != 0) {
/* Bulk-Only *mass storge reset of class-specific request */
- DEBUG_SETUP("%s:BOT Rest:invalid wLength =%d, setup returned\n",
- __func__, usb_ctrl->wLength);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s:BOT Rest:invalid wLength =%d, setup returned\n",
+ __func__, usb_ctrl->wLength);
s3c_udc_ep0_set_stall(ep);
dev->ep0state = WAIT_FOR_SETUP;
@@ -1323,8 +1366,9 @@ void s3c_ep0_setup(struct s3c_udc *dev)
if (dev->req_std) {
switch (usb_ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
- DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n",
- __func__, usb_ctrl->wValue);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_SET_ADDRESS (%d)\n",
+ __func__, usb_ctrl->wValue);
if (usb_ctrl->bRequestType
!= (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
break;
@@ -1333,9 +1377,11 @@ void s3c_ep0_setup(struct s3c_udc *dev)
return;
case USB_REQ_SET_CONFIGURATION:
- DEBUG_SETUP("=====================================\n");
- DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n",
- __func__, usb_ctrl->wValue);
+ debug_cond(DEBUG_SETUP != 0,
+ "=====================================\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: USB_REQ_SET_CONFIGURATION (%d)\n",
+ __func__, usb_ctrl->wValue);
if (usb_ctrl->bRequestType == USB_RECIP_DEVICE)
reset_available = 1;
@@ -1343,13 +1389,15 @@ void s3c_ep0_setup(struct s3c_udc *dev)
break;
case USB_REQ_GET_DESCRIPTOR:
- DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n",
- __func__);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_GET_DESCRIPTOR\n",
+ __func__);
break;
case USB_REQ_SET_INTERFACE:
- DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n",
- __func__, usb_ctrl->wValue);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_SET_INTERFACE (%d)\n",
+ __func__, usb_ctrl->wValue);
if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE)
reset_available = 1;
@@ -1357,8 +1405,9 @@ void s3c_ep0_setup(struct s3c_udc *dev)
break;
case USB_REQ_GET_CONFIGURATION:
- DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n",
- __func__);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_GET_CONFIGURATION\n",
+ __func__);
break;
case USB_REQ_GET_STATUS:
@@ -1384,8 +1433,9 @@ void s3c_ep0_setup(struct s3c_udc *dev)
break;
default:
- DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x"
- "happened.\n", __func__, usb_ctrl->bRequest);
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** Default of usb_ctrl->bRequest=0x%x"
+ "happened.\n", __func__, usb_ctrl->bRequest);
break;
}
}
@@ -1394,7 +1444,8 @@ void s3c_ep0_setup(struct s3c_udc *dev)
if (likely(dev->driver)) {
/* device-2-host (IN) or no data setup command,
* process immediately */
- DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n",
+ debug_cond(DEBUG_SETUP != 0,
+ "%s:usb_ctrlreq will be passed to fsg_setup()\n",
__func__);
spin_unlock(&dev->lock);
@@ -1406,17 +1457,20 @@ void s3c_ep0_setup(struct s3c_udc *dev)
s3c_udc_ep0_set_stall(ep);
dev->ep0state = WAIT_FOR_SETUP;
- DEBUG_SETUP("\tdev->driver->setup failed (%d),"
+ debug_cond(DEBUG_SETUP != 0,
+ "\tdev->driver->setup failed (%d),"
" bRequest = %d\n",
i, usb_ctrl->bRequest);
} else if (dev->req_pending) {
dev->req_pending = 0;
- DEBUG_SETUP("\tdev->req_pending...\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "\tdev->req_pending...\n");
}
- DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]);
+ debug_cond(DEBUG_SETUP != 0,
+ "\tep0state = %s\n", state_names[dev->ep0state]);
}
}
@@ -1427,18 +1481,21 @@ void s3c_ep0_setup(struct s3c_udc *dev)
static void s3c_handle_ep0(struct s3c_udc *dev)
{
if (dev->ep0state == WAIT_FOR_SETUP) {
- DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: WAIT_FOR_SETUP\n", __func__);
s3c_ep0_setup(dev);
} else {
- DEBUG_OUT_EP("%s: strange state!!(state = %s)\n",
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: strange state!!(state = %s)\n",
__func__, state_names[dev->ep0state]);
}
}
static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep)
{
- DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep));
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: ep_is_in = %d\n", __func__, ep_is_in(ep));
if (ep_is_in(ep)) {
dev->ep0state = DATA_STATE_XMIT;
s3c_ep0_write(dev);
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 95555cf96b..4dbe060d6f 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -13,6 +13,7 @@
#include <common.h>
#include <asm/errno.h>
#include <linux/usb/ch9.h>
+#include <usbdescriptors.h>
#include <linux/usb/gadget.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 0d4657edf2..59c3e57563 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -50,6 +50,7 @@ COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o
COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o
COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o
COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
+COBJS-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o
COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 38d6ae03d3..04300be110 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -108,99 +108,6 @@ static struct descriptor {
#define ehci_is_TDI() (0)
#endif
-#if defined(CONFIG_EHCI_DCACHE)
-/*
- * Routines to handle (flush/invalidate) the dcache for the QH and qTD
- * structures and data buffers. This is needed on platforms using this
- * EHCI support with dcache enabled.
- */
-static void flush_invalidate(u32 addr, int size, int flush)
-{
- if (flush)
- flush_dcache_range(addr, addr + size);
- else
- invalidate_dcache_range(addr, addr + size);
-}
-
-static void cache_qtd(struct qTD *qtd, int flush)
-{
- u32 *ptr = (u32 *)qtd->qt_buffer[0];
- int len = (qtd->qt_token & 0x7fff0000) >> 16;
-
- flush_invalidate((u32)qtd, sizeof(struct qTD), flush);
- if (ptr && len)
- flush_invalidate((u32)ptr, len, flush);
-}
-
-
-static inline struct QH *qh_addr(struct QH *qh)
-{
- return (struct QH *)((u32)qh & 0xffffffe0);
-}
-
-static void cache_qh(struct QH *qh, int flush)
-{
- struct qTD *qtd;
- struct qTD *next;
- static struct qTD *first_qtd;
-
- /*
- * Walk the QH list and flush/invalidate all entries
- */
- while (1) {
- flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush);
- if ((u32)qh & QH_LINK_TYPE_QH)
- break;
- qh = qh_addr(qh);
- qh = (struct QH *)qh->qh_link;
- }
- qh = qh_addr(qh);
-
- /*
- * Save first qTD pointer, needed for invalidating pass on this QH
- */
- if (flush)
- first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay &
- 0xffffffe0);
- else
- qtd = first_qtd;
-
- /*
- * Walk the qTD list and flush/invalidate all entries
- */
- while (1) {
- if (qtd == NULL)
- break;
- cache_qtd(qtd, flush);
- next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0);
- if (next == qtd)
- break;
- qtd = next;
- }
-}
-
-static inline void ehci_flush_dcache(struct QH *qh)
-{
- cache_qh(qh, 1);
-}
-
-static inline void ehci_invalidate_dcache(struct QH *qh)
-{
- cache_qh(qh, 0);
-}
-#else /* CONFIG_EHCI_DCACHE */
-/*
- *
- */
-static inline void ehci_flush_dcache(struct QH *qh)
-{
-}
-
-static inline void ehci_invalidate_dcache(struct QH *qh)
-{
-}
-#endif /* CONFIG_EHCI_DCACHE */
-
void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
{
mdelay(50);
@@ -225,11 +132,6 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
return -1;
}
-static void ehci_free(void *p, size_t sz)
-{
-
-}
-
static int ehci_reset(void)
{
uint32_t cmd;
@@ -266,43 +168,22 @@ out:
return ret;
}
-static void *ehci_alloc(size_t sz, size_t align)
-{
- static struct QH qh __attribute__((aligned(32)));
- static struct qTD td[3] __attribute__((aligned (32)));
- static int ntds;
- void *p;
-
- switch (sz) {
- case sizeof(struct QH):
- p = &qh;
- ntds = 0;
- break;
- case sizeof(struct qTD):
- if (ntds == 3) {
- debug("out of TDs\n");
- return NULL;
- }
- p = &td[ntds];
- ntds++;
- break;
- default:
- debug("unknown allocation size\n");
- return NULL;
- }
-
- memset(p, 0, sz);
- return p;
-}
-
static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
{
- uint32_t addr, delta, next;
+ uint32_t delta, next;
+ uint32_t addr = (uint32_t)buf;
+ size_t rsz = roundup(sz, 32);
int idx;
- addr = (uint32_t) buf;
+ if (sz != rsz)
+ debug("EHCI-HCD: Misaligned buffer size (%08x)\n", sz);
+
+ if (addr & 31)
+ debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf);
+
idx = 0;
while (idx < 5) {
+ flush_dcache_range(addr, addr + rsz);
td->qt_buffer[idx] = cpu_to_hc32(addr);
td->qt_buffer_hi[idx] = 0;
next = (addr + 4096) & ~4095;
@@ -326,8 +207,10 @@ static int
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
{
- struct QH *qh;
- struct qTD *td;
+ static struct QH qh __attribute__((aligned(32)));
+ static struct qTD qtd[3] __attribute__((aligned (32)));
+ int qtd_counter = 0;
+
volatile struct qTD *vtd;
unsigned long ts;
uint32_t *tdp;
@@ -346,12 +229,22 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
le16_to_cpu(req->value), le16_to_cpu(req->value),
le16_to_cpu(req->index));
- qh = ehci_alloc(sizeof(struct QH), 32);
- if (qh == NULL) {
- debug("unable to allocate QH\n");
- return -1;
- }
- qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
+ memset(&qh, 0, sizeof(struct QH));
+ memset(qtd, 0, sizeof(qtd));
+
+ toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
+ /*
+ * Setup QH (3.6 in ehci-r10.pdf)
+ *
+ * qh_link ................. 03-00 H
+ * qh_endpt1 ............... 07-04 H
+ * qh_endpt2 ............... 0B-08 H
+ * - qh_curtd
+ * qh_overlay.qt_next ...... 13-10 H
+ * - qh_overlay.qt_altnext
+ */
+ qh.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
usb_pipeendpoint(pipe) == 0) ? 1 : 0;
endpt = (8 << 28) |
@@ -362,88 +255,98 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
(usb_pipespeed(pipe) << 12) |
(usb_pipeendpoint(pipe) << 8) |
(0 << 7) | (usb_pipedevice(pipe) << 0);
- qh->qh_endpt1 = cpu_to_hc32(endpt);
+ qh.qh_endpt1 = cpu_to_hc32(endpt);
endpt = (1 << 30) |
(dev->portnr << 23) |
(dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
- qh->qh_endpt2 = cpu_to_hc32(endpt);
- qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
-
- td = NULL;
- tdp = &qh->qh_overlay.qt_next;
+ qh.qh_endpt2 = cpu_to_hc32(endpt);
+ qh.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
- toggle =
- usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+ tdp = &qh.qh_overlay.qt_next;
if (req != NULL) {
- td = ehci_alloc(sizeof(struct qTD), 32);
- if (td == NULL) {
- debug("unable to allocate SETUP td\n");
- goto fail;
- }
- td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
- td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+ /*
+ * Setup request qTD (3.5 in ehci-r10.pdf)
+ *
+ * qt_next ................ 03-00 H
+ * qt_altnext ............. 07-04 H
+ * qt_token ............... 0B-08 H
+ *
+ * [ buffer, buffer_hi ] loaded with "req".
+ */
+ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+ qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (0 << 31) |
(sizeof(*req) << 16) |
(0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
- td->qt_token = cpu_to_hc32(token);
- if (ehci_td_buffer(td, req, sizeof(*req)) != 0) {
+ qtd[qtd_counter].qt_token = cpu_to_hc32(token);
+ if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) {
debug("unable construct SETUP td\n");
- ehci_free(td, sizeof(*td));
goto fail;
}
- *tdp = cpu_to_hc32((uint32_t) td);
- tdp = &td->qt_next;
+ /* Update previous qTD! */
+ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
+ tdp = &qtd[qtd_counter++].qt_next;
toggle = 1;
}
if (length > 0 || req == NULL) {
- td = ehci_alloc(sizeof(struct qTD), 32);
- if (td == NULL) {
- debug("unable to allocate DATA td\n");
- goto fail;
- }
- td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
- td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+ /*
+ * Setup request qTD (3.5 in ehci-r10.pdf)
+ *
+ * qt_next ................ 03-00 H
+ * qt_altnext ............. 07-04 H
+ * qt_token ............... 0B-08 H
+ *
+ * [ buffer, buffer_hi ] loaded with "buffer".
+ */
+ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+ qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
(length << 16) |
((req == NULL ? 1 : 0) << 15) |
(0 << 12) |
(3 << 10) |
((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
- td->qt_token = cpu_to_hc32(token);
- if (ehci_td_buffer(td, buffer, length) != 0) {
+ qtd[qtd_counter].qt_token = cpu_to_hc32(token);
+ if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) {
debug("unable construct DATA td\n");
- ehci_free(td, sizeof(*td));
goto fail;
}
- *tdp = cpu_to_hc32((uint32_t) td);
- tdp = &td->qt_next;
+ /* Update previous qTD! */
+ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
+ tdp = &qtd[qtd_counter++].qt_next;
}
if (req != NULL) {
- td = ehci_alloc(sizeof(struct qTD), 32);
- if (td == NULL) {
- debug("unable to allocate ACK td\n");
- goto fail;
- }
- td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
- td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+ /*
+ * Setup request qTD (3.5 in ehci-r10.pdf)
+ *
+ * qt_next ................ 03-00 H
+ * qt_altnext ............. 07-04 H
+ * qt_token ............... 0B-08 H
+ */
+ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+ qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
(0 << 16) |
(1 << 15) |
(0 << 12) |
(3 << 10) |
((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
- td->qt_token = cpu_to_hc32(token);
- *tdp = cpu_to_hc32((uint32_t) td);
- tdp = &td->qt_next;
+ qtd[qtd_counter].qt_token = cpu_to_hc32(token);
+ /* Update previous qTD! */
+ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
+ tdp = &qtd[qtd_counter++].qt_next;
}
- qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);
+ qh_list.qh_link = cpu_to_hc32((uint32_t)&qh | QH_LINK_TYPE_QH);
/* Flush dcache */
- ehci_flush_dcache(&qh_list);
+ flush_dcache_range((uint32_t)&qh_list,
+ (uint32_t)&qh_list + sizeof(struct QH));
+ flush_dcache_range((uint32_t)&qh, (uint32_t)&qh + sizeof(struct QH));
+ flush_dcache_range((uint32_t)qtd, (uint32_t)qtd + sizeof(qtd));
usbsts = ehci_readl(&hcor->or_usbsts);
ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));
@@ -462,17 +365,27 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
/* Wait for TDs to be processed. */
ts = get_timer(0);
- vtd = td;
+ vtd = &qtd[qtd_counter - 1];
timeout = USB_TIMEOUT_MS(pipe);
do {
/* Invalidate dcache */
- ehci_invalidate_dcache(&qh_list);
+ invalidate_dcache_range((uint32_t)&qh_list,
+ (uint32_t)&qh_list + sizeof(struct QH));
+ invalidate_dcache_range((uint32_t)&qh,
+ (uint32_t)&qh + sizeof(struct QH));
+ invalidate_dcache_range((uint32_t)qtd,
+ (uint32_t)qtd + sizeof(qtd));
+
token = hc32_to_cpu(vtd->qt_token);
if (!(token & 0x80))
break;
WATCHDOG_RESET();
} while (get_timer(ts) < timeout);
+ /* Invalidate the memory area occupied by buffer */
+ invalidate_dcache_range(((uint32_t)buffer & ~31),
+ ((uint32_t)buffer & ~31) + roundup(length, 32));
+
/* Check that the TD processing happened */
if (token & 0x80) {
printf("EHCI timed out on TD - token=%#x\n", token);
@@ -492,7 +405,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
- token = hc32_to_cpu(qh->qh_overlay.qt_token);
+ token = hc32_to_cpu(qh.qh_overlay.qt_token);
if (!(token & 0x80)) {
debug("TOKEN=%#x\n", token);
switch (token & 0xfc) {
@@ -531,13 +444,6 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
fail:
- td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
- while (td != (void *)QT_NEXT_TERMINATE) {
- qh->qh_overlay.qt_next = td->qt_next;
- ehci_free(td, sizeof(*td));
- td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
- }
- ehci_free(qh, sizeof(*qh));
return -1;
}
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index 5dec673c8e..42c77fe333 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -73,7 +73,8 @@ static void usbh1_internal_phy_clock_gate(int on)
static void usbh1_power_config(void)
{
- struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
+ struct anatop_regs __iomem *anatop =
+ (struct anatop_regs __iomem *)ANATOP_BASE_ADDR;
/*
* Some phy and power's special controls for host1
* 1. The external charger detector needs to be disabled
@@ -87,7 +88,7 @@ static void usbh1_power_config(void)
&anatop->usb2_chrg_detect);
__raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS,
- &anatop->usb2_pll_480_ctrl);
+ &anatop->usb2_pll_480_ctrl_clr);
__raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE |
ANADIG_USB2_PLL_480_CTRL_POWER |
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index 61dbccd53c..45cbd18a00 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -125,11 +125,7 @@ int ehci_hcd_init(void)
hcor = (struct ehci_hcor *)((uint32_t) hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
setbits_le32(&ehci->usbmode, CM_HOST);
-#ifdef CONFIG_MX31
- setbits_le32(&ehci->control, USB_EN);
-
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
-#endif
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
udelay(10000);
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
new file mode 100644
index 0000000000..4dd4ec168f
--- /dev/null
+++ b/drivers/usb/host/ehci-s5p.c
@@ -0,0 +1,110 @@
+/*
+ * SAMSUNG S5P USB HOST EHCI Controller
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Vivek Gautam <gautam.vivek@samsung.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <common.h>
+#include <usb.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/ehci-s5p.h>
+#include "ehci.h"
+#include "ehci-core.h"
+
+/* Setup the EHCI host controller. */
+static void setup_usb_phy(struct s5p_usb_phy *usb)
+{
+ clrbits_le32(&usb->usbphyctrl0,
+ HOST_CTRL0_FSEL_MASK |
+ HOST_CTRL0_COMMONON_N |
+ /* HOST Phy setting */
+ HOST_CTRL0_PHYSWRST |
+ HOST_CTRL0_PHYSWRSTALL |
+ HOST_CTRL0_SIDDQ |
+ HOST_CTRL0_FORCESUSPEND |
+ HOST_CTRL0_FORCESLEEP);
+
+ setbits_le32(&usb->usbphyctrl0,
+ /* Setting up the ref freq */
+ (CLK_24MHZ << 16) |
+ /* HOST Phy setting */
+ HOST_CTRL0_LINKSWRST |
+ HOST_CTRL0_UTMISWRST);
+ udelay(10);
+ clrbits_le32(&usb->usbphyctrl0,
+ HOST_CTRL0_LINKSWRST |
+ HOST_CTRL0_UTMISWRST);
+ udelay(20);
+
+ /* EHCI Ctrl setting */
+ setbits_le32(&usb->ehcictrl,
+ EHCICTRL_ENAINCRXALIGN |
+ EHCICTRL_ENAINCR4 |
+ EHCICTRL_ENAINCR8 |
+ EHCICTRL_ENAINCR16);
+}
+
+/* Reset the EHCI host controller. */
+static void reset_usb_phy(struct s5p_usb_phy *usb)
+{
+ /* HOST_PHY reset */
+ setbits_le32(&usb->usbphyctrl0,
+ HOST_CTRL0_PHYSWRST |
+ HOST_CTRL0_PHYSWRSTALL |
+ HOST_CTRL0_SIDDQ |
+ HOST_CTRL0_FORCESUSPEND |
+ HOST_CTRL0_FORCESLEEP);
+}
+
+/*
+ * EHCI-initialization
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ */
+int ehci_hcd_init(void)
+{
+ struct s5p_usb_phy *usb;
+
+ usb = (struct s5p_usb_phy *)samsung_get_base_usb_phy();
+ setup_usb_phy(usb);
+
+ hccr = (struct ehci_hccr *)(EXYNOS5_USB_HOST_EHCI_BASE);
+ hcor = (struct ehci_hcor *)((uint32_t) hccr
+ + HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+ debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n",
+ (uint32_t)hccr, (uint32_t)hcor,
+ (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+ return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the EHCI host controller.
+ */
+int ehci_hcd_stop()
+{
+ struct s5p_usb_phy *usb;
+
+ usb = (struct s5p_usb_phy *)samsung_get_base_usb_phy();
+ reset_usb_phy(usb);
+
+ return 0;
+}
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 20b5503c90..ed4c60d8f9 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -32,6 +32,10 @@ COBJS-$(CONFIG_USB_DAVINCI) += davinci.o
COBJS-$(CONFIG_USB_OMAP3) += omap3.o
COBJS-$(CONFIG_USB_DA8XX) += da8xx.o
COBJS-$(CONFIG_USB_AM35X) += am35x.o
+COBJS-$(CONFIG_USB_RDA) += rda.o
+ifndef CONFIG_SPL_BUILD
+COBJS-$(CONFIG_MUSB_DMA) += musbhsdma.o
+endif
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 545ebf4b50..5972916f85 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -102,6 +102,7 @@ void musb_configure_ep(const struct musb_epinfo *epinfo, u8 cnt)
if (csr & MUSB_TXCSR_TXPKTRDY)
writew(csr | MUSB_TXCSR_FLUSHFIFO,
&musbr->txcsr);
+ writew(epinfo->epsize, &musbr->txmaxp);
} else {
/* Configure fifo size and fifo base address */
config_fifo(rx, idx, fifoaddr);
@@ -115,6 +116,7 @@ void musb_configure_ep(const struct musb_epinfo *epinfo, u8 cnt)
if (csr & MUSB_RXCSR_RXPKTRDY)
writew(csr | MUSB_RXCSR_FLUSHFIFO,
&musbr->rxcsr);
+ writew(epinfo->epsize, &musbr->rxmaxp);
}
fifoaddr += epinfo->epsize;
epinfo++;
diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c
index 2df52c1c33..2464b05577 100644
--- a/drivers/usb/musb/musb_hcd.c
+++ b/drivers/usb/musb/musb_hcd.c
@@ -1113,10 +1113,11 @@ int usb_lowlevel_init(void)
* should be a usb device connected.
*/
timeout = musb_cfg.timeout;
- while (timeout--)
+ while (timeout) {
if (readb(&musbr->devctl) & MUSB_DEVCTL_HM)
break;
-
+ timeout--;
+ }
/* if musb core is not in host mode, then return */
if (!timeout)
return -1;
diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c
index 09cdec31a9..035fa235cd 100644
--- a/drivers/usb/musb/musb_udc.c
+++ b/drivers/usb/musb/musb_udc.c
@@ -50,27 +50,28 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
+#define DEBUG
#include <common.h>
#include <usb/musb_udc.h>
#include "../gadget/ep0.h"
#include "musb_core.h"
+#include <usb/musb_dma.h>
#if defined(CONFIG_USB_OMAP3)
#include "omap3.h"
#elif defined(CONFIG_USB_AM35X)
#include "am35x.h"
#elif defined(CONFIG_USB_DAVINCI)
#include "davinci.h"
+#elif defined(CONFIG_USB_RDA)
+#include "rda.h"
#endif
/* Define MUSB_DEBUG for debugging */
-/* #define MUSB_DEBUG */
+ #define MUSB_DEBUG
#include "musb_debug.h"
-#define MAX_ENDPOINT 15
-
-#define GET_ENDPOINT(dev,ep) \
-(((struct usb_device_instance *)(dev))->bus->endpoint_array + ep)
+#define MAX_ENDPOINT 6
#define SET_EP0_STATE(s) \
do { \
@@ -95,8 +96,8 @@ do { \
} while (0)
/* static implies these initialized to 0 or NULL */
-static int debug_setup;
-static int debug_level;
+static int debug_setup = 1;
+static int debug_level = 4;
static struct musb_epinfo epinfo[MAX_ENDPOINT * 2];
static enum ep0_state_enum {
IDLE = 0,
@@ -111,7 +112,10 @@ static char *ep0_state_strings[4] = {
"SET_ADDRESS",
};
-static struct urb *ep0_urb;
+static u8 ep0_buffer_data[URB_BUF_SIZE] __attribute__((__aligned__(4)));
+
+static struct urb ep0_urb_instance;
+static struct urb *ep0_urb = &ep0_urb_instance;
struct usb_endpoint_instance *ep0_endpoint;
static struct usb_device_instance *udc_device;
static int enabled;
@@ -125,6 +129,9 @@ static void musb_db_regs(void)
b = readb(&musbr->faddr);
serial_printf("\tfaddr 0x%2.2x\n", b);
+ w = readw(&musbr->hwvers);
+ serial_printf("\thardware version 0x%4.4x\n", w);
+
b = readb(&musbr->power);
musb_print_pwr(b);
@@ -174,7 +181,6 @@ static void musb_peri_softconnect(void)
readw(&musbr->intrtx);
udelay(1000 * 1000); /* 1 sec */
-
/* Power on MUSB */
power = readb(&musbr->power);
power |= MUSB_POWER_SOFTCONN;
@@ -182,7 +188,7 @@ static void musb_peri_softconnect(void)
* The usb device interface is usb 1.1
* Disable 2.0 high speed by clearring the hsenable bit.
*/
- power &= ~MUSB_POWER_HSENAB;
+ //power &= ~MUSB_POWER_HSENAB;
writeb(power, &musbr->power);
/* Check if device is in b-peripheral mode */
@@ -398,7 +404,7 @@ static void musb_peri_ep0_idle(void)
__PRETTY_FUNCTION__,
udc_device->address, faddr);
udelay(1000 * 1000);
- hang();
+ return;
}
}
@@ -638,63 +644,82 @@ static void musb_peri_ep0(void)
musb_peri_ep0_rx();
}
+static inline struct usb_endpoint_instance *get_endpoint(
+ struct usb_device_instance *dev, unsigned int ep)
+{
+ int i;
+ for(i = 0; i < MAX_ENDPOINT; i++) {
+ if(dev->bus->endpoint_array[i].endpoint_address == (ep | USB_DIR_OUT))
+ return &(dev->bus->endpoint_array[i]);
+ }
+ return NULL;
+}
+
+#ifdef CONFIG_MUSB_DMA
+#define DMA_BUF_SIZE 1024
+static u8 dma_buf[DMA_BUF_SIZE] __attribute__((__aligned__(CONFIG_SYS_CACHELINE_SIZE)));
+#endif
static void musb_peri_rx_ep(unsigned int ep)
{
- u16 peri_rxcount = readw(&musbr->ep[ep].epN.rxcount);
+ u16 peri_rxcount = udc_chars_in_rxfifo(ep);
if (peri_rxcount) {
struct usb_endpoint_instance *endpoint;
- u32 length;
u8 *data;
- endpoint = GET_ENDPOINT(udc_device, ep);
+ endpoint = get_endpoint((struct usb_device_instance *)udc_device, ep);
if (endpoint && endpoint->rcv_urb) {
struct urb *urb = endpoint->rcv_urb;
- unsigned int remaining_space = urb->buffer_length -
- urb->actual_length;
+ int urb_bad = 0; /* urb is good */
+ u32 xfer;
- if (remaining_space) {
- int urb_bad = 0; /* urb is good */
-
- if (peri_rxcount > remaining_space)
- length = remaining_space;
- else
- length = peri_rxcount;
-
- data = (u8 *) urb->buffer_data;
- data += urb->actual_length;
-
- /* The common musb fifo reader */
- read_fifo(ep, length, data);
+ xfer = MIN(peri_rxcount, urb->req_length);
+ data = (u8 *) urb->buffer;
+ if ((data == 0) || (xfer == 0))
+ return;
+#ifndef CONFIG_MUSB_DMA
+ /* The common musb fifo reader */
+ read_fifo(ep, xfer, data);
+ if (xfer == peri_rxcount) {//yes, we must read out all data
musb_peri_rx_ack(ep);
-
- /*
- * urb's actual_length is updated in
- * usbd_rcv_complete
- */
- usbd_rcv_complete(endpoint, length, urb_bad);
-
+ }
+#else
+ if (urb->use_dma && xfer <= DMA_BUF_SIZE &&
+ configure_dma_channel(ep, endpoint->rcv_packetSize, 0,
+ (u32)dma_buf, xfer, 0) == 0) {
+ wait_dma_xfer_done();
+ memcpy(data, dma_buf, xfer);
+ if (xfer == peri_rxcount)
+ musb_peri_rx_ack(ep);
} else {
- if (debug_level > 0)
- serial_printf("ERROR : %s %d no space "
- "in rcv buffer\n",
- __PRETTY_FUNCTION__, ep);
+ read_fifo(ep, xfer, data);
+ if (xfer == peri_rxcount) {//yes, we must read out all data
+ musb_peri_rx_ack(ep);
+ }
}
+#endif
+ /*
+ * urb's actual_length is updated in
+ * usbd_rcv_complete
+ */
+ usbd_rcv_complete(endpoint, xfer, urb_bad);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d problem with "
"endpoint\n",
__PRETTY_FUNCTION__, ep);
}
-
} else {
- if (debug_level > 0)
- serial_printf("ERROR : %s %d with nothing to do\n",
- __PRETTY_FUNCTION__, ep);
+ musb_peri_rx_ack(ep);
}
}
+void poll_rx_ep(unsigned int ep)
+{
+ musb_peri_rx_ep(ep);
+}
+
static void musb_peri_rx(u16 intr)
{
unsigned int ep;
@@ -703,9 +728,11 @@ static void musb_peri_rx(u16 intr)
if (0x01 & intr)
musb_peri_ep0();
- for (ep = 1; ep < 16; ep++) {
- if ((1 << ep) & intr)
+ for (ep = 1; ep < MAX_ENDPOINT; ep++) {
+ if ((1 << ep) & intr) {
+ //serial_puts("from irq\n");
musb_peri_rx_ep(ep);
+ }
}
}
@@ -761,12 +788,13 @@ void udc_irq(void)
usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0);
}
+/*
if (MUSB_INTR_SOF & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_ACTIVITY, 0);
musb_peri_resume();
}
-
+*/
if (MUSB_INTR_SUSPEND & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_INACTIVE, 0);
@@ -780,7 +808,6 @@ void udc_irq(void)
if (intrrx)
musb_peri_rx(intrrx);
-
if (intrtx)
musb_peri_tx(intrtx);
} else {
@@ -808,6 +835,49 @@ void udc_unset_nak(int ep_num)
/* noop */
}
+unsigned int udc_chars_in_rxfifo(unsigned int ep)
+{
+ u16 cnt;
+
+ cnt = readw(&musbr->ep[ep].epN.rxcount);
+ /*
+ * when the cnt is not aligned for 4, the data is not put into fifo by
+ * udc, so wait a moment!!!!!!!!
+ */
+ if ((cnt & 0x03) != 0)
+ udelay(100);
+
+ return cnt;
+}
+
+void udc_clear_halt(unsigned int ep, int is_in)
+{
+ u16 csr;
+
+ if(ep > MAX_ENDPOINT) {
+ printf("%s epnum %d too big\n", __func__, ep);
+ return;
+ }
+ printf("%s epnum %d is %s\n", __func__, ep, is_in ? "in" : "out");
+ if (is_in) {
+ csr = readw(&musbr->ep[ep].epN.txcsr);
+ csr |= MUSB_TXCSR_CLRDATATOG | MUSB_TXCSR_P_WZC_BITS;
+ csr &= ~(MUSB_TXCSR_P_SENDSTALL |
+ MUSB_TXCSR_P_SENTSTALL |
+ MUSB_TXCSR_TXPKTRDY);
+ writew(csr, &musbr->ep[ep].epN.txcsr);
+ } else {
+ csr = readw(&musbr->ep[ep].epN.rxcsr);
+ csr |= MUSB_RXCSR_CLRDATATOG | MUSB_RXCSR_P_WZC_BITS;
+ csr &= ~(MUSB_RXCSR_P_SENDSTALL |
+ MUSB_RXCSR_P_SENTSTALL);
+ writew(csr, &musbr->ep[ep].epN.rxcsr);
+ }
+
+ return;
+}
+
+#define WRITE_TIMEOUT 1000000 /* us */
int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
{
int ret = 0;
@@ -816,6 +886,7 @@ int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
if (endpoint->tx_urb && endpoint->state == 0) {
unsigned int ep = endpoint->endpoint_address &
USB_ENDPOINT_NUMBER_MASK;
+ int timeout = WRITE_TIMEOUT;
u16 peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
@@ -825,9 +896,10 @@ int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
}
+/*
if (debug_level > 1)
musb_print_txcsr(peri_txcsr);
-
+*/
/* Check if a packet is waiting to be sent */
if (!(peri_txcsr & MUSB_TXCSR_TXPKTRDY)) {
u32 length;
@@ -849,9 +921,24 @@ int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
musb_peri_tx_ready(ep);
- endpoint->last = length;
- /* usbd_tx_complete will take care of updating 'sent' */
- usbd_tx_complete(endpoint);
+ while ((peri_txcsr & MUSB_TXCSR_FIFONOTEMPTY) && timeout) {
+ peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
+ timeout--;
+ udelay(1);
+ }
+
+ if (timeout <= 0) {
+ /* timeout, clear register */
+ serial_printf("ERROR, can not write out\n");
+ return -1;
+ } else {
+ endpoint->last = length;
+ /* usbd_tx_complete will take care of updating 'sent' */
+ usbd_tx_complete(endpoint);
+ }
+ } else {
+ printf("ERROR: txcsr = %#x, TXPKTRDY is set.\n", peri_txcsr);
+ return -1;
}
} else {
if (debug_level > 0)
@@ -871,20 +958,25 @@ void udc_setup_ep(struct usb_device_instance *device, unsigned int id,
/* EP0 */
ep0_endpoint = endpoint;
ep0_endpoint->endpoint_address = 0xff;
- ep0_urb = usbd_alloc_urb(device, endpoint);
+ //ep0_urb = usbd_alloc_urb(device, endpoint);
+ usbd_init_urb(ep0_urb, device, endpoint, ep0_buffer_data, URB_BUF_SIZE);
} else if (MAX_ENDPOINT >= id) {
int ep_addr;
+ int index;
/* Check the direction */
ep_addr = endpoint->endpoint_address;
if (USB_DIR_IN == (ep_addr & USB_ENDPOINT_DIR_MASK)) {
/* IN */
- epinfo[(id * 2) + 1].epsize = endpoint->tx_packetSize;
+ index = (id - 1) * 2 + 1;
+ epinfo[index].epsize = endpoint->tx_packetSize;
} else {
/* OUT */
- epinfo[id * 2].epsize = endpoint->rcv_packetSize;
+ index = (id - 1) * 2;
+ epinfo[index].epsize = endpoint->rcv_packetSize;
}
+
musb_configure_ep(&epinfo[0],
sizeof(epinfo) / sizeof(struct musb_epinfo));
} else {
@@ -918,6 +1010,13 @@ void udc_disable(void)
enabled = 0;
}
+int is_usbd_high_speed(void)
+{
+ u8 power;
+ power = readb(&musbr->power);
+
+ return ((power & MUSB_POWER_HSMODE) ? 1 : 0);
+}
void udc_startup_events(struct usb_device_instance *device)
{
/* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
@@ -935,6 +1034,17 @@ void udc_startup_events(struct usb_device_instance *device)
udc_enable(device);
}
+void udc_hot_startup(struct usb_device_instance *device)
+{
+ u8 faddr;
+
+ usbd_device_event_irq(device, DEVICE_CONFIGURED, 0);
+ udc_enable(device);
+
+ faddr = readb(&musbr->faddr);
+ udc_device->address = faddr;
+}
+
int udc_init(void)
{
int ret;
@@ -947,6 +1057,7 @@ int udc_init(void)
/* Configure all the endpoint FIFO's and start usb controller */
musbr = musb_cfg.regs;
+
/* Initialize the endpoints */
for (ep_loop = 0; ep_loop < MAX_ENDPOINT * 2; ep_loop++) {
epinfo[ep_loop].epnum = (ep_loop / 2) + 1;
@@ -958,6 +1069,30 @@ int udc_init(void)
ret = 0;
end:
-
return ret;
}
+
+int udc_soft_init(void)
+{
+ int ep_loop;
+
+ /* Configure all the endpoint FIFO's and start usb controller */
+ musbr = musb_cfg.regs;
+
+
+ /* Initialize the endpoints */
+ for (ep_loop = 0; ep_loop < MAX_ENDPOINT * 2; ep_loop++) {
+ epinfo[ep_loop].epnum = (ep_loop / 2) + 1;
+ epinfo[ep_loop].epdir = ep_loop % 2; /* OUT, IN */
+ epinfo[ep_loop].epsize = 0;
+ }
+
+ readb(&musbr->intrusb);
+ readw(&musbr->intrrx);
+ readw(&musbr->intrtx);
+
+ musb_db_regs();
+
+ return 0;
+}
+
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
new file mode 100644
index 0000000000..3fb1b66ea2
--- /dev/null
+++ b/drivers/usb/musb/musbhsdma.c
@@ -0,0 +1,118 @@
+#include <common.h>
+#include "musbhsdma.h"
+#include "rda.h"
+
+
+static u32 start_addr = 0;
+static u32 dma_req_len = 0;
+static int complete = 0;
+
+int configure_dma_channel(u32 epnum, u16 packet_sz, u8 mode,
+ u32 dma_addr, u32 len, int tx)
+{
+ void *mbase = (void *)MUSB_BASE;
+ u8 bchannel = 0;
+ u16 csr = 0;
+
+ complete = 0;
+
+ /* check alignment for 32(0x20) */
+ if ((dma_addr % 32) != 0) {
+ //printf("%s: dma addr %#x is not aligned.\n", __func__, dma_addr);
+ return -1;
+ }
+ if ((len % 32) != 0) {
+ //printf("%s: dma xfer size %#x not aligned.\n", __func__, len);
+ return -1;
+ }
+
+ if (mode) {
+ csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
+ BUG_ON(len < packet_sz);
+ }
+ csr |= MUSB_HSDMA_BURSTMODE_INCR16
+ << MUSB_HSDMA_BURSTMODE_SHIFT;
+
+ csr |= (epnum << MUSB_HSDMA_ENDPOINT_SHIFT)
+ | (1 << MUSB_HSDMA_ENABLE_SHIFT)
+ | (1 << MUSB_HSDMA_IRQENABLE_SHIFT)
+ | (tx ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)
+ : 0);
+
+ start_addr = dma_addr;
+ dma_req_len = len;
+ /* address/count */
+ musb_write_hsdma_addr(mbase, bchannel, dma_addr);
+ musb_write_hsdma_count(mbase, bchannel, len);
+
+ if(tx)
+ flush_dcache_range(dma_addr, dma_addr+len);
+ else
+ invalidate_dcache_range(dma_addr, dma_addr+len);
+
+ /* control (this should start things) */
+ musb_writew(mbase,
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_CONTROL),
+ csr);
+
+ return 0;
+}
+
+static int dma_controller_irq(void)
+{
+ void *mbase = (void *)MUSB_BASE;
+ u8 bchannel = 0;
+ u8 int_hsdma;
+
+ u32 addr;
+ u16 csr;
+ u32 actual_len;
+
+
+ int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
+
+ if (int_hsdma & (1 << bchannel)) {
+
+ csr = musb_readw(mbase,
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel,
+ MUSB_HSDMA_CONTROL));
+
+ if (csr & (1 << MUSB_HSDMA_BUSERROR_SHIFT)) {
+ serial_printf("dma bus error\n");
+ } else {
+ addr = musb_read_hsdma_addr(mbase,
+ bchannel);
+ actual_len = addr
+ - start_addr;
+ /*
+ serial_printf("0x%x -> 0x%x (%zu / %d) %s\n",
+ start_addr,
+ addr, actual_len,
+ dma_req_len,
+ (actual_len
+ < dma_req_len) ?
+ "=> reconfig 0" : "=> complete");
+ */
+ if (actual_len == dma_req_len)
+ complete = 1;
+ }
+ }
+
+ return 0;
+}
+
+int wait_dma_xfer_done(void)
+{
+ int timeout = 100000;
+
+ while(!complete && timeout) {
+ dma_controller_irq();
+ timeout--;
+ }
+ if (timeout <= 0) {
+ serial_printf("ERROR: dma xfer timeout\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
new file mode 100644
index 0000000000..65d6700a03
--- /dev/null
+++ b/drivers/usb/musb/musbhsdma.h
@@ -0,0 +1,95 @@
+/*
+ * MUSB OTG driver - support for Mentor's DMA controller
+ *
+ * Copyright 2005 Mentor Graphics Corporation
+ * Copyright (C) 2005-2007 by Texas Instruments
+ *
+ * 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.
+ *
+ * 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
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <asm/io.h>
+
+static inline u8 musb_readb(const void *addr, unsigned offset)
+ { return readb(addr + offset); }
+
+static inline u16 musb_readw(const void *addr, unsigned offset)
+ { return readw(addr + offset); }
+
+static inline u32 musb_readl(const void *addr, unsigned offset)
+ { return readl(addr + offset); }
+
+
+static inline void musb_writeb(void *addr, unsigned offset, u8 data)
+ { writeb(data, addr + offset); }
+static inline void musb_writew(void *addr, unsigned offset, u16 data)
+ { writew(data, addr + offset); }
+
+static inline void musb_writel(void *addr, unsigned offset, u32 data)
+ { writel(data, addr + offset); }
+
+#define MUSB_HSDMA_BASE 0x200
+#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
+#define MUSB_HSDMA_CONTROL 0x4
+#define MUSB_HSDMA_ADDRESS 0x8
+#define MUSB_HSDMA_COUNT 0xc
+
+#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
+ (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)
+
+#define musb_read_hsdma_addr(mbase, bchannel) \
+ musb_readl(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS))
+
+#define musb_write_hsdma_addr(mbase, bchannel, addr) \
+ musb_writel(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), \
+ addr)
+
+#define musb_read_hsdma_count(mbase, bchannel) \
+ musb_readl(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT))
+
+#define musb_write_hsdma_count(mbase, bchannel, len) \
+ musb_writel(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), \
+ len)
+
+/* control register (16-bit): */
+#define MUSB_HSDMA_ENABLE_SHIFT 0
+#define MUSB_HSDMA_TRANSMIT_SHIFT 1
+#define MUSB_HSDMA_MODE1_SHIFT 2
+#define MUSB_HSDMA_IRQENABLE_SHIFT 3
+#define MUSB_HSDMA_ENDPOINT_SHIFT 4
+#define MUSB_HSDMA_BUSERROR_SHIFT 8
+#define MUSB_HSDMA_BURSTMODE_SHIFT 9
+#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
+#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
+#define MUSB_HSDMA_BURSTMODE_INCR4 1
+#define MUSB_HSDMA_BURSTMODE_INCR8 2
+#define MUSB_HSDMA_BURSTMODE_INCR16 3
+
+#define MUSB_HSDMA_CHANNELS 8
+
diff --git a/drivers/usb/musb/rda.c b/drivers/usb/musb/rda.c
new file mode 100644
index 0000000000..db2dbf52af
--- /dev/null
+++ b/drivers/usb/musb/rda.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2009 Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * This is file is based on
+ * repository git.gitorious.org/u-boot-omap3/mainline.git,
+ * branch omap3-dev-usb, file drivers/usb/host/omap3530_usb.c
+ *
+ * This is the unique part of its copyright :
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2009 Texas Instruments
+ *
+ * ------------------------------------------------------------------------
+ *
+ * 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 <common.h>
+#include "rda.h"
+#include <asm/arch/reg_sysctrl.h>
+#include <asm/arch/hwcfg.h>
+#include <asm/arch/rda_sys.h>
+#include "musb_core.h"
+
+#define MONITOR_REG (MUSB_BASE + 0x80)
+#define MONITOR_TRIG_REG (MUSB_BASE + 0x84)
+#define UDC_PHY_CLK_REG (MUSB_BASE + 0x8c)
+
+struct musb_config musb_cfg = {
+ .regs = (struct musb_regs *)MUSB_BASE,
+ .timeout = RDA_FPGA_USB_TIMEOUT,
+ .musb_speed = 0,
+};
+
+static void udc_soft_reset(void)
+{
+ serial_printf("reset musb otg core...\n");
+ hwp_sysCtrlAp->AHB1_Rst_Set = SYS_CTRL_AP_SET_AHB1_RST_USBC;
+ udelay(1000);
+ hwp_sysCtrlAp->AHB1_Rst_Clr = SYS_CTRL_AP_CLR_AHB1_RST_USBC;
+}
+/*
+static void set_udc_monitor(int testcase)
+{
+ u32 value;
+
+ serial_printf("monitor trigger offset :%x \n", MONITOR_TRIG_REG);
+ value = 0x100 | (testcase & 0xff);
+ serial_printf("monitor trigger value :%x \n", value);
+ writel(value, MONITOR_TRIG_REG);
+
+ serial_printf("monitor offset :%x, result : %x\n", MONITOR_REG, readl(MONITOR_REG));
+}
+*/
+void udc_power_on(void)
+{
+}
+
+void udc_power_off(void)
+{
+}
+
+/*
+ udc maybe initialized by other module,always by ROM code
+ */
+int udc_is_initialized(void)
+{
+ /* some hacks for 8850E U02 USB issue, remove them in future. */
+#if defined(CONFIG_MACH_RDA8850E)
+ /* pdl1 always do hardware usb init */
+#if defined(CONFIG_SPL_BUILD) && \
+ defined(CONFIG_RDA_PDL)
+ return 0;
+#endif
+ /* u-boot calib/autocall/pdl2 mode, need to do hardware usb init */
+#ifndef CONFIG_RDA_PDL
+ return 0;
+#endif
+#endif
+
+ return rda_bm_is_download();
+}
+
+static void udc_setup_pll(void)
+{
+ unsigned mask;
+ unsigned locked;
+ int cnt = 10; //timeout is in ms;
+
+ hwp_sysCtrlAp->Cfg_Pll_Ctrl[3] =
+ SYS_CTRL_AP_AP_PLL_ENABLE_ENABLE |
+ SYS_CTRL_AP_AP_PLL_LOCK_RESET_NO_RESET |
+ SYS_CTRL_AP_AP_PLL_LOCK_NUM_LOW(6)
+ | SYS_CTRL_AP_AP_PLL_LOCK_NUM_HIGH(30);
+
+ mask = SYS_CTRL_AP_PLL_LOCKED_USB_MASK;
+
+ locked = SYS_CTRL_AP_PLL_LOCKED_USB_LOCKED;
+
+ while (((hwp_sysCtrlAp->Sel_Clock & mask) != locked) && cnt) {
+ mdelay(1);
+ cnt--;
+ }
+ if (cnt == 0)
+ printf("ERROR, cannot lock usb pll\n");
+}
+
+static void musb_phy_init(void)
+{
+ udc_setup_pll();
+ /*
+ if (rda_metal_id_get() == 0) {
+ writel(0xf001, UDC_PHY_CLK_REG);
+ } else if (rda_metal_id_get() == 1) {
+ writel(0x5900f000, UDC_PHY_CLK_REG);
+ }
+ */
+}
+
+int musb_platform_init(void)
+{
+ udc_soft_reset();
+ musb_phy_init();
+
+ return 0;
+}
+
+void musb_platform_deinit(void)
+{
+ /* noop */
+}
+
+void print_reg(void)
+{
+ u8 power;
+ u8 testmode;
+ u8 devctl;
+ musbr = musb_cfg.regs;
+ power = readb(&musbr->power);
+ testmode = readb(&musbr->testmode);
+ devctl = readb(&musbr->devctl);
+ printf("usb power: %x \n",power);
+ printf("usb testmode: %x \n",testmode);
+ printf("usb devctl: %x \n",devctl);
+}
+
+int do_usb_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
+{
+ u8 devctl;
+ u8 test;
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+ if (argc == 2) {
+ if (strcmp(argv[1],"init") == 0) {
+ musbr = musb_cfg.regs;
+ musb_platform_init();
+ musb_start();
+ devctl = readb(&musbr->devctl);
+ devctl |= MUSB_DEVCTL_SESSION;
+ writeb(devctl,&musbr->devctl);
+ printf("usb init finish\n");;
+ } else if (strcmp(argv[1],"j") == 0) {
+ test = readb(&musbr->testmode);
+ test &= ~(MUSB_TEST_K | MUSB_TEST_J);
+ test |= MUSB_TEST_J;
+ writeb(test,&musbr->testmode);
+ printf("usb test_j finish\n");
+ } else if (strcmp(argv[1],"k") == 0) {
+ test = readb(&musbr->testmode);
+ test &= ~(MUSB_TEST_K | MUSB_TEST_J);
+ test |= MUSB_TEST_K;
+ writeb(test,&musbr->testmode);
+ printf("usb test_k finish\n");
+ } else if (strcmp(argv[1],"host") == 0) {
+ writeb(MUSB_POWER_HSENAB,&musbr->power);
+ test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS;
+ writeb(test,&musbr->testmode);
+ printf("usb force host finish\n");
+ } else {
+ printf("error argument!\n");
+ return CMD_RET_USAGE;
+ }
+#ifdef USB_TEST_DEBUG
+ print_reg();
+#endif
+ }
+ return 0;
+}
+
+U_BOOT_CMD(
+ usb_test, 2, 1, do_usb_test,
+ "rda usb slave test",
+ "[init, j, k, host]\n"
+ " -init: USB INIT\n"
+ " -j: TEST_J\n"
+ " -k: TEST_k\n"
+ " -host: FORCE_HOST\n"
+);
diff --git a/drivers/usb/musb/rda.h b/drivers/usb/musb/rda.h
new file mode 100644
index 0000000000..751d421878
--- /dev/null
+++ b/drivers/usb/musb/rda.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009 Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * This file is based on the file drivers/usb/musb/davinci.h
+ *
+ * This is the unique part of its copyright:
+ *
+ * --------------------------------------------------------------------
+ *
+ * Copyright (c) 2008 Texas Instruments
+ * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
+ *
+ * --------------------------------------------------------------------
+ *
+ * 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
+ */
+#ifndef _MUSB_RDA_H_
+#define _MUSB_RDA_H_
+
+#include <asm/arch/rda_iomap.h>
+#include "musb_core.h"
+
+/* Base address of MUSB registers */
+#define MUSB_BASE RDA_USB_BASE
+
+/* Timeout for USB module */
+#define RDA_FPGA_USB_TIMEOUT 0x3FFFFFF
+
+int musb_platform_init(void);
+
+#endif /* _MUSB_RDA_H */
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 6252f6a25f..4fad20dd6e 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -28,7 +28,11 @@ LIB := $(obj)libvideo.o
COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o
+COBJS-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o
+COBJS-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
+ exynos_mipi_dsi_lowlevel.o
COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
+COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o
COBJS-$(CONFIG_S6E63D6) += s6e63d6.o
COBJS-$(CONFIG_SED156X) += sed156x.o
COBJS-$(CONFIG_VIDEO_AMBA) += amba.o
diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c
new file mode 100644
index 0000000000..a1cf44964b
--- /dev/null
+++ b/drivers/video/exynos_fb.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <config.h>
+#include <common.h>
+#include <lcd.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/mipi_dsim.h>
+#include <asm/arch/system.h>
+
+#include "exynos_fb.h"
+
+int lcd_line_length;
+int lcd_color_fg;
+int lcd_color_bg;
+
+void *lcd_base;
+void *lcd_console_address;
+
+short console_col;
+short console_row;
+
+static unsigned int panel_width, panel_height;
+
+/* LCD Panel data */
+vidinfo_t panel_info;
+
+static void exynos_lcd_init_mem(void *lcdbase, vidinfo_t *vid)
+{
+ unsigned long palette_size;
+ unsigned int fb_size;
+
+ fb_size = vid->vl_row * vid->vl_col * (NBITS(vid->vl_bpix) >> 3);
+
+ lcd_base = lcdbase;
+
+ palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16;
+
+ exynos_fimd_lcd_init_mem((unsigned long)lcd_base,
+ (unsigned long)fb_size, palette_size);
+}
+
+static void exynos_lcd_init(vidinfo_t *vid)
+{
+ exynos_fimd_lcd_init(vid);
+}
+
+static void lcd_panel_on(vidinfo_t *vid)
+{
+ udelay(vid->init_delay);
+
+ if (vid->backlight_reset)
+ vid->backlight_reset();
+
+ if (vid->cfg_gpio)
+ vid->cfg_gpio();
+
+ if (vid->lcd_power_on)
+ vid->lcd_power_on();
+
+ udelay(vid->power_on_delay);
+
+ if (vid->reset_lcd) {
+ vid->reset_lcd();
+ udelay(vid->reset_delay);
+ }
+
+ if (vid->backlight_on)
+ vid->backlight_on(1);
+
+ if (vid->cfg_ldo)
+ vid->cfg_ldo();
+
+ if (vid->enable_ldo)
+ vid->enable_ldo(1);
+
+ if (vid->mipi_enabled)
+ exynos_mipi_dsi_init();
+}
+
+void lcd_ctrl_init(void *lcdbase)
+{
+ set_system_display_ctrl();
+ set_lcd_clk();
+
+ /* initialize parameters which is specific to panel. */
+ init_panel_info(&panel_info);
+
+ panel_width = panel_info.vl_width;
+ panel_height = panel_info.vl_height;
+
+ exynos_lcd_init_mem(lcdbase, &panel_info);
+
+ exynos_lcd_init(&panel_info);
+}
+
+void lcd_enable(void)
+{
+ lcd_panel_on(&panel_info);
+}
+
+/* dummy function */
+void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
+{
+ return;
+}
diff --git a/drivers/video/exynos_fb.h b/drivers/video/exynos_fb.h
new file mode 100644
index 0000000000..66f5da6d46
--- /dev/null
+++ b/drivers/video/exynos_fb.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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
+ */
+
+#ifndef _EXYNOS_FB_H_
+#define _EXYNOS_FB_H_
+
+#include <asm/arch/fb.h>
+
+#define MAX_CLOCK (86 * 1000000)
+
+enum exynos_fb_rgb_mode_t {
+ MODE_RGB_P = 0,
+ MODE_BGR_P = 1,
+ MODE_RGB_S = 2,
+ MODE_BGR_S = 3,
+};
+
+enum exynos_cpu_auto_cmd_rate {
+ DISABLE_AUTO_FRM,
+ PER_TWO_FRM,
+ PER_FOUR_FRM,
+ PER_SIX_FRM,
+ PER_EIGHT_FRM,
+ PER_TEN_FRM,
+ PER_TWELVE_FRM,
+ PER_FOURTEEN_FRM,
+ PER_SIXTEEN_FRM,
+ PER_EIGHTEEN_FRM,
+ PER_TWENTY_FRM,
+ PER_TWENTY_TWO_FRM,
+ PER_TWENTY_FOUR_FRM,
+ PER_TWENTY_SIX_FRM,
+ PER_TWENTY_EIGHT_FRM,
+ PER_THIRTY_FRM,
+};
+
+void exynos_fimd_lcd_init_mem(unsigned long screen_base, unsigned long fb_size,
+ unsigned long palette_size);
+void exynos_fimd_lcd_init(vidinfo_t *vid);
+unsigned long exynos_fimd_calc_fbsize(void);
+
+#endif
diff --git a/drivers/video/exynos_fimd.c b/drivers/video/exynos_fimd.c
new file mode 100644
index 0000000000..6416b90fcc
--- /dev/null
+++ b/drivers/video/exynos_fimd.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <config.h>
+#include <common.h>
+#include <asm/io.h>
+#include <lcd.h>
+#include <div64.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cpu.h>
+#include "exynos_fb.h"
+
+static unsigned long *lcd_base_addr;
+static vidinfo_t *pvid;
+
+void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size,
+ u_long palette_size)
+{
+ lcd_base_addr = (unsigned long *)screen_base;
+}
+
+static void exynos_fimd_set_dualrgb(unsigned int enabled)
+{
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+ unsigned int cfg = 0;
+
+ if (enabled) {
+ cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
+ EXYNOS_DUALRGB_VDEN_EN_ENABLE;
+
+ /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
+ cfg |= EXYNOS_DUALRGB_SUB_CNT(pvid->vl_col / 2) |
+ EXYNOS_DUALRGB_MAIN_CNT(0);
+ }
+
+ writel(cfg, &fimd_ctrl->dualrgb);
+}
+
+static void exynos_fimd_set_par(unsigned int win_id)
+{
+ unsigned int cfg = 0;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ /* set window control */
+ cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
+ EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
+ EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
+ EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
+
+ /* DATAPATH is DMA */
+ cfg |= EXYNOS_WINCON_DATAPATH_DMA;
+
+ /* bpp is 32 */
+ cfg |= EXYNOS_WINCON_WSWP_ENABLE;
+
+ /* dma burst is 16 */
+ cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
+
+ /* pixel format is unpacked RGB888 */
+ cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888;
+
+ writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ /* set window position to x=0, y=0*/
+ cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a +
+ EXYNOS_VIDOSD(win_id));
+
+ cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) |
+ EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b +
+ EXYNOS_VIDOSD(win_id));
+
+ /* set window size for window0*/
+ cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c +
+ EXYNOS_VIDOSD(win_id));
+}
+
+static void exynos_fimd_set_buffer_address(unsigned int win_id)
+{
+ unsigned long start_addr, end_addr;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ start_addr = (unsigned long)lcd_base_addr;
+ end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) *
+ pvid->vl_row);
+
+ writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 +
+ EXYNOS_BUFFER_OFFSET(win_id));
+ writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 +
+ EXYNOS_BUFFER_OFFSET(win_id));
+}
+
+static void exynos_fimd_set_clock(vidinfo_t *pvid)
+{
+ unsigned int cfg = 0, div = 0, remainder, remainder_div;
+ unsigned long pixel_clock;
+ unsigned long long src_clock;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ if (pvid->dual_lcd_enabled) {
+ pixel_clock = pvid->vl_freq *
+ (pvid->vl_hspw + pvid->vl_hfpd +
+ pvid->vl_hbpd + pvid->vl_col / 2) *
+ (pvid->vl_vspw + pvid->vl_vfpd +
+ pvid->vl_vbpd + pvid->vl_row);
+ } else if (pvid->interface_mode == FIMD_CPU_INTERFACE) {
+ pixel_clock = pvid->vl_freq *
+ pvid->vl_width * pvid->vl_height *
+ (pvid->cs_setup + pvid->wr_setup +
+ pvid->wr_act + pvid->wr_hold + 1);
+ } else {
+ pixel_clock = pvid->vl_freq *
+ (pvid->vl_hspw + pvid->vl_hfpd +
+ pvid->vl_hbpd + pvid->vl_col) *
+ (pvid->vl_vspw + pvid->vl_vfpd +
+ pvid->vl_vbpd + pvid->vl_row);
+ }
+
+ cfg = readl(&fimd_ctrl->vidcon0);
+ cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
+ EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
+ EXYNOS_VIDCON0_CLKDIR_MASK);
+ cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
+ EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
+
+ if (pixel_clock > MAX_CLOCK)
+ pixel_clock = MAX_CLOCK;
+
+ src_clock = (unsigned long long) get_lcd_clk();
+
+ /* get quotient and remainder. */
+ remainder = do_div(src_clock, pixel_clock);
+ div = src_clock;
+
+ remainder *= 10;
+ remainder_div = remainder / pixel_clock;
+
+ /* round about one places of decimals. */
+ if (remainder_div >= 5)
+ div++;
+
+ /* in case of dual lcd mode. */
+ if (pvid->dual_lcd_enabled)
+ div--;
+
+ cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
+ writel(cfg, &fimd_ctrl->vidcon0);
+}
+
+void exynos_set_trigger(void)
+{
+ unsigned int cfg = 0;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ cfg = readl(&fimd_ctrl->trigcon);
+
+ cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
+
+ writel(cfg, &fimd_ctrl->trigcon);
+}
+
+int exynos_is_i80_frame_done(void)
+{
+ unsigned int cfg = 0;
+ int status;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ cfg = readl(&fimd_ctrl->trigcon);
+
+ /* frame done func is valid only when TRIMODE[0] is set to 1. */
+ status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
+ EXYNOS_I80STATUS_TRIG_DONE;
+
+ return status;
+}
+
+static void exynos_fimd_lcd_on(void)
+{
+ unsigned int cfg = 0;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ /* display on */
+ cfg = readl(&fimd_ctrl->vidcon0);
+ cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
+ writel(cfg, &fimd_ctrl->vidcon0);
+}
+
+static void exynos_fimd_window_on(unsigned int win_id)
+{
+ unsigned int cfg = 0;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ /* enable window */
+ cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
+ EXYNOS_WINCON(win_id));
+ cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
+ writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ cfg = readl(&fimd_ctrl->winshmap);
+ cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
+ writel(cfg, &fimd_ctrl->winshmap);
+}
+
+void exynos_fimd_lcd_off(void)
+{
+ unsigned int cfg = 0;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ cfg = readl(&fimd_ctrl->vidcon0);
+ cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
+ writel(cfg, &fimd_ctrl->vidcon0);
+}
+
+void exynos_fimd_window_off(unsigned int win_id)
+{
+ unsigned int cfg = 0;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
+ EXYNOS_WINCON(win_id));
+ cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
+ writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ cfg = readl(&fimd_ctrl->winshmap);
+ cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
+ writel(cfg, &fimd_ctrl->winshmap);
+}
+
+void exynos_fimd_lcd_init(vidinfo_t *vid)
+{
+ unsigned int cfg = 0, rgb_mode;
+ struct exynos4_fb *fimd_ctrl =
+ (struct exynos4_fb *)samsung_get_base_fimd();
+
+ /* store panel info to global variable */
+ pvid = vid;
+
+ rgb_mode = MODE_RGB_P;
+
+ if (vid->interface_mode == FIMD_RGB_INTERFACE) {
+ cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
+ writel(cfg, &fimd_ctrl->vidcon0);
+
+ cfg = readl(&fimd_ctrl->vidcon2);
+ cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
+ EXYNOS_VIDCON2_TVFORMATSEL_MASK |
+ EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
+ cfg |= EXYNOS_VIDCON2_WB_DISABLE;
+ writel(cfg, &fimd_ctrl->vidcon2);
+
+ /* set polarity */
+ cfg = 0;
+ if (!pvid->vl_clkp)
+ cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
+ if (!pvid->vl_hsp)
+ cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
+ if (!pvid->vl_vsp)
+ cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
+ if (!pvid->vl_dp)
+ cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
+
+ writel(cfg, &fimd_ctrl->vidcon1);
+
+ /* set timing */
+ cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1);
+ cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1);
+ cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1);
+ writel(cfg, &fimd_ctrl->vidtcon0);
+
+ cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1);
+ cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1);
+ cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1);
+
+ writel(cfg, &fimd_ctrl->vidtcon1);
+
+ /* set lcd size */
+ cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1);
+ cfg |= EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1);
+
+ writel(cfg, &fimd_ctrl->vidtcon2);
+ }
+
+ /* set display mode */
+ cfg = readl(&fimd_ctrl->vidcon0);
+ cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
+ cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
+ writel(cfg, &fimd_ctrl->vidcon0);
+
+ /* set par */
+ exynos_fimd_set_par(pvid->win_id);
+
+ /* set memory address */
+ exynos_fimd_set_buffer_address(pvid->win_id);
+
+ /* set buffer size */
+ cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8);
+ writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 +
+ EXYNOS_BUFFER_SIZE(pvid->win_id));
+
+ /* set clock */
+ exynos_fimd_set_clock(pvid);
+
+ /* set rgb mode to dual lcd. */
+ exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled);
+
+ /* display on */
+ exynos_fimd_lcd_on();
+
+ /* window on */
+ exynos_fimd_window_on(pvid->win_id);
+}
+
+unsigned long exynos_fimd_calc_fbsize(void)
+{
+ return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8);
+}
diff --git a/drivers/video/exynos_mipi_dsi.c b/drivers/video/exynos_mipi_dsi.c
new file mode 100644
index 0000000000..aee248c84f
--- /dev/null
+++ b/drivers/video/exynos_mipi_dsi.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <common.h>
+#include <malloc.h>
+#include <linux/err.h>
+#include <asm/arch/dsim.h>
+#include <asm/arch/mipi_dsim.h>
+#include <asm/arch/power.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+
+#include "exynos_mipi_dsi_lowlevel.h"
+#include "exynos_mipi_dsi_common.h"
+
+#define master_to_driver(a) (a->dsim_lcd_drv)
+#define master_to_device(a) (a->dsim_lcd_dev)
+
+static struct exynos_platform_mipi_dsim *dsim_pd;
+
+struct mipi_dsim_ddi {
+ int bus_id;
+ struct list_head list;
+ struct mipi_dsim_lcd_device *dsim_lcd_dev;
+ struct mipi_dsim_lcd_driver *dsim_lcd_drv;
+};
+
+static LIST_HEAD(dsim_ddi_list);
+static LIST_HEAD(dsim_lcd_dev_list);
+
+int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
+{
+ struct mipi_dsim_ddi *dsim_ddi;
+
+ if (!lcd_dev) {
+ debug("mipi_dsim_lcd_device is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (!lcd_dev->name) {
+ debug("dsim_lcd_device name is NULL.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
+ if (!dsim_ddi) {
+ debug("failed to allocate dsim_ddi object.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi->dsim_lcd_dev = lcd_dev;
+
+ list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
+
+ return 0;
+}
+
+struct mipi_dsim_ddi
+ *exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv)
+{
+ struct mipi_dsim_ddi *dsim_ddi;
+ struct mipi_dsim_lcd_device *lcd_dev;
+
+ list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) {
+ lcd_dev = dsim_ddi->dsim_lcd_dev;
+ if (!lcd_dev)
+ continue;
+
+ if (lcd_drv->id >= 0) {
+ if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0 &&
+ lcd_drv->id == lcd_dev->id) {
+ /**
+ * bus_id would be used to identify
+ * connected bus.
+ */
+ dsim_ddi->bus_id = lcd_dev->bus_id;
+
+ return dsim_ddi;
+ }
+ } else {
+ if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) {
+ /**
+ * bus_id would be used to identify
+ * connected bus.
+ */
+ dsim_ddi->bus_id = lcd_dev->bus_id;
+
+ return dsim_ddi;
+ }
+ }
+
+ kfree(dsim_ddi);
+ list_del(&dsim_ddi_list);
+ }
+
+ return NULL;
+}
+
+int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv)
+{
+ struct mipi_dsim_ddi *dsim_ddi;
+
+ if (!lcd_drv) {
+ debug("mipi_dsim_lcd_driver is NULL.\n");
+ return -EFAULT;
+ }
+
+ if (!lcd_drv->name) {
+ debug("dsim_lcd_driver name is NULL.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
+ if (!dsim_ddi) {
+ debug("mipi_dsim_ddi object not found.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi->dsim_lcd_drv = lcd_drv;
+
+ debug("registered panel driver(%s) to mipi-dsi driver.\n",
+ lcd_drv->name);
+
+ return 0;
+
+}
+
+struct mipi_dsim_ddi
+ *exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim,
+ const char *name)
+{
+ struct mipi_dsim_ddi *dsim_ddi;
+ struct mipi_dsim_lcd_driver *lcd_drv;
+ struct mipi_dsim_lcd_device *lcd_dev;
+
+ list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) {
+ lcd_drv = dsim_ddi->dsim_lcd_drv;
+ lcd_dev = dsim_ddi->dsim_lcd_dev;
+ if (!lcd_drv || !lcd_dev)
+ continue;
+
+ debug("lcd_drv->id = %d, lcd_dev->id = %d\n",
+ lcd_drv->id, lcd_dev->id);
+
+ if ((strcmp(lcd_drv->name, name) == 0)) {
+ lcd_dev->master = dsim;
+
+ dsim->dsim_lcd_dev = lcd_dev;
+ dsim->dsim_lcd_drv = lcd_drv;
+
+ return dsim_ddi;
+ }
+ }
+
+ return NULL;
+}
+
+/* define MIPI-DSI Master operations. */
+static struct mipi_dsim_master_ops master_ops = {
+ .cmd_write = exynos_mipi_dsi_wr_data,
+ .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status,
+ .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done,
+};
+
+int exynos_mipi_dsi_init(void)
+{
+ struct mipi_dsim_device *dsim;
+ struct mipi_dsim_config *dsim_config;
+ struct mipi_dsim_ddi *dsim_ddi;
+
+ dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL);
+ if (!dsim) {
+ debug("failed to allocate dsim object.\n");
+ return -EFAULT;
+ }
+
+ /* get mipi_dsim_config. */
+ dsim_config = dsim_pd->dsim_config;
+ if (dsim_config == NULL) {
+ debug("failed to get dsim config data.\n");
+ return -EFAULT;
+ }
+
+ dsim->pd = dsim_pd;
+ dsim->dsim_config = dsim_config;
+ dsim->master_ops = &master_ops;
+
+ /* bind lcd ddi matched with panel name. */
+ dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
+ if (!dsim_ddi) {
+ debug("mipi_dsim_ddi object not found.\n");
+ return -ENOSYS;
+ }
+ if (dsim_pd->lcd_power)
+ dsim_pd->lcd_power();
+
+ if (dsim_pd->mipi_power)
+ dsim_pd->mipi_power();
+
+ /* phy_enable(unsigned int dev_index, unsigned int enable) */
+ if (dsim_pd->phy_enable)
+ dsim_pd->phy_enable(0, 1);
+
+ set_mipi_clk();
+
+ exynos_mipi_dsi_init_dsim(dsim);
+ exynos_mipi_dsi_init_link(dsim);
+ exynos_mipi_dsi_set_hs_enable(dsim);
+
+ /* set display timing. */
+ exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config);
+
+ /* initialize mipi-dsi client(lcd panel). */
+ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->mipi_panel_init) {
+ dsim_ddi->dsim_lcd_drv->mipi_panel_init(dsim);
+ dsim_ddi->dsim_lcd_drv->mipi_display_on(dsim);
+ }
+
+ debug("mipi-dsi driver(%s mode) has been probed.\n",
+ (dsim_config->e_interface == DSIM_COMMAND) ?
+ "CPU" : "RGB");
+
+ return 0;
+}
+
+void exynos_set_dsim_platform_data(struct exynos_platform_mipi_dsim *pd)
+{
+ if (pd == NULL) {
+ debug("pd is NULL\n");
+ return;
+ }
+
+ dsim_pd = pd;
+}
diff --git a/drivers/video/exynos_mipi_dsi_common.c b/drivers/video/exynos_mipi_dsi_common.c
new file mode 100644
index 0000000000..6eeb46424f
--- /dev/null
+++ b/drivers/video/exynos_mipi_dsi_common.c
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <common.h>
+#include <lcd.h>
+#include <linux/err.h>
+#include <asm/arch/dsim.h>
+#include <asm/arch/mipi_dsim.h>
+
+#include "exynos_mipi_dsi_lowlevel.h"
+
+#define MHZ (1000 * 1000)
+#define FIN_HZ (24 * MHZ)
+
+#define DFIN_PLL_MIN_HZ (6 * MHZ)
+#define DFIN_PLL_MAX_HZ (12 * MHZ)
+
+#define DFVCO_MIN_HZ (500 * MHZ)
+#define DFVCO_MAX_HZ (1000 * MHZ)
+
+#define TRY_GET_FIFO_TIMEOUT (5000 * 2)
+
+/* MIPI-DSIM status types. */
+enum {
+ DSIM_STATE_INIT, /* should be initialized. */
+ DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */
+ DSIM_STATE_HSCLKEN, /* HS clock was enabled. */
+ DSIM_STATE_ULPS
+};
+
+/* define DSI lane types. */
+enum {
+ DSIM_LANE_CLOCK = (1 << 0),
+ DSIM_LANE_DATA0 = (1 << 1),
+ DSIM_LANE_DATA1 = (1 << 2),
+ DSIM_LANE_DATA2 = (1 << 3),
+ DSIM_LANE_DATA3 = (1 << 4)
+};
+
+static unsigned int dpll_table[15] = {
+ 100, 120, 170, 220, 270,
+ 320, 390, 450, 510, 560,
+ 640, 690, 770, 870, 950
+};
+
+static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
+ unsigned int data0, unsigned int data1)
+{
+ unsigned int data_cnt = 0, payload = 0;
+
+ /* in case that data count is more then 4 */
+ for (data_cnt = 0; data_cnt < data1; data_cnt += 4) {
+ /*
+ * after sending 4bytes per one time,
+ * send remainder data less then 4.
+ */
+ if ((data1 - data_cnt) < 4) {
+ if ((data1 - data_cnt) == 3) {
+ payload = *(u8 *)(data0 + data_cnt) |
+ (*(u8 *)(data0 + (data_cnt + 1))) << 8 |
+ (*(u8 *)(data0 + (data_cnt + 2))) << 16;
+ debug("count = 3 payload = %x, %x %x %x\n",
+ payload, *(u8 *)(data0 + data_cnt),
+ *(u8 *)(data0 + (data_cnt + 1)),
+ *(u8 *)(data0 + (data_cnt + 2)));
+ } else if ((data1 - data_cnt) == 2) {
+ payload = *(u8 *)(data0 + data_cnt) |
+ (*(u8 *)(data0 + (data_cnt + 1))) << 8;
+ debug("count = 2 payload = %x, %x %x\n", payload,
+ *(u8 *)(data0 + data_cnt),
+ *(u8 *)(data0 + (data_cnt + 1)));
+ } else if ((data1 - data_cnt) == 1) {
+ payload = *(u8 *)(data0 + data_cnt);
+ }
+ } else {
+ /* send 4bytes per one time. */
+ payload = *(u8 *)(data0 + data_cnt) |
+ (*(u8 *)(data0 + (data_cnt + 1))) << 8 |
+ (*(u8 *)(data0 + (data_cnt + 2))) << 16 |
+ (*(u8 *)(data0 + (data_cnt + 3))) << 24;
+
+ debug("count = 4 payload = %x, %x %x %x %x\n",
+ payload, *(u8 *)(data0 + data_cnt),
+ *(u8 *)(data0 + (data_cnt + 1)),
+ *(u8 *)(data0 + (data_cnt + 2)),
+ *(u8 *)(data0 + (data_cnt + 3)));
+
+ }
+ exynos_mipi_dsi_wr_tx_data(dsim, payload);
+ }
+}
+
+int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
+ unsigned int data0, unsigned int data1)
+{
+ unsigned int timeout = TRY_GET_FIFO_TIMEOUT;
+ unsigned long delay_val, delay;
+ unsigned int check_rx_ack = 0;
+
+ if (dsim->state == DSIM_STATE_ULPS) {
+ debug("state is ULPS.\n");
+
+ return -EINVAL;
+ }
+
+ delay_val = MHZ / dsim->dsim_config->esc_clk;
+ delay = 10 * delay_val;
+
+ mdelay(delay);
+
+ /* only if transfer mode is LPDT, wait SFR becomes empty. */
+ if (dsim->state == DSIM_STATE_STOP) {
+ while (!(exynos_mipi_dsi_get_fifo_state(dsim) &
+ SFR_HEADER_EMPTY)) {
+ if ((timeout--) > 0)
+ mdelay(1);
+ else {
+ debug("SRF header fifo is not empty.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ switch (data_id) {
+ /* short packet types of packet types for command. */
+ case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+ case MIPI_DSI_DCS_SHORT_WRITE:
+ case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+ case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+ debug("data0 = %x data1 = %x\n",
+ data0, data1);
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0, data1);
+ if (check_rx_ack) {
+ /* process response func should be implemented */
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ /* general command */
+ case MIPI_DSI_COLOR_MODE_OFF:
+ case MIPI_DSI_COLOR_MODE_ON:
+ case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+ case MIPI_DSI_TURN_ON_PERIPHERAL:
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0, data1);
+ if (check_rx_ack) {
+ /* process response func should be implemented. */
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ /* packet types for video data */
+ case MIPI_DSI_V_SYNC_START:
+ case MIPI_DSI_V_SYNC_END:
+ case MIPI_DSI_H_SYNC_START:
+ case MIPI_DSI_H_SYNC_END:
+ case MIPI_DSI_END_OF_TRANSMISSION:
+ return 0;
+
+ /* short and response packet types for command */
+ case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+ case MIPI_DSI_DCS_READ:
+ exynos_mipi_dsi_clear_all_interrupt(dsim);
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0, data1);
+ /* process response func should be implemented. */
+ return 0;
+
+ /* long packet type and null packet */
+ case MIPI_DSI_NULL_PACKET:
+ case MIPI_DSI_BLANKING_PACKET:
+ return 0;
+ case MIPI_DSI_GENERIC_LONG_WRITE:
+ case MIPI_DSI_DCS_LONG_WRITE:
+ {
+ unsigned int data_cnt = 0, payload = 0;
+
+ /* if data count is less then 4, then send 3bytes data. */
+ if (data1 < 4) {
+ payload = *(u8 *)(data0) |
+ *(u8 *)(data0 + 1) << 8 |
+ *(u8 *)(data0 + 2) << 16;
+
+ exynos_mipi_dsi_wr_tx_data(dsim, payload);
+
+ debug("count = %d payload = %x,%x %x %x\n",
+ data1, payload,
+ *(u8 *)(data0 + data_cnt),
+ *(u8 *)(data0 + (data_cnt + 1)),
+ *(u8 *)(data0 + (data_cnt + 2)));
+ } else {
+ /* in case that data count is more then 4 */
+ exynos_mipi_dsi_long_data_wr(dsim, data0, data1);
+ }
+
+ /* put data into header fifo */
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data1 & 0xff,
+ (data1 & 0xff00) >> 8);
+
+ }
+ if (check_rx_ack)
+ /* process response func should be implemented. */
+ return 0;
+ else
+ return -EINVAL;
+
+ /* packet typo for video data */
+ case MIPI_DSI_PACKED_PIXEL_STREAM_16:
+ case MIPI_DSI_PACKED_PIXEL_STREAM_18:
+ case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
+ case MIPI_DSI_PACKED_PIXEL_STREAM_24:
+ if (check_rx_ack) {
+ /* process response func should be implemented. */
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+ default:
+ debug("data id %x is not supported current DSI spec.\n",
+ data_id);
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable)
+{
+ int sw_timeout;
+
+ if (enable) {
+ sw_timeout = 1000;
+
+ exynos_mipi_dsi_clear_interrupt(dsim);
+ exynos_mipi_dsi_enable_pll(dsim, 1);
+ while (1) {
+ sw_timeout--;
+ if (exynos_mipi_dsi_is_pll_stable(dsim))
+ return 0;
+ if (sw_timeout == 0)
+ return -EINVAL;
+ }
+ } else
+ exynos_mipi_dsi_enable_pll(dsim, 0);
+
+ return 0;
+}
+
+unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler)
+{
+ unsigned long dfin_pll, dfvco, dpll_out;
+ unsigned int i, freq_band = 0xf;
+
+ dfin_pll = (FIN_HZ / pre_divider);
+
+ /******************************************************
+ * Serial Clock(=ByteClk X 8) FreqBand[3:0] *
+ ******************************************************
+ * ~ 99.99 MHz 0000
+ * 100 ~ 119.99 MHz 0001
+ * 120 ~ 159.99 MHz 0010
+ * 160 ~ 199.99 MHz 0011
+ * 200 ~ 239.99 MHz 0100
+ * 140 ~ 319.99 MHz 0101
+ * 320 ~ 389.99 MHz 0110
+ * 390 ~ 449.99 MHz 0111
+ * 450 ~ 509.99 MHz 1000
+ * 510 ~ 559.99 MHz 1001
+ * 560 ~ 639.99 MHz 1010
+ * 640 ~ 689.99 MHz 1011
+ * 690 ~ 769.99 MHz 1100
+ * 770 ~ 869.99 MHz 1101
+ * 870 ~ 949.99 MHz 1110
+ * 950 ~ 1000 MHz 1111
+ ******************************************************/
+ if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) {
+ debug("fin_pll range should be 6MHz ~ 12MHz\n");
+ exynos_mipi_dsi_enable_afc(dsim, 0, 0);
+ } else {
+ if (dfin_pll < 7 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x1);
+ else if (dfin_pll < 8 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x0);
+ else if (dfin_pll < 9 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x3);
+ else if (dfin_pll < 10 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x2);
+ else if (dfin_pll < 11 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x5);
+ else
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x4);
+ }
+
+ dfvco = dfin_pll * main_divider;
+ debug("dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
+ dfvco, dfin_pll, main_divider);
+ if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
+ debug("fvco range should be 500MHz ~ 1000MHz\n");
+
+ dpll_out = dfvco / (1 << scaler);
+ debug("dpll_out = %lu, dfvco = %lu, scaler = %d\n",
+ dpll_out, dfvco, scaler);
+
+ for (i = 0; i < ARRAY_SIZE(dpll_table); i++) {
+ if (dpll_out < dpll_table[i] * MHZ) {
+ freq_band = i;
+ break;
+ }
+ }
+
+ debug("freq_band = %d\n", freq_band);
+
+ exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler);
+
+ exynos_mipi_dsi_hs_zero_ctrl(dsim, 0);
+ exynos_mipi_dsi_prep_ctrl(dsim, 0);
+
+ /* Freq Band */
+ exynos_mipi_dsi_pll_freq_band(dsim, freq_band);
+
+ /* Stable time */
+ exynos_mipi_dsi_pll_stable_time(dsim,
+ dsim->dsim_config->pll_stable_time);
+
+ /* Enable PLL */
+ debug("FOUT of mipi dphy pll is %luMHz\n",
+ (dpll_out / MHZ));
+
+ return dpll_out;
+}
+
+int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
+ unsigned int byte_clk_sel, unsigned int enable)
+{
+ unsigned int esc_div;
+ unsigned long esc_clk_error_rate;
+ unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0;
+
+ if (enable) {
+ dsim->e_clk_src = byte_clk_sel;
+
+ /* Escape mode clock and byte clock source */
+ exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel);
+
+ /* DPHY, DSIM Link : D-PHY clock out */
+ if (byte_clk_sel == DSIM_PLL_OUT_DIV8) {
+ hs_clk = exynos_mipi_dsi_change_pll(dsim,
+ dsim->dsim_config->p, dsim->dsim_config->m,
+ dsim->dsim_config->s);
+ if (hs_clk == 0) {
+ debug("failed to get hs clock.\n");
+ return -EINVAL;
+ }
+
+ byte_clk = hs_clk / 8;
+ exynos_mipi_dsi_enable_pll_bypass(dsim, 0);
+ exynos_mipi_dsi_pll_on(dsim, 1);
+ /* DPHY : D-PHY clock out, DSIM link : external clock out */
+ } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8)
+ debug("not support EXT CLK source for MIPI DSIM\n");
+ else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS)
+ debug("not support EXT CLK source for MIPI DSIM\n");
+
+ /* escape clock divider */
+ esc_div = byte_clk / (dsim->dsim_config->esc_clk);
+ debug("esc_div = %d, byte_clk = %lu, esc_clk = %lu\n",
+ esc_div, byte_clk, dsim->dsim_config->esc_clk);
+ if ((byte_clk / esc_div) >= (20 * MHZ) ||
+ (byte_clk / esc_div) > dsim->dsim_config->esc_clk)
+ esc_div += 1;
+
+ escape_clk = byte_clk / esc_div;
+ debug("escape_clk = %lu, byte_clk = %lu, esc_div = %d\n",
+ escape_clk, byte_clk, esc_div);
+
+ /* enable escape clock. */
+ exynos_mipi_dsi_enable_byte_clock(dsim, 1);
+
+ /* enable byte clk and escape clock */
+ exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div);
+ /* escape clock on lane */
+ exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
+ (DSIM_LANE_CLOCK | dsim->data_lane), 1);
+
+ debug("byte clock is %luMHz\n",
+ (byte_clk / MHZ));
+ debug("escape clock that user's need is %lu\n",
+ (dsim->dsim_config->esc_clk / MHZ));
+ debug("escape clock divider is %x\n", esc_div);
+ debug("escape clock is %luMHz\n",
+ ((byte_clk / esc_div) / MHZ));
+
+ if ((byte_clk / esc_div) > escape_clk) {
+ esc_clk_error_rate = escape_clk /
+ (byte_clk / esc_div);
+ debug("error rate is %lu over.\n",
+ (esc_clk_error_rate / 100));
+ } else if ((byte_clk / esc_div) < (escape_clk)) {
+ esc_clk_error_rate = (byte_clk / esc_div) /
+ escape_clk;
+ debug("error rate is %lu under.\n",
+ (esc_clk_error_rate / 100));
+ }
+ } else {
+ exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
+ (DSIM_LANE_CLOCK | dsim->data_lane), 0);
+ exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0);
+
+ /* disable escape clock. */
+ exynos_mipi_dsi_enable_byte_clock(dsim, 0);
+
+ if (byte_clk_sel == DSIM_PLL_OUT_DIV8)
+ exynos_mipi_dsi_pll_on(dsim, 0);
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim)
+{
+ dsim->state = DSIM_STATE_INIT;
+
+ switch (dsim->dsim_config->e_no_data_lane) {
+ case DSIM_DATA_LANE_1:
+ dsim->data_lane = DSIM_LANE_DATA0;
+ break;
+ case DSIM_DATA_LANE_2:
+ dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1;
+ break;
+ case DSIM_DATA_LANE_3:
+ dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
+ DSIM_LANE_DATA2;
+ break;
+ case DSIM_DATA_LANE_4:
+ dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
+ DSIM_LANE_DATA2 | DSIM_LANE_DATA3;
+ break;
+ default:
+ debug("data lane is invalid.\n");
+ return -EINVAL;
+ };
+
+ exynos_mipi_dsi_sw_reset(dsim);
+ exynos_mipi_dsi_dp_dn_swap(dsim, 0);
+
+ return 0;
+}
+
+int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ /* enable only frame done interrupt */
+ exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable);
+
+ return 0;
+}
+
+static void convert_to_fb_videomode(struct fb_videomode *mode1,
+ vidinfo_t *mode2)
+{
+ mode1->xres = mode2->vl_width;
+ mode1->yres = mode2->vl_height;
+ mode1->upper_margin = mode2->vl_vfpd;
+ mode1->lower_margin = mode2->vl_vbpd;
+ mode1->left_margin = mode2->vl_hfpd;
+ mode1->right_margin = mode2->vl_hbpd;
+ mode1->vsync_len = mode2->vl_vspw;
+ mode1->hsync_len = mode2->vl_hspw;
+}
+
+int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_config)
+{
+ struct exynos_platform_mipi_dsim *dsim_pd;
+ struct fb_videomode lcd_video;
+ vidinfo_t *vid;
+
+ dsim_pd = (struct exynos_platform_mipi_dsim *)dsim->pd;
+ vid = (vidinfo_t *)dsim_pd->lcd_panel_info;
+
+ convert_to_fb_videomode(&lcd_video, vid);
+
+ /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
+ if (dsim->dsim_config->e_interface == (u32) DSIM_VIDEO) {
+ if (dsim->dsim_config->auto_vertical_cnt == 0) {
+ exynos_mipi_dsi_set_main_disp_vporch(dsim,
+ vid->vl_cmd_allow_len,
+ lcd_video.upper_margin,
+ lcd_video.lower_margin);
+ exynos_mipi_dsi_set_main_disp_hporch(dsim,
+ lcd_video.left_margin,
+ lcd_video.right_margin);
+ exynos_mipi_dsi_set_main_disp_sync_area(dsim,
+ lcd_video.vsync_len,
+ lcd_video.hsync_len);
+ }
+ }
+
+ exynos_mipi_dsi_set_main_disp_resol(dsim, lcd_video.xres,
+ lcd_video.yres);
+
+ exynos_mipi_dsi_display_config(dsim, dsim->dsim_config);
+
+ debug("lcd panel ==> width = %d, height = %d\n",
+ lcd_video.xres, lcd_video.yres);
+
+ return 0;
+}
+
+int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim)
+{
+ unsigned int time_out = 100;
+
+ switch (dsim->state) {
+ case DSIM_STATE_INIT:
+ exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f);
+
+ /* dsi configuration */
+ exynos_mipi_dsi_init_config(dsim);
+ exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1);
+ exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1);
+
+ /* set clock configuration */
+ exynos_mipi_dsi_set_clock(dsim,
+ dsim->dsim_config->e_byte_clk, 1);
+
+ /* check clock and data lane state are stop state */
+ while (!(exynos_mipi_dsi_is_lane_state(dsim))) {
+ time_out--;
+ if (time_out == 0) {
+ debug("DSI Master is not stop state.\n");
+ debug("Check initialization process\n");
+
+ return -EINVAL;
+ }
+ }
+
+ dsim->state = DSIM_STATE_STOP;
+
+ /* BTA sequence counters */
+ exynos_mipi_dsi_set_stop_state_counter(dsim,
+ dsim->dsim_config->stop_holding_cnt);
+ exynos_mipi_dsi_set_bta_timeout(dsim,
+ dsim->dsim_config->bta_timeout);
+ exynos_mipi_dsi_set_lpdr_timeout(dsim,
+ dsim->dsim_config->rx_timeout);
+
+ return 0;
+ default:
+ debug("DSI Master is already init.\n");
+ return 0;
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim)
+{
+ if (dsim->state == DSIM_STATE_STOP) {
+ if (dsim->e_clk_src != DSIM_EXT_CLK_BYPASS) {
+ dsim->state = DSIM_STATE_HSCLKEN;
+
+ /* set LCDC and CPU transfer mode to HS. */
+ exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
+ exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
+
+ exynos_mipi_dsi_enable_hs_clock(dsim, 1);
+
+ return 0;
+ } else
+ debug("clock source is external bypass.\n");
+ } else
+ debug("DSIM is not stop state.\n");
+
+ return 0;
+}
+
+int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int mode)
+{
+ if (mode) {
+ if (dsim->state != DSIM_STATE_HSCLKEN) {
+ debug("HS Clock lane is not enabled.\n");
+ return -EINVAL;
+ }
+
+ exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
+ } else {
+ if (dsim->state == DSIM_STATE_INIT || dsim->state ==
+ DSIM_STATE_ULPS) {
+ debug("DSI Master is not STOP or HSDT state.\n");
+ return -EINVAL;
+ }
+
+ exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
+{
+ return _exynos_mipi_dsi_get_frame_done_status(dsim);
+}
+
+int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
+{
+ _exynos_mipi_dsi_clear_frame_done(dsim);
+
+ return 0;
+}
diff --git a/drivers/video/exynos_mipi_dsi_common.h b/drivers/video/exynos_mipi_dsi_common.h
new file mode 100644
index 0000000000..4d80679a5d
--- /dev/null
+++ b/drivers/video/exynos_mipi_dsi_common.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <linux/fb.h>
+
+#ifndef _EXYNOS_MIPI_DSI_COMMON_H
+#define _EXYNOS_MIPI_DSI_COMMON_H
+
+int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
+ unsigned int data0, unsigned int data1);
+int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable);
+unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler);
+int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
+ unsigned int byte_clk_sel, unsigned int enable);
+int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_info);
+int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int mode);
+int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
+
+#endif /* _EXYNOS_MIPI_DSI_COMMON_H */
diff --git a/drivers/video/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos_mipi_dsi_lowlevel.c
new file mode 100644
index 0000000000..d61b773616
--- /dev/null
+++ b/drivers/video/exynos_mipi_dsi_lowlevel.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <common.h>
+#include <asm/arch/dsim.h>
+#include <asm/arch/mipi_dsim.h>
+#include <asm/arch/power.h>
+#include <asm/arch/cpu.h>
+
+#include "exynos_mipi_dsi_lowlevel.h"
+#include "exynos_mipi_dsi_common.h"
+
+void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = readl(&mipi_dsim->swrst);
+
+ reg |= DSIM_FUNCRST;
+
+ writel(reg, &mipi_dsim->swrst);
+}
+
+void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg = 0;
+
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = readl(&mipi_dsim->swrst);
+
+ reg |= DSIM_SWRST;
+ reg |= DSIM_FUNCRST;
+
+ writel(reg, &mipi_dsim->swrst);
+}
+
+void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->intsrc);
+
+ reg |= INTSRC_SWRST_RELEASE;
+
+ writel(reg, &mipi_dsim->intsrc);
+}
+
+void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
+ unsigned int mode, unsigned int mask)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->intmsk);
+
+ if (mask)
+ reg |= mode;
+ else
+ reg &= ~mode;
+
+ writel(reg, &mipi_dsim->intmsk);
+}
+
+void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
+ unsigned int cfg)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = readl(&mipi_dsim->fifoctrl);
+
+ writel(reg & ~(cfg), &mipi_dsim->fifoctrl);
+ udelay(10 * 1000);
+ reg |= cfg;
+
+ writel(reg, &mipi_dsim->fifoctrl);
+}
+
+/*
+ * this function set PLL P, M and S value in D-PHY
+ */
+void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
+ unsigned int value)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ writel(DSIM_AFC_CTL(value), &mipi_dsim->phyacchr);
+}
+
+void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int width_resol, unsigned int height_resol)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ /* standby should be set after configuration so set to not ready*/
+ reg = (readl(&mipi_dsim->mdresol)) & ~(DSIM_MAIN_STAND_BY);
+ writel(reg, &mipi_dsim->mdresol);
+
+ /* reset resolution */
+ reg &= ~(DSIM_MAIN_VRESOL(0x7ff) | DSIM_MAIN_HRESOL(0x7ff));
+ reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol);
+
+ reg |= DSIM_MAIN_STAND_BY;
+ writel(reg, &mipi_dsim->mdresol);
+}
+
+void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
+ unsigned int cmd_allow, unsigned int vfront, unsigned int vback)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = (readl(&mipi_dsim->mvporch)) &
+ ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) |
+ (DSIM_MAIN_VBP_MASK));
+
+ reg |= ((cmd_allow & 0xf) << DSIM_CMD_ALLOW_SHIFT) |
+ ((vfront & 0x7ff) << DSIM_STABLE_VFP_SHIFT) |
+ ((vback & 0x7ff) << DSIM_MAIN_VBP_SHIFT);
+
+ writel(reg, &mipi_dsim->mvporch);
+}
+
+void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
+ unsigned int front, unsigned int back)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = (readl(&mipi_dsim->mhporch)) &
+ ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK));
+
+ reg |= (front << DSIM_MAIN_HFP_SHIFT) | (back << DSIM_MAIN_HBP_SHIFT);
+
+ writel(reg, &mipi_dsim->mhporch);
+}
+
+void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = (readl(&mipi_dsim->msync)) &
+ ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK));
+
+ reg |= ((vert & 0x3ff) << DSIM_MAIN_VSA_SHIFT) |
+ (hori << DSIM_MAIN_HSA_SHIFT);
+
+ writel(reg, &mipi_dsim->msync);
+}
+
+void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = (readl(&mipi_dsim->sdresol)) &
+ ~(DSIM_SUB_STANDY_MASK);
+
+ writel(reg, &mipi_dsim->sdresol);
+
+ reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK);
+ reg |= ((vert & 0x7ff) << DSIM_SUB_VRESOL_SHIFT) |
+ ((hori & 0x7ff) << DSIM_SUB_HRESOL_SHIFT);
+ writel(reg, &mipi_dsim->sdresol);
+
+ /* DSIM STANDBY */
+ reg |= (1 << DSIM_SUB_STANDY_SHIFT);
+ writel(reg, &mipi_dsim->sdresol);
+}
+
+void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim)
+{
+ struct mipi_dsim_config *dsim_config = dsim->dsim_config;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int cfg = (readl(&mipi_dsim->config)) &
+ ~((1 << DSIM_EOT_PACKET_SHIFT) |
+ (0x1f << DSIM_HSA_MODE_SHIFT) |
+ (0x3 << DSIM_NUM_OF_DATALANE_SHIFT));
+
+ cfg |= (dsim_config->auto_flush << DSIM_AUTO_FLUSH_SHIFT) |
+ (dsim_config->eot_disable << DSIM_EOT_PACKET_SHIFT) |
+ (dsim_config->auto_vertical_cnt << DSIM_AUTO_MODE_SHIFT) |
+ (dsim_config->hse << DSIM_HSE_MODE_SHIFT) |
+ (dsim_config->hfp << DSIM_HFP_MODE_SHIFT) |
+ (dsim_config->hbp << DSIM_HBP_MODE_SHIFT) |
+ (dsim_config->hsa << DSIM_HSA_MODE_SHIFT) |
+ (dsim_config->e_no_data_lane << DSIM_NUM_OF_DATALANE_SHIFT);
+
+ writel(cfg, &mipi_dsim->config);
+}
+
+void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_config)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ u32 reg = (readl(&mipi_dsim->config)) &
+ ~((0x3 << DSIM_BURST_MODE_SHIFT) | (1 << DSIM_VIDEO_MODE_SHIFT)
+ | (0x3 << DSIM_MAINVC_SHIFT) | (0x7 << DSIM_MAINPIX_SHIFT)
+ | (0x3 << DSIM_SUBVC_SHIFT) | (0x7 << DSIM_SUBPIX_SHIFT));
+
+ if (dsim_config->e_interface == DSIM_VIDEO)
+ reg |= (1 << DSIM_VIDEO_MODE_SHIFT);
+ else if (dsim_config->e_interface == DSIM_COMMAND)
+ reg &= ~(1 << DSIM_VIDEO_MODE_SHIFT);
+ else {
+ printf("unknown lcd type.\n");
+ return;
+ }
+
+ /* main lcd */
+ reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << DSIM_BURST_MODE_SHIFT
+ | ((u8) (dsim_config->e_virtual_ch) & 0x3) << DSIM_MAINVC_SHIFT
+ | ((u8) (dsim_config->e_pixel_format) & 0x7) << DSIM_MAINPIX_SHIFT;
+
+ writel(reg, &mipi_dsim->config);
+}
+
+void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim,
+ unsigned int lane, unsigned int enable)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = readl(&mipi_dsim->config);
+
+ if (enable)
+ reg |= DSIM_LANE_ENx(lane);
+ else
+ reg &= ~DSIM_LANE_ENx(lane);
+
+ writel(reg, &mipi_dsim->config);
+}
+
+void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
+ unsigned int count)
+{
+ unsigned int cfg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ /* get the data lane number. */
+ cfg = DSIM_NUM_OF_DATA_LANE(count);
+
+ writel(cfg, &mipi_dsim->config);
+}
+
+void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim,
+ unsigned int enable, unsigned int afc_code)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->phyacchr);
+
+ reg = 0;
+
+ if (enable) {
+ reg |= DSIM_AFC_EN;
+ reg &= ~(0x7 << DSIM_AFC_CTL_SHIFT);
+ reg |= DSIM_AFC_CTL(afc_code);
+ } else
+ reg &= ~DSIM_AFC_EN;
+
+ writel(reg, &mipi_dsim->phyacchr);
+}
+
+void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
+ ~(DSIM_PLL_BYPASS_EXTERNAL);
+
+ reg |= enable << DSIM_PLL_BYPASS_SHIFT;
+
+ writel(reg, &mipi_dsim->clkctrl);
+}
+
+void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
+ unsigned int freq_band)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
+ ~(0x1f << DSIM_FREQ_BAND_SHIFT);
+
+ reg |= ((freq_band & 0x1f) << DSIM_FREQ_BAND_SHIFT);
+
+ writel(reg, &mipi_dsim->pllctrl);
+}
+
+void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
+ ~(0x7ffff << 1);
+
+ reg |= ((pre_divider & 0x3f) << DSIM_PREDIV_SHIFT) |
+ ((main_divider & 0x1ff) << DSIM_MAIN_SHIFT) |
+ ((scaler & 0x7) << DSIM_SCALER_SHIFT);
+
+ writel(reg, &mipi_dsim->pllctrl);
+}
+
+void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
+ unsigned int lock_time)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ writel(lock_time, &mipi_dsim->plltmr);
+}
+
+void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
+ ~(0x1 << DSIM_PLL_EN_SHIFT);
+
+ reg |= ((enable & 0x1) << DSIM_PLL_EN_SHIFT);
+
+ writel(reg, &mipi_dsim->pllctrl);
+}
+
+void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
+ unsigned int src)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
+ ~(0x3 << DSIM_BYTE_CLK_SRC_SHIFT);
+
+ reg |= ((unsigned int) src) << DSIM_BYTE_CLK_SRC_SHIFT;
+
+ writel(reg, &mipi_dsim->clkctrl);
+}
+
+void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
+ ~(1 << DSIM_BYTE_CLKEN_SHIFT);
+
+ reg |= enable << DSIM_BYTE_CLKEN_SHIFT;
+
+ writel(reg, &mipi_dsim->clkctrl);
+}
+
+void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
+ unsigned int enable, unsigned int prs_val)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
+ ~((1 << DSIM_ESC_CLKEN_SHIFT) | (0xffff));
+
+ reg |= enable << DSIM_ESC_CLKEN_SHIFT;
+ if (enable)
+ reg |= prs_val;
+
+ writel(reg, &mipi_dsim->clkctrl);
+}
+
+void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
+ unsigned int lane_sel, unsigned int enable)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->clkctrl);
+
+ if (enable)
+ reg |= DSIM_LANE_ESC_CLKEN(lane_sel);
+ else
+ reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel);
+
+ writel(reg, &mipi_dsim->clkctrl);
+}
+
+void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->escmode)) &
+ ~(0x1 << DSIM_FORCE_STOP_STATE_SHIFT);
+
+ reg |= ((enable & 0x1) << DSIM_FORCE_STOP_STATE_SHIFT);
+
+ writel(reg, &mipi_dsim->escmode);
+}
+
+unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->status);
+
+ /**
+ * check clock and data lane states.
+ * if MIPI-DSI controller was enabled at bootloader then
+ * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK.
+ * so it should be checked for two case.
+ */
+ if ((reg & DSIM_STOP_STATE_DAT(0xf)) &&
+ ((reg & DSIM_STOP_STATE_CLK) ||
+ (reg & DSIM_TX_READY_HS_CLK)))
+ return 1;
+ else
+ return 0;
+}
+
+void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
+ unsigned int cnt_val)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->escmode)) &
+ ~(0x7ff << DSIM_STOP_STATE_CNT_SHIFT);
+
+ reg |= ((cnt_val & 0x7ff) << DSIM_STOP_STATE_CNT_SHIFT);
+
+ writel(reg, &mipi_dsim->escmode);
+}
+
+void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->timeout)) &
+ ~(0xff << DSIM_BTA_TOUT_SHIFT);
+
+ reg |= (timeout << DSIM_BTA_TOUT_SHIFT);
+
+ writel(reg, &mipi_dsim->timeout);
+}
+
+void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->timeout)) &
+ ~(0xffff << DSIM_LPDR_TOUT_SHIFT);
+
+ reg |= (timeout << DSIM_LPDR_TOUT_SHIFT);
+
+ writel(reg, &mipi_dsim->timeout);
+}
+
+void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->escmode);
+
+ reg &= ~DSIM_CMD_LPDT_LP;
+
+ if (lp)
+ reg |= DSIM_CMD_LPDT_LP;
+
+ writel(reg, &mipi_dsim->escmode);
+}
+
+void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->escmode);
+
+ reg &= ~DSIM_TX_LPDT_LP;
+
+ if (lp)
+ reg |= DSIM_TX_LPDT_LP;
+
+ writel(reg, &mipi_dsim->escmode);
+}
+
+void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
+ ~(1 << DSIM_TX_REQUEST_HSCLK_SHIFT);
+
+ reg |= enable << DSIM_TX_REQUEST_HSCLK_SHIFT;
+
+ writel(reg, &mipi_dsim->clkctrl);
+}
+
+void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
+ unsigned int swap_en)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->phyacchr1);
+
+ reg &= ~(0x3 << DSIM_DPDN_SWAP_DATA_SHIFT);
+ reg |= (swap_en & 0x3) << DSIM_DPDN_SWAP_DATA_SHIFT;
+
+ writel(reg, &mipi_dsim->phyacchr1);
+}
+
+void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
+ unsigned int hs_zero)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
+ ~(0xf << DSIM_ZEROCTRL_SHIFT);
+
+ reg |= ((hs_zero & 0xf) << DSIM_ZEROCTRL_SHIFT);
+
+ writel(reg, &mipi_dsim->pllctrl);
+}
+
+void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
+ ~(0x7 << DSIM_PRECTRL_SHIFT);
+
+ reg |= ((prep & 0x7) << DSIM_PRECTRL_SHIFT);
+
+ writel(reg, &mipi_dsim->pllctrl);
+}
+
+void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->intsrc);
+
+ reg |= INTSRC_PLL_STABLE;
+
+ writel(reg, &mipi_dsim->intsrc);
+}
+
+void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ writel(0xffffffff, &mipi_dsim->intsrc);
+}
+
+unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ reg = readl(&mipi_dsim->status);
+
+ return reg & DSIM_PLL_STABLE ? 1 : 0;
+}
+
+unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ return readl(&mipi_dsim->fifoctrl) & ~(0x1f);
+}
+
+void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
+ unsigned int di, unsigned int data0, unsigned int data1)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = (DSIM_PKTHDR_DAT1(data1) | DSIM_PKTHDR_DAT0(data0) |
+ DSIM_PKTHDR_DI(di));
+
+ writel(reg, &mipi_dsim->pkthdr);
+}
+
+unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device
+ *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->intsrc);
+
+ return (reg & INTSRC_FRAME_DONE) ? 1 : 0;
+}
+
+void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+ unsigned int reg = readl(&mipi_dsim->intsrc);
+
+ writel(reg | INTSRC_FRAME_DONE, &mipi_dsim->intsrc);
+}
+
+void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
+ unsigned int tx_data)
+{
+ struct exynos_mipi_dsim *mipi_dsim =
+ (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
+
+ writel(tx_data, &mipi_dsim->payload);
+}
diff --git a/drivers/video/exynos_mipi_dsi_lowlevel.h b/drivers/video/exynos_mipi_dsi_lowlevel.h
new file mode 100644
index 0000000000..4b8c441cb5
--- /dev/null
+++ b/drivers/video/exynos_mipi_dsi_lowlevel.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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
+ */
+
+#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H
+#define _EXYNOS_MIPI_DSI_LOWLEVEL_H
+
+void exynos_mipi_dsi_register(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
+ unsigned int mode, unsigned int mask);
+void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
+ unsigned int count);
+void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
+ unsigned int cfg);
+void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
+ unsigned int value);
+void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
+ unsigned int value);
+void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int width_resol, unsigned int height_resol);
+void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
+ unsigned int cmd_allow, unsigned int vfront, unsigned int vback);
+void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
+ unsigned int front, unsigned int back);
+void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori);
+void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori);
+void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_config);
+void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
+ unsigned int count);
+void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim,
+ unsigned int lane, unsigned int enable);
+void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim,
+ unsigned int enable, unsigned int afc_code);
+void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
+ unsigned int freq_band);
+void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler);
+void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
+ unsigned int lock_time);
+void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
+ unsigned int src);
+void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
+ unsigned int enable, unsigned int prs_val);
+void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
+ unsigned int lane_sel, unsigned int enable);
+void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
+ unsigned int cnt_val);
+void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout);
+void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout);
+void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp);
+void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp);
+void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
+ unsigned int swap_en);
+void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
+ unsigned int hs_zero);
+void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim,
+ unsigned int prep);
+void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim);
+unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim);
+unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim);
+unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device
+ *dsim);
+void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
+ unsigned int di, unsigned int data0, unsigned int data1);
+void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
+ unsigned int tx_data);
+
+#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */
diff --git a/drivers/video/s6e8ax0.c b/drivers/video/s6e8ax0.c
new file mode 100644
index 0000000000..1ec7fd6e5a
--- /dev/null
+++ b/drivers/video/s6e8ax0.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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 <common.h>
+#include <asm/arch/mipi_dsim.h>
+
+#include "exynos_mipi_dsi_lowlevel.h"
+#include "exynos_mipi_dsi_common.h"
+
+static void s6e8ax0_panel_cond(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x4c,
+ 0x6e, 0x10, 0x27, 0x7d, 0x3f, 0x10, 0x00, 0x00, 0x20,
+ 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08,
+ 0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc3,
+ 0xff, 0xff, 0xc8
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_cond(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xf2, 0x80, 0x03, 0x0d
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_gamma_cond(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ /* 7500K 2.2 Set (M3, 300cd) */
+ const unsigned char data_to_send[] = {
+ 0xfa, 0x01, 0x0f, 0x00, 0x0f, 0xda, 0xc0, 0xe4, 0xc8,
+ 0xc8, 0xc6, 0xd3, 0xd6, 0xd0, 0xab, 0xb2, 0xa6, 0xbf,
+ 0xc2, 0xb9, 0x00, 0x93, 0x00, 0x86, 0x00, 0xd1
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_gamma_update(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xf7, 0x3);
+}
+
+static void s6e8ax0_etc_source_control(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xf6, 0x00, 0x02, 0x00
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_pentile_control(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
+ 0x00
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_mipi_control1(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_mipi_control2(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_power_control(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_mipi_control3(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xe3, 0x40);
+}
+
+static void s6e8ax0_etc_mipi_control4(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_set(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xb1, 0x04, 0x00
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_on(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+
+ ops->cmd_write(dsim_dev,
+ MIPI_DSI_DCS_SHORT_WRITE, 0x29, 0x00);
+}
+
+static void s6e8ax0_sleep_out(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+
+ ops->cmd_write(dsim_dev,
+ MIPI_DSI_DCS_SHORT_WRITE, 0x11, 0x00);
+}
+
+static void s6e8ax0_apply_level1_key(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xf0, 0x5a, 0x5a
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_apply_mtp_key(struct mipi_dsim_device *dsim_dev)
+{
+ struct mipi_dsim_master_ops *ops = dsim_dev->master_ops;
+ const unsigned char data_to_send[] = {
+ 0xf1, 0x5a, 0x5a
+ };
+
+ ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_panel_init(struct mipi_dsim_device *dsim_dev)
+{
+ /*
+ * in case of setting gamma and panel condition at first,
+ * it shuold be setting like below.
+ * set_gamma() -> set_panel_condition()
+ */
+
+ s6e8ax0_apply_level1_key(dsim_dev);
+ s6e8ax0_apply_mtp_key(dsim_dev);
+
+ s6e8ax0_sleep_out(dsim_dev);
+ mdelay(5);
+ s6e8ax0_panel_cond(dsim_dev);
+ s6e8ax0_display_cond(dsim_dev);
+ s6e8ax0_gamma_cond(dsim_dev);
+ s6e8ax0_gamma_update(dsim_dev);
+
+ s6e8ax0_etc_source_control(dsim_dev);
+ s6e8ax0_elvss_set(dsim_dev);
+ s6e8ax0_etc_pentile_control(dsim_dev);
+ s6e8ax0_etc_mipi_control1(dsim_dev);
+ s6e8ax0_etc_mipi_control2(dsim_dev);
+ s6e8ax0_etc_power_control(dsim_dev);
+ s6e8ax0_etc_mipi_control3(dsim_dev);
+ s6e8ax0_etc_mipi_control4(dsim_dev);
+}
+
+static int s6e8ax0_panel_set(struct mipi_dsim_device *dsim_dev)
+{
+ s6e8ax0_panel_init(dsim_dev);
+
+ return 0;
+}
+
+static void s6e8ax0_display_enable(struct mipi_dsim_device *dsim_dev)
+{
+ s6e8ax0_display_on(dsim_dev);
+}
+
+static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
+ .name = "s6e8ax0",
+ .id = -1,
+
+ .mipi_panel_init = s6e8ax0_panel_set,
+ .mipi_display_on = s6e8ax0_display_enable,
+};
+
+void s6e8ax0_init(void)
+{
+ exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
+}