Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2
  3#include <linux/acpi.h>
  4#include <linux/clk.h>
  5#include <linux/device.h>
  6#include <linux/err.h>
  7#include <linux/init.h>
  8#include <linux/mod_devicetable.h>
  9#include <linux/platform_device.h>
 10#include <linux/pm_runtime.h>
 11#include <linux/property.h>
 12#include <linux/types.h>
 13
 14#include "spi-pxa2xx.h"
 15
 16static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param)
 17{
 18	return param == chan->device->dev;
 19}
 20
 21static int
 22pxa2xx_spi_init_ssp(struct platform_device *pdev, struct ssp_device *ssp, enum pxa_ssp_type type)
 23{
 24	struct device *dev = &pdev->dev;
 25	struct resource *res;
 26	int status;
 27	u64 uid;
 28
 29	ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 30	if (IS_ERR(ssp->mmio_base))
 31		return PTR_ERR(ssp->mmio_base);
 32
 33	ssp->phys_base = res->start;
 34
 35	ssp->clk = devm_clk_get(dev, NULL);
 36	if (IS_ERR(ssp->clk))
 37		return PTR_ERR(ssp->clk);
 38
 39	ssp->irq = platform_get_irq(pdev, 0);
 40	if (ssp->irq < 0)
 41		return ssp->irq;
 42
 43	ssp->type = type;
 44	ssp->dev = dev;
 45
 46	status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid);
 47	if (status)
 48		ssp->port_id = -1;
 49	else
 50		ssp->port_id = uid;
 51
 52	return 0;
 53}
 54
 55static void pxa2xx_spi_ssp_release(void *ssp)
 56{
 57	pxa_ssp_free(ssp);
 58}
 59
 60static struct ssp_device *pxa2xx_spi_ssp_request(struct platform_device *pdev)
 61{
 62	struct ssp_device *ssp;
 63	int status;
 64
 65	ssp = pxa_ssp_request(pdev->id, pdev->name);
 66	if (!ssp)
 67		return NULL;
 68
 69	status = devm_add_action_or_reset(&pdev->dev, pxa2xx_spi_ssp_release, ssp);
 70	if (status)
 71		return ERR_PTR(status);
 72
 73	return ssp;
 74}
 75
 76static struct pxa2xx_spi_controller *
 77pxa2xx_spi_init_pdata(struct platform_device *pdev)
 78{
 79	struct pxa2xx_spi_controller *pdata;
 80	struct device *dev = &pdev->dev;
 81	struct device *parent = dev->parent;
 82	const void *match = device_get_match_data(dev);
 83	enum pxa_ssp_type type = SSP_UNDEFINED;
 84	struct ssp_device *ssp;
 85	bool is_lpss_priv;
 86	u32 num_cs = 1;
 87	int status;
 88
 89	ssp = pxa2xx_spi_ssp_request(pdev);
 90	if (IS_ERR(ssp))
 91		return ERR_CAST(ssp);
 92	if (ssp) {
 93		type = ssp->type;
 94	} else if (match) {
 95		type = (enum pxa_ssp_type)(uintptr_t)match;
 96	} else {
 97		u32 value;
 98
 99		status = device_property_read_u32(dev, "intel,spi-pxa2xx-type", &value);
100		if (status)
101			return ERR_PTR(status);
102
103		type = (enum pxa_ssp_type)value;
104	}
105
106	/* Validate the SSP type correctness */
107	if (!(type > SSP_UNDEFINED && type < SSP_MAX))
108		return ERR_PTR(-EINVAL);
109
110	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
111	if (!pdata)
112		return ERR_PTR(-ENOMEM);
113
114	/* Platforms with iDMA 64-bit */
115	is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv");
116	if (is_lpss_priv) {
117		pdata->tx_param = parent;
118		pdata->rx_param = parent;
119		pdata->dma_filter = pxa2xx_spi_idma_filter;
120	}
121
122	/* Read number of chip select pins, if provided */
123	device_property_read_u32(dev, "num-cs", &num_cs);
124
125	pdata->num_chipselect = num_cs;
126	pdata->is_target = device_property_read_bool(dev, "spi-slave");
127	pdata->enable_dma = true;
128	pdata->dma_burst_size = 1;
129
130	/* If SSP has been already enumerated, use it */
131	if (ssp)
132		return pdata;
133
134	status = pxa2xx_spi_init_ssp(pdev, &pdata->ssp, type);
135	if (status)
136		return ERR_PTR(status);
137
138	return pdata;
139}
140
141static int pxa2xx_spi_platform_probe(struct platform_device *pdev)
142{
143	struct pxa2xx_spi_controller *platform_info;
144	struct device *dev = &pdev->dev;
145	struct ssp_device *ssp;
146	int ret;
147
148	platform_info = dev_get_platdata(dev);
149	if (!platform_info) {
150		platform_info = pxa2xx_spi_init_pdata(pdev);
151		if (IS_ERR(platform_info))
152			return dev_err_probe(dev, PTR_ERR(platform_info), "missing platform data\n");
153	}
154
155	ssp = pxa2xx_spi_ssp_request(pdev);
156	if (IS_ERR(ssp))
157		return PTR_ERR(ssp);
158	if (!ssp)
159		ssp = &platform_info->ssp;
160
161	pm_runtime_set_autosuspend_delay(dev, 50);
162	pm_runtime_use_autosuspend(dev);
163	pm_runtime_set_active(dev);
164	pm_runtime_enable(dev);
165
166	ret = pxa2xx_spi_probe(dev, ssp, platform_info);
167	if (ret)
168		pm_runtime_disable(dev);
169
170	return ret;
171}
172
173static void pxa2xx_spi_platform_remove(struct platform_device *pdev)
174{
175	struct device *dev = &pdev->dev;
176
177	pm_runtime_get_sync(dev);
178
179	pxa2xx_spi_remove(dev);
180
181	pm_runtime_put_noidle(dev);
182	pm_runtime_disable(dev);
183}
184
185static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
186	{ "80860F0E" },
187	{ "8086228E" },
188	{ "INT33C0" },
189	{ "INT33C1" },
190	{ "INT3430" },
191	{ "INT3431" },
192	{}
193};
194MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
195
196static const struct of_device_id pxa2xx_spi_of_match[] = {
197	{ .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP },
198	{}
199};
200MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match);
201
202static struct platform_driver driver = {
203	.driver = {
204		.name	= "pxa2xx-spi",
205		.pm	= pm_ptr(&pxa2xx_spi_pm_ops),
206		.acpi_match_table = pxa2xx_spi_acpi_match,
207		.of_match_table = pxa2xx_spi_of_match,
208	},
209	.probe = pxa2xx_spi_platform_probe,
210	.remove = pxa2xx_spi_platform_remove,
211};
212
213static int __init pxa2xx_spi_init(void)
214{
215	return platform_driver_register(&driver);
216}
217subsys_initcall(pxa2xx_spi_init);
218
219static void __exit pxa2xx_spi_exit(void)
220{
221	platform_driver_unregister(&driver);
222}
223module_exit(pxa2xx_spi_exit);
224
225MODULE_AUTHOR("Stephen Street");
226MODULE_DESCRIPTION("PXA2xx SSP SPI Controller platform driver");
227MODULE_LICENSE("GPL");
228MODULE_IMPORT_NS("SPI_PXA2xx");
229MODULE_ALIAS("platform:pxa2xx-spi");
230MODULE_SOFTDEP("pre: dw_dmac");