From 4e4141a526dd7f5ac3ce1458ae79ea6e5a515b06 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:13:46 +0300 Subject: sdhci: Add support for bus-specific IO memory accessors Currently the SDHCI driver works with PCI accessors (write{l,b,w} and read{l,b,w}). With this patch drivers may change memory accessors, so that we can support hosts with "weird" IO memory access requirments. For example, in "FSL eSDHC" SDHCI hardware all registers are 32 bit width, with big-endian addressing. That is, readb(0x2f) should turn into readb(0x2c), and readw(0x2c) should be translated to le16_to_cpu(readw(0x2e)). Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 43c37c68d07..d9733f84106 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -10,6 +10,9 @@ */ #include +#include +#include +#include /* * Controller registers @@ -267,9 +270,101 @@ struct sdhci_host { struct sdhci_ops { +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + u32 (*readl)(struct sdhci_host *host, int reg); + u16 (*readw)(struct sdhci_host *host, int reg); + u8 (*readb)(struct sdhci_host *host, int reg); + void (*writel)(struct sdhci_host *host, u32 val, int reg); + void (*writew)(struct sdhci_host *host, u16 val, int reg); + void (*writeb)(struct sdhci_host *host, u8 val, int reg); +#endif + int (*enable_dma)(struct sdhci_host *host); }; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + +static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + if (unlikely(host->ops->writel)) + host->ops->writel(host, val, reg); + else + writel(val, host->ioaddr + reg); +} + +static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + if (unlikely(host->ops->writew)) + host->ops->writew(host, val, reg); + else + writew(val, host->ioaddr + reg); +} + +static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + if (unlikely(host->ops->writeb)) + host->ops->writeb(host, val, reg); + else + writeb(val, host->ioaddr + reg); +} + +static inline u32 sdhci_readl(struct sdhci_host *host, int reg) +{ + if (unlikely(host->ops->readl)) + return host->ops->readl(host, reg); + else + return readl(host->ioaddr + reg); +} + +static inline u16 sdhci_readw(struct sdhci_host *host, int reg) +{ + if (unlikely(host->ops->readw)) + return host->ops->readw(host, reg); + else + return readw(host->ioaddr + reg); +} + +static inline u8 sdhci_readb(struct sdhci_host *host, int reg) +{ + if (unlikely(host->ops->readb)) + return host->ops->readb(host, reg); + else + return readb(host->ioaddr + reg); +} + +#else + +static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + writel(val, host->ioaddr + reg); +} + +static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + writew(val, host->ioaddr + reg); +} + +static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + writeb(val, host->ioaddr + reg); +} + +static inline u32 sdhci_readl(struct sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static inline u16 sdhci_readw(struct sdhci_host *host, int reg) +{ + return readw(host->ioaddr + reg); +} + +static inline u8 sdhci_readb(struct sdhci_host *host, int reg) +{ + return readb(host->ioaddr + reg); +} + +#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ extern struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size); -- cgit v1.2.3 From 7260cf5e12393536ce61d184c3fc750fb2ba635a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:13:48 +0300 Subject: sdhci: Split card-detection IRQs management from sdhci_init() Card detection interrupts should be handled separately as they should not be enabled before mmc_add_host() returns and should be disabled before calling mmc_remove_host(). The same is for suspend and resume routines. sdhci_init() no longer enables card-detection irqs. Instead, two new functions implemented: sdhci_enable_card_detection() and sdhci_disable_card_detection(). New sdhci_reinit() call implemented to behave the same way as the old sdhci_init(). Also, this patch implements and uses few new helpers to manage IRQs in a more conveinient way, that is: - sdhci_clear_set_irqs() - sdhci_unmask_irqs() - sdhci_mask_irqs() - SDHCI_INT_ALL_MASK constant sdhci_enable_sdio_irq() converted to these new helpers, plus the helpers will be used by the subsequent patches. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d9733f84106..a9e25c6c0c0 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -126,6 +126,7 @@ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ SDHCI_INT_DATA_END_BIT) +#define SDHCI_INT_ALL_MASK ((unsigned int)-1) #define SDHCI_ACMD12_ERR 0x3C -- cgit v1.2.3 From 68d1fb7e229c6f95be4fbbe3eb46b24e41184924 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:13:52 +0300 Subject: sdhci: Add support for card-detection polling This patch adds SDHCI_QUIRK_BROKEN_CARD_DETECTION quirk. When specified, sdhci driver will set MMC_CAP_NEEDS_POLL MMC host capability, and won't enable card insert/remove interrupts. This is needed for hosts with unreliable card detection, such as FSL eSDHC. The original eSDHC driver was tring to "debounce" card-detection IRQs by reading present state and disabling particular interrupts. But with this debouncing scheme I noticed that sometimes we miss card insertion/removal events. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index a9e25c6c0c0..968d713950f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -214,6 +214,8 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) /* Controller does not provide transfer-complete interrupt when not busy */ #define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) +/* Controller has unreliable card detection */ +#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ -- cgit v1.2.3 From c5075a1089e808d8f471ce21b02810cc98ab2692 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:13:54 +0300 Subject: sdhci: Add support for hosts reporting inverted write-protect state This patch adds SDHCI_QUIRK_INVERTED_WRITE_PROTECT quirk. When specified, the sdhci driver will invert WP state. p.s. Actually, the quirk is more board-specific than controller-specific. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 968d713950f..6980f2725b8 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -216,6 +216,8 @@ struct sdhci_host { #define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) /* Controller has unreliable card detection */ #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) +/* Controller reports inverted write-protect state */ +#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ -- cgit v1.2.3 From 4240ff0a02cb52f7d10dc1df6d82ba9c27dba07b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 17 Mar 2009 00:13:57 +0300 Subject: sdhci: Add get_{max,timeout}_clock callbacks Some controllers do not provide clock information in their capabilities (in the Samsung case, it is because there are multiple clock sources available to the controller). Add hooks to allow the system to supply clock information. p.s. In the original Ben's patch there was a bug that makes sdhci_add_host() return -ENODEV even if callbacks were specified. This is fixed now. Signed-off-by: Ben Dooks Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 6980f2725b8..aab0652a458 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -285,6 +285,8 @@ struct sdhci_ops { #endif int (*enable_dma)(struct sdhci_host *host); + unsigned int (*get_max_clock)(struct sdhci_host *host); + unsigned int (*get_timeout_clock)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 8114634ccb54d67a8c01e5825d95bff4e7f7b357 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:13:59 +0300 Subject: sdhci: Add set_clock callback and a quirk for nonstandard clocks FSL eSDHC hosts have incompatible register map to manage the SDCLK. This patch adds set_clock callback so that drivers could overwrite set_clock behaviour. Similar patch[1] was posted by Ben Dooks, though in Ben's version the callback is named change_clock, plus the patch has some unrelated bits that makes the patch difficult to reuse. [1] http://lkml.org/lkml/2008/12/2/160 Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index aab0652a458..b9bc622735b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -218,6 +218,8 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) /* Controller reports inverted write-protect state */ #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) +/* Controller has nonstandard clock management */ +#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ @@ -284,6 +286,8 @@ struct sdhci_ops { void (*writeb)(struct sdhci_host *host, u8 val, int reg); #endif + void (*set_clock)(struct sdhci_host *host, unsigned int clock); + int (*enable_dma)(struct sdhci_host *host); unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); -- cgit v1.2.3 From 3e3bf20756aeee57a40fd37b923263c9a51b8c68 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:14:00 +0300 Subject: sdhci: Add quirk for controllers that need small delays for PIO Small udelay is needed to make eSDHC work in PIO mode. Without the delay reading causes endless interrupt storm, and writing corrupts data. The first guess would be that we must wait for some bit in some register, but I didn't find any reliable bits that change before and after the delay. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b9bc622735b..c5ce9ee1a1b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -220,6 +220,8 @@ struct sdhci_host { #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) /* Controller has nonstandard clock management */ #define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) +/* Controller does not like fast PIO transfers */ +#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ -- cgit v1.2.3 From 063a9dbbce5559770b7e2e2f51bd29adf3ab9b1e Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:14:02 +0300 Subject: sdhci: Add quirk for controllers that need IRQ re-init after reset FSL eSDHC controllers losing signal/interrupt enable states after reset, so we should re-enable them. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c5ce9ee1a1b..2962102b695 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -222,6 +222,8 @@ struct sdhci_host { #define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) +/* Controller losing signal/interrupt enable states after reset */ +#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ -- cgit v1.2.3 From 0633f654241483edc8a235ab87264ff6bbcd08d5 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 17 Mar 2009 00:14:03 +0300 Subject: sdhci: Add quirk for forcing maximum block size to 2048 bytes FSL eSDHC controllers can support maximum block size up to 4096 bytes, the MBL (Maximum Block Length) field in the capabilities register extended by one bit, and is set to 0x3. But the SDHCI core doesn't support blocks of 4096 bytes, and thus forces blksz to the lowest value -- 512 bytes. With this patch we can pin up the blksz to the maximum supported block size, i.e. 2048 bytes. Signed-off-by: Anton Vorontsov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host/sdhci.h') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 2962102b695..f20a834f430 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -224,6 +224,8 @@ struct sdhci_host { #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) /* Controller losing signal/interrupt enable states after reset */ #define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) +/* Controller has to be forced to use block size of 2048 bytes */ +#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ -- cgit v1.2.3