7063c0d942
dw_spi driver in upstream only supports PIO mode, and this patch will support it to cowork with the Designware dma controller used on Intel Moorestown platform, at the same time it provides a general framework to support dw_spi core to cowork with dma controllers on other platforms It has been tested with a Option GTM501L 3G modem and Infenion 60x60 modem. To use DMA mode, DMA controller 2 of Moorestown has to be enabled Also change the dma interface suggested by Linus Walleij. Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Feng Tang <feng.tang@intel.com> [Typo fix and renames to match intel_mid_dma renaming] Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
180 lines
4.1 KiB
C
180 lines
4.1 KiB
C
/*
|
|
* dw_spi_pci.c - PCI interface driver for DW SPI Core
|
|
*
|
|
* Copyright (c) 2009, Intel Corporation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spi/dw_spi.h>
|
|
#include <linux/spi/spi.h>
|
|
|
|
#define DRIVER_NAME "dw_spi_pci"
|
|
|
|
struct dw_spi_pci {
|
|
struct pci_dev *pdev;
|
|
struct dw_spi dws;
|
|
};
|
|
|
|
static int __devinit spi_pci_probe(struct pci_dev *pdev,
|
|
const struct pci_device_id *ent)
|
|
{
|
|
struct dw_spi_pci *dwpci;
|
|
struct dw_spi *dws;
|
|
int pci_bar = 0;
|
|
int ret;
|
|
|
|
printk(KERN_INFO "DW: found PCI SPI controller(ID: %04x:%04x)\n",
|
|
pdev->vendor, pdev->device);
|
|
|
|
ret = pci_enable_device(pdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dwpci = kzalloc(sizeof(struct dw_spi_pci), GFP_KERNEL);
|
|
if (!dwpci) {
|
|
ret = -ENOMEM;
|
|
goto err_disable;
|
|
}
|
|
|
|
dwpci->pdev = pdev;
|
|
dws = &dwpci->dws;
|
|
|
|
/* Get basic io resource and map it */
|
|
dws->paddr = pci_resource_start(pdev, pci_bar);
|
|
dws->iolen = pci_resource_len(pdev, pci_bar);
|
|
|
|
ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
|
|
if (ret)
|
|
goto err_kfree;
|
|
|
|
dws->regs = ioremap_nocache((unsigned long)dws->paddr,
|
|
pci_resource_len(pdev, pci_bar));
|
|
if (!dws->regs) {
|
|
ret = -ENOMEM;
|
|
goto err_release_reg;
|
|
}
|
|
|
|
dws->parent_dev = &pdev->dev;
|
|
dws->bus_num = 0;
|
|
dws->num_cs = 4;
|
|
dws->irq = pdev->irq;
|
|
|
|
/*
|
|
* Specific handling for Intel MID paltforms, like dma setup,
|
|
* clock rate, FIFO depth.
|
|
*/
|
|
if (pdev->device == 0x0800) {
|
|
ret = dw_spi_mid_init(dws);
|
|
if (ret)
|
|
goto err_unmap;
|
|
}
|
|
|
|
ret = dw_spi_add_host(dws);
|
|
if (ret)
|
|
goto err_unmap;
|
|
|
|
/* PCI hook and SPI hook use the same drv data */
|
|
pci_set_drvdata(pdev, dwpci);
|
|
return 0;
|
|
|
|
err_unmap:
|
|
iounmap(dws->regs);
|
|
err_release_reg:
|
|
pci_release_region(pdev, pci_bar);
|
|
err_kfree:
|
|
kfree(dwpci);
|
|
err_disable:
|
|
pci_disable_device(pdev);
|
|
return ret;
|
|
}
|
|
|
|
static void __devexit spi_pci_remove(struct pci_dev *pdev)
|
|
{
|
|
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
dw_spi_remove_host(&dwpci->dws);
|
|
iounmap(dwpci->dws.regs);
|
|
pci_release_region(pdev, 0);
|
|
kfree(dwpci);
|
|
pci_disable_device(pdev);
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int spi_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
{
|
|
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
|
|
int ret;
|
|
|
|
ret = dw_spi_suspend_host(&dwpci->dws);
|
|
if (ret)
|
|
return ret;
|
|
pci_save_state(pdev);
|
|
pci_disable_device(pdev);
|
|
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
|
return ret;
|
|
}
|
|
|
|
static int spi_resume(struct pci_dev *pdev)
|
|
{
|
|
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
|
|
int ret;
|
|
|
|
pci_set_power_state(pdev, PCI_D0);
|
|
pci_restore_state(pdev);
|
|
ret = pci_enable_device(pdev);
|
|
if (ret)
|
|
return ret;
|
|
return dw_spi_resume_host(&dwpci->dws);
|
|
}
|
|
#else
|
|
#define spi_suspend NULL
|
|
#define spi_resume NULL
|
|
#endif
|
|
|
|
static const struct pci_device_id pci_ids[] __devinitdata = {
|
|
/* Intel MID platform SPI controller 0 */
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
|
|
{},
|
|
};
|
|
|
|
static struct pci_driver dw_spi_driver = {
|
|
.name = DRIVER_NAME,
|
|
.id_table = pci_ids,
|
|
.probe = spi_pci_probe,
|
|
.remove = __devexit_p(spi_pci_remove),
|
|
.suspend = spi_suspend,
|
|
.resume = spi_resume,
|
|
};
|
|
|
|
static int __init mrst_spi_init(void)
|
|
{
|
|
return pci_register_driver(&dw_spi_driver);
|
|
}
|
|
|
|
static void __exit mrst_spi_exit(void)
|
|
{
|
|
pci_unregister_driver(&dw_spi_driver);
|
|
}
|
|
|
|
module_init(mrst_spi_init);
|
|
module_exit(mrst_spi_exit);
|
|
|
|
MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
|
|
MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
|
|
MODULE_LICENSE("GPL v2");
|