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 35 static DEFINE_MUTEX(ssp_lock); 36 static LIST_HEAD(ssp_list); 37 38 struct 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 } 59 EXPORT_SYMBOL(pxa_ssp_request); 60 61 struct 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 } 83 EXPORT_SYMBOL(pxa_ssp_request_of); 84 85 void 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 } 95 EXPORT_SYMBOL(pxa_ssp_free); 96 97 #ifdef CONFIG_OF 98 static 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 }; 108 MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); 109 #endif 110 111 static 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 181 static 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 196 static 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 206 static 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 216 static int __init pxa_ssp_init(void) 217 { 218 return platform_driver_register(&pxa_ssp_driver); 219 } 220 221 static void __exit pxa_ssp_exit(void) 222 { 223 platform_driver_unregister(&pxa_ssp_driver); 224 } 225 226 arch_initcall(pxa_ssp_init); 227 module_exit(pxa_ssp_exit); 228 229 MODULE_DESCRIPTION("PXA SSP driver"); 230 MODULE_AUTHOR("Liam Girdwood"); 231 MODULE_LICENSE("GPL"); 232