diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-21 02:28:57 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-21 02:28:57 +0300 |
commit | ff0c7e18629b8bd64681313a88ce55e182c9fee6 (patch) | |
tree | 70efa650f6458514e1d7e5e3f72cc8f87fc5cf2c /drivers/mmc/host | |
parent | 5b0ed5964928b0aaf0d644c17c886c7f5ea4bb3f (diff) | |
parent | a1f925bc4fa899b3c0f2dcbc432d572c36e74e71 (diff) | |
download | linux-ff0c7e18629b8bd64681313a88ce55e182c9fee6.tar.xz |
Merge tag 'arm-boardfile-remove-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC boardfile updates from Arnd Bergmann
"Unused boardfile removal for 6.3
This is a follow-up to the deprecation of most of the old-style board
files that was merged in linux-6.0, removing them for good.
This branch is almost exclusively dead code removal based on those
annotations. Some device driver removals went through separate
subsystem trees, but the majority is in the same branch, in order to
better handle dependencies between the patches and avoid breaking
bisection.
Unfortunately that leads to merge conflicts against other changes in
the subsystem trees, but they should all be trivial to resolve by
removing the files.
See commit 7d0d3fa7339e ("Merge tag 'arm-boardfiles-6.0' of
git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc") for the
description of which machines were marked unused and are now removed.
The only removals that got postponed are Terastation WXL (mv78xx0) and
Jornada720 (StrongARM1100), which turned out to still have potential
users"
* tag 'arm-boardfile-remove-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (91 commits)
mmc: omap: drop TPS65010 dependency
ARM: pxa: restore mfp-pxa320.h
usb: ohci-omap: avoid unused-variable warning
ARM: debug: remove references in DEBUG_UART_8250_SHIFT to removed configs
ARM: s3c: remove obsolete s3c-cpu-freq header
MAINTAINERS: adjust SAMSUNG SOC CLOCK DRIVERS after s3c24xx support removal
MAINTAINERS: update file entries after arm multi-platform rework and mach-pxa removal
ARM: remove CONFIG_UNUSED_BOARD_FILES
mfd: remove htc-pasic3 driver
w1: remove ds1wm driver
usb: remove ohci-tmio driver
fbdev: remove w100fb driver
fbdev: remove tmiofb driver
mmc: remove tmio_mmc driver
mfd: remove ucb1400 support
mfd: remove toshiba tmio drivers
rtc: remove v3020 driver
power: remove pda_power supply driver
ASoC: pxa: remove unused board support
pcmcia: remove unused pxa/sa1100 drivers
...
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r-- | drivers/mmc/host/Kconfig | 68 | ||||
-rw-r--r-- | drivers/mmc/host/Makefile | 3 | ||||
-rw-r--r-- | drivers/mmc/host/s3cmci.c | 1777 | ||||
-rw-r--r-- | drivers/mmc/host/s3cmci.h | 75 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-cns3xxx.c | 113 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc.c | 227 |
6 files changed, 2 insertions, 2261 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 5e19a961c34d..e13b0b0b8ebb 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -262,17 +262,6 @@ config MMC_SDHCI_CADENCE If unsure, say N. -config MMC_SDHCI_CNS3XXX - tristate "SDHCI support on the Cavium Networks CNS3xxx SoC" - depends on ARCH_CNS3XXX || COMPILE_TEST - depends on MMC_SDHCI_PLTFM - help - This selects the SDHCI support for CNS3xxx System-on-Chip devices. - - If you have a controller with this interface, say Y or M here. - - If unsure, say N. - config MMC_SDHCI_ESDHC_MCF tristate "SDHCI support for the Freescale eSDHC ColdFire controller" depends on M5441x @@ -332,9 +321,8 @@ config MMC_SDHCI_S3C depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST help This selects the Secure Digital Host Controller Interface (SDHCI) - often referrered to as the HSMMC block in some of the Samsung S3C - (S3C2416, S3C2443, S3C6410), S5Pv210 and Exynos (Exynso4210, - Exynos4412) SoCs. + often referrered to as the HSMMC block in some of the Samsung + S3C6410, S5Pv210 and Exynos (Exynso4210, Exynos4412) SoCs. If you have a controller with this interface (thereforeyou build for such Samsung SoC), say Y or M here. @@ -500,7 +488,6 @@ config MMC_SDHCI_ST config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP - depends on TPS65010 || !MACH_OMAP_H2 help This selects the TI OMAP Multimedia card Interface. If you have an OMAP board with a Multimedia Card slot, @@ -640,49 +627,6 @@ config MMC_SPI If unsure, or if your system has no SPI master driver, say N. -config MMC_S3C - tristate "Samsung S3C SD/MMC Card Interface support" - depends on ARCH_S3C24XX || COMPILE_TEST - depends on S3C24XX_DMAC || COMPILE_TEST - help - This selects a driver for the MCI interface found in - Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. - If you have a board based on one of those and a MMC/SD - slot, say Y or M here. - - If unsure, say N. - -config MMC_S3C_HW_SDIO_IRQ - bool "Hardware support for SDIO IRQ" - depends on MMC_S3C - help - Enable the hardware support for SDIO interrupts instead of using - the generic polling code. - -choice - prompt "Samsung S3C SD/MMC transfer code" - depends on MMC_S3C - -config MMC_S3C_PIO - bool "Use PIO transfers only" - help - Use PIO to transfer data between memory and the hardware. - - PIO is slower than DMA as it requires CPU instructions to - move the data. This has been the traditional default for - the S3C MCI driver. - -config MMC_S3C_DMA - bool "Use DMA transfers only" - help - Use DMA to transfer data between memory and the hardware. - - Currently, the DMA support in this driver seems to not be - working properly and needs to be debugged before this - option is useful. - -endchoice - config MMC_SDRICOH_CS tristate "MMC/SD driver for Ricoh Bay1Controllers" depends on PCI && PCMCIA @@ -710,14 +654,6 @@ config MMC_SDHCI_SPRD config MMC_TMIO_CORE tristate -config MMC_TMIO - tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support" - depends on MFD_TMIO || MFD_ASIC3 || COMPILE_TEST - select MMC_TMIO_CORE - help - This provides support for the SD/MMC cell found in TC6393XB, - T7L66XB and also HTC ASIC3 - config MMC_SDHI tristate "Renesas SDHI SD/SDIO controller support" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index ba0c6d0cd85d..b498c17cd124 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -34,9 +34,7 @@ obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o -obj-$(CONFIG_MMC_S3C) += s3cmci.o obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o -obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_core.o obj-$(CONFIG_MMC_SDHI_SYS_DMAC) += renesas_sdhi_sys_dmac.o @@ -77,7 +75,6 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o -obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF) += sdhci-esdhc-mcf.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c deleted file mode 100644 index 8d5929a32d34..000000000000 --- a/drivers/mmc/host/s3cmci.c +++ /dev/null @@ -1,1777 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver - * - * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> - * - * Current driver maintained by Ben Dooks and Simtec Electronics - * Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org> - */ - -#include <linux/module.h> -#include <linux/dmaengine.h> -#include <linux/dma-mapping.h> -#include <linux/clk.h> -#include <linux/mmc/host.h> -#include <linux/platform_device.h> -#include <linux/cpufreq.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/gpio/consumer.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/mmc/slot-gpio.h> -#include <linux/platform_data/mmc-s3cmci.h> - -#include "s3cmci.h" - -#define DRIVER_NAME "s3c-mci" - -#define S3C2410_SDICON (0x00) -#define S3C2410_SDIPRE (0x04) -#define S3C2410_SDICMDARG (0x08) -#define S3C2410_SDICMDCON (0x0C) -#define S3C2410_SDICMDSTAT (0x10) -#define S3C2410_SDIRSP0 (0x14) -#define S3C2410_SDIRSP1 (0x18) -#define S3C2410_SDIRSP2 (0x1C) -#define S3C2410_SDIRSP3 (0x20) -#define S3C2410_SDITIMER (0x24) -#define S3C2410_SDIBSIZE (0x28) -#define S3C2410_SDIDCON (0x2C) -#define S3C2410_SDIDCNT (0x30) -#define S3C2410_SDIDSTA (0x34) -#define S3C2410_SDIFSTA (0x38) - -#define S3C2410_SDIDATA (0x3C) -#define S3C2410_SDIIMSK (0x40) - -#define S3C2440_SDIDATA (0x40) -#define S3C2440_SDIIMSK (0x3C) - -#define S3C2440_SDICON_SDRESET (1 << 8) -#define S3C2410_SDICON_SDIOIRQ (1 << 3) -#define S3C2410_SDICON_FIFORESET (1 << 1) -#define S3C2410_SDICON_CLOCKTYPE (1 << 0) - -#define S3C2410_SDICMDCON_LONGRSP (1 << 10) -#define S3C2410_SDICMDCON_WAITRSP (1 << 9) -#define S3C2410_SDICMDCON_CMDSTART (1 << 8) -#define S3C2410_SDICMDCON_SENDERHOST (1 << 6) -#define S3C2410_SDICMDCON_INDEX (0x3f) - -#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12) -#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11) -#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10) -#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9) - -#define S3C2440_SDIDCON_DS_WORD (2 << 22) -#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20) -#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19) -#define S3C2410_SDIDCON_BLOCKMODE (1 << 17) -#define S3C2410_SDIDCON_WIDEBUS (1 << 16) -#define S3C2410_SDIDCON_DMAEN (1 << 15) -#define S3C2410_SDIDCON_STOP (1 << 14) -#define S3C2440_SDIDCON_DATSTART (1 << 14) - -#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12) -#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12) - -#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF) - -#define S3C2410_SDIDSTA_SDIOIRQDETECT (1 << 9) -#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8) -#define S3C2410_SDIDSTA_CRCFAIL (1 << 7) -#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6) -#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5) -#define S3C2410_SDIDSTA_XFERFINISH (1 << 4) -#define S3C2410_SDIDSTA_TXDATAON (1 << 1) -#define S3C2410_SDIDSTA_RXDATAON (1 << 0) - -#define S3C2440_SDIFSTA_FIFORESET (1 << 16) -#define S3C2440_SDIFSTA_FIFOFAIL (3 << 14) -#define S3C2410_SDIFSTA_TFDET (1 << 13) -#define S3C2410_SDIFSTA_RFDET (1 << 12) -#define S3C2410_SDIFSTA_COUNTMASK (0x7f) - -#define S3C2410_SDIIMSK_RESPONSECRC (1 << 17) -#define S3C2410_SDIIMSK_CMDSENT (1 << 16) -#define S3C2410_SDIIMSK_CMDTIMEOUT (1 << 15) -#define S3C2410_SDIIMSK_RESPONSEND (1 << 14) -#define S3C2410_SDIIMSK_SDIOIRQ (1 << 12) -#define S3C2410_SDIIMSK_FIFOFAIL (1 << 11) -#define S3C2410_SDIIMSK_CRCSTATUS (1 << 10) -#define S3C2410_SDIIMSK_DATACRC (1 << 9) -#define S3C2410_SDIIMSK_DATATIMEOUT (1 << 8) -#define S3C2410_SDIIMSK_DATAFINISH (1 << 7) -#define S3C2410_SDIIMSK_TXFIFOHALF (1 << 4) -#define S3C2410_SDIIMSK_RXFIFOLAST (1 << 2) -#define S3C2410_SDIIMSK_RXFIFOHALF (1 << 0) - -enum dbg_channels { - dbg_err = (1 << 0), - dbg_debug = (1 << 1), - dbg_info = (1 << 2), - dbg_irq = (1 << 3), - dbg_sg = (1 << 4), - dbg_dma = (1 << 5), - dbg_pio = (1 << 6), - dbg_fail = (1 << 7), - dbg_conf = (1 << 8), -}; - -static const int dbgmap_err = dbg_fail; -static const int dbgmap_info = dbg_info | dbg_conf; -static const int dbgmap_debug = dbg_err | dbg_debug; - -#define dbg(host, channels, args...) \ - do { \ - if (dbgmap_err & channels) \ - dev_err(&host->pdev->dev, args); \ - else if (dbgmap_info & channels) \ - dev_info(&host->pdev->dev, args); \ - else if (dbgmap_debug & channels) \ - dev_dbg(&host->pdev->dev, args); \ - } while (0) - -static void finalize_request(struct s3cmci_host *host); -static void s3cmci_send_request(struct mmc_host *mmc); -static void s3cmci_reset(struct s3cmci_host *host); - -#ifdef CONFIG_MMC_DEBUG - -static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) -{ - u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer; - u32 datcon, datcnt, datsta, fsta; - - con = readl(host->base + S3C2410_SDICON); - pre = readl(host->base + S3C2410_SDIPRE); - cmdarg = readl(host->base + S3C2410_SDICMDARG); - cmdcon = readl(host->base + S3C2410_SDICMDCON); - cmdsta = readl(host->base + S3C2410_SDICMDSTAT); - r0 = readl(host->base + S3C2410_SDIRSP0); - r1 = readl(host->base + S3C2410_SDIRSP1); - r2 = readl(host->base + S3C2410_SDIRSP2); - r3 = readl(host->base + S3C2410_SDIRSP3); - timer = readl(host->base + S3C2410_SDITIMER); - datcon = readl(host->base + S3C2410_SDIDCON); - datcnt = readl(host->base + S3C2410_SDIDCNT); - datsta = readl(host->base + S3C2410_SDIDSTA); - fsta = readl(host->base + S3C2410_SDIFSTA); - - dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n", - prefix, con, pre, timer); - - dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n", - prefix, cmdcon, cmdarg, cmdsta); - - dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]" - " DSTA:[%08x] DCNT:[%08x]\n", - prefix, datcon, fsta, datsta, datcnt); - - dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]" - " R2:[%08x] R3:[%08x]\n", - prefix, r0, r1, r2, r3); -} - -static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd, - int stop) -{ - snprintf(host->dbgmsg_cmd, 300, - "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u", - host->ccnt, (stop ? " (STOP)" : ""), - cmd->opcode, cmd->arg, cmd->flags, cmd->retries); - - if (cmd->data) { - snprintf(host->dbgmsg_dat, 300, - "#%u bsize:%u blocks:%u bytes:%u", - host->dcnt, cmd->data->blksz, - cmd->data->blocks, - cmd->data->blocks * cmd->data->blksz); - } else { - host->dbgmsg_dat[0] = '\0'; - } -} - -static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd, - int fail) -{ - unsigned int dbglvl = fail ? dbg_fail : dbg_debug; - - if (!cmd) - return; - - if (cmd->error == 0) { - dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n", - host->dbgmsg_cmd, cmd->resp[0]); - } else { - dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n", - cmd->error, host->dbgmsg_cmd, host->status); - } - - if (!cmd->data) - return; - - if (cmd->data->error == 0) { - dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat); - } else { - dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n", - cmd->data->error, host->dbgmsg_dat, - readl(host->base + S3C2410_SDIDCNT)); - } -} -#else -static void dbg_dumpcmd(struct s3cmci_host *host, - struct mmc_command *cmd, int fail) { } - -static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd, - int stop) { } - -static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { } - -#endif /* CONFIG_MMC_DEBUG */ - -/** - * s3cmci_host_usedma - return whether the host is using dma or pio - * @host: The host state - * - * Return true if the host is using DMA to transfer data, else false - * to use PIO mode. Will return static data depending on the driver - * configuration. - */ -static inline bool s3cmci_host_usedma(struct s3cmci_host *host) -{ -#ifdef CONFIG_MMC_S3C_PIO - return false; -#else /* CONFIG_MMC_S3C_DMA */ - return true; -#endif -} - -static inline u32 enable_imask(struct s3cmci_host *host, u32 imask) -{ - u32 newmask; - - newmask = readl(host->base + host->sdiimsk); - newmask |= imask; - - writel(newmask, host->base + host->sdiimsk); - - return newmask; -} - -static inline u32 disable_imask(struct s3cmci_host *host, u32 imask) -{ - u32 newmask; - - newmask = readl(host->base + host->sdiimsk); - newmask &= ~imask; - - writel(newmask, host->base + host->sdiimsk); - - return newmask; -} - -static inline void clear_imask(struct s3cmci_host *host) -{ - u32 mask = readl(host->base + host->sdiimsk); - - /* preserve the SDIO IRQ mask state */ - mask &= S3C2410_SDIIMSK_SDIOIRQ; - writel(mask, host->base + host->sdiimsk); -} - -/** - * s3cmci_check_sdio_irq - test whether the SDIO IRQ is being signalled - * @host: The host to check. - * - * Test to see if the SDIO interrupt is being signalled in case the - * controller has failed to re-detect a card interrupt. Read GPE8 and - * see if it is low and if so, signal a SDIO interrupt. - * - * This is currently called if a request is finished (we assume that the - * bus is now idle) and when the SDIO IRQ is enabled in case the IRQ is - * already being indicated. -*/ -static void s3cmci_check_sdio_irq(struct s3cmci_host *host) -{ - if (host->sdio_irqen) { - if (host->pdata->bus[3] && - gpiod_get_value(host->pdata->bus[3]) == 0) { - pr_debug("%s: signalling irq\n", __func__); - mmc_signal_sdio_irq(host->mmc); - } - } -} - -static inline int get_data_buffer(struct s3cmci_host *host, - u32 *bytes, u32 **pointer) -{ - struct scatterlist *sg; - - if (host->pio_active == XFER_NONE) - return -EINVAL; - - if ((!host->mrq) || (!host->mrq->data)) - return -EINVAL; - - if (host->pio_sgptr >= host->mrq->data->sg_len) { - dbg(host, dbg_debug, "no more buffers (%i/%i)\n", - host->pio_sgptr, host->mrq->data->sg_len); - return -EBUSY; - } - sg = &host->mrq->data->sg[host->pio_sgptr]; - - *bytes = sg->length; - *pointer = sg_virt(sg); - - host->pio_sgptr++; - - dbg(host, dbg_sg, "new buffer (%i/%i)\n", - host->pio_sgptr, host->mrq->data->sg_len); - - return 0; -} - -static inline u32 fifo_count(struct s3cmci_host *host) -{ - u32 fifostat = readl(host->base + S3C2410_SDIFSTA); - - fifostat &= S3C2410_SDIFSTA_COUNTMASK; - return fifostat; -} - -static inline u32 fifo_free(struct s3cmci_host *host) -{ - u32 fifostat = readl(host->base + S3C2410_SDIFSTA); - - fifostat &= S3C2410_SDIFSTA_COUNTMASK; - return 63 - fifostat; -} - -/** - * s3cmci_enable_irq - enable IRQ, after having disabled it. - * @host: The device state. - * @more: True if more IRQs are expected from transfer. - * - * Enable the main IRQ if needed after it has been disabled. - * - * The IRQ can be one of the following states: - * - disabled during IDLE - * - disabled whilst processing data - * - enabled during transfer - * - enabled whilst awaiting SDIO interrupt detection - */ -static void s3cmci_enable_irq(struct s3cmci_host *host, bool more) -{ - unsigned long flags; - bool enable = false; - - local_irq_save(flags); - - host->irq_enabled = more; - host->irq_disabled = false; - - enable = more | host->sdio_irqen; - - if (host->irq_state != enable) { - host->irq_state = enable; - - if (enable) - enable_irq(host->irq); - else - disable_irq(host->irq); - } - - local_irq_restore(flags); -} - -static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer) -{ - unsigned long flags; - - local_irq_save(flags); - - /* pr_debug("%s: transfer %d\n", __func__, transfer); */ - - host->irq_disabled = transfer; - - if (transfer && host->irq_state) { - host->irq_state = false; - disable_irq(host->irq); - } - - local_irq_restore(flags); -} - -static void do_pio_read(struct s3cmci_host *host) -{ - int res; - u32 fifo; - u32 *ptr; - u32 fifo_words; - void __iomem *from_ptr; - - /* write real prescaler to host, it might be set slow to fix */ - writel(host->prescaler, host->base + S3C2410_SDIPRE); - - from_ptr = host->base + host->sdidata; - - while ((fifo = fifo_count(host))) { - if (!host->pio_bytes) { - res = get_data_buffer(host, &host->pio_bytes, - &host->pio_ptr); - if (res) { - host->pio_active = XFER_NONE; - host->complete_what = COMPLETION_FINALIZE; - - dbg(host, dbg_pio, "pio_read(): " - "complete (no more data).\n"); - return; - } - - dbg(host, dbg_pio, - "pio_read(): new target: [%i]@[%p]\n", - host->pio_bytes, host->pio_ptr); - } - - dbg(host, dbg_pio, - "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", - fifo, host->pio_bytes, - readl(host->base + S3C2410_SDIDCNT)); - - /* If we have reached the end of the block, we can - * read a word and get 1 to 3 bytes. If we in the - * middle of the block, we have to read full words, - * otherwise we will write garbage, so round down to - * an even multiple of 4. */ - if (fifo >= host->pio_bytes) - fifo = host->pio_bytes; - else - fifo -= fifo & 3; - - host->pio_bytes -= fifo; - host->pio_count += fifo; - - fifo_words = fifo >> 2; - ptr = host->pio_ptr; - while (fifo_words--) - *ptr++ = readl(from_ptr); - host->pio_ptr = ptr; - - if (fifo & 3) { - u32 n = fifo & 3; - u32 data = readl(from_ptr); - u8 *p = (u8 *)host->pio_ptr; - - while (n--) { - *p++ = data; - data >>= 8; - } - } - } - - if (!host->pio_bytes) { - res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr); - if (res) { - dbg(host, dbg_pio, - "pio_read(): complete (no more buffers).\n"); - host->pio_active = XFER_NONE; - host->complete_what = COMPLETION_FINALIZE; - - return; - } - } - - enable_imask(host, - S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); -} - -static void do_pio_write(struct s3cmci_host *host) -{ - void __iomem *to_ptr; - int res; - u32 fifo; - u32 *ptr; - - to_ptr = host->base + host->sdidata; - - while ((fifo = fifo_free(host)) > 3) { - if (!host->pio_bytes) { - res = get_data_buffer(host, &host->pio_bytes, - &host->pio_ptr); - if (res) { - dbg(host, dbg_pio, - "pio_write(): complete (no more data).\n"); - host->pio_active = XFER_NONE; - - return; - } - - dbg(host, dbg_pio, - "pio_write(): new source: [%i]@[%p]\n", - host->pio_bytes, host->pio_ptr); - - } - - /* If we have reached the end of the block, we have to - * write exactly the remaining number of bytes. If we - * in the middle of the block, we have to write full - * words, so round down to an even multiple of 4. */ - if (fifo >= host->pio_bytes) - fifo = host->pio_bytes; - else - fifo -= fifo & 3; - - host->pio_bytes -= fifo; - host->pio_count += fifo; - - fifo = (fifo + 3) >> 2; - ptr = host->pio_ptr; - while (fifo--) - writel(*ptr++, to_ptr); - host->pio_ptr = ptr; - } - - enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); -} - -static void pio_tasklet(struct tasklet_struct *t) -{ - struct s3cmci_host *host = from_tasklet(host, t, pio_tasklet); - - s3cmci_disable_irq(host, true); - - if (host->pio_active == XFER_WRITE) - do_pio_write(host); - - if (host->pio_active == XFER_READ) - do_pio_read(host); - - if (host->complete_what == COMPLETION_FINALIZE) { - clear_imask(host); - if (host->pio_active != XFER_NONE) { - dbg(host, dbg_err, "unfinished %s " - "- pio_count:[%u] pio_bytes:[%u]\n", - (host->pio_active == XFER_READ) ? "read" : "write", - host->pio_count, host->pio_bytes); - - if (host->mrq->data) - host->mrq->data->error = -EINVAL; - } - - s3cmci_enable_irq(host, false); - finalize_request(host); - } else - s3cmci_enable_irq(host, true); -} - -/* - * ISR for SDI Interface IRQ - * Communication between driver and ISR works as follows: - * host->mrq points to current request - * host->complete_what Indicates when the request is considered done - * COMPLETION_CMDSENT when the command was sent - * COMPLETION_RSPFIN when a response was received - * COMPLETION_XFERFINISH when the data transfer is finished - * COMPLETION_XFERFINISH_RSPFIN both of the above. - * host->complete_request is the completion-object the driver waits for - * - * 1) Driver sets up host->mrq and host->complete_what - * 2) Driver prepares the transfer - * 3) Driver enables interrupts - * 4) Driver starts transfer - * 5) Driver waits for host->complete_rquest - * 6) ISR checks for request status (errors and success) - * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error - * 7) ISR completes host->complete_request - * 8) ISR disables interrupts - * 9) Driver wakes up and takes care of the request - * - * Note: "->error"-fields are expected to be set to 0 before the request - * was issued by mmc.c - therefore they are only set, when an error - * contition comes up - */ - -static irqreturn_t s3cmci_irq(int irq, void *dev_id) -{ - struct s3cmci_host *host = dev_id; - struct mmc_command *cmd; - u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; - u32 mci_cclear = 0, mci_dclear; - unsigned long iflags; - - mci_dsta = readl(host->base + S3C2410_SDIDSTA); - mci_imsk = readl(host->base + host->sdiimsk); - - if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) { - if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) { - mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT; - writel(mci_dclear, host->base + S3C2410_SDIDSTA); - - mmc_signal_sdio_irq(host->mmc); - return IRQ_HANDLED; - } - } - - spin_lock_irqsave(&host->complete_lock, iflags); - - mci_csta = readl(host->base + S3C2410_SDICMDSTAT); - mci_dcnt = readl(host->base + S3C2410_SDIDCNT); - mci_fsta = readl(host->base + S3C2410_SDIFSTA); - mci_dclear = 0; - - if ((host->complete_what == COMPLETION_NONE) || - (host->complete_what == COMPLETION_FINALIZE)) { - host->status = "nothing to complete"; - clear_imask(host); - goto irq_out; - } - - if (!host->mrq) { - host->status = "no active mrq"; - clear_imask(host); - goto irq_out; - } - - cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; - - if (!cmd) { - host->status = "no active cmd"; - clear_imask(host); - goto irq_out; - } - - if (!s3cmci_host_usedma(host)) { - if ((host->pio_active == XFER_WRITE) && - (mci_fsta & S3C2410_SDIFSTA_TFDET)) { - - disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); - tasklet_schedule(&host->pio_tasklet); - host->status = "pio tx"; - } - - if ((host->pio_active == XFER_READ) && - (mci_fsta & S3C2410_SDIFSTA_RFDET)) { - - disable_imask(host, - S3C2410_SDIIMSK_RXFIFOHALF | - S3C2410_SDIIMSK_RXFIFOLAST); - - tasklet_schedule(&host->pio_tasklet); - host->status = "pio rx"; - } - } - - if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { - dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n"); - cmd->error = -ETIMEDOUT; - host->status = "error: command timeout"; - goto fail_transfer; - } - - if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) { - if (host->complete_what == COMPLETION_CMDSENT) { - host->status = "ok: command sent"; - goto close_transfer; - } - - mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT; - } - - if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) { - if (cmd->flags & MMC_RSP_CRC) { - if (host->mrq->cmd->flags & MMC_RSP_136) { - dbg(host, dbg_irq, - "fixup: ignore CRC fail with long rsp\n"); - } else { - /* note, we used to fail the transfer - * here, but it seems that this is just - * the hardware getting it wrong. - * - * cmd->error = -EILSEQ; - * host->status = "error: bad command crc"; - * goto fail_transfer; - */ - } - } - - mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL; - } - - if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) { - if (host->complete_what == COMPLETION_RSPFIN) { - host->status = "ok: command response received"; - goto close_transfer; - } - - if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) - host->complete_what = COMPLETION_XFERFINISH; - - mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN; - } - - /* errors handled after this point are only relevant - when a data transfer is in progress */ - - if (!cmd->data) - goto clear_status_bits; - - /* Check for FIFO failure */ - if (host->is2440) { - if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) { - dbg(host, dbg_err, "FIFO failure\n"); - host->mrq->data->error = -EILSEQ; - host->status = "error: 2440 fifo failure"; - goto fail_transfer; - } - } else { - if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) { - dbg(host, dbg_err, "FIFO failure\n"); - cmd->data->error = -EILSEQ; - host->status = "error: fifo failure"; - goto fail_transfer; - } - } - - if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) { - dbg(host, dbg_err, "bad data crc (outgoing)\n"); - cmd->data->error = -EILSEQ; - host->status = "error: bad data crc (outgoing)"; - goto fail_transfer; - } - - if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) { - dbg(host, dbg_err, "bad data crc (incoming)\n"); - cmd->data->error = -EILSEQ; - host->status = "error: bad data crc (incoming)"; - goto fail_transfer; - } - - if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) { - dbg(host, dbg_err, "data timeout\n"); - cmd->data->error = -ETIMEDOUT; - host->status = "error: data timeout"; - goto fail_transfer; - } - - if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) { - if (host->complete_what == COMPLETION_XFERFINISH) { - host->status = "ok: data transfer completed"; - goto close_transfer; - } - - if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) - host->complete_what = COMPLETION_RSPFIN; - - mci_dclear |= S3C2410_SDIDSTA_XFERFINISH; - } - -clear_status_bits: - writel(mci_cclear, host->base + S3C2410_SDICMDSTAT); - writel(mci_dclear, host->base + S3C2410_SDIDSTA); - - goto irq_out; - -fail_transfer: - host->pio_active = XFER_NONE; - -close_transfer: - host->complete_what = COMPLETION_FINALIZE; - - clear_imask(host); - tasklet_schedule(&host->pio_tasklet); - - goto irq_out; - -irq_out: - dbg(host, dbg_irq, - "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n", - mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status); - - spin_unlock_irqrestore(&host->complete_lock, iflags); - return IRQ_HANDLED; - -} - -static void s3cmci_dma_done_callback(void *arg) -{ - struct s3cmci_host *host = arg; - unsigned long iflags; - - BUG_ON(!host->mrq); - BUG_ON(!host->mrq->data); - - spin_lock_irqsave(&host->complete_lock, iflags); - - dbg(host, dbg_dma, "DMA FINISHED\n"); - - host->dma_complete = 1; - host->complete_what = COMPLETION_FINALIZE; - - tasklet_schedule(&host->pio_tasklet); - spin_unlock_irqrestore(&host->complete_lock, iflags); - -} - -static void finalize_request(struct s3cmci_host *host) -{ - struct mmc_request *mrq = host->mrq; - struct mmc_command *cmd; - int debug_as_failure = 0; - - if (host->complete_what != COMPLETION_FINALIZE) - return; - - if (!mrq) - return; - cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; - - if (cmd->data && (cmd->error == 0) && - (cmd->data->error == 0)) { - if (s3cmci_host_usedma(host) && (!host->dma_complete)) { - dbg(host, dbg_dma, "DMA Missing (%d)!\n", - host->dma_complete); - return; - } - } - - /* Read response from controller. */ - cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0); - cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1); - cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2); - cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3); - - writel(host->prescaler, host->base + S3C2410_SDIPRE); - - if (cmd->error) - debug_as_failure = 1; - - if (cmd->data && cmd->data->error) - debug_as_failure = 1; - - dbg_dumpcmd(host, cmd, debug_as_failure); - - /* Cleanup controller */ - writel(0, host->base + S3C2410_SDICMDARG); - writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); - writel(0, host->base + S3C2410_SDICMDCON); - clear_imask(host); - - if (cmd->data && cmd->error) - cmd->data->error = cmd->error; - - if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) { - host->cmd_is_stop = 1; - s3cmci_send_request(host->mmc); - return; - } - - /* If we have no data transfer we are finished here */ - if (!mrq->data) - goto request_done; - - /* Calculate the amout of bytes transfer if there was no error */ - if (mrq->data->error == 0) { - mrq->data->bytes_xfered = - (mrq->data->blocks * mrq->data->blksz); - } else { - mrq->data->bytes_xfered = 0; - } - - /* If we had an error while transferring data we flush the - * DMA channel and the fifo to clear out any garbage. */ - if (mrq->data->error != 0) { - if (s3cmci_host_usedma(host)) - dmaengine_terminate_all(host->dma); - - if (host->is2440) { - /* Clear failure register and reset fifo. */ - writel(S3C2440_SDIFSTA_FIFORESET | - S3C2440_SDIFSTA_FIFOFAIL, - host->base + S3C2410_SDIFSTA); - } else { - u32 mci_con; - - /* reset fifo */ - mci_con = readl(host->base + S3C2410_SDICON); - mci_con |= S3C2410_SDICON_FIFORESET; - - writel(mci_con, host->base + S3C2410_SDICON); - } - } - -request_done: - host->complete_what = COMPLETION_NONE; - host->mrq = NULL; - - s3cmci_check_sdio_irq(host); - mmc_request_done(host->mmc, mrq); -} - -static void s3cmci_send_command(struct s3cmci_host *host, - struct mmc_command *cmd) -{ - u32 ccon, imsk; - - imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT | - S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT | - S3C2410_SDIIMSK_RESPONSECRC; - - enable_imask(host, imsk); - - if (cmd->data) - host->complete_what = COMPLETION_XFERFINISH_RSPFIN; - else if (cmd->flags & MMC_RSP_PRESENT) - host->complete_what = COMPLETION_RSPFIN; - else - host->complete_what = COMPLETION_CMDSENT; - - writel(cmd->arg, host->base + S3C2410_SDICMDARG); - - ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX; - ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; - - if (cmd->flags & MMC_RSP_PRESENT) - ccon |= S3C2410_SDICMDCON_WAITRSP; - - if (cmd->flags & MMC_RSP_136) - ccon |= S3C2410_SDICMDCON_LONGRSP; - - writel(ccon, host->base + S3C2410_SDICMDCON); -} - -static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) -{ - u32 dcon, imsk, stoptries = 3; - - if ((data->blksz & 3) != 0) { - /* We cannot deal with unaligned blocks with more than - * one block being transferred. */ - - if (data->blocks > 1) { - pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n", - __func__, data->blksz); - return -EINVAL; - } - } - - while (readl(host->base + S3C2410_SDIDSTA) & - (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { - - dbg(host, dbg_err, - "mci_setup_data() transfer stillin progress.\n"); - - writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); - s3cmci_reset(host); - - if ((stoptries--) == 0) { - dbg_dumpregs(host, "DRF"); - return -EINVAL; - } - } - - dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; - - if (s3cmci_host_usedma(host)) - dcon |= S3C2410_SDIDCON_DMAEN; - - if (host->bus_width == MMC_BUS_WIDTH_4) - dcon |= S3C2410_SDIDCON_WIDEBUS; - - dcon |= S3C2410_SDIDCON_BLOCKMODE; - - if (data->flags & MMC_DATA_WRITE) { - dcon |= S3C2410_SDIDCON_TXAFTERRESP; - dcon |= S3C2410_SDIDCON_XFER_TXSTART; - } - - if (data->flags & MMC_DATA_READ) { - dcon |= S3C2410_SDIDCON_RXAFTERCMD; - dcon |= S3C2410_SDIDCON_XFER_RXSTART; - } - - if (host->is2440) { - dcon |= S3C2440_SDIDCON_DS_WORD; - dcon |= S3C2440_SDIDCON_DATSTART; - } - - writel(dcon, host->base + S3C2410_SDIDCON); - - /* write BSIZE register */ - - writel(data->blksz, host->base + S3C2410_SDIBSIZE); - - /* add to IMASK register */ - imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | - S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; - - enable_imask(host, imsk); - - /* write TIMER register */ - - if (host->is2440) { - writel(0x007FFFFF, host->base + S3C2410_SDITIMER); - } else { - writel(0x0000FFFF, host->base + S3C2410_SDITIMER); - - /* FIX: set slow clock to prevent timeouts on read */ - if (data->flags & MMC_DATA_READ) - writel(0xFF, host->base + S3C2410_SDIPRE); - } - - return 0; -} - -#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ) - -static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) -{ - int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; - - BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); - - host->pio_sgptr = 0; - host->pio_bytes = 0; - host->pio_count = 0; - host->pio_active = rw ? XFER_WRITE : XFER_READ; - - if (rw) { - do_pio_write(host); - enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); - } else { - enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF - | S3C2410_SDIIMSK_RXFIFOLAST); - } - - return 0; -} - -static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) -{ - int rw = data->flags & MMC_DATA_WRITE; - struct dma_async_tx_descriptor *desc; - struct dma_slave_config conf = { - .src_addr = host->mem->start + host->sdidata, - .dst_addr = host->mem->start + host->sdidata, - .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, - .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, - }; - - BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); - - /* Restore prescaler value */ - writel(host->prescaler, host->base + S3C2410_SDIPRE); - - if (!rw) - conf.direction = DMA_DEV_TO_MEM; - else - conf.direction = DMA_MEM_TO_DEV; - - dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - mmc_get_dma_dir(data)); - - dmaengine_slave_config(host->dma, &conf); - desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len, - conf.direction, - DMA_CTRL_ACK | DMA_PREP_INTERRUPT); - if (!desc) - goto unmap_exit; - desc->callback = s3cmci_dma_done_callback; - desc->callback_param = host; - dmaengine_submit(desc); - dma_async_issue_pending(host->dma); - - return 0; - -unmap_exit: - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - mmc_get_dma_dir(data)); - return -ENOMEM; -} - -static void s3cmci_send_request(struct mmc_host *mmc) -{ - struct s3cmci_host *host = mmc_priv(mmc); - struct mmc_request *mrq = host->mrq; - struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; - - host->ccnt++; - prepare_dbgmsg(host, cmd, host->cmd_is_stop); - - /* Clear command, data and fifo status registers - Fifo clear only necessary on 2440, but doesn't hurt on 2410 - */ - writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); - writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); - writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA); - - if (cmd->data) { - int res = s3cmci_setup_data(host, cmd->data); - - host->dcnt++; - - if (res) { - dbg(host, dbg_err, "setup data error %d\n", res); - cmd->error = res; - cmd->data->error = res; - - mmc_request_done(mmc, mrq); - return; - } - - if (s3cmci_host_usedma(host)) - res = s3cmci_prepare_dma(host, cmd->data); - else - res = s3cmci_prepare_pio(host, cmd->data); - - if (res) { - dbg(host, dbg_err, "data prepare error %d\n", res); - cmd->error = res; - cmd->data->error = res; - - mmc_request_done(mmc, mrq); - return; - } - } - - /* Send command */ - s3cmci_send_command(host, cmd); - - /* Enable Interrupt */ - s3cmci_enable_irq(host, true); -} - -static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct s3cmci_host *host = mmc_priv(mmc); - - host->status = "mmc request"; - host->cmd_is_stop = 0; - host->mrq = mrq; - - if (mmc_gpio_get_cd(mmc) == 0) { - dbg(host, dbg_err, "%s: no medium present\n", __func__); - host->mrq->cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, mrq); - } else - s3cmci_send_request(mmc); -} - -static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios) -{ - u32 mci_psc; - - /* Set clock */ - for (mci_psc = 0; mci_psc < 255; mci_psc++) { - host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1)); - - if (host->real_rate <= ios->clock) - break; - } - - if (mci_psc > 255) - mci_psc = 255; - - host->prescaler = mci_psc; - writel(host->prescaler, host->base + S3C2410_SDIPRE); - - /* If requested clock is 0, real_rate will be 0, too */ - if (ios->clock == 0) - host->real_rate = 0; -} - -static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct s3cmci_host *host = mmc_priv(mmc); - u32 mci_con; - - /* Set the power state */ - - mci_con = readl(host->base + S3C2410_SDICON); - - switch (ios->power_mode) { - case MMC_POWER_ON: - case MMC_POWER_UP: - if (!host->is2440) - mci_con |= S3C2410_SDICON_FIFORESET; - break; - - case MMC_POWER_OFF: - default: - if (host->is2440) - mci_con |= S3C2440_SDICON_SDRESET; - break; - } - - if (host->pdata->set_power) - host->pdata->set_power(ios->power_mode, ios->vdd); - - s3cmci_set_clk(host, ios); - - /* Set CLOCK_ENABLE */ - if (ios->clock) - mci_con |= S3C2410_SDICON_CLOCKTYPE; - else - mci_con &= ~S3C2410_SDICON_CLOCKTYPE; - - writel(mci_con, host->base + S3C2410_SDICON); - - if ((ios->power_mode == MMC_POWER_ON) || - (ios->power_mode == MMC_POWER_UP)) { - dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n", - host->real_rate/1000, ios->clock/1000); - } else { - dbg(host, dbg_conf, "powered down.\n"); - } - - host->bus_width = ios->bus_width; -} - -static void s3cmci_reset(struct s3cmci_host *host) -{ - u32 con = readl(host->base + S3C2410_SDICON); - - con |= S3C2440_SDICON_SDRESET; - writel(con, host->base + S3C2410_SDICON); -} - -static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable) -{ - struct s3cmci_host *host = mmc_priv(mmc); - unsigned long flags; - u32 con; - - local_irq_save(flags); - - con = readl(host->base + S3C2410_SDICON); - host->sdio_irqen = enable; - - if (enable == host->sdio_irqen) - goto same_state; - - if (enable) { - con |= S3C2410_SDICON_SDIOIRQ; - enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); - - if (!host->irq_state && !host->irq_disabled) { - host->irq_state = true; - enable_irq(host->irq); - } - } else { - disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); - con &= ~S3C2410_SDICON_SDIOIRQ; - - if (!host->irq_enabled && host->irq_state) { - disable_irq_nosync(host->irq); - host->irq_state = false; - } - } - - writel(con, host->base + S3C2410_SDICON); - - same_state: - local_irq_restore(flags); - - s3cmci_check_sdio_irq(host); -} - -static const struct mmc_host_ops s3cmci_ops = { - .request = s3cmci_request, - .set_ios = s3cmci_set_ios, - .get_ro = mmc_gpio_get_ro, - .get_cd = mmc_gpio_get_cd, - .enable_sdio_irq = s3cmci_enable_sdio_irq, -}; - -#ifdef CONFIG_ARM_S3C24XX_CPUFREQ - -static int s3cmci_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - struct s3cmci_host *host; - struct mmc_host *mmc; - unsigned long newclk; - unsigned long flags; - - host = container_of(nb, struct s3cmci_host, freq_transition); - newclk = clk_get_rate(host->clk); - mmc = host->mmc; - - if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) || - (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) { - spin_lock_irqsave(&mmc->lock, flags); - - host->clk_rate = newclk; - - if (mmc->ios.power_mode != MMC_POWER_OFF && - mmc->ios.clock != 0) - s3cmci_set_clk(host, &mmc->ios); - - spin_unlock_irqrestore(&mmc->lock, flags); - } - - return 0; -} - -static inline int s3cmci_cpufreq_register(struct s3cmci_host *host) -{ - host->freq_transition.notifier_call = s3cmci_cpufreq_transition; - - return cpufreq_register_notifier(&host->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host) -{ - cpufreq_unregister_notifier(&host->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -#else -static inline int s3cmci_cpufreq_register(struct s3cmci_host *host) -{ - return 0; -} - -static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host) -{ -} -#endif - - -#ifdef CONFIG_DEBUG_FS - -static int s3cmci_state_show(struct seq_file *seq, void *v) -{ - struct s3cmci_host *host = seq->private; - - seq_printf(seq, "Register base = 0x%p\n", host->base); - seq_printf(seq, "Clock rate = %ld\n", host->clk_rate); - seq_printf(seq, "Prescale = %d\n", host->prescaler); - seq_printf(seq, "is2440 = %d\n", host->is2440); - seq_printf(seq, "IRQ = %d\n", host->irq); - seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled); - seq_printf(seq, "IRQ disabled = %d\n", host->irq_disabled); - seq_printf(seq, "IRQ state = %d\n", host->irq_state); - seq_printf(seq, "CD IRQ = %d\n", host->irq_cd); - seq_printf(seq, "Do DMA = %d\n", s3cmci_host_usedma(host)); - seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk); - seq_printf(seq, "SDIDATA at %d\n", host->sdidata); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(s3cmci_state); - -#define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r } - -struct s3cmci_reg { - unsigned short addr; - unsigned char *name; -}; - -static const struct s3cmci_reg debug_regs[] = { - DBG_REG(CON), - DBG_REG(PRE), - DBG_REG(CMDARG), - DBG_REG(CMDCON), - DBG_REG(CMDSTAT), - DBG_REG(RSP0), - DBG_REG(RSP1), - DBG_REG(RSP2), - DBG_REG(RSP3), - DBG_REG(TIMER), - DBG_REG(BSIZE), - DBG_REG(DCON), - DBG_REG(DCNT), - DBG_REG(DSTA), - DBG_REG(FSTA), - {} -}; - -static int s3cmci_regs_show(struct seq_file *seq, void *v) -{ - struct s3cmci_host *host = seq->private; - const struct s3cmci_reg *rptr = debug_regs; - - for (; rptr->name; rptr++) - seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name, - readl(host->base + rptr->addr)); - - seq_printf(seq, "SDIIMSK\t=0x%08x\n", readl(host->base + host->sdiimsk)); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(s3cmci_regs); - -static void s3cmci_debugfs_attach(struct s3cmci_host *host) -{ - struct device *dev = &host->pdev->dev; - struct dentry *root; - - root = debugfs_create_dir(dev_name(dev), NULL); - host->debug_root = root; - - debugfs_create_file("state", 0444, root, host, &s3cmci_state_fops); - debugfs_create_file("regs", 0444, root, host, &s3cmci_regs_fops); -} - -static void s3cmci_debugfs_remove(struct s3cmci_host *host) -{ - debugfs_remove_recursive(host->debug_root); -} - -#else -static inline void s3cmci_debugfs_attach(struct s3cmci_host *host) { } -static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { } - -#endif /* CONFIG_DEBUG_FS */ - -static int s3cmci_probe_pdata(struct s3cmci_host *host) -{ - struct platform_device *pdev = host->pdev; - struct mmc_host *mmc = host->mmc; - struct s3c24xx_mci_pdata *pdata; - int i, ret; - - host->is2440 = platform_get_device_id(pdev)->driver_data; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "need platform data"); - return -ENXIO; - } - - for (i = 0; i < 6; i++) { - pdata->bus[i] = devm_gpiod_get_index(&pdev->dev, "bus", i, - GPIOD_OUT_LOW); - if (IS_ERR(pdata->bus[i])) { - dev_err(&pdev->dev, "failed to get gpio %d\n", i); - return PTR_ERR(pdata->bus[i]); - } - } - - if (pdata->no_wprotect) - mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; - - if (pdata->no_detect) - mmc->caps |= MMC_CAP_NEEDS_POLL; - - if (pdata->wprotect_invert) - mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - - /* If we get -ENOENT we have no card detect GPIO line */ - ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); - if (ret != -ENOENT) { - dev_err(&pdev->dev, "error requesting GPIO for CD %d\n", - ret); - return ret; - } - - ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0); - if (ret != -ENOENT) { - dev_err(&pdev->dev, "error requesting GPIO for WP %d\n", - ret); - return ret; - } - - return 0; -} - -static int s3cmci_probe_dt(struct s3cmci_host *host) -{ - struct platform_device *pdev = host->pdev; - struct s3c24xx_mci_pdata *pdata; - struct mmc_host *mmc = host->mmc; - int ret; - - host->is2440 = (long) of_device_get_match_data(&pdev->dev); - - ret = mmc_of_parse(mmc); - if (ret) - return ret; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdev->dev.platform_data = pdata; - - return 0; -} - -static int s3cmci_probe(struct platform_device *pdev) -{ - struct s3cmci_host *host; - struct mmc_host *mmc; - int ret; - - mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto probe_out; - } - - host = mmc_priv(mmc); - host->mmc = mmc; - host->pdev = pdev; - - if (pdev->dev.of_node) - ret = s3cmci_probe_dt(host); - else - ret = s3cmci_probe_pdata(host); - - if (ret) - goto probe_free_host; - - host->pdata = pdev->dev.platform_data; - - spin_lock_init(&host->complete_lock); - tasklet_setup(&host->pio_tasklet, pio_tasklet); - - if (host->is2440) { - host->sdiimsk = S3C2440_SDIIMSK; - host->sdidata = S3C2440_SDIDATA; - host->clk_div = 1; - } else { - host->sdiimsk = S3C2410_SDIIMSK; - host->sdidata = S3C2410_SDIDATA; - host->clk_div = 2; - } - - host->complete_what = COMPLETION_NONE; - host->pio_active = XFER_NONE; - - host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!host->mem) { - dev_err(&pdev->dev, - "failed to get io memory region resource.\n"); - - ret = -ENOENT; - goto probe_free_host; - } - - host->mem = request_mem_region(host->mem->start, - resource_size(host->mem), pdev->name); - - if (!host->mem) { - dev_err(&pdev->dev, "failed to request io memory region.\n"); - ret = -ENOENT; - goto probe_free_host; - } - - host->base = ioremap(host->mem->start, resource_size(host->mem)); - if (!host->base) { - dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); - ret = -EINVAL; - goto probe_free_mem_region; - } - - host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; - goto probe_iounmap; - } - - if (request_irq(host->irq, s3cmci_irq, IRQF_NO_AUTOEN, DRIVER_NAME, host)) { - dev_err(&pdev->dev, "failed to request mci interrupt.\n"); - ret = -ENOENT; - goto probe_iounmap; - } - - host->irq_state = false; - - /* Depending on the dma state, get a DMA channel to use. */ - - if (s3cmci_host_usedma(host)) { - host->dma = dma_request_chan(&pdev->dev, "rx-tx"); - ret = PTR_ERR_OR_ZERO(host->dma); - if (ret) { - dev_err(&pdev->dev, "cannot get DMA channel.\n"); - goto probe_free_irq; - } - } - - host->clk = clk_get(&pdev->dev, "sdi"); - if (IS_ERR(host->clk)) { - dev_err(&pdev->dev, "failed to find clock source.\n"); - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto probe_free_dma; - } - - ret = clk_prepare_enable(host->clk); - if (ret) { - dev_err(&pdev->dev, "failed to enable clock source.\n"); - goto clk_free; - } - - host->clk_rate = clk_get_rate(host->clk); - - mmc->ops = &s3cmci_ops; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; -#else - mmc->caps = MMC_CAP_4_BIT_DATA; -#endif - mmc->f_min = host->clk_rate / (host->clk_div * 256); - mmc->f_max = host->clk_rate / host->clk_div; - - if (host->pdata->ocr_avail) - mmc->ocr_avail = host->pdata->ocr_avail; - - mmc->max_blk_count = 4095; - mmc->max_blk_size = 4095; - mmc->max_req_size = 4095 * 512; - mmc->max_seg_size = mmc->max_req_size; - - mmc->max_segs = 128; - - dbg(host, dbg_debug, - "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%p.\n", - (host->is2440?"2440":""), - host->base, host->irq, host->irq_cd, host->dma); - - ret = s3cmci_cpufreq_register(host); - if (ret) { - dev_err(&pdev->dev, "failed to register cpufreq\n"); - goto free_dmabuf; - } - - ret = mmc_add_host(mmc); - if (ret) { - dev_err(&pdev->dev, "failed to add mmc host.\n"); - goto free_cpufreq; - } - - s3cmci_debugfs_attach(host); - - platform_set_drvdata(pdev, mmc); - dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc), - s3cmci_host_usedma(host) ? "dma" : "pio", - mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw"); - - return 0; - - free_cpufreq: - s3cmci_cpufreq_deregister(host); - - free_dmabuf: - clk_disable_unprepare(host->clk); - - clk_free: - clk_put(host->clk); - - probe_free_dma: - if (s3cmci_host_usedma(host)) - dma_release_channel(host->dma); - - probe_free_irq: - free_irq(host->irq, host); - - probe_iounmap: - iounmap(host->base); - - probe_free_mem_region: - release_mem_region(host->mem->start, resource_size(host->mem)); - - probe_free_host: - mmc_free_host(mmc); - - probe_out: - return ret; -} - -static void s3cmci_shutdown(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - struct s3cmci_host *host = mmc_priv(mmc); - - if (host->irq_cd >= 0) - free_irq(host->irq_cd, host); - - s3cmci_debugfs_remove(host); - s3cmci_cpufreq_deregister(host); - mmc_remove_host(mmc); - clk_disable_unprepare(host->clk); -} - -static int s3cmci_remove(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - struct s3cmci_host *host = mmc_priv(mmc); - - s3cmci_shutdown(pdev); - - clk_put(host->clk); - - tasklet_disable(&host->pio_tasklet); - - if (s3cmci_host_usedma(host)) - dma_release_channel(host->dma); - - free_irq(host->irq, host); - - iounmap(host->base); - release_mem_region(host->mem->start, resource_size(host->mem)); - - mmc_free_host(mmc); - return 0; -} - -static const struct of_device_id s3cmci_dt_match[] = { - { - .compatible = "samsung,s3c2410-sdi", - .data = (void *)0, - }, - { - .compatible = "samsung,s3c2412-sdi", - .data = (void *)1, - }, - { - .compatible = "samsung,s3c2440-sdi", - .data = (void *)1, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, s3cmci_dt_match); - -static const struct platform_device_id s3cmci_driver_ids[] = { - { - .name = "s3c2410-sdi", - .driver_data = 0, - }, { - .name = "s3c2412-sdi", - .driver_data = 1, - }, { - .name = "s3c2440-sdi", - .driver_data = 1, - }, - { } -}; - -MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); - -static struct platform_driver s3cmci_driver = { - .driver = { - .name = "s3c-sdi", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = s3cmci_dt_match, - }, - .id_table = s3cmci_driver_ids, - .probe = s3cmci_probe, - .remove = s3cmci_remove, - .shutdown = s3cmci_shutdown, -}; - -module_platform_driver(s3cmci_driver); - -MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>"); diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h deleted file mode 100644 index 8b65d7ad9f97..000000000000 --- a/drivers/mmc/host/s3cmci.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver - * - * Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved. - */ - -enum s3cmci_waitfor { - COMPLETION_NONE, - COMPLETION_FINALIZE, - COMPLETION_CMDSENT, - COMPLETION_RSPFIN, - COMPLETION_XFERFINISH, - COMPLETION_XFERFINISH_RSPFIN, -}; - -struct s3cmci_host { - struct platform_device *pdev; - struct s3c24xx_mci_pdata *pdata; - struct mmc_host *mmc; - struct resource *mem; - struct clk *clk; - void __iomem *base; - int irq; - int irq_cd; - struct dma_chan *dma; - - unsigned long clk_rate; - unsigned long clk_div; - unsigned long real_rate; - u8 prescaler; - - int is2440; - unsigned sdiimsk; - unsigned sdidata; - - bool irq_disabled; - bool irq_enabled; - bool irq_state; - int sdio_irqen; - - struct mmc_request *mrq; - int cmd_is_stop; - - spinlock_t complete_lock; - enum s3cmci_waitfor complete_what; - - int dma_complete; - - u32 pio_sgptr; - u32 pio_bytes; - u32 pio_count; - u32 *pio_ptr; -#define XFER_NONE 0 -#define XFER_READ 1 -#define XFER_WRITE 2 - u32 pio_active; - - int bus_width; - - char dbgmsg_cmd[301]; - char dbgmsg_dat[301]; - char *status; - - unsigned int ccnt, dcnt; - struct tasklet_struct pio_tasklet; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debug_root; -#endif - -#ifdef CONFIG_ARM_S3C24XX_CPUFREQ - struct notifier_block freq_transition; -#endif -}; diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c deleted file mode 100644 index 2a29c7a4f308..000000000000 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * SDHCI support for CNS3xxx SoC - * - * Copyright 2008 Cavium Networks - * Copyright 2010 MontaVista Software, LLC. - * - * Authors: Scott Shu - * Anton Vorontsov <avorontsov@mvista.com> - */ - -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/mmc/host.h> -#include <linux/module.h> -#include "sdhci-pltfm.h" - -static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) -{ - return 150000000; -} - -static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) -{ - struct device *dev = mmc_dev(host->mmc); - int div = 1; - u16 clk; - unsigned long timeout; - - host->mmc->actual_clock = 0; - - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); - - if (clock == 0) - return; - - while (host->max_clk / div > clock) { - /* - * On CNS3xxx divider grows linearly up to 4, and then - * exponentially up to 256. - */ - if (div < 4) - div += 1; - else if (div < 256) - div *= 2; - else - break; - } - - dev_dbg(dev, "desired SD clock: %d, actual: %d\n", - clock, host->max_clk / div); - - /* Divide by 3 is special. */ - if (div != 3) - div >>= 1; - - clk = div << SDHCI_DIVIDER_SHIFT; - clk |= SDHCI_CLOCK_INT_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - timeout = 20; - while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) - & SDHCI_CLOCK_INT_STABLE)) { - if (timeout == 0) { - dev_warn(dev, "clock is unstable"); - break; - } - timeout--; - mdelay(1); - } - - clk |= SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); -} - -static const struct sdhci_ops sdhci_cns3xxx_ops = { - .get_max_clock = sdhci_cns3xxx_get_max_clk, - .set_clock = sdhci_cns3xxx_set_clock, - .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, -}; - -static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { - .ops = &sdhci_cns3xxx_ops, - .quirks = SDHCI_QUIRK_BROKEN_DMA | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT | - SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, -}; - -static int sdhci_cns3xxx_probe(struct platform_device *pdev) -{ - return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0); -} - -static struct platform_driver sdhci_cns3xxx_driver = { - .driver = { - .name = "sdhci-cns3xxx", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_pltfm_pmops, - }, - .probe = sdhci_cns3xxx_probe, - .remove = sdhci_pltfm_unregister, -}; - -module_platform_driver(sdhci_cns3xxx_driver); - -MODULE_DESCRIPTION("SDHCI driver for CNS3xxx"); -MODULE_AUTHOR("Scott Shu, " - "Anton Vorontsov <avorontsov@mvista.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c deleted file mode 100644 index 53a2ad9a24b8..000000000000 --- a/drivers/mmc/host/tmio_mmc.c +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for the MMC / SD / SDIO cell found in: - * - * TC6393XB TC6391XB TC6387XB T7L66XB ASIC3 - * - * Copyright (C) 2017 Renesas Electronics Corporation - * Copyright (C) 2017 Horms Solutions, Simon Horman - * Copyright (C) 2007 Ian Molton - * Copyright (C) 2004 Ian Molton - */ - -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tmio.h> -#include <linux/mmc/host.h> -#include <linux/module.h> -#include <linux/pagemap.h> -#include <linux/scatterlist.h> - -#include "tmio_mmc.h" - -/* Registers specific to this variant */ -#define CTL_SDIO_REGS 0x100 -#define CTL_CLK_AND_WAIT_CTL 0x138 -#define CTL_RESET_SDIO 0x1e0 - -static void tmio_mmc_clk_start(struct tmio_mmc_host *host) -{ - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - - usleep_range(10000, 11000); - sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100); - usleep_range(10000, 11000); -} - -static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) -{ - sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000); - usleep_range(10000, 11000); - - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - - usleep_range(10000, 11000); -} - -static void tmio_mmc_set_clock(struct tmio_mmc_host *host, - unsigned int new_clock) -{ - unsigned int divisor; - u32 clk = 0; - int clk_sel; - - if (new_clock == 0) { - tmio_mmc_clk_stop(host); - return; - } - - divisor = host->pdata->hclk / new_clock; - - /* bit7 set: 1/512, ... bit0 set: 1/4, all bits clear: 1/2 */ - clk_sel = (divisor <= 1); - clk = clk_sel ? 0 : (roundup_pow_of_two(divisor) >> 2); - - host->pdata->set_clk_div(host->pdev, clk_sel); - - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK); - usleep_range(10000, 11000); - - tmio_mmc_clk_start(host); -} - -static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve) -{ - sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000); - usleep_range(10000, 11000); - sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001); - usleep_range(10000, 11000); -} - -#ifdef CONFIG_PM_SLEEP -static int tmio_mmc_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - const struct mfd_cell *cell = mfd_get_cell(pdev); - int ret; - - ret = pm_runtime_force_suspend(dev); - - /* Tell MFD core it can disable us now.*/ - if (!ret && cell->disable) - cell->disable(pdev); - - return ret; -} - -static int tmio_mmc_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - const struct mfd_cell *cell = mfd_get_cell(pdev); - int ret = 0; - - /* Tell the MFD core we are ready to be enabled */ - if (cell->resume) - ret = cell->resume(pdev); - - if (!ret) - ret = pm_runtime_force_resume(dev); - - return ret; -} -#endif - -static int tmio_mmc_probe(struct platform_device *pdev) -{ - const struct mfd_cell *cell = mfd_get_cell(pdev); - struct tmio_mmc_data *pdata; - struct tmio_mmc_host *host; - struct resource *res; - int ret = -EINVAL, irq; - - if (pdev->num_resources != 2) - goto out; - - pdata = pdev->dev.platform_data; - if (!pdata || !pdata->hclk) - goto out; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto out; - } - - /* Tell the MFD core we are ready to be enabled */ - if (cell->enable) { - ret = cell->enable(pdev); - if (ret) - goto out; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -EINVAL; - goto cell_disable; - } - - host = tmio_mmc_host_alloc(pdev, pdata); - if (IS_ERR(host)) { - ret = PTR_ERR(host); - goto cell_disable; - } - - /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ - host->bus_shift = resource_size(res) >> 10; - host->set_clock = tmio_mmc_set_clock; - host->reset = tmio_mmc_reset; - - host->mmc->f_max = pdata->hclk; - host->mmc->f_min = pdata->hclk / 512; - - ret = tmio_mmc_host_probe(host); - if (ret) - goto host_free; - - ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, - IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), host); - if (ret) - goto host_remove; - - pr_info("%s at 0x%p irq %d\n", mmc_hostname(host->mmc), host->ctl, irq); - - return 0; - -host_remove: - tmio_mmc_host_remove(host); -host_free: - tmio_mmc_host_free(host); -cell_disable: - if (cell->disable) - cell->disable(pdev); -out: - return ret; -} - -static int tmio_mmc_remove(struct platform_device *pdev) -{ - const struct mfd_cell *cell = mfd_get_cell(pdev); - struct tmio_mmc_host *host = platform_get_drvdata(pdev); - - tmio_mmc_host_remove(host); - if (cell->disable) - cell->disable(pdev); - - return 0; -} - -/* ------------------- device registration ----------------------- */ - -static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume) - SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, - tmio_mmc_host_runtime_resume, NULL) -}; - -static struct platform_driver tmio_mmc_driver = { - .driver = { - .name = "tmio-mmc", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &tmio_mmc_dev_pm_ops, - }, - .probe = tmio_mmc_probe, - .remove = tmio_mmc_remove, -}; - -module_platform_driver(tmio_mmc_driver); - -MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver"); -MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tmio-mmc"); |