]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/cbus/cbus.c
Merge branch 'omap-fixes'
[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 #include <linux/gpio.h>
32
33 #include <mach/board.h>
34 #include <mach/board-nokia.h>
35
36 #include <asm/io.h>
37
38 #include "cbus.h"
39
40 struct cbus_host *cbus_host = NULL;
41
42 #ifdef CONFIG_ARCH_OMAP1
43 /* We use our own MPUIO functions to get closer to 1MHz bus speed */
44
45 static inline void cbus_set_gpio_direction(u32 base, int mpuio, int is_input)
46 {
47         u16 w;
48
49         mpuio &= 0x0f;
50         w = __raw_readw(base + OMAP_MPUIO_IO_CNTL);
51         if (is_input)
52                 w |= 1 << mpuio;
53         else
54                 w &= ~(1 << mpuio);
55         __raw_writew(w, base + OMAP_MPUIO_IO_CNTL);
56
57 }
58
59 static inline void cbus_set_gpio_dataout(u32 base, int mpuio, int enable)
60 {
61         u16 w;
62
63         mpuio &= 0x0f;
64         w = __raw_readw(base + OMAP_MPUIO_OUTPUT);
65         if (enable)
66                 w |= 1 << mpuio;
67         else
68                 w &= ~(1 << mpuio);
69         __raw_writew(w, base + OMAP_MPUIO_OUTPUT);
70 }
71
72 static inline int cbus_get_gpio_datain(u32 base, int mpuio)
73 {
74         mpuio &= 0x0f;
75
76         return (__raw_readw(base + OMAP_MPUIO_INPUT_LATCH) & (1 << mpuio)) != 0;
77 }
78
79 static void cbus_send_bit(struct cbus_host *host, u32 base, int bit,
80                           int set_to_input)
81 {
82         cbus_set_gpio_dataout(base, host->dat_gpio, bit ? 1 : 0);
83         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
84
85         /* The data bit is read on the rising edge of CLK */
86         if (set_to_input)
87                 cbus_set_gpio_direction(base, host->dat_gpio, 1);
88
89         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
90 }
91
92 static u8 cbus_receive_bit(struct cbus_host *host, u32 base)
93 {
94         u8 ret;
95
96         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
97         ret = cbus_get_gpio_datain(base, host->dat_gpio);
98         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
99
100         return ret;
101 }
102
103 #define cbus_output(base, gpio, val)    cbus_set_gpio_direction(base, gpio, 0)
104
105 #else
106
107 #define cbus_output(base, gpio, val)    gpio_direction_output(gpio, val)
108 #define cbus_set_gpio_dataout(base, gpio, enable) gpio_set_value(gpio, enable)
109 #define cbus_get_gpio_datain(base, int, gpio) gpio_get_value(gpio)
110
111 static void _cbus_send_bit(struct cbus_host *host, int bit, int set_to_input)
112 {
113         gpio_set_value(host->dat_gpio, bit ? 1 : 0);
114         gpio_set_value(host->clk_gpio, 1);
115
116         /* The data bit is read on the rising edge of CLK */
117         if (set_to_input)
118                 gpio_direction_input(host->dat_gpio);
119
120         gpio_set_value(host->clk_gpio, 0);
121 }
122
123 static u8 _cbus_receive_bit(struct cbus_host *host)
124 {
125         u8 ret;
126
127         gpio_set_value(host->clk_gpio, 1);
128         ret = gpio_get_value(host->dat_gpio);
129         gpio_set_value(host->clk_gpio, 0);
130
131         return ret;
132 }
133
134 #define cbus_send_bit(host, base, bit, set_to_input) _cbus_send_bit(host, bit, set_to_input)
135 #define cbus_receive_bit(host, base) _cbus_receive_bit(host)
136
137 #endif
138
139 static int cbus_transfer(struct cbus_host *host, int dev, int reg, int data)
140 {
141         int i;
142         int is_read = 0;
143         unsigned long flags;
144         u32 base;
145
146 #ifdef CONFIG_ARCH_OMAP1
147         base = OMAP1_IO_ADDRESS(OMAP_MPUIO_BASE);
148 #else
149         base = 0;
150 #endif
151
152         if (data < 0)
153                 is_read = 1;
154
155         /* We don't want interrupts disturbing our transfer */
156         spin_lock_irqsave(&host->lock, flags);
157
158         /* Reset state and start of transfer, SEL stays down during transfer */
159         cbus_set_gpio_dataout(base, host->sel_gpio, 0);
160
161         /* Set the DAT pin to output */
162         cbus_output(base, host->dat_gpio, 1);
163
164         /* Send the device address */
165         for (i = 3; i > 0; i--)
166                 cbus_send_bit(host, base, dev & (1 << (i - 1)), 0);
167
168         /* Send the rw flag */
169         cbus_send_bit(host, base, is_read, 0);
170
171         /* Send the register address */
172         for (i = 5; i > 0; i--) {
173                 int set_to_input = 0;
174
175                 if (is_read && i == 1)
176                         set_to_input = 1;
177
178                 cbus_send_bit(host, base, reg & (1 << (i - 1)), set_to_input);
179         }
180
181         if (!is_read) {
182                 for (i = 16; i > 0; i--)
183                         cbus_send_bit(host, base, data & (1 << (i - 1)), 0);
184         } else {
185                 cbus_set_gpio_dataout(base, host->clk_gpio, 1);
186                 data = 0;
187
188                 for (i = 16; i > 0; i--) {
189                         u8 bit = cbus_receive_bit(host, base);
190
191                         if (bit)
192                                 data |= 1 << (i - 1);
193                 }
194         }
195
196         /* Indicate end of transfer, SEL goes up until next transfer */
197         cbus_set_gpio_dataout(base, host->sel_gpio, 1);
198         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
199         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
200
201         spin_unlock_irqrestore(&host->lock, flags);
202
203         return is_read ? data : 0;
204 }
205
206 /*
207  * Read a given register from the device
208  */
209 int cbus_read_reg(struct cbus_host *host, int dev, int reg)
210 {
211         return cbus_host ? cbus_transfer(host, dev, reg, -1) : -ENODEV;
212 }
213
214 /*
215  * Write to a given register of the device
216  */
217 int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val)
218 {
219         return cbus_host ? cbus_transfer(host, dev, reg, (int)val) : -ENODEV;
220 }
221
222 int __init cbus_bus_init(void)
223 {
224         const struct omap_cbus_config * cbus_config;
225         struct cbus_host *chost;
226         int ret;
227
228         chost = kmalloc(sizeof (*chost), GFP_KERNEL);
229         if (chost == NULL)
230                 return -ENOMEM;
231
232         memset(chost, 0, sizeof (*chost));
233
234         spin_lock_init(&chost->lock);
235
236         cbus_config = omap_get_config(OMAP_TAG_CBUS, struct omap_cbus_config);
237
238         if (cbus_config == NULL) {
239                 printk(KERN_ERR "cbus: Unable to retrieve config data\n");
240                 return -ENODATA;
241         }
242
243         chost->clk_gpio = cbus_config->clk_gpio;
244         chost->dat_gpio = cbus_config->dat_gpio;
245         chost->sel_gpio = cbus_config->sel_gpio;
246
247 #ifdef CONFIG_ARCH_OMAP1
248         if (!OMAP_GPIO_IS_MPUIO(chost->clk_gpio) ||
249             !OMAP_GPIO_IS_MPUIO(chost->dat_gpio) ||
250             !OMAP_GPIO_IS_MPUIO(chost->sel_gpio)) {
251                 printk(KERN_ERR "cbus: Only MPUIO pins supported\n");
252                 ret = -ENODEV;
253                 goto exit1;
254         }
255 #endif
256
257         if ((ret = gpio_request(chost->clk_gpio, "CBUS clk")) < 0)
258                 goto exit1;
259
260         if ((ret = gpio_request(chost->dat_gpio, "CBUS data")) < 0)
261                 goto exit2;
262
263         if ((ret = gpio_request(chost->sel_gpio, "CBUS sel")) < 0)
264                 goto exit3;
265
266         gpio_direction_output(chost->clk_gpio, 0);
267         gpio_direction_input(chost->dat_gpio);
268         gpio_direction_output(chost->sel_gpio, 1);
269
270         gpio_set_value(chost->clk_gpio, 1);
271         gpio_set_value(chost->clk_gpio, 0);
272
273         cbus_host = chost;
274
275         return 0;
276 exit3:
277         gpio_free(chost->dat_gpio);
278 exit2:
279         gpio_free(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");