📄 mmc_slot_s3c2410.c
字号:
/* * drivers/mmc/mmc_slot_s3c2410.c * * MMC slot interfaces specific to S3C2410 SD Controller * * Copyright (C) 2002-2003 MIZI Research, Inc. * * Author: Chan Gyun Jeong <cgjeong@mizi.com> * $Id: mmc_slot_s3c2410.c,v 1.2 2004/01/26 08:29:56 laputa Exp $ * * Revision History: * * 2002-12-07 Chan Gyun Jeong <cgjeong@mizi.com> * - initial release * * 2003-01-13 Chan Gyun Jeong <cgjeong@mizi.com> * - clean up, and sort of improvements * * 2003-01-17 Chan Gyun Jeong <cgjeong@mizi.com> * - support for interrupt-driven data I/O * * 2004-01-06 kwang hyun LA <laputa: nala.la@samsung.com> * - pre-scaler caculate method changed for s3c2410 * * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/irq.h>#include <linux/delay.h>#include <asm/hardware.h>#include <asm/arch/cpu_s3c2410.h>#include "mmc.h"/* Board-specific definitions */#ifdef CONFIG_S3C2410_SMDK#define SD_GPIO_CD GPIO_nCD_SD#define SD_IRQ_CD IRQ_nCD_SD#define SD_CD_LOWACTIVE#undef USE_POLLING#define USE_INTERRUPT#endifstatic struct tq_struct card_detect_task;static volatile int card_in;#ifdef USE_INTERRUPTstatic DECLARE_WAIT_QUEUE_HEAD(wq);static volatile int reading;static u_char *buffer;static int bufcnt;static int datalen;static volatile int error;#endif/* * MMC Data R/W functions */static int read_data(struct mmc_slot *slot, u_char *buf, int len) {#ifdef USE_POLLING int i = 0; int cnt; u32 stat; DEBUG2(4, "[%s] ", __FUNCTION__); while (i < len) { stat = SDIDSTA; if (!card_in) { SDIDSTA = stat; return -ENODEV; } if (stat & SDIDSTA_TOUT) { DEBUG2(2, "[%s] Timeout Error\n", __FUNCTION__); SDIDSTA = stat; /* clear */ return -EIO; } stat = SDIFSTA; if (stat & SDIFSTA_RX) { cnt = (stat & SDIFSTA_CNT); while (cnt) { *((u32 *)(buf + i)) = SDIDAT;#ifdef CONFIG_MMC_DEBUG DEBUG2(4, "%02x", buf[i + 0]); DEBUG2(4, "%02x", buf[i + 1]); DEBUG2(4, "%02x", buf[i + 2]); DEBUG2(4, "%02x", buf[i + 3]);#endif i += sizeof(u32); cnt -= sizeof(u32); } } } DEBUG2(4, "\n"); do { stat = SDIDSTA; if (!card_in) { SDIDSTA = stat; return -ENODEV; } } while (!(stat & SDIDSTA_TOUT) && !(stat & SDIDSTA_DFIN)); SDIDSTA = stat; /* clear */ return 0;#elif defined(USE_INTERRUPT) interruptible_sleep_on(&wq); if (!card_in) { return -ENODEV; } if (error) { return -EIO; } return 0;#endif /* USE_POLLING */}static int write_data(struct mmc_slot *slot, const u_char *buf, int len){#ifdef USE_POLLING int i; int cnt; u32 stat;#ifdef CONFIG_MMC_DEBUG DEBUG2(4, "[%s] ", __FUNCTION__); for(i = 0; i < len; i++) DEBUG2(4, "%02x", buf[i]); DEBUG2(4, "\n");#endif i = 0; while (i < len) { stat = SDIFSTA; if (!card_in) { SDIDSTA = stat; return -ENODEV; } if (stat & SDIFSTA_TX) { cnt = SDI_MAX_TX_FIFO - (stat & SDIFSTA_CNT); while (cnt) { SDIDAT = *((u32 *)(buf + i)); i += sizeof(u32); cnt -= sizeof(u32); } } } do { stat = SDIDSTA; if (!card_in) { SDIDSTA = stat; return -ENODEV; } } while (!(stat & SDIDSTA_TOUT) && !(stat & SDIDSTA_DFIN)); SDIDSTA = stat; /* clear */ return 0;#elif defined(USE_INTERRUPT) interruptible_sleep_on(&wq); if (!card_in) { return -ENODEV; } if (error) { return -EIO; } return 0; #endif /* USE_POLLING */}#ifdef USE_INTERRUPTstatic void sdi_interrupt(int irq, void *dev_id, struct pt_regs *regs){ u32 stat; u32 cnt; if (reading) {#ifdef CONFIG_MMC_DEBUG if (bufcnt == 0) { DEBUG2(4, "[%s] RX: ", __FUNCTION__); }#endif stat = SDIFSTA; if (stat & SDIFSTA_RX_LAST) { DEBUG2(5, "\nRX_LAST\n"); cnt = (stat & SDIFSTA_CNT) / sizeof(u32); while (cnt) { *((u32 *)(buffer + bufcnt)) = SDIDAT;#ifdef CONFIG_MMC_DEBUG DEBUG2(4, "%02x", buffer[bufcnt + 0]); DEBUG2(4, "%02x", buffer[bufcnt + 1]); DEBUG2(4, "%02x", buffer[bufcnt + 2]); DEBUG2(4, "%02x", buffer[bufcnt + 3]);#endif bufcnt += sizeof(u32); cnt--; } } else if (stat & SDIFSTA_RX_FULL) { DEBUG2(5, "\nRX_FULL\n"); cnt = SDI_MAX_RX_FIFO / sizeof(u32); while (cnt && bufcnt < datalen) { *((u32 *)(buffer + bufcnt)) = SDIDAT;#ifdef CONFIG_MMC_DEBUG DEBUG2(4, "%02x", buffer[bufcnt + 0]); DEBUG2(4, "%02x", buffer[bufcnt + 1]); DEBUG2(4, "%02x", buffer[bufcnt + 2]); DEBUG2(4, "%02x", buffer[bufcnt + 3]);#endif bufcnt += sizeof(u32); cnt--; } } stat = SDIDSTA; if (stat & SDIDSTA_DFIN) { DEBUG2(4, "\n"); DEBUG2(5, "DFIN\n"); SDIDSTA = stat; wake_up_interruptible(&wq); } else if (stat & SDIDSTA_TOUT) { DEBUG2(4, "\n"); DEBUG2(5, "DTOUT\n"); SDIDSTA = stat; error = 1; wake_up_interruptible(&wq); } } else { stat = SDIFSTA; if (stat & SDIFSTA_TX_EMP) { DEBUG2(5, "TX_EMP\n"); cnt = SDI_MAX_TX_FIFO / sizeof(u32); while (cnt && bufcnt < datalen) { SDIDAT = *((u32 *)(buffer + bufcnt)); bufcnt += sizeof(u32); cnt--; } } stat = SDIDSTA; if (stat & SDIDSTA_DFIN) { DEBUG2(5, "DFIN\n"); SDIDSTA = stat; wake_up_interruptible(&wq); } else if (stat & SDIDSTA_TOUT) { DEBUG2(5, "DTOUT\n"); SDIDSTA = stat; error = 1; wake_up_interruptible(&wq); } }}#endif /* USE_INTERRRUPT *//* * SD Slot Interface funtions */static void power_up(struct mmc_slot *slot){ /* Set block size to 512 bytes */ SDIBSIZE = 0x200; /* Set timeout count */ SDIDTIMER = 0xffff; // timeout value for 2410 [22..0] /* Disable SDI interrupt */ SDIIMSK = 0x0; /* Type B, FIFO reset, SD clock enable */ SDICON = SDICON_LE | SDICON_FRESET | SDICON_ENCLK; /* clear all */ SDIDSTA = SDIDSTA_ALL; /* Wait for power-up */ mdelay(125);}static void power_down(struct mmc_slot *slot){ /* Disable SD Controller */ SDICON = 0x0; SDIIMSK = 0x0; SDIDTIMER = 0x0; SDIBSIZE = 0x0;}static void set_clock(struct mmc_slot *slot, int rate){ unsigned long val, pclk; if (rate == MMC_MMC_CLOCK_HIGH) { rate = 20000000; } pclk = s3c2410_get_bus_clk(GET_PCLK); // laputa to receive s3c2410 PCLK from 2.4.19 val = (pclk / (rate)) ; // protect mmc over clocking to set prescler SDIPRE = (SDIPRE_MSK & val); // prescaler set value anding}static int send_cmd(struct mmc_slot *slot, struct mmc_cmd *cmd){ int ret; u32 stat; if (cmd->res_flag & MMC_RES_FLAG_RDATA) {#ifdef USE_INTERRUPT SDIIMSK = SDIIMSK_TOUT | SDIIMSK_DFIN | SDIIMSK_RX_LAST | SDIIMSK_RX_FULL; reading = 1; buffer = cmd->data; bufcnt = 0; datalen = cmd->data_len; error = 0;#endif stat = SDIDCON_RACMD_1 | SDIDCON_BLK | SDIDCON_RX | 1 << 0; SDIBSIZE = cmd->data_len; SDICON |= SDICON_FRESET; SDIDCON = stat; } else if (cmd->res_flag & MMC_RES_FLAG_WDATA) {#ifdef USE_INTERRUPT SDIIMSK = SDIIMSK_TOUT | SDIIMSK_DFIN | SDIIMSK_TX_EMP; reading = 0; buffer = cmd->data; bufcnt = 0; datalen = cmd->data_len; error = 0;#endif stat = SDIDCON_TARSP_1 | SDIDCON_BLK | SDIDCON_TX | 1 << 0; SDIBSIZE = cmd->data_len; SDICON |= SDICON_FRESET; SDIDCON = stat; } else if (cmd->res_flag & MMC_RES_FLAG_DATALINE) {#ifdef USE_INTERRUPT SDIIMSK = SDIIMSK_TOUT | SDIIMSK_DFIN | SDIIMSK_RX_LAST | SDIIMSK_RX_FULL; reading = 1; buffer = cmd->res; bufcnt = 0; datalen = cmd->data_len; error = 0;#endif stat = SDIDCON_RACMD_1 | SDIDCON_BLK | SDIDCON_RX | 1 << 0; SDIBSIZE = cmd->data_len; SDICON |= SDICON_FRESET; SDIDCON = stat; } stat = SDICCON_START | cmd->cmd; if (cmd->res_type != MMC_RES_TYPE_NONE) { stat |= SDICCON_WRSP; if (cmd->res_type == MMC_RES_TYPE_R2) { stat |= SDICCON_LRSP; } } SDICARG = cmd->arg; SDICCON = stat; if (cmd->res_type != MMC_RES_TYPE_NONE) { if (cmd->res_flag & MMC_RES_FLAG_DATALINE) { ret = read_data(slot, cmd->res, cmd->data_len); goto cmdexit; } else { while(1) { stat = SDICSTA; if (!card_in) { SDICSTA = stat; return -ENODEV; } if (stat & SDICSTA_TOUT) { ret = -ETIMEDOUT; SDICSTA = stat; /* clear bits */ DEBUG2(2, "[%s] Timeout Error\n", __FUNCTION__); goto cmdexit; } if (stat & SDICSTA_RSP) break; } if (!(cmd->res_flag & MMC_RES_FLAG_NOCRC)) { if ((stat & SDICSTA_ALLFLAG) != (SDICSTA_SENT | SDICSTA_RSP)) {#if 0 ret = -EIO; SDICSTA = stat; /* clear bits */ DEBUG2(2, "[%s] CRC Error\n", __FUNCTION__); goto cmdexit;#endif } } SDICSTA = stat; /* clear bits */ if (cmd->res_flag & MMC_RES_FLAG_RDATA) { ret = read_data(slot, cmd->data, cmd->data_len); goto cmdexit; } if (cmd->res_flag & MMC_RES_FLAG_WDATA) { ret = write_data(slot, cmd->data, cmd->data_len); goto cmdexit; } if (mmc_get_res_len(cmd->res_type) == MMC_RES_LEN_LONG) { stat = SDIRSP0; stat = cpu_to_be32(stat); memcpy(cmd->res, &stat, sizeof(stat)); stat = SDIRSP1; stat = cpu_to_be32(stat); memcpy(cmd->res + (1 * sizeof(stat)), &stat, sizeof(stat)); stat = SDIRSP2; stat = cpu_to_be32(stat); memcpy(cmd->res + (2 * sizeof(stat)), &stat, sizeof(stat)); stat = SDIRSP3; stat = cpu_to_be32(stat); memcpy(cmd->res + (3 * sizeof(stat)), &stat, sizeof(stat)); } else { stat = SDIRSP0; stat = cpu_to_be32(stat); memcpy(cmd->res, &stat, sizeof(stat)); stat = SDIRSP1; stat = cpu_to_be32(stat); memcpy(cmd->res + (1 * sizeof(stat)), &stat, sizeof(stat)); } } } else { do { stat = SDICSTA; if (!card_in) { SDICSTA = stat; return -ENODEV; } } while (!(stat & SDICSTA_SENT)); SDICSTA = stat; /* clear bits */ } ret = 0; cmdexit: return ret;}#define WIDE_BUS 0#define STANDARD_BUS 1static struct mmc_slot slot = { id: 0, narrow_bus: WIDE_BUS, power_up: power_up, power_down: power_down, set_clock: set_clock, send_cmd: send_cmd,};static void add_task_handler(void *data){ struct mmc_slot *slot = (struct mmc_slot *)data; int wp;#ifdef CONFIG_S3C2410_SMDK /* Not available on SMDK2410 */ wp = 0;#endif if (wp) { slot->readonly = 1; } else { slot->readonly = 0; } add_mmc_device(slot);}static void del_task_handler(void *data){ struct mmc_slot *slot = (struct mmc_slot *)data; del_mmc_device(slot);}static void card_detect_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int empty;#ifdef SD_CD_LOWACTIVE empty = read_gpio_bit(SD_GPIO_CD);#else empty = !read_gpio_bit(SD_GPIO_CD);#endif DEBUG2(1, "[%s] SD CD = %d\n", __FUNCTION__, !empty); if (!card_in && !empty) { /* card inserted */ DEBUG2(1, "[%s] card inserted\n", __FUNCTION__); card_in = 1; card_detect_task.data = (void *)dev_id; card_detect_task.routine = add_task_handler; schedule_task(&card_detect_task); } else if (card_in && empty) { /* card ejected */ DEBUG2(1, "[%s] card ejected\n", __FUNCTION__); card_in = 0;#ifdef USE_INTERRUPT wake_up_interruptible(&wq);#endif card_detect_task.data = (void *)dev_id; card_detect_task.routine = del_task_handler; schedule_task(&card_detect_task); }}static int __init init_mmc_slot_s3c2410(void){ int ret; /* Initialize status variables */ card_in = 0; /* Initialize h/w settings */ set_gpio_ctrl(GPIO_SDDAT3 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN); set_gpio_ctrl(GPIO_SDDAT2 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN); set_gpio_ctrl(GPIO_SDDAT1 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN); set_gpio_ctrl(GPIO_SDDAT0 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN); set_gpio_ctrl(GPIO_SDCMD | GPIO_MODE_SDCMD | GPIO_PULLUP_EN); set_gpio_ctrl(GPIO_SDCLK | GPIO_MODE_SDCLK | GPIO_PULLUP_EN);#ifdef CONFIG_S3C2410_SMDK set_gpio_ctrl(SD_GPIO_CD);#endif /* Register IRQ handlers */ set_external_irq(SD_IRQ_CD, EXT_BOTH_EDGES, GPIO_PULLUP_DIS); ret = request_irq(SD_IRQ_CD, card_detect_interrupt, SA_INTERRUPT, "SD CD", (void *)&slot); if (ret) { printk("MMC Slot: request_irq(SD CD) failed\n"); goto err1; }#ifdef USE_INTERRUPT ret = request_irq(IRQ_SDI, sdi_interrupt, SA_INTERRUPT, "SDI", (void *)&slot); if (ret) { printk("MMC Slot: request_irq(SDI) failed\n"); goto err2; }#endif printk("MMC Slot initialized\n"); card_detect_interrupt(SD_IRQ_CD, (void *)&slot, NULL); return 0;#ifdef USE_INTERRUPT err2: free_irq(SD_IRQ_CD, (void *)&slot);#endif err1: return ret;}static void __exit exit_mmc_slot_s3c2410(void){#ifdef USE_INTERRUPT free_irq(IRQ_SDI, (void *)&slot);#endif free_irq(SD_IRQ_CD, (void *)&slot); if (card_in) { card_in = 0; del_task_handler((void *)&slot); }}module_init(init_mmc_slot_s3c2410);module_exit(exit_mmc_slot_s3c2410);MODULE_AUTHOR("Chan Gyun Jeong <cgjeong@mizi.com>");MODULE_LICENSE("Not GPL, Proprietary License");MODULE_DESCRIPTION("MMC slot interfaces specific to S3C2410 SD Controller");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -