]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/cbus/cbus.c
Add Nokia CBUS support
[linux-2.6-omap-h63xx.git] / drivers / cbus / cbus.c
1 /*
2  * drivers/cbus/cbus.c
3  *
4  * Support functions for CBUS serial protocol
5  *
6  * Copyright (C) 2004, 2005 Nokia Corporation
7  *
8  * Written by Juha Yrjölä <juha.yrjola@nokia.com>,
9  *            David Weinehall <david.weinehall@nokia.com>, and
10  *            Mikko Ylinen <mikko.k.ylinen@nokia.com>
11  *
12  * This file is subject to the terms and conditions of the GNU General
13  * Public License. See the file "COPYING" in the main directory of this
14  * archive for more details.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include <linux/device.h>
27 #include <linux/init.h>
28 #include <linux/kernel.h>
29 #include <linux/delay.h>
30 #include <linux/spinlock.h>
31
32 #include <asm/arch/gpio.h>
33 #include <asm/arch/board.h>
34
35 #include <asm/io.h>
36
37 #include "cbus.h"
38
39 struct cbus_host *cbus_host = NULL;
40
41 #ifdef CONFIG_ARCH_OMAP1
42 /* We use our own MPUIO functions to get closer to 1MHz bus speed */
43
44 static inline void cbus_set_gpio_direction(u32 base, int mpuio, int is_input)
45 {
46         u16 w;
47
48         mpuio &= 0x0f;
49         w = __raw_readw(base + OMAP_MPUIO_IO_CNTL);
50         if (is_input)
51                 w |= 1 << mpuio;
52         else
53                 w &= ~(1 << mpuio);
54         __raw_writew(w, base + OMAP_MPUIO_IO_CNTL);
55
56 }
57
58 static inline void cbus_set_gpio_dataout(u32 base, int mpuio, int enable)
59 {
60         u16 w;
61
62         mpuio &= 0x0f;
63         w = __raw_readw(base + OMAP_MPUIO_OUTPUT);
64         if (enable)
65                 w |= 1 << mpuio;
66         else
67                 w &= ~(1 << mpuio);
68         __raw_writew(w, base + OMAP_MPUIO_OUTPUT);
69 }
70
71 static inline int cbus_get_gpio_datain(u32 base, int mpuio)
72 {
73         mpuio &= 0x0f;
74
75         return (__raw_readw(base + OMAP_MPUIO_INPUT_LATCH) & (1 << mpuio)) != 0;
76 }
77
78 static void cbus_send_bit(struct cbus_host *host, u32 base, int bit,
79                           int set_to_input)
80 {
81         cbus_set_gpio_dataout(base, host->dat_gpio, bit ? 1 : 0);
82         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
83
84         /* The data bit is read on the rising edge of CLK */
85         if (set_to_input)
86                 cbus_set_gpio_direction(base, host->dat_gpio, 1);
87
88         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
89 }
90
91 static u8 cbus_receive_bit(struct cbus_host *host, u32 base)
92 {
93         u8 ret;
94
95         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
96         ret = cbus_get_gpio_datain(base, host->dat_gpio);
97         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
98
99         return ret;
100 }
101
102 #else
103
104 #define cbus_set_gpio_direction(base, gpio, is_input) omap_set_gpio_direction(gpio, is_input)
105 #define cbus_set_gpio_dataout(base, gpio, enable) omap_set_gpio_dataout(gpio, enable)
106 #define cbus_get_gpio_datain(base, int, gpio) omap_get_gpio_datain(gpio)
107
108 static void _cbus_send_bit(struct cbus_host *host, int bit, int set_to_input)
109 {
110         omap_set_gpio_dataout(host->dat_gpio, bit ? 1 : 0);
111         omap_set_gpio_dataout(host->clk_gpio, 1);
112
113         /* The data bit is read on the rising edge of CLK */
114         if (set_to_input)
115                 omap_set_gpio_direction(host->dat_gpio, 1);
116
117         omap_set_gpio_dataout(host->clk_gpio, 0);
118 }
119
120 static u8 _cbus_receive_bit(struct cbus_host *host)
121 {
122         u8 ret;
123
124         omap_set_gpio_dataout(host->clk_gpio, 1);
125         ret = omap_get_gpio_datain(host->dat_gpio);
126         omap_set_gpio_dataout(host->clk_gpio, 0);
127
128         return ret;
129 }
130
131 #define cbus_send_bit(host, base, bit, set_to_input) _cbus_send_bit(host, bit, set_to_input)
132 #define cbus_receive_bit(host, base) _cbus_receive_bit(host)
133
134 #endif
135
136 static int cbus_transfer(struct cbus_host *host, int dev, int reg, int data)
137 {
138         int i;
139         int is_read = 0;
140         unsigned long flags;
141         u32 base;
142
143 #ifdef CONFIG_ARCH_OMAP1
144         base = (u32) io_p2v(OMAP_MPUIO_BASE);
145 #else
146         base = 0;
147 #endif
148
149         if (data < 0)
150                 is_read = 1;
151
152         /* We don't want interrupts disturbing our transfer */
153         spin_lock_irqsave(&host->lock, flags);
154
155         /* Reset state and start of transfer, SEL stays down during transfer */
156         cbus_set_gpio_dataout(base, host->sel_gpio, 0);
157
158         /* Set the DAT pin to output */
159         cbus_set_gpio_direction(base, host->dat_gpio, 0);
160
161         /* Send the device address */
162         for (i = 3; i > 0; i--)
163                 cbus_send_bit(host, base, dev & (1 << (i - 1)), 0);
164
165         /* Send the rw flag */
166         cbus_send_bit(host, base, is_read, 0);
167
168         /* Send the register address */
169         for (i = 5; i > 0; i--) {
170                 int set_to_input = 0;
171
172                 if (is_read && i == 1)
173                         set_to_input = 1;
174
175                 cbus_send_bit(host, base, reg & (1 << (i - 1)), set_to_input);
176         }
177
178         if (!is_read) {
179                 for (i = 16; i > 0; i--)
180                         cbus_send_bit(host, base, data & (1 << (i - 1)), 0);
181         } else {
182                 cbus_set_gpio_dataout(base, host->clk_gpio, 1);
183                 data = 0;
184
185                 for (i = 16; i > 0; i--) {
186                         u8 bit = cbus_receive_bit(host, base);
187
188                         if (bit)
189                                 data |= 1 << (i - 1);
190                 }
191         }
192
193         /* Indicate end of transfer, SEL goes up until next transfer */
194         cbus_set_gpio_dataout(base, host->sel_gpio, 1);
195         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
196         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
197
198         spin_unlock_irqrestore(&host->lock, flags);
199
200         return is_read ? data : 0;
201 }
202
203 /*
204  * Read a given register from the device
205  */
206 int cbus_read_reg(struct cbus_host *host, int dev, int reg)
207 {
208         return cbus_host ? cbus_transfer(host, dev, reg, -1) : -ENODEV;
209 }
210
211 /*
212  * Write to a given register of the device
213  */
214 int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val)
215 {
216         return cbus_host ? cbus_transfer(host, dev, reg, (int)val) : -ENODEV;
217 }
218
219 int __init cbus_bus_init(void)
220 {
221         const struct omap_cbus_config * cbus_config;
222         struct cbus_host *chost;
223         int ret;
224
225         chost = kmalloc(sizeof (*chost), GFP_KERNEL);
226         if (chost == NULL)
227                 return -ENOMEM;
228
229         memset(chost, 0, sizeof (*chost));
230
231         spin_lock_init(&chost->lock);
232
233         cbus_config = omap_get_config(OMAP_TAG_CBUS, struct omap_cbus_config);
234
235         if (cbus_config == NULL) {
236                 printk(KERN_ERR "cbus: Unable to retrieve config data\n");
237                 return -ENODATA;
238         }
239
240         chost->clk_gpio = cbus_config->clk_gpio;
241         chost->dat_gpio = cbus_config->dat_gpio;
242         chost->sel_gpio = cbus_config->sel_gpio;
243
244 #ifdef CONFIG_ARCH_OMAP1
245         if (!OMAP_GPIO_IS_MPUIO(chost->clk_gpio) ||
246             !OMAP_GPIO_IS_MPUIO(chost->dat_gpio) ||
247             !OMAP_GPIO_IS_MPUIO(chost->sel_gpio)) {
248                 printk(KERN_ERR "cbus: Only MPUIO pins supported\n");
249                 ret = -ENODEV;
250                 goto exit1;
251         }
252 #endif
253
254         if ((ret = omap_request_gpio(chost->clk_gpio)) < 0)
255                 goto exit1;
256
257         if ((ret = omap_request_gpio(chost->dat_gpio)) < 0)
258                 goto exit2;
259
260         if ((ret = omap_request_gpio(chost->sel_gpio)) < 0)
261                 goto exit3;
262
263         omap_set_gpio_dataout(chost->clk_gpio, 0);
264         omap_set_gpio_dataout(chost->sel_gpio, 1);
265
266         omap_set_gpio_direction(chost->clk_gpio, 0);
267         omap_set_gpio_direction(chost->dat_gpio, 1);
268         omap_set_gpio_direction(chost->sel_gpio, 0);
269
270         omap_set_gpio_dataout(chost->clk_gpio, 1);
271         omap_set_gpio_dataout(chost->clk_gpio, 0);
272
273         cbus_host = chost;
274
275         return 0;
276 exit3:
277         omap_free_gpio(chost->dat_gpio);
278 exit2:
279         omap_free_gpio(chost->clk_gpio);
280 exit1:
281         kfree(chost);
282         return ret;
283 }
284
285 subsys_initcall(cbus_bus_init);
286
287 EXPORT_SYMBOL(cbus_host);
288 EXPORT_SYMBOL(cbus_read_reg);
289 EXPORT_SYMBOL(cbus_write_reg);
290
291 MODULE_DESCRIPTION("CBUS serial protocol");
292 MODULE_LICENSE("GPL");
293 MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");