xref: /openbmc/linux/drivers/spi/spi-dw-pci.c (revision 3c6a73cc)
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 };
34 
35 static struct spi_pci_desc spi_pci_mid_desc = {
36 	.setup = dw_spi_mid_init,
37 };
38 
39 static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
40 {
41 	struct dw_spi_pci *dwpci;
42 	struct dw_spi *dws;
43 	struct spi_pci_desc *desc = (struct spi_pci_desc *)ent->driver_data;
44 	int pci_bar = 0;
45 	int ret;
46 
47 	ret = pcim_enable_device(pdev);
48 	if (ret)
49 		return ret;
50 
51 	dwpci = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_pci),
52 			GFP_KERNEL);
53 	if (!dwpci)
54 		return -ENOMEM;
55 
56 	dwpci->pdev = pdev;
57 	dws = &dwpci->dws;
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 
68 	dws->bus_num = 0;
69 	dws->num_cs = 4;
70 	dws->irq = pdev->irq;
71 
72 	/*
73 	 * Specific handling for paltforms, like dma setup,
74 	 * clock rate, FIFO depth.
75 	 */
76 	if (desc && desc->setup) {
77 		ret = desc->setup(dws);
78 		if (ret)
79 			return ret;
80 	}
81 
82 	ret = dw_spi_add_host(&pdev->dev, dws);
83 	if (ret)
84 		return ret;
85 
86 	/* PCI hook and SPI hook use the same drv data */
87 	pci_set_drvdata(pdev, dwpci);
88 
89 	dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
90 		pdev->vendor, pdev->device);
91 
92 	return 0;
93 }
94 
95 static void spi_pci_remove(struct pci_dev *pdev)
96 {
97 	struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
98 
99 	dw_spi_remove_host(&dwpci->dws);
100 }
101 
102 #ifdef CONFIG_PM_SLEEP
103 static int spi_suspend(struct device *dev)
104 {
105 	struct pci_dev *pdev = to_pci_dev(dev);
106 	struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
107 
108 	return dw_spi_suspend_host(&dwpci->dws);
109 }
110 
111 static int spi_resume(struct device *dev)
112 {
113 	struct pci_dev *pdev = to_pci_dev(dev);
114 	struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
115 
116 	return dw_spi_resume_host(&dwpci->dws);
117 }
118 #endif
119 
120 static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume);
121 
122 static const struct pci_device_id pci_ids[] = {
123 	/* Intel MID platform SPI controller 0 */
124 	{ PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc},
125 	{},
126 };
127 
128 static struct pci_driver dw_spi_driver = {
129 	.name =		DRIVER_NAME,
130 	.id_table =	pci_ids,
131 	.probe =	spi_pci_probe,
132 	.remove =	spi_pci_remove,
133 	.driver         = {
134 		.pm     = &dw_spi_pm_ops,
135 	},
136 };
137 
138 module_pci_driver(dw_spi_driver);
139 
140 MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
141 MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
142 MODULE_LICENSE("GPL v2");
143