Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

mmc: Add JZ4770 driver from Ingenic

Taken from linux-2.6.31.3-jz-20110809-FR1.patch.gz.
Only the drivers relevant to JZ4770 are included.

The capacity hacks controlled by CONFIG_JZ_BOOT_FROM_MSC0 were omitted.
All except one were within "#if 0" blocks.

Greatly reduced amount of MMC/SD Kconfig options:
These all controlled things that should be specified in the platform data
structs in board support file instead.

Kconfig cleanups, make it more like JZ4740:
Renamed MMC_JZSOC to MMC_JZ4770.
Improved descriptions and help texts.
Only show JZ_SYSTEM_AT_CARD if the driver is selected.

Removed unused function. The comment said "for Atheros wifi", so it is
probably intended for some SDIO card. However, we don't have it so it's
better to remove the code than keep it with us unmaintained.

Removed feature to write-protect part of the SD card.
I don't see why this would have to be a driver feature.

Converted to use module_platform_driver(). It previously used
subsys_initcall(), but the MMC subsystem doesn't have to be
initialized early.

Return error code instead of starting an endless loop.

Register definitions are now part of the driver, not part of the
platform headers, since only this one driver should be touching
the MSC registers.

Various cleanups, such as removing includes where a struct declaration
suffices and removing function pointers that always point to the same
function.

Removed unnecessary BUG_ON check that triggered when the physical
address is in highmem.

Stripped header so only platform data definition remains.

JZ4770: Customize GPIO pins using data instead of callbacks.
Used an approach similar to the JZ4740 driver. The platform data
describes which GPIO pins are used and whether they are high or low
active, the driver will then manage those pins.

Use two-edge interrupt trigger. This feature is already emulated in
our interrupt controller driver, no need to emulate it again in the
MMC driver.

Removed PIO support. We only want to use DMA in production and having
the extra code present complicates refactoring.

Declare jz_mmc_detect() in header.

Handle power management using struct dev_pm_ops.

Removed debug code from jz_mmc_main.c.

Inline jz_mmc_controller_(de)init.

Renamed "plat" field to the more conventional "pdata".

Clean up probe function.
Make it more like the jz4740 driver's probe function.
Also use devres where possible.
No need to set drvdata to NULL, the kernel does that for us.
And don't pretend that DMA is optional.

Store struct jz_mmc_host as drvdata.
The original driver stored struct mmc_host instead.
Also removed redundant NULL checks: if the drvdata is NULL, the driver
is not operational and these functions will not be called.

Handle clock and power more like JZ4740 driver.
The set_ios handler is where it all happens.
For some reason, the driver hangs at initialisation if the reset is
performed with disabled device clock. I haven't figured out yet why,
but I have reduced the time that the clock is enabled to a minimum.

Eliminate CONFIG_JZ_SYSTEM_AT_CARD.
Instead of a compile-time option and hardcoded to MSC0, look at the
platform data: if there is no status_irq defined, treat it as an
always-present card.

Refuse to probe without platform data.
There is really no way we can run without knowing which GPIO pins to
use, so don't even try. By bailing out early we can avoid checks later.
Also print an error message if we refuse to probe because of an invalid
device ID.

Put change detection IRQ request in its own function.
Also changed the order of host and pdev arguments for the other request
functions, to be consistent with the JZ4740 driver.

Leave change detection to generic MMC code.

Removed unused fields from platform data.

Removed redundant max_bus_width platform data field.
Instead, set bus width capabilities based on the bus_width field.

Use slot-gpio implementations of read-only and change detect.
Also updated GPIO labels to use the same name that slot-gpio does
(device name instead of host name).

Enable erase capability.

Added "nonremovable" flag to platform data.

Removed semaphore around requests. The MMC framework already serializes
requests, there is no need to enforce this again in the host driver.

Removed unused #defines from internal header.

Don't log when a nonremovable card lacks change detect.

Use standard name for DMA descriptor struct.
Instead of using a macro, use the name directly, including "struct".
This is required now that the platform header no longer has a typedef.
Also moved inclusion of the DMA header to jz_mmc_dma.c, since that is
the only source file that actually has to know about DMA.

Include DMAC header explicitly.

Use generic clock API to get rate.
Also use the clock that actually belongs to the host in question,
instead of a hardcoded MSC0.
  • Loading branch information...
commit 0a1be0ed302f2cb7f4c8a7b93402f1fcd102e1d0 1 parent 6515d08
@mthuurne mthuurne authored
View
39 arch/mips/include/asm/mach-jz4770/mmc.h
@@ -0,0 +1,39 @@
+/*
+ * arch/mips/include/asm/mach-jz4770/mmc.h
+ *
+ * JZ4770 MMC/SD Controller platform data
+ *
+ * Copyright (C) 2010 Ingenic Semiconductor Co., Ltd.
+ */
+
+#ifndef __ASM_MACH_JZ4770_MMC_H__
+#define __ASM_MACH_JZ4770_MMC_H__
+
+struct jz_mmc_platform_data {
+ unsigned int ocr_mask; /* available voltages */
+ unsigned char support_sdio;
+ unsigned char bus_width;
+
+ int gpio_card_detect;
+ int gpio_read_only;
+ int gpio_power;
+ unsigned card_detect_active_low:1;
+ unsigned read_only_active_low:1;
+ unsigned power_active_low:1;
+
+ /*
+ * Set this if the card cannot be physically removed from the device.
+ */
+ unsigned nonremovable:1;
+
+ /*
+ * Each of the three MMC/SD Controllers can either use a private pin
+ * section with 4 data pins, or a shared pin section with 8 data pins.
+ * Set this flag if this MSC is the one using the shared pins.
+ * Note that there are 8 data pins available, but bus_width determines
+ * how many are actually used.
+ */
+ unsigned use_shared_8bit_pins:1;
+};
+
+#endif /* __ASM_MACH_JZ4770_MMC_H__ */
View
9 drivers/mmc/host/Kconfig
@@ -603,6 +603,15 @@ config MMC_JZ4740
If you have a board based on such a SoC and with a SD/MMC slot,
say Y or M here.
+config MMC_JZ4770
+ tristate "JZ4770 SD/Multimedia Card Interface support"
+ depends on MACH_JZ4770
+ help
+ This selects support for the SD/MMC controller on Ingenic JZ4770
+ SoCs.
+ If you have a board based on such a SoC and with a SD/MMC slot,
+ say Y or M here.
+
config MMC_VUB300
tristate "VUB300 USB to SDIO/SD/MMC Host Controller support"
depends on USB
View
1  drivers/mmc/host/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
+obj-$(CONFIG_MMC_JZ4770) += jzmmc/
obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
View
8 drivers/mmc/host/jzmmc/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for MMC/SD host controller drivers
+#
+
+obj-$(CONFIG_MMC_JZ4770) += jz_mmc_main.o \
+ jz_mmc_msc.o \
+ jz_mmc_dma.o \
+ jz_mmc_gpio.o
View
178 drivers/mmc/host/jzmmc/include/chip-msc.h
@@ -0,0 +1,178 @@
+/*
+ * drivers/mmc/host/jzmmc/include/chip-msc.h
+ *
+ * JZ4770 MSC register definition.
+ *
+ * Copyright (C) 2010 Ingenic Semiconductor Co., Ltd.
+ */
+
+#ifndef __CHIP_MSC_H__
+#define __CHIP_MSC_H__
+
+#include <asm/mach-jz4770/jz4770misc.h>
+
+
+#define MSC0_BASE 0xB0021000
+#define MSC1_BASE 0xB0022000
+#define MSC2_BASE 0xB0023000
+
+
+/* n = 0, 1 (MSC0, MSC1) */
+#define MSC_STRPCL(n) (MSC0_BASE + (n)*0x1000 + 0x000)
+#define MSC_STAT(n) (MSC0_BASE + (n)*0x1000 + 0x004)
+#define MSC_CLKRT(n) (MSC0_BASE + (n)*0x1000 + 0x008)
+#define MSC_CMDAT(n) (MSC0_BASE + (n)*0x1000 + 0x00C)
+#define MSC_RESTO(n) (MSC0_BASE + (n)*0x1000 + 0x010)
+#define MSC_RDTO(n) (MSC0_BASE + (n)*0x1000 + 0x014)
+#define MSC_BLKLEN(n) (MSC0_BASE + (n)*0x1000 + 0x018)
+#define MSC_NOB(n) (MSC0_BASE + (n)*0x1000 + 0x01C)
+#define MSC_SNOB(n) (MSC0_BASE + (n)*0x1000 + 0x020)
+#define MSC_IMASK(n) (MSC0_BASE + (n)*0x1000 + 0x024)
+#define MSC_IREG(n) (MSC0_BASE + (n)*0x1000 + 0x028)
+#define MSC_CMD(n) (MSC0_BASE + (n)*0x1000 + 0x02C)
+#define MSC_ARG(n) (MSC0_BASE + (n)*0x1000 + 0x030)
+#define MSC_RES(n) (MSC0_BASE + (n)*0x1000 + 0x034)
+#define MSC_RXFIFO(n) (MSC0_BASE + (n)*0x1000 + 0x038)
+#define MSC_TXFIFO(n) (MSC0_BASE + (n)*0x1000 + 0x03C)
+#define MSC_LPM(n) (MSC0_BASE + (n)*0x1000 + 0x040)
+
+#define REG_MSC_STRPCL(n) REG16(MSC_STRPCL(n))
+#define REG_MSC_STAT(n) REG32(MSC_STAT(n))
+#define REG_MSC_CLKRT(n) REG16(MSC_CLKRT(n))
+#define REG_MSC_CMDAT(n) REG32(MSC_CMDAT(n))
+#define REG_MSC_RESTO(n) REG16(MSC_RESTO(n))
+#define REG_MSC_RDTO(n) REG32(MSC_RDTO(n))
+#define REG_MSC_BLKLEN(n) REG16(MSC_BLKLEN(n))
+#define REG_MSC_NOB(n) REG16(MSC_NOB(n))
+#define REG_MSC_SNOB(n) REG16(MSC_SNOB(n))
+#define REG_MSC_IMASK(n) REG32(MSC_IMASK(n))
+#define REG_MSC_IREG(n) REG16(MSC_IREG(n))
+#define REG_MSC_CMD(n) REG8(MSC_CMD(n))
+#define REG_MSC_ARG(n) REG32(MSC_ARG(n))
+#define REG_MSC_RES(n) REG16(MSC_RES(n))
+#define REG_MSC_RXFIFO(n) REG32(MSC_RXFIFO(n))
+#define REG_MSC_TXFIFO(n) REG32(MSC_TXFIFO(n))
+#define REG_MSC_LPM(n) REG32(MSC_LPM(n))
+
+/* MSC Clock and Control Register (MSC_STRPCL) */
+#define MSC_STRPCL_SEND_CCSD (1 << 15) /*send command completion signal disable to ceata */
+#define MSC_STRPCL_SEND_AS_CCSD (1 << 14) /*send internally generated stop after sending ccsd */
+#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7)
+#define MSC_STRPCL_EXIT_TRANSFER (1 << 6)
+#define MSC_STRPCL_START_READWAIT (1 << 5)
+#define MSC_STRPCL_STOP_READWAIT (1 << 4)
+#define MSC_STRPCL_RESET (1 << 3)
+#define MSC_STRPCL_START_OP (1 << 2)
+#define MSC_STRPCL_CLOCK_CONTROL_BIT 0
+#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT)
+ #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */
+ #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */
+
+/* MSC Status Register (MSC_STAT) */
+#define MSC_STAT_AUTO_CMD_DONE (1 << 31) /*12 is internally generated by controller has finished */
+#define MSC_STAT_IS_RESETTING (1 << 15)
+#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14)
+#define MSC_STAT_PRG_DONE (1 << 13)
+#define MSC_STAT_DATA_TRAN_DONE (1 << 12)
+#define MSC_STAT_END_CMD_RES (1 << 11)
+#define MSC_STAT_DATA_FIFO_AFULL (1 << 10)
+#define MSC_STAT_IS_READWAIT (1 << 9)
+#define MSC_STAT_CLK_EN (1 << 8)
+#define MSC_STAT_DATA_FIFO_FULL (1 << 7)
+#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6)
+#define MSC_STAT_CRC_RES_ERR (1 << 5)
+#define MSC_STAT_CRC_READ_ERROR (1 << 4)
+#define MSC_STAT_CRC_WRITE_ERROR_BIT 2
+#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT)
+ #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */
+ #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */
+ #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */
+#define MSC_STAT_TIME_OUT_RES (1 << 1)
+#define MSC_STAT_TIME_OUT_READ (1 << 0)
+
+/* MSC Bus Clock Control Register (MSC_CLKRT) */
+#define MSC_CLKRT_CLK_RATE_BIT 0
+#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT)
+#define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */
+#define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */
+
+/* MSC Command Sequence Control Register (MSC_CMDAT) */
+#define MSC_CMDAT_CCS_EXPECTED (1 << 31) /* interrupts are enabled in ce-ata */
+#define MSC_CMDAT_READ_CEATA (1 << 30)
+#define MSC_CMDAT_SDIO_PRDT (1 << 17) /* exact 2 cycle */
+#define MSC_CMDAT_SEND_AS_STOP (1 << 16)
+#define MSC_CMDAT_RTRG_BIT 14
+#define MSC_CMDAT_RTRG_EQUALT_8 (0x0 << MSC_CMDAT_RTRG_BIT) /*reset value*/
+ #define MSC_CMDAT_RTRG_EQUALT_16 (0x1 << MSC_CMDAT_RTRG_BIT)
+ #define MSC_CMDAT_RTRG_EQUALT_24 (0x2 << MSC_CMDAT_RTRG_BIT)
+#define MSC_CMDAT_TTRG_BIT 12
+#define MSC_CMDAT_TTRG_LESS_8 (0x0 << MSC_CMDAT_TTRG_BIT) /*reset value*/
+ #define MSC_CMDAT_TTRG_LESS_16 (0x1 << MSC_CMDAT_TTRG_BIT)
+ #define MSC_CMDAT_TTRG_LESS_24 (0x2 << MSC_CMDAT_TTRG_BIT)
+#define MSC_CMDAT_STOP_ABORT (1 << 11)
+#define MSC_CMDAT_BUS_WIDTH_BIT 9
+#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT)
+ #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */
+ #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */
+ #define MSC_CMDAT_BUS_WIDTH_8BIT (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) /* 8-bit data bus */
+#define MSC_CMDAT_DMA_EN (1 << 8)
+#define MSC_CMDAT_INIT (1 << 7)
+#define MSC_CMDAT_BUSY (1 << 6)
+#define MSC_CMDAT_STREAM_BLOCK (1 << 5)
+#define MSC_CMDAT_WRITE (1 << 4)
+#define MSC_CMDAT_READ (0 << 4)
+#define MSC_CMDAT_DATA_EN (1 << 3)
+#define MSC_CMDAT_RESPONSE_BIT 0
+#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT)
+ #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */
+ #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */
+ #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */
+ #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */
+ #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */
+ #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */
+ #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */
+ #define MSC_CMDAT_RESRONSE_R7 (0x7 << MSC_CMDAT_RESPONSE_BIT) /* Format R7 */
+
+/* MSC Interrupts Mask Register (MSC_IMASK) */
+#define MSC_IMASK_AUTO_CMD_DONE (1 << 15)
+#define MSC_IMASK_DATA_FIFO_FULL (1 << 14)
+#define MSC_IMASK_DATA_FIFO_EMP (1 << 13)
+#define MSC_IMASK_CRC_RES_ERR (1 << 12)
+#define MSC_IMASK_CRC_READ_ERR (1 << 11)
+#define MSC_IMASK_CRC_WRITE_ERR (1 << 10)
+#define MSC_IMASK_TIME_OUT_RES (1 << 9)
+#define MSC_IMASK_TIME_OUT_READ (1 << 8)
+#define MSC_IMASK_SDIO (1 << 7)
+#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6)
+#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5)
+#define MSC_IMASK_END_CMD_RES (1 << 2)
+#define MSC_IMASK_PRG_DONE (1 << 1)
+#define MSC_IMASK_DATA_TRAN_DONE (1 << 0)
+
+/* MSC Interrupts Status Register (MSC_IREG) */
+#define MSC_IREG_AUTO_CMD_DONE (1 << 15)
+#define MSC_IREG_DATA_FIFO_FULL (1 << 14)
+#define MSC_IREG_DATA_FIFO_EMP (1 << 13)
+#define MSC_IREG_CRC_RES_ERR (1 << 12)
+#define MSC_IREG_CRC_READ_ERR (1 << 11)
+#define MSC_IREG_CRC_WRITE_ERR (1 << 10)
+#define MSC_IREG_TIMEOUT_RES (1 << 9)
+#define MSC_IREG_TIMEOUT_READ (1 << 8)
+#define MSC_IREG_SDIO (1 << 7)
+#define MSC_IREG_TXFIFO_WR_REQ (1 << 6)
+#define MSC_IREG_RXFIFO_RD_REQ (1 << 5)
+#define MSC_IREG_END_CMD_RES (1 << 2)
+#define MSC_IREG_PRG_DONE (1 << 1)
+#define MSC_IREG_DATA_TRAN_DONE (1 << 0)
+
+/* MSC Low Power Mode Register (MSC_LPM) */
+#define MSC_SET_HISPD (1 << 31)
+#define MSC_SET_LPM (1 << 0)
+
+#endif /* __CHIP_MSC_H__ */
View
28 drivers/mmc/host/jzmmc/include/jz_mmc_dma.h
@@ -0,0 +1,28 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/dma/jz_mmc_dma.h
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#ifndef __JZ_MMC_DMA_H__
+#define __JZ_MMC_DMA_H__
+
+struct jz_mmc_host;
+
+void jz_mmc_start_dma(struct jz_mmc_host *host);
+void jz_mmc_stop_dma(struct jz_mmc_host *host);
+
+void jz_mmc_start_normal_dma(struct jz_mmc_host *host, unsigned long phyaddr,
+ int count, int mode, int ds);
+void jz_mmc_start_scatter_dma(int chan, struct jz_mmc_host *host,
+ struct scatterlist *sg, unsigned int sg_len,
+ int mode);
+
+int jz_mmc_init_dma(struct jz_mmc_host *host);
+void jz_mmc_deinit_dma(struct jz_mmc_host *host);
+
+#endif /* __JZ_MMC_DMA_H__ */
View
20 drivers/mmc/host/jzmmc/include/jz_mmc_gpio.h
@@ -0,0 +1,20 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/gpio/jz_mmc_gpio.h
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#ifndef __JZ_MMC_GPIO_H__
+#define __JZ_MMC_GPIO_H__
+
+struct jz_mmc_host;
+struct platform_device;
+
+int jz_mmc_gpio_init(struct jz_mmc_host *host, struct platform_device *pdev);
+void jz_mmc_gpio_deinit(struct jz_mmc_host *host, struct platform_device *pdev);
+
+#endif /* __JZ_MMC_GPIO_H__ */
View
71 drivers/mmc/host/jzmmc/include/jz_mmc_host.h
@@ -0,0 +1,71 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/jz_mmc_host.h
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#ifndef __JZ_MMC_HOST_H__
+#define __JZ_MMC_HOST_H__
+
+#define USE_DMA_DESC
+//#define USE_DMA_UNCACHE
+//#define MSC_DEBUG_DMA
+
+#define USE_DMA_BUSRT_64
+
+#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */
+#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */
+#define SD_CLOCK_HIGH 48000000 /* 24 MHz for SD Cards */
+#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */
+
+
+struct clk;
+
+struct jz_mmc_host {
+ struct mmc_host *mmc;
+
+ struct clk *clk;
+
+ /* host resources */
+ void __iomem *base;
+ unsigned int pdev_id;
+ int irq;
+ int dma_id;
+ struct jz_mmc_platform_data *pdata;
+
+ /* mmc request related */
+ unsigned int cmdat;
+ struct mmc_request *curr_mrq;
+ int curr_res_type;
+
+ /* data transter related */
+ struct {
+ int len;
+ int dir;
+ int channel;
+ } dma;
+#ifdef USE_DMA_DESC
+#ifdef MSC_DEBUG_DMA
+ int num_desc;
+ int last_direction;
+#endif
+ struct jz_dma_desc_8word *dma_desc;
+#endif
+ wait_queue_head_t data_wait_queue;
+ volatile int data_ack;
+ volatile int data_err;
+
+ /* card detect related */
+ int card_detect_irq;
+
+ /* labels for gpio pins */
+ char *label_power;
+};
+
+void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq);
+
+#endif /* __JZ_MMC_HOST_H__ */
View
27 drivers/mmc/host/jzmmc/include/jz_mmc_msc.h
@@ -0,0 +1,27 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/msc/jz_mmc_msc.c
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#ifndef __JZ_MMC_MSC_H__
+#define __JZ_MMC_MSC_H__
+
+struct jz_mmc_host;
+
+void jz_mmc_data_start(struct jz_mmc_host *host);
+
+void jz_mmc_reset(struct jz_mmc_host *host);
+
+void jz_mmc_set_clock(struct jz_mmc_host *host, int rate);
+
+void jz_mmc_execute_cmd(struct jz_mmc_host *host);
+
+int jz_mmc_msc_init(struct jz_mmc_host *host);
+void jz_mmc_msc_deinit(struct jz_mmc_host *host);
+
+#endif /* __JZ_MMC_MSC_H__ */
View
540 drivers/mmc/host/jzmmc/jz_mmc_dma.c
@@ -0,0 +1,540 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/dma/jz_mmc_dma.c
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-jz4770/dma.h>
+#include <asm/mach-jz4770/jz4770dmac.h>
+
+#include "include/chip-msc.h"
+#include "include/jz_mmc_dma.h"
+#include "include/jz_mmc_host.h"
+
+
+void jz_mmc_stop_dma(struct jz_mmc_host *host)
+{
+ u32 old_counter = REG_DMAC_DTCR(host->dma.channel);
+ u32 cur_counter;
+
+ //WARN(1, "mmc%d called %s\n", host->pdev_id, __FUNCTION__);
+ /* wait for the counter not change */
+ while (1) { /* wait forever, even when the card is removed */
+ schedule_timeout(3); /* 30ms */
+ cur_counter = REG_DMAC_DTCR(host->dma.channel);
+ if (cur_counter == old_counter)
+ break;
+ old_counter = cur_counter;
+ }
+
+ // Stop all
+ REG_DMAC_DCCSR(host->dma.channel) = 0;
+
+ // Clear all
+ REG_DMAC_DCMD(host->dma.channel) = 0;
+ REG_DMAC_DSAR(host->dma.channel) = 0;
+ REG_DMAC_DTAR(host->dma.channel) = 0;
+ REG_DMAC_DTCR(host->dma.channel) = 0;
+ REG_DMAC_DRSR(host->dma.channel) = 0;
+ REG_DMAC_DDA(host->dma.channel) = 0;
+}
+
+static inline int best_burst_size(unsigned int dma_len) {
+#ifdef USE_DMA_BUSRT_64
+ if (dma_len % 64 == 0)
+ return 64;
+#endif
+
+ if (dma_len % 32 == 0)
+ return 32;
+
+ if (dma_len % 16 == 0)
+ return 16;
+
+#ifdef USE_DMA_BUSRT_64
+ if (dma_len > 64)
+ return 64;
+#endif
+
+ if (dma_len > 32)
+ return 32;
+
+ if (dma_len > 16)
+ return 16;
+
+ return 4;
+}
+
+#ifndef DMAC_DRSR_RS_MSC2OUT
+#define DMAC_DRSR_RS_MSC2OUT 0
+#endif
+
+#ifndef DMAC_DRSR_RS_MSC2IN
+#define DMAC_DRSR_RS_MSC2IN 0
+#endif
+
+#define MSC_SET_OUT_REQ_SRC(ctrler_id, dst) \
+ do { \
+ switch((ctrler_id)) { \
+ case 0: \
+ (dst) = DMAC_DRSR_RS_MSC0OUT; \
+ break; \
+ case 1: \
+ (dst) = DMAC_DRSR_RS_MSC1OUT; \
+ break; \
+ case 2: \
+ (dst) = DMAC_DRSR_RS_MSC2OUT; \
+ break; \
+ default: \
+ BUG(); \
+ } \
+ } while(0)
+
+
+#define MSC_SET_IN_REQ_SRC(ctrler_id, dst) \
+ do { \
+ switch((ctrler_id)) { \
+ case 0: \
+ (dst) = DMAC_DRSR_RS_MSC0IN; \
+ break; \
+ case 1: \
+ (dst) = DMAC_DRSR_RS_MSC1IN; \
+ break; \
+ case 2: \
+ (dst) = DMAC_DRSR_RS_MSC2IN; \
+ break; \
+ default: \
+ BUG(); \
+ } \
+ } while (0)
+
+#ifdef USE_DMA_DESC
+static void sg_to_desc(struct scatterlist *sgentry,
+ struct jz_dma_desc_8word *first_desc,
+ int *desc_pos /* IN OUT */, int mode, int ctrl_id,
+ struct jz_mmc_host *host) {
+ struct jz_dma_desc_8word *desc = NULL;
+ int pos = *desc_pos;
+ unsigned int ds = 32; /* ehh, 32byte is the best */
+ unsigned int next;
+ dma_addr_t dma_addr;
+ unsigned int dma_len;
+ unsigned int head_unalign_size = 0; /* ds = 4byte */
+ unsigned int aligned_size = 0;
+ unsigned int tail_unalign_size = 0; /* ds = 4byte */
+ dma_addr_t best_dma_addr = 0;
+ dma_addr_t last_best_dma_addr = 0;
+ dma_addr_t dma_desc_phys_addr = CPHYSADDR((unsigned long)first_desc);
+
+ BUG_ON((unsigned long)sg_virt(sgentry) & 0x3);
+
+ dma_addr = sg_dma_address(sgentry);
+ dma_len = sg_dma_len(sgentry);
+
+ BUG_ON(dma_len % 4); /* we do NOT support transfer size < 4byte */
+
+ ds = best_burst_size(dma_len);
+ best_dma_addr = (dma_addr + ds - 1) & ~(ds - 1);
+ last_best_dma_addr = (dma_addr + dma_len) & ~(ds - 1);
+
+ head_unalign_size = best_dma_addr - dma_addr;
+ tail_unalign_size = dma_addr + dma_len - last_best_dma_addr;
+ aligned_size = dma_len - head_unalign_size - tail_unalign_size;
+
+#if 0
+ /* just for test */
+ if (aligned_size > 2 * ds) {
+ aligned_size -= 2 * ds;
+ head_unalign_size += ds;
+ best_dma_addr += ds;
+
+ tail_unalign_size += ds;
+ last_best_dma_addr -= ds;
+ } else if (aligned_size > ds) {
+ aligned_size -= ds;
+ head_unalign_size += ds;
+ best_dma_addr += ds;
+ }
+#endif
+
+ BUG_ON(head_unalign_size % 4);
+ BUG_ON(tail_unalign_size % 4);
+
+ if (head_unalign_size) {
+ desc = first_desc + pos;
+ next = (dma_desc_phys_addr + (pos + 1) * (sizeof(struct jz_dma_desc_8word))) >> 4;
+ desc->dcmd = DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_LINK;
+ if (DMA_MODE_WRITE == mode) {
+ desc->dcmd |= DMAC_DCMD_SAI;
+ desc->dsadr = (unsigned int)dma_addr; /* DMA source address */
+ desc->dtadr = CPHYSADDR(MSC_TXFIFO(ctrl_id)); /* DMA target address */
+ MSC_SET_OUT_REQ_SRC(ctrl_id, desc->dreqt);
+ } else {
+ desc->dcmd |= DMAC_DCMD_DAI;
+ desc->dsadr = CPHYSADDR(MSC_RXFIFO(ctrl_id));
+ desc->dtadr = (unsigned int)dma_addr;
+ MSC_SET_IN_REQ_SRC(ctrl_id, desc->dreqt);
+ }
+ desc->ddadr = (next << 24) | (head_unalign_size >> 2) ;
+
+ pos ++;
+ }
+
+ if (aligned_size) {
+ desc = first_desc + pos;
+ next = (dma_desc_phys_addr + (pos + 1) * (sizeof(struct jz_dma_desc_8word))) >> 4;
+
+ desc->dcmd = DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_LINK;
+ switch (ds) {
+#ifdef USE_DMA_BUSRT_64
+ case 64:
+ desc->dcmd |= DMAC_DCMD_DS_64BYTE;
+ break;
+#endif
+
+ case 32:
+ desc->dcmd |= DMAC_DCMD_DS_32BYTE;
+ break;
+
+ case 16:
+ desc->dcmd |= DMAC_DCMD_DS_16BYTE;
+ break;
+
+ case 4:
+ desc->dcmd |= DMAC_DCMD_DS_32BIT;
+ break;
+
+ default:
+ ;
+ }
+
+ if (DMA_MODE_WRITE == mode) {
+ desc->dcmd |= DMAC_DCMD_SAI;
+ desc->dsadr = (unsigned int)best_dma_addr; /* DMA source address */
+ desc->dtadr = CPHYSADDR(MSC_TXFIFO(ctrl_id)); /* DMA target address */
+ MSC_SET_OUT_REQ_SRC(ctrl_id, desc->dreqt);
+ } else {
+ desc->dcmd |= DMAC_DCMD_DAI;
+ desc->dsadr = CPHYSADDR(MSC_RXFIFO(ctrl_id));
+ desc->dtadr = (unsigned int)best_dma_addr;
+ MSC_SET_IN_REQ_SRC(ctrl_id, desc->dreqt);
+ }
+ desc->ddadr = (next << 24) | (aligned_size / ds) ;
+
+ pos ++;
+ }
+
+
+ if (tail_unalign_size) {
+ desc = first_desc + pos;
+ next = (dma_desc_phys_addr + (pos + 1) * (sizeof(struct jz_dma_desc_8word))) >> 4;
+
+ desc->dcmd = DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_LINK;
+ if (DMA_MODE_WRITE == mode) {
+ desc->dcmd |= DMAC_DCMD_SAI;
+ desc->dsadr = (unsigned int)last_best_dma_addr; /* DMA source address */
+ desc->dtadr = CPHYSADDR(MSC_TXFIFO(ctrl_id)); /* DMA target address */
+ MSC_SET_OUT_REQ_SRC(ctrl_id, desc->dreqt);
+ } else {
+ desc->dcmd |= DMAC_DCMD_DAI;
+ desc->dsadr = CPHYSADDR(MSC_RXFIFO(ctrl_id));
+ desc->dtadr = (unsigned int)last_best_dma_addr;
+ MSC_SET_IN_REQ_SRC(ctrl_id, desc->dreqt);
+ }
+ desc->ddadr = (next << 24) | (tail_unalign_size >> 2) ;
+
+ pos ++;
+
+ }
+
+ *desc_pos = pos;
+}
+
+#ifdef MSC_DEBUG_DMA
+#define JZ_MSC_RECORD_DESC_NUM(num) host->num_desc = (num)
+#else
+#define JZ_MSC_RECORD_DESC_NUM(num) do { } while(0)
+#endif
+
+void jz_mmc_start_scatter_dma(int chan, struct jz_mmc_host *host,
+ struct scatterlist *sg, unsigned int sg_len, int mode) {
+ int i = 0;
+ int desc_pos = 0;
+ dma_addr_t dma_desc_phy_addr = 0;
+ struct mmc_data *data = host->curr_mrq->data;
+ struct scatterlist *sgentry;
+ struct jz_dma_desc_8word *desc;
+ struct jz_dma_desc_8word *desc_first;
+ unsigned long flags;
+ unsigned long start_time = jiffies;
+
+ while (REG_DMAC_DMACR(chan / HALF_DMA_NUM) & (DMAC_DMACR_HLT | DMAC_DMACR_AR)) {
+ if (jiffies - start_time > 10) { /* 100ms */
+ printk("DMAC unavailable! REG_DMAC_DMACR(%d) = 0x%08x\n", chan / HALF_DMA_NUM, REG_DMAC_DMACR(chan / HALF_DMA_NUM));
+ jz_mmc_stop_dma(host);
+ break;
+ }
+ }
+
+ start_time = jiffies;
+ while (REG_DMAC_DCCSR(chan) & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR)) {
+ if (jiffies - start_time > 10) { /* 100ms */
+ printk("DMA channel %d unavailable! REG_DMAC_DCCSR(%d) = 0x%08x\n", chan, chan, REG_DMAC_DCCSR(chan));
+ jz_mmc_stop_dma(host);
+ break;
+ }
+ }
+
+ REG_DMAC_DCCSR(chan) |= DMAC_DCCSR_DES8;
+ REG_DMAC_DCCSR(chan) &= ~DMAC_DCCSR_NDES;
+
+ /* Setup request source */
+ if (DMA_MODE_WRITE == mode) {
+ MSC_SET_OUT_REQ_SRC(host->pdev_id, REG_DMAC_DRSR(chan));
+ } else {
+ MSC_SET_IN_REQ_SRC(host->pdev_id, REG_DMAC_DRSR(chan));
+ }
+
+#ifdef MSC_DEBUG_DMA
+ if (DMA_MODE_WRITE == mode) {
+ host->last_direction = 1;
+ } else {
+ host->last_direction = 0;
+ }
+#endif
+
+ desc = host->dma_desc;
+ JZ_MSC_RECORD_DESC_NUM(desc_pos);
+ desc_first = desc;
+
+ dma_desc_phy_addr = CPHYSADDR((unsigned long)desc);
+
+ memset(desc, 0, PAGE_SIZE);
+
+ desc_pos = 0;
+ flags = claim_dma_lock();
+ for_each_sg(data->sg, sgentry, host->dma.len, i) {
+ sg_to_desc(sgentry, desc, &desc_pos, mode, host->pdev_id, host);
+ }
+
+ desc = desc + (desc_pos - 1);
+ desc->dcmd |= DMAC_DCMD_TIE;
+ desc->dcmd &= ~DMAC_DCMD_LINK;
+ desc->ddadr &= ~0xff000000;
+
+ dma_cache_wback_inv((unsigned long)desc_first, PAGE_SIZE);
+
+ /* Setup DMA descriptor address */
+ REG_DMAC_DDA(chan) = dma_desc_phy_addr;
+
+ /* DMA doorbell set -- start DMA now ... */
+ REG_DMAC_DMADBSR(chan / HALF_DMA_NUM) = 1 << (chan - (chan / HALF_DMA_NUM) * HALF_DMA_NUM) ;
+
+ /* Enable DMA */
+ REG_DMAC_DMACR(chan / HALF_DMA_NUM) |= DMAC_DMACR_DMAE;
+
+ REG_DMAC_DCCSR(chan) |= DMAC_DCCSR_EN;
+
+ release_dma_lock(flags);
+
+}
+#else
+void jz_mmc_start_normal_dma(struct jz_mmc_host *host, unsigned long phyaddr, int count, int mode, int ds)
+{
+ unsigned long flags;
+ unsigned long start_time = jiffies;
+ int chan = host->dma.channel;
+ u32 dma_cmd = 0;
+ u32 src_addr = 0;
+ u32 dst_addr = 0;
+ u32 req_src = 0;
+
+ while (REG_DMAC_DMACR(chan / HALF_DMA_NUM) & (DMAC_DMACR_HLT | DMAC_DMACR_AR)) {
+ if (jiffies - start_time > 10) { /* 100ms */
+ printk("DMAC unavailable! REG_DMAC_DMACR(%d) = 0x%08x\n", chan / HALF_DMA_NUM, REG_DMAC_DMACR(chan / HALF_DMA_NUM));
+ jz_mmc_stop_dma(host);
+ break;
+ }
+ }
+
+ start_time = jiffies;
+ while (REG_DMAC_DCCSR(chan) & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR)) {
+ if (jiffies - start_time > 10) { /* 100ms */
+ printk("DMA channel %d unavailable! REG_DMAC_DCCSR(%d) = 0x%08x\n", chan, chan, REG_DMAC_DCCSR(chan));
+ jz_mmc_stop_dma(host);
+ break;
+ }
+ }
+
+ flags = claim_dma_lock();
+ dma_cmd = DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_TIE;
+ switch (ds) {
+#ifdef USE_DMA_BUSRT_64
+ case 64:
+ dma_cmd |= DMAC_DCMD_DS_64BYTE;
+ break;
+#endif
+
+ case 32:
+ dma_cmd |= DMAC_DCMD_DS_32BYTE;
+ break;
+
+ case 16:
+ dma_cmd |= DMAC_DCMD_DS_16BYTE;
+ break;
+
+ case 4:
+ dma_cmd |= DMAC_DCMD_DS_32BIT;
+ break;
+
+ default:
+ ;
+ }
+ if (DMA_MODE_WRITE == mode) {
+ dma_cmd |= DMAC_DCMD_SAI;
+ src_addr = (unsigned int)phyaddr; /* DMA source address */
+ dst_addr = CPHYSADDR(MSC_TXFIFO(host->pdev_id)); /* DMA target address */
+ MSC_SET_OUT_REQ_SRC(host->pdev_id, req_src);
+ } else {
+ dma_cmd |= DMAC_DCMD_DAI;
+ src_addr = CPHYSADDR(MSC_RXFIFO(host->pdev_id));
+ dst_addr = (unsigned int)phyaddr;
+ MSC_SET_IN_REQ_SRC(host->pdev_id, req_src);
+ }
+
+ REG_DMAC_DCCSR(chan) |= DMAC_DCCSR_NDES; /* No-descriptor transfer */
+ REG_DMAC_DSAR(chan) = src_addr;
+ REG_DMAC_DTAR(chan) = dst_addr;
+ REG_DMAC_DTCR(chan) = (count + ds - 1) / ds;
+ REG_DMAC_DCMD(chan) = dma_cmd;
+ REG_DMAC_DRSR(chan) = req_src;
+
+ REG_DMAC_DMACR(chan / HALF_DMA_NUM) |= DMAC_DMACR_DMAE;
+ REG_DMAC_DCCSR(chan) |= DMAC_DCCSR_EN;
+
+ release_dma_lock(flags);
+}
+#endif
+
+void jz_mmc_start_dma(struct jz_mmc_host *host) {
+ struct mmc_data *data = host->curr_mrq->data;
+ int mode;
+#ifndef USE_DMA_DESC
+ int i;
+ int ds = 4;
+ struct scatterlist *sgentry;
+#endif
+
+ if (data->flags & MMC_DATA_WRITE) {
+ mode = DMA_MODE_WRITE;
+ host->dma.dir = DMA_TO_DEVICE;
+ } else {
+ mode = DMA_MODE_READ;
+ host->dma.dir = DMA_FROM_DEVICE;
+ }
+
+ host->dma.len =
+ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma.dir);
+
+#ifdef USE_DMA_DESC
+ jz_mmc_start_scatter_dma(host->dma.channel, host, data->sg, host->dma.len, mode);
+#else
+ for_each_sg(data->sg, sgentry, host->dma.len, i) {
+ dma_cache_wback_inv((unsigned long)CKSEG0ADDR(sg_dma_address(sgentry) + data->sg->offset),
+ sg_dma_len(sgentry));
+
+ if ((likely(sg_dma_len(sgentry) % 32 == 0)))
+ ds = 32; /* 32 byte */
+ else if (sg_dma_len(sgentry) % 16 == 0)
+ ds = 16; /* 16 byte */
+ else
+ ds = 4; /* default to 4 byte */
+
+ /*
+ * FIXME: bug here!!!!! if NR_SG > 1(current NR_SG==1),
+ * must wait for current dma done, then next sg
+ */
+ jz_mmc_start_normal_dma(host, sg_dma_address(sgentry),
+ sg_dma_len(sgentry), mode, ds);
+ }
+#endif
+}
+
+static irqreturn_t jz_mmc_dma_callback(int irq, void *devid)
+{
+ struct jz_mmc_host *host = devid;
+ int chan = host->dma.channel;
+
+ disable_dma(chan);
+
+ host->data_err = 0;
+ if (__dmac_channel_address_error_detected(chan)) {
+ printk("%s: DMAC address error.\n",
+ __FUNCTION__);
+ __dmac_channel_clear_address_error(chan);
+ host->data_err = 1;
+ wmb();
+ }
+
+ if (__dmac_channel_transmit_halt_detected(chan)) {
+ printk("%s: DMA chan%d Halted.\n", __func__, chan);
+ __dmac_channel_clear_transmit_halt(chan);
+ host->data_err = 1;
+ wmb();
+ }
+ if (__dmac_channel_transmit_end_detected(chan)) {
+ __dmac_channel_clear_transmit_end(chan);
+ }
+
+ if (host->dma.dir == DMA_FROM_DEVICE) {
+ host->data_ack = 1;
+ wmb();
+ wake_up_interruptible(&host->data_wait_queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static char *msc_dma_name[] = {
+ "msc0_dma",
+ "msc1_dma",
+ "msc2_dma",
+};
+
+int jz_mmc_init_dma(struct jz_mmc_host *host)
+{
+ host->dma.channel = jz_request_dma(host->dma_id,
+ msc_dma_name[host->pdev_id],
+ jz_mmc_dma_callback,
+ 0, host);
+ if (host->dma.channel < 0) {
+ printk(KERN_ERR "jz_request_dma failed for MMC Rx\n");
+ goto err_out;
+ }
+
+ REG_DMAC_DMACR(host->dma.channel / HALF_DMA_NUM) |= DMAC_DMACR_FMSC;
+
+#ifdef USE_DMA_DESC
+ host->dma_desc = (struct jz_dma_desc_8word *)__get_free_pages(GFP_KERNEL, 0);
+#endif
+
+ return 0;
+err_out:
+ return -ENODEV;
+}
+
+void jz_mmc_deinit_dma(struct jz_mmc_host *host)
+{
+ jz_free_dma(host->dma.channel);
+}
View
168 drivers/mmc/host/jzmmc/jz_mmc_gpio.c
@@ -0,0 +1,168 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/gpio/jz_mmc_gpio.c
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/mach-jz4770/jz4770gpio.h>
+#include <asm/mach-jz4770/mmc.h>
+
+#include "include/chip-msc.h"
+#include "include/jz_mmc_gpio.h"
+#include "include/jz_mmc_host.h"
+#include "include/jz_mmc_msc.h"
+
+
+static int jz_mmc_request_card_gpios(struct platform_device *pdev,
+ struct jz_mmc_host *host)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_mmc_platform_data *pdata = dev->platform_data;
+ const char *name = dev_name(dev);
+ int port;
+ unsigned int mask;
+ unsigned int i;
+
+ /* Determine which pins to use. */
+ if (pdata->use_shared_8bit_pins) {
+ /* GPE20-29 */
+ port = 4;
+ mask = BIT(29) | BIT(28) | ((BIT(pdata->bus_width) - 1) << 20);
+ } else {
+ /* Use private pins for this MSC. */
+ if (pdata->bus_width == 8) {
+ dev_err(dev, "8-bit bus must use shared pins\n");
+ return -EINVAL;
+ }
+ if (host->pdev_id == 0) {
+ /* GPA18-23 */
+ port = 0;
+ mask = pdata->bus_width == 4 ? 0x00FC0000 : 0x001C0000;
+ } else if (host->pdev_id == 1) {
+ /* GPD20-25 */
+ port = 3;
+ mask = pdata->bus_width == 4 ? 0x03F00000 : 0x03100000;
+ } else if (host->pdev_id == 2) {
+ /* GPB20-21, 28-31 */
+ port = 1;
+ mask = pdata->bus_width == 4 ? 0xF0300000 : 0x30100000;
+ } else {
+ BUG();
+ }
+ }
+
+ /* Request pins. */
+ for (i = 0; i < 32; i++) {
+ if (mask & BIT(i)) {
+ unsigned int pin = port * 32 + i;
+ int ret = devm_gpio_request(dev, pin, name);
+ if (ret) {
+ dev_err(dev,
+ "Failed to request gpio pin %d: %d\n",
+ pin, ret);
+ return ret;
+ }
+ }
+ }
+
+ /* Select GPIO function for each pin. */
+ if (pdata->use_shared_8bit_pins) {
+ REG_GPIO_PXINTC (4) = mask;
+ REG_GPIO_PXMASKC(4) = mask;
+ if (host->pdev_id == 1)
+ REG_GPIO_PXPAT0S(4) = mask;
+ else
+ REG_GPIO_PXPAT0C(4) = mask;
+ if (host->pdev_id == 2)
+ REG_GPIO_PXPAT1S(4) = mask;
+ else
+ REG_GPIO_PXPAT1C(4) = mask;
+ } else {
+ REG_GPIO_PXINTC (port) = mask;
+ REG_GPIO_PXMASKC(port) = mask;
+ if (host->pdev_id == 0) {
+ REG_GPIO_PXPAT0S(port) = mask & ~BIT(20);
+ REG_GPIO_PXPAT0C(port) = mask & BIT(20);
+ } else {
+ REG_GPIO_PXPAT0C(port) = mask;
+ }
+ REG_GPIO_PXPAT1C(port) = mask;
+ }
+
+ return 0;
+}
+
+static int jz_mmc_request_power_gpio(struct platform_device *pdev,
+ struct jz_mmc_host *host)
+{
+ struct device *dev = &pdev->dev;
+ struct jz_mmc_platform_data *pdata = dev->platform_data;
+ char *label = kasprintf(GFP_KERNEL, "%s power", dev_name(dev));
+ int ret;
+
+ ret = devm_gpio_request(dev, pdata->gpio_power, label);
+ if (ret) {
+ dev_err(dev, "Failed to request power gpio: %d\n", ret);
+ kfree(label);
+ return ret;
+ }
+ host->label_power = label;
+
+ gpio_direction_output(pdata->gpio_power, pdata->power_active_low);
+
+ return 0;
+}
+
+int jz_mmc_gpio_init(struct jz_mmc_host *host, struct platform_device *pdev)
+{
+ struct jz_mmc_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ ret = jz_mmc_request_card_gpios(pdev, host);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = jz_mmc_request_power_gpio(pdev, host);
+ if (ret)
+ return ret;
+ }
+
+ if (gpio_is_valid(pdata->gpio_read_only)) {
+ ret = mmc_gpio_request_ro(host->mmc, pdata->gpio_read_only);
+ if (ret)
+ return ret;
+ }
+
+ if (gpio_is_valid(pdata->gpio_card_detect)) {
+ ret = mmc_gpio_request_cd(host->mmc, pdata->gpio_card_detect,
+ 0);
+ if (ret)
+ return ret;
+ } else if (!pdata->nonremovable) {
+ dev_info(&pdev->dev, "No card detect facilities available\n");
+ }
+
+ return 0;
+}
+
+void jz_mmc_gpio_deinit(struct jz_mmc_host *host, struct platform_device *pdev)
+{
+ if (host->card_detect_irq >= 0) {
+ disable_irq(host->card_detect_irq);
+ device_init_wakeup(&pdev->dev, 0);
+ }
+
+ kfree(host->label_power);
+}
View
325 drivers/mmc/host/jzmmc/jz_mmc_main.c
@@ -0,0 +1,325 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/jz_mmc_main.c - JZ SD/MMC driver
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/pm.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+
+#include <asm/mach-jz4770/mmc.h>
+
+#include "include/chip-msc.h"
+#include "include/jz_mmc_dma.h"
+#include "include/jz_mmc_gpio.h"
+#include "include/jz_mmc_host.h"
+#include "include/jz_mmc_msc.h"
+
+
+#define JZ_MAX_MSC_NUM 3
+
+void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq)
+{
+ host->curr_mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
+static void jz_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct jz_mmc_host *host = mmc_priv(mmc);
+
+ if (SD_IO_SEND_OP_COND == mrq->cmd->opcode) {
+ if(host->pdata->support_sdio == 0) {
+ mrq->cmd->error = -ETIMEDOUT;
+ jz_mmc_finish_request(host, mrq);
+ return;
+ }
+ }
+
+ BUG_ON (host->curr_mrq);
+ host->curr_mrq = mrq;
+ jz_mmc_execute_cmd(host);
+ jz_mmc_finish_request(host, mrq);
+}
+
+/* set clock and power */
+static void jz_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct jz_mmc_host *host = mmc_priv(mmc);
+
+ if (ios->clock)
+ jz_mmc_set_clock(host, ios->clock);
+
+ switch (ios->power_mode) {
+ case MMC_POWER_UP:
+ jz_mmc_reset(host);
+ if (gpio_is_valid(host->pdata->gpio_power))
+ gpio_set_value(host->pdata->gpio_power,
+ !host->pdata->power_active_low);
+ host->cmdat |= MSC_CMDAT_INIT;
+ clk_enable(host->clk);
+ break;
+ case MMC_POWER_ON:
+ break;
+ default:
+ if (gpio_is_valid(host->pdata->gpio_power))
+ gpio_set_value(host->pdata->gpio_power,
+ host->pdata->power_active_low);
+ clk_disable(host->clk);
+ break;
+ }
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
+
+ host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK;
+
+ if(host->pdata->bus_width == 4)
+ host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT;
+ else
+ host->cmdat |= host->pdata->bus_width;
+ } else if (ios->bus_width == MMC_BUS_WIDTH_8) {
+
+ host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK;
+
+ if(host->pdata->bus_width == 8)
+ host->cmdat |= MSC_CMDAT_BUS_WIDTH_8BIT;
+// else
+// host->cmdat |= host->pdata->bus_width;
+ } else {
+ /* 1 bit bus*/
+ host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_8BIT;
+ }
+}
+
+static const struct mmc_host_ops jz_mmc_ops = {
+ .request = jz_mmc_request,
+ .set_ios = jz_mmc_set_ios,
+ .get_cd = mmc_gpio_get_cd,
+ .get_ro = mmc_gpio_get_ro,
+};
+
+static int jz_mmc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz_mmc_platform_data *pdata = pdev->dev.platform_data;
+ struct mmc_host *mmc;
+ struct jz_mmc_host *host;
+
+ struct resource *dmares;
+
+ char clk_name[5];
+
+ if (pdev->id < 0 || pdev->id >= JZ_MAX_MSC_NUM) {
+ dev_err(&pdev->dev, "Device ID out of range: %d\n", pdev->id);
+ return -EINVAL;
+ }
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Missing platform data\n");
+ return -EINVAL;
+ }
+
+ mmc = mmc_alloc_host(sizeof(struct jz_mmc_host), &pdev->dev);
+ if (!mmc) {
+ dev_err(&pdev->dev, "Failed to alloc mmc host structure\n");
+ return -ENOMEM;
+ }
+
+ host = mmc_priv(mmc);
+ host->pdata = pdata;
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0) {
+ ret = host->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+ goto err_free_host;
+ }
+
+ sprintf(clk_name, "mmc%i", pdev->id);
+ host->clk = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(host->clk)) {
+ ret = PTR_ERR(host->clk);
+ dev_err(&pdev->dev, "Failed to get mmc clock\n");
+ goto err_free_host;
+ }
+
+ host->base = devm_ioremap_resource(&pdev->dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (IS_ERR(host->base)) {
+ ret = PTR_ERR(host->base);
+ dev_err(&pdev->dev, "Failed to get and remap mmio region\n");
+ goto err_free_host;
+ }
+
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmares) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform dma\n");
+ goto err_free_host;
+ }
+ host->dma_id = dmares->start;
+
+ /*
+ * Setup MMC host structure
+ */
+ mmc->ops = &jz_mmc_ops;
+ mmc->f_min = MMC_CLOCK_SLOW;
+ mmc->f_max = SD_CLOCK_HIGH;
+ mmc->ocr_avail = pdata->ocr_mask;
+
+ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+ MMC_CAP_ERASE;
+ if (pdata->bus_width >= 4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ if (pdata->bus_width >= 8)
+ mmc->caps |= MMC_CAP_8_BIT_DATA;
+ if (pdata->nonremovable)
+ mmc->caps |= MMC_CAP_NONREMOVABLE;
+
+ if (!pdata->card_detect_active_low)
+ mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
+ if (!pdata->read_only_active_low)
+ mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
+
+ mmc->max_blk_size = 4095;
+ mmc->max_blk_count = 65535;
+ mmc->max_req_size = PAGE_SIZE * 16;
+
+ mmc->max_segs = 1;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ host->pdev_id = pdev->id;
+ host->mmc = mmc;
+ //spin_lock_init(&host->lock);
+
+ ret = jz_mmc_msc_init(host);
+ if (ret)
+ goto err_free_host;
+
+ ret = jz_mmc_gpio_init(host, pdev);
+ if (ret)
+ goto err_deinit_msc;
+
+ ret = jz_mmc_init_dma(host);
+ if (ret)
+ goto err_deinit_gpio;
+
+ if (gpio_is_valid(host->pdata->gpio_power))
+ gpio_set_value(host->pdata->gpio_power,
+ !host->pdata->power_active_low);
+
+ platform_set_drvdata(pdev, host);
+ ret = mmc_add_host(mmc);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
+ goto err_deinit_dma;
+ }
+ dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
+
+ return 0;
+
+err_deinit_dma:
+ jz_mmc_deinit_dma(host);
+err_deinit_gpio:
+ jz_mmc_gpio_deinit(host, pdev);
+err_deinit_msc:
+ jz_mmc_msc_deinit(host);
+err_free_host:
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+static int jz_mmc_remove(struct platform_device *pdev)
+{
+ struct jz_mmc_host *host = platform_get_drvdata(pdev);
+
+ if (gpio_is_valid(host->pdata->gpio_power))
+ gpio_set_value(host->pdata->gpio_power,
+ host->pdata->power_active_low);
+
+ jz_mmc_deinit_dma(host);
+ jz_mmc_gpio_deinit(host, pdev);
+ jz_mmc_msc_deinit(host);
+
+ mmc_remove_host(host->mmc);
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int jz_mmc_suspend(struct device *dev)
+{
+ struct jz_mmc_host *host = dev_get_drvdata(dev);
+
+ return mmc_suspend_host(host->mmc);
+}
+
+static int jz_mmc_resume(struct device *dev)
+{
+ struct jz_mmc_host *host = dev_get_drvdata(dev);
+ int ret;
+
+ ret = mmc_resume_host(host->mmc);
+ if (!ret)
+ return ret;
+
+ if (host->card_detect_irq < 0)
+ if (clk_get_rate(host->clk) > SD_CLOCK_FAST)
+ REG_MSC_LPM(host->pdev_id) |= 1 << 31;
+
+ return 0;
+}
+
+const struct dev_pm_ops jz_mmc_pm_ops = {
+ .suspend = jz_mmc_suspend,
+ .resume = jz_mmc_resume,
+ .poweroff = jz_mmc_suspend,
+ .restore = jz_mmc_resume,
+};
+
+#define JZ_MMC_PM_OPS (&jz_mmc_pm_ops)
+#else
+#define JZ_MMC_PM_OPS NULL
+#endif
+
+static struct platform_driver jz_msc_driver = {
+ .probe = jz_mmc_probe,
+ .remove = jz_mmc_remove,
+ .driver = {
+ .name = "jz-msc",
+ .owner = THIS_MODULE,
+ .pm = JZ_MMC_PM_OPS,
+ },
+};
+
+module_platform_driver(jz_msc_driver);
+
+MODULE_DESCRIPTION("JZ47XX SD/Multimedia Card Interface Driver");
+MODULE_LICENSE("GPL");
View
784 drivers/mmc/host/jzmmc/jz_mmc_msc.c
@@ -0,0 +1,784 @@
+/*
+ * linux/drivers/mmc/host/jz_mmc/msc/jz_mmc_msc.c
+ *
+ * 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.
+ *
+ * Copyright (c) Ingenic Semiconductor Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/semaphore.h>
+#include <linux/export.h>
+#include <linux/kthread.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/moduleparam.h>
+#include <linux/scatterlist.h>
+
+#include <asm/mach-jz4770/mmc.h>
+
+#include "include/chip-msc.h"
+#include "include/jz_mmc_dma.h"
+#include "include/jz_mmc_host.h"
+#include "include/jz_mmc_msc.h"
+
+
+#define MSC_STAT_ERR_BITS 0x3f
+#define WAITMASK \
+ (MSC_STAT_CRC_RES_ERR | \
+ MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_MASK | \
+ MSC_STAT_TIME_OUT_RES | MSC_STAT_TIME_OUT_READ)
+
+#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+
+#if 1
+
+static int jzmmc_trace_level = 0;
+static int jzmmc_trace_cmd_code = -1;
+static int jzmmc_trace_data_len = -1;
+static int jzmmc_trace_id = 0;
+module_param(jzmmc_trace_level, int, 0644);
+module_param(jzmmc_trace_cmd_code, int, 0644);
+module_param(jzmmc_trace_data_len, int, 0644);
+module_param(jzmmc_trace_id, int, 0644);
+
+#define TRACE_CMD_REQ() \
+ ({ \
+ if ( (jzmmc_trace_id & (1 << host->pdev_id)) && (jzmmc_trace_level & 0x1)) \
+ if ( (jzmmc_trace_cmd_code == -1) || (jzmmc_trace_cmd_code == cmd->opcode) ) \
+ printk("%s: execute_cmd: opcode = %d cmdat = %#0x arg = %#0x data_flags = %#0x\n", \
+ mmc_hostname(host->mmc), cmd->opcode, REG_MSC_CMDAT(host->pdev_id), REG_MSC_ARG(host->pdev_id), \
+ host->curr_mrq->data ? host->curr_mrq->data->flags : 0); \
+ })
+
+#define TRACE_CMD_RES() \
+ ({ \
+ if ( (jzmmc_trace_id & (1 << host->pdev_id)) && (jzmmc_trace_level & 0x1)) \
+ if ( (jzmmc_trace_cmd_code == -1) || (jzmmc_trace_cmd_code == cmd->opcode) ) \
+ printk("%s: cmd done: curr_res_type = %d resp[0] = %#0x err = %d state = %#0x\n", \
+ mmc_hostname(host->mmc), host->curr_res_type, cmd->resp[0], cmd->error, \
+ REG_MSC_STAT(host->pdev_id)); \
+ })
+
+#define TRACE_DATA_REQ() \
+ ({ \
+ if ((jzmmc_trace_id & (1 << host->pdev_id)) && (jzmmc_trace_level & 0x2) && host->curr_mrq->data ) { \
+ if ((jzmmc_trace_data_len == -1) || \
+ (jzmmc_trace_data_len == host->curr_mrq->data->blksz * host->curr_mrq->data->blocks) ) \
+ printk("%s: blksz %d blocks %d flags %08x " \
+ "tsac %d ms nsac %d\n", \
+ mmc_hostname(host->mmc), host->curr_mrq->data->blksz, \
+ host->curr_mrq->data->blocks, host->curr_mrq->data->flags, \
+ host->curr_mrq->data->timeout_ns / 1000000, \
+ host->curr_mrq->data->timeout_clks); \
+ } \
+ })
+
+#define TRACE_DATA_DONE() \
+ ({ \
+ if ((jzmmc_trace_id & (1 << host->pdev_id)) && (jzmmc_trace_level & 0x2)) \
+ if ((jzmmc_trace_data_len == -1) || \
+ (jzmmc_trace_data_len == data->blksz * data->blocks) ) \
+ printk("%s: stat = 0x%08x error = %d bytes_xfered = %d stop = %p\n", \
+ mmc_hostname(host->mmc), stat, data->error, \
+ data->bytes_xfered, host->curr_mrq->stop); \
+ })
+
+#define JZ_MMC_P_REG_BY_ID(reg_name, id) \
+ printk("" #reg_name "(%d) = 0x%08x\n", id, reg_name(id))
+
+void jz_mmc_dump_regs(int msc_id, int line) {
+ printk("***** msc%d regs, line = %d *****\n", msc_id, line);
+
+ JZ_MMC_P_REG_BY_ID(REG_MSC_STRPCL, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_STAT, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_CLKRT, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_CMDAT, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_RESTO, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_RDTO, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_BLKLEN, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_NOB, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_SNOB, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_IMASK, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_IREG, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_CMD, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_ARG, msc_id);
+ //JZ_MMC_P_REG_BY_ID(REG_MSC_RES, msc_id);
+ //JZ_MMC_P_REG_BY_ID(REG_MSC_RXFIFO, msc_id);
+ //JZ_MMC_P_REG_BY_ID(REG_MSC_TXFIFO, msc_id);
+ JZ_MMC_P_REG_BY_ID(REG_MSC_LPM, msc_id);
+}
+EXPORT_SYMBOL(jz_mmc_dump_regs);
+
+#else
+#define TRACE_CMD_REQ() do { } while(0)
+#define TRACE_CMD_RES() do { } while(0)
+#define TRACE_DATA_REQ() do { } while(0)
+#define TRACE_DATA_DONE() do { } while(0)
+#define jz_mmc_dump_regs(__mid, __ln) do { } while(0)
+#endif
+
+static int jz_mmc_data_done(struct jz_mmc_host *host);
+
+static void msc_irq_mask_all(int msc_id)
+{
+ REG_MSC_IMASK(msc_id) = 0xffff;
+ REG_MSC_IREG(msc_id) = 0xffff;
+}
+
+void jz_mmc_reset(struct jz_mmc_host *host)
+{
+ u32 clkrt = REG_MSC_CLKRT(host->pdev_id);
+
+// while (REG_MSC_STAT(host->pdev_id) & MSC_STAT_CLK_EN);
+
+ clk_enable(host->clk);
+
+ REG_MSC_STRPCL(host->pdev_id) = MSC_STRPCL_RESET;
+ while (REG_MSC_STAT(host->pdev_id) & MSC_STAT_IS_RESETTING);
+
+ // __msc_start_clk(host->pdev_id);
+ REG_MSC_LPM(host->pdev_id) = 0x1; // Low power mode
+
+ clk_disable(host->clk);
+
+ msc_irq_mask_all(host->pdev_id);
+
+ REG_MSC_RDTO(host->pdev_id) = 0xffffffff;
+ REG_MSC_RESTO(host->pdev_id) = 0xff;
+
+ REG_MSC_CLKRT(host->pdev_id) = clkrt;
+}
+
+static inline int msc_calc_clkrt(int is_low, u32 rate)
+{
+ u32 clkrt;
+ u32 clk_src = is_low ? 24000000 : 48000000;
+
+ clkrt = 0;
+ while (rate < clk_src) {
+ clkrt++;
+ clk_src >>= 1;
+ }
+ return clkrt;
+}
+
+void jz_mmc_set_clock(struct jz_mmc_host *host, int rate)
+{
+ int clkrt;
+
+ /* __cpm_select_msc_clk_high will select 48M clock for MMC/SD card
+ * perhaps this will made some card with bad quality init fail,or
+ * bad stabilization.
+ */
+
+ if (rate > SD_CLOCK_FAST) {
+ clk_set_rate(host->clk, 48 * 1000 * 1000);
+ clkrt = msc_calc_clkrt(0, rate);
+ // send cmd and data at clock rising
+ REG_MSC_LPM(host->pdev_id) |= 0x1 << 31;
+ } else {
+ clk_set_rate(host->clk, 24 * 1000 * 1000);
+ clkrt = msc_calc_clkrt(1, rate);
+ // send cmd and data at clock falling
+ REG_MSC_LPM(host->pdev_id) &= ~(0x1 << 31);
+ }
+ REG_MSC_CLKRT(host->pdev_id) = clkrt;
+}
+
+static void jz_mmc_enable_irq(struct jz_mmc_host *host, unsigned int mask)
+{
+ REG_MSC_IMASK(host->pdev_id) &= ~mask;
+}
+
+static void jz_mmc_disable_irq(struct jz_mmc_host *host, unsigned int mask)
+{
+ REG_MSC_IMASK(host->pdev_id) |= mask;
+}
+
+static int jz_mmc_parse_cmd_response(struct jz_mmc_host *host, unsigned int stat)
+{
+ struct mmc_command *cmd = host->curr_mrq->cmd;
+ int i, temp[16] = {0};
+ unsigned char *buf;
+ unsigned int res, v, w1, w2;
+
+ if (!cmd)
+ return -EINVAL;
+
+ /* NOTE: we must flush the FIFO, despite of fail or success*/
+ buf = (u8 *) temp;
+ switch (host->curr_res_type) {
+ case 1:
+ {
+ /*
+ * Did I mention this is Sick. We always need to
+ * discard the upper 8 bits of the first 16-bit word.
+ */
+
+ res = REG_MSC_RES(host->pdev_id);
+ buf[0] = (res >> 8) & 0xff;
+ buf[1] = res & 0xff;
+
+ res = REG_MSC_RES(host->pdev_id);
+ buf[2] = (res >> 8) & 0xff;
+ buf[3] = res & 0xff;
+
+ res = REG_MSC_RES(host->pdev_id);
+ buf[4] = res & 0xff;
+
+ cmd->resp[0] =
+ buf[1] << 24 | buf[2] << 16 | buf[3] << 8 |
+ buf[4];
+
+ // printk("opcode = %d, cmd->resp = 0x%08x\n", cmd->opcode, cmd->resp[0]);
+ break;
+ }
+ case 2:
+ {
+ res = REG_MSC_RES(host->pdev_id);
+ v = res & 0xffff;
+ for (i = 0; i < 4; i++) {
+ res = REG_MSC_RES(host->pdev_id);
+ w1 = res & 0xffff;
+ res = REG_MSC_RES(host->pdev_id);
+ w2 = res & 0xffff;
+ cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
+ v = w2;
+ }
+ break;
+ }
+ case 0:
+ break;
+ }
+
+ if (stat & MSC_STAT_TIME_OUT_RES) {
+ /* :-( our customer do not want to see SO MANY timeouts :-(
+ so only CMD5 can return timeout error!!! */
+
+ /*
+ * Note: we can not return timeout when CMD SD_SWITCH or MMC_SWITCH
+ * because we declared that out host->caps support MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA and MMC_CAP_MMC_HIGHSPEED
+ * if in the future some error occured because of this, we must add some code to remember
+ * which mode(SDIO/SD/MMC) the MSC is in
+ */
+ switch(cmd->opcode) {
+ case SD_IO_SEND_OP_COND:
+ //case SD_SWITCH:
+ //case MMC_SWITCH:
+ case SD_SEND_IF_COND:
+ case MMC_APP_CMD:
+ cmd->error = -ETIMEDOUT;
+ break;
+ default:
+ /* silly, isn't it??? */
+ printk("jz-msc%d: ignored MSC_STAT_TIME_OUT_RES, cmd=%d\n", host->pdev_id, cmd->opcode);
+ }
+ } else if (stat & MSC_STAT_CRC_RES_ERR && cmd->flags & MMC_RSP_CRC) {
+ printk("jz-msc%d: MSC_STAT_CRC, cmd=%d\n", host->pdev_id, cmd->opcode);
+ if (cmd->opcode == MMC_ALL_SEND_CID ||
+ cmd->opcode == MMC_SEND_CSD ||
+ cmd->opcode == MMC_SEND_CID) {
+ /* a bogus CRC error can appear if the msb of
+ the 15 byte response is a one */
+ if ((cmd->resp[0] & 0x80000000) == 0)
+ cmd->error = -EILSEQ;
+ }
+ }
+
+ TRACE_CMD_RES();
+
+ return cmd->error;
+}
+
+void jz_mmc_data_start(struct jz_mmc_host *host)
+{
+ struct mmc_data *data = host->curr_mrq->data;
+ unsigned int nob = data->blocks;
+ unsigned int block_size = data->blksz;
+
+ /* NOTE: this flag is never test! */
+ if (data->flags & MMC_DATA_STREAM)
+ nob = 0xffff;
+
+ REG_MSC_NOB(host->pdev_id) = nob;
+ REG_MSC_BLKLEN(host->pdev_id) = block_size;
+
+ jz_mmc_start_dma(host);
+}
+
+volatile u32 junk = 0;
+EXPORT_SYMBOL(junk);
+
+void jz_mmc_data_stop(struct jz_mmc_host *host) {
+ int junked = 1;
+
+ jz_mmc_stop_dma(host);
+
+ /* What if the data not arrived imediately? our while exits, but data remain in fifo! */
+ while (!(REG_MSC_STAT(host->pdev_id) & MSC_STAT_DATA_FIFO_EMPTY)) {
+ if (junked)
+ jz_mmc_dump_regs(host->pdev_id, __LINE__);
+ junked = 0;
+ junk = REG_MSC_RXFIFO(host->pdev_id);
+ printk("warning: fifo not empty when dma stopped!!! junk = 0x%08x\n", junk);
+ }
+}
+
+static int need_wait_prog_done(struct mmc_command *cmd) {
+ if (cmd->flags & MMC_RSP_BUSY) {
+ return 1;
+ } else {
+ switch(cmd->opcode) { /* R1b cmds need wait PROG_DONE */
+ case 12:
+ case 28:
+ case 29:
+ case 38:
+ return 1;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void jz_mmc_set_cmdat(struct jz_mmc_host *host) {
+ struct mmc_request *mrq = host->curr_mrq;
+ struct mmc_command *cmd = mrq->cmd;
+ u32 cmdat;
+
+ cmdat = host->cmdat;
+ rmb();
+ host->cmdat &= ~MSC_CMDAT_INIT;
+
+ if(mrq->data) {
+ cmdat &= ~MSC_CMDAT_BUSY;
+
+ if ((cmd->opcode == 51) | (cmd->opcode == 8)) {
+ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK;
+ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN;
+ } else
+ cmdat |= MSC_CMDAT_DATA_EN;
+
+ cmdat |= MSC_CMDAT_DMA_EN;
+
+ if (mrq->data->flags & MMC_DATA_WRITE)
+ cmdat |= MSC_CMDAT_WRITE;
+
+ if (mrq->data->flags & MMC_DATA_STREAM)
+ cmdat |= MSC_CMDAT_STREAM_BLOCK;
+ }
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdat |= MSC_CMDAT_BUSY;
+
+ switch (RSP_TYPE(mmc_resp_type(cmd))) {
+ case RSP_TYPE(MMC_RSP_R1): // r1, r1b, r5, r6, r7
+ cmdat |= MSC_CMDAT_RESPONSE_R1;
+ host->curr_res_type = 1;
+ break;
+ case RSP_TYPE(MMC_RSP_R3): // r3, r4
+ cmdat |= MSC_CMDAT_RESPONSE_R3;
+ host->curr_res_type = 1;
+ break;
+ case RSP_TYPE(MMC_RSP_R2): // r2
+ cmdat |= MSC_CMDAT_RESPONSE_R2;
+ host->curr_res_type = 2;
+ break;
+ default:
+ break;
+ }
+
+ // Multi-read || Multi-write
+ //if(cmd->opcode == MMC_READ_MULTIPLE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)
+ if (host->curr_mrq->stop)
+ cmdat |= MSC_CMDAT_SEND_AS_STOP;
+
+#ifdef USE_DMA_BUSRT_64
+ cmdat |= MSC_CMDAT_RTRG_EQUALT_16 | MSC_CMDAT_TTRG_LESS_16;
+#endif
+ REG_MSC_CMDAT(host->pdev_id) = cmdat;
+}
+
+static void jz_mmc_set_cmdarg(struct jz_mmc_host *host) {
+ struct mmc_command *cmd = host->curr_mrq->cmd;
+
+ if(host->pdata->bus_width == 1) {
+ if (cmd->opcode == 6) {
+ /* set 1 bit sd card bus*/
+ if (cmd->arg == 2) {
+ REG_MSC_ARG(host->pdev_id) = 0;
+ }
+
+ /* set 1 bit mmc card bus*/
+ if (cmd->arg == 0x3b70101) {
+ REG_MSC_ARG(host->pdev_id) = 0x3b70001;
+ }
+ } else
+ REG_MSC_ARG(host->pdev_id) = cmd->arg;
+ } else if(host->pdata->bus_width == 8) {
+ if (cmd->opcode == 6) {
+ /* set 8 bit mmc card bus*/
+ if (cmd->arg == 0x3b70101) {
+ REG_MSC_ARG(host->pdev_id) = 0x3b70201;
+ } else
+ REG_MSC_ARG(host->pdev_id) = cmd->arg;
+ } else
+ REG_MSC_ARG(host->pdev_id) = cmd->arg;
+ } else
+ REG_MSC_ARG(host->pdev_id) = cmd->arg;
+}
+
+#if 0
+static void jz_mmc_status_checker(unsigned long arg) {
+ struct jz_mmc_host *host = (struct jz_mmc_host *)arg;
+
+ host->status = REG_MSC_STAT(host->pdev_id);
+ if ((host->status & host->st_mask) || (host->eject)) {
+ if (host->en_usr_intr)
+ wake_up_interruptible(&host->status_check_queue);
+ else
+ wake_up(&host->status_check_queue);
+ } else if ((host->st_check_timeout < 0) ||
+ (host->st_check_timeout > host->st_check_interval)) {
+ if (host->st_check_timeout < 0)
+ host->st_check_timeout -= host->st_check_interval;
+ host->status_check_timer.expires = jiffies + host->st_check_interval;
+ host->status_check_timer.data = (unsigned long)host;
+
+
+ add_timer(&host->status_check_timer);
+ } else {
+ host->st_check_timeout = 0;
+ wake_up_interruptible(&host->status_check_queue);
+ }
+}
+
+
+/**
+ * timeout: -1 for wait forever until contition meet, otherwise the timeout value in jiffies
+ * en_usr_intr: if allow user interrupt
+ * Warning: if timeout == 0 && en_usr_intr == 0, this will wait forever if the condition never meet
+ **/
+static u32 jz_mmc_wait_status(struct jz_mmc_host *host, u32 st_mask,
+ int timeout, int interval, int en_usr_intr) {
+ int ret = 0;
+
+ init_timer(&host->status_check_timer);
+ host->status_check_timer.expires = jiffies + interval;
+ host->status_check_timer.data = (unsigned long)host;
+ host->status = 0;
+ host->st_mask = st_mask;
+ host->st_check_timeout = timeout;
+ host->st_check_interval = interval;
+ host->en_usr_intr = en_usr_intr;
+
+ add_timer(&host->status_check_timer);
+
+ if (en_usr_intr)
+ ret = wait_event_interruptible(host->status_check_queue,
+ (host->status & st_mask) ||
+ (host->st_check_timeout == 0) ||
+ (host->eject));
+ else
+ wait_event(host->status_check_queue,
+ (host->status & st_mask) ||
+ (host->st_check_timeout == 0) ||
+ (host->eject));
+
+ /* in case when the condition is meet before wait_event, the timer must del right away */
+ del_timer_sync(&host->status_check_timer);
+ return ret;
+}
+#endif
+
+//int wait_cmd_done = 0;
+//extern volatile int error_may_happen;
+
+static u32 jz_mmc_wait_cmd_done(struct jz_mmc_host *host) {
+ u32 timeout = 0x7fffffff;
+ struct mmc_command *cmd = host->curr_mrq->cmd;
+ int cmd_succ = 0;
+ u32 stat = 0;
+
+#if 0
+ /* this may slow down the card response from the usrs' view, but more friendly to other kernel parts */
+ jz_mmc_wait_status(host, MSC_STAT_END_CMD_RES | MSC_STAT_TIME_OUT_RES | MSC_STAT_CRC_RES_ERR,
+ -1, 1, 0); /* interval: 1jiffie = 10ms */
+#else
+ //wait_cmd_done = 1;
+ while (!(REG_MSC_STAT(host->pdev_id) & (MSC_STAT_END_CMD_RES | MSC_STAT_TIME_OUT_RES | MSC_STAT_CRC_RES_ERR))) {
+#if 0
+ if (error_may_happen)
+ jz_mmc_dump_regs(host->pdev_id, __LINE__);
+#endif
+ }
+ //error_may_happen = 0;
+ //wait_cmd_done = 0;
+#endif
+
+ if (REG_MSC_STAT(host->pdev_id) & MSC_STAT_TIME_OUT_RES)
+ cmd->error = -ETIMEDOUT;
+
+ /* Check for status, avoid be cleaned by following command*/
+ stat = REG_MSC_STAT(host->pdev_id);
+ if ((stat & MSC_STAT_END_CMD_RES) &&
+ !(stat & (MSC_STAT_TIME_OUT_RES | MSC_STAT_CRC_RES_ERR)))
+ cmd_succ = 1;
+
+ REG_MSC_IREG(host->pdev_id) = MSC_IREG_END_CMD_RES; /* clear irq flag */
+
+ if (cmd_succ && need_wait_prog_done(cmd)) {
+ timeout = 0x7fffffff;
+ while (--timeout && !(REG_MSC_IREG(host->pdev_id) & MSC_IREG_PRG_DONE))
+ ;
+
+ stat |= (REG_MSC_STAT(host->pdev_id) & MSC_STAT_ERR_BITS);
+ REG_MSC_IREG(host->pdev_id) = MSC_IREG_PRG_DONE; /* clear status */
+ if (timeout == 0) {
+ cmd->error = -ETIMEDOUT;
+ printk("JZ-MSC%d: wait prog_done error when execute_cmd!, state = 0x%08x\n", host->pdev_id, stat);
+ }
+ }
+
+ return stat;
+}
+
+static void jz_mmc_send_stop_cmd(struct jz_mmc_host *host) {
+ struct mmc_command *stop_cmd = host->curr_mrq->stop;
+
+ REG_MSC_CMD(host->pdev_id) = stop_cmd->opcode;
+ REG_MSC_ARG(host->pdev_id) = stop_cmd->arg;
+
+ REG_MSC_CMDAT(host->pdev_id) = MSC_CMDAT_BUSY | MSC_CMDAT_RESPONSE_R1;
+
+ REG_MSC_RESTO(host->pdev_id) = 0xff;
+
+ REG_MSC_STRPCL(host->pdev_id) |= MSC_STRPCL_START_OP;
+
+ /* Becarefull, maybe endless */
+ while(!(REG_MSC_STAT(host->pdev_id) & (MSC_STAT_PRG_DONE | MSC_STAT_ERR_BITS)));
+
+ if (REG_MSC_STAT(host->pdev_id) | MSC_STAT_ERR_BITS)
+ stop_cmd->error = -ETIMEDOUT;
+
+ REG_MSC_IREG(host->pdev_id) = MSC_IREG_PRG_DONE;
+}
+
+static int jz_mmc_data_done(struct jz_mmc_host *host)
+{
+ struct mmc_data *data = host->curr_mrq->data;
+ int stat = 0;
+ u32 timeout = 0x7fffffff;
+
+ if (!data)
+ return -EINVAL;
+
+ stat = REG_MSC_STAT(host->pdev_id);
+ REG_MSC_IREG(host->pdev_id) = MSC_IREG_DATA_TRAN_DONE; /* clear status */
+
+ if (host->curr_mrq && (host->curr_mrq->data->flags & MMC_DATA_WRITE)) {
+ while (--timeout && !(REG_MSC_IREG(host->pdev_id) & MSC_IREG_PRG_DONE))
+ ;
+ if (timeout == 0) {
+ /* FIXME: aha, we never see this situation happen, what can we do if it happened???
+ * block.c will send cmd13??? */
+ //host->curr.mrq->cmd->error = -ETIMEDOUT;
+ printk(KERN_ERR"PRG_DONE not done!!!\n");
+ }
+ stat |= REG_MSC_STAT(host->pdev_id);
+ REG_MSC_IREG(host->pdev_id) = MSC_IREG_PRG_DONE; /* clear status */
+ }
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma.len,
+ host->dma.dir);
+
+ if (stat & MSC_STAT_TIME_OUT_READ) {
+ printk("MMC/SD/SDIO timeout, MMC_STAT 0x%x opcode = %d data flags = 0x%0x blocks = %d blksz = %d\n",
+ stat,
+ host->curr_mrq? host->curr_mrq->cmd->opcode : -1,
+ data->flags,
+ data->blocks,
+ data->blksz);
+ data->error = -ETIMEDOUT;
+ } else if (stat & (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR)) {
+ printk("jz-msc%d: MMC/SD/SDIO CRC error, MMC_STAT 0x%x, cmd=%d\n",
+ host->pdev_id, stat,
+ host->curr_mrq? host->curr_mrq->cmd->opcode : -1);
+ 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 == 0)
+ data->bytes_xfered = data->blocks * data->blksz;
+ else
+ data->bytes_xfered = 0;
+
+ TRACE_DATA_DONE();
+
+ // jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE);
+ if (host->curr_mrq->stop) {
+ if ((!(REG_MSC_STAT(host->pdev_id) & MSC_STAT_AUTO_CMD_DONE)) && data->error)
+ jz_mmc_send_stop_cmd(host);
+ else
+ while(!(REG_MSC_STAT(host->pdev_id) & (MSC_STAT_AUTO_CMD_DONE | MSC_STAT_ERR_BITS)));
+
+ REG_MSC_CMDAT(host->pdev_id) &= ~(MSC_CMDAT_SEND_AS_STOP);
+ }
+
+ if (host->data_err) {
+ data->bytes_xfered = 0;
+ host->data_err = 0;
+ }
+
+ return 0;
+}
+
+void jz_mmc_execute_cmd(struct jz_mmc_host *host)
+{
+ struct mmc_request *mrq = host->curr_mrq;
+ struct mmc_data *data = mrq->data;
+ struct mmc_command *cmd = mrq->cmd;
+ unsigned int stat;
+ int err = 0;
+
+ /* mask interrupts */
+ REG_MSC_IMASK(host->pdev_id) = 0xffff;
+ /* clear status */
+ REG_MSC_IREG(host->pdev_id) = 0xffff;
+
+ jz_mmc_set_cmdat(host);
+ REG_MSC_CMD(host->pdev_id) = cmd->opcode;
+ jz_mmc_set_cmdarg(host);
+
+ /* reset NOB and BLKLEN */
+ //REG_MSC_NOB(host->pdev_id) = 0;
+ //REG_MSC_BLKLEN(host->pdev_id) = 0;
+
+ TRACE_CMD_REQ();
+
+ if(data && (data->flags & MMC_DATA_READ))
+ jz_mmc_data_start(host);
+
+ REG_MSC_RESTO(host->pdev_id) = 0xff;
+ /* Send command */
+ REG_MSC_STRPCL(host->pdev_id) = MSC_STRPCL_START_OP;
+ stat = jz_mmc_wait_cmd_done(host);
+ if (cmd->error)
+ goto cmd_err;
+
+ TRACE_DATA_REQ();
+
+ if (jz_mmc_parse_cmd_response(host, stat))
+ goto cmd_err;
+
+ if (host->curr_mrq->data) {
+ int acked = 0;
+ if(host->curr_mrq->data->flags & MMC_DATA_WRITE) {
+ jz_mmc_enable_irq(host, MSC_IMASK_DATA_TRAN_DONE);
+ jz_mmc_data_start(host);
+ }
+
+ err = wait_event_interruptible_timeout(
+ host->data_wait_queue,
+ host->data_ack ||
+ (REG_MSC_STAT(host->pdev_id) & WAITMASK),
+ 6 * HZ);
+
+ while(!(REG_MSC_STAT(host->pdev_id) & MSC_STAT_DATA_TRAN_DONE));
+ REG_MSC_STAT(host->pdev_id) &= ~(MSC_STAT_DATA_TRAN_DONE);
+
+ acked = host->data_ack;
+ host->data_ack = 0;
+
+ if (acked)
+ jz_mmc_data_done(host);
+ else {
+ if (err == -ERESTARTSYS) /* user cancelled */
+ cmd->error = -ECANCELED;
+ else if (!err) {
+ printk("Timeout while IRQ_dma, opcode = %d\n", cmd->opcode);
+ printk("REG_MSC_STAT(host->pdev_id) = %x\n", REG_MSC_STAT(host->pdev_id));
+ jz_mmc_dump_regs(host->pdev_id, __LINE__);
+ cmd->error = -ETIMEDOUT;
+ }
+ goto data_wait_err;
+ }
+
+ }
+ return;
+
+cmd_err:
+data_wait_err:
+ if (host->curr_mrq->data)
+ host->curr_mrq->data->bytes_xfered = 0;
+
+ if (host->curr_mrq->data)
+ jz_mmc_data_stop(host);
+}
+
+static irqreturn_t jz_mmc_irq(int irq, void *devid)
+{
+ struct jz_mmc_host *host = devid;
+ unsigned int ireg = 0;
+
+ ireg = REG_MSC_IREG(host->pdev_id);
+ if (ireg) {
+ if (ireg & MSC_IREG_DATA_TRAN_DONE) {
+ jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE);
+ BUG_ON(host->data_ack);
+ host->data_ack = 1;
+ wmb();
+ wake_up_interruptible(&host->data_wait_queue);
+ }
+ }
+
+
+ return IRQ_HANDLED;
+}
+
+static char *msc_trans_irq_name[] = {
+ "msc_trans_0",
+ "msc_trans_1",
+ "msc_trans_2",
+};
+
+int jz_mmc_msc_init(struct jz_mmc_host *host)
+{
+ int ret = 0;
+
+ jz_mmc_reset(host);
+
+ host->data_ack = 0;
+ init_waitqueue_head(&host->data_wait_queue);
+#if 0
+ init_waitqueue_head(&host->status_check_queue);
+ init_timer(&host->status_check_timer);
+ host->status_check_timer.function = jz_mmc_status_checker;
+#endif
+
+ ret = request_irq(host->irq, jz_mmc_irq, 0, msc_trans_irq_name[host->pdev_id], host);
+ if (ret) {
+ printk(KERN_ERR "MMC/SD: can't request MMC/SD IRQ\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+void jz_mmc_msc_deinit(struct jz_mmc_host *host)
+{
+ free_irq(host->irq, &host);
+}
Please sign in to comment.
Something went wrong with that request. Please try again.