xref: /linux/drivers/soc/pxa/ssp.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/arch/arm/mach-pxa/ssp.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  based on linux/arch/arm/mach-sa1100/ssp.c by Russell King
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Copyright (C) 2003 Russell King.
81da177e4SLinus Torvalds  *  Copyright (C) 2003 Wolfson Microelectronics PLC
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  PXA2xx SSP driver.  This provides the generic core for simple
111da177e4SLinus Torvalds  *  IO-based SSP applications and allows easy port setup for DMA access.
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  *  Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/kernel.h>
181da177e4SLinus Torvalds #include <linux/sched.h>
191da177e4SLinus Torvalds #include <linux/slab.h>
201da177e4SLinus Torvalds #include <linux/errno.h>
211da177e4SLinus Torvalds #include <linux/interrupt.h>
221da177e4SLinus Torvalds #include <linux/ioport.h>
231da177e4SLinus Torvalds #include <linux/init.h>
2400431707SArjan van de Ven #include <linux/mutex.h>
2588286450Seric miao #include <linux/clk.h>
2688286450Seric miao #include <linux/err.h>
2788286450Seric miao #include <linux/platform_device.h>
28b5867a5cSAndy Shevchenko #include <linux/pxa2xx_ssp.h>
29fced80c7SRussell King #include <linux/io.h>
30a6e56c28SDaniel Mack #include <linux/of.h>
31a6e56c28SDaniel Mack #include <linux/of_device.h>
3288286450Seric miao 
331da177e4SLinus Torvalds #include <asm/irq.h>
341da177e4SLinus Torvalds 
3588286450Seric miao static DEFINE_MUTEX(ssp_lock);
3688286450Seric miao static LIST_HEAD(ssp_list);
3788286450Seric miao 
pxa_ssp_request(int port,const char * label)38baffe169SHaojian Zhuang struct ssp_device *pxa_ssp_request(int port, const char *label)
3988286450Seric miao {
4088286450Seric miao 	struct ssp_device *ssp = NULL;
4188286450Seric miao 
4288286450Seric miao 	mutex_lock(&ssp_lock);
4388286450Seric miao 
4488286450Seric miao 	list_for_each_entry(ssp, &ssp_list, node) {
4588286450Seric miao 		if (ssp->port_id == port && ssp->use_count == 0) {
4688286450Seric miao 			ssp->use_count++;
4788286450Seric miao 			ssp->label = label;
4888286450Seric miao 			break;
4988286450Seric miao 		}
5088286450Seric miao 	}
5188286450Seric miao 
5288286450Seric miao 	mutex_unlock(&ssp_lock);
5388286450Seric miao 
54a4aff223SGuennadi Liakhovetski 	if (&ssp->node == &ssp_list)
5588286450Seric miao 		return NULL;
5688286450Seric miao 
5788286450Seric miao 	return ssp;
5888286450Seric miao }
59baffe169SHaojian Zhuang EXPORT_SYMBOL(pxa_ssp_request);
6088286450Seric miao 
pxa_ssp_request_of(const struct device_node * of_node,const char * label)616446221cSDaniel Mack struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
626446221cSDaniel Mack 				      const char *label)
636446221cSDaniel Mack {
646446221cSDaniel Mack 	struct ssp_device *ssp = NULL;
656446221cSDaniel Mack 
666446221cSDaniel Mack 	mutex_lock(&ssp_lock);
676446221cSDaniel Mack 
686446221cSDaniel Mack 	list_for_each_entry(ssp, &ssp_list, node) {
696446221cSDaniel Mack 		if (ssp->of_node == of_node && ssp->use_count == 0) {
706446221cSDaniel Mack 			ssp->use_count++;
716446221cSDaniel Mack 			ssp->label = label;
726446221cSDaniel Mack 			break;
736446221cSDaniel Mack 		}
746446221cSDaniel Mack 	}
756446221cSDaniel Mack 
766446221cSDaniel Mack 	mutex_unlock(&ssp_lock);
776446221cSDaniel Mack 
786446221cSDaniel Mack 	if (&ssp->node == &ssp_list)
796446221cSDaniel Mack 		return NULL;
806446221cSDaniel Mack 
816446221cSDaniel Mack 	return ssp;
826446221cSDaniel Mack }
836446221cSDaniel Mack EXPORT_SYMBOL(pxa_ssp_request_of);
846446221cSDaniel Mack 
pxa_ssp_free(struct ssp_device * ssp)85baffe169SHaojian Zhuang void pxa_ssp_free(struct ssp_device *ssp)
8688286450Seric miao {
8788286450Seric miao 	mutex_lock(&ssp_lock);
8888286450Seric miao 	if (ssp->use_count) {
8988286450Seric miao 		ssp->use_count--;
9088286450Seric miao 		ssp->label = NULL;
9188286450Seric miao 	} else
924f3d9577SAndy Shevchenko 		dev_err(ssp->dev, "device already free\n");
9388286450Seric miao 	mutex_unlock(&ssp_lock);
9488286450Seric miao }
95baffe169SHaojian Zhuang EXPORT_SYMBOL(pxa_ssp_free);
9688286450Seric miao 
97a6e56c28SDaniel Mack #ifdef CONFIG_OF
98a6e56c28SDaniel Mack static const struct of_device_id pxa_ssp_of_ids[] = {
99a6e56c28SDaniel Mack 	{ .compatible = "mrvl,pxa25x-ssp",	.data = (void *) PXA25x_SSP },
100a6e56c28SDaniel Mack 	{ .compatible = "mvrl,pxa25x-nssp",	.data = (void *) PXA25x_NSSP },
101a6e56c28SDaniel Mack 	{ .compatible = "mrvl,pxa27x-ssp",	.data = (void *) PXA27x_SSP },
102a6e56c28SDaniel Mack 	{ .compatible = "mrvl,pxa3xx-ssp",	.data = (void *) PXA3xx_SSP },
103a6e56c28SDaniel Mack 	{ .compatible = "mvrl,pxa168-ssp",	.data = (void *) PXA168_SSP },
104a6e56c28SDaniel Mack 	{ .compatible = "mrvl,pxa910-ssp",	.data = (void *) PXA910_SSP },
105a6e56c28SDaniel Mack 	{ .compatible = "mrvl,ce4100-ssp",	.data = (void *) CE4100_SSP },
106a6e56c28SDaniel Mack 	{ },
107a6e56c28SDaniel Mack };
108a6e56c28SDaniel Mack MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
109a6e56c28SDaniel Mack #endif
110a6e56c28SDaniel Mack 
pxa_ssp_probe(struct platform_device * pdev)111351a102dSGreg Kroah-Hartman static int pxa_ssp_probe(struct platform_device *pdev)
11288286450Seric miao {
11388286450Seric miao 	struct resource *res;
11488286450Seric miao 	struct ssp_device *ssp;
115970d8a71SDaniel Mack 	struct device *dev = &pdev->dev;
11688286450Seric miao 
1171c459de1SDaniel Mack 	ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL);
11864be2814SDaniel Mack 	if (ssp == NULL)
11988286450Seric miao 		return -ENOMEM;
12064be2814SDaniel Mack 
1214f3d9577SAndy Shevchenko 	ssp->dev = dev;
12288286450Seric miao 
1231c459de1SDaniel Mack 	ssp->clk = devm_clk_get(dev, NULL);
1241c459de1SDaniel Mack 	if (IS_ERR(ssp->clk))
1251c459de1SDaniel Mack 		return PTR_ERR(ssp->clk);
12688286450Seric miao 
12788286450Seric miao 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
12888286450Seric miao 	if (res == NULL) {
129970d8a71SDaniel Mack 		dev_err(dev, "no memory resource defined\n");
1301c459de1SDaniel Mack 		return -ENODEV;
13188286450Seric miao 	}
13288286450Seric miao 
1331c459de1SDaniel Mack 	res = devm_request_mem_region(dev, res->start, resource_size(res),
13488286450Seric miao 				      pdev->name);
13588286450Seric miao 	if (res == NULL) {
136970d8a71SDaniel Mack 		dev_err(dev, "failed to request memory resource\n");
1371c459de1SDaniel Mack 		return -EBUSY;
13888286450Seric miao 	}
13988286450Seric miao 
14088286450Seric miao 	ssp->phys_base = res->start;
14188286450Seric miao 
1421c459de1SDaniel Mack 	ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res));
14388286450Seric miao 	if (ssp->mmio_base == NULL) {
144970d8a71SDaniel Mack 		dev_err(dev, "failed to ioremap() registers\n");
1451c459de1SDaniel Mack 		return -ENODEV;
14688286450Seric miao 	}
14788286450Seric miao 
14888286450Seric miao 	ssp->irq = platform_get_irq(pdev, 0);
1491d9ae5a1Szhang songyi 	if (ssp->irq < 0)
1501c459de1SDaniel Mack 		return -ENODEV;
15188286450Seric miao 
152a6e56c28SDaniel Mack 	if (dev->of_node) {
153a6e56c28SDaniel Mack 		const struct of_device_id *id =
154a6e56c28SDaniel Mack 			of_match_device(of_match_ptr(pxa_ssp_of_ids), dev);
1556db359b5SDuje Mihanović 		ssp->type = (uintptr_t) id->data;
156a6e56c28SDaniel Mack 	} else {
157a6e56c28SDaniel Mack 		const struct platform_device_id *id =
158a6e56c28SDaniel Mack 			platform_get_device_id(pdev);
1596db359b5SDuje Mihanović 		ssp->type = id->driver_data;
160a6e56c28SDaniel Mack 
16188286450Seric miao 		/* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
16288286450Seric miao 		 * starts from 0, do a translation here
16388286450Seric miao 		 */
16488286450Seric miao 		ssp->port_id = pdev->id + 1;
165a6e56c28SDaniel Mack 	}
166a6e56c28SDaniel Mack 
16788286450Seric miao 	ssp->use_count = 0;
1686446221cSDaniel Mack 	ssp->of_node = dev->of_node;
16988286450Seric miao 
17088286450Seric miao 	mutex_lock(&ssp_lock);
17188286450Seric miao 	list_add(&ssp->node, &ssp_list);
17288286450Seric miao 	mutex_unlock(&ssp_lock);
17388286450Seric miao 
17488286450Seric miao 	platform_set_drvdata(pdev, ssp);
17588286450Seric miao 
1761c459de1SDaniel Mack 	return 0;
17788286450Seric miao }
17888286450Seric miao 
pxa_ssp_remove(struct platform_device * pdev)179e77e6e3eSUwe Kleine-König static void pxa_ssp_remove(struct platform_device *pdev)
18088286450Seric miao {
181a4d18465SUwe Kleine-König 	struct ssp_device *ssp = platform_get_drvdata(pdev);
18288286450Seric miao 
18388286450Seric miao 	mutex_lock(&ssp_lock);
18488286450Seric miao 	list_del(&ssp->node);
18588286450Seric miao 	mutex_unlock(&ssp_lock);
18688286450Seric miao }
18788286450Seric miao 
1886427d450SEric Miao static const struct platform_device_id ssp_id_table[] = {
1896427d450SEric Miao 	{ "pxa25x-ssp",		PXA25x_SSP },
1906427d450SEric Miao 	{ "pxa25x-nssp",	PXA25x_NSSP },
1916427d450SEric Miao 	{ "pxa27x-ssp",		PXA27x_SSP },
1926f0243a1SDaniel Mack 	{ "pxa3xx-ssp",		PXA3xx_SSP },
1937e499228SHaojian Zhuang 	{ "pxa168-ssp",		PXA168_SSP },
19460172215SQiao Zhou 	{ "pxa910-ssp",		PXA910_SSP },
1956427d450SEric Miao 	{ },
19688286450Seric miao };
19788286450Seric miao 
198baffe169SHaojian Zhuang static struct platform_driver pxa_ssp_driver = {
199baffe169SHaojian Zhuang 	.probe		= pxa_ssp_probe,
200*511c06e3SUwe Kleine-König 	.remove		= pxa_ssp_remove,
20188286450Seric miao 	.driver		= {
2026427d450SEric Miao 		.name		= "pxa2xx-ssp",
203a6e56c28SDaniel Mack 		.of_match_table	= of_match_ptr(pxa_ssp_of_ids),
20488286450Seric miao 	},
2056427d450SEric Miao 	.id_table	= ssp_id_table,
20688286450Seric miao };
20788286450Seric miao 
pxa_ssp_init(void)20888286450Seric miao static int __init pxa_ssp_init(void)
20988286450Seric miao {
210baffe169SHaojian Zhuang 	return platform_driver_register(&pxa_ssp_driver);
21188286450Seric miao }
21288286450Seric miao 
pxa_ssp_exit(void)21388286450Seric miao static void __exit pxa_ssp_exit(void)
21488286450Seric miao {
215baffe169SHaojian Zhuang 	platform_driver_unregister(&pxa_ssp_driver);
21688286450Seric miao }
21788286450Seric miao 
218cae05541SRussell King arch_initcall(pxa_ssp_init);
21988286450Seric miao module_exit(pxa_ssp_exit);
22088286450Seric miao 
2211da177e4SLinus Torvalds MODULE_DESCRIPTION("PXA SSP driver");
2221da177e4SLinus Torvalds MODULE_AUTHOR("Liam Girdwood");
2231da177e4SLinus Torvalds MODULE_LICENSE("GPL");
224