dslinux/linux-2.6.x/drivers/mmc m3sd_c.c m3sd_s.S
amadeus
dslinux_amadeus at user.in-berlin.de
Sun Sep 17 00:35:25 CEST 2006
Update of /cvsroot/dslinux/dslinux/linux-2.6.x/drivers/mmc
In directory antilope:/tmp/cvs-serv20954/linux-2.6.x/drivers/mmc
Added Files:
m3sd_c.c m3sd_s.S
Log Message:
new M3SD driver
--- NEW FILE: m3sd_s.S ---
/*
* linux/drivers/mmc/m3sd_s.S - M3 SD driver
*
* Copyright (C) 2006 Amadeus, 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.
*
* This driver must be in main memory.
*/
/* common macros for all NDS GBA ROM device drivers */
#include <asm/arch/gbarom-macro.S>
.TEXT
/*****************************************************************************/
/* IO registers */
#define REG_M3SD_DIR 0x08800000 @ direction control register
#define REG_M3SD_DAT 0x09000000 @ SD data line, 8 bits at a time
#define REG_M3SD_CMD 0x09200000 @ SD command byte
#define REG_M3SD_ARGH 0x09400000 @ SD command argument, high halfword
#define REG_M3SD_ARGL 0x09600000 @ SD command argument, low halfword
#define REG_M3SD_STS 0x09800000 @ command and status register
/* LBA1 register of M3 CF */
#define REG_M3CF_LBA1 0x08860000 @ 1st byte of sector address
/*****************************************************************************/
@ Test if the card is present.
@ This test is tricky because if it's NOT this card,
@ we are not allowed to destroy contents of GBA ROM space.
@ R0: Return != 0 if present.
.ALIGN
.GLOBAL m3sd_detect_card
m3sd_detect_card:
gba_prefix
@ read and save old value @ REG_M3CF_LBA1
ldr r3, =REG_M3CF_LBA1
ldrh r2, [r3]
ldr r1, =0x789A @ random value
strh r1, [r3]
@ switch M3 to IO mode
m3_set_io
@ test if the value has remained the same
ldr r3, =REG_M3CF_LBA1
ldrh ip, [r3]
teq r1, ip
moveq r0, #0 @ YES: no M3
beq m3sd_detect_exit
@ it's a M3. Test for CF. If not CF, it must be a SD.
@ test if the lower 8 bit of LBA1 are read- and writable
ldrb ip, [r3]
eor ip, ip, #0xFF @ invert lower 8 bit of LBA1
strh ip, [r3] @ store complement in LBA1
ldrb r0, [r3]
teq ip, r0 @ are they the same?
movne r0, #1 @ NO: no CF, must be SD
bne m3sd_detect_exit
@ make sure the register is 8 bit, not 16
ldr ip, =0xAA55
strh ip, [r3]
ldrh r0, [r3]
teq ip, r0 @ are they the same?
moveq r0, #1 @ YES: no CF, must be SD
movne r0, #0 @ NO: it's a CF
m3sd_detect_exit:
m3_set_ram @ switch back to RAM
@ restore RAM contents
ldr r3, =REG_M3CF_LBA1
strh r2, [r3]
gba_suffix
mov pc, lr
/*****************************************************************************/
@ macro to read a byte of response (from CMD line)
@ register input:
@ R3 = REG_M3SD_STS
@ R4 = REG_M3SD_DIR
@ register used:
@ R5, CC
@ register output:
@ IP = byte to read
@ error codes:
@ R0 = 3: timeout @ readbyte, first half
@ R0 = 4: timeout @ readbyte, 2nd half
.macro readbyte exitlabel
mov ip, #0x02
strh ip, [r4] @ REG_M3SD_DIR
strh ip, [r3] @ REG_M3SD_STS
mov r5, #16384 @ timeout counter
1: subs r5, r5, #1
moveq r0, #3 @ exit R0=3
beq \exitlabel
ldrh ip, [r3] @ REG_M3SD_STS
tst ip, #0x08
beq 1b
mov r5, #16384 @ timeout counter
2: subs r5, r5, #1
moveq r0, #4 @ exit R0=4
beq \exitlabel
ldrh ip, [r3] @ REG_M3SD_STS
tst ip, #0x08
bne 2b
ldr r5, =REG_M3SD_DAT
ldrb ip, [r5] @ get data byte
.endm
/*****************************************************************************/
@ Write at minimum 8 clock cycles to the card.
@ Wait max. 1ms for the CMD line to become HIGH.
@ Send a command to the card.
@ if R2 > 0:
@ Wait max. 1ms for the CMD line to become LOW.
@ Read a response from the device.
@ R0: pointer to start of command & response. 32bit aligned.
@ R1: length of the command (including CRC7).
@ R2: length of response.
@ R0 Return 0 if OK, 1 if timeout waiting for CMD HIGH,
@ 2 if timeout waiting for CMD LOW
@ 3 if timeout @ readbyte, first half
@ 4 if timeout @ readbyte, 2nd half
@ 5 if timeout for M3 command processing
.ALIGN
.GLOBAL m3sd_send_command_resp
m3sd_send_command_resp:
stmfd sp!,{r4-r8} @ use additional registers
gba_prefix
m3_set_io @ switch to IO mode
@ setup pointers to the most used card registers
ldr r3, =REG_M3SD_STS
ldr r4, =REG_M3SD_DIR
@ Wait max. 1ms for the CMD line to become HIGH.
@ this implies at minimum 8 clock cycles.
mov r6, #1024 @ retry counter
m3sd_send_command_cmdhigh:
subs r6, r6, #1
moveq r0, #1 @ exit R0=1
beq m3sd_send_command_exit
readbyte m3sd_send_command_exit
teq ip, #0xFF @ all bits high?
bne m3sd_send_command_cmdhigh
@ Send a command to the card.
mov ip, #0x08
strh ip, [r3]
ldr r5, =REG_M3SD_CMD
ldrh ip, [r0, #0]
strh ip, [r5]
ldr r5, =REG_M3SD_ARGH
ldrh ip, [r0, #2]
strh ip, [r5]
ldr r5, =REG_M3SD_ARGL
ldrh ip, [r0, #4]
strh ip, [r5]
@ CRC is done by the M3.
@ wait until M3 has processed the command.
mov ip, #0x29
strh ip, [r4] @ REG_M3SD_DIR
mov r5, #16384 @ timeout counter
m3sd_send_command_proc:
subs r5, r5, #1
moveq r0, #0x09
streqh r0, [r4] @ REG_M3SD_DIR
moveq r0, #5 @ exit R0=5
beq m3sd_send_command_exit
ldrh ip, [r3] @ REG_M3SD_STS
tst ip, #0x01
beq m3sd_send_command_proc
mov ip, #0x09
strh ip, [r4] @ REG_M3SD_DIR
@ test if we have to read a response
teq r2, #0 @ reslen = 0?
moveq r0, #0 @ exit R0=0
beq m3sd_send_command_exit
@ wait until start of response
mov r6, #256 @ max. byte count
m3sd_send_command_wait:
subs r6, r6, #1
moveq r0, #2 @ exit R0=2
beq m3sd_send_command_exit
readbyte m3sd_send_command_exit
teq ip, #0xFF
beq m3sd_send_command_wait
@ now we have the first bits of the response in IP
@ align to first bit (==0)
mov r6, #8 @ shift count
mov r5, #0x80 @ test pattern
m3sd_send_command_align:
tst ip, r5 @ is this bit 0?
movne r5, r5, lsr #1
subne r6, r6, #1
bne m3sd_send_command_align
mov r7, ip @ save byte in R7
@ read bytes and store into response buffer
m3sd_read_response_loop:
readbyte m3sd_send_command_exit
orr r7, ip, r7, lsl #8 @ shift data byte in R7
mov ip, r7, lsr r6 @ deshifting
strb ip, [r0], #1 @ store byte
subs r2, r2, #1
bne m3sd_read_response_loop
mov r0, #0 @ success code
@ exit
m3sd_send_command_exit:
m3_set_ram @ swith to RAM mode
gba_suffix
ldmfd sp!,{r4-r8} @ restore used registers
mov pc, lr
/*****************************************************************************/
@ Wait for the DATA line to become HIGH.
@ R0: Return != 0 if OK, 0 if timeout.
@ Maximum length of testing is 1ms.
.ALIGN
.GLOBAL m3sd_wait_ready
m3sd_wait_ready:
gba_prefix
m3_set_io @ switch to IO mode
ldr r2, =REG_M3SD_DIR
ldr r1, =REG_M3SD_DAT
mov ip, #1024 @ number of tries
m3sd_wait_ready_again:
mov r3, #8 @ high times counter
m3sd_wait_ready_loop:
subs ip, ip, #1 @ dec. loop count
moveq r0, #0 @ exit R0=0
beq m3sd_wait_ready_exit
mov r0, #0x00
strh r0, [r2] @ REG_M3SD_DIR
mov r0, r0 @ nop
mov r0, r0 @ nop
mov r0, #0x08
strh r0, [r2] @ REG_M3SD_DIR
ldrh r0, [r1] @ REG_M3SD_DAT
tst r0, #0x100
beq m3sd_wait_ready_again @ low: once more
subs r3, r3, #1 @ high: dec high counter
bne m3sd_wait_ready_loop @ test again
mov r0, #1 @ 8 times HIGH - OK!
m3sd_wait_ready_exit:
m3_set_ram @ swith to RAM mode
gba_suffix
mov pc, lr
/*****************************************************************************/
@ Send a Data block incl. CRC.
@ R0: pointer to start of data. 32bit aligned.
@ R1: number of bytes to send.
@ R0: Return != 0 if OK, 0 if CRC missing or error. */
.ALIGN
.GLOBAL m3sd_send_data
m3sd_send_data:
stmfd sp!,{r4-r5} @ use additional registers
gba_prefix
m3_set_io @ switch to IO mode
@ setup pointers to the most used card registers
ldr r3, =REG_M3SD_STS
ldr r4, =REG_M3SD_DIR
ldr r5, =REG_M3SD_DAT
mov ip, #0x04
strh ip, [r4] @ REG_M3SD_DIR
mov ip, #0x00
strh ip, [r3] @ REG_M3SD_STS
@ start bit
mov ip, #0x00
strh ip, [r5] @ REG_M3SD_DAT
mov ip, #0x04
strh ip, [r4] @ REG_M3SD_DIR
mov ip, ip @ nop
mov ip, ip @ nop
mov ip, #0x0C
strh ip, [r4] @ REG_M3SD_DIR
m3sd_send_data_loop:
ldrb ip, [r0], #1 @ read next byte
mov r2, ip, lsr #4 @ MSN
strh r2, [r5] @ REG_M3SD_DAT
mov r2, #0x04
strh r2, [r4] @ REG_M3SD_DIR
mov r2, r2 @ nop
mov r2, r2 @ nop
mov r2, #0x0C
strh r2, [r4] @ REG_M3SD_DIR
strh ip, [r5] @ REG_M3SD_DAT
mov r2, #0x04
strh r2, [r4] @ REG_M3SD_DIR
mov r2, r2 @ nop
mov r2, r2 @ nop
mov r2, #0x0C
strh r2, [r4] @ REG_M3SD_DIR
subs r1, r1, #1
bne m3sd_send_data_loop
@ wait a little while
mov ip, #32
m3sd_send_wait:
mov r1, r1
subs ip, ip, #1
bne m3sd_send_wait
@ send some FF
mov r1, #32
m3sd_send_FF:
mov ip, #0xFF
strh ip, [r5] @ REG_M3SD_DAT
mov r2, #0x04
strh r2, [r4] @ REG_M3SD_DIR
mov r2, r2 @ nop
mov r2, r2 @ nop
mov r2, #0x0C
strh r2, [r4] @ REG_M3SD_DIR
subs r1, r1, #1
bne m3sd_send_FF
mov r0, #1 @ R0=OK
m3sd_send_data_exit:
m3_set_ram @ swith to RAM mode
gba_suffix
ldmfd sp!,{r4-r5} @ restore used registers
mov pc, lr
/*****************************************************************************/
@ Wait max 1ms for the DATA line to become LOW.
@ Receive a Data block and CRC.
@ R0: pointer to start of data. 32bit aligned.
@ R1: number of bytes to receive (incl. CRC)
@ R0: Return != 0 if OK, 0 if timeout.
.ALIGN
.GLOBAL m3sd_read_data
m3sd_read_data:
stmfd sp!,{r4-r8} @ use additional registers
gba_prefix
m3_set_io @ switch to IO mode
@ setup pointers to the most used card registers
ldr r3, =REG_M3SD_STS
ldr r4, =REG_M3SD_DIR
mov r2, #0x49
strh r2, [r4] @ REG_M3SD_DIR
mov r2, #1024
m3sd_read_data_wait:
subs r2, r2, #1 @ dec. loop count
moveq r2, #0x09
streqh r2, [r4] @ REG_M3SD_DIR
moveq r0, #0 @ exit R0=0
beq m3sd_read_data_exit
ldrh ip, [r3] @ REG_M3SD_STS
tst ip, #0x40
beq m3sd_read_data_wait
mov r2, #0x09
strh r2, [r4] @ REG_M3SD_DIR
mov r2, #0x08
strh r2, [r4] @ REG_M3SD_DIR
mov r2, #0x04
strh r2, [r3] @ REG_M3SD_STS
ldrh r2, [r4] @ REG_M3SD_DIR
m3sd_read_data_loop:
ldrh r2, [r4] @ REG_M3SD_DIR
strh r2, [r0], #2
subs r1, r1, #2
bne m3sd_read_data_loop
mov ip, #0x08
strh ip, [r3] @ REG_M3SD_STS
mov r0, #1 @ R0=OK
m3sd_read_data_exit:
m3_set_ram @ swith to RAM mode
gba_suffix
ldmfd sp!,{r4-r8} @ restore used registers
mov pc, lr
/*****************************************************************************/
@ Write at minimum 8 clock cycles to the card.
.ALIGN
.GLOBAL m3sd_send_clocks
m3sd_send_clocks:
stmfd sp!,{r4-r5} @ use additional registers
gba_prefix
m3_set_io @ switch to IO mode
@ setup pointers to the most used card registers
ldr r3, =REG_M3SD_STS
ldr r4, =REG_M3SD_DIR
readbyte m3sd_send_clocks_exit
m3sd_send_clocks_exit:
m3_set_ram @ swith to RAM mode
gba_suffix
ldmfd sp!,{r4-r5} @ restore used registers
mov pc, lr
/*****************************************************************************/
@ Calculate the data crc.
@ R0: pointer to start of data. 32bit aligned.
@ R1: number of bytes of CRC calculation.
@ R2: pointer to start of CRC.
.ALIGN
.GLOBAL m3sd_calc_crc
m3sd_calc_crc:
stmfd sp!,{r4-r9}
mov r9,r2
mov r3,#0
mov r4,#0
mov r5,#0
mov r6,#0
ldr r7,=0x80808080
ldr r8,=0x1021
mov r1,r1,lsl #3
m3sd_calc_crc_loop:
tst r7,#0x80
ldrneb r2,[r0],#1
mov r3,r3,lsl #1
tst r3,#0x10000
eorne r3,r3,r8
tst r2,r7,lsr #24
eorne r3,r3,r8
mov r4,r4,lsl #1
tst r4,#0x10000
eorne r4,r4,r8
tst r2,r7,lsr #25
eorne r4,r4,r8
mov r5,r5,lsl #1
tst r5,#0x10000
eorne r5,r5,r8
tst r2,r7,lsr #26
eorne r5,r5,r8
mov r6,r6,lsl #1
tst r6,#0x10000
eorne r6,r6,r8
tst r2,r7,lsr #27
eorne r6,r6,r8
mov r7,r7,ror #4
subs r1,r1,#4
bne m3sd_calc_crc_loop
mov r2,r9
mov r8,#16
m3sd_calc_crc_write_data:
mov r7,r7,lsl #4
tst r3,#0x8000
orrne r7,r7,#8
tst r4,#0x8000
orrne r7,r7,#4
tst r5,#0x8000
orrne r7,r7,#2
tst r6,#0x8000
orrne r7,r7,#1
mov r3,r3,lsl #1
mov r4,r4,lsl #1
mov r5,r5,lsl #1
mov r6,r6,lsl #1
sub r8,r8,#1
tst r8,#1
streqb r7,[r2], #1
cmp r8,#0
bne m3sd_calc_crc_write_data
ldmfd sp!,{r4-r9}
mov pc, lr
/*****************************************************************************/
.END
/*****************************************************************************/
--- NEW FILE: m3sd_c.c ---
/*
* linux/drivers/mmc/m3sd_c.c - M3 SD driver
*
* Copyright (C) 2006 Amadeus, All Rights Reserved.
* Based on the old non-mmc driver by by Jean-Pierre Thomasset.
*
* 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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>
#include <linux/kmod.h>
#include <linux/timer.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/scatterlist.h>
#include <asm/sizes.h>
#undef READ_CRC /* do CRC for data reads */
#undef HALT_ON_ERROR /* stop after error, so we can see what's happened */
#ifdef CONFIG_MMC_DEBUG
#define READ_CRC
#define HALT_ON_ERROR
#define DBG(x...) printk(x)
#else
#define DBG(x...) do { } while (0)
#endif
#ifdef HALT_ON_ERROR
#define halt() do { } while (1)
#else
#define halt() do { } while (0)
#endif
#define BLOCKSIZE 512
#define DRIVER_NAME "m3sd"
#define DRIVER_VERSION "1.1.1"
/*****************************************************************************/
struct m3sd_host {
struct mmc_host *mmc;
u8 inactive;
};
/*****************************************************************************/
/*
* Low-level assembler stuff in m3sd_s.S
*/
/* Test if the card is present.
Return != 0 if present. */
extern int m3sd_detect_card(void);
/* Write at minimum 8 clock cycles to the card.
Wait max. 1ms for the CMD line to become HIGH.
Send a command to the card. Skip 2 Z bits.
if reslen > 0:
Wait max. 1ms for the CMD line to become LOW.
Read a response from the device and skip 2 Z bits.
data = pointer to start of command & response. 32bit aligned.
len = length of the command (including CRC7).
reslen = length of response.
Return 0 if OK, 1 if timeout waiting for CMD HIGH,
2 if timeout waiting for CMD LOW */
extern int m3sd_send_command_resp(u8 *data, int len, int reslen);
/* Wait for the DATA line to become HIGH.
Return != 0 if OK, 0 if timeout.
Maximum length of testing is 1ms. */
extern int m3sd_wait_ready(void);
/* Write a start bit, send a Data block incl. CRC.
Write the end bit. Skip 2 Z bits.
Wait max. 1ms for the start of the CRC response.
Check the CRC response.
data = pointer to start of data. 32bit aligned.
len = number of bytes to send.
Return != 0 if OK, 0 if CRC missing or error. */
extern int m3sd_send_data(u8 *data, int len);
/* Wait max 1ms for the DATA line to become LOW.
Receive a Data block and CRC, skip the end bit.
data = pointer to start of data. 32bit aligned.
len = number of bytes to receive (incl. CRC)
Return != 0 if OK, 0 if timeout. */
extern int m3sd_read_data(u8 *data, int len);
/* Write at minimum 8 clock cycles to the card. */
extern void m3sd_send_clocks(void);
/* Calculate the data crc.
data = pointer to start of data. 32bit aligned.
len = number of bytes of CRC calculation.
crc = pointer to start of CRC. */
extern void m3sd_calc_crc(u8 *data, int len, u8* crc);
/*****************************************************************************/
/* Timer delayed execution of last clocks */
static volatile int sd_active;
static struct timer_list clock_timer;
/*****************************************************************************/
/* Buffer in main memory(!), for transfer of commands, responses and data
between the C and assembler part of this driver. This buffer is 32bit aligned.
This buffer holds data and CRC (two times for reading).
*/
static u8 sd_databuf[BLOCKSIZE+16] __attribute__ ((aligned (4)));
/*****************************************************************************/
/* Improved CRC7 function provided by cory1492 */
inline static u8 m3sd_crc7(u8* data, int cnt)
{
int i;
u8 crc, temp;
crc = 0;
for ( ; cnt; cnt--)
{
temp = *data++;
for (i = 0; i < 8; i++)
{
crc <<= 1;
if ((temp & 0x80) ^ (crc & 0x80)) crc ^= 0x09;
temp <<= 1;
}
}
crc = (crc << 1) | 1; /* stop bit */
return(crc);
}
/* Wait until ready on the data lines */
/* return 0 if ready, 1 if busy */
inline static int m3sd_waitready(void)
{
int i;
for (i = 1000; i; i--) {
if (m3sd_wait_ready())
return 0;
}
return 1;
}
/* send a SD command */
/* @return 0 for success, != 0 otherwise */
static int m3sd_send_cmd(struct mmc_command *cmd)
{
u8 *p = sd_databuf;
u16 *p16 = (u16 *)p;
int i;
int reslen;
/* stop the clock timer, we will send a command */
del_timer(&clock_timer);
/* Start without error */
cmd->error = MMC_ERR_NONE;
/* write command and arg into command buffer */
/* write 3 x 16 bit */
*p16++ = cmd->opcode | 0x40;
*p16++ = cmd->arg >> 16;
*p16 = cmd->arg;
/* calculate length of response */
reslen = 0;
if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_LONG)
reslen = 18;
if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT)
reslen = 6;
if ((cmd->opcode == MMC_READ_SINGLE_BLOCK)
|| (cmd->opcode == MMC_READ_MULTIPLE_BLOCK)
|| (cmd->opcode == SD_APP_SEND_SCR))
reslen = 0;
/* waiting for CMD line high, write command to card, read response */
for (i = 250; i; i--) {
int ret = m3sd_send_command_resp(sd_databuf, 6, reslen);
if (!ret) /* all OK */
break;
if (ret == 1) /* Card busy, try again */
continue;
if (ret >= 2) { /* Timeout error */
printk(KERN_ERR "CMD:Error %d at command/response\n", ret);
cmd->error = MMC_ERR_TIMEOUT;
return -1;
}
}
if (!i) {
printk( KERN_ERR "CMD:Timeout before command\n");
cmd->error = MMC_ERR_TIMEOUT;
halt();
return -1;
}
/* what type of response do we get? */
switch (cmd->flags & MMC_RSP_MASK) {
case MMC_RSP_NONE: /* no response */
break;
case MMC_RSP_SHORT: /* 48 bit = 6 bytes response */
case MMC_RSP_LONG: /* 136 bit response */
/* Skip response, if it is a read data command.
Response and data may come the same time. */
switch (cmd->opcode) {
case MMC_READ_SINGLE_BLOCK:
case MMC_READ_MULTIPLE_BLOCK:
case SD_APP_SEND_SCR:
cmd->resp[0] = 0x00000B20; /* fake response */
return 0;
}
p = sd_databuf;
p++; /* skip command byte */
cmd->resp[0] = *p++ << 24;
cmd->resp[0] |= *p++ << 16;
cmd->resp[0] |= *p++ << 8;
cmd->resp[0] |= *p++;
if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_LONG) {
cmd->resp[1] = *p++ << 24;
cmd->resp[1] |= *p++ << 16;
cmd->resp[1] |= *p++ << 8;
cmd->resp[1] |= *p++;
cmd->resp[2] = *p++ << 24;
cmd->resp[2] |= *p++ << 16;
cmd->resp[2] |= *p++ << 8;
cmd->resp[2] |= *p++;
cmd->resp[3] = *p++ << 24;
cmd->resp[3] |= *p++ << 16;
cmd->resp[3] |= *p++ << 8;
cmd->resp[3] |= *p++;
}
DBG("Got native response %X %X %X %X\n", cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
break;
default:
printk(KERN_ERR "Invalid flags code from mmc layer: %d\n", cmd->flags);
cmd->error = MMC_ERR_INVALID;
halt();
return -1;
}
/* Busy command? (== STOP) */
if (cmd->flags & MMC_RSP_BUSY) {
/* Wait until ready on the data lines */
if (m3sd_waitready()) {
/* Timeout error */
printk(KERN_ERR "Busy: timeout waiting for not busy\n");
cmd->error = MMC_ERR_TIMEOUT;
halt();
return -1;
}
}
/* success */
return 0;
}
/*
* Transfer data blocks from/to sd card.
*/
static void m3sd_xfer( struct mmc_data *data )
{
int sgindex;
int size = data->blocks << data->blksz_bits;
struct scatterlist *sg = data->sg;
int i;
DBG("%s Bytes=%d\n",__FUNCTION__, size);
/* Iterate through the s/g list */
data->bytes_xfered = 0;
data->error = MMC_ERR_NONE;
for (sgindex = 0; sgindex < data->sg_len; sgindex++) {
u8 *p;
int sglength;
/* get the start address */
p = (page_address(sg[sgindex].page)+sg[sgindex].offset);
/* compute the length */
sglength = sg[sgindex].length;
if (size < sglength)
sglength = size;
/* transfer the physical sectors */
while (sglength > 0) {
int length = sglength;
/* break down to sector length */
if (length > 512)
length = 512;
/* do the transfer */
if (data->flags & MMC_DATA_READ) {
/* wait max. 100ms for start bit, read the data+crc+end bit */
for( i=100; i; i--) {
if (m3sd_read_data(sd_databuf, length+8))
break;
}
if (!i) {
/* Timeout error */
printk(KERN_ERR "Read: timeout waiting for read data\n");
data->error = MMC_ERR_TIMEOUT;
halt();
return;
}
#ifdef READ_CRC
/* check the CRC */
m3sd_calc_crc(sd_databuf, length, &sd_databuf[BLOCKSIZE+8]);
for (i = 0; i < 8; i++) {
if (sd_databuf[length+i] != sd_databuf[BLOCKSIZE+8+i]) {
printk (KERN_ERR "Read: CRC read different\n");
data->error = MMC_ERR_BADCRC;
halt();
return;
}
}
#endif
/* copy the data */
memcpy(p, sd_databuf, length);
} else if (data->flags & MMC_DATA_WRITE) {
/* copy the data */
memcpy(sd_databuf, p, length);
/* calculate the crc */
m3sd_calc_crc(sd_databuf, length, &sd_databuf[length]);
/* Wait until ready on the data lines */
if (m3sd_waitready()) {
/* Timeout error */
printk(KERN_ERR "before Write: timeout waiting for ready\n");
data->error = MMC_ERR_TIMEOUT;
halt();
return;
}
/* Write data, check CRC response */
if (!m3sd_send_data(sd_databuf, length+8)) {
/* Timeout error */
printk(KERN_ERR "BAD CRC response\n");
data->error = MMC_ERR_TIMEOUT;
halt();
return;
}
}
/* ready with one physical sector */
p += length;
data->bytes_xfered += length;
size -= length;
sglength -= length;
}
/* stop if all done */
if (size <= 0) break;
}
if (data->flags & MMC_DATA_WRITE) {
/* Wait until ready on the data lines */
if (m3sd_waitready()) {
/* Timeout error */
printk(KERN_ERR "Write: timeout waiting for ready\n");
data->error = MMC_ERR_TIMEOUT;
halt();
return;
}
}
DBG("xfer done\n");
}
static void m3sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct m3sd_host *host = mmc_priv(mmc);
int ret;
int retry = 1;
/* access to SD in progress */
sd_active = 1;
DBG("%s Opcode %d, arg %d\n",__FUNCTION__, mrq->cmd->opcode, mrq->cmd->arg);
/* Fooling mmc core to only do 4bit data transfers.
if the hardware/the driver can only do 4bit transfers. */
if (mmc->card_selected) {
mmc->card_selected->scr.bus_widths |= SD_SCR_BUS_WIDTH_4;
}
switch (mrq->cmd->opcode){
/* do a retry for data I/O */
case MMC_READ_SINGLE_BLOCK:
case MMC_READ_MULTIPLE_BLOCK:
case MMC_WRITE_BLOCK:
case MMC_WRITE_MULTIPLE_BLOCK:
retry = 3;
break;
/* check if we have an inactivation command */
case SD_APP_OP_COND:
case MMC_GO_INACTIVE_STATE:
host->inactive = 1;
break;
/* MMC_ALL_SEND_CID: fake a timeout for the second card */
/* (maybe better if we know to switch CS on/off) */
case MMC_ALL_SEND_CID:
if (!host->inactive) {
DBG("Fake Timeout for second SEND_CID\n");
mrq->cmd->error = MMC_ERR_TIMEOUT;
mmc_request_done(mmc, mrq);
sd_active = 0;
return;
}
break;
}
while (retry--) {
/* Send the command to the card */
mrq->cmd->error = MMC_ERR_NONE;
ret = m3sd_send_cmd(mrq->cmd);
if (ret)
continue;
/* check if we have an activation */
if (mrq->cmd->opcode == MMC_SET_RELATIVE_ADDR)
host->inactive = 0;
if ( mrq->data ) {
mrq->data->error = MMC_ERR_NONE;
m3sd_xfer( mrq->data );
if (mrq->stop) {
mrq->stop->error = MMC_ERR_NONE;
ret = m3sd_send_cmd(mrq->stop);
if (ret)
continue;
}
if (mrq->data->error != MMC_ERR_NONE)
continue;
}
break;
}
/* access done */
sd_active = 0;
/* start timer for the last clock cycles */
mod_timer(&clock_timer, jiffies + HZ/10);
mmc_request_done(mmc, mrq);
}
/* send 8 clock bits after last command */
static void m3sd_timeout(unsigned long arg)
{
if (sd_active)
return; /* nothing to do, next timeout will come */
/* send 8 clocks after last command */
m3sd_send_clocks();
}
/* read the write protect switch */
static int m3sd_get_ro(struct mmc_host *mmc)
{
DBG("%s\n",__FUNCTION__);
return 0;
}
/* set operating conditions for the host */
static void m3sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
#if 0
struct dsmemsd_host *host = mmc_priv(mmc);
DBG("%s\n",__FUNCTION__);
DBG("bus_mode = %x\n", ios->bus_mode);
DBG("chip_select = %x\n", ios->chip_select);
DBG("bus_width = %x\n", ios->bus_width);
DBG("m3sd_set_ios: clock %u power %u vdd %u.%01u\n",
ios->clock, ios->power_mode, (ios->vdd-4+17) / 10,
(ios->vdd-4+17) % 10);
#endif
/* ignore power mode changes - we are always on */
/* ignore chip select changes for now - don't know how to change */
/* ignore clock - only one speed available */
/* ignore vdd - only 3.3 volts available */
/* ignore bus width - only 4 bit available */
/* ignore bus mode - only push-pull available */
}
static struct mmc_host_ops m3sd_ops = {
.request = m3sd_request,
.get_ro = m3sd_get_ro,
.set_ios = m3sd_set_ios,
};
/* setup the SD host controller */
static int m3sd_probe(struct device *dev)
{
struct mmc_host *mmc;
struct m3sd_host *host = NULL;
DBG("%s\n",__FUNCTION__);
if (m3sd_detect_card()) {
printk(KERN_INFO "M3 SD detected\n");
} else {
/* device not found */
return -ENODEV;
}
/* allocate host data structures */
mmc = mmc_alloc_host(sizeof(struct m3sd_host), dev);
if (!mmc) {
return -ENOMEM;
}
/* init timer structure */
init_timer(&clock_timer);
clock_timer.function = m3sd_timeout;
/* populate host data structures */
mmc->ops = &m3sd_ops;
mmc->f_min = 25000000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA;
mmc->mode = MMC_MODE_SD;
/* As we are doing transfers in software, we have no real limits.
So use some big numbers here. */
mmc->max_seg_size = 0x10000;
mmc->max_hw_segs = 0x100;
mmc->max_phys_segs= 0x100;
mmc->max_sectors = 0x100;
host = mmc_priv(mmc);
host->mmc = mmc;
dev_set_drvdata(dev, mmc);
mmc_add_host(mmc);
return 0;
}
static int m3sd_remove(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
dev_set_drvdata(dev, NULL);
if (mmc) {
mmc_remove_host(mmc);
mmc_free_host(mmc);
}
return 0;
}
/* no power managment here */
#define m3sd_suspend NULL
#define m3sd_resume NULL
static void m3sd_platform_release(struct device * device){
/* never */
}
static struct platform_device m3sd_device = {
.name = DRIVER_NAME,
.dev = {
.release = m3sd_platform_release,
}
};
static struct device_driver m3sd_driver = {
.name = DRIVER_NAME,
.bus = &platform_bus_type,
.probe = m3sd_probe,
.remove = m3sd_remove,
.suspend = m3sd_suspend,
.resume = m3sd_resume,
};
static int __init m3sd_init(void)
{
int ret;
ret = driver_register(&m3sd_driver);
if (!ret) {
ret = platform_device_register( &m3sd_device );
}
return ret ;
}
static void __exit m3sd_exit(void)
{
driver_unregister(&m3sd_driver);
}
module_init(m3sd_init);
module_exit(m3sd_exit);
MODULE_DESCRIPTION("M3 SD Driver");
MODULE_LICENSE("GPL");
More information about the dslinux-commit
mailing list