diff --git a/bootloader/mini2440.c b/bootloader/mini2440.c index c17f386b5b..dd811600f3 100644 --- a/bootloader/mini2440.c +++ b/bootloader/mini2440.c @@ -5,7 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * + * $Id$ * * Copyright (C) 2009 by Bob Cousins, Lyre Project * @@ -43,10 +43,8 @@ #include "backlight-target.h" #include "lcd-target.h" #include "debug-target.h" -#if 0 #include "dma-target.h" #include "uart-s3c2440.h" -#endif #include "led-mini2440.h" /* Show the Rockbox logo - in show_logo.c */ @@ -55,14 +53,11 @@ extern int show_logo(void); int main(void) { -#if 0 /* required later */ unsigned char* loadbuffer; int buffer_size; int rc; int(*kernel_entry)(void); -#endif - int start, elapsed; led_init(); clear_leds(LED_ALL); @@ -74,20 +69,11 @@ int main(void) lcd_init(); lcd_setfont(FONT_SYSFIXED); button_init(); -#if 0 dma_init(); uart_init_device(UART_DEBUG); -#endif -/* mini2440_test(); */ - -#if 1 - show_logo(); - /* pause here for now */ - led_flash(LED1|LED4, LED2|LED3); -#endif - -#if 0 +/* mini2440_test(); */ + /* TODO */ /* Show debug messages if button is pressed */ @@ -130,15 +116,11 @@ int main(void) if (rc == EOK) { - uart_printf ("entering kernel\n"); cpucache_invalidate(); kernel_entry = (void*) loadbuffer; rc = kernel_entry(); } - uart_printf ("stopping %d\n", rc); -#endif - /* end stop - should not get here */ led_flash(LED_ALL, LED_NONE); while (1); /* avoid warning */ diff --git a/firmware/SOURCES b/firmware/SOURCES index a3c47a6ddb..5406b161ad 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1399,24 +1399,26 @@ target/arm/at91sam/lyre_proto1/timer-lyre_proto1.c target/arm/lcd-as-memframe.S target/arm/mmu-arm.S target/arm/s3c2440/debug-s3c2440.c +target/arm/s3c2440/dma-s3c2440.c target/arm/s3c2440/kernel-s3c2440.c target/arm/s3c2440/lcd-s3c2440.c +target/arm/s3c2440/sd-s3c2440.c target/arm/s3c2440/system-s3c2440.c +target/arm/s3c2440/uart-s3c2440.c target/arm/s3c2440/mini2440/backlight-mini2440.c target/arm/s3c2440/mini2440/button-mini2440.c target/arm/s3c2440/mini2440/led-mini2440.c target/arm/s3c2440/mini2440/power-mini2440.c #ifdef BOOTLOADER -/*target/arm/s3c2440/mini2440/mini2440_test.c*/ #endif #ifndef BOOTLOADER target/arm/s3c2440/mini2440/adc-mini2440.c -target/arm/s3c2440/mini2440/i2c-mini2440.c +//target/arm/s3c2440/mini2440/i2c-mini2440.c target/arm/s3c2440/mini2440/pcm-mini2440.c target/arm/s3c2440/mini2440/powermgmt-mini2440.c target/arm/s3c2440/mini2440/power-mini2440.c target/arm/s3c2440/mini2440/timer-mini2440.c -target/arm/s3c2440/mini2440/usb-mini2440.c +//target/arm/s3c2440/mini2440/usb-mini2440.c #endif #endif /* SIMULATOR */ #endif /* MINI2440 */ diff --git a/firmware/export/s3c2440.h b/firmware/export/s3c2440.h index 5f595214a1..c783b37f29 100644 --- a/firmware/export/s3c2440.h +++ b/firmware/export/s3c2440.h @@ -212,6 +212,41 @@ #define DMASKTRIG_ON (1 << 1) #define DMASKTRIG_SW_TRIG (1 << 0) +/* Get DMA request source (HWSRCSEL) from the map for the specified channel */ +#define DMA_GET_SRC(map,channel) ( ((map) << (channel*8)) & 0xff) + +#define DMA_CH0(x) (x<<0) +#define DMA_CH1(x) (x<<8) +#define DMA_CH2(x) (x<<16) +#define DMA_CH3(x) (x<<24) + +#define DMA_INVALID 0xff +#define DMA_INV0 DMA_CH0(DMA_INVALID) +#define DMA_INV1 DMA_CH1(DMA_INVALID) +#define DMA_INV2 DMA_CH2(DMA_INVALID) +#define DMA_INV3 DMA_CH3(DMA_INVALID) + +/* This map encodes the DMA request source field (HWSRCSEL) since it's value + * depends on channel number and peripheral type. + */ +#define DMA_SRC_MAP_XDREQ0 (DMA_CH0(0) | DMA_INV1 | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_XDREQ1 (DMA_INV0 | DMA_CH1(0) | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_USB_EP1 (DMA_CH0(4) | DMA_INV1 | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_USB_EP2 (DMA_INV0 | DMA_CH1(4) | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_USB_EP3 (DMA_INV0 | DMA_INV1 | DMA_CH2(4) | DMA_INV3) +#define DMA_SRC_MAP_USB_EP4 (DMA_INV0 | DMA_INV1 | DMA_INV2 | DMA_CH3(4)) +#define DMA_SRC_MAP_SDI (DMA_CH0(2) | DMA_CH1(6) | DMA_CH2(2) | DMA_CH3(1)) +#define DMA_SRC_MAP_UART0 (DMA_CH0(1) | DMA_INV1 | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_UART1 (DMA_INV0 | DMA_CH1(1) | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_UART2 (DMA_INV0 | DMA_INV1 | DMA_INV2 | DMA_CH3(0)) +#define DMA_SRC_MAP_I2SSDO (DMA_CH0(5) | DMA_INV1 | DMA_CH2(0) | DMA_INV3) +#define DMA_SRC_MAP_I2SSDI (DMA_INV0 | DMA_CH1(2) | DMA_CH2(1) | DMA_INV3) +#define DMA_SRC_MAP_PCMOUT (DMA_INV0 | DMA_CH1(5) | DMA_INV2 | DMA_CH3(6)) +#define DMA_SRC_MAP_PCMIN (DMA_CH0(6) | DMA_INV1 | DMA_CH2(5) | DMA_INV3) +#define DMA_SRC_MAP_MICIN (DMA_INV0 | DMA_INV1 | DMA_CH2(6) | DMA_CH3(5)) +#define DMA_SRC_MAP_SPI0 (DMA_INV0 | DMA_CH1(3) | DMA_INV2 | DMA_INV3) +#define DMA_SRC_MAP_SPI1 (DMA_INV0 | DMA_INV1 | DMA_INV2 | DMA_CH3(2)) +#define DMA_SRC_MAP_TIMER (DMA_CH0(3) | DMA_INV1 | DMA_CH2(3) | DMA_CH3(3)) /* Clock & Power Management */ @@ -543,6 +578,102 @@ #define SDIDAT_HBE (*(volatile unsigned short *)0x5A000041) /* 16 */ #define SDIDAT_BBE (*(volatile unsigned char *)0x5A000043) /* 8 */ +/* SDI register bit definitions. S3C2440 is a superset of S3C2410 */ + +#define S3C2440_SDICON_SDRESET (1<<8) +#define S3C2440_SDICON_MMCCLOCK (1<<5) +#define S3C2410_SDICON_BYTEORDER (1<<4) +#define S3C2410_SDICON_SDIOIRQ (1<<3) +#define S3C2410_SDICON_RWAITEN (1<<2) +#define S3C2410_SDICON_FIFORESET (1<<1) +#define S3C2410_SDICON_CLOCKTYPE (1<<0) + +#define S3C2410_SDICMDCON_ABORT (1<<12) +#define S3C2410_SDICMDCON_WITHDATA (1<<11) +#define S3C2410_SDICMDCON_LONGRSP (1<<10) +#define S3C2410_SDICMDCON_WAITRSP (1<<9) +#define S3C2410_SDICMDCON_CMDSTART (1<<8) +#define S3C2410_SDICMDCON_SENDERHOST (1<<6) +#define S3C2410_SDICMDCON_INDEX (0x3f) + +#define S3C2410_SDICMDSTAT_CRCFAIL (1<<12) +#define S3C2410_SDICMDSTAT_CMDSENT (1<<11) +#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1<<10) +#define S3C2410_SDICMDSTAT_RSPFIN (1<<9) +#define S3C2410_SDICMDSTAT_XFERING (1<<8) +#define S3C2410_SDICMDSTAT_INDEX (0xff) + +#define S3C2440_SDIDCON_DS_BYTE (0<<22) +#define S3C2440_SDIDCON_DS_HALFWORD (1<<22) +#define S3C2440_SDIDCON_DS_WORD (2<<22) +#define S3C2410_SDIDCON_IRQPERIOD (1<<21) +#define S3C2410_SDIDCON_TXAFTERRESP (1<<20) +#define S3C2410_SDIDCON_RXAFTERCMD (1<<19) +#define S3C2410_SDIDCON_BUSYAFTERCMD (1<<18) +#define S3C2410_SDIDCON_BLOCKMODE (1<<17) +#define S3C2410_SDIDCON_WIDEBUS (1<<16) +#define S3C2410_SDIDCON_DMAEN (1<<15) +#define S3C2410_SDIDCON_STOP (0<<14) +#define S3C2440_SDIDCON_DATSTART (1<<14) +#define S3C2410_SDIDCON_DATMODE (3<<12) +#define S3C2410_SDIDCON_BLKNUM (0xfff) + +/* constants for S3C2410_SDIDCON_DATMODE */ +#define S3C2410_SDIDCON_XFER_READY (0<<12) +#define S3C2410_SDIDCON_XFER_CHKSTART (1<<12) +#define S3C2410_SDIDCON_XFER_RXSTART (2<<12) +#define S3C2410_SDIDCON_XFER_TXSTART (3<<12) + +#define S3C2410_SDIDCNT_BLKNUM_MASK (0xFFF) +#define S3C2410_SDIDCNT_BLKNUM_SHIFT (12) + +#define S3C2410_SDIDSTA_RDYWAITREQ (1<<10) +#define S3C2410_SDIDSTA_SDIOIRQDETECT (1<<9) +#define S3C2410_SDIDSTA_FIFOFAIL (1<<8) /* reserved on 2440 */ +#define S3C2410_SDIDSTA_CRCFAIL (1<<7) +#define S3C2410_SDIDSTA_RXCRCFAIL (1<<6) +#define S3C2410_SDIDSTA_DATATIMEOUT (1<<5) +#define S3C2410_SDIDSTA_XFERFINISH (1<<4) +#define S3C2410_SDIDSTA_BUSYFINISH (1<<3) +#define S3C2410_SDIDSTA_SBITERR (1<<2) /* reserved on 2410a/2440 */ +#define S3C2410_SDIDSTA_TXDATAON (1<<1) +#define S3C2410_SDIDSTA_RXDATAON (1<<0) + +#define S3C2410_SDIDSTA_CLEAR_BITS ( S3C2410_SDIDSTA_BUSYFINISH | \ + S3C2410_SDIDSTA_XFERFINISH | S3C2410_SDIDSTA_DATATIMEOUT | \ + S3C2410_SDIDSTA_RXCRCFAIL | S3C2410_SDIDSTA_CRCFAIL | \ + S3C2410_SDIDSTA_SDIOIRQDETECT | S3C2410_SDIDSTA_RDYWAITREQ ) + +#define S3C2440_SDIFSTA_FIFORESET (1<<16) +#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */ +#define S3C2410_SDIFSTA_TFDET (1<<13) +#define S3C2410_SDIFSTA_RFDET (1<<12) +#define S3C2410_SDIFSTA_TFHALF (1<<11) +#define S3C2410_SDIFSTA_TFEMPTY (1<<10) +#define S3C2410_SDIFSTA_RFLAST (1<<9) +#define S3C2410_SDIFSTA_RFFULL (1<<8) +#define S3C2410_SDIFSTA_RFHALF (1<<7) +#define S3C2410_SDIFSTA_COUNTMASK (0x7f) + +#define S3C2410_SDIIMSK_RESPONSECRC (1<<17) +#define S3C2410_SDIIMSK_CMDSENT (1<<16) +#define S3C2410_SDIIMSK_CMDTIMEOUT (1<<15) +#define S3C2410_SDIIMSK_RESPONSEND (1<<14) +#define S3C2410_SDIIMSK_READWAIT (1<<13) +#define S3C2410_SDIIMSK_SDIOIRQ (1<<12) +#define S3C2410_SDIIMSK_FIFOFAIL (1<<11) +#define S3C2410_SDIIMSK_CRCSTATUS (1<<10) +#define S3C2410_SDIIMSK_DATACRC (1<<9) +#define S3C2410_SDIIMSK_DATATIMEOUT (1<<8) +#define S3C2410_SDIIMSK_DATAFINISH (1<<7) +#define S3C2410_SDIIMSK_BUSYFINISH (1<<6) +#define S3C2410_SDIIMSK_SBITERR (1<<5) /* reserved 2440/2410a */ +#define S3C2410_SDIIMSK_TXFIFOHALF (1<<4) +#define S3C2410_SDIIMSK_TXFIFOEMPTY (1<<3) +#define S3C2410_SDIIMSK_RXFIFOLAST (1<<2) +#define S3C2410_SDIIMSK_RXFIFOFULL (1<<1) +#define S3C2410_SDIIMSK_RXFIFOHALF (1<<0) + /* AC97 Audio-CODEC Interface */ #define AC_GLBCTRL (*(volatile unsigned long *)0x5B000000) /* AC97 global control register */ diff --git a/firmware/export/sd.h b/firmware/export/sd.h index 0b64bd768d..7204b1a0bf 100644 --- a/firmware/export/sd.h +++ b/firmware/export/sd.h @@ -100,4 +100,47 @@ int sd_num_drives(int first_drive); #define SD_SEND_SCR 51 /* acmd51 */ #define SD_APP_CMD 55 +/* + SD/MMC status in R1, for native mode (SPI bits are different) + Type + e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition + a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read + */ + +#define SD_R1_OUT_OF_RANGE (1 << 31) /* erx, c */ +#define SD_R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define SD_R1_BLOCK_LEN_ERROR (1 << 29) /* erx, c */ +#define SD_R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define SD_R1_ERASE_PARAM (1 << 27) /* exx, c */ +#define SD_R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define SD_R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define SD_R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define SD_R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define SD_R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define SD_R1_CARD_ECC_FAILED (1 << 21) /* erx, c */ +#define SD_R1_CC_ERROR (1 << 20) /* erx, c */ +#define SD_R1_ERROR (1 << 19) /* erx, c */ +#define SD_R1_UNDERRUN (1 << 18) /* ex, c */ +#define SD_R1_OVERRUN (1 << 17) /* ex, c */ +#define SD_R1_CSD_OVERWRITE (1 << 16) /* erx, c */ +#define SD_R1_WP_ERASE_SKIP (1 << 15) /* erx, c */ +#define SD_R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define SD_R1_ERASE_RESET (1 << 13) /* sr, c */ +#define SD_R1_STATUS(x) (x & 0xFFFFE000) +#define SD_R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define SD_R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define SD_R1_APP_CMD (1 << 5) /* sr, c */ +#define SD_R1_AKE_SEQ_ERROR (1 << 3) /* er, c */ + +/* SD OCR bits */ +#define SD_OCR_CARD_CAPACITY_STATUS (1 << 30) /* Card Capacity Status */ + #endif diff --git a/firmware/target/arm/s3c2440/dma-s3c2440.c b/firmware/target/arm/s3c2440/dma-s3c2440.c new file mode 100644 index 0000000000..b83897cb22 --- /dev/null +++ b/firmware/target/arm/s3c2440/dma-s3c2440.c @@ -0,0 +1,207 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright 2009 by Bob Cousins + * + * 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. + * + ****************************************************************************/ + +#include +#include "config.h" +#include "panic.h" +#include "system.h" +#include "mmu-arm.h" +#include "s3c2440.h" +#include "dma-target.h" +#include "system-target.h" + +#define NUM_CHANNELS 4 + +static int dma_used = 0; + +/* Status flags */ +#define STATUS_CHANNEL_ACTIVE (1<<0) + +struct dma_channel_state +{ + volatile unsigned status; + void (*callback)(void); +} dma_state [NUM_CHANNELS]; + +struct dma_channel_regs +{ + volatile unsigned long disrc; + volatile unsigned long disrcc; + volatile unsigned long didst; + volatile unsigned long didstc; + volatile unsigned long dcon; + volatile unsigned long dstat; + volatile unsigned long dcsrc; + volatile unsigned long dcdst; + volatile unsigned long dmasktrig; + volatile unsigned long reserved [7]; /* pad to 0x40 bytes */ +}; + + +struct dma_channel_regs *dma_regs [4] = + { + (struct dma_channel_regs *) &DISRC0, + (struct dma_channel_regs *) &DISRC1, + (struct dma_channel_regs *) &DISRC2, + (struct dma_channel_regs *) &DISRC3 + } +; + + +void dma_init(void) +{ + /* TODO */ + + /* Enable interupt on DMA Finish */ + /* Clear pending source */ + SRCPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK; + INTPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK; + + /* Enable interrupt in controller */ + s3c_regclr32(&INTMOD, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK); + s3c_regclr32(&INTMSK, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK); +} + +void dma_retain(void) +{ + /* TODO */ + dma_used++; + if(dma_used > 0) + { + /* Enable DMA controller, clock? */ + } +} + +void dma_release(void) +{ + /* TODO */ + if (dma_used > 0) + dma_used--; + if(dma_used == 0) + { + /* Disable DMA */ + } +} + + +inline void dma_disable_channel(int channel) +{ + struct dma_channel_regs *regs = dma_regs [channel]; + + /* disable the specified channel */ + + /* Reset the channel */ + regs->dmasktrig |= DMASKTRIG_STOP; + + /* Wait for DMA controller to be ready */ + while(regs->dmasktrig & DMASKTRIG_ON) + ; + while(regs->dstat & DSTAT_STAT_BUSY) + ; +} + +void dma_enable_channel(int channel, struct dma_request *request) +{ + struct dma_channel_regs *regs = dma_regs [channel]; + + /* TODO - transfer sizes (assumes word) */ + + if (DMA_GET_SRC(request->source_map, channel) == DMA_INVALID) + panicf ("DMA: invalid channel"); + + /* setup a transfer on specified channel */ + dma_disable_channel (channel); + + if((unsigned long)request->source_addr < UNCACHED_BASE_ADDR) + regs->disrc = (unsigned long)request->source_addr + UNCACHED_BASE_ADDR; + else + regs->disrc = (unsigned long)request->source_addr; + regs->disrcc = request->source_control; + + if((unsigned long)request->dest_addr < UNCACHED_BASE_ADDR) + regs->didst = (unsigned long)request->dest_addr + UNCACHED_BASE_ADDR; + else + regs->didst = (unsigned long)request->dest_addr; + regs->didstc = request->dest_control; + + regs->dcon = request->control | request->count | + DMA_GET_SRC(request->source_map, channel) * DCON_HWSRCSEL; + + dma_state [channel].callback = request->callback; + + /* Activate the channel */ + invalidate_dcache_range((void *)request->dest_addr, request->count * 4); + + dma_state [channel].status |= STATUS_CHANNEL_ACTIVE; + regs->dmasktrig = DMASKTRIG_ON; + + if ((request->control & DCON_HW_SEL) == 0) + { + /* Start DMA */ + regs->dmasktrig |= DMASKTRIG_SW_TRIG; + } + +} + +/* ISRs */ +inline void generic_isr (unsigned channel) +{ + if (dma_state [channel].status | STATUS_CHANNEL_ACTIVE) + { + if (dma_state [channel].callback) + /* call callback for relevant channel */ + dma_state [channel].callback(); + + dma_state [channel].status &= ~STATUS_CHANNEL_ACTIVE; + } +} + +void DMA0(void) +{ + generic_isr (0); + /* Ack the interrupt */ + SRCPND = DMA0_MASK; + INTPND = DMA0_MASK; +} + +void DMA1(void) +{ + generic_isr (1); + /* Ack the interrupt */ + SRCPND = DMA1_MASK; + INTPND = DMA1_MASK; +} + +void DMA2(void) +{ + generic_isr (2); + /* Ack the interrupt */ + SRCPND = DMA2_MASK; + INTPND = DMA2_MASK; +} + +void DMA3(void) +{ + generic_isr (3); + /* Ack the interrupt */ + SRCPND = DMA3_MASK; + INTPND = DMA3_MASK; +} diff --git a/firmware/target/arm/s3c2440/dma-target.h b/firmware/target/arm/s3c2440/dma-target.h new file mode 100644 index 0000000000..1baf7feab5 --- /dev/null +++ b/firmware/target/arm/s3c2440/dma-target.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright © 2009 by Bob Cousins + * + * 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. + * + ****************************************************************************/ + +#ifndef _DMA_TARGET_H +#define _DMA_TARGET_H + +#include +#include + +/* DMA Channel assignments */ +#ifdef GIGABEAT_F +#define DMA_CHAN_ATA 0 +#define DMA_CHAN_AUDIO_OUT 2 +#elif defined(MINI2440) +#define DMA_CHAN_SD 0 +#define DMA_CHAN_AUDIO_OUT 2 +#else +#error Unsupported target +#endif + +struct dma_request +{ + volatile void *source_addr; + volatile void *dest_addr; + unsigned long count; + unsigned long source_control; + unsigned long dest_control; + unsigned long source_map; + unsigned long control; + void (*callback)(void); +}; + +void dma_init(void); +void dma_enable_channel(int channel, struct dma_request *request); + +inline void dma_disable_channel(int channel); + +void dma_retain(void); +void dma_release(void); + +#endif diff --git a/firmware/target/arm/s3c2440/sd-s3c2440.c b/firmware/target/arm/s3c2440/sd-s3c2440.c new file mode 100644 index 0000000000..78c9e9bf23 --- /dev/null +++ b/firmware/target/arm/s3c2440/sd-s3c2440.c @@ -0,0 +1,911 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 by Bob Cousins + * + * 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. + * + ****************************************************************************/ + +#define SD_DEBUG + +#include "sd.h" +#include "system.h" +#include +#include "hotswap.h" +#include "thread.h" +#include "panic.h" + +#ifdef SD_DEBUG +#include "uart-s3c2440.h" +#endif +#include "dma-target.h" +#include "system-target.h" +#include "led-mini2440.h" + +/* The configuration method is not very flexible. */ +#define CARD_NUM_SLOT 0 +#define NUM_CARDS 2 + +#if NUM_CARDS < NUM_DRIVES +#error NUM_CARDS less than NUM_DRIVES +#endif + +#define EC_OK 0 +#define EC_FAILED 1 +#define EC_NOCARD 2 +#define EC_WAIT_STATE_FAILED 3 +#define EC_POWER_UP 4 +#define EC_FIFO_WR_EMPTY 5 +#define EC_FIFO_WR_DONE 6 +#define EC_TRAN_READ_ENTRY 7 +#define EC_TRAN_READ_EXIT 8 +#define EC_TRAN_WRITE_ENTRY 9 +#define EC_TRAN_WRITE_EXIT 10 +#define EC_COMMAND 11 +#define EC_WRITE_PROTECT 12 + +#define MIN_YIELD_PERIOD 1000 +#define UNALIGNED_NUM_SECTORS 10 +#define MAX_TRANSFER_ERRORS 10 + +/* command flags for send_cmd */ +#define MCI_NO_FLAGS (0<<0) +#define MCI_RESP (1<<0) +#define MCI_LONG_RESP (1<<1) +#define MCI_ARG (1<<2) + +#define INITIAL_CLK 400000 /* Initial clock */ +#define SD_CLK 24000000 /* Clock for SD cards */ +#define MMC_CLK 15000000 /* Clock for MMC cards */ + +#define SD_ACTIVE_LED LED4 + +#ifdef SD_DEBUG +#define dbgprintf uart_printf +#else +#define dbgprintf(...) +#endif + +struct sd_card_status +{ + int retry; + int retry_max; +}; + +/** static, private data **/ + +/* for compatibility */ +static long last_disk_activity = -1; + +static bool initialized = false; +static bool sd_enabled = false; +static long next_yield = 0; + +static tCardInfo card_info [NUM_CARDS]; + +#ifdef HAVE_MULTIDRIVE +static int curr_card = 0; /* current active card */ +#if 0 +static struct sd_card_status sd_status[NUM_CARDS] = +{ +#if NUM_CARDS > 1 + {0, 10}, +#endif + {0, 10} +}; +#endif +#endif + +/* Shoot for around 75% usage */ +static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)]; +static const char sd_thread_name[] = "sd"; +static struct mutex sd_mtx SHAREDBSS_ATTR; +static struct event_queue sd_queue; +static struct wakeup transfer_completion_signal; +static volatile unsigned int transfer_error[NUM_VOLUMES]; +/* align on cache line size */ +static unsigned char aligned_buffer[UNALIGNED_NUM_SECTORS * SD_BLOCK_SIZE] + __attribute__((aligned(32))); +static unsigned char * uncached_buffer; + + +/* TODO: should be in target include file */ +/***************************************************************************** + Definitions specific to Mini2440 + *****************************************************************************/ +#define FCLK 405000000 +#define HCLK (FCLK/4) /* = 101,250,000 */ +#define PCLK (HCLK/2) /* = 50,625,000 */ + +#define SD_CD (1<<8) /* Port G */ +#define SD_WP (1<<8) /* Port H */ + +/***************************************************************************** + Functions specific to S3C2440 SoC + *****************************************************************************/ +static inline void mci_delay(void) +{ + /* Does this get optimised out ? */ + int i = 0xffff; + while(i--) ; +} + +#ifdef SD_DEBUG +static unsigned reg_copy[16], reg_copy2[16]; +static void get_regs (unsigned *regs) +{ + unsigned j; + volatile unsigned long *sdi_reg = &SDICON; + + for (j=0; j < 16;j++) + { + *regs++ = *sdi_reg++; + } +} + +static void dump_regs (unsigned *regs1, unsigned *regs2) +{ + unsigned j; + volatile unsigned long*sdi_reg = &SDICON; + unsigned long diff; + + for (j=0; j < 16;j++) + { + diff = *regs1 ^ *regs2; + if (diff) + uart_printf ("%8x %8x %8x %8x\n", sdi_reg, *regs1, *regs2, diff ); + regs1++; + regs2++; + sdi_reg++; + } +} +#endif + +static void debug_r1(int cmd) +{ +#if defined(SD_DEBUG) + uart_printf("CMD%2.2d:SDICSTA=%04x [%c%c%c%c%c-%c%c%c%c%c%c%c] SDIRSP0=%08x [%d %s] \n", + cmd, + SDICSTA, + (SDICSTA & S3C2410_SDICMDSTAT_CRCFAIL) ? 'C' : ' ', + (SDICSTA & S3C2410_SDICMDSTAT_CMDSENT) ? 'S' : ' ', + (SDICSTA & S3C2410_SDICMDSTAT_CMDTIMEOUT) ? 'T' : ' ', + (SDICSTA & S3C2410_SDICMDSTAT_RSPFIN) ? 'R' : ' ', + (SDICSTA & S3C2410_SDICMDSTAT_XFERING) ? 'X' : ' ', + + (SDICSTA & 0x40) ? 'P' : ' ', + (SDICSTA & 0x20) ? 'A' : ' ', + (SDICSTA & 0x10) ? 'E' : ' ', + (SDICSTA & 0x08) ? 'C' : ' ', + (SDICSTA & 0x04) ? 'I' : ' ', + (SDICSTA & 0x02) ? 'R' : ' ', + (SDICSTA & 0x01) ? 'Z' : ' ', + + SDIRSP0, + SD_R1_CURRENT_STATE(SDIRSP0), + (SDIRSP0 & SD_R1_READY_FOR_DATA) ? "RDY " : " " + ); +#else + (void)cmd; +#endif +} + +void SDI (void) +{ + int status = SDIDSTA; + + transfer_error[curr_card] = status +#if 0 + & ( S3C2410_SDIDSTA_CRCFAIL | S3C2410_SDIDSTA_RXCRCFAIL | + S3C2410_SDIDSTA_DATATIMEOUT ) +#endif + ; + + SDIDSTA |= S3C2410_SDIDSTA_CLEAR_BITS; /* needed to clear int */ + + dbgprintf ("SDI %x\n", transfer_error[curr_card]); + + wakeup_signal(&transfer_completion_signal); + + /* Ack the interrupt */ + SRCPND = SDI_MASK; + INTPND = SDI_MASK; +} + +#if 0 +void dma_callback (void) +{ + const int status = SDIDSTA; + + transfer_error[0] = status & (S3C2410_SDIDSTA_CRCFAIL | + S3C2410_SDIDSTA_RXCRCFAIL | + S3C2410_SDIDSTA_DATATIMEOUT ); + + SDIDSTA |= S3C2410_SDIDSTA_CLEAR_BITS; /* needed to clear int */ + + dbgprintf ("dma_cb\n"); + wakeup_signal(&transfer_completion_signal); +} +#endif + +static void init_sdi_controller(const int card_no) +{ + (void)card_no; + +/*****************************************************************************/ +#ifdef MINI2440 + /* Specific to Mini2440 */ + + /* Enable pullups on SDCMD and SDDAT pins */ + S3C2440_GPIO_PULLUP (GPEUP, 6, GPIO_PULLUP_ENABLE); + S3C2440_GPIO_PULLUP (GPEUP, 7, GPIO_PULLUP_ENABLE); + S3C2440_GPIO_PULLUP (GPEUP, 8, GPIO_PULLUP_ENABLE); + S3C2440_GPIO_PULLUP (GPEUP, 9, GPIO_PULLUP_ENABLE); + S3C2440_GPIO_PULLUP (GPEUP, 10, GPIO_PULLUP_ENABLE); + + /* Enable special function for SDCMD, SDCLK and SDDAT pins */ + S3C2440_GPIO_CONFIG (GPECON, 5, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPECON, 6, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPECON, 7, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPECON, 8, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPECON, 9, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPECON, 10, GPIO_FUNCTION); + + /* Card Detect input */ + S3C2440_GPIO_CONFIG (GPGCON, 8, GPIO_INPUT); + + /* Write Protect input */ + S3C2440_GPIO_CONFIG (GPHCON, 8, GPIO_INPUT); +/*****************************************************************************/ +#else +#error Unsupported target +#endif +/*****************************************************************************/ + + /* About 400KHz for initial comms with card */ + SDIPRE = PCLK / INITIAL_CLK - 1; + /* Byte order=Type A (Little Endian), clock enable */ + SDICON = S3C2410_SDICON_CLOCKTYPE; + SDIFSTA |= S3C2440_SDIFSTA_FIFORESET; + SDIBSIZE = SD_BLOCK_SIZE; + SDIDTIMER= 0x7fffff; /* Set timeout count - max value */ + + /* Enable interupt on Data Finish or data transfer error */ + /* Clear pending source */ + SRCPND = SDI_MASK; + INTPND = SDI_MASK; + +#if 1 + /* Enable interrupt in controller */ + s3c_regclr32(&INTMOD, SDI_MASK); + s3c_regclr32(&INTMSK, SDI_MASK); + + SDIIMSK |= S3C2410_SDIIMSK_DATAFINISH + | S3C2410_SDIIMSK_DATATIMEOUT + | S3C2410_SDIIMSK_DATACRC + | S3C2410_SDIIMSK_CRCSTATUS + | S3C2410_SDIIMSK_FIFOFAIL + ; +#endif +} + +static bool send_cmd(const int card_no, const int cmd, const int arg, + const int flags, long *response) +{ + bool ret; + unsigned val, status; + (void)card_no; + +#ifdef SD_DEBUG + get_regs (reg_copy); +#endif + /* A major bodge. For some reason a delay is required here */ + mci_delay(); + dbgprintf ("send_cmd: c=%3.3d a=%08x f=%02x \n", cmd, arg, flags); + +#ifdef SD_DEBUG + get_regs (reg_copy2); + dump_regs (reg_copy, reg_copy2); +#endif + +#if 0 + while (SDICSTA & S3C2410_SDICMDSTAT_XFERING) + ; /* wait ?? */ +#endif + /* set up new command */ + + if (flags & MCI_ARG) + SDICARG = arg; + else + SDICARG = 0; + + val = cmd | S3C2410_SDICMDCON_CMDSTART | S3C2410_SDICMDCON_SENDERHOST; + if(flags & MCI_RESP) + { + val |= S3C2410_SDICMDCON_WAITRSP; + if(flags & MCI_LONG_RESP) + val |= S3C2410_SDICMDCON_LONGRSP; + } + + /* Clear command/data status flags */ + SDICSTA |= 0x0f << 9; + SDIDSTA |= S3C2410_SDIDSTA_CLEAR_BITS; + + /* Initiate the command */ + SDICCON = val; + + if (flags & MCI_RESP) + { + /* wait for response or timeout */ + do + { + status = SDICSTA; + } while ( (status & (S3C2410_SDICMDSTAT_RSPFIN | + S3C2410_SDICMDSTAT_CMDTIMEOUT) ) == 0); + debug_r1(cmd); + if (status & S3C2410_SDICMDSTAT_CMDTIMEOUT) + ret = false; + else if (status & (S3C2410_SDICMDSTAT_RSPFIN)) + { + /* resp received */ + if(flags & MCI_LONG_RESP) + { + /* store the response in reverse word order */ + response[0] = SDIRSP3; + response[1] = SDIRSP2; + response[2] = SDIRSP1; + response[3] = SDIRSP0; + } + else + response[0] = SDIRSP0; + ret = true; + } + else + ret = true; + } + else + { + /* wait for command completion or timeout */ + do + { + status = SDICSTA; + } while ( (status & (S3C2410_SDICMDSTAT_CMDSENT | + S3C2410_SDICMDSTAT_CMDTIMEOUT)) == 0); + debug_r1(cmd); + if (status & S3C2410_SDICMDSTAT_CMDTIMEOUT) + ret = false; + else + ret = true; + } + + /* Clear Command status flags */ + SDICSTA |= 0x0f << 9; + + mci_delay(); + + return ret; +} + +static int sd_init_card(const int card_no) +{ + unsigned long temp_reg[4]; + unsigned long response; + long init_timeout; + bool sdhc; + int i; + + if(!send_cmd(card_no, SD_GO_IDLE_STATE, 0, MCI_NO_FLAGS, NULL)) + return -1; + + mci_delay(); + + sdhc = false; + if(send_cmd(card_no, SD_SEND_IF_COND, 0x1AA, MCI_RESP|MCI_ARG, &response)) + if((response & 0xFFF) == 0x1AA) + sdhc = true; + + /* timeout for initialization is 1sec, from SD Specification 2.00 */ + init_timeout = current_tick + HZ; + + do { + /* timeout */ + if(current_tick > init_timeout) + return -2; + + /* app_cmd */ + if( !send_cmd(card_no, SD_APP_CMD, 0, MCI_RESP|MCI_ARG, &response) || + !(response & (1<<5)) ) + { + return -3; + } + + /* acmd41 */ + if(!send_cmd(card_no, SD_APP_OP_COND, (sdhc ? 0x40FF8000 : (1<<23)), + MCI_RESP|MCI_ARG, &card_info[card_no].ocr)) + { + return -4; + } + + } while(!(card_info[card_no].ocr & (1<<31))); + + /* send CID */ + if(!send_cmd(card_no, SD_ALL_SEND_CID, 0, MCI_RESP|MCI_LONG_RESP|MCI_ARG, + temp_reg)) + return -5; + + for(i=0; i<4; i++) + card_info[card_no].cid[3-i] = temp_reg[i]; + + /* send RCA */ + if(!send_cmd(card_no, SD_SEND_RELATIVE_ADDR, 0, MCI_RESP|MCI_ARG, + &card_info[card_no].rca)) + return -6; + + /* send CSD */ + if(!send_cmd(card_no, SD_SEND_CSD, card_info[card_no].rca, + MCI_RESP|MCI_LONG_RESP|MCI_ARG, temp_reg)) + return -7; + + for(i=0; i<4; i++) + card_info[card_no].csd[3-i] = temp_reg[i]; + + sd_parse_csd(&card_info[card_no]); + + if(!send_cmd(card_no, SD_SELECT_CARD, card_info[card_no].rca, MCI_ARG, NULL)) + return -9; + + if(!send_cmd(card_no, SD_APP_CMD, card_info[card_no].rca, MCI_ARG, NULL)) + return -10; + + if(!send_cmd(card_no, SD_SET_BUS_WIDTH, card_info[card_no].rca | 2, MCI_ARG, NULL)) + return -11; + + if(!send_cmd(card_no, SD_SET_BLOCKLEN, card_info[card_no].blocksize, MCI_ARG, + NULL)) + return -12; + + card_info[card_no].initialized = 1; + + /* full speed for controller clock */ + SDIPRE = PCLK / SD_CLK - 1; + mci_delay(); + + return EC_OK; +} + +/***************************************************************************** + Generic functions + *****************************************************************************/ + +/*****************************************************************************/ +#ifdef HAVE_HOTSWAP + +bool card_detect_target(void) +{ + /* TODO - use interrupt on change? */ +#ifdef MINI2440 + return (GPGDAT & SD_CD) == 0; +#else +#error Unsupported target +#endif +} + +void card_enable_monitoring_target(bool on) +{ + (void)on; + /* TODO */ +} + +#if 0 +void EXT0(void) +{ + static struct timeout sd1_oneshot; + + /* TODO */ +} +#endif + +bool sd_removable(IF_MD_NONVOID(int card_no)) +{ +#ifndef HAVE_MULTIDRIVE + const int card_no = 0; +#endif + dbgprintf ("sd_remov (hs) [%d] %d\n", card_no, card_no == CARD_NUM_SLOT ); + return (card_no == CARD_NUM_SLOT); +} + +bool sd_present(IF_MD_NONVOID(int card_no)) +{ +#ifdef HAVE_MULTIDRIVE + (void)card_no; +#endif + dbgprintf ("sd_pres (hs) [%d] %d\n", card_no, card_detect_target()); + return card_detect_target(); +} + +/*****************************************************************************/ +#else + +bool card_detect_target(void) +{ + /* not applicable */ + return false; +} + +bool sd_removable(IF_MD_NONVOID(int card_no)) +{ +#ifndef HAVE_MULTIDRIVE + const int card_no = 0; +#endif + (void)card_no; + + /* not applicable */ + dbgprintf ("sd_remov"); + return false; +} + +#endif /* HAVE_HOTSWAP */ +/*****************************************************************************/ + +static void sd_thread(void) __attribute__((noreturn)); +static void sd_thread(void) +{ + struct queue_event ev; + + /* TODO */ + while (1) + { + queue_wait_w_tmo(&sd_queue, &ev, HZ); + + switch ( ev.id ) + { + } + } +} + +static int sd_wait_for_state(const int card_no, unsigned int state) +{ + unsigned long response = 0; + unsigned int timeout = HZ; /* ticks */ + long t = current_tick; + + while (1) + { + long tick; + + if(!send_cmd(card_no, SD_SEND_STATUS, card_info[card_no].rca, + MCI_RESP|MCI_ARG, &response)) + return -1; + + if( (SD_R1_CURRENT_STATE(response) == state) ) + return 0; + + if(TIME_AFTER(current_tick, t + timeout)) + return -2; + + if (TIME_AFTER((tick = current_tick), next_yield)) + { + yield(); + timeout += current_tick - tick; + next_yield = tick + MIN_YIELD_PERIOD; + } + } +} + +static int sd_transfer_sectors(IF_MD2(int card_no,) unsigned long start, + int count, void* buf, const bool write) +{ +#ifndef HAVE_MULTIDRIVE + const int card_no = 0; +#endif + int ret = EC_OK; + unsigned loops = 0; + struct dma_request request; + + mutex_lock(&sd_mtx); + sd_enable(true); + set_leds(SD_ACTIVE_LED); + + curr_card = card_no; + if (card_info[card_no].initialized <= 0) + { + ret = sd_init_card(card_no); + if (!(card_info[card_no].initialized)) + goto sd_transfer_error; + } + + last_disk_activity = current_tick; + + ret = sd_wait_for_state(card_no, SD_TRAN); + if (ret < 0) + { + ret -= 20; + goto sd_transfer_error; + } + + dma_retain(); + + while(count) + { + /* 128 * 512 = 2^16, and doesn't fit in the 16 bits of DATA_LENGTH + * register, so we have to transfer maximum 127 sectors at a time. */ + unsigned int transfer = (count >= 128) ? 127 : count; /* sectors */ + void *dma_buf; + const int cmd = + write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK; + unsigned long start_addr = start; + + dma_buf = aligned_buffer; + if(transfer > UNALIGNED_NUM_SECTORS) + transfer = UNALIGNED_NUM_SECTORS; + if(write) + memcpy(uncached_buffer, buf, transfer * SD_BLOCK_SIZE); + + /* Set start_addr to the correct unit (blocks or bytes) */ + if(!(card_info[card_no].ocr & SD_OCR_CARD_CAPACITY_STATUS))/* not SDHC */ + start_addr *= SD_BLOCK_SIZE; + + /* TODO? */ + SDIFSTA = SDIFSTA | S3C2440_SDIFSTA_FIFORESET; + SDIDCON = S3C2440_SDIDCON_DS_WORD | + S3C2410_SDIDCON_BLOCKMODE | S3C2410_SDIDCON_WIDEBUS | + S3C2410_SDIDCON_DMAEN | + S3C2440_SDIDCON_DATSTART | + ( transfer << 0); + if (write) + SDIDCON |= S3C2410_SDIDCON_TXAFTERRESP | S3C2410_SDIDCON_XFER_TXSTART; + else + SDIDCON |= S3C2410_SDIDCON_RXAFTERCMD | S3C2410_SDIDCON_XFER_RXSTART; + + SDIDSTA |= S3C2410_SDIDSTA_CLEAR_BITS; /* needed to clear int */ + SRCPND = SDI_MASK; + INTPND = SDI_MASK; + + /* Initiate read/write command */ + if(!send_cmd(card_no, cmd, start_addr, MCI_ARG | MCI_RESP, NULL)) + { + ret -= 3*20; + goto sd_transfer_error; + } + + if(write) + { + request.source_addr = dma_buf; + request.source_control = DISRCC_LOC_AHB | DISRCC_INC_AUTO; + request.dest_addr = &SDIDAT_LLE; + request.dest_control = DISRCC_LOC_APB | DISRCC_INC_FIXED; + request.count = transfer * SD_BLOCK_SIZE / sizeof(long); + request.source_map = DMA_SRC_MAP_SDI; + request.control = DCON_DMD_HS | DCON_SYNC_APB | + DCON_HW_SEL | + DCON_NO_RELOAD | DCON_DSZ_WORD; + request.callback = NULL; + + dma_enable_channel(0, &request); + } + else + { + request.source_addr = &SDIDAT_LLE; + request.source_control = DISRCC_LOC_APB | DISRCC_INC_FIXED; + request.dest_addr = dma_buf; + request.dest_control = DISRCC_LOC_AHB | DISRCC_INC_AUTO; + request.count = transfer * SD_BLOCK_SIZE / sizeof(long); + request.source_map = DMA_SRC_MAP_SDI; + request.control = DCON_DMD_HS | DCON_SYNC_APB | + DCON_HW_SEL | + DCON_NO_RELOAD | DCON_DSZ_WORD; + request.callback = NULL; + + dma_enable_channel(0, &request); + } + +#if 0 + /* FIXME : we should check if the timeouts calculated from the card's + * CSD are lower, and use them if it is the case + * Note : the OF doesn't seem to use them anyway */ + MCI_DATA_TIMER(drive) = write ? + SD_MAX_WRITE_TIMEOUT : SD_MAX_READ_TIMEOUT; + MCI_DATA_LENGTH(drive) = transfer * card_info[drive].blocksize; + MCI_DATA_CTRL(drive) = (1<<0) /* enable */ | + (!write<<1) /* transfer direction */ | + (1<<3) /* DMA */ | + (9<<4) /* 2^9 = 512 */ ; +#endif + + wakeup_wait(&transfer_completion_signal, 100 /*TIMEOUT_BLOCK*/); + + /* wait for DMA to finish */ + while (DSTAT0 & DSTAT_STAT_BUSY) + ; + +#if 0 + status = SDIDSTA; + while ((status & (S3C2410_SDIDSTA_DATATIMEOUT|S3C2410_SDIDSTA_XFERFINISH)) == 0) + { + status = SDIDSTA; + } + uart_printf("%x \n", status); +#endif + if( transfer_error[card_no] & S3C2410_SDIDSTA_XFERFINISH ) + { + if(!write) + memcpy(buf, uncached_buffer, transfer * SD_BLOCK_SIZE); + buf += transfer * SD_BLOCK_SIZE; + start += transfer; + count -= transfer; + loops = 0; /* reset errors counter */ + } + else + { + dbgprintf ("SD transfer error : 0x%x\n", transfer_error[card_no]); + + if(loops++ > MAX_TRANSFER_ERRORS) + { + led_flash(LED1|LED2, LED3|LED4); + /* panicf("SD transfer error : 0x%x", transfer_error[card_no]); */ + } + } + + last_disk_activity = current_tick; + + if(!send_cmd(card_no, SD_STOP_TRANSMISSION, 0, MCI_RESP, NULL)) + { + ret = -4*20; + goto sd_transfer_error; + } + +#if 0 + ret = sd_wait_for_state(card_no, SD_TRAN); + if (ret < 0) + { + ret -= 5*20; + goto sd_transfer_error; + } +#endif + } + + ret = EC_OK; + +sd_transfer_error: + + dma_release(); + + clear_leds(SD_ACTIVE_LED); + sd_enable(false); + + if (ret) /* error */ + card_info[card_no].initialized = 0; + + mutex_unlock(&sd_mtx); + return ret; +} + +int sd_read_sectors(IF_MD2(int card_no,) unsigned long start, int incount, + void* inbuf) +{ + int ret; + +#ifdef HAVE_MULTIDRIVE + dbgprintf ("sd_read %d %x %d\n", card_no, start, incount); +#else + dbgprintf ("sd_read %x %d\n", start, incount); +#endif + ret = sd_transfer_sectors(IF_MD2(card_no,) start, incount, inbuf, false); + dbgprintf ("sd_read, ret=%d\n", ret); + return ret; +} + +/*****************************************************************************/ +#ifndef X_BOOTLOADER +/* writing not required for Bootloader - or is it? */ +int sd_write_sectors(IF_MD2(int card_no,) unsigned long start, int count, + const void* outbuf) +{ + dbgprintf ("sd_write %d %x %d\n", card_no, start, count); + + return sd_transfer_sectors(IF_MD2(card_no,) start, count, outbuf, true); +} +#endif /* BOOTLOADER */ +/*****************************************************************************/ + +void sd_enable(bool on) +{ + dbgprintf ("sd_enable %d\n", on); + /* TODO: enable/disable SDI clock */ + + if (sd_enabled == on) + return; /* nothing to do */ + if (on) + { + sd_enabled = true; + } + else + { + sd_enabled = false; + } +} + +int sd_init(void) +{ + int ret = EC_OK; + dbgprintf ("\n==============================\n"); + dbgprintf (" sd_init\n"); + dbgprintf ("==============================\n"); + + init_sdi_controller (0); +#ifndef BOOTLOADER + sd_enabled = true; + sd_enable(false); +#endif + wakeup_init(&transfer_completion_signal); + /* init mutex */ + mutex_init(&sd_mtx); + queue_init(&sd_queue, true); + create_thread(sd_thread, sd_stack, sizeof(sd_stack), 0, + sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU)); + + uncached_buffer = UNCACHED_ADDR(&aligned_buffer[0]); + + initialized = true; + return ret; +} + +long sd_last_disk_activity(void) +{ + return last_disk_activity; +} + +tCardInfo *card_get_info_target(int card_no) +{ + return &card_info[card_no]; +} + +/*****************************************************************************/ +#ifdef CONFIG_STORAGE_MULTI + +int sd_num_drives(int first_drive) +{ + dbgprintf ("sd_num_drv"); +#if 0 + /* Store which logical drive number(s) we have been assigned */ + sd_first_drive = first_drive; +#endif + + return NUM_CARDS; +} + +void sd_sleepnow(void) +{ +} + +bool sd_disk_is_active(void) +{ + return false; +} + +int sd_soft_reset(void) +{ + return 0; +} + +int sd_spinup_time(void) +{ + return 0; +} + +#endif /* CONFIG_STORAGE_MULTI */ +/*****************************************************************************/ + diff --git a/firmware/target/arm/s3c2440/uart-s3c2440.c b/firmware/target/arm/s3c2440/uart-s3c2440.c new file mode 100644 index 0000000000..2a61b61a39 --- /dev/null +++ b/firmware/target/arm/s3c2440/uart-s3c2440.c @@ -0,0 +1,248 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 by Bob Cousins + * + * 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. + * + ****************************************************************************/ + +/* Include Standard files */ +#include +#include +#include +#include "inttypes.h" +#include "string.h" +#include "cpu.h" +#include "system.h" +#include "kernel.h" +#include "thread.h" + +#include "uart-s3c2440.h" + +#define FCLK 405000000 +#define HCLK (FCLK/4) /* = 101,250,000 */ +#define PCLK (HCLK/2) /* = 50,625,000 */ + +#define MAX_TX_BUF 1024 + + +/**************************************************************************** + * General purpose debug function + ****************************************************************************/ + +void uart_printf (const char *format, ...) +{ + static bool debug_uart_init = false; + static char tx_buf [MAX_TX_BUF]; + + int len; + unsigned char *ptr; + va_list ap; + va_start(ap, format); + + ptr = tx_buf; + len = vsnprintf(ptr, sizeof(tx_buf), format, ap); + va_end(ap); + + if (!debug_uart_init) + { + uart_init_device(UART_DEBUG); + debug_uart_init = true; + } + + uart_send (UART_DEBUG, tx_buf, len); +} + +/**************************************************************************** + * Device level functions specific to S3C2440 + *****************************************************************************/ + +bool uart_init (void) +{ + /* anything ? */ + return true; +} + +bool uart_init_device (unsigned dev) +{ + /* set GPIOs, clock enable? etc */ + + switch (dev) + { + case 0: + { + S3C2440_GPIO_CONFIG (GPHCON, 2, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPHCON, 3, GPIO_FUNCTION); + S3C2440_GPIO_PULLUP (GPHUP, 2, GPIO_PULLUP_DISABLE); + S3C2440_GPIO_PULLUP (GPHUP, 3, GPIO_PULLUP_DISABLE); + break; + } + case 1: + { + S3C2440_GPIO_CONFIG (GPHCON, 4, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPHCON, 5, GPIO_FUNCTION); + S3C2440_GPIO_PULLUP (GPHUP, 4, GPIO_PULLUP_DISABLE); + S3C2440_GPIO_PULLUP (GPHUP, 5, GPIO_PULLUP_DISABLE); + break; + } + case 2: + { + S3C2440_GPIO_CONFIG (GPHCON, 6, GPIO_FUNCTION); + S3C2440_GPIO_CONFIG (GPHCON, 7, GPIO_FUNCTION); + S3C2440_GPIO_PULLUP (GPHUP, 6, GPIO_PULLUP_DISABLE); + S3C2440_GPIO_PULLUP (GPHUP, 7, GPIO_PULLUP_DISABLE); + break; + } + default: + return false; + } + + /* set a default configuration */ + uart_config (dev, 115200, 8, UART_NO_PARITY, UART_1_STOP_BIT); + return true; +} + +bool uart_config (unsigned dev, unsigned speed, unsigned num_bits, + unsigned parity, unsigned stop_bits) +{ + switch (dev) + { + case 0: + ULCON0 = (parity << 3) + (stop_bits << 2) + (num_bits-5); + UCON0 = (1 << 2) + (1 << 0); /* enable TX, RX, use PCLK */ + UBRDIV0 = PCLK / (speed*16); + break; + + case 1: + ULCON1 = (parity << 3) + (stop_bits << 2) + (num_bits-5); + UCON1 = (1 << 2) + (1 << 0); /* enable TX, RX, use PCLK */ + UBRDIV1 = PCLK / (speed*16); + break; + + case 2: + ULCON2 = (parity << 3) + (stop_bits << 2) + (num_bits-5); + UCON2 = (1 << 2) + (1 << 0); /* enable TX, RX, use PCLK */ + UBRDIV2 = PCLK / (speed*16); + break; + } + + return true; +} + +bool uart_send_byte (unsigned dev, char ch) +{ + switch (dev) + { + case 0: + /* wait for transmit buffer empty */ + while ((UTRSTAT0 & 0x02) == 0) + ; + UTXH0 = ch; + break; + + case 1: + /* wait for transmit buffer empty */ + while ((UTRSTAT1 & 0x02) == 0) + ; + UTXH1 = ch; + break; + + case 2: + /* wait for transmit buffer empty */ + while ((UTRSTAT2 & 0x02) == 0) + ; + UTXH2 = ch; + break; + } + + return true; +} + +char uart_rx_ready (unsigned dev) +{ + switch (dev) + { + case 0: + /* wait for receive buffer data ready */ + if (UTRSTAT0 & 0x01) + return true; + else + return false; + break; + case 1: + /* wait for receive buffer data ready */ + if (UTRSTAT1 & 0x01) + return true; + else + return false; + break; + case 2: + /* wait for receive buffer data ready */ + if (UTRSTAT2 & 0x01) + return true; + else + return false; + break; + } + return false; +} + +char uart_read_byte (unsigned dev) +{ + switch (dev) + { + case 0: + while (!uart_rx_ready(dev)) + ; + return URXH0; + break; + case 1: + while (!uart_rx_ready(dev)) + ; + return URXH1; + break; + case 2: + while (!uart_rx_ready(dev)) + ; + return URXH2; + break; + } + + return true; +} + +/**************************************************************************** + * General + *****************************************************************************/ + +bool uart_send (unsigned dev, char *buf, unsigned len) +{ + unsigned index=0; + while (index ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * + * Copyright (C) 2009 by Bob Cousins + * + * 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. + * + ****************************************************************************/ + +#ifndef __UART_S3C2440_H__ +#define __UART_S3C2440_H__ + +#define UART_DEBUG 0 + +#define UART_NO_PARITY 0 +#define UART_ODD_PARITY 4 +#define UART_EVEN_PARITY 5 +#define UART_MARK_PARITY 6 +#define UART_SPACE_PARITY 7 + +#define UART_1_STOP_BIT 0 +#define UART_2_STOP_BIT 1 + +bool uart_init (void); +void uart_printf (const char *format, ...); + +/* low level routines */ +bool uart_init_device (unsigned dev); +bool uart_config (unsigned dev, unsigned speed, unsigned num_bits, unsigned parity, unsigned stop_bits); +bool uart_send (unsigned dev, char *buf, unsigned len); + +char uart_read_byte (unsigned dev); +char uart_rx_ready (unsigned dev); + + +#endif