aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/atmel-ssc.c
diff options
context:
space:
mode:
authorHans-Christian Egtvedt <hcegtvedt@atmel.com>2007-10-17 02:26:11 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-17 11:42:47 -0400
commiteb1f2930609bb01bb3a970f145b5004e5163742a (patch)
treec22f195dbc5f58bba7cd82c4a09136b2a553086e /drivers/misc/atmel-ssc.c
parent94f582f82a85a941678c80c60a3baa8187c74217 (diff)
Driver for the Atmel on-chip SSC on AT32AP and AT91
The Synchronous Serial Controller (SSC) on Atmel microprocessors are capable of tranceiving many frame based protocols, like I2S. Tested on the AT32AP7000/ATSTK1000. This driver is used in the ALSA sound driver for the AT73C213 external DAC on the ATSTK1000 development board for AVR32. This sound driver will be submitted soon. Hardware documentation can be found in the AT32AP7000 data sheet, which can be downloaded from http://www.atmel.com/dyn/products/datasheets.asp?family_id=682 [akpm@linux-foundation.org: init spinlock at compile time] Signed-off-by: Hans-Christian Egtvedt <hcegtvedt@atmel.com> Acked-by: Haavard Skinnemoen <hskinnemoen@atmel.com> Cc: David Brownell <david-b@pacbell.net> Cc: Andrew Victor <andrew@sanpeople.com> Cc: Patrice Vilchez <patrice.vilchez@rfo.atmel.com> Cc: Nicolas Ferre <nicolas.ferre@rfo.atmel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc/atmel-ssc.c')
-rw-r--r--drivers/misc/atmel-ssc.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
new file mode 100644
index 000000000000..058ccac700d0
--- /dev/null
+++ b/drivers/misc/atmel-ssc.c
@@ -0,0 +1,174 @@
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/list.h>
17#include <linux/spinlock.h>
18#include <linux/atmel-ssc.h>
19
20/* Serialize access to ssc_list and user count */
21static DEFINE_SPINLOCK(user_lock);
22static LIST_HEAD(ssc_list);
23
24struct 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 dev_dbg(&ssc->pdev->dev, "could not find requested device\n");
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}
55EXPORT_SYMBOL(ssc_request);
56
57void 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}
68EXPORT_SYMBOL(ssc_free);
69
70static 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, regs->end - regs->start + 1);
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
129out_unmap:
130 iounmap(ssc->regs);
131out_clk:
132 clk_put(ssc->clk);
133out_free:
134 kfree(ssc);
135out:
136 return retval;
137}
138
139static 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
153static struct platform_driver ssc_driver = {
154 .remove = __devexit_p(ssc_remove),
155 .driver = {
156 .name = "ssc",
157 },
158};
159
160static int __init ssc_init(void)
161{
162 return platform_driver_probe(&ssc_driver, ssc_probe);
163}
164module_init(ssc_init);
165
166static void __exit ssc_exit(void)
167{
168 platform_driver_unregister(&ssc_driver);
169}
170module_exit(ssc_exit);
171
172MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
173MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91");
174MODULE_LICENSE("GPL");