David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 1 | /********************************************************************* |
| 2 | * |
| 3 | * Filename: toim3232-sir.c |
| 4 | * Version: 1.0 |
| 5 | * Description: Implementation of dongles based on the Vishay/Temic |
| 6 | * TOIM3232 SIR Endec chipset. Currently only the |
| 7 | * IRWave IR320ST-2 is tested, although it should work |
| 8 | * with any TOIM3232 or TOIM4232 chipset based RS232 |
| 9 | * dongle with minimal modification. |
| 10 | * Based heavily on the Tekram driver (tekram.c), |
| 11 | * with thanks to Dag Brattli and Martin Diehl. |
| 12 | * Status: Experimental. |
| 13 | * Author: David Basden <davidb-irda@rcpt.to> |
| 14 | * Created at: Thu Feb 09 23:47:32 2006 |
| 15 | * |
| 16 | * Copyright (c) 2006 David Basden. |
| 17 | * Copyright (c) 1998-1999 Dag Brattli, |
| 18 | * Copyright (c) 2002 Martin Diehl, |
| 19 | * All Rights Reserved. |
| 20 | * |
| 21 | * This program is free software; you can redistribute it and/or |
| 22 | * modify it under the terms of the GNU General Public License as |
| 23 | * published by the Free Software Foundation; either version 2 of |
| 24 | * the License, or (at your option) any later version. |
| 25 | * |
| 26 | * Neither Dag Brattli nor University of Tromsø admit liability nor |
| 27 | * provide warranty for any of this software. This material is |
| 28 | * provided "AS-IS" and at no charge. |
| 29 | * |
| 30 | ********************************************************************/ |
| 31 | |
| 32 | /* |
| 33 | * This driver has currently only been tested on the IRWave IR320ST-2 |
| 34 | * |
| 35 | * PROTOCOL: |
| 36 | * |
| 37 | * The protocol for talking to the TOIM3232 is quite easy, and is |
| 38 | * designed to interface with RS232 with only level convertors. The |
| 39 | * BR/~D line on the chip is brought high to signal 'command mode', |
| 40 | * where a command byte is sent to select the baudrate of the RS232 |
| 41 | * interface and the pulse length of the IRDA output. When BR/~D |
| 42 | * is brought low, the dongle then changes to the selected baudrate, |
| 43 | * and the RS232 interface is used for data until BR/~D is brought |
| 44 | * high again. The initial speed for the TOIMx323 after RESET is |
| 45 | * 9600 baud. The baudrate for command-mode is the last selected |
| 46 | * baud-rate, or 9600 after a RESET. |
| 47 | * |
| 48 | * The dongle I have (below) adds some extra hardware on the front end, |
| 49 | * but this is mostly directed towards pariasitic power from the RS232 |
| 50 | * line rather than changing very much about how to communicate with |
| 51 | * the TOIM3232. |
| 52 | * |
| 53 | * The protocol to talk to the TOIM4232 chipset seems to be almost |
| 54 | * identical to the TOIM3232 (and the 4232 datasheet is more detailed) |
| 55 | * so this code will probably work on that as well, although I haven't |
| 56 | * tested it on that hardware. |
| 57 | * |
| 58 | * Target dongle variations that might be common: |
| 59 | * |
| 60 | * DTR and RTS function: |
| 61 | * The data sheet for the 4232 has a sample implementation that hooks the |
| 62 | * DTR and RTS lines to the RESET and BaudRate/~Data lines of the |
| 63 | * chip (through line-converters). Given both DTR and RTS would have to |
| 64 | * be held low in normal operation, and the TOIMx232 requires +5V to |
| 65 | * signal ground, most dongle designers would almost certainly choose |
| 66 | * an implementation that kept at least one of DTR or RTS high in |
| 67 | * normal operation to provide power to the dongle, but will likely |
| 68 | * vary between designs. |
| 69 | * |
| 70 | * User specified command bits: |
| 71 | * There are two user-controllable output lines from the TOIMx232 that |
| 72 | * can be set low or high by setting the appropriate bits in the |
| 73 | * high-nibble of the command byte (when setting speed and pulse length). |
| 74 | * These might be used to switch on and off added hardware or extra |
| 75 | * dongle features. |
| 76 | * |
| 77 | * |
| 78 | * Target hardware: IRWave IR320ST-2 |
| 79 | * |
| 80 | * The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic |
| 81 | * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transciever. |
| 82 | * It uses a hex inverter and some discrete components to buffer and |
| 83 | * line convert the RS232 down to 5V. |
| 84 | * |
| 85 | * The dongle is powered through a voltage regulator, fed by a large |
| 86 | * capacitor. To switch the dongle on, DTR is brought high to charge |
| 87 | * the capacitor and drive the voltage regulator. DTR isn't associated |
| 88 | * with any control lines on the TOIM3232. Parisitic power is also taken |
| 89 | * from the RTS, TD and RD lines when brought high, but through resistors. |
| 90 | * When DTR is low, the circuit might lose power even with RTS high. |
| 91 | * |
| 92 | * RTS is inverted and attached to the BR/~D input pin. When RTS |
| 93 | * is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode. |
| 94 | * RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command |
| 95 | * mode'. |
| 96 | * |
| 97 | * For some unknown reason, the RESET line isn't actually connected |
| 98 | * to anything. This means to reset the dongle to get it to a known |
| 99 | * state (9600 baud) you must drop DTR and RTS low, wait for the power |
| 100 | * capacitor to discharge, and then bring DTR (and RTS for data mode) |
| 101 | * high again, and wait for the capacitor to charge, the power supply |
| 102 | * to stabilise, and the oscillator clock to stabilise. |
| 103 | * |
| 104 | * Fortunately, if the current baudrate is known, the chipset can |
| 105 | * easily change speed by entering command mode without having to |
| 106 | * reset the dongle first. |
| 107 | * |
| 108 | * Major Components: |
| 109 | * |
| 110 | * - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings |
| 111 | * to IRDA pulse timings |
| 112 | * - 3.6864MHz crystal to drive TOIM3232 clock oscillator |
| 113 | * - DM74lS04M Inverting Hex line buffer for RS232 input buffering |
| 114 | * and level conversion |
| 115 | * - PJ2951AC 150mA voltage regulator |
| 116 | * - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver |
| 117 | * |
| 118 | */ |
| 119 | |
| 120 | #include <linux/module.h> |
| 121 | #include <linux/delay.h> |
| 122 | #include <linux/init.h> |
Alexey Dobriyan | d43c36d | 2009-10-07 17:09:06 +0400 | [diff] [blame^] | 123 | #include <linux/sched.h> |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 124 | |
| 125 | #include <net/irda/irda.h> |
| 126 | |
| 127 | #include "sir-dev.h" |
| 128 | |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 129 | static int toim3232delay = 150; /* default is 150 ms */ |
Andrew Morton | 73a6c63 | 2006-03-20 22:33:41 -0800 | [diff] [blame] | 130 | module_param(toim3232delay, int, 0); |
| 131 | MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay"); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 132 | |
| 133 | #if 0 |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 134 | static int toim3232flipdtr = 0; /* default is DTR high to reset */ |
Andrew Morton | 73a6c63 | 2006-03-20 22:33:41 -0800 | [diff] [blame] | 135 | module_param(toim3232flipdtr, int, 0); |
| 136 | MODULE_PARM_DESC(toim3232flipdtr, "toim3232 dongle invert DTR (Reset)"); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 137 | |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 138 | static int toim3232fliprts = 0; /* default is RTS high for baud change */ |
Andrew Morton | 73a6c63 | 2006-03-20 22:33:41 -0800 | [diff] [blame] | 139 | module_param(toim3232fliptrs, int, 0); |
| 140 | MODULE_PARM_DESC(toim3232fliprts, "toim3232 dongle invert RTS (BR/D)"); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 141 | #endif |
| 142 | |
| 143 | static int toim3232_open(struct sir_dev *); |
| 144 | static int toim3232_close(struct sir_dev *); |
| 145 | static int toim3232_change_speed(struct sir_dev *, unsigned); |
| 146 | static int toim3232_reset(struct sir_dev *); |
| 147 | |
| 148 | #define TOIM3232_115200 0x00 |
| 149 | #define TOIM3232_57600 0x01 |
| 150 | #define TOIM3232_38400 0x02 |
| 151 | #define TOIM3232_19200 0x03 |
| 152 | #define TOIM3232_9600 0x06 |
| 153 | #define TOIM3232_2400 0x0A |
| 154 | |
| 155 | #define TOIM3232_PW 0x10 /* Pulse select bit */ |
| 156 | |
| 157 | static struct dongle_driver toim3232 = { |
| 158 | .owner = THIS_MODULE, |
| 159 | .driver_name = "Vishay TOIM3232", |
| 160 | .type = IRDA_TOIM3232_DONGLE, |
| 161 | .open = toim3232_open, |
| 162 | .close = toim3232_close, |
| 163 | .reset = toim3232_reset, |
| 164 | .set_speed = toim3232_change_speed, |
| 165 | }; |
| 166 | |
| 167 | static int __init toim3232_sir_init(void) |
| 168 | { |
| 169 | if (toim3232delay < 1 || toim3232delay > 500) |
| 170 | toim3232delay = 200; |
| 171 | IRDA_DEBUG(1, "%s - using %d ms delay\n", |
| 172 | toim3232.driver_name, toim3232delay); |
| 173 | return irda_register_dongle(&toim3232); |
| 174 | } |
| 175 | |
| 176 | static void __exit toim3232_sir_cleanup(void) |
| 177 | { |
| 178 | irda_unregister_dongle(&toim3232); |
| 179 | } |
| 180 | |
| 181 | static int toim3232_open(struct sir_dev *dev) |
| 182 | { |
| 183 | struct qos_info *qos = &dev->qos; |
| 184 | |
Harvey Harrison | a97a6f1 | 2008-07-30 17:20:18 -0700 | [diff] [blame] | 185 | IRDA_DEBUG(2, "%s()\n", __func__); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 186 | |
| 187 | /* Pull the lines high to start with. |
| 188 | * |
| 189 | * For the IR320ST-2, we need to charge the main supply capacitor to |
| 190 | * switch the device on. We keep DTR high throughout to do this. |
| 191 | * When RTS, TD and RD are high, they will also trickle-charge the |
| 192 | * cap. RTS is high for data transmission, and low for baud rate select. |
| 193 | * -- DGB |
| 194 | */ |
| 195 | sirdev_set_dtr_rts(dev, TRUE, TRUE); |
| 196 | |
| 197 | /* The TOI3232 supports many speeds between 1200bps and 115000bps. |
| 198 | * We really only care about those supported by the IRDA spec, but |
| 199 | * 38400 seems to be implemented in many places */ |
| 200 | qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; |
| 201 | |
| 202 | /* From the tekram driver. Not sure what a reasonable value is -- DGB */ |
| 203 | qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ |
| 204 | irda_qos_bits_to_value(qos); |
| 205 | |
| 206 | /* irda thread waits 50 msec for power settling */ |
| 207 | |
| 208 | return 0; |
| 209 | } |
| 210 | |
| 211 | static int toim3232_close(struct sir_dev *dev) |
| 212 | { |
Harvey Harrison | a97a6f1 | 2008-07-30 17:20:18 -0700 | [diff] [blame] | 213 | IRDA_DEBUG(2, "%s()\n", __func__); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 214 | |
| 215 | /* Power off dongle */ |
| 216 | sirdev_set_dtr_rts(dev, FALSE, FALSE); |
| 217 | |
| 218 | return 0; |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | * Function toim3232change_speed (dev, state, speed) |
| 223 | * |
| 224 | * Set the speed for the TOIM3232 based dongle. Warning, this |
| 225 | * function must be called with a process context! |
| 226 | * |
| 227 | * Algorithm |
| 228 | * 1. keep DTR high but clear RTS to bring into baud programming mode |
| 229 | * 2. wait at least 7us to enter programming mode |
| 230 | * 3. send control word to set baud rate and timing |
| 231 | * 4. wait at least 1us |
| 232 | * 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver) |
| 233 | * 6. should take effect immediately (although probably worth waiting) |
| 234 | */ |
| 235 | |
| 236 | #define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) |
| 237 | |
| 238 | static int toim3232_change_speed(struct sir_dev *dev, unsigned speed) |
| 239 | { |
| 240 | unsigned state = dev->fsm.substate; |
| 241 | unsigned delay = 0; |
| 242 | u8 byte; |
| 243 | static int ret = 0; |
| 244 | |
Harvey Harrison | a97a6f1 | 2008-07-30 17:20:18 -0700 | [diff] [blame] | 245 | IRDA_DEBUG(2, "%s()\n", __func__); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 246 | |
| 247 | switch(state) { |
| 248 | case SIRDEV_STATE_DONGLE_SPEED: |
| 249 | |
| 250 | /* Figure out what we are going to send as a control byte */ |
| 251 | switch (speed) { |
| 252 | case 2400: |
| 253 | byte = TOIM3232_PW|TOIM3232_2400; |
| 254 | break; |
| 255 | default: |
| 256 | speed = 9600; |
| 257 | ret = -EINVAL; |
| 258 | /* fall thru */ |
| 259 | case 9600: |
| 260 | byte = TOIM3232_PW|TOIM3232_9600; |
| 261 | break; |
| 262 | case 19200: |
| 263 | byte = TOIM3232_PW|TOIM3232_19200; |
| 264 | break; |
| 265 | case 38400: |
| 266 | byte = TOIM3232_PW|TOIM3232_38400; |
| 267 | break; |
| 268 | case 57600: |
| 269 | byte = TOIM3232_PW|TOIM3232_57600; |
| 270 | break; |
| 271 | case 115200: |
| 272 | byte = TOIM3232_115200; |
| 273 | break; |
| 274 | } |
| 275 | |
| 276 | /* Set DTR, Clear RTS: Go into baud programming mode */ |
| 277 | sirdev_set_dtr_rts(dev, TRUE, FALSE); |
| 278 | |
| 279 | /* Wait at least 7us */ |
| 280 | udelay(14); |
| 281 | |
| 282 | /* Write control byte */ |
| 283 | sirdev_raw_write(dev, &byte, 1); |
| 284 | |
| 285 | dev->speed = speed; |
| 286 | |
| 287 | state = TOIM3232_STATE_WAIT_SPEED; |
| 288 | delay = toim3232delay; |
| 289 | break; |
| 290 | |
| 291 | case TOIM3232_STATE_WAIT_SPEED: |
| 292 | /* Have transmitted control byte * Wait for 'at least 1us' */ |
| 293 | udelay(14); |
| 294 | |
| 295 | /* Set DTR, Set RTS: Go into normal data mode */ |
| 296 | sirdev_set_dtr_rts(dev, TRUE, TRUE); |
| 297 | |
| 298 | /* Wait (TODO: check this is needed) */ |
| 299 | udelay(50); |
| 300 | break; |
| 301 | |
| 302 | default: |
Harvey Harrison | a97a6f1 | 2008-07-30 17:20:18 -0700 | [diff] [blame] | 303 | printk(KERN_ERR "%s - undefined state %d\n", __func__, state); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 304 | ret = -EINVAL; |
| 305 | break; |
| 306 | } |
| 307 | |
| 308 | dev->fsm.substate = state; |
| 309 | return (delay > 0) ? delay : ret; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | * Function toim3232reset (driver) |
| 314 | * |
| 315 | * This function resets the toim3232 dongle. Warning, this function |
| 316 | * must be called with a process context!! |
| 317 | * |
| 318 | * What we should do is: |
| 319 | * 0. Pull RESET high |
| 320 | * 1. Wait for at least 7us |
| 321 | * 2. Pull RESET low |
| 322 | * 3. Wait for at least 7us |
| 323 | * 4. Pull BR/~D high |
| 324 | * 5. Wait for at least 7us |
| 325 | * 6. Send control byte to set baud rate |
| 326 | * 7. Wait at least 1us after stop bit |
| 327 | * 8. Pull BR/~D low |
| 328 | * 9. Should then be in data mode |
| 329 | * |
| 330 | * Because the IR320ST-2 doesn't have the RESET line connected for some reason, |
| 331 | * we'll have to do something else. |
| 332 | * |
| 333 | * The default speed after a RESET is 9600, so lets try just bringing it up in |
| 334 | * data mode after switching it off, waiting for the supply capacitor to |
| 335 | * discharge, and then switch it back on. This isn't actually pulling RESET |
| 336 | * high, but it seems to have the same effect. |
| 337 | * |
| 338 | * This behaviour will probably work on dongles that have the RESET line connected, |
| 339 | * but if not, add a flag for the IR320ST-2, and implment the above-listed proper |
| 340 | * behaviour. |
| 341 | * |
| 342 | * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we |
| 343 | * need to have pull RTS low |
| 344 | */ |
| 345 | |
| 346 | static int toim3232_reset(struct sir_dev *dev) |
| 347 | { |
Harvey Harrison | a97a6f1 | 2008-07-30 17:20:18 -0700 | [diff] [blame] | 348 | IRDA_DEBUG(2, "%s()\n", __func__); |
David Basden | 0ac81ae | 2006-03-20 22:21:10 -0800 | [diff] [blame] | 349 | |
| 350 | /* Switch off both DTR and RTS to switch off dongle */ |
| 351 | sirdev_set_dtr_rts(dev, FALSE, FALSE); |
| 352 | |
| 353 | /* Should sleep a while. This might be evil doing it this way.*/ |
| 354 | set_current_state(TASK_UNINTERRUPTIBLE); |
| 355 | schedule_timeout(msecs_to_jiffies(50)); |
| 356 | |
| 357 | /* Set DTR, Set RTS (data mode) */ |
| 358 | sirdev_set_dtr_rts(dev, TRUE, TRUE); |
| 359 | |
| 360 | /* Wait at least 10 ms for power to stabilize again */ |
| 361 | set_current_state(TASK_UNINTERRUPTIBLE); |
| 362 | schedule_timeout(msecs_to_jiffies(10)); |
| 363 | |
| 364 | /* Speed should now be 9600 */ |
| 365 | dev->speed = 9600; |
| 366 | |
| 367 | return 0; |
| 368 | } |
| 369 | |
| 370 | MODULE_AUTHOR("David Basden <davidb-linux@rcpt.to>"); |
| 371 | MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver"); |
| 372 | MODULE_LICENSE("GPL"); |
| 373 | MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */ |
| 374 | |
| 375 | module_init(toim3232_sir_init); |
| 376 | module_exit(toim3232_sir_cleanup); |