Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
189
drivers/mmc/host/Kconfig
Normal file
189
drivers/mmc/host/Kconfig
Normal file
@@ -0,0 +1,189 @@
|
||||
#
|
||||
# MMC/SD host controller drivers
|
||||
#
|
||||
|
||||
comment "MMC/SD Host Controller Drivers"
|
||||
|
||||
config MMC_ARMMMCI
|
||||
tristate "ARM AMBA Multimedia Card Interface support"
|
||||
depends on ARM_AMBA
|
||||
help
|
||||
This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
|
||||
Interface (PL180 and PL181) support. If you have an ARM(R)
|
||||
platform with a Multimedia Card slot, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_PXA
|
||||
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
|
||||
depends on ARCH_PXA
|
||||
help
|
||||
This selects the Intel(R) PXA(R) Multimedia card Interface.
|
||||
If you have a PXA(R) platform with a Multimedia Card slot,
|
||||
say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI
|
||||
tristate "Secure Digital Host Controller Interface support (EXPERIMENTAL)"
|
||||
depends on PCI && EXPERIMENTAL
|
||||
help
|
||||
This select the generic Secure Digital Host Controller Interface.
|
||||
It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
|
||||
and Toshiba(R). Most controllers found in laptops are of this type.
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_RICOH_MMC
|
||||
tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
|
||||
depends on PCI && EXPERIMENTAL && MMC_SDHCI
|
||||
help
|
||||
This selects the disabler for the Ricoh MMC Controller. This
|
||||
proprietary controller is unnecessary because the SDHCI driver
|
||||
supports MMC cards on the SD controller, but if it is not
|
||||
disabled, it will steal the MMC cards away - rendering them
|
||||
useless. It is safe to select this driver even if you don't
|
||||
have a Ricoh based card reader.
|
||||
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called ricoh_mmc.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config MMC_OMAP
|
||||
tristate "TI OMAP Multimedia Card Interface support"
|
||||
depends on ARCH_OMAP
|
||||
select TPS65010 if MACH_OMAP_H2
|
||||
help
|
||||
This selects the TI OMAP Multimedia card Interface.
|
||||
If you have an OMAP board with a Multimedia Card slot,
|
||||
say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_WBSD
|
||||
tristate "Winbond W83L51xD SD/MMC Card Interface support"
|
||||
depends on ISA_DMA_API
|
||||
help
|
||||
This selects the Winbond(R) W83L51xD Secure digital and
|
||||
Multimedia card Interface.
|
||||
If you have a machine with a integrated W83L518D or W83L519D
|
||||
SD/MMC card reader, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_AU1X
|
||||
tristate "Alchemy AU1XX0 MMC Card Interface support"
|
||||
depends on SOC_AU1200
|
||||
help
|
||||
This selects the AMD Alchemy(R) Multimedia card interface.
|
||||
If you have a Alchemy platform with a MMC slot, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_AT91
|
||||
tristate "AT91 SD/MMC Card Interface support"
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
This selects the AT91 MCI controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_IMX
|
||||
tristate "Motorola i.MX Multimedia Card Interface support"
|
||||
depends on ARCH_IMX
|
||||
help
|
||||
This selects the Motorola i.MX Multimedia card Interface.
|
||||
If you have a i.MX platform with a Multimedia Card slot,
|
||||
say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_TIFM_SD
|
||||
tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && PCI
|
||||
select TIFM_CORE
|
||||
help
|
||||
Say Y here if you want to be able to access MMC/SD cards with
|
||||
the Texas Instruments(R) Flash Media card reader, found in many
|
||||
laptops.
|
||||
This option 'selects' (turns on, enables) 'TIFM_CORE', but you
|
||||
probably also need appropriate card reader host adapter, such as
|
||||
'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
|
||||
(TIFM_7XX1)'.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tifm_sd.
|
||||
|
||||
config MMC_SPI
|
||||
tristate "MMC/SD over SPI (EXPERIMENTAL)"
|
||||
depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
|
||||
select CRC7
|
||||
select CRC_ITU_T
|
||||
help
|
||||
Some systems accss MMC/SD cards using a SPI controller instead of
|
||||
using a "native" MMC/SD controller. This has a disadvantage of
|
||||
being relatively high overhead, but a compensating advantage of
|
||||
working on many systems without dedicated MMC/SD controllers.
|
||||
|
||||
If unsure, or if your system has no SPI master driver, say N.
|
||||
|
||||
config MMC_S3C
|
||||
tristate "Samsung S3C Multimedia Card Interface support"
|
||||
select MMC_BLOCK
|
||||
select MMC_SUPPORT_MOVINAND
|
||||
depends on ARCH_S3C2410 && MMC && !(CPU_S3C6400 || CPU_S3C6410)
|
||||
help
|
||||
This selects the Samsung S3C Multimedia Card Interface
|
||||
support.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HSMMC_S3C
|
||||
tristate "Samsung S3C High Speed MMC Interface support"
|
||||
select MMC_BLOCK
|
||||
select MMC_SUPPORT_MOVINAND
|
||||
depends on (CPU_S3C2443 || CPU_S3C2450 || CPU_S3C2416 || CPU_S3C6400 || CPU_S3C6410) && MMC
|
||||
help
|
||||
This selects the Samsung S3C Multimedia Card Interface
|
||||
support.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config USE_MMC_AS_ROOT
|
||||
bool "MMC can be used as a Root Filesystem Device"
|
||||
depends on HSMMC_S3C && !MACH_SANJOSE2
|
||||
default y
|
||||
help
|
||||
Say Y here when you want to use MMC as a root file system.
|
||||
|
||||
config HSMMC_S3C_IRQ_WORKAROUND
|
||||
bool "Fix IRQ Hanging in HS-MMC"
|
||||
depends on HSMMC_S3C
|
||||
default y
|
||||
help
|
||||
Say Y here when you are using S3C6400 or S3C6410.
|
||||
|
||||
config HSMMC_SCATTERGATHER
|
||||
bool "Support Scatter and Gather in HS-MMC"
|
||||
depends on HSMMC_S3C
|
||||
help
|
||||
Say Y here. This option can support scatter and gather
|
||||
using DMA boundary in HS-MMC DMA engine.
|
||||
|
||||
config MMC_SUPPORT_MOVINAND
|
||||
bool "Support moviNAND from Samsung Electronics"
|
||||
depends on (MMC_S3C || HSMMC_S3C) && MMC
|
||||
help
|
||||
Say Y here to enable MMC Spec 4.2 and support Samsung
|
||||
moviNAND.
|
||||
|
||||
config S3CMMC_DEBUG
|
||||
bool "To debug interface, enable all debug messages"
|
||||
depends on (MMC_S3C || HSMMC_S3C)
|
||||
help
|
||||
Say Y here to enable Debug Messages.
|
||||
|
||||
|
||||
21
drivers/mmc/host/Makefile
Normal file
21
drivers/mmc/host/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Makefile for MMC/SD host controller drivers
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
|
||||
obj-$(CONFIG_MMC_PXA) += pxamci.o
|
||||
obj-$(CONFIG_MMC_IMX) += imxmmc.o
|
||||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
|
||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||
obj-$(CONFIG_MMC_OMAP) += omap.o
|
||||
obj-$(CONFIG_MMC_AT91) += at91_mci.o
|
||||
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
|
||||
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
|
||||
obj-$(CONFIG_HSMMC_S3C) += s3c-hsmmc.o
|
||||
|
||||
1031
drivers/mmc/host/at91_mci.c
Normal file
1031
drivers/mmc/host/at91_mci.c
Normal file
File diff suppressed because it is too large
Load Diff
1022
drivers/mmc/host/au1xmmc.c
Normal file
1022
drivers/mmc/host/au1xmmc.c
Normal file
File diff suppressed because it is too large
Load Diff
96
drivers/mmc/host/au1xmmc.h
Normal file
96
drivers/mmc/host/au1xmmc.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef _AU1XMMC_H_
|
||||
#define _AU1XMMC_H_
|
||||
|
||||
/* Hardware definitions */
|
||||
|
||||
#define AU1XMMC_DESCRIPTOR_COUNT 1
|
||||
#define AU1XMMC_DESCRIPTOR_SIZE 2048
|
||||
|
||||
#define AU1XMMC_OCR ( MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
|
||||
MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
|
||||
MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)
|
||||
|
||||
/* Easy access macros */
|
||||
|
||||
#define HOST_STATUS(h) ((h)->iobase + SD_STATUS)
|
||||
#define HOST_CONFIG(h) ((h)->iobase + SD_CONFIG)
|
||||
#define HOST_ENABLE(h) ((h)->iobase + SD_ENABLE)
|
||||
#define HOST_TXPORT(h) ((h)->iobase + SD_TXPORT)
|
||||
#define HOST_RXPORT(h) ((h)->iobase + SD_RXPORT)
|
||||
#define HOST_CMDARG(h) ((h)->iobase + SD_CMDARG)
|
||||
#define HOST_BLKSIZE(h) ((h)->iobase + SD_BLKSIZE)
|
||||
#define HOST_CMD(h) ((h)->iobase + SD_CMD)
|
||||
#define HOST_CONFIG2(h) ((h)->iobase + SD_CONFIG2)
|
||||
#define HOST_TIMEOUT(h) ((h)->iobase + SD_TIMEOUT)
|
||||
#define HOST_DEBUG(h) ((h)->iobase + SD_DEBUG)
|
||||
|
||||
#define DMA_CHANNEL(h) \
|
||||
( ((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
|
||||
|
||||
/* This gives us a hard value for the stop command that we can write directly
|
||||
* to the command register
|
||||
*/
|
||||
|
||||
#define STOP_CMD (SD_CMD_RT_1B|SD_CMD_CT_7|(0xC << SD_CMD_CI_SHIFT)|SD_CMD_GO)
|
||||
|
||||
/* This is the set of interrupts that we configure by default */
|
||||
|
||||
#if 0
|
||||
#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_DD | \
|
||||
SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I)
|
||||
#endif
|
||||
|
||||
#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | \
|
||||
SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I)
|
||||
/* The poll event (looking for insert/remove events runs twice a second */
|
||||
#define AU1XMMC_DETECT_TIMEOUT (HZ/2)
|
||||
|
||||
struct au1xmmc_host {
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_request *mrq;
|
||||
|
||||
u32 id;
|
||||
|
||||
u32 flags;
|
||||
u32 iobase;
|
||||
u32 clock;
|
||||
u32 bus_width;
|
||||
u32 power_mode;
|
||||
|
||||
int status;
|
||||
|
||||
struct {
|
||||
int len;
|
||||
int dir;
|
||||
} dma;
|
||||
|
||||
struct {
|
||||
int index;
|
||||
int offset;
|
||||
int len;
|
||||
} pio;
|
||||
|
||||
u32 tx_chan;
|
||||
u32 rx_chan;
|
||||
|
||||
struct timer_list timer;
|
||||
struct tasklet_struct finish_task;
|
||||
struct tasklet_struct data_task;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/* Status flags used by the host structure */
|
||||
|
||||
#define HOST_F_XMIT 0x0001
|
||||
#define HOST_F_RECV 0x0002
|
||||
#define HOST_F_DMA 0x0010
|
||||
#define HOST_F_ACTIVE 0x0100
|
||||
#define HOST_F_STOP 0x1000
|
||||
|
||||
#define HOST_S_IDLE 0x0001
|
||||
#define HOST_S_CMD 0x0002
|
||||
#define HOST_S_DATA 0x0003
|
||||
#define HOST_S_STOP 0x0004
|
||||
|
||||
#endif
|
||||
1149
drivers/mmc/host/imxmmc.c
Normal file
1149
drivers/mmc/host/imxmmc.c
Normal file
File diff suppressed because it is too large
Load Diff
67
drivers/mmc/host/imxmmc.h
Normal file
67
drivers/mmc/host/imxmmc.h
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x)))
|
||||
|
||||
#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00)
|
||||
#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04)
|
||||
#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08)
|
||||
#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C)
|
||||
#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10)
|
||||
#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14)
|
||||
#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18)
|
||||
#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C)
|
||||
#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20)
|
||||
#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24)
|
||||
#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28)
|
||||
#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C)
|
||||
#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30)
|
||||
#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34)
|
||||
#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38)
|
||||
#define MMC_BUFFER_ACCESS_OFS 0x38
|
||||
|
||||
|
||||
#define STR_STP_CLK_ENDIAN (1<<5)
|
||||
#define STR_STP_CLK_RESET (1<<3)
|
||||
#define STR_STP_CLK_ENABLE (1<<2)
|
||||
#define STR_STP_CLK_START_CLK (1<<1)
|
||||
#define STR_STP_CLK_STOP_CLK (1<<0)
|
||||
#define STATUS_CARD_PRESENCE (1<<15)
|
||||
#define STATUS_SDIO_INT_ACTIVE (1<<14)
|
||||
#define STATUS_END_CMD_RESP (1<<13)
|
||||
#define STATUS_WRITE_OP_DONE (1<<12)
|
||||
#define STATUS_DATA_TRANS_DONE (1<<11)
|
||||
#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10)
|
||||
#define STATUS_CARD_BUS_CLK_RUN (1<<8)
|
||||
#define STATUS_APPL_BUFF_FF (1<<7)
|
||||
#define STATUS_APPL_BUFF_FE (1<<6)
|
||||
#define STATUS_RESP_CRC_ERR (1<<5)
|
||||
#define STATUS_CRC_READ_ERR (1<<3)
|
||||
#define STATUS_CRC_WRITE_ERR (1<<2)
|
||||
#define STATUS_TIME_OUT_RESP (1<<1)
|
||||
#define STATUS_TIME_OUT_READ (1<<0)
|
||||
#define STATUS_ERR_MASK 0x2f
|
||||
#define CLK_RATE_PRESCALER(x) ((x) & 0x7)
|
||||
#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3)
|
||||
#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12)
|
||||
#define CMD_DAT_CONT_STOP_READWAIT (1<<11)
|
||||
#define CMD_DAT_CONT_START_READWAIT (1<<10)
|
||||
#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8)
|
||||
#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8)
|
||||
#define CMD_DAT_CONT_INIT (1<<7)
|
||||
#define CMD_DAT_CONT_BUSY (1<<6)
|
||||
#define CMD_DAT_CONT_STREAM_BLOCK (1<<5)
|
||||
#define CMD_DAT_CONT_WRITE (1<<4)
|
||||
#define CMD_DAT_CONT_DATA_ENABLE (1<<3)
|
||||
#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
|
||||
#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
|
||||
#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
|
||||
#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
|
||||
#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
|
||||
#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
|
||||
#define INT_MASK_AUTO_CARD_DETECT (1<<6)
|
||||
#define INT_MASK_DAT0_EN (1<<5)
|
||||
#define INT_MASK_SDIO (1<<4)
|
||||
#define INT_MASK_BUF_READY (1<<3)
|
||||
#define INT_MASK_END_CMD_RES (1<<2)
|
||||
#define INT_MASK_WRITE_OP_DONE (1<<1)
|
||||
#define INT_MASK_DATA_TRAN (1<<0)
|
||||
#define INT_ALL (0x7f)
|
||||
1413
drivers/mmc/host/mmc_spi.c
Normal file
1413
drivers/mmc/host/mmc_spi.c
Normal file
File diff suppressed because it is too large
Load Diff
711
drivers/mmc/host/mmci.c
Normal file
711
drivers/mmc/host/mmci.c
Normal file
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
* linux/drivers/mmc/host/mmci.c - 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.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/sizes.h>
|
||||
#include <asm/mach/mmc.h>
|
||||
|
||||
#include "mmci.h"
|
||||
|
||||
#define DRIVER_NAME "mmci-pl18x"
|
||||
|
||||
#define DBG(host,fmt,args...) \
|
||||
pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args)
|
||||
|
||||
static unsigned int fmax = 515633;
|
||||
|
||||
static void
|
||||
mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
writel(0, host->base + MMCICOMMAND);
|
||||
|
||||
BUG_ON(host->data);
|
||||
|
||||
host->mrq = NULL;
|
||||
host->cmd = NULL;
|
||||
|
||||
if (mrq->data)
|
||||
mrq->data->bytes_xfered = host->data_xfered;
|
||||
|
||||
/*
|
||||
* Need to drop the host lock here; mmc_request_done may call
|
||||
* back into the driver...
|
||||
*/
|
||||
spin_unlock(&host->lock);
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
spin_lock(&host->lock);
|
||||
}
|
||||
|
||||
static void mmci_stop_data(struct mmci_host *host)
|
||||
{
|
||||
writel(0, host->base + MMCIDATACTRL);
|
||||
writel(0, host->base + MMCIMASK1);
|
||||
host->data = NULL;
|
||||
}
|
||||
|
||||
static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
|
||||
{
|
||||
unsigned int datactrl, timeout, irqmask;
|
||||
unsigned long long clks;
|
||||
void __iomem *base;
|
||||
int blksz_bits;
|
||||
|
||||
DBG(host, "blksz %04x blks %04x flags %08x\n",
|
||||
data->blksz, data->blocks, data->flags);
|
||||
|
||||
host->data = data;
|
||||
host->size = data->blksz;
|
||||
host->data_xfered = 0;
|
||||
|
||||
mmci_init_sg(host, data);
|
||||
|
||||
clks = (unsigned long long)data->timeout_ns * host->cclk;
|
||||
do_div(clks, 1000000000UL);
|
||||
|
||||
timeout = data->timeout_clks + (unsigned int)clks;
|
||||
|
||||
base = host->base;
|
||||
writel(timeout, base + MMCIDATATIMER);
|
||||
writel(host->size, base + MMCIDATALENGTH);
|
||||
|
||||
blksz_bits = ffs(data->blksz) - 1;
|
||||
BUG_ON(1 << blksz_bits != data->blksz);
|
||||
|
||||
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
datactrl |= MCI_DPSM_DIRECTION;
|
||||
irqmask = MCI_RXFIFOHALFFULLMASK;
|
||||
|
||||
/*
|
||||
* If we have less than a FIFOSIZE of bytes to transfer,
|
||||
* trigger a PIO interrupt as soon as any data is available.
|
||||
*/
|
||||
if (host->size < MCI_FIFOSIZE)
|
||||
irqmask |= MCI_RXDATAAVLBLMASK;
|
||||
} else {
|
||||
/*
|
||||
* We don't actually need to include "FIFO empty" here
|
||||
* since its implicit in "FIFO half empty".
|
||||
*/
|
||||
irqmask = MCI_TXFIFOHALFEMPTYMASK;
|
||||
}
|
||||
|
||||
writel(datactrl, base + MMCIDATACTRL);
|
||||
writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
|
||||
writel(irqmask, base + MMCIMASK1);
|
||||
}
|
||||
|
||||
static void
|
||||
mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
|
||||
{
|
||||
void __iomem *base = host->base;
|
||||
|
||||
DBG(host, "op %02x arg %08x flags %08x\n",
|
||||
cmd->opcode, cmd->arg, cmd->flags);
|
||||
|
||||
if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
|
||||
writel(0, base + MMCICOMMAND);
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
c |= cmd->opcode | MCI_CPSM_ENABLE;
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
if (cmd->flags & MMC_RSP_136)
|
||||
c |= MCI_CPSM_LONGRSP;
|
||||
c |= MCI_CPSM_RESPONSE;
|
||||
}
|
||||
if (/*interrupt*/0)
|
||||
c |= MCI_CPSM_INTERRUPT;
|
||||
|
||||
host->cmd = cmd;
|
||||
|
||||
writel(cmd->arg, base + MMCIARGUMENT);
|
||||
writel(c, base + MMCICOMMAND);
|
||||
}
|
||||
|
||||
static void
|
||||
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
||||
unsigned int status)
|
||||
{
|
||||
if (status & MCI_DATABLOCKEND) {
|
||||
host->data_xfered += data->blksz;
|
||||
}
|
||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
|
||||
if (status & MCI_DATACRCFAIL)
|
||||
data->error = -EILSEQ;
|
||||
else if (status & MCI_DATATIMEOUT)
|
||||
data->error = -ETIMEDOUT;
|
||||
else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
|
||||
data->error = -EIO;
|
||||
status |= MCI_DATAEND;
|
||||
|
||||
/*
|
||||
* We hit an error condition. Ensure that any data
|
||||
* partially written to a page is properly coherent.
|
||||
*/
|
||||
if (host->sg_len && data->flags & MMC_DATA_READ)
|
||||
flush_dcache_page(sg_page(host->sg_ptr));
|
||||
}
|
||||
if (status & MCI_DATAEND) {
|
||||
mmci_stop_data(host);
|
||||
|
||||
if (!data->stop) {
|
||||
mmci_request_end(host, data->mrq);
|
||||
} else {
|
||||
mmci_start_command(host, data->stop, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
||||
unsigned int status)
|
||||
{
|
||||
void __iomem *base = host->base;
|
||||
|
||||
host->cmd = NULL;
|
||||
|
||||
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
||||
cmd->resp[1] = readl(base + MMCIRESPONSE1);
|
||||
cmd->resp[2] = readl(base + MMCIRESPONSE2);
|
||||
cmd->resp[3] = readl(base + MMCIRESPONSE3);
|
||||
|
||||
if (status & MCI_CMDTIMEOUT) {
|
||||
cmd->error = -ETIMEDOUT;
|
||||
} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
|
||||
cmd->error = -EILSEQ;
|
||||
}
|
||||
|
||||
if (!cmd->data || cmd->error) {
|
||||
if (host->data)
|
||||
mmci_stop_data(host);
|
||||
mmci_request_end(host, cmd->mrq);
|
||||
} else if (!(cmd->data->flags & MMC_DATA_READ)) {
|
||||
mmci_start_data(host, cmd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
|
||||
{
|
||||
void __iomem *base = host->base;
|
||||
char *ptr = buffer;
|
||||
u32 status;
|
||||
|
||||
do {
|
||||
int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
|
||||
|
||||
if (count > remain)
|
||||
count = remain;
|
||||
|
||||
if (count <= 0)
|
||||
break;
|
||||
|
||||
readsl(base + MMCIFIFO, ptr, count >> 2);
|
||||
|
||||
ptr += count;
|
||||
remain -= count;
|
||||
|
||||
if (remain == 0)
|
||||
break;
|
||||
|
||||
status = readl(base + MMCISTATUS);
|
||||
} while (status & MCI_RXDATAAVLBL);
|
||||
|
||||
return ptr - buffer;
|
||||
}
|
||||
|
||||
static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
|
||||
{
|
||||
void __iomem *base = host->base;
|
||||
char *ptr = buffer;
|
||||
|
||||
do {
|
||||
unsigned int count, maxcnt;
|
||||
|
||||
maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
|
||||
count = min(remain, maxcnt);
|
||||
|
||||
writesl(base + MMCIFIFO, ptr, count >> 2);
|
||||
|
||||
ptr += count;
|
||||
remain -= count;
|
||||
|
||||
if (remain == 0)
|
||||
break;
|
||||
|
||||
status = readl(base + MMCISTATUS);
|
||||
} while (status & MCI_TXFIFOHALFEMPTY);
|
||||
|
||||
return ptr - buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
* PIO data transfer IRQ handler.
|
||||
*/
|
||||
static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mmci_host *host = dev_id;
|
||||
void __iomem *base = host->base;
|
||||
u32 status;
|
||||
|
||||
status = readl(base + MMCISTATUS);
|
||||
|
||||
DBG(host, "irq1 %08x\n", status);
|
||||
|
||||
do {
|
||||
unsigned long flags;
|
||||
unsigned int remain, len;
|
||||
char *buffer;
|
||||
|
||||
/*
|
||||
* For write, we only need to test the half-empty flag
|
||||
* here - if the FIFO is completely empty, then by
|
||||
* definition it is more than half empty.
|
||||
*
|
||||
* For read, check for data available.
|
||||
*/
|
||||
if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Map the current scatter buffer.
|
||||
*/
|
||||
buffer = mmci_kmap_atomic(host, &flags) + host->sg_off;
|
||||
remain = host->sg_ptr->length - host->sg_off;
|
||||
|
||||
len = 0;
|
||||
if (status & MCI_RXACTIVE)
|
||||
len = mmci_pio_read(host, buffer, remain);
|
||||
if (status & MCI_TXACTIVE)
|
||||
len = mmci_pio_write(host, buffer, remain, status);
|
||||
|
||||
/*
|
||||
* Unmap the buffer.
|
||||
*/
|
||||
mmci_kunmap_atomic(host, buffer, &flags);
|
||||
|
||||
host->sg_off += len;
|
||||
host->size -= len;
|
||||
remain -= len;
|
||||
|
||||
if (remain)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If we were reading, and we have completed this
|
||||
* page, ensure that the data cache is coherent.
|
||||
*/
|
||||
if (status & MCI_RXACTIVE)
|
||||
flush_dcache_page(sg_page(host->sg_ptr));
|
||||
|
||||
if (!mmci_next_sg(host))
|
||||
break;
|
||||
|
||||
status = readl(base + MMCISTATUS);
|
||||
} while (1);
|
||||
|
||||
/*
|
||||
* If we're nearing the end of the read, switch to
|
||||
* "any data available" mode.
|
||||
*/
|
||||
if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE)
|
||||
writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
|
||||
|
||||
/*
|
||||
* If we run out of data, disable the data IRQs; this
|
||||
* prevents a race where the FIFO becomes empty before
|
||||
* the chip itself has disabled the data path, and
|
||||
* stops us racing with our data end IRQ.
|
||||
*/
|
||||
if (host->size == 0) {
|
||||
writel(0, base + MMCIMASK1);
|
||||
writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle completion of command and data transfers.
|
||||
*/
|
||||
static irqreturn_t mmci_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mmci_host *host = dev_id;
|
||||
u32 status;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
|
||||
do {
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
|
||||
status = readl(host->base + MMCISTATUS);
|
||||
status &= readl(host->base + MMCIMASK0);
|
||||
writel(status, host->base + MMCICLEAR);
|
||||
|
||||
DBG(host, "irq0 %08x\n", status);
|
||||
|
||||
data = host->data;
|
||||
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
|
||||
MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
|
||||
mmci_data_irq(host, data, status);
|
||||
|
||||
cmd = host->cmd;
|
||||
if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
|
||||
mmci_cmd_irq(host, cmd, status);
|
||||
|
||||
ret = 1;
|
||||
} while (status);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
|
||||
return IRQ_RETVAL(ret);
|
||||
}
|
||||
|
||||
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
|
||||
WARN_ON(host->mrq != NULL);
|
||||
|
||||
if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
|
||||
printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
|
||||
mmc_hostname(mmc), mrq->data->blksz);
|
||||
mrq->cmd->error = -EINVAL;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irq(&host->lock);
|
||||
|
||||
host->mrq = mrq;
|
||||
|
||||
if (mrq->data && mrq->data->flags & MMC_DATA_READ)
|
||||
mmci_start_data(host, mrq->data);
|
||||
|
||||
mmci_start_command(host, mrq->cmd, 0);
|
||||
|
||||
spin_unlock_irq(&host->lock);
|
||||
}
|
||||
|
||||
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
u32 clk = 0, pwr = 0;
|
||||
|
||||
if (ios->clock) {
|
||||
if (ios->clock >= host->mclk) {
|
||||
clk = MCI_CLK_BYPASS;
|
||||
host->cclk = host->mclk;
|
||||
} else {
|
||||
clk = host->mclk / (2 * ios->clock) - 1;
|
||||
if (clk > 256)
|
||||
clk = 255;
|
||||
host->cclk = host->mclk / (2 * (clk + 1));
|
||||
}
|
||||
clk |= MCI_CLK_ENABLE;
|
||||
}
|
||||
|
||||
if (host->plat->translate_vdd)
|
||||
pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
|
||||
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
pwr |= MCI_PWR_UP;
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
pwr |= MCI_PWR_ON;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
||||
pwr |= MCI_ROD;
|
||||
|
||||
writel(clk, host->base + MMCICLOCK);
|
||||
|
||||
if (host->pwr != pwr) {
|
||||
host->pwr = pwr;
|
||||
writel(pwr, host->base + MMCIPOWER);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops mmci_ops = {
|
||||
.request = mmci_request,
|
||||
.set_ios = mmci_set_ios,
|
||||
};
|
||||
|
||||
static void mmci_check_status(unsigned long data)
|
||||
{
|
||||
struct mmci_host *host = (struct mmci_host *)data;
|
||||
unsigned int status;
|
||||
|
||||
status = host->plat->status(mmc_dev(host->mmc));
|
||||
if (status ^ host->oldstat)
|
||||
mmc_detect_change(host->mmc, 0);
|
||||
|
||||
host->oldstat = status;
|
||||
mod_timer(&host->timer, jiffies + HZ);
|
||||
}
|
||||
|
||||
static int mmci_probe(struct amba_device *dev, void *id)
|
||||
{
|
||||
struct mmc_platform_data *plat = dev->dev.platform_data;
|
||||
struct mmci_host *host;
|
||||
struct mmc_host *mmc;
|
||||
int ret;
|
||||
|
||||
/* must have platform data */
|
||||
if (!plat) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = amba_request_regions(dev, DRIVER_NAME);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
|
||||
if (!mmc) {
|
||||
ret = -ENOMEM;
|
||||
goto rel_regions;
|
||||
}
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->clk = clk_get(&dev->dev, "MCLK");
|
||||
if (IS_ERR(host->clk)) {
|
||||
ret = PTR_ERR(host->clk);
|
||||
host->clk = NULL;
|
||||
goto host_free;
|
||||
}
|
||||
|
||||
ret = clk_enable(host->clk);
|
||||
if (ret)
|
||||
goto clk_free;
|
||||
|
||||
host->plat = plat;
|
||||
host->mclk = clk_get_rate(host->clk);
|
||||
host->mmc = mmc;
|
||||
host->base = ioremap(dev->res.start, SZ_4K);
|
||||
if (!host->base) {
|
||||
ret = -ENOMEM;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
mmc->ops = &mmci_ops;
|
||||
mmc->f_min = (host->mclk + 511) / 512;
|
||||
mmc->f_max = min(host->mclk, fmax);
|
||||
mmc->ocr_avail = plat->ocr_mask;
|
||||
mmc->caps = MMC_CAP_MULTIWRITE;
|
||||
|
||||
/*
|
||||
* We can do SGIO
|
||||
*/
|
||||
mmc->max_hw_segs = 16;
|
||||
mmc->max_phys_segs = NR_SG;
|
||||
|
||||
/*
|
||||
* Since we only have a 16-bit data length register, we must
|
||||
* ensure that we don't exceed 2^16-1 bytes in a single request.
|
||||
*/
|
||||
mmc->max_req_size = 65535;
|
||||
|
||||
/*
|
||||
* Set the maximum segment size. Since we aren't doing DMA
|
||||
* (yet) we are only limited by the data length register.
|
||||
*/
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
/*
|
||||
* Block size can be up to 2048 bytes, but must be a power of two.
|
||||
*/
|
||||
mmc->max_blk_size = 2048;
|
||||
|
||||
/*
|
||||
* No limit on the number of blocks transferred.
|
||||
*/
|
||||
mmc->max_blk_count = mmc->max_req_size;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
writel(0, host->base + MMCIMASK0);
|
||||
writel(0, host->base + MMCIMASK1);
|
||||
writel(0xfff, host->base + MMCICLEAR);
|
||||
|
||||
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
|
||||
if (ret)
|
||||
goto unmap;
|
||||
|
||||
ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);
|
||||
if (ret)
|
||||
goto irq0_free;
|
||||
|
||||
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
|
||||
|
||||
amba_set_drvdata(dev, mmc);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
|
||||
mmc_hostname(mmc), amba_rev(dev), amba_config(dev),
|
||||
(unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]);
|
||||
|
||||
init_timer(&host->timer);
|
||||
host->timer.data = (unsigned long)host;
|
||||
host->timer.function = mmci_check_status;
|
||||
host->timer.expires = jiffies + HZ;
|
||||
add_timer(&host->timer);
|
||||
|
||||
return 0;
|
||||
|
||||
irq0_free:
|
||||
free_irq(dev->irq[0], host);
|
||||
unmap:
|
||||
iounmap(host->base);
|
||||
clk_disable:
|
||||
clk_disable(host->clk);
|
||||
clk_free:
|
||||
clk_put(host->clk);
|
||||
host_free:
|
||||
mmc_free_host(mmc);
|
||||
rel_regions:
|
||||
amba_release_regions(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmci_remove(struct amba_device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = amba_get_drvdata(dev);
|
||||
|
||||
amba_set_drvdata(dev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
|
||||
del_timer_sync(&host->timer);
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
writel(0, host->base + MMCIMASK0);
|
||||
writel(0, host->base + MMCIMASK1);
|
||||
|
||||
writel(0, host->base + MMCICOMMAND);
|
||||
writel(0, host->base + MMCIDATACTRL);
|
||||
|
||||
free_irq(dev->irq[0], host);
|
||||
free_irq(dev->irq[1], host);
|
||||
|
||||
iounmap(host->base);
|
||||
clk_disable(host->clk);
|
||||
clk_put(host->clk);
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
amba_release_regions(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mmci_suspend(struct amba_device *dev, pm_message_t state)
|
||||
{
|
||||
struct mmc_host *mmc = amba_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc) {
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
|
||||
ret = mmc_suspend_host(mmc, state);
|
||||
if (ret == 0)
|
||||
writel(0, host->base + MMCIMASK0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmci_resume(struct amba_device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = amba_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc) {
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
|
||||
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
|
||||
|
||||
ret = mmc_resume_host(mmc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define mmci_suspend NULL
|
||||
#define mmci_resume NULL
|
||||
#endif
|
||||
|
||||
static struct amba_id mmci_ids[] = {
|
||||
{
|
||||
.id = 0x00041180,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{
|
||||
.id = 0x00041181,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static struct amba_driver mmci_driver = {
|
||||
.drv = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = mmci_probe,
|
||||
.remove = mmci_remove,
|
||||
.suspend = mmci_suspend,
|
||||
.resume = mmci_resume,
|
||||
.id_table = mmci_ids,
|
||||
};
|
||||
|
||||
static int __init mmci_init(void)
|
||||
{
|
||||
return amba_driver_register(&mmci_driver);
|
||||
}
|
||||
|
||||
static void __exit mmci_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&mmci_driver);
|
||||
}
|
||||
|
||||
module_init(mmci_init);
|
||||
module_exit(mmci_exit);
|
||||
module_param(fmax, uint, 0444);
|
||||
|
||||
MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
179
drivers/mmc/host/mmci.h
Normal file
179
drivers/mmc/host/mmci.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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 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)
|
||||
|
||||
#define MMCIRESPCMD 0x010
|
||||
#define MMCIRESPONSE0 0x014
|
||||
#define MMCIRESPONSE1 0x018
|
||||
#define MMCIRESPONSE2 0x01c
|
||||
#define MMCIRESPONSE3 0x020
|
||||
#define MMCIDATATIMER 0x024
|
||||
#define MMCIDATALENGTH 0x028
|
||||
#define MMCIDATACTRL 0x02c
|
||||
#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)
|
||||
|
||||
#define MMCIMASK1 0x040
|
||||
#define MMCIFIFOCNT 0x048
|
||||
#define MMCIFIFO 0x080 /* to 0x0bc */
|
||||
|
||||
#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
|
||||
|
||||
struct clk;
|
||||
|
||||
struct mmci_host {
|
||||
void __iomem *base;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
struct mmc_host *mmc;
|
||||
struct clk *clk;
|
||||
|
||||
unsigned int data_xfered;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
unsigned int mclk;
|
||||
unsigned int cclk;
|
||||
u32 pwr;
|
||||
struct mmc_platform_data *plat;
|
||||
|
||||
struct timer_list timer;
|
||||
unsigned int oldstat;
|
||||
|
||||
unsigned int sg_len;
|
||||
|
||||
/* pio stuff */
|
||||
struct scatterlist *sg_ptr;
|
||||
unsigned int sg_off;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
|
||||
{
|
||||
/*
|
||||
* Ideally, we want the higher levels to pass us a scatter list.
|
||||
*/
|
||||
host->sg_len = data->sg_len;
|
||||
host->sg_ptr = data->sg;
|
||||
host->sg_off = 0;
|
||||
}
|
||||
|
||||
static inline int mmci_next_sg(struct mmci_host *host)
|
||||
{
|
||||
host->sg_ptr++;
|
||||
host->sg_off = 0;
|
||||
return --host->sg_len;
|
||||
}
|
||||
|
||||
static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
|
||||
{
|
||||
struct scatterlist *sg = host->sg_ptr;
|
||||
|
||||
local_irq_save(*flags);
|
||||
return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
|
||||
}
|
||||
|
||||
static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags)
|
||||
{
|
||||
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
|
||||
local_irq_restore(*flags);
|
||||
}
|
||||
1277
drivers/mmc/host/omap.c
Normal file
1277
drivers/mmc/host/omap.c
Normal file
File diff suppressed because it is too large
Load Diff
684
drivers/mmc/host/pxamci.c
Normal file
684
drivers/mmc/host/pxamci.c
Normal file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* linux/drivers/mmc/host/pxa.c - PXA MMCI driver
|
||||
*
|
||||
* Copyright (C) 2003 Russell King, 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 hardware is really sick:
|
||||
* - No way to clear interrupts.
|
||||
* - Have to turn off the clock whenever we touch the device.
|
||||
* - Doesn't tell you how many data blocks were transferred.
|
||||
* Yuck!
|
||||
*
|
||||
* 1 and 3 byte data transfers not supported
|
||||
* max block length up to 1023
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/mmc.h>
|
||||
|
||||
#include "pxamci.h"
|
||||
|
||||
#define DRIVER_NAME "pxa2xx-mci"
|
||||
|
||||
#define NR_SG 1
|
||||
#define CLKRT_OFF (~0)
|
||||
|
||||
struct pxamci_host {
|
||||
struct mmc_host *mmc;
|
||||
spinlock_t lock;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
unsigned long clkrate;
|
||||
int irq;
|
||||
int dma;
|
||||
unsigned int clkrt;
|
||||
unsigned int cmdat;
|
||||
unsigned int imask;
|
||||
unsigned int power_mode;
|
||||
struct pxamci_platform_data *pdata;
|
||||
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
|
||||
dma_addr_t sg_dma;
|
||||
struct pxa_dma_desc *sg_cpu;
|
||||
unsigned int dma_len;
|
||||
|
||||
unsigned int dma_dir;
|
||||
};
|
||||
|
||||
static void pxamci_stop_clock(struct pxamci_host *host)
|
||||
{
|
||||
if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
|
||||
unsigned long timeout = 10000;
|
||||
unsigned int v;
|
||||
|
||||
writel(STOP_CLOCK, host->base + MMC_STRPCL);
|
||||
|
||||
do {
|
||||
v = readl(host->base + MMC_STAT);
|
||||
if (!(v & STAT_CLK_EN))
|
||||
break;
|
||||
udelay(1);
|
||||
} while (timeout--);
|
||||
|
||||
if (v & STAT_CLK_EN)
|
||||
dev_err(mmc_dev(host->mmc), "unable to stop clock\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->imask &= ~mask;
|
||||
writel(host->imask, host->base + MMC_I_MASK);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->imask |= mask;
|
||||
writel(host->imask, host->base + MMC_I_MASK);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
|
||||
{
|
||||
unsigned int nob = data->blocks;
|
||||
unsigned long long clks;
|
||||
unsigned int timeout;
|
||||
u32 dcmd;
|
||||
int i;
|
||||
|
||||
host->data = data;
|
||||
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
nob = 0xffff;
|
||||
|
||||
writel(nob, host->base + MMC_NOB);
|
||||
writel(data->blksz, host->base + MMC_BLKLEN);
|
||||
|
||||
clks = (unsigned long long)data->timeout_ns * host->clkrate;
|
||||
do_div(clks, 1000000000UL);
|
||||
timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
|
||||
writel((timeout + 255) / 256, host->base + MMC_RDTO);
|
||||
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
host->dma_dir = DMA_FROM_DEVICE;
|
||||
dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
|
||||
DRCMRTXMMC = 0;
|
||||
DRCMRRXMMC = host->dma | DRCMR_MAPVLD;
|
||||
} else {
|
||||
host->dma_dir = DMA_TO_DEVICE;
|
||||
dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;
|
||||
DRCMRRXMMC = 0;
|
||||
DRCMRTXMMC = host->dma | DRCMR_MAPVLD;
|
||||
}
|
||||
|
||||
dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
|
||||
|
||||
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
host->dma_dir);
|
||||
|
||||
for (i = 0; i < host->dma_len; i++) {
|
||||
unsigned int length = sg_dma_len(&data->sg[i]);
|
||||
host->sg_cpu[i].dcmd = dcmd | length;
|
||||
if (length & 31 && !(data->flags & MMC_DATA_READ))
|
||||
host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
|
||||
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
|
||||
} else {
|
||||
host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
|
||||
host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
|
||||
}
|
||||
host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
|
||||
sizeof(struct pxa_dma_desc);
|
||||
}
|
||||
host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
|
||||
wmb();
|
||||
|
||||
DDADR(host->dma) = host->sg_dma;
|
||||
DCSR(host->dma) = DCSR_RUN;
|
||||
}
|
||||
|
||||
static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
|
||||
{
|
||||
WARN_ON(host->cmd != NULL);
|
||||
host->cmd = cmd;
|
||||
|
||||
if (cmd->flags & MMC_RSP_BUSY)
|
||||
cmdat |= CMDAT_BUSY;
|
||||
|
||||
#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
|
||||
switch (RSP_TYPE(mmc_resp_type(cmd))) {
|
||||
case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */
|
||||
cmdat |= CMDAT_RESP_SHORT;
|
||||
break;
|
||||
case RSP_TYPE(MMC_RSP_R3):
|
||||
cmdat |= CMDAT_RESP_R3;
|
||||
break;
|
||||
case RSP_TYPE(MMC_RSP_R2):
|
||||
cmdat |= CMDAT_RESP_R2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writel(cmd->opcode, host->base + MMC_CMD);
|
||||
writel(cmd->arg >> 16, host->base + MMC_ARGH);
|
||||
writel(cmd->arg & 0xffff, host->base + MMC_ARGL);
|
||||
writel(cmdat, host->base + MMC_CMDAT);
|
||||
writel(host->clkrt, host->base + MMC_CLKRT);
|
||||
|
||||
writel(START_CLOCK, host->base + MMC_STRPCL);
|
||||
|
||||
pxamci_enable_irq(host, END_CMD_RES);
|
||||
}
|
||||
|
||||
static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
host->mrq = NULL;
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
|
||||
{
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
int i;
|
||||
u32 v;
|
||||
|
||||
if (!cmd)
|
||||
return 0;
|
||||
|
||||
host->cmd = NULL;
|
||||
|
||||
/*
|
||||
* Did I mention this is Sick. We always need to
|
||||
* discard the upper 8 bits of the first 16-bit word.
|
||||
*/
|
||||
v = readl(host->base + MMC_RES) & 0xffff;
|
||||
for (i = 0; i < 4; i++) {
|
||||
u32 w1 = readl(host->base + MMC_RES) & 0xffff;
|
||||
u32 w2 = readl(host->base + MMC_RES) & 0xffff;
|
||||
cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
|
||||
v = w2;
|
||||
}
|
||||
|
||||
if (stat & STAT_TIME_OUT_RESPONSE) {
|
||||
cmd->error = -ETIMEDOUT;
|
||||
} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
|
||||
#ifdef CONFIG_PXA27x
|
||||
/*
|
||||
* workaround for erratum #42:
|
||||
* Intel PXA27x Family Processor Specification Update Rev 001
|
||||
* A bogus CRC error can appear if the msb of a 136 bit
|
||||
* response is a one.
|
||||
*/
|
||||
if (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000) {
|
||||
pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode);
|
||||
} else
|
||||
#endif
|
||||
cmd->error = -EILSEQ;
|
||||
}
|
||||
|
||||
pxamci_disable_irq(host, END_CMD_RES);
|
||||
if (host->data && !cmd->error) {
|
||||
pxamci_enable_irq(host, DATA_TRAN_DONE);
|
||||
} else {
|
||||
pxamci_finish_request(host, host->mrq);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
DCSR(host->dma) = 0;
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
|
||||
host->dma_dir);
|
||||
|
||||
if (stat & STAT_READ_TIME_OUT)
|
||||
data->error = -ETIMEDOUT;
|
||||
else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
|
||||
data->error = -EILSEQ;
|
||||
|
||||
/*
|
||||
* There appears to be a hardware design bug here. There seems to
|
||||
* be no way to find out how much data was transferred to the card.
|
||||
* This means that if there was an error on any block, we mark all
|
||||
* data blocks as being in error.
|
||||
*/
|
||||
if (!data->error)
|
||||
data->bytes_xfered = data->blocks * data->blksz;
|
||||
else
|
||||
data->bytes_xfered = 0;
|
||||
|
||||
pxamci_disable_irq(host, DATA_TRAN_DONE);
|
||||
|
||||
host->data = NULL;
|
||||
if (host->mrq->stop) {
|
||||
pxamci_stop_clock(host);
|
||||
pxamci_start_cmd(host, host->mrq->stop, host->cmdat);
|
||||
} else {
|
||||
pxamci_finish_request(host, host->mrq);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static irqreturn_t pxamci_irq(int irq, void *devid)
|
||||
{
|
||||
struct pxamci_host *host = devid;
|
||||
unsigned int ireg;
|
||||
int handled = 0;
|
||||
|
||||
ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);
|
||||
|
||||
if (ireg) {
|
||||
unsigned stat = readl(host->base + MMC_STAT);
|
||||
|
||||
pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat);
|
||||
|
||||
if (ireg & END_CMD_RES)
|
||||
handled |= pxamci_cmd_done(host, stat);
|
||||
if (ireg & DATA_TRAN_DONE)
|
||||
handled |= pxamci_data_done(host, stat);
|
||||
if (ireg & SDIO_INT) {
|
||||
mmc_signal_sdio_irq(host->mmc);
|
||||
handled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct pxamci_host *host = mmc_priv(mmc);
|
||||
unsigned int cmdat;
|
||||
|
||||
WARN_ON(host->mrq != NULL);
|
||||
|
||||
host->mrq = mrq;
|
||||
|
||||
pxamci_stop_clock(host);
|
||||
|
||||
cmdat = host->cmdat;
|
||||
host->cmdat &= ~CMDAT_INIT;
|
||||
|
||||
if (mrq->data) {
|
||||
pxamci_setup_data(host, mrq->data);
|
||||
|
||||
cmdat &= ~CMDAT_BUSY;
|
||||
cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
|
||||
if (mrq->data->flags & MMC_DATA_WRITE)
|
||||
cmdat |= CMDAT_WRITE;
|
||||
|
||||
if (mrq->data->flags & MMC_DATA_STREAM)
|
||||
cmdat |= CMDAT_STREAM;
|
||||
}
|
||||
|
||||
pxamci_start_cmd(host, mrq->cmd, cmdat);
|
||||
}
|
||||
|
||||
static int pxamci_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
struct pxamci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (host->pdata && host->pdata->get_ro)
|
||||
return host->pdata->get_ro(mmc_dev(mmc));
|
||||
/* Host doesn't support read only detection so assume writeable */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct pxamci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (ios->clock) {
|
||||
unsigned long rate = host->clkrate;
|
||||
unsigned int clk = rate / ios->clock;
|
||||
|
||||
if (host->clkrt == CLKRT_OFF)
|
||||
clk_enable(host->clk);
|
||||
|
||||
/*
|
||||
* clk might result in a lower divisor than we
|
||||
* desire. check for that condition and adjust
|
||||
* as appropriate.
|
||||
*/
|
||||
if (rate / clk > ios->clock)
|
||||
clk <<= 1;
|
||||
host->clkrt = fls(clk) - 1;
|
||||
|
||||
/*
|
||||
* we write clkrt on the next command
|
||||
*/
|
||||
} else {
|
||||
pxamci_stop_clock(host);
|
||||
if (host->clkrt != CLKRT_OFF) {
|
||||
host->clkrt = CLKRT_OFF;
|
||||
clk_disable(host->clk);
|
||||
}
|
||||
}
|
||||
|
||||
if (host->power_mode != ios->power_mode) {
|
||||
host->power_mode = ios->power_mode;
|
||||
|
||||
if (host->pdata && host->pdata->setpower)
|
||||
host->pdata->setpower(mmc_dev(mmc), ios->vdd);
|
||||
|
||||
if (ios->power_mode == MMC_POWER_ON)
|
||||
host->cmdat |= CMDAT_INIT;
|
||||
}
|
||||
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_4)
|
||||
host->cmdat |= CMDAT_SD_4DAT;
|
||||
else
|
||||
host->cmdat &= ~CMDAT_SD_4DAT;
|
||||
|
||||
pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
|
||||
host->clkrt, host->cmdat);
|
||||
}
|
||||
|
||||
static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
|
||||
{
|
||||
struct pxamci_host *pxa_host = mmc_priv(host);
|
||||
|
||||
if (enable)
|
||||
pxamci_enable_irq(pxa_host, SDIO_INT);
|
||||
else
|
||||
pxamci_disable_irq(pxa_host, SDIO_INT);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops pxamci_ops = {
|
||||
.request = pxamci_request,
|
||||
.get_ro = pxamci_get_ro,
|
||||
.set_ios = pxamci_set_ios,
|
||||
.enable_sdio_irq = pxamci_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static void pxamci_dma_irq(int dma, void *devid)
|
||||
{
|
||||
struct pxamci_host *host = devid;
|
||||
int dcsr = DCSR(dma);
|
||||
DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
|
||||
|
||||
if (dcsr & DCSR_ENDINTR) {
|
||||
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
|
||||
} else {
|
||||
printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
|
||||
mmc_hostname(host->mmc), dma, dcsr);
|
||||
host->data->error = -EIO;
|
||||
pxamci_data_done(host, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t pxamci_detect_irq(int irq, void *devid)
|
||||
{
|
||||
struct pxamci_host *host = mmc_priv(devid);
|
||||
|
||||
mmc_detect_change(devid, host->pdata->detect_delay);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pxamci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
struct pxamci_host *host = NULL;
|
||||
struct resource *r;
|
||||
int ret, irq;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!r || irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
r = request_mem_region(r->start, SZ_4K, DRIVER_NAME);
|
||||
if (!r)
|
||||
return -EBUSY;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
|
||||
if (!mmc) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmc->ops = &pxamci_ops;
|
||||
|
||||
/*
|
||||
* We can do SG-DMA, but we don't because we never know how much
|
||||
* data we successfully wrote to the card.
|
||||
*/
|
||||
mmc->max_phys_segs = NR_SG;
|
||||
|
||||
/*
|
||||
* Our hardware DMA can handle a maximum of one page per SG entry.
|
||||
*/
|
||||
mmc->max_seg_size = PAGE_SIZE;
|
||||
|
||||
/*
|
||||
* Block length register is only 10 bits before PXA27x.
|
||||
*/
|
||||
mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048;
|
||||
|
||||
/*
|
||||
* Block count register is 16 bits.
|
||||
*/
|
||||
mmc->max_blk_count = 65535;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->dma = -1;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
host->clkrt = CLKRT_OFF;
|
||||
|
||||
host->clk = clk_get(&pdev->dev, "MMCCLK");
|
||||
if (IS_ERR(host->clk)) {
|
||||
ret = PTR_ERR(host->clk);
|
||||
host->clk = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
host->clkrate = clk_get_rate(host->clk);
|
||||
|
||||
/*
|
||||
* Calculate minimum clock rate, rounding up.
|
||||
*/
|
||||
mmc->f_min = (host->clkrate + 63) / 64;
|
||||
mmc->f_max = host->clkrate;
|
||||
|
||||
mmc->ocr_avail = host->pdata ?
|
||||
host->pdata->ocr_mask :
|
||||
MMC_VDD_32_33|MMC_VDD_33_34;
|
||||
mmc->caps = 0;
|
||||
host->cmdat = 0;
|
||||
if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) {
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
||||
host->cmdat |= CMDAT_SDIO_INT_EN;
|
||||
}
|
||||
|
||||
host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
|
||||
if (!host->sg_cpu) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
host->res = r;
|
||||
host->irq = irq;
|
||||
host->imask = MMC_I_MASK_ALL;
|
||||
|
||||
host->base = ioremap(r->start, SZ_4K);
|
||||
if (!host->base) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that the host controller is shut down, and setup
|
||||
* with our defaults.
|
||||
*/
|
||||
pxamci_stop_clock(host);
|
||||
writel(0, host->base + MMC_SPI);
|
||||
writel(64, host->base + MMC_RESTO);
|
||||
writel(host->imask, host->base + MMC_I_MASK);
|
||||
|
||||
host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
|
||||
pxamci_dma_irq, host);
|
||||
if (host->dma < 0) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
||||
if (host->pdata && host->pdata->init)
|
||||
host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (host) {
|
||||
if (host->dma >= 0)
|
||||
pxa_free_dma(host->dma);
|
||||
if (host->base)
|
||||
iounmap(host->base);
|
||||
if (host->sg_cpu)
|
||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||
if (host->clk)
|
||||
clk_put(host->clk);
|
||||
}
|
||||
if (mmc)
|
||||
mmc_free_host(mmc);
|
||||
release_resource(r);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pxamci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
struct pxamci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (host->pdata && host->pdata->exit)
|
||||
host->pdata->exit(&pdev->dev, mmc);
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
pxamci_stop_clock(host);
|
||||
writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
|
||||
END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
|
||||
host->base + MMC_I_MASK);
|
||||
|
||||
DRCMRRXMMC = 0;
|
||||
DRCMRTXMMC = 0;
|
||||
|
||||
free_irq(host->irq, host);
|
||||
pxa_free_dma(host->dma);
|
||||
iounmap(host->base);
|
||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||
|
||||
clk_put(host->clk);
|
||||
|
||||
release_resource(host->res);
|
||||
|
||||
mmc_free_host(mmc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pxamci_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc)
|
||||
ret = mmc_suspend_host(mmc, state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pxamci_resume(struct platform_device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc)
|
||||
ret = mmc_resume_host(mmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define pxamci_suspend NULL
|
||||
#define pxamci_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver pxamci_driver = {
|
||||
.probe = pxamci_probe,
|
||||
.remove = pxamci_remove,
|
||||
.suspend = pxamci_suspend,
|
||||
.resume = pxamci_resume,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pxamci_init(void)
|
||||
{
|
||||
return platform_driver_register(&pxamci_driver);
|
||||
}
|
||||
|
||||
static void __exit pxamci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pxamci_driver);
|
||||
}
|
||||
|
||||
module_init(pxamci_init);
|
||||
module_exit(pxamci_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
90
drivers/mmc/host/pxamci.h
Normal file
90
drivers/mmc/host/pxamci.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#define MMC_STRPCL 0x0000
|
||||
#define STOP_CLOCK (1 << 0)
|
||||
#define START_CLOCK (2 << 0)
|
||||
|
||||
#define MMC_STAT 0x0004
|
||||
#define STAT_END_CMD_RES (1 << 13)
|
||||
#define STAT_PRG_DONE (1 << 12)
|
||||
#define STAT_DATA_TRAN_DONE (1 << 11)
|
||||
#define STAT_CLK_EN (1 << 8)
|
||||
#define STAT_RECV_FIFO_FULL (1 << 7)
|
||||
#define STAT_XMIT_FIFO_EMPTY (1 << 6)
|
||||
#define STAT_RES_CRC_ERR (1 << 5)
|
||||
#define STAT_SPI_READ_ERROR_TOKEN (1 << 4)
|
||||
#define STAT_CRC_READ_ERROR (1 << 3)
|
||||
#define STAT_CRC_WRITE_ERROR (1 << 2)
|
||||
#define STAT_TIME_OUT_RESPONSE (1 << 1)
|
||||
#define STAT_READ_TIME_OUT (1 << 0)
|
||||
|
||||
#define MMC_CLKRT 0x0008 /* 3 bit */
|
||||
|
||||
#define MMC_SPI 0x000c
|
||||
#define SPI_CS_ADDRESS (1 << 3)
|
||||
#define SPI_CS_EN (1 << 2)
|
||||
#define CRC_ON (1 << 1)
|
||||
#define SPI_EN (1 << 0)
|
||||
|
||||
#define MMC_CMDAT 0x0010
|
||||
#define CMDAT_SDIO_INT_EN (1 << 11)
|
||||
#define CMDAT_SD_4DAT (1 << 8)
|
||||
#define CMDAT_DMAEN (1 << 7)
|
||||
#define CMDAT_INIT (1 << 6)
|
||||
#define CMDAT_BUSY (1 << 5)
|
||||
#define CMDAT_STREAM (1 << 4) /* 1 = stream */
|
||||
#define CMDAT_WRITE (1 << 3) /* 1 = write */
|
||||
#define CMDAT_DATAEN (1 << 2)
|
||||
#define CMDAT_RESP_NONE (0 << 0)
|
||||
#define CMDAT_RESP_SHORT (1 << 0)
|
||||
#define CMDAT_RESP_R2 (2 << 0)
|
||||
#define CMDAT_RESP_R3 (3 << 0)
|
||||
|
||||
#define MMC_RESTO 0x0014 /* 7 bit */
|
||||
|
||||
#define MMC_RDTO 0x0018 /* 16 bit */
|
||||
|
||||
#define MMC_BLKLEN 0x001c /* 10 bit */
|
||||
|
||||
#define MMC_NOB 0x0020 /* 16 bit */
|
||||
|
||||
#define MMC_PRTBUF 0x0024
|
||||
#define BUF_PART_FULL (1 << 0)
|
||||
|
||||
#define MMC_I_MASK 0x0028
|
||||
|
||||
/*PXA27x MMC interrupts*/
|
||||
#define SDIO_SUSPEND_ACK (1 << 12)
|
||||
#define SDIO_INT (1 << 11)
|
||||
#define RD_STALLED (1 << 10)
|
||||
#define RES_ERR (1 << 9)
|
||||
#define DAT_ERR (1 << 8)
|
||||
#define TINT (1 << 7)
|
||||
|
||||
/*PXA2xx MMC interrupts*/
|
||||
#define TXFIFO_WR_REQ (1 << 6)
|
||||
#define RXFIFO_RD_REQ (1 << 5)
|
||||
#define CLK_IS_OFF (1 << 4)
|
||||
#define STOP_CMD (1 << 3)
|
||||
#define END_CMD_RES (1 << 2)
|
||||
#define PRG_DONE (1 << 1)
|
||||
#define DATA_TRAN_DONE (1 << 0)
|
||||
|
||||
#ifdef CONFIG_PXA27x
|
||||
#define MMC_I_MASK_ALL 0x00001fff
|
||||
#else
|
||||
#define MMC_I_MASK_ALL 0x0000007f
|
||||
#endif
|
||||
|
||||
#define MMC_I_REG 0x002c
|
||||
/* same as MMC_I_MASK */
|
||||
|
||||
#define MMC_CMD 0x0030
|
||||
|
||||
#define MMC_ARGH 0x0034 /* 16 bit */
|
||||
|
||||
#define MMC_ARGL 0x0038 /* 16 bit */
|
||||
|
||||
#define MMC_RES 0x003c /* 16 bit */
|
||||
|
||||
#define MMC_RXFIFO 0x0040 /* 8 bit */
|
||||
|
||||
#define MMC_TXFIFO 0x0044 /* 8 bit */
|
||||
151
drivers/mmc/host/ricoh_mmc.c
Normal file
151
drivers/mmc/host/ricoh_mmc.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
|
||||
*
|
||||
* Copyright (C) 2007 Philip Langdale, 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a conceptually ridiculous driver, but it is required by the way
|
||||
* the Ricoh multi-function R5C832 works. This chip implements firewire
|
||||
* and four different memory card controllers. Two of those controllers are
|
||||
* an SDHCI controller and a proprietary MMC controller. The linux SDHCI
|
||||
* driver supports MMC cards but the chip detects MMC cards in hardware
|
||||
* and directs them to the MMC controller - so the SDHCI driver never sees
|
||||
* them. To get around this, we must disable the useless MMC controller.
|
||||
* At that point, the SDHCI controller will start seeing them. As a bonus,
|
||||
* a detection event occurs immediately, even if the MMC card is already
|
||||
* in the reader.
|
||||
*
|
||||
* The relevant registers live on the firewire function, so this is unavoidably
|
||||
* ugly. Such is life.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#define DRIVER_NAME "ricoh-mmc"
|
||||
|
||||
static const struct pci_device_id pci_ids[] __devinitdata = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_RICOH,
|
||||
.device = PCI_DEVICE_ID_RICOH_R5C843,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
},
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
u8 rev;
|
||||
|
||||
struct pci_dev *fw_dev = NULL;
|
||||
|
||||
BUG_ON(pdev == NULL);
|
||||
BUG_ON(ent == NULL);
|
||||
|
||||
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
|
||||
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
|
||||
pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
|
||||
(int)rev);
|
||||
|
||||
while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
|
||||
if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
|
||||
pdev->bus == fw_dev->bus) {
|
||||
u8 write_enable;
|
||||
u8 disable;
|
||||
|
||||
pci_read_config_byte(fw_dev, 0xCB, &disable);
|
||||
if (disable & 0x02) {
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": Controller already disabled. Nothing to do.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pci_read_config_byte(fw_dev, 0xCA, &write_enable);
|
||||
pci_write_config_byte(fw_dev, 0xCA, 0x57);
|
||||
pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
|
||||
pci_write_config_byte(fw_dev, 0xCA, write_enable);
|
||||
|
||||
pci_set_drvdata(pdev, fw_dev);
|
||||
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": Controller is now disabled.\n");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pci_get_drvdata(pdev) == NULL) {
|
||||
printk(KERN_WARNING DRIVER_NAME
|
||||
": Main firewire function not found. Cannot disable controller.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
|
||||
{
|
||||
u8 write_enable;
|
||||
u8 disable;
|
||||
struct pci_dev *fw_dev = NULL;
|
||||
|
||||
fw_dev = pci_get_drvdata(pdev);
|
||||
BUG_ON(fw_dev == NULL);
|
||||
|
||||
pci_read_config_byte(fw_dev, 0xCA, &write_enable);
|
||||
pci_read_config_byte(fw_dev, 0xCB, &disable);
|
||||
pci_write_config_byte(fw_dev, 0xCA, 0x57);
|
||||
pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
|
||||
pci_write_config_byte(fw_dev, 0xCA, write_enable);
|
||||
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": Controller is now re-enabled.\n");
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
}
|
||||
|
||||
static struct pci_driver ricoh_mmc_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = pci_ids,
|
||||
.probe = ricoh_mmc_probe,
|
||||
.remove = __devexit_p(ricoh_mmc_remove),
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Driver init/exit *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static int __init ricoh_mmc_drv_init(void)
|
||||
{
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": Ricoh MMC Controller disabling driver\n");
|
||||
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
|
||||
|
||||
return pci_register_driver(&ricoh_mmc_driver);
|
||||
}
|
||||
|
||||
static void __exit ricoh_mmc_drv_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ricoh_mmc_driver);
|
||||
}
|
||||
|
||||
module_init(ricoh_mmc_drv_init);
|
||||
module_exit(ricoh_mmc_drv_exit);
|
||||
|
||||
MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
|
||||
MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
1822
drivers/mmc/host/s3c-hsmmc.c
Normal file
1822
drivers/mmc/host/s3c-hsmmc.c
Normal file
File diff suppressed because it is too large
Load Diff
94
drivers/mmc/host/s3c-hsmmc.h
Normal file
94
drivers/mmc/host/s3c-hsmmc.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* linux/drivers/mmc/s3c-hsmmc.h - Samsung S3C HS-MMC Interface driver
|
||||
*
|
||||
* $Id: s3c-hsmmc.h,v 1.14 2008/04/11 04:27:18 jsgood Exp $
|
||||
*
|
||||
* Copyright (C) 2004 Thomas Kleffel, 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.
|
||||
*/
|
||||
|
||||
#ifndef MHZ
|
||||
#define MHZ (1000*1000)
|
||||
#endif
|
||||
|
||||
#define s3c_hsmmc_readl(x) readl((host->base)+(x))
|
||||
#define s3c_hsmmc_readw(x) readw((host->base)+(x))
|
||||
#define s3c_hsmmc_readb(x) readb((host->base)+(x))
|
||||
|
||||
#define s3c_hsmmc_writel(v,x) writel((v),(host->base)+(x))
|
||||
#define s3c_hsmmc_writew(v,x) writew((v),(host->base)+(x))
|
||||
#define s3c_hsmmc_writeb(v,x) writeb((v),(host->base)+(x))
|
||||
|
||||
#define S3C_HSMMC_CLOCK_ON 1
|
||||
#define S3C_HSMMC_CLOCK_OFF 0
|
||||
|
||||
#ifndef CONFIG_S3C_HSMMC_MAX_HW_SEGS
|
||||
#define CONFIG_S3C_HSMMC_MAX_HW_SEGS 32
|
||||
#endif
|
||||
|
||||
|
||||
/* For SDMA */
|
||||
struct s3c_hsmmc_dma_blk {
|
||||
dma_addr_t dma_address; /* dma address */
|
||||
uint length; /* length */
|
||||
uint boundary; /* Host DMA Buffer Boundary */
|
||||
void *original;
|
||||
};
|
||||
|
||||
/* For ADMA2 */
|
||||
struct s3c_hsmmc_adma_descr {
|
||||
volatile u32 length_attr; /* length + attribute */
|
||||
volatile u32 dma_address; /* dma address */
|
||||
} __packed;
|
||||
|
||||
|
||||
struct s3c_hsmmc_host {
|
||||
void __iomem *base;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
struct mmc_host *mmc;
|
||||
struct clk *clk[3];
|
||||
struct resource *mem;
|
||||
|
||||
struct timer_list timer;
|
||||
|
||||
struct s3c_hsmmc_cfg *plat_data;
|
||||
|
||||
int irq;
|
||||
int irq_cd;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
struct tasklet_struct card_tasklet; /* Tasklet structures */
|
||||
struct tasklet_struct finish_tasklet;
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
|
||||
#define S3C_HSMMC_USE_DMA (1<<0)
|
||||
int flags; /* Host attributes */
|
||||
|
||||
uint dma_dir;
|
||||
|
||||
#ifdef CONFIG_HSMMC_SCATTERGATHER
|
||||
uint sg_len; /* size of scatter list */
|
||||
|
||||
/* For SDMA */
|
||||
uint dma_blk; /* total dmablk number */
|
||||
uint next_blk; /* next block to send */
|
||||
struct s3c_hsmmc_dma_blk dblk[CONFIG_S3C_HSMMC_MAX_HW_SEGS*4];
|
||||
|
||||
/* when pseudo algo cannot deal with sglist */
|
||||
#define S3C_HSMMC_MALLOC_SIZE PAGE_SIZE
|
||||
#define S3C_HSMMC_MAX_SUB_BUF CONFIG_S3C_HSMMC_MAX_HW_SEGS
|
||||
void *sub_block[S3C_HSMMC_MAX_SUB_BUF];
|
||||
|
||||
/* For ADMA2 */
|
||||
struct s3c_hsmmc_adma_descr sdma_descr_tbl[CONFIG_S3C_HSMMC_MAX_HW_SEGS];
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
1667
drivers/mmc/host/sdhci.c
Normal file
1667
drivers/mmc/host/sdhci.c
Normal file
File diff suppressed because it is too large
Load Diff
213
drivers/mmc/host/sdhci.h
Normal file
213
drivers/mmc/host/sdhci.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
|
||||
*
|
||||
* Copyright (C) 2005-2007 Pierre Ossman, 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PCI registers
|
||||
*/
|
||||
|
||||
#define PCI_SDHCI_IFPIO 0x00
|
||||
#define PCI_SDHCI_IFDMA 0x01
|
||||
#define PCI_SDHCI_IFVENDOR 0x02
|
||||
|
||||
#define PCI_SLOT_INFO 0x40 /* 8 bits */
|
||||
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
|
||||
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
|
||||
|
||||
/*
|
||||
* Controller registers
|
||||
*/
|
||||
|
||||
#define SDHCI_DMA_ADDRESS 0x00
|
||||
|
||||
#define SDHCI_BLOCK_SIZE 0x04
|
||||
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
|
||||
|
||||
#define SDHCI_BLOCK_COUNT 0x06
|
||||
|
||||
#define SDHCI_ARGUMENT 0x08
|
||||
|
||||
#define SDHCI_TRANSFER_MODE 0x0C
|
||||
#define SDHCI_TRNS_DMA 0x01
|
||||
#define SDHCI_TRNS_BLK_CNT_EN 0x02
|
||||
#define SDHCI_TRNS_ACMD12 0x04
|
||||
#define SDHCI_TRNS_READ 0x10
|
||||
#define SDHCI_TRNS_MULTI 0x20
|
||||
|
||||
#define SDHCI_COMMAND 0x0E
|
||||
#define SDHCI_CMD_RESP_MASK 0x03
|
||||
#define SDHCI_CMD_CRC 0x08
|
||||
#define SDHCI_CMD_INDEX 0x10
|
||||
#define SDHCI_CMD_DATA 0x20
|
||||
|
||||
#define SDHCI_CMD_RESP_NONE 0x00
|
||||
#define SDHCI_CMD_RESP_LONG 0x01
|
||||
#define SDHCI_CMD_RESP_SHORT 0x02
|
||||
#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
|
||||
|
||||
#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
|
||||
|
||||
#define SDHCI_RESPONSE 0x10
|
||||
|
||||
#define SDHCI_BUFFER 0x20
|
||||
|
||||
#define SDHCI_PRESENT_STATE 0x24
|
||||
#define SDHCI_CMD_INHIBIT 0x00000001
|
||||
#define SDHCI_DATA_INHIBIT 0x00000002
|
||||
#define SDHCI_DOING_WRITE 0x00000100
|
||||
#define SDHCI_DOING_READ 0x00000200
|
||||
#define SDHCI_SPACE_AVAILABLE 0x00000400
|
||||
#define SDHCI_DATA_AVAILABLE 0x00000800
|
||||
#define SDHCI_CARD_PRESENT 0x00010000
|
||||
#define SDHCI_WRITE_PROTECT 0x00080000
|
||||
|
||||
#define SDHCI_HOST_CONTROL 0x28
|
||||
#define SDHCI_CTRL_LED 0x01
|
||||
#define SDHCI_CTRL_4BITBUS 0x02
|
||||
#define SDHCI_CTRL_HISPD 0x04
|
||||
|
||||
#define SDHCI_POWER_CONTROL 0x29
|
||||
#define SDHCI_POWER_ON 0x01
|
||||
#define SDHCI_POWER_180 0x0A
|
||||
#define SDHCI_POWER_300 0x0C
|
||||
#define SDHCI_POWER_330 0x0E
|
||||
|
||||
#define SDHCI_BLOCK_GAP_CONTROL 0x2A
|
||||
|
||||
#define SDHCI_WAKE_UP_CONTROL 0x2B
|
||||
|
||||
#define SDHCI_CLOCK_CONTROL 0x2C
|
||||
#define SDHCI_DIVIDER_SHIFT 8
|
||||
#define SDHCI_CLOCK_CARD_EN 0x0004
|
||||
#define SDHCI_CLOCK_INT_STABLE 0x0002
|
||||
#define SDHCI_CLOCK_INT_EN 0x0001
|
||||
|
||||
#define SDHCI_TIMEOUT_CONTROL 0x2E
|
||||
|
||||
#define SDHCI_SOFTWARE_RESET 0x2F
|
||||
#define SDHCI_RESET_ALL 0x01
|
||||
#define SDHCI_RESET_CMD 0x02
|
||||
#define SDHCI_RESET_DATA 0x04
|
||||
|
||||
#define SDHCI_INT_STATUS 0x30
|
||||
#define SDHCI_INT_ENABLE 0x34
|
||||
#define SDHCI_SIGNAL_ENABLE 0x38
|
||||
#define SDHCI_INT_RESPONSE 0x00000001
|
||||
#define SDHCI_INT_DATA_END 0x00000002
|
||||
#define SDHCI_INT_DMA_END 0x00000008
|
||||
#define SDHCI_INT_SPACE_AVAIL 0x00000010
|
||||
#define SDHCI_INT_DATA_AVAIL 0x00000020
|
||||
#define SDHCI_INT_CARD_INSERT 0x00000040
|
||||
#define SDHCI_INT_CARD_REMOVE 0x00000080
|
||||
#define SDHCI_INT_CARD_INT 0x00000100
|
||||
#define SDHCI_INT_ERROR 0x00008000
|
||||
#define SDHCI_INT_TIMEOUT 0x00010000
|
||||
#define SDHCI_INT_CRC 0x00020000
|
||||
#define SDHCI_INT_END_BIT 0x00040000
|
||||
#define SDHCI_INT_INDEX 0x00080000
|
||||
#define SDHCI_INT_DATA_TIMEOUT 0x00100000
|
||||
#define SDHCI_INT_DATA_CRC 0x00200000
|
||||
#define SDHCI_INT_DATA_END_BIT 0x00400000
|
||||
#define SDHCI_INT_BUS_POWER 0x00800000
|
||||
#define SDHCI_INT_ACMD12ERR 0x01000000
|
||||
|
||||
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
|
||||
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
|
||||
|
||||
#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
|
||||
SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
|
||||
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
|
||||
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
||||
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
|
||||
SDHCI_INT_DATA_END_BIT)
|
||||
|
||||
#define SDHCI_ACMD12_ERR 0x3C
|
||||
|
||||
/* 3E-3F reserved */
|
||||
|
||||
#define SDHCI_CAPABILITIES 0x40
|
||||
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
|
||||
#define SDHCI_TIMEOUT_CLK_SHIFT 0
|
||||
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
|
||||
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
|
||||
#define SDHCI_CLOCK_BASE_SHIFT 8
|
||||
#define SDHCI_MAX_BLOCK_MASK 0x00030000
|
||||
#define SDHCI_MAX_BLOCK_SHIFT 16
|
||||
#define SDHCI_CAN_DO_HISPD 0x00200000
|
||||
#define SDHCI_CAN_DO_DMA 0x00400000
|
||||
#define SDHCI_CAN_VDD_330 0x01000000
|
||||
#define SDHCI_CAN_VDD_300 0x02000000
|
||||
#define SDHCI_CAN_VDD_180 0x04000000
|
||||
|
||||
/* 44-47 reserved for more caps */
|
||||
|
||||
#define SDHCI_MAX_CURRENT 0x48
|
||||
|
||||
/* 4C-4F reserved for more max current */
|
||||
|
||||
/* 50-FB reserved */
|
||||
|
||||
#define SDHCI_SLOT_INT_STATUS 0xFC
|
||||
|
||||
#define SDHCI_HOST_VERSION 0xFE
|
||||
#define SDHCI_VENDOR_VER_MASK 0xFF00
|
||||
#define SDHCI_VENDOR_VER_SHIFT 8
|
||||
#define SDHCI_SPEC_VER_MASK 0x00FF
|
||||
#define SDHCI_SPEC_VER_SHIFT 0
|
||||
|
||||
struct sdhci_chip;
|
||||
|
||||
struct sdhci_host {
|
||||
struct sdhci_chip *chip;
|
||||
struct mmc_host *mmc; /* MMC structure */
|
||||
|
||||
spinlock_t lock; /* Mutex */
|
||||
|
||||
int flags; /* Host attributes */
|
||||
#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */
|
||||
#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */
|
||||
|
||||
unsigned int max_clk; /* Max possible freq (MHz) */
|
||||
unsigned int timeout_clk; /* Timeout freq (KHz) */
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
unsigned short power; /* Current voltage */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
int data_early:1; /* Data finished before cmd */
|
||||
|
||||
struct scatterlist *cur_sg; /* We're working on this */
|
||||
int num_sg; /* Entries left */
|
||||
int offset; /* Offset into current sg */
|
||||
int remain; /* Bytes left in current */
|
||||
|
||||
char slot_descr[20]; /* Name for reservations */
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
int bar; /* PCI BAR index */
|
||||
unsigned long addr; /* Bus address */
|
||||
void __iomem * ioaddr; /* Mapped address */
|
||||
|
||||
struct tasklet_struct card_tasklet; /* Tasklet structures */
|
||||
struct tasklet_struct finish_tasklet;
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
};
|
||||
|
||||
struct sdhci_chip {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
unsigned long quirks;
|
||||
|
||||
int num_slots; /* Slots on controller */
|
||||
struct sdhci_host *hosts[0]; /* Pointers to hosts */
|
||||
};
|
||||
1095
drivers/mmc/host/tifm_sd.c
Normal file
1095
drivers/mmc/host/tifm_sd.c
Normal file
File diff suppressed because it is too large
Load Diff
2047
drivers/mmc/host/wbsd.c
Normal file
2047
drivers/mmc/host/wbsd.c
Normal file
File diff suppressed because it is too large
Load Diff
185
drivers/mmc/host/wbsd.h
Normal file
185
drivers/mmc/host/wbsd.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* linux/drivers/mmc/host/wbsd.h - Winbond W83L51xD SD/MMC driver
|
||||
*
|
||||
* Copyright (C) 2004-2007 Pierre Ossman, 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
|
||||
#define LOCK_CODE 0xAA
|
||||
|
||||
#define WBSD_CONF_SWRST 0x02
|
||||
#define WBSD_CONF_DEVICE 0x07
|
||||
#define WBSD_CONF_ID_HI 0x20
|
||||
#define WBSD_CONF_ID_LO 0x21
|
||||
#define WBSD_CONF_POWER 0x22
|
||||
#define WBSD_CONF_PME 0x23
|
||||
#define WBSD_CONF_PMES 0x24
|
||||
|
||||
#define WBSD_CONF_ENABLE 0x30
|
||||
#define WBSD_CONF_PORT_HI 0x60
|
||||
#define WBSD_CONF_PORT_LO 0x61
|
||||
#define WBSD_CONF_IRQ 0x70
|
||||
#define WBSD_CONF_DRQ 0x74
|
||||
|
||||
#define WBSD_CONF_PINS 0xF0
|
||||
|
||||
#define DEVICE_SD 0x03
|
||||
|
||||
#define WBSD_PINS_DAT3_HI 0x20
|
||||
#define WBSD_PINS_DAT3_OUT 0x10
|
||||
#define WBSD_PINS_GP11_HI 0x04
|
||||
#define WBSD_PINS_DETECT_GP11 0x02
|
||||
#define WBSD_PINS_DETECT_DAT3 0x01
|
||||
|
||||
#define WBSD_CMDR 0x00
|
||||
#define WBSD_DFR 0x01
|
||||
#define WBSD_EIR 0x02
|
||||
#define WBSD_ISR 0x03
|
||||
#define WBSD_FSR 0x04
|
||||
#define WBSD_IDXR 0x05
|
||||
#define WBSD_DATAR 0x06
|
||||
#define WBSD_CSR 0x07
|
||||
|
||||
#define WBSD_EINT_CARD 0x40
|
||||
#define WBSD_EINT_FIFO_THRE 0x20
|
||||
#define WBSD_EINT_CRC 0x10
|
||||
#define WBSD_EINT_TIMEOUT 0x08
|
||||
#define WBSD_EINT_PROGEND 0x04
|
||||
#define WBSD_EINT_BUSYEND 0x02
|
||||
#define WBSD_EINT_TC 0x01
|
||||
|
||||
#define WBSD_INT_PENDING 0x80
|
||||
#define WBSD_INT_CARD 0x40
|
||||
#define WBSD_INT_FIFO_THRE 0x20
|
||||
#define WBSD_INT_CRC 0x10
|
||||
#define WBSD_INT_TIMEOUT 0x08
|
||||
#define WBSD_INT_PROGEND 0x04
|
||||
#define WBSD_INT_BUSYEND 0x02
|
||||
#define WBSD_INT_TC 0x01
|
||||
|
||||
#define WBSD_FIFO_EMPTY 0x80
|
||||
#define WBSD_FIFO_FULL 0x40
|
||||
#define WBSD_FIFO_EMTHRE 0x20
|
||||
#define WBSD_FIFO_FUTHRE 0x10
|
||||
#define WBSD_FIFO_SZMASK 0x0F
|
||||
|
||||
#define WBSD_MSLED 0x20
|
||||
#define WBSD_POWER_N 0x10
|
||||
#define WBSD_WRPT 0x04
|
||||
#define WBSD_CARDPRESENT 0x01
|
||||
|
||||
#define WBSD_IDX_CLK 0x01
|
||||
#define WBSD_IDX_PBSMSB 0x02
|
||||
#define WBSD_IDX_TAAC 0x03
|
||||
#define WBSD_IDX_NSAC 0x04
|
||||
#define WBSD_IDX_PBSLSB 0x05
|
||||
#define WBSD_IDX_SETUP 0x06
|
||||
#define WBSD_IDX_DMA 0x07
|
||||
#define WBSD_IDX_FIFOEN 0x08
|
||||
#define WBSD_IDX_STATUS 0x10
|
||||
#define WBSD_IDX_RSPLEN 0x1E
|
||||
#define WBSD_IDX_RESP0 0x1F
|
||||
#define WBSD_IDX_RESP1 0x20
|
||||
#define WBSD_IDX_RESP2 0x21
|
||||
#define WBSD_IDX_RESP3 0x22
|
||||
#define WBSD_IDX_RESP4 0x23
|
||||
#define WBSD_IDX_RESP5 0x24
|
||||
#define WBSD_IDX_RESP6 0x25
|
||||
#define WBSD_IDX_RESP7 0x26
|
||||
#define WBSD_IDX_RESP8 0x27
|
||||
#define WBSD_IDX_RESP9 0x28
|
||||
#define WBSD_IDX_RESP10 0x29
|
||||
#define WBSD_IDX_RESP11 0x2A
|
||||
#define WBSD_IDX_RESP12 0x2B
|
||||
#define WBSD_IDX_RESP13 0x2C
|
||||
#define WBSD_IDX_RESP14 0x2D
|
||||
#define WBSD_IDX_RESP15 0x2E
|
||||
#define WBSD_IDX_RESP16 0x2F
|
||||
#define WBSD_IDX_CRCSTATUS 0x30
|
||||
#define WBSD_IDX_ISR 0x3F
|
||||
|
||||
#define WBSD_CLK_375K 0x00
|
||||
#define WBSD_CLK_12M 0x01
|
||||
#define WBSD_CLK_16M 0x02
|
||||
#define WBSD_CLK_24M 0x03
|
||||
|
||||
#define WBSD_DATA_WIDTH 0x01
|
||||
|
||||
#define WBSD_DAT3_H 0x08
|
||||
#define WBSD_FIFO_RESET 0x04
|
||||
#define WBSD_SOFT_RESET 0x02
|
||||
#define WBSD_INC_INDEX 0x01
|
||||
|
||||
#define WBSD_DMA_SINGLE 0x02
|
||||
#define WBSD_DMA_ENABLE 0x01
|
||||
|
||||
#define WBSD_FIFOEN_EMPTY 0x20
|
||||
#define WBSD_FIFOEN_FULL 0x10
|
||||
#define WBSD_FIFO_THREMASK 0x0F
|
||||
|
||||
#define WBSD_BLOCK_READ 0x80
|
||||
#define WBSD_BLOCK_WRITE 0x40
|
||||
#define WBSD_BUSY 0x20
|
||||
#define WBSD_CARDTRAFFIC 0x04
|
||||
#define WBSD_SENDCMD 0x02
|
||||
#define WBSD_RECVRES 0x01
|
||||
|
||||
#define WBSD_RSP_SHORT 0x00
|
||||
#define WBSD_RSP_LONG 0x01
|
||||
|
||||
#define WBSD_CRC_MASK 0x1F
|
||||
#define WBSD_CRC_OK 0x05 /* S010E (00101) */
|
||||
#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */
|
||||
|
||||
#define WBSD_DMA_SIZE 65536
|
||||
|
||||
struct wbsd_host
|
||||
{
|
||||
struct mmc_host* mmc; /* MMC structure */
|
||||
|
||||
spinlock_t lock; /* Mutex */
|
||||
|
||||
int flags; /* Driver states */
|
||||
|
||||
#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */
|
||||
#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */
|
||||
|
||||
struct mmc_request* mrq; /* Current request */
|
||||
|
||||
u8 isr; /* Accumulated ISR */
|
||||
|
||||
struct scatterlist* cur_sg; /* Current SG entry */
|
||||
unsigned int num_sg; /* Number of entries left */
|
||||
|
||||
unsigned int offset; /* Offset into current entry */
|
||||
unsigned int remain; /* Data left in curren entry */
|
||||
|
||||
char* dma_buffer; /* ISA DMA buffer */
|
||||
dma_addr_t dma_addr; /* Physical address for same */
|
||||
|
||||
int firsterr; /* See fifo functions */
|
||||
|
||||
u8 clk; /* Current clock speed */
|
||||
unsigned char bus_width; /* Current bus width */
|
||||
|
||||
int config; /* Config port */
|
||||
u8 unlock_code; /* Code to unlock config */
|
||||
|
||||
int chip_id; /* ID of controller */
|
||||
|
||||
int base; /* I/O port base */
|
||||
int irq; /* Interrupt */
|
||||
int dma; /* DMA channel */
|
||||
|
||||
struct tasklet_struct card_tasklet; /* Tasklet structures */
|
||||
struct tasklet_struct fifo_tasklet;
|
||||
struct tasklet_struct crc_tasklet;
|
||||
struct tasklet_struct timeout_tasklet;
|
||||
struct tasklet_struct finish_tasklet;
|
||||
|
||||
struct timer_list ignore_timer; /* Ignore detection timer */
|
||||
};
|
||||
Reference in New Issue
Block a user