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