diff options
Diffstat (limited to 'drivers')
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(®s->xfertyp, xfertyp); #endif + + /* Mask all irqs */ + esdhc_write32(®s->irqsigen, 0); + /* Wait for the command to complete */ - while (!(esdhc_read32(®s->irqstat) & IRQSTAT_CC)) + while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE))) ; irqstat = esdhc_read32(®s->irqstat); esdhc_write32(®s->irqstat, irqstat); + /* Reset CMD and DATA portions on error */ + if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { + esdhc_write32(®s->sysctl, esdhc_read32(®s->sysctl) | + SYSCTL_RSTC); + while (esdhc_read32(®s->sysctl) & SYSCTL_RSTC) + ; + + if (data) { + esdhc_write32(®s->sysctl, + esdhc_read32(®s->sysctl) | + SYSCTL_RSTD); + while ((esdhc_read32(®s->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(®s->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(®s->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 = ð->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(®->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, ®->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(®->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(®->out_endp[EP0_CON].doepctl); writel(ep_ctrl|DEPCTL_EPENA, ®->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(®->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(®->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(®->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(®->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, ®->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(®->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(®->out_endp[EP0_CON].doepctl)); } @@ -145,14 +146,15 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) ®->out_endp[ep_num].doeptsiz); writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->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(®->out_endp[ep_num].doepdma), - readl(®->out_endp[ep_num].doeptsiz), - readl(®->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(®->out_endp[ep_num].doepdma), + readl(®->out_endp[ep_num].doeptsiz), + readl(®->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(®->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, ®->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(®->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(®->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, ®->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(®->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(®->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(®->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(®->dcfg)); + debug_cond(DEBUG_EP0 != 0, + "%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", + __func__, address, readl(®->dcfg)); dev->usb_address = address; } @@ -778,8 +793,9 @@ static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); - DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", - __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); + debug_cond(DEBUG_EP0 != 0, + "%s: set ep%d stall, DIEPCTL0 = 0x%p\n", + __func__, ep_index(ep), ®->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); +} |