linux/arch/arm/plat-pxa/ssp.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-pxa/ssp.c
   3 *
   4 *  based on linux/arch/arm/mach-sa1100/ssp.c by Russell King
   5 *
   6 *  Copyright (C) 2003 Russell King.
   7 *  Copyright (C) 2003 Wolfson Microelectronics PLC
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 *
  13 *  PXA2xx SSP driver.  This provides the generic core for simple
  14 *  IO-based SSP applications and allows easy port setup for DMA access.
  15 *
  16 *  Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/kernel.h>
  21#include <linux/sched.h>
  22#include <linux/slab.h>
  23#include <linux/errno.h>
  24#include <linux/interrupt.h>
  25#include <linux/ioport.h>
  26#include <linux/init.h>
  27#include <linux/mutex.h>
  28#include <linux/clk.h>
  29#include <linux/err.h>
  30#include <linux/platform_device.h>
  31#include <linux/spi/pxa2xx_spi.h>
  32#include <linux/io.h>
  33
  34#include <asm/irq.h>
  35#include <mach/hardware.h>
  36
  37static DEFINE_MUTEX(ssp_lock);
  38static LIST_HEAD(ssp_list);
  39
  40struct ssp_device *pxa_ssp_request(int port, const char *label)
  41{
  42        struct ssp_device *ssp = NULL;
  43
  44        mutex_lock(&ssp_lock);
  45
  46        list_for_each_entry(ssp, &ssp_list, node) {
  47                if (ssp->port_id == port && ssp->use_count == 0) {
  48                        ssp->use_count++;
  49                        ssp->label = label;
  50                        break;
  51                }
  52        }
  53
  54        mutex_unlock(&ssp_lock);
  55
  56        if (&ssp->node == &ssp_list)
  57                return NULL;
  58
  59        return ssp;
  60}
  61EXPORT_SYMBOL(pxa_ssp_request);
  62
  63void pxa_ssp_free(struct ssp_device *ssp)
  64{
  65        mutex_lock(&ssp_lock);
  66        if (ssp->use_count) {
  67                ssp->use_count--;
  68                ssp->label = NULL;
  69        } else
  70                dev_err(&ssp->pdev->dev, "device already free\n");
  71        mutex_unlock(&ssp_lock);
  72}
  73EXPORT_SYMBOL(pxa_ssp_free);
  74
  75static int pxa_ssp_probe(struct platform_device *pdev)
  76{
  77        const struct platform_device_id *id = platform_get_device_id(pdev);
  78        struct resource *res;
  79        struct ssp_device *ssp;
  80        int ret = 0;
  81
  82        ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL);
  83        if (ssp == NULL) {
  84                dev_err(&pdev->dev, "failed to allocate memory");
  85                return -ENOMEM;
  86        }
  87        ssp->pdev = pdev;
  88
  89        ssp->clk = clk_get(&pdev->dev, NULL);
  90        if (IS_ERR(ssp->clk)) {
  91                ret = PTR_ERR(ssp->clk);
  92                goto err_free;
  93        }
  94
  95        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
  96        if (res == NULL) {
  97                dev_err(&pdev->dev, "no SSP RX DRCMR defined\n");
  98                ret = -ENODEV;
  99                goto err_free_clk;
 100        }
 101        ssp->drcmr_rx = res->start;
 102
 103        res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 104        if (res == NULL) {
 105                dev_err(&pdev->dev, "no SSP TX DRCMR defined\n");
 106                ret = -ENODEV;
 107                goto err_free_clk;
 108        }
 109        ssp->drcmr_tx = res->start;
 110
 111        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 112        if (res == NULL) {
 113                dev_err(&pdev->dev, "no memory resource defined\n");
 114                ret = -ENODEV;
 115                goto err_free_clk;
 116        }
 117
 118        res = request_mem_region(res->start, resource_size(res),
 119                        pdev->name);
 120        if (res == NULL) {
 121                dev_err(&pdev->dev, "failed to request memory resource\n");
 122                ret = -EBUSY;
 123                goto err_free_clk;
 124        }
 125
 126        ssp->phys_base = res->start;
 127
 128        ssp->mmio_base = ioremap(res->start, resource_size(res));
 129        if (ssp->mmio_base == NULL) {
 130                dev_err(&pdev->dev, "failed to ioremap() registers\n");
 131                ret = -ENODEV;
 132                goto err_free_mem;
 133        }
 134
 135        ssp->irq = platform_get_irq(pdev, 0);
 136        if (ssp->irq < 0) {
 137                dev_err(&pdev->dev, "no IRQ resource defined\n");
 138                ret = -ENODEV;
 139                goto err_free_io;
 140        }
 141
 142        /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
 143         * starts from 0, do a translation here
 144         */
 145        ssp->port_id = pdev->id + 1;
 146        ssp->use_count = 0;
 147        ssp->type = (int)id->driver_data;
 148
 149        mutex_lock(&ssp_lock);
 150        list_add(&ssp->node, &ssp_list);
 151        mutex_unlock(&ssp_lock);
 152
 153        platform_set_drvdata(pdev, ssp);
 154        return 0;
 155
 156err_free_io:
 157        iounmap(ssp->mmio_base);
 158err_free_mem:
 159        release_mem_region(res->start, resource_size(res));
 160err_free_clk:
 161        clk_put(ssp->clk);
 162err_free:
 163        kfree(ssp);
 164        return ret;
 165}
 166
 167static int pxa_ssp_remove(struct platform_device *pdev)
 168{
 169        struct resource *res;
 170        struct ssp_device *ssp;
 171
 172        ssp = platform_get_drvdata(pdev);
 173        if (ssp == NULL)
 174                return -ENODEV;
 175
 176        iounmap(ssp->mmio_base);
 177
 178        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 179        release_mem_region(res->start, resource_size(res));
 180
 181        clk_put(ssp->clk);
 182
 183        mutex_lock(&ssp_lock);
 184        list_del(&ssp->node);
 185        mutex_unlock(&ssp_lock);
 186
 187        kfree(ssp);
 188        return 0;
 189}
 190
 191static const struct platform_device_id ssp_id_table[] = {
 192        { "pxa25x-ssp",         PXA25x_SSP },
 193        { "pxa25x-nssp",        PXA25x_NSSP },
 194        { "pxa27x-ssp",         PXA27x_SSP },
 195        { "pxa168-ssp",         PXA168_SSP },
 196        { "pxa910-ssp",         PXA910_SSP },
 197        { },
 198};
 199
 200static struct platform_driver pxa_ssp_driver = {
 201        .probe          = pxa_ssp_probe,
 202        .remove         = pxa_ssp_remove,
 203        .driver         = {
 204                .owner  = THIS_MODULE,
 205                .name   = "pxa2xx-ssp",
 206        },
 207        .id_table       = ssp_id_table,
 208};
 209
 210static int __init pxa_ssp_init(void)
 211{
 212        return platform_driver_register(&pxa_ssp_driver);
 213}
 214
 215static void __exit pxa_ssp_exit(void)
 216{
 217        platform_driver_unregister(&pxa_ssp_driver);
 218}
 219
 220arch_initcall(pxa_ssp_init);
 221module_exit(pxa_ssp_exit);
 222
 223MODULE_DESCRIPTION("PXA SSP driver");
 224MODULE_AUTHOR("Liam Girdwood");
 225MODULE_LICENSE("GPL");
 226