Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  linux/arch/arm/mach-pxa/ssp.c
0004  *
0005  *  based on linux/arch/arm/mach-sa1100/ssp.c by Russell King
0006  *
0007  *  Copyright (C) 2003 Russell King.
0008  *  Copyright (C) 2003 Wolfson Microelectronics PLC
0009  *
0010  *  PXA2xx SSP driver.  This provides the generic core for simple
0011  *  IO-based SSP applications and allows easy port setup for DMA access.
0012  *
0013  *  Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
0014  */
0015 
0016 #include <linux/module.h>
0017 #include <linux/kernel.h>
0018 #include <linux/sched.h>
0019 #include <linux/slab.h>
0020 #include <linux/errno.h>
0021 #include <linux/interrupt.h>
0022 #include <linux/ioport.h>
0023 #include <linux/init.h>
0024 #include <linux/mutex.h>
0025 #include <linux/clk.h>
0026 #include <linux/err.h>
0027 #include <linux/platform_device.h>
0028 #include <linux/spi/pxa2xx_spi.h>
0029 #include <linux/io.h>
0030 #include <linux/of.h>
0031 #include <linux/of_device.h>
0032 
0033 #include <asm/irq.h>
0034 
0035 static DEFINE_MUTEX(ssp_lock);
0036 static LIST_HEAD(ssp_list);
0037 
0038 struct ssp_device *pxa_ssp_request(int port, const char *label)
0039 {
0040     struct ssp_device *ssp = NULL;
0041 
0042     mutex_lock(&ssp_lock);
0043 
0044     list_for_each_entry(ssp, &ssp_list, node) {
0045         if (ssp->port_id == port && ssp->use_count == 0) {
0046             ssp->use_count++;
0047             ssp->label = label;
0048             break;
0049         }
0050     }
0051 
0052     mutex_unlock(&ssp_lock);
0053 
0054     if (&ssp->node == &ssp_list)
0055         return NULL;
0056 
0057     return ssp;
0058 }
0059 EXPORT_SYMBOL(pxa_ssp_request);
0060 
0061 struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
0062                       const char *label)
0063 {
0064     struct ssp_device *ssp = NULL;
0065 
0066     mutex_lock(&ssp_lock);
0067 
0068     list_for_each_entry(ssp, &ssp_list, node) {
0069         if (ssp->of_node == of_node && ssp->use_count == 0) {
0070             ssp->use_count++;
0071             ssp->label = label;
0072             break;
0073         }
0074     }
0075 
0076     mutex_unlock(&ssp_lock);
0077 
0078     if (&ssp->node == &ssp_list)
0079         return NULL;
0080 
0081     return ssp;
0082 }
0083 EXPORT_SYMBOL(pxa_ssp_request_of);
0084 
0085 void pxa_ssp_free(struct ssp_device *ssp)
0086 {
0087     mutex_lock(&ssp_lock);
0088     if (ssp->use_count) {
0089         ssp->use_count--;
0090         ssp->label = NULL;
0091     } else
0092         dev_err(ssp->dev, "device already free\n");
0093     mutex_unlock(&ssp_lock);
0094 }
0095 EXPORT_SYMBOL(pxa_ssp_free);
0096 
0097 #ifdef CONFIG_OF
0098 static const struct of_device_id pxa_ssp_of_ids[] = {
0099     { .compatible = "mrvl,pxa25x-ssp",  .data = (void *) PXA25x_SSP },
0100     { .compatible = "mvrl,pxa25x-nssp", .data = (void *) PXA25x_NSSP },
0101     { .compatible = "mrvl,pxa27x-ssp",  .data = (void *) PXA27x_SSP },
0102     { .compatible = "mrvl,pxa3xx-ssp",  .data = (void *) PXA3xx_SSP },
0103     { .compatible = "mvrl,pxa168-ssp",  .data = (void *) PXA168_SSP },
0104     { .compatible = "mrvl,pxa910-ssp",  .data = (void *) PXA910_SSP },
0105     { .compatible = "mrvl,ce4100-ssp",  .data = (void *) CE4100_SSP },
0106     { },
0107 };
0108 MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
0109 #endif
0110 
0111 static int pxa_ssp_probe(struct platform_device *pdev)
0112 {
0113     struct resource *res;
0114     struct ssp_device *ssp;
0115     struct device *dev = &pdev->dev;
0116 
0117     ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL);
0118     if (ssp == NULL)
0119         return -ENOMEM;
0120 
0121     ssp->dev = dev;
0122 
0123     ssp->clk = devm_clk_get(dev, NULL);
0124     if (IS_ERR(ssp->clk))
0125         return PTR_ERR(ssp->clk);
0126 
0127     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0128     if (res == NULL) {
0129         dev_err(dev, "no memory resource defined\n");
0130         return -ENODEV;
0131     }
0132 
0133     res = devm_request_mem_region(dev, res->start, resource_size(res),
0134                       pdev->name);
0135     if (res == NULL) {
0136         dev_err(dev, "failed to request memory resource\n");
0137         return -EBUSY;
0138     }
0139 
0140     ssp->phys_base = res->start;
0141 
0142     ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res));
0143     if (ssp->mmio_base == NULL) {
0144         dev_err(dev, "failed to ioremap() registers\n");
0145         return -ENODEV;
0146     }
0147 
0148     ssp->irq = platform_get_irq(pdev, 0);
0149     if (ssp->irq < 0) {
0150         dev_err(dev, "no IRQ resource defined\n");
0151         return -ENODEV;
0152     }
0153 
0154     if (dev->of_node) {
0155         const struct of_device_id *id =
0156             of_match_device(of_match_ptr(pxa_ssp_of_ids), dev);
0157         ssp->type = (int) id->data;
0158     } else {
0159         const struct platform_device_id *id =
0160             platform_get_device_id(pdev);
0161         ssp->type = (int) id->driver_data;
0162 
0163         /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
0164          * starts from 0, do a translation here
0165          */
0166         ssp->port_id = pdev->id + 1;
0167     }
0168 
0169     ssp->use_count = 0;
0170     ssp->of_node = dev->of_node;
0171 
0172     mutex_lock(&ssp_lock);
0173     list_add(&ssp->node, &ssp_list);
0174     mutex_unlock(&ssp_lock);
0175 
0176     platform_set_drvdata(pdev, ssp);
0177 
0178     return 0;
0179 }
0180 
0181 static int pxa_ssp_remove(struct platform_device *pdev)
0182 {
0183     struct ssp_device *ssp;
0184 
0185     ssp = platform_get_drvdata(pdev);
0186     if (ssp == NULL)
0187         return -ENODEV;
0188 
0189     mutex_lock(&ssp_lock);
0190     list_del(&ssp->node);
0191     mutex_unlock(&ssp_lock);
0192 
0193     return 0;
0194 }
0195 
0196 static const struct platform_device_id ssp_id_table[] = {
0197     { "pxa25x-ssp",     PXA25x_SSP },
0198     { "pxa25x-nssp",    PXA25x_NSSP },
0199     { "pxa27x-ssp",     PXA27x_SSP },
0200     { "pxa3xx-ssp",     PXA3xx_SSP },
0201     { "pxa168-ssp",     PXA168_SSP },
0202     { "pxa910-ssp",     PXA910_SSP },
0203     { },
0204 };
0205 
0206 static struct platform_driver pxa_ssp_driver = {
0207     .probe      = pxa_ssp_probe,
0208     .remove     = pxa_ssp_remove,
0209     .driver     = {
0210         .name       = "pxa2xx-ssp",
0211         .of_match_table = of_match_ptr(pxa_ssp_of_ids),
0212     },
0213     .id_table   = ssp_id_table,
0214 };
0215 
0216 static int __init pxa_ssp_init(void)
0217 {
0218     return platform_driver_register(&pxa_ssp_driver);
0219 }
0220 
0221 static void __exit pxa_ssp_exit(void)
0222 {
0223     platform_driver_unregister(&pxa_ssp_driver);
0224 }
0225 
0226 arch_initcall(pxa_ssp_init);
0227 module_exit(pxa_ssp_exit);
0228 
0229 MODULE_DESCRIPTION("PXA SSP driver");
0230 MODULE_AUTHOR("Liam Girdwood");
0231 MODULE_LICENSE("GPL");