Add a platform driver for ThunderX ARM SOCs. Signed-off-by: Jan Glauber <jglauber@xxxxxxxxxx> Signed-off-by: David Daney <david.daney@xxxxxxxxxx> Signed-off-by: Steven J. Hill <steven.hill@xxxxxxxxxx> --- drivers/mmc/host/Kconfig | 10 ++ drivers/mmc/host/Makefile | 2 + drivers/mmc/host/cavium-mmc.h | 38 ++++++ drivers/mmc/host/cavium-pci-thunderx.c | 203 +++++++++++++++++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 drivers/mmc/host/cavium-pci-thunderx.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 68cc811..3983dee 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -632,6 +632,16 @@ config MMC_CAVIUM_OCTEON If unsure, say N. +config MMC_CAVIUM_THUNDERX + tristate "Cavium ThunderX SD/MMC Card Interface support" + depends on PCI && 64BIT && (ARM64 || COMPILE_TEST) + select GPIO_THUNDERX + help + This selects Cavium ThunderX SD/MMC Card Interface. + If you have an Cavium ARM64 board with a Multimedia Card slot + or builtin eMMC chip say Y or M here. If built as a module + the module will be called thunderx_mmc.ko. + config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" depends on HAS_DMA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index c7f0ccf..0068610 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -44,6 +44,8 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o octeon-mmc-objs := cavium-mmc.o cavium-pltfm-octeon.o obj-$(CONFIG_MMC_CAVIUM_OCTEON) += octeon-mmc.o +thunderx-mmc-objs := cavium-mmc.o cavium-pci-thunderx.o +obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o diff --git a/drivers/mmc/host/cavium-mmc.h b/drivers/mmc/host/cavium-mmc.h index 5f41be9..64a837c 100644 --- a/drivers/mmc/host/cavium-mmc.h +++ b/drivers/mmc/host/cavium-mmc.h @@ -11,11 +11,14 @@ #include <linux/io.h> #include <linux/mmc/host.h> #include <linux/of.h> +#include <linux/pci.h> #include <linux/scatterlist.h> #include <linux/semaphore.h> #define CAVIUM_MAX_MMC 4 +#if IS_ENABLED(CONFIG_MMC_CAVIUM_OCTEON) + #define MIO_EMM_DMA_CFG 0x00 #define MIO_EMM_DMA_ADR 0x08 @@ -35,6 +38,36 @@ #define MIO_EMM_BUF_IDX 0xe0 #define MIO_EMM_BUF_DAT 0xe8 +#elif CONFIG_MMC_CAVIUM_THUNDERX + +#define MIO_EMM_DMA_CFG 0x180 +#define MIO_EMM_DMA_ADR 0x188 +#define MIO_EMM_DMA_INT 0x190 +#define MIO_EMM_DMA_INT_W1S 0x198 +#define MIO_EMM_DMA_INT_ENA_W1S 0x1a0 +#define MIO_EMM_DMA_INT_ENA_W1C 0x1a8 + +#define MIO_EMM_CFG 0x2000 +#define MIO_EMM_SWITCH 0x2048 +#define MIO_EMM_DMA 0x2050 +#define MIO_EMM_CMD 0x2058 +#define MIO_EMM_RSP_STS 0x2060 +#define MIO_EMM_RSP_LO 0x2068 +#define MIO_EMM_RSP_HI 0x2070 +#define MIO_EMM_INT 0x2078 +#define MIO_EMM_INT_EN 0x2080 +#define MIO_EMM_WDOG 0x2088 +#define MIO_EMM_SAMPLE 0x2090 +#define MIO_EMM_STS_MASK 0x2098 +#define MIO_EMM_RCA 0x20a0 +#define MIO_EMM_BUF_IDX 0x20e0 +#define MIO_EMM_BUF_DAT 0x20e8 + +#define MIO_EMM_INT_EN_SET 0x20b0 +#define MIO_EMM_INT_EN_CLR 0x20b8 + +#endif + struct cvm_mmc_host { struct device *dev; void __iomem *base; @@ -66,6 +99,11 @@ struct cvm_mmc_host { void (*dmar_fixup)(struct cvm_mmc_host *, struct mmc_command *, struct mmc_data *, u64); void (*dmar_fixup_done)(struct cvm_mmc_host *); + +#if IS_ENABLED(CONFIG_MMC_CAVIUM_THUNDERX) + struct msix_entry *mmc_msix; + unsigned int msix_count; +#endif }; struct cvm_mmc_slot { diff --git a/drivers/mmc/host/cavium-pci-thunderx.c b/drivers/mmc/host/cavium-pci-thunderx.c new file mode 100644 index 0000000..5052c4e --- /dev/null +++ b/drivers/mmc/host/cavium-pci-thunderx.c @@ -0,0 +1,203 @@ +/* + * Driver for MMC and SSD cards for Cavium ThunderX SOCs. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2016 Cavium Inc. + */ +#include <linux/dma-mapping.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include "cavium-mmc.h" + +struct platform_device *slot_pdev[2]; + +static void thunder_mmc_acquire_bus(struct cvm_mmc_host *host) +{ + down(&host->mmc_serializer); +} + +static void thunder_mmc_release_bus(struct cvm_mmc_host *host) +{ + up(&host->mmc_serializer); +} + +static void thunder_mmc_int_enable(struct cvm_mmc_host *host, u64 val) +{ + writeq(val, host->base + MIO_EMM_INT); + writeq(val, host->base + MIO_EMM_INT_EN_SET); +} + +static int thunder_mmc_register_interrupts(struct cvm_mmc_host *host, + struct pci_dev *pdev) +{ + int ret, i; + + host->msix_count = pci_msix_vec_count(pdev); + host->mmc_msix = devm_kzalloc(&pdev->dev, + (sizeof(struct msix_entry)) * host->msix_count, GFP_KERNEL); + if (!host->mmc_msix) + return -ENOMEM; + + for (i = 0; i < host->msix_count; i++) + host->mmc_msix[i].entry = i; + + ret = pci_enable_msix(pdev, host->mmc_msix, host->msix_count); + if (ret) + return ret; + + /* register interrupts */ + for (i = 0; i < host->msix_count; i++) { + ret = devm_request_irq(&pdev->dev, host->mmc_msix[i].vector, + cvm_mmc_interrupt, + 0, KBUILD_MODNAME, host); + if (ret) + return ret; + } + return 0; +} + +static int thunder_mmc_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *child_node; + struct cvm_mmc_host *host; + int ret, i = 0; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + pci_set_drvdata(pdev, host); + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) + return ret; + + host->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!host->base) + return -EINVAL; + + /* On ThunderX these are identical */ + host->dma_base = host->base; + + host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + ret = clk_prepare_enable(host->clk); + if (ret) + return ret; + host->sys_freq = clk_get_rate(host->clk); + + spin_lock_init(&host->irq_handler_lock); + sema_init(&host->mmc_serializer, 1); + + host->dev = dev; + host->acquire_bus = thunder_mmc_acquire_bus; + host->release_bus = thunder_mmc_release_bus; + host->int_enable = thunder_mmc_int_enable; + + host->big_dma_addr = true; + host->need_irq_handler_lock = true; + host->last_slot = -1; + + ret = dma_set_mask(dev, DMA_BIT_MASK(48)); + if (ret) + goto error; + + /* + * Clear out any pending interrupts that may be left over from + * bootloader. Writing 1 to the bits clears them. + */ + writeq(127, host->base + MIO_EMM_INT_EN); + writeq(3, host->base + MIO_EMM_DMA_INT_ENA_W1C); + + ret = thunder_mmc_register_interrupts(host, pdev); + if (ret) + goto error; + + for_each_child_of_node(node, child_node) { + /* + * TODO: mmc_of_parse and devm* require one device per slot. + * Create a dummy device per slot and set the node pointer to + * the slot. The easiest way to get this is using + * of_platform_device_create. + */ + if (!slot_pdev[i]) + slot_pdev[i] = of_platform_device_create(child_node, NULL, + &pdev->dev); + if (!slot_pdev[i]) + continue; + ret = cvm_mmc_slot_probe(&slot_pdev[i]->dev, host); + if (ret) + goto error; + i++; + } + dev_info(dev, "probed\n"); + return 0; + +error: + clk_disable_unprepare(host->clk); + return ret; +} + +static void thunder_mmc_remove(struct pci_dev *pdev) +{ + struct cvm_mmc_host *host = pci_get_drvdata(pdev); + union mio_emm_dma_cfg dma_cfg; + int i; + + for (i = 0; i < CAVIUM_MAX_MMC; i++) + if (host->slot[i]) { + cvm_mmc_slot_remove(host->slot[i]); + platform_device_del(slot_pdev[i]); + } + + dma_cfg.val = readq(host->dma_base + MIO_EMM_DMA_CFG); + dma_cfg.s.en = 0; + writeq(dma_cfg.val, host->dma_base + MIO_EMM_DMA_CFG); + + clk_disable_unprepare(host->clk); +} + +static const struct pci_device_id thunder_mmc_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa010) }, + { 0, } /* end of table */ +}; + +static struct pci_driver thunder_mmc_driver = { + .name = KBUILD_MODNAME, + .id_table = thunder_mmc_id_table, + .probe = thunder_mmc_probe, + .remove = thunder_mmc_remove, +}; + +static int __init thunder_mmc_init_module(void) +{ + return pci_register_driver(&thunder_mmc_driver); +} + +static void __exit thunder_mmc_exit_module(void) +{ + pci_unregister_driver(&thunder_mmc_driver); +} + +module_init(thunder_mmc_init_module); +module_exit(thunder_mmc_exit_module); + +MODULE_AUTHOR("Cavium Inc."); +MODULE_DESCRIPTION("Cavium ThunderX eMMC Driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table); -- 2.9.0.rc0.21.g7777322 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html