From e1b483848120507a909e95417e938324ed377bd5 Mon Sep 17 00:00:00 2001 From: Rafaël Carré Date: Wed, 29 Oct 2008 20:21:59 +0000 Subject: 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 --- firmware/target/arm/as3525/ata_sd_as3525.c | 341 +++++++++++++++++++++++++++++ firmware/target/arm/as3525/mmci.h | 123 +++++++++++ 2 files changed, 464 insertions(+) create mode 100644 firmware/target/arm/as3525/ata_sd_as3525.c create mode 100644 firmware/target/arm/as3525/mmci.h (limited to 'firmware/target') 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 -- cgit v1.2.3