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 spi_pci_desc { 27 int (*setup)(struct dw_spi *); 28 u16 num_cs; 29 u16 bus_num; 30 }; 31 32 static struct spi_pci_desc spi_pci_mid_desc_1 = { 33 .setup = dw_spi_mid_init, 34 .num_cs = 5, 35 .bus_num = 0, 36 }; 37 38 static struct spi_pci_desc spi_pci_mid_desc_2 = { 39 .setup = dw_spi_mid_init, 40 .num_cs = 2, 41 .bus_num = 1, 42 }; 43 44 static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 45 { 46 struct dw_spi *dws; 47 struct spi_pci_desc *desc = (struct spi_pci_desc *)ent->driver_data; 48 int pci_bar = 0; 49 int ret; 50 51 ret = pcim_enable_device(pdev); 52 if (ret) 53 return ret; 54 55 dws = devm_kzalloc(&pdev->dev, sizeof(*dws), GFP_KERNEL); 56 if (!dws) 57 return -ENOMEM; 58 59 /* Get basic io resource and map it */ 60 dws->paddr = pci_resource_start(pdev, pci_bar); 61 62 ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev)); 63 if (ret) 64 return ret; 65 66 dws->regs = pcim_iomap_table(pdev)[pci_bar]; 67 dws->irq = pdev->irq; 68 69 /* 70 * Specific handling for platforms, like dma setup, 71 * clock rate, FIFO depth. 72 */ 73 if (desc) { 74 dws->num_cs = desc->num_cs; 75 dws->bus_num = desc->bus_num; 76 77 if (desc->setup) { 78 ret = desc->setup(dws); 79 if (ret) 80 return ret; 81 } 82 } else { 83 return -ENODEV; 84 } 85 86 ret = dw_spi_add_host(&pdev->dev, dws); 87 if (ret) 88 return ret; 89 90 /* PCI hook and SPI hook use the same drv data */ 91 pci_set_drvdata(pdev, dws); 92 93 dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n", 94 pdev->vendor, pdev->device); 95 96 return 0; 97 } 98 99 static void spi_pci_remove(struct pci_dev *pdev) 100 { 101 struct dw_spi *dws = pci_get_drvdata(pdev); 102 103 dw_spi_remove_host(dws); 104 } 105 106 #ifdef CONFIG_PM_SLEEP 107 static int spi_suspend(struct device *dev) 108 { 109 struct pci_dev *pdev = to_pci_dev(dev); 110 struct dw_spi *dws = pci_get_drvdata(pdev); 111 112 return dw_spi_suspend_host(dws); 113 } 114 115 static int spi_resume(struct device *dev) 116 { 117 struct pci_dev *pdev = to_pci_dev(dev); 118 struct dw_spi *dws = pci_get_drvdata(pdev); 119 120 return dw_spi_resume_host(dws); 121 } 122 #endif 123 124 static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume); 125 126 static const struct pci_device_id pci_ids[] = { 127 /* Intel MID platform SPI controller 0 */ 128 /* 129 * The access to the device 8086:0801 is disabled by HW, since it's 130 * exclusively used by SCU to communicate with MSIC. 131 */ 132 /* Intel MID platform SPI controller 1 */ 133 { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1}, 134 /* Intel MID platform SPI controller 2 */ 135 { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2}, 136 {}, 137 }; 138 139 static struct pci_driver dw_spi_driver = { 140 .name = DRIVER_NAME, 141 .id_table = pci_ids, 142 .probe = spi_pci_probe, 143 .remove = spi_pci_remove, 144 .driver = { 145 .pm = &dw_spi_pm_ops, 146 }, 147 }; 148 149 module_pci_driver(dw_spi_driver); 150 151 MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>"); 152 MODULE_DESCRIPTION("PCI interface driver for DW SPI Core"); 153 MODULE_LICENSE("GPL v2"); 154