linux/drivers/spi/dw_spi_pci.c
<<
>>
Prefs
   1/*
   2 * dw_spi_pci.c - PCI interface driver for DW SPI Core
   3 *
   4 * Copyright (c) 2009, 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 * You should have received a copy of the GNU General Public License along
  16 * with this program; if not, write to the Free Software Foundation,
  17 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  18 */
  19
  20#include <linux/interrupt.h>
  21#include <linux/pci.h>
  22#include <linux/slab.h>
  23#include <linux/spi/spi.h>
  24
  25#include "dw_spi.h"
  26
  27#define DRIVER_NAME "dw_spi_pci"
  28
  29struct dw_spi_pci {
  30        struct pci_dev  *pdev;
  31        struct dw_spi   dws;
  32};
  33
  34static int __devinit spi_pci_probe(struct pci_dev *pdev,
  35        const struct pci_device_id *ent)
  36{
  37        struct dw_spi_pci *dwpci;
  38        struct dw_spi *dws;
  39        int pci_bar = 0;
  40        int ret;
  41
  42        printk(KERN_INFO "DW: found PCI SPI controller(ID: %04x:%04x)\n",
  43                pdev->vendor, pdev->device);
  44
  45        ret = pci_enable_device(pdev);
  46        if (ret)
  47                return ret;
  48
  49        dwpci = kzalloc(sizeof(struct dw_spi_pci), GFP_KERNEL);
  50        if (!dwpci) {
  51                ret = -ENOMEM;
  52                goto err_disable;
  53        }
  54
  55        dwpci->pdev = pdev;
  56        dws = &dwpci->dws;
  57
  58        /* Get basic io resource and map it */
  59        dws->paddr = pci_resource_start(pdev, pci_bar);
  60        dws->iolen = pci_resource_len(pdev, pci_bar);
  61
  62        ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
  63        if (ret)
  64                goto err_kfree;
  65
  66        dws->regs = ioremap_nocache((unsigned long)dws->paddr,
  67                                pci_resource_len(pdev, pci_bar));
  68        if (!dws->regs) {
  69                ret = -ENOMEM;
  70                goto err_release_reg;
  71        }
  72
  73        dws->parent_dev = &pdev->dev;
  74        dws->bus_num = 0;
  75        dws->num_cs = 4;
  76        dws->irq = pdev->irq;
  77
  78        /*
  79         * Specific handling for Intel MID paltforms, like dma setup,
  80         * clock rate, FIFO depth.
  81         */
  82        if (pdev->device == 0x0800) {
  83                ret = dw_spi_mid_init(dws);
  84                if (ret)
  85                        goto err_unmap;
  86        }
  87
  88        ret = dw_spi_add_host(dws);
  89        if (ret)
  90                goto err_unmap;
  91
  92        /* PCI hook and SPI hook use the same drv data */
  93        pci_set_drvdata(pdev, dwpci);
  94        return 0;
  95
  96err_unmap:
  97        iounmap(dws->regs);
  98err_release_reg:
  99        pci_release_region(pdev, pci_bar);
 100err_kfree:
 101        kfree(dwpci);
 102err_disable:
 103        pci_disable_device(pdev);
 104        return ret;
 105}
 106
 107static void __devexit spi_pci_remove(struct pci_dev *pdev)
 108{
 109        struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
 110
 111        pci_set_drvdata(pdev, NULL);
 112        dw_spi_remove_host(&dwpci->dws);
 113        iounmap(dwpci->dws.regs);
 114        pci_release_region(pdev, 0);
 115        kfree(dwpci);
 116        pci_disable_device(pdev);
 117}
 118
 119#ifdef CONFIG_PM
 120static int spi_suspend(struct pci_dev *pdev, pm_message_t state)
 121{
 122        struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
 123        int ret;
 124
 125        ret = dw_spi_suspend_host(&dwpci->dws);
 126        if (ret)
 127                return ret;
 128        pci_save_state(pdev);
 129        pci_disable_device(pdev);
 130        pci_set_power_state(pdev, pci_choose_state(pdev, state));
 131        return ret;
 132}
 133
 134static int spi_resume(struct pci_dev *pdev)
 135{
 136        struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
 137        int ret;
 138
 139        pci_set_power_state(pdev, PCI_D0);
 140        pci_restore_state(pdev);
 141        ret = pci_enable_device(pdev);
 142        if (ret)
 143                return ret;
 144        return dw_spi_resume_host(&dwpci->dws);
 145}
 146#else
 147#define spi_suspend     NULL
 148#define spi_resume      NULL
 149#endif
 150
 151static const struct pci_device_id pci_ids[] __devinitdata = {
 152        /* Intel MID platform SPI controller 0 */
 153        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
 154        {},
 155};
 156
 157static struct pci_driver dw_spi_driver = {
 158        .name =         DRIVER_NAME,
 159        .id_table =     pci_ids,
 160        .probe =        spi_pci_probe,
 161        .remove =       __devexit_p(spi_pci_remove),
 162        .suspend =      spi_suspend,
 163        .resume =       spi_resume,
 164};
 165
 166static int __init mrst_spi_init(void)
 167{
 168        return pci_register_driver(&dw_spi_driver);
 169}
 170
 171static void __exit mrst_spi_exit(void)
 172{
 173        pci_unregister_driver(&dw_spi_driver);
 174}
 175
 176module_init(mrst_spi_init);
 177module_exit(mrst_spi_exit);
 178
 179MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
 180MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
 181MODULE_LICENSE("GPL v2");
 182