]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/cbus/cbus.c
use gpio_direction_output (OMAP tree only)
[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 <mach/gpio.h>
33 #include <mach/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 #define cbus_output(base, gpio, val)    cbus_set_gpio_direction(base, gpio, 0)
103
104 #else
105
106 #define cbus_output(base, gpio, val)    gpio_direction_output(gpio, val)
107 #define cbus_set_gpio_dataout(base, gpio, enable) gpio_set_value(gpio, enable)
108 #define cbus_get_gpio_datain(base, int, gpio) gpio_get_value(gpio)
109
110 static void _cbus_send_bit(struct cbus_host *host, int bit, int set_to_input)
111 {
112         gpio_set_value(host->dat_gpio, bit ? 1 : 0);
113         gpio_set_value(host->clk_gpio, 1);
114
115         /* The data bit is read on the rising edge of CLK */
116         if (set_to_input)
117                 gpio_direction_input(host->dat_gpio);
118
119         gpio_set_value(host->clk_gpio, 0);
120 }
121
122 static u8 _cbus_receive_bit(struct cbus_host *host)
123 {
124         u8 ret;
125
126         gpio_set_value(host->clk_gpio, 1);
127         ret = gpio_get_value(host->dat_gpio);
128         gpio_set_value(host->clk_gpio, 0);
129
130         return ret;
131 }
132
133 #define cbus_send_bit(host, base, bit, set_to_input) _cbus_send_bit(host, bit, set_to_input)
134 #define cbus_receive_bit(host, base) _cbus_receive_bit(host)
135
136 #endif
137
138 static int cbus_transfer(struct cbus_host *host, int dev, int reg, int data)
139 {
140         int i;
141         int is_read = 0;
142         unsigned long flags;
143         u32 base;
144
145 #ifdef CONFIG_ARCH_OMAP1
146         base = OMAP1_IO_ADDRESS(OMAP_MPUIO_BASE);
147 #else
148         base = 0;
149 #endif
150
151         if (data < 0)
152                 is_read = 1;
153
154         /* We don't want interrupts disturbing our transfer */
155         spin_lock_irqsave(&host->lock, flags);
156
157         /* Reset state and start of transfer, SEL stays down during transfer */
158         cbus_set_gpio_dataout(base, host->sel_gpio, 0);
159
160         /* Set the DAT pin to output */
161         cbus_output(base, host->dat_gpio, 1);
162
163         /* Send the device address */
164         for (i = 3; i > 0; i--)
165                 cbus_send_bit(host, base, dev & (1 << (i - 1)), 0);
166
167         /* Send the rw flag */
168         cbus_send_bit(host, base, is_read, 0);
169
170         /* Send the register address */
171         for (i = 5; i > 0; i--) {
172                 int set_to_input = 0;
173
174                 if (is_read && i == 1)
175                         set_to_input = 1;
176
177                 cbus_send_bit(host, base, reg & (1 << (i - 1)), set_to_input);
178         }
179
180         if (!is_read) {
181                 for (i = 16; i > 0; i--)
182                         cbus_send_bit(host, base, data & (1 << (i - 1)), 0);
183         } else {
184                 cbus_set_gpio_dataout(base, host->clk_gpio, 1);
185                 data = 0;
186
187                 for (i = 16; i > 0; i--) {
188                         u8 bit = cbus_receive_bit(host, base);
189
190                         if (bit)
191                                 data |= 1 << (i - 1);
192                 }
193         }
194
195         /* Indicate end of transfer, SEL goes up until next transfer */
196         cbus_set_gpio_dataout(base, host->sel_gpio, 1);
197         cbus_set_gpio_dataout(base, host->clk_gpio, 1);
198         cbus_set_gpio_dataout(base, host->clk_gpio, 0);
199
200         spin_unlock_irqrestore(&host->lock, flags);
201
202         return is_read ? data : 0;
203 }
204
205 /*
206  * Read a given register from the device
207  */
208 int cbus_read_reg(struct cbus_host *host, int dev, int reg)
209 {
210         return cbus_host ? cbus_transfer(host, dev, reg, -1) : -ENODEV;
211 }
212
213 /*
214  * Write to a given register of the device
215  */
216 int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val)
217 {
218         return cbus_host ? cbus_transfer(host, dev, reg, (int)val) : -ENODEV;
219 }
220
221 int __init cbus_bus_init(void)
222 {
223         const struct omap_cbus_config * cbus_config;
224         struct cbus_host *chost;
225         int ret;
226
227         chost = kmalloc(sizeof (*chost), GFP_KERNEL);
228         if (chost == NULL)
229                 return -ENOMEM;
230
231         memset(chost, 0, sizeof (*chost));
232
233         spin_lock_init(&chost->lock);
234
235         cbus_config = omap_get_config(OMAP_TAG_CBUS, struct omap_cbus_config);
236
237         if (cbus_config == NULL) {
238                 printk(KERN_ERR "cbus: Unable to retrieve config data\n");
239                 return -ENODATA;
240         }
241
242         chost->clk_gpio = cbus_config->clk_gpio;
243         chost->dat_gpio = cbus_config->dat_gpio;
244         chost->sel_gpio = cbus_config->sel_gpio;
245
246 #ifdef CONFIG_ARCH_OMAP1
247         if (!OMAP_GPIO_IS_MPUIO(chost->clk_gpio) ||
248             !OMAP_GPIO_IS_MPUIO(chost->dat_gpio) ||
249             !OMAP_GPIO_IS_MPUIO(chost->sel_gpio)) {
250                 printk(KERN_ERR "cbus: Only MPUIO pins supported\n");
251                 ret = -ENODEV;
252                 goto exit1;
253         }
254 #endif
255
256         if ((ret = omap_request_gpio(chost->clk_gpio)) < 0)
257                 goto exit1;
258
259         if ((ret = omap_request_gpio(chost->dat_gpio)) < 0)
260                 goto exit2;
261
262         if ((ret = omap_request_gpio(chost->sel_gpio)) < 0)
263                 goto exit3;
264
265         gpio_direction_output(chost->clk_gpio, 0);
266         gpio_direction_input(chost->dat_gpio);
267         gpio_direction_output(chost->sel_gpio, 1);
268
269         gpio_set_value(chost->clk_gpio, 1);
270         gpio_set_value(chost->clk_gpio, 0);
271
272         cbus_host = chost;
273
274         return 0;
275 exit3:
276         omap_free_gpio(chost->dat_gpio);
277 exit2:
278         omap_free_gpio(chost->clk_gpio);
279 exit1:
280         kfree(chost);
281         return ret;
282 }
283
284 subsys_initcall(cbus_bus_init);
285
286 EXPORT_SYMBOL(cbus_host);
287 EXPORT_SYMBOL(cbus_read_reg);
288 EXPORT_SYMBOL(cbus_write_reg);
289
290 MODULE_DESCRIPTION("CBUS serial protocol");
291 MODULE_LICENSE("GPL");
292 MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");