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