1 /* 2 * PCI interface driver for DW SPI Core 3 * 4 * Copyright (c) 2009, 2014 Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 */ 15 16 #include <linux/interrupt.h> 17 #include <linux/pci.h> 18 #include <linux/slab.h> 19 #include <linux/spi/spi.h> 20 #include <linux/module.h> 21 22 #include "spi-dw.h" 23 24 #define DRIVER_NAME "dw_spi_pci" 25 26 struct dw_spi_pci { 27 struct pci_dev *pdev; 28 struct dw_spi dws; 29 }; 30 31 struct spi_pci_desc { 32 int (*setup)(struct dw_spi *); 33 u16 num_cs; 34 u16 bus_num; 35 }; 36 37 static struct spi_pci_desc spi_pci_mid_desc_1 = { 38 .setup = dw_spi_mid_init, 39 .num_cs = 32, 40 .bus_num = 0, 41 }; 42 43 static struct spi_pci_desc spi_pci_mid_desc_2 = { 44 .setup = dw_spi_mid_init, 45 .num_cs = 4, 46 .bus_num = 1, 47 }; 48 49 static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 50 { 51 struct dw_spi_pci *dwpci; 52 struct dw_spi *dws; 53 struct spi_pci_desc *desc = (struct spi_pci_desc *)ent->driver_data; 54 int pci_bar = 0; 55 int ret; 56 57 ret = pcim_enable_device(pdev); 58 if (ret) 59 return ret; 60 61 dwpci = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_pci), 62 GFP_KERNEL); 63 if (!dwpci) 64 return -ENOMEM; 65 66 dwpci->pdev = pdev; 67 dws = &dwpci->dws; 68 69 /* Get basic io resource and map it */ 70 dws->paddr = pci_resource_start(pdev, pci_bar); 71 72 ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev)); 73 if (ret) 74 return ret; 75 76 dws->regs = pcim_iomap_table(pdev)[pci_bar]; 77 78 dws->irq = pdev->irq; 79 80 /* 81 * Specific handling for paltforms, like dma setup, 82 * clock rate, FIFO depth. 83 */ 84 if (desc) { 85 dws->num_cs = desc->num_cs; 86 dws->bus_num = desc->bus_num; 87 88 if (desc->setup) { 89 ret = desc->setup(dws); 90 if (ret) 91 return ret; 92 } 93 } else { 94 return -ENODEV; 95 } 96 97 ret = dw_spi_add_host(&pdev->dev, dws); 98 if (ret) 99 return ret; 100 101 /* PCI hook and SPI hook use the same drv data */ 102 pci_set_drvdata(pdev, dwpci); 103 104 dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n", 105 pdev->vendor, pdev->device); 106 107 return 0; 108 } 109 110 static void spi_pci_remove(struct pci_dev *pdev) 111 { 112 struct dw_spi_pci *dwpci = pci_get_drvdata(pdev); 113 114 dw_spi_remove_host(&dwpci->dws); 115 } 116 117 #ifdef CONFIG_PM_SLEEP 118 static int spi_suspend(struct device *dev) 119 { 120 struct pci_dev *pdev = to_pci_dev(dev); 121 struct dw_spi_pci *dwpci = pci_get_drvdata(pdev); 122 123 return dw_spi_suspend_host(&dwpci->dws); 124 } 125 126 static int spi_resume(struct device *dev) 127 { 128 struct pci_dev *pdev = to_pci_dev(dev); 129 struct dw_spi_pci *dwpci = pci_get_drvdata(pdev); 130 131 return dw_spi_resume_host(&dwpci->dws); 132 } 133 #endif 134 135 static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume); 136 137 static const struct pci_device_id pci_ids[] = { 138 /* Intel MID platform SPI controller 0 */ 139 /* 140 * The access to the device 8086:0801 is disabled by HW, since it's 141 * exclusively used by SCU to communicate with MSIC. 142 */ 143 /* Intel MID platform SPI controller 1 */ 144 { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1}, 145 /* Intel MID platform SPI controller 2 */ 146 { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2}, 147 {}, 148 }; 149 150 static struct pci_driver dw_spi_driver = { 151 .name = DRIVER_NAME, 152 .id_table = pci_ids, 153 .probe = spi_pci_probe, 154 .remove = spi_pci_remove, 155 .driver = { 156 .pm = &dw_spi_pm_ops, 157 }, 158 }; 159 160 module_pci_driver(dw_spi_driver); 161 162 MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>"); 163 MODULE_DESCRIPTION("PCI interface driver for DW SPI Core"); 164 MODULE_LICENSE("GPL v2"); 165