xref: /openbmc/linux/drivers/spi/spi-dw-pci.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * PCI interface driver for DW SPI Core
4   *
5   * Copyright (c) 2009, 2014 Intel Corporation.
6   */
7  
8  #include <linux/pci.h>
9  #include <linux/pm_runtime.h>
10  #include <linux/slab.h>
11  #include <linux/spi/spi.h>
12  #include <linux/module.h>
13  
14  #include "spi-dw.h"
15  
16  #define DRIVER_NAME "dw_spi_pci"
17  
18  /* HW info for MRST Clk Control Unit, 32b reg per controller */
19  #define MRST_SPI_CLK_BASE	100000000	/* 100m */
20  #define MRST_CLK_SPI_REG	0xff11d86c
21  #define CLK_SPI_BDIV_OFFSET	0
22  #define CLK_SPI_BDIV_MASK	0x00000007
23  #define CLK_SPI_CDIV_OFFSET	9
24  #define CLK_SPI_CDIV_MASK	0x00000e00
25  #define CLK_SPI_DISABLE_OFFSET	8
26  
27  struct dw_spi_pci_desc {
28  	int	(*setup)(struct dw_spi *);
29  	u16	num_cs;
30  	u16	bus_num;
31  	u32	max_freq;
32  };
33  
dw_spi_pci_mid_init(struct dw_spi * dws)34  static int dw_spi_pci_mid_init(struct dw_spi *dws)
35  {
36  	void __iomem *clk_reg;
37  	u32 clk_cdiv;
38  
39  	clk_reg = ioremap(MRST_CLK_SPI_REG, 16);
40  	if (!clk_reg)
41  		return -ENOMEM;
42  
43  	/* Get SPI controller operating freq info */
44  	clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32));
45  	clk_cdiv &= CLK_SPI_CDIV_MASK;
46  	clk_cdiv >>= CLK_SPI_CDIV_OFFSET;
47  	dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1);
48  
49  	iounmap(clk_reg);
50  
51  	dw_spi_dma_setup_mfld(dws);
52  
53  	return 0;
54  }
55  
dw_spi_pci_generic_init(struct dw_spi * dws)56  static int dw_spi_pci_generic_init(struct dw_spi *dws)
57  {
58  	dw_spi_dma_setup_generic(dws);
59  
60  	return 0;
61  }
62  
63  static struct dw_spi_pci_desc dw_spi_pci_mid_desc_1 = {
64  	.setup = dw_spi_pci_mid_init,
65  	.num_cs = 5,
66  	.bus_num = 0,
67  };
68  
69  static struct dw_spi_pci_desc dw_spi_pci_mid_desc_2 = {
70  	.setup = dw_spi_pci_mid_init,
71  	.num_cs = 2,
72  	.bus_num = 1,
73  };
74  
75  static struct dw_spi_pci_desc dw_spi_pci_ehl_desc = {
76  	.setup = dw_spi_pci_generic_init,
77  	.num_cs = 2,
78  	.bus_num = -1,
79  	.max_freq = 100000000,
80  };
81  
dw_spi_pci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)82  static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
83  {
84  	struct dw_spi_pci_desc *desc = (struct dw_spi_pci_desc *)ent->driver_data;
85  	struct dw_spi *dws;
86  	int pci_bar = 0;
87  	int ret;
88  
89  	ret = pcim_enable_device(pdev);
90  	if (ret)
91  		return ret;
92  
93  	dws = devm_kzalloc(&pdev->dev, sizeof(*dws), GFP_KERNEL);
94  	if (!dws)
95  		return -ENOMEM;
96  
97  	/* Get basic io resource and map it */
98  	dws->paddr = pci_resource_start(pdev, pci_bar);
99  	pci_set_master(pdev);
100  
101  	ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev));
102  	if (ret)
103  		return ret;
104  
105  	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
106  	if (ret < 0)
107  		return ret;
108  
109  	dws->regs = pcim_iomap_table(pdev)[pci_bar];
110  	dws->irq = pci_irq_vector(pdev, 0);
111  
112  	/*
113  	 * Specific handling for platforms, like dma setup,
114  	 * clock rate, FIFO depth.
115  	 */
116  	if (desc) {
117  		dws->num_cs = desc->num_cs;
118  		dws->bus_num = desc->bus_num;
119  		dws->max_freq = desc->max_freq;
120  
121  		if (desc->setup) {
122  			ret = desc->setup(dws);
123  			if (ret)
124  				goto err_free_irq_vectors;
125  		}
126  	} else {
127  		ret = -ENODEV;
128  		goto err_free_irq_vectors;
129  	}
130  
131  	ret = dw_spi_add_host(&pdev->dev, dws);
132  	if (ret)
133  		goto err_free_irq_vectors;
134  
135  	/* PCI hook and SPI hook use the same drv data */
136  	pci_set_drvdata(pdev, dws);
137  
138  	dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
139  		pdev->vendor, pdev->device);
140  
141  	pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
142  	pm_runtime_use_autosuspend(&pdev->dev);
143  	pm_runtime_put_autosuspend(&pdev->dev);
144  	pm_runtime_allow(&pdev->dev);
145  
146  	return 0;
147  
148  err_free_irq_vectors:
149  	pci_free_irq_vectors(pdev);
150  	return ret;
151  }
152  
dw_spi_pci_remove(struct pci_dev * pdev)153  static void dw_spi_pci_remove(struct pci_dev *pdev)
154  {
155  	struct dw_spi *dws = pci_get_drvdata(pdev);
156  
157  	pm_runtime_forbid(&pdev->dev);
158  	pm_runtime_get_noresume(&pdev->dev);
159  
160  	dw_spi_remove_host(dws);
161  	pci_free_irq_vectors(pdev);
162  }
163  
164  #ifdef CONFIG_PM_SLEEP
dw_spi_pci_suspend(struct device * dev)165  static int dw_spi_pci_suspend(struct device *dev)
166  {
167  	struct dw_spi *dws = dev_get_drvdata(dev);
168  
169  	return dw_spi_suspend_host(dws);
170  }
171  
dw_spi_pci_resume(struct device * dev)172  static int dw_spi_pci_resume(struct device *dev)
173  {
174  	struct dw_spi *dws = dev_get_drvdata(dev);
175  
176  	return dw_spi_resume_host(dws);
177  }
178  #endif
179  
180  static SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume);
181  
182  static const struct pci_device_id dw_spi_pci_ids[] = {
183  	/* Intel MID platform SPI controller 0 */
184  	/*
185  	 * The access to the device 8086:0801 is disabled by HW, since it's
186  	 * exclusively used by SCU to communicate with MSIC.
187  	 */
188  	/* Intel MID platform SPI controller 1 */
189  	{ PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&dw_spi_pci_mid_desc_1},
190  	/* Intel MID platform SPI controller 2 */
191  	{ PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&dw_spi_pci_mid_desc_2},
192  	/* Intel Elkhart Lake PSE SPI controllers */
193  	{ PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
194  	{ PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
195  	{ PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
196  	{ PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
197  	{},
198  };
199  MODULE_DEVICE_TABLE(pci, dw_spi_pci_ids);
200  
201  static struct pci_driver dw_spi_pci_driver = {
202  	.name =		DRIVER_NAME,
203  	.id_table =	dw_spi_pci_ids,
204  	.probe =	dw_spi_pci_probe,
205  	.remove =	dw_spi_pci_remove,
206  	.driver         = {
207  		.pm     = &dw_spi_pci_pm_ops,
208  	},
209  };
210  module_pci_driver(dw_spi_pci_driver);
211  
212  MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
213  MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
214  MODULE_LICENSE("GPL v2");
215  MODULE_IMPORT_NS(SPI_DW_CORE);
216