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