summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2008-10-29 20:21:59 +0000
committerRafaël Carré <rafael.carre@gmail.com>2008-10-29 20:21:59 +0000
commite1b483848120507a909e95417e938324ed377bd5 (patch)
tree3496098d3d781071cde64b165bfa77951835c186 /firmware/target/arm
parent4c457afe899b32f74cfa761b5bd078a296a75251 (diff)
Embryo of a SD driver for Sansav2
Debug code included, needed until the bootloader is ready git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18926 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/as3525/ata_sd_as3525.c341
-rw-r--r--firmware/target/arm/as3525/mmci.h123
2 files changed, 464 insertions, 0 deletions
diff --git a/firmware/target/arm/as3525/ata_sd_as3525.c b/firmware/target/arm/as3525/ata_sd_as3525.c
new file mode 100644
index 0000000000..e8f899bc37
--- /dev/null
+++ b/firmware/target/arm/as3525/ata_sd_as3525.c
@@ -0,0 +1,341 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright © 2008 Rafaël Carré
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Driver for the ARM PL180 SD/MMC controller inside AS3525 SoC */
+
+#include "config.h" /* for HAVE_MULTIVOLUME */
+
+#include "as3525.h"
+#include "mmci.h"
+#include "panic.h"
+#include "stdbool.h"
+#include "ata.h"
+
+#define NAND_AS3525 0
+#define SD_AS3525 1
+static int pl180_base[2] = { NAND_FLASH_BASE, SD_MCI_BASE };
+
+/* ARM PL180 registers */
+#define MMC_POWER(i) (*(volatile unsigned long *) (pl180_base[i]+0x00))
+#define MMC_CLOCK(i) (*(volatile unsigned long *) (pl180_base[i]+0x04))
+#define MMC_ARGUMENT(i) (*(volatile unsigned long *) (pl180_base[i]+0x08))
+#define MMC_COMMAND(i) (*(volatile unsigned long *) (pl180_base[i]+0x0C))
+#define MMC_RESPCMD(i) (*(volatile unsigned long *) (pl180_base[i]+0x10))
+#define MMC_RESP0(i) (*(volatile unsigned long *) (pl180_base[i]+0x14))
+#define MMC_RESP1(i) (*(volatile unsigned long *) (pl180_base[i]+0x18))
+#define MMC_RESP2(i) (*(volatile unsigned long *) (pl180_base[i]+0x1C))
+#define MMC_RESP3(i) (*(volatile unsigned long *) (pl180_base[i]+0x20))
+#define MMC_DATACTRL(i) (*(volatile unsigned long *) (pl180_base[i]+0x2C))
+#define MMC_STATUS(i) (*(volatile unsigned long *) (pl180_base[i]+0x34))
+#define MMC_CLEAR(i) (*(volatile unsigned long *) (pl180_base[i]+0x38))
+#define MMC_MASK0(i) (*(volatile unsigned long *) (pl180_base[i]+0x3C))
+#define MMC_MASK1(i) (*(volatile unsigned long *) (pl180_base[i]+0x40))
+#define MMC_SELECT(i) (*(volatile unsigned long *) (pl180_base[i]+0x44))
+
+
+/* SD commands */
+#define GO_IDLE_STATE 0
+#define MMC_CMD_READ_CID 2
+#define SEND_IF_COND 8
+#define SEND_OP_COND 41
+#define APP_CMD 55
+
+/* command flags */
+#define MMC_NO_FLAGS (0<<0)
+#define MMC_RESP (1<<0)
+#define MMC_LONG_RESP (1<<1)
+#define MMC_ARG (1<<2)
+
+#ifdef BOOTLOADER
+#define DEBUG
+void reset_screen(void);
+void printf(const char *format, ...);
+#endif
+
+struct mmc_command
+{
+ int cmd;
+ int arg;
+ int resp[4];
+ int flags;
+};
+
+static inline void mci_delay(void) { int i = 0xffff; while(i--) ; }
+
+static void mci_set_clock_divider(const int drive, int divider)
+{
+ int clock = MMC_CLOCK(drive);
+
+ if(divider > 1)
+ {
+ /* use divide logic */
+ clock &= ~MCI_CLK_BYPASS;
+
+ /* convert divider to MMC_CLOCK logic */
+ divider = (divider/2) - 1;
+ if(divider >= 256)
+ divider = 255;
+ }
+ else
+ {
+ /* bypass dividing logic */
+ clock |= MCI_CLK_BYPASS;
+ divider = 0;
+ }
+
+ MMC_CLOCK(drive) = clock | divider;
+
+ mci_delay();
+}
+
+static int send_cmd(const int drive, struct mmc_command *cmd)
+{
+ int val, status;
+
+ while(MMC_STATUS(drive) & MCI_CMDACTIVE); /* useless */
+
+ if(MMC_COMMAND(drive) & MCI_CPSM_ENABLE) /* clears existing command */
+ {
+ MMC_COMMAND(drive) = 0;
+ mci_delay();
+ }
+
+ val = cmd->cmd | MCI_CPSM_ENABLE;
+ if(cmd->flags & MMC_RESP)
+ {
+ val |= MCI_CPSM_RESPONSE;
+ if(cmd->flags & MMC_LONG_RESP)
+ val |= MCI_CPSM_LONGRSP;
+ }
+
+ MMC_CLEAR(drive) = 0x7ff;
+
+ MMC_ARGUMENT(drive) = (cmd->flags & MMC_ARG) ? cmd->arg : 0;
+ MMC_COMMAND(drive) = val;
+
+ while(MMC_STATUS(drive) & MCI_CMDACTIVE);
+
+ MMC_COMMAND(drive) = 0;
+ MMC_ARGUMENT(drive) = ~0;
+
+ do
+ {
+ status = MMC_STATUS(drive);
+ if(cmd->flags & MMC_RESP)
+ {
+ if(status & MCI_CMDTIMEOUT)
+ {
+ if(cmd->cmd == SEND_IF_COND)
+ break; /* SDHC test can fail */
+ panicf("Response timeout");
+ }
+ else if(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND))
+ { /* resp received */
+ cmd->resp[0] = MMC_RESP0(drive);
+ if(cmd->flags & MMC_LONG_RESP)
+ {
+ cmd->resp[1] = MMC_RESP1(drive);
+ cmd->resp[2] = MMC_RESP2(drive);
+ cmd->resp[3] = MMC_RESP3(drive);
+ }
+ break;
+ }
+ }
+ else
+ if(status & MCI_CMDSENT)
+ break;
+
+ } while(1);
+
+ MMC_CLEAR(drive) = 0x7ff;
+ return status;
+}
+
+static void sd_init_card(const int drive)
+{
+ struct mmc_command cmd_app, cmd_op_cond, cmd_idle, cmd_if_cond;
+ int status;
+ bool sdhc;
+
+#ifdef DEBUG
+ reset_screen();
+ printf("now - powered up");
+#endif
+
+ cmd_idle.cmd = GO_IDLE_STATE;
+ cmd_idle.arg = 0;
+ cmd_idle.flags = MMC_NO_FLAGS;
+ if(send_cmd(drive, &cmd_idle) != MCI_CMDSENT)
+ panicf("goto idle failed!");
+#ifdef DEBUG
+ else
+ printf("now - idle");
+#endif
+
+ mci_delay();
+
+ cmd_if_cond.cmd = SEND_IF_COND;
+ cmd_if_cond.arg = (1 /* 2.7-3.6V */ << 8) | 0xAA /* check pattern */;
+ cmd_if_cond.flags = MMC_RESP | MMC_ARG;
+
+ cmd_app.cmd = APP_CMD;
+ cmd_app.flags = MMC_RESP | MMC_ARG;
+ cmd_app.arg = 0; /* 31:16 RCA (0) , 15:0 stuff bits */
+
+ cmd_op_cond.cmd = SEND_OP_COND;
+ cmd_op_cond.flags = MMC_RESP | MMC_ARG;
+
+#ifdef DEBUG
+ printf("now - card powering up");
+#endif
+
+ sdhc = false;
+ status = send_cmd(drive, &cmd_if_cond);
+ if(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND))
+ {
+ if((cmd_if_cond.resp[0] & 0xFFF) == cmd_if_cond.arg)
+ sdhc = true;
+#ifdef DEBUG
+ else
+ printf("Bad resp: %x",cmd_if_cond.arg);
+#endif
+ }
+#ifdef DEBUG
+ else
+ printf("cmd_if_cond stat: 0x%x",status);
+
+ printf("%s Capacity",sdhc?"High":"Normal");
+ mci_delay();
+ mci_delay();
+ mci_delay();
+#endif
+
+#ifdef DEBUG
+ int loop = 0;
+#endif
+ do {
+ mci_delay();
+ mci_delay();
+#ifdef DEBUG
+ reset_screen();
+ printf("Loop number #%d", ++loop);
+#endif
+ /* app_cmd */
+ status = send_cmd(drive, &cmd_app);
+ if( !(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND)) ||
+ !(cmd_app.resp[0] & (1<<5)) )
+ {
+ panicf("app_cmd failed");
+ }
+
+ cmd_op_cond.arg = sdhc ? 0x40FF8000 : (8<<0x14); /* ocr */
+ status = send_cmd(drive, &cmd_op_cond);
+ if(!(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND)))
+ panicf("cmd_op_cond failed");
+
+#ifdef DEBUG
+ printf("OP COND: 0x%.8x", cmd_op_cond.resp[0]);
+#endif
+ } while(!(cmd_op_cond.resp[0] & (1<<31))); /* until card is powered up */
+
+#ifdef DEBUG
+ printf("now - card ready !");
+#endif
+}
+
+static void init_pl180_controller(const int drive)
+{
+ MMC_COMMAND(drive) = MMC_DATACTRL(drive) = 0;
+ MMC_CLEAR(drive) = 0x7ff;
+
+ MMC_MASK0(drive) = MMC_MASK1(drive) = 0; /* disable all interrupts */
+
+ MMC_POWER(drive) = MCI_PWR_UP | (10 /*voltage*/ << 2); /* use OF voltage */
+ mci_delay();
+
+ MMC_POWER(drive) |= MCI_PWR_ON;
+ mci_delay();
+
+ MMC_SELECT(drive) = 0;
+
+ MMC_CLOCK(drive) = MCI_CLK_ENABLE;
+ MMC_CLOCK(drive) &= ~MCI_CLK_PWRSAVE;
+
+ /* set MCLK divider */
+ mci_set_clock_divider(drive, 200);
+}
+
+int ata_init(void)
+{
+ /* reset peripherals */
+
+ CCU_SRC =
+#ifdef HAVE_MULTIVOLUME
+ CCU_SRC_SDMCI_EN |
+#endif
+ CCU_SRC_NAF_EN | CCU_SRC_IDE_EN | CCU_SRC_IDE_AHB_EN | CCU_SRC_MST_EN;
+
+ CCU_SRL = CCU_SRL_MAGIC_NUMBER;
+ CCU_SRL = 0;
+
+ GPIOC_DIR &= ~(1<<1);
+ if(GPIOC_PIN(1))
+ CCU_SPARE1 |= 4; /* sets bit 3 of undocumented register */
+ else
+ CCU_SPARE1 &= ~4; /* or clear it */
+
+ CGU_IDE = (1<<7)|(1<<6); /* enable, 24MHz clock */
+ CGU_MEMSTICK = (1<<8); /* enable, 24MHz clock */
+
+ CGU_PERI |= CGU_NAF_CLOCK_ENABLE;
+#ifdef HAVE_MULTIVOLUME
+ CGU_PERI |= CGU_MCI_CLOCK_ENABLE;
+#endif
+
+ CCU_IO &= ~8; /* bits 3:2 = 01, xpd is SD interface */
+ CCU_IO |= 4;
+
+ init_pl180_controller(NAND_AS3525);
+ sd_init_card(NAND_AS3525);
+
+#ifdef HAVE_MULTIVOLUME
+ init_pl180_controller(SD_AS3525);
+ sd_init_card(SD_AS3525);
+#endif
+
+ return 0;
+}
+
+int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int count, void* buf)
+{
+ (void)start;
+ (void)count;
+ (void)buf;
+ return 0; /* TODO */
+}
+
+int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf)
+{
+ (void)start;
+ (void)count;
+ (void)buf;
+ return 0; /* TODO */
+}
diff --git a/firmware/target/arm/as3525/mmci.h b/firmware/target/arm/as3525/mmci.h
new file mode 100644
index 0000000000..284eee0c75
--- /dev/null
+++ b/firmware/target/arm/as3525/mmci.h
@@ -0,0 +1,123 @@
+/*
+ * linux/drivers/mmc/host/mmci.h - ARM PrimeCell MMCI PL180/1 driver
+ *
+ * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * 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.
+ */
+//#define MMCIPOWER 0x000
+#define MCI_PWR_OFF 0x00
+#define MCI_PWR_UP 0x02
+#define MCI_PWR_ON 0x03
+#define MCI_OD (1 << 6)
+#define MCI_ROD (1 << 7)
+
+//#define MMCICLOCK 0x004
+#define MCI_CLK_ENABLE (1 << 8)
+#define MCI_CLK_PWRSAVE (1 << 9)
+#define MCI_CLK_BYPASS (1 << 10)
+#define MCI_WIDEBUS (1 << 11)
+
+//#define MMCIARGUMENT 0x008
+//#define MMCICOMMAND 0x00c
+#define MCI_CPSM_RESPONSE (1 << 6)
+#define MCI_CPSM_LONGRSP (1 << 7)
+#define MCI_CPSM_INTERRUPT (1 << 8)
+#define MCI_CPSM_PENDING (1 << 9)
+#define MCI_CPSM_ENABLE (1 << 10)
+
+#if 0
+#define MMCIRESPCMD 0x010
+#define MMCIRESPONSE0 0x014
+#define MMCIRESPONSE1 0x018
+#define MMCIRESPONSE2 0x01c
+#define MMCIRESPONSE3 0x020
+#define MMCIDATATIMER 0x024
+#define MMCIDATALENGTH 0x028
+#define MMCIDATACTRL 0x02c
+#endif
+#define MCI_DPSM_ENABLE (1 << 0)
+#define MCI_DPSM_DIRECTION (1 << 1)
+#define MCI_DPSM_MODE (1 << 2)
+#define MCI_DPSM_DMAENABLE (1 << 3)
+
+//#define MMCIDATACNT 0x030
+//#define MMCISTATUS 0x034
+#define MCI_CMDCRCFAIL (1 << 0)
+#define MCI_DATACRCFAIL (1 << 1)
+#define MCI_CMDTIMEOUT (1 << 2)
+#define MCI_DATATIMEOUT (1 << 3)
+#define MCI_TXUNDERRUN (1 << 4)
+#define MCI_RXOVERRUN (1 << 5)
+#define MCI_CMDRESPEND (1 << 6)
+#define MCI_CMDSENT (1 << 7)
+#define MCI_DATAEND (1 << 8)
+#define MCI_DATABLOCKEND (1 << 10)
+#define MCI_CMDACTIVE (1 << 11)
+#define MCI_TXACTIVE (1 << 12)
+#define MCI_RXACTIVE (1 << 13)
+#define MCI_TXFIFOHALFEMPTY (1 << 14)
+#define MCI_RXFIFOHALFFULL (1 << 15)
+#define MCI_TXFIFOFULL (1 << 16)
+#define MCI_RXFIFOFULL (1 << 17)
+#define MCI_TXFIFOEMPTY (1 << 18)
+#define MCI_RXFIFOEMPTY (1 << 19)
+#define MCI_TXDATAAVLBL (1 << 20)
+#define MCI_RXDATAAVLBL (1 << 21)
+
+//#define MMCICLEAR 0x038
+#define MCI_CMDCRCFAILCLR (1 << 0)
+#define MCI_DATACRCFAILCLR (1 << 1)
+#define MCI_CMDTIMEOUTCLR (1 << 2)
+#define MCI_DATATIMEOUTCLR (1 << 3)
+#define MCI_TXUNDERRUNCLR (1 << 4)
+#define MCI_RXOVERRUNCLR (1 << 5)
+#define MCI_CMDRESPENDCLR (1 << 6)
+#define MCI_CMDSENTCLR (1 << 7)
+#define MCI_DATAENDCLR (1 << 8)
+#define MCI_DATABLOCKENDCLR (1 << 10)
+
+//#define MMCIMASK0 0x03c
+#define MCI_CMDCRCFAILMASK (1 << 0)
+#define MCI_DATACRCFAILMASK (1 << 1)
+#define MCI_CMDTIMEOUTMASK (1 << 2)
+#define MCI_DATATIMEOUTMASK (1 << 3)
+#define MCI_TXUNDERRUNMASK (1 << 4)
+#define MCI_RXOVERRUNMASK (1 << 5)
+#define MCI_CMDRESPENDMASK (1 << 6)
+#define MCI_CMDSENTMASK (1 << 7)
+#define MCI_DATAENDMASK (1 << 8)
+#define MCI_DATABLOCKENDMASK (1 << 10)
+#define MCI_CMDACTIVEMASK (1 << 11)
+#define MCI_TXACTIVEMASK (1 << 12)
+#define MCI_RXACTIVEMASK (1 << 13)
+#define MCI_TXFIFOHALFEMPTYMASK (1 << 14)
+#define MCI_RXFIFOHALFFULLMASK (1 << 15)
+#define MCI_TXFIFOFULLMASK (1 << 16)
+#define MCI_RXFIFOFULLMASK (1 << 17)
+#define MCI_TXFIFOEMPTYMASK (1 << 18)
+#define MCI_RXFIFOEMPTYMASK (1 << 19)
+#define MCI_TXDATAAVLBLMASK (1 << 20)
+#define MCI_RXDATAAVLBLMASK (1 << 21)
+
+#if 0
+#define MMCIMASK1 0x040
+#define MMCIFIFOCNT 0x048
+#define MMCIFIFO 0x080 /* to 0x0bc */
+#endif
+
+#define MCI_IRQENABLE \
+ (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \
+ MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
+ MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK)
+
+/*
+ * The size of the FIFO in bytes.
+ */
+#define MCI_FIFOSIZE (16*4)
+
+#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
+
+#define NR_SG 16