linux/arch/arm/plat-pxa/ssp.c
<<
>>
Prefs
   1// 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
  35static DEFINE_MUTEX(ssp_lock);
  36static LIST_HEAD(ssp_list);
  37
  38struct ssp_device *pxa_ssp_request(int port, const char *label)
  39{
  40        struct ssp_device *ssp = NULL;
  41
  42        mutex_lock(&ssp_lock);
  43
  44        list_for_each_entry(ssp, &ssp_list, node) {
  45                if (ssp->port_id == port && ssp->use_count == 0) {
  46                        ssp->use_count++;
  47                        ssp->label = label;
  48                        break;
  49                }
  50        }
  51
  52        mutex_unlock(&ssp_lock);
  53
  54        if (&ssp->node == &ssp_list)
  55                return NULL;
  56
  57        return ssp;
  58}
  59EXPORT_SYMBOL(pxa_ssp_request);
  60
  61struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
  62                                      const char *label)
  63{
  64        struct ssp_device *ssp = NULL;
  65
  66        mutex_lock(&ssp_lock);
  67
  68        list_for_each_entry(ssp, &ssp_list, node) {
  69                if (ssp->of_node == of_node && ssp->use_count == 0) {
  70                        ssp->use_count++;
  71                        ssp->label = label;
  72                        break;
  73                }
  74        }
  75
  76        mutex_unlock(&ssp_lock);
  77
  78        if (&ssp->node == &ssp_list)
  79                return NULL;
  80
  81        return ssp;
  82}
  83EXPORT_SYMBOL(pxa_ssp_request_of);
  84
  85void pxa_ssp_free(struct ssp_device *ssp)
  86{
  87        mutex_lock(&ssp_lock);
  88        if (ssp->use_count) {
  89                ssp->use_count--;
  90                ssp->label = NULL;
  91        } else
  92                dev_err(ssp->dev, "device already free\n");
  93        mutex_unlock(&ssp_lock);
  94}
  95EXPORT_SYMBOL(pxa_ssp_free);
  96
  97#ifdef CONFIG_OF
  98static const struct of_device_id pxa_ssp_of_ids[] = {
  99        { .compatible = "mrvl,pxa25x-ssp",      .data = (void *) PXA25x_SSP },
 100        { .compatible = "mvrl,pxa25x-nssp",     .data = (void *) PXA25x_NSSP },
 101        { .compatible = "mrvl,pxa27x-ssp",      .data = (void *) PXA27x_SSP },
 102        { .compatible = "mrvl,pxa3xx-ssp",      .data = (void *) PXA3xx_SSP },
 103        { .compatible = "mvrl,pxa168-ssp",      .data = (void *) PXA168_SSP },
 104        { .compatible = "mrvl,pxa910-ssp",      .data = (void *) PXA910_SSP },
 105        { .compatible = "mrvl,ce4100-ssp",      .data = (void *) CE4100_SSP },
 106        { },
 107};
 108MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
 109#endif
 110
 111static int pxa_ssp_probe(struct platform_device *pdev)
 112{
 113        struct resource *res;
 114        struct ssp_device *ssp;
 115        struct device *dev = &pdev->dev;
 116
 117        ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL);
 118        if (ssp == NULL)
 119                return -ENOMEM;
 120
 121        ssp->dev = dev;
 122
 123        ssp->clk = devm_clk_get(dev, NULL);
 124        if (IS_ERR(ssp->clk))
 125                return PTR_ERR(ssp->clk);
 126
 127        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 128        if (res == NULL) {
 129                dev_err(dev, "no memory resource defined\n");
 130                return -ENODEV;
 131        }
 132
 133        res = devm_request_mem_region(dev, res->start, resource_size(res),
 134                                      pdev->name);
 135        if (res == NULL) {
 136                dev_err(dev, "failed to request memory resource\n");
 137                return -EBUSY;
 138        }
 139
 140        ssp->phys_base = res->start;
 141
 142        ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res));
 143        if (ssp->mmio_base == NULL) {
 144                dev_err(dev, "failed to ioremap() registers\n");
 145                return -ENODEV;
 146        }
 147
 148        ssp->irq = platform_get_irq(pdev, 0);
 149        if (ssp->irq < 0) {
 150                dev_err(dev, "no IRQ resource defined\n");
 151                return -ENODEV;
 152        }
 153
 154        if (dev->of_node) {
 155                const struct of_device_id *id =
 156                        of_match_device(of_match_ptr(pxa_ssp_of_ids), dev);
 157                ssp->type = (int) id->data;
 158        } else {
 159                const struct platform_device_id *id =
 160                        platform_get_device_id(pdev);
 161                ssp->type = (int) id->driver_data;
 162
 163                /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
 164                 * starts from 0, do a translation here
 165                 */
 166                ssp->port_id = pdev->id + 1;
 167        }
 168
 169        ssp->use_count = 0;
 170        ssp->of_node = dev->of_node;
 171
 172        mutex_lock(&ssp_lock);
 173        list_add(&ssp->node, &ssp_list);
 174        mutex_unlock(&ssp_lock);
 175
 176        platform_set_drvdata(pdev, ssp);
 177
 178        return 0;
 179}
 180
 181static int pxa_ssp_remove(struct platform_device *pdev)
 182{
 183        struct ssp_device *ssp;
 184
 185        ssp = platform_get_drvdata(pdev);
 186        if (ssp == NULL)
 187                return -ENODEV;
 188
 189        mutex_lock(&ssp_lock);
 190        list_del(&ssp->node);
 191        mutex_unlock(&ssp_lock);
 192
 193        return 0;
 194}
 195
 196static const struct platform_device_id ssp_id_table[] = {
 197        { "pxa25x-ssp",         PXA25x_SSP },
 198        { "pxa25x-nssp",        PXA25x_NSSP },
 199        { "pxa27x-ssp",         PXA27x_SSP },
 200        { "pxa3xx-ssp",         PXA3xx_SSP },
 201        { "pxa168-ssp",         PXA168_SSP },
 202        { "pxa910-ssp",         PXA910_SSP },
 203        { },
 204};
 205
 206static struct platform_driver pxa_ssp_driver = {
 207        .probe          = pxa_ssp_probe,
 208        .remove         = pxa_ssp_remove,
 209        .driver         = {
 210                .name           = "pxa2xx-ssp",
 211                .of_match_table = of_match_ptr(pxa_ssp_of_ids),
 212        },
 213        .id_table       = ssp_id_table,
 214};
 215
 216static int __init pxa_ssp_init(void)
 217{
 218        return platform_driver_register(&pxa_ssp_driver);
 219}
 220
 221static void __exit pxa_ssp_exit(void)
 222{
 223        platform_driver_unregister(&pxa_ssp_driver);
 224}
 225
 226arch_initcall(pxa_ssp_init);
 227module_exit(pxa_ssp_exit);
 228
 229MODULE_DESCRIPTION("PXA SSP driver");
 230MODULE_AUTHOR("Liam Girdwood");
 231MODULE_LICENSE("GPL");
 232