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