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/io.h> 32 33#include <asm/irq.h> 34#include <mach/hardware.h> 35#include <plat/ssp.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 __devinit 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 __devexit 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 { }, 197}; 198 199static struct platform_driver pxa_ssp_driver = { 200 .probe = pxa_ssp_probe, 201 .remove = __devexit_p(pxa_ssp_remove), 202 .driver = { 203 .owner = THIS_MODULE, 204 .name = "pxa2xx-ssp", 205 }, 206 .id_table = ssp_id_table, 207}; 208 209static int __init pxa_ssp_init(void) 210{ 211 return platform_driver_register(&pxa_ssp_driver); 212} 213 214static void __exit pxa_ssp_exit(void) 215{ 216 platform_driver_unregister(&pxa_ssp_driver); 217} 218 219arch_initcall(pxa_ssp_init); 220module_exit(pxa_ssp_exit); 221 222MODULE_DESCRIPTION("PXA SSP driver"); 223MODULE_AUTHOR("Liam Girdwood"); 224MODULE_LICENSE("GPL"); 225