1 /* 2 * Atmel SSC driver 3 * 4 * Copyright (C) 2007 Atmel Corporation 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11 #include <linux/platform_device.h> 12 #include <linux/list.h> 13 #include <linux/clk.h> 14 #include <linux/err.h> 15 #include <linux/io.h> 16 #include <linux/spinlock.h> 17 #include <linux/atmel-ssc.h> 18 #include <linux/slab.h> 19 #include <linux/module.h> 20 21 /* Serialize access to ssc_list and user count */ 22 static DEFINE_SPINLOCK(user_lock); 23 static LIST_HEAD(ssc_list); 24 25 struct ssc_device *ssc_request(unsigned int ssc_num) 26 { 27 int ssc_valid = 0; 28 struct ssc_device *ssc; 29 30 spin_lock(&user_lock); 31 list_for_each_entry(ssc, &ssc_list, list) { 32 if (ssc->pdev->id == ssc_num) { 33 ssc_valid = 1; 34 break; 35 } 36 } 37 38 if (!ssc_valid) { 39 spin_unlock(&user_lock); 40 pr_err("ssc: ssc%d platform device is missing\n", ssc_num); 41 return ERR_PTR(-ENODEV); 42 } 43 44 if (ssc->user) { 45 spin_unlock(&user_lock); 46 dev_dbg(&ssc->pdev->dev, "module busy\n"); 47 return ERR_PTR(-EBUSY); 48 } 49 ssc->user++; 50 spin_unlock(&user_lock); 51 52 clk_enable(ssc->clk); 53 54 return ssc; 55 } 56 EXPORT_SYMBOL(ssc_request); 57 58 void ssc_free(struct ssc_device *ssc) 59 { 60 spin_lock(&user_lock); 61 if (ssc->user) { 62 ssc->user--; 63 clk_disable(ssc->clk); 64 } else { 65 dev_dbg(&ssc->pdev->dev, "device already free\n"); 66 } 67 spin_unlock(&user_lock); 68 } 69 EXPORT_SYMBOL(ssc_free); 70 71 static int __init ssc_probe(struct platform_device *pdev) 72 { 73 int retval = 0; 74 struct resource *regs; 75 struct ssc_device *ssc; 76 77 ssc = kzalloc(sizeof(struct ssc_device), GFP_KERNEL); 78 if (!ssc) { 79 dev_dbg(&pdev->dev, "out of memory\n"); 80 retval = -ENOMEM; 81 goto out; 82 } 83 84 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 85 if (!regs) { 86 dev_dbg(&pdev->dev, "no mmio resource defined\n"); 87 retval = -ENXIO; 88 goto out_free; 89 } 90 91 ssc->clk = clk_get(&pdev->dev, "pclk"); 92 if (IS_ERR(ssc->clk)) { 93 dev_dbg(&pdev->dev, "no pclk clock defined\n"); 94 retval = -ENXIO; 95 goto out_free; 96 } 97 98 ssc->pdev = pdev; 99 ssc->regs = ioremap(regs->start, resource_size(regs)); 100 if (!ssc->regs) { 101 dev_dbg(&pdev->dev, "ioremap failed\n"); 102 retval = -EINVAL; 103 goto out_clk; 104 } 105 106 /* disable all interrupts */ 107 clk_enable(ssc->clk); 108 ssc_writel(ssc->regs, IDR, ~0UL); 109 ssc_readl(ssc->regs, SR); 110 clk_disable(ssc->clk); 111 112 ssc->irq = platform_get_irq(pdev, 0); 113 if (!ssc->irq) { 114 dev_dbg(&pdev->dev, "could not get irq\n"); 115 retval = -ENXIO; 116 goto out_unmap; 117 } 118 119 spin_lock(&user_lock); 120 list_add_tail(&ssc->list, &ssc_list); 121 spin_unlock(&user_lock); 122 123 platform_set_drvdata(pdev, ssc); 124 125 dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", 126 ssc->regs, ssc->irq); 127 128 goto out; 129 130 out_unmap: 131 iounmap(ssc->regs); 132 out_clk: 133 clk_put(ssc->clk); 134 out_free: 135 kfree(ssc); 136 out: 137 return retval; 138 } 139 140 static int __devexit ssc_remove(struct platform_device *pdev) 141 { 142 struct ssc_device *ssc = platform_get_drvdata(pdev); 143 144 spin_lock(&user_lock); 145 iounmap(ssc->regs); 146 clk_put(ssc->clk); 147 list_del(&ssc->list); 148 kfree(ssc); 149 spin_unlock(&user_lock); 150 151 return 0; 152 } 153 154 static struct platform_driver ssc_driver = { 155 .remove = __devexit_p(ssc_remove), 156 .driver = { 157 .name = "ssc", 158 .owner = THIS_MODULE, 159 }, 160 }; 161 162 static int __init ssc_init(void) 163 { 164 return platform_driver_probe(&ssc_driver, ssc_probe); 165 } 166 module_init(ssc_init); 167 168 static void __exit ssc_exit(void) 169 { 170 platform_driver_unregister(&ssc_driver); 171 } 172 module_exit(ssc_exit); 173 174 MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); 175 MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91"); 176 MODULE_LICENSE("GPL"); 177 MODULE_ALIAS("platform:ssc"); 178