1/*
2 *  linux/arch/arm/mach-pxa/ssp.c
3 *
4 *  based on linux/arch/arm/mach-sa1100/ssp.c by Russell King
5 *
6 *  Copyright (C) 2003 Russell King.
7 *  Copyright (C) 2003 Wolfson Microelectronics PLC
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 *  PXA2xx SSP driver.  This provides the generic core for simple
14 *  IO-based SSP applications and allows easy port setup for DMA access.
15 *
16 *  Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/sched.h>
22#include <linux/slab.h>
23#include <linux/errno.h>
24#include <linux/interrupt.h>
25#include <linux/ioport.h>
26#include <linux/init.h>
27#include <linux/mutex.h>
28#include <linux/clk.h>
29#include <linux/err.h>
30#include <linux/platform_device.h>
31#include <linux/spi/pxa2xx_spi.h>
32#include <linux/io.h>
33#include <linux/of.h>
34#include <linux/of_device.h>
35
36#include <asm/irq.h>
37#include <mach/hardware.h>
38
39static DEFINE_MUTEX(ssp_lock);
40static LIST_HEAD(ssp_list);
41
42struct ssp_device *pxa_ssp_request(int port, const char *label)
43{
44	struct ssp_device *ssp = NULL;
45
46	mutex_lock(&ssp_lock);
47
48	list_for_each_entry(ssp, &ssp_list, node) {
49		if (ssp->port_id == port && ssp->use_count == 0) {
50			ssp->use_count++;
51			ssp->label = label;
52			break;
53		}
54	}
55
56	mutex_unlock(&ssp_lock);
57
58	if (&ssp->node == &ssp_list)
59		return NULL;
60
61	return ssp;
62}
63EXPORT_SYMBOL(pxa_ssp_request);
64
65struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
66				      const char *label)
67{
68	struct ssp_device *ssp = NULL;
69
70	mutex_lock(&ssp_lock);
71
72	list_for_each_entry(ssp, &ssp_list, node) {
73		if (ssp->of_node == of_node && ssp->use_count == 0) {
74			ssp->use_count++;
75			ssp->label = label;
76			break;
77		}
78	}
79
80	mutex_unlock(&ssp_lock);
81
82	if (&ssp->node == &ssp_list)
83		return NULL;
84
85	return ssp;
86}
87EXPORT_SYMBOL(pxa_ssp_request_of);
88
89void pxa_ssp_free(struct ssp_device *ssp)
90{
91	mutex_lock(&ssp_lock);
92	if (ssp->use_count) {
93		ssp->use_count--;
94		ssp->label = NULL;
95	} else
96		dev_err(&ssp->pdev->dev, "device already free\n");
97	mutex_unlock(&ssp_lock);
98}
99EXPORT_SYMBOL(pxa_ssp_free);
100
101#ifdef CONFIG_OF
102static const struct of_device_id pxa_ssp_of_ids[] = {
103	{ .compatible = "mrvl,pxa25x-ssp",	.data = (void *) PXA25x_SSP },
104	{ .compatible = "mvrl,pxa25x-nssp",	.data = (void *) PXA25x_NSSP },
105	{ .compatible = "mrvl,pxa27x-ssp",	.data = (void *) PXA27x_SSP },
106	{ .compatible = "mrvl,pxa3xx-ssp",	.data = (void *) PXA3xx_SSP },
107	{ .compatible = "mvrl,pxa168-ssp",	.data = (void *) PXA168_SSP },
108	{ .compatible = "mrvl,pxa910-ssp",	.data = (void *) PXA910_SSP },
109	{ .compatible = "mrvl,ce4100-ssp",	.data = (void *) CE4100_SSP },
110	{ .compatible = "mrvl,lpss-ssp",	.data = (void *) LPSS_SSP },
111	{ },
112};
113MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
114#endif
115
116static int pxa_ssp_probe(struct platform_device *pdev)
117{
118	struct resource *res;
119	struct ssp_device *ssp;
120	struct device *dev = &pdev->dev;
121
122	ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL);
123	if (ssp == NULL)
124		return -ENOMEM;
125
126	ssp->pdev = pdev;
127
128	ssp->clk = devm_clk_get(dev, NULL);
129	if (IS_ERR(ssp->clk))
130		return PTR_ERR(ssp->clk);
131
132	if (dev->of_node) {
133		struct of_phandle_args dma_spec;
134		struct device_node *np = dev->of_node;
135		int ret;
136
137		/*
138		 * FIXME: we should allocate the DMA channel from this
139		 * context and pass the channel down to the ssp users.
140		 * For now, we lookup the rx and tx indices manually
141		 */
142
143		/* rx */
144		ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells",
145						 0, &dma_spec);
146
147		if (ret) {
148			dev_err(dev, "Can't parse dmas property\n");
149			return -ENODEV;
150		}
151		ssp->drcmr_rx = dma_spec.args[0];
152		of_node_put(dma_spec.np);
153
154		/* tx */
155		ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells",
156						 1, &dma_spec);
157		if (ret) {
158			dev_err(dev, "Can't parse dmas property\n");
159			return -ENODEV;
160		}
161		ssp->drcmr_tx = dma_spec.args[0];
162		of_node_put(dma_spec.np);
163	} else {
164		res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
165		if (res == NULL) {
166			dev_err(dev, "no SSP RX DRCMR defined\n");
167			return -ENODEV;
168		}
169		ssp->drcmr_rx = res->start;
170
171		res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
172		if (res == NULL) {
173			dev_err(dev, "no SSP TX DRCMR defined\n");
174			return -ENODEV;
175		}
176		ssp->drcmr_tx = res->start;
177	}
178
179	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
180	if (res == NULL) {
181		dev_err(dev, "no memory resource defined\n");
182		return -ENODEV;
183	}
184
185	res = devm_request_mem_region(dev, res->start, resource_size(res),
186				      pdev->name);
187	if (res == NULL) {
188		dev_err(dev, "failed to request memory resource\n");
189		return -EBUSY;
190	}
191
192	ssp->phys_base = res->start;
193
194	ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res));
195	if (ssp->mmio_base == NULL) {
196		dev_err(dev, "failed to ioremap() registers\n");
197		return -ENODEV;
198	}
199
200	ssp->irq = platform_get_irq(pdev, 0);
201	if (ssp->irq < 0) {
202		dev_err(dev, "no IRQ resource defined\n");
203		return -ENODEV;
204	}
205
206	if (dev->of_node) {
207		const struct of_device_id *id =
208			of_match_device(of_match_ptr(pxa_ssp_of_ids), dev);
209		ssp->type = (int) id->data;
210	} else {
211		const struct platform_device_id *id =
212			platform_get_device_id(pdev);
213		ssp->type = (int) id->driver_data;
214
215		/* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
216		 * starts from 0, do a translation here
217		 */
218		ssp->port_id = pdev->id + 1;
219	}
220
221	ssp->use_count = 0;
222	ssp->of_node = dev->of_node;
223
224	mutex_lock(&ssp_lock);
225	list_add(&ssp->node, &ssp_list);
226	mutex_unlock(&ssp_lock);
227
228	platform_set_drvdata(pdev, ssp);
229
230	return 0;
231}
232
233static int pxa_ssp_remove(struct platform_device *pdev)
234{
235	struct resource *res;
236	struct ssp_device *ssp;
237
238	ssp = platform_get_drvdata(pdev);
239	if (ssp == NULL)
240		return -ENODEV;
241
242	iounmap(ssp->mmio_base);
243
244	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
245	release_mem_region(res->start, resource_size(res));
246
247	clk_put(ssp->clk);
248
249	mutex_lock(&ssp_lock);
250	list_del(&ssp->node);
251	mutex_unlock(&ssp_lock);
252
253	kfree(ssp);
254	return 0;
255}
256
257static const struct platform_device_id ssp_id_table[] = {
258	{ "pxa25x-ssp",		PXA25x_SSP },
259	{ "pxa25x-nssp",	PXA25x_NSSP },
260	{ "pxa27x-ssp",		PXA27x_SSP },
261	{ "pxa3xx-ssp",		PXA3xx_SSP },
262	{ "pxa168-ssp",		PXA168_SSP },
263	{ "pxa910-ssp",		PXA910_SSP },
264	{ },
265};
266
267static struct platform_driver pxa_ssp_driver = {
268	.probe		= pxa_ssp_probe,
269	.remove		= pxa_ssp_remove,
270	.driver		= {
271		.name		= "pxa2xx-ssp",
272		.of_match_table	= of_match_ptr(pxa_ssp_of_ids),
273	},
274	.id_table	= ssp_id_table,
275};
276
277static int __init pxa_ssp_init(void)
278{
279	return platform_driver_register(&pxa_ssp_driver);
280}
281
282static void __exit pxa_ssp_exit(void)
283{
284	platform_driver_unregister(&pxa_ssp_driver);
285}
286
287arch_initcall(pxa_ssp_init);
288module_exit(pxa_ssp_exit);
289
290MODULE_DESCRIPTION("PXA SSP driver");
291MODULE_AUTHOR("Liam Girdwood");
292MODULE_LICENSE("GPL");
293