1166bac38SJan Glauber /* 2166bac38SJan Glauber * Driver for MMC and SSD cards for Cavium ThunderX SOCs. 3166bac38SJan Glauber * 4166bac38SJan Glauber * This file is subject to the terms and conditions of the GNU General Public 5166bac38SJan Glauber * License. See the file "COPYING" in the main directory of this archive 6166bac38SJan Glauber * for more details. 7166bac38SJan Glauber * 8166bac38SJan Glauber * Copyright (C) 2016 Cavium Inc. 9166bac38SJan Glauber */ 10b917c6d1SJan Glauber #include <linux/device.h> 11166bac38SJan Glauber #include <linux/dma-mapping.h> 12166bac38SJan Glauber #include <linux/interrupt.h> 13166bac38SJan Glauber #include <linux/mmc/mmc.h> 14166bac38SJan Glauber #include <linux/module.h> 15166bac38SJan Glauber #include <linux/of.h> 16166bac38SJan Glauber #include <linux/of_platform.h> 17166bac38SJan Glauber #include <linux/pci.h> 18166bac38SJan Glauber #include "cavium.h" 19166bac38SJan Glauber 20166bac38SJan Glauber static void thunder_mmc_acquire_bus(struct cvm_mmc_host *host) 21166bac38SJan Glauber { 22166bac38SJan Glauber down(&host->mmc_serializer); 23166bac38SJan Glauber } 24166bac38SJan Glauber 25166bac38SJan Glauber static void thunder_mmc_release_bus(struct cvm_mmc_host *host) 26166bac38SJan Glauber { 27166bac38SJan Glauber up(&host->mmc_serializer); 28166bac38SJan Glauber } 29166bac38SJan Glauber 30166bac38SJan Glauber static void thunder_mmc_int_enable(struct cvm_mmc_host *host, u64 val) 31166bac38SJan Glauber { 32166bac38SJan Glauber writeq(val, host->base + MIO_EMM_INT(host)); 33166bac38SJan Glauber writeq(val, host->base + MIO_EMM_INT_EN_SET(host)); 34166bac38SJan Glauber } 35166bac38SJan Glauber 36166bac38SJan Glauber static int thunder_mmc_register_interrupts(struct cvm_mmc_host *host, 37166bac38SJan Glauber struct pci_dev *pdev) 38166bac38SJan Glauber { 39166bac38SJan Glauber int nvec, ret, i; 40166bac38SJan Glauber 41166bac38SJan Glauber nvec = pci_alloc_irq_vectors(pdev, 1, 9, PCI_IRQ_MSIX); 42166bac38SJan Glauber if (nvec < 0) 43166bac38SJan Glauber return nvec; 44166bac38SJan Glauber 45166bac38SJan Glauber /* register interrupts */ 46166bac38SJan Glauber for (i = 0; i < nvec; i++) { 47166bac38SJan Glauber ret = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, i), 48166bac38SJan Glauber cvm_mmc_interrupt, 49166bac38SJan Glauber 0, cvm_mmc_irq_names[i], host); 50166bac38SJan Glauber if (ret) 51166bac38SJan Glauber return ret; 52166bac38SJan Glauber } 53166bac38SJan Glauber return 0; 54166bac38SJan Glauber } 55166bac38SJan Glauber 56166bac38SJan Glauber static int thunder_mmc_probe(struct pci_dev *pdev, 57166bac38SJan Glauber const struct pci_device_id *id) 58166bac38SJan Glauber { 59166bac38SJan Glauber struct device_node *node = pdev->dev.of_node; 60166bac38SJan Glauber struct device *dev = &pdev->dev; 61166bac38SJan Glauber struct device_node *child_node; 62166bac38SJan Glauber struct cvm_mmc_host *host; 63166bac38SJan Glauber int ret, i = 0; 64166bac38SJan Glauber 65166bac38SJan Glauber host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); 66166bac38SJan Glauber if (!host) 67166bac38SJan Glauber return -ENOMEM; 68166bac38SJan Glauber 69166bac38SJan Glauber pci_set_drvdata(pdev, host); 70166bac38SJan Glauber ret = pcim_enable_device(pdev); 71166bac38SJan Glauber if (ret) 72166bac38SJan Glauber return ret; 73166bac38SJan Glauber 74166bac38SJan Glauber ret = pci_request_regions(pdev, KBUILD_MODNAME); 75166bac38SJan Glauber if (ret) 76166bac38SJan Glauber return ret; 77166bac38SJan Glauber 78166bac38SJan Glauber host->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); 79166bac38SJan Glauber if (!host->base) 80166bac38SJan Glauber return -EINVAL; 81166bac38SJan Glauber 82166bac38SJan Glauber /* On ThunderX these are identical */ 83166bac38SJan Glauber host->dma_base = host->base; 84166bac38SJan Glauber 85166bac38SJan Glauber host->reg_off = 0x2000; 86cd76e5c5SJan Glauber host->reg_off_dma = 0x160; 87166bac38SJan Glauber 88166bac38SJan Glauber host->clk = devm_clk_get(dev, NULL); 89166bac38SJan Glauber if (IS_ERR(host->clk)) 90166bac38SJan Glauber return PTR_ERR(host->clk); 91166bac38SJan Glauber 92166bac38SJan Glauber ret = clk_prepare_enable(host->clk); 93166bac38SJan Glauber if (ret) 94166bac38SJan Glauber return ret; 95166bac38SJan Glauber host->sys_freq = clk_get_rate(host->clk); 96166bac38SJan Glauber 97166bac38SJan Glauber spin_lock_init(&host->irq_handler_lock); 98166bac38SJan Glauber sema_init(&host->mmc_serializer, 1); 99166bac38SJan Glauber 100166bac38SJan Glauber host->dev = dev; 101166bac38SJan Glauber host->acquire_bus = thunder_mmc_acquire_bus; 102166bac38SJan Glauber host->release_bus = thunder_mmc_release_bus; 103166bac38SJan Glauber host->int_enable = thunder_mmc_int_enable; 104166bac38SJan Glauber 105cd76e5c5SJan Glauber host->use_sg = true; 106166bac38SJan Glauber host->big_dma_addr = true; 107166bac38SJan Glauber host->need_irq_handler_lock = true; 108166bac38SJan Glauber host->last_slot = -1; 109166bac38SJan Glauber 110166bac38SJan Glauber ret = dma_set_mask(dev, DMA_BIT_MASK(48)); 111166bac38SJan Glauber if (ret) 112166bac38SJan Glauber goto error; 113166bac38SJan Glauber 114166bac38SJan Glauber /* 115166bac38SJan Glauber * Clear out any pending interrupts that may be left over from 116166bac38SJan Glauber * bootloader. Writing 1 to the bits clears them. 117166bac38SJan Glauber */ 118166bac38SJan Glauber writeq(127, host->base + MIO_EMM_INT_EN(host)); 119166bac38SJan Glauber writeq(3, host->base + MIO_EMM_DMA_INT_ENA_W1C(host)); 120cd76e5c5SJan Glauber /* Clear DMA FIFO */ 121cd76e5c5SJan Glauber writeq(BIT_ULL(16), host->base + MIO_EMM_DMA_FIFO_CFG(host)); 122166bac38SJan Glauber 123166bac38SJan Glauber ret = thunder_mmc_register_interrupts(host, pdev); 124166bac38SJan Glauber if (ret) 125166bac38SJan Glauber goto error; 126166bac38SJan Glauber 127166bac38SJan Glauber for_each_child_of_node(node, child_node) { 128166bac38SJan Glauber /* 129166bac38SJan Glauber * mmc_of_parse and devm* require one device per slot. 130166bac38SJan Glauber * Create a dummy device per slot and set the node pointer to 131166bac38SJan Glauber * the slot. The easiest way to get this is using 132166bac38SJan Glauber * of_platform_device_create. 133166bac38SJan Glauber */ 134166bac38SJan Glauber if (of_device_is_compatible(child_node, "mmc-slot")) { 135166bac38SJan Glauber host->slot_pdev[i] = of_platform_device_create(child_node, NULL, 136166bac38SJan Glauber &pdev->dev); 137166bac38SJan Glauber if (!host->slot_pdev[i]) 138166bac38SJan Glauber continue; 139166bac38SJan Glauber 140166bac38SJan Glauber ret = cvm_mmc_of_slot_probe(&host->slot_pdev[i]->dev, host); 141166bac38SJan Glauber if (ret) 142166bac38SJan Glauber goto error; 143166bac38SJan Glauber } 144166bac38SJan Glauber i++; 145166bac38SJan Glauber } 146166bac38SJan Glauber dev_info(dev, "probed\n"); 147166bac38SJan Glauber return 0; 148166bac38SJan Glauber 149166bac38SJan Glauber error: 1508fb83b14SJan Glauber for (i = 0; i < CAVIUM_MAX_MMC; i++) { 1518fb83b14SJan Glauber if (host->slot[i]) 1528fb83b14SJan Glauber cvm_mmc_of_slot_remove(host->slot[i]); 153b917c6d1SJan Glauber if (host->slot_pdev[i]) { 154b917c6d1SJan Glauber get_device(&host->slot_pdev[i]->dev); 1558fb83b14SJan Glauber of_platform_device_destroy(&host->slot_pdev[i]->dev, NULL); 156b917c6d1SJan Glauber put_device(&host->slot_pdev[i]->dev); 157b917c6d1SJan Glauber } 1588fb83b14SJan Glauber } 159166bac38SJan Glauber clk_disable_unprepare(host->clk); 160166bac38SJan Glauber return ret; 161166bac38SJan Glauber } 162166bac38SJan Glauber 163166bac38SJan Glauber static void thunder_mmc_remove(struct pci_dev *pdev) 164166bac38SJan Glauber { 165166bac38SJan Glauber struct cvm_mmc_host *host = pci_get_drvdata(pdev); 166166bac38SJan Glauber u64 dma_cfg; 167166bac38SJan Glauber int i; 168166bac38SJan Glauber 169166bac38SJan Glauber for (i = 0; i < CAVIUM_MAX_MMC; i++) 170166bac38SJan Glauber if (host->slot[i]) 171166bac38SJan Glauber cvm_mmc_of_slot_remove(host->slot[i]); 172166bac38SJan Glauber 173166bac38SJan Glauber dma_cfg = readq(host->dma_base + MIO_EMM_DMA_CFG(host)); 174166bac38SJan Glauber dma_cfg &= ~MIO_EMM_DMA_CFG_EN; 175166bac38SJan Glauber writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host)); 176166bac38SJan Glauber 177166bac38SJan Glauber clk_disable_unprepare(host->clk); 178166bac38SJan Glauber } 179166bac38SJan Glauber 180166bac38SJan Glauber static const struct pci_device_id thunder_mmc_id_table[] = { 181166bac38SJan Glauber { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa010) }, 182166bac38SJan Glauber { 0, } /* end of table */ 183166bac38SJan Glauber }; 184166bac38SJan Glauber 185166bac38SJan Glauber static struct pci_driver thunder_mmc_driver = { 186166bac38SJan Glauber .name = KBUILD_MODNAME, 187166bac38SJan Glauber .id_table = thunder_mmc_id_table, 188166bac38SJan Glauber .probe = thunder_mmc_probe, 189166bac38SJan Glauber .remove = thunder_mmc_remove, 190166bac38SJan Glauber }; 191166bac38SJan Glauber 192dfc28b11SWei Yongjun module_pci_driver(thunder_mmc_driver); 193166bac38SJan Glauber 194166bac38SJan Glauber MODULE_AUTHOR("Cavium Inc."); 195166bac38SJan Glauber MODULE_DESCRIPTION("Cavium ThunderX eMMC Driver"); 196166bac38SJan Glauber MODULE_LICENSE("GPL"); 197166bac38SJan Glauber MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table); 198