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