1 /*********************************************************************
2 *
3 *
4 * Filename: mcp2120.c
5 * Version: 1.0
6 * Description: Implementation for the MCP2120 (Microchip)
7 * Status: Experimental.
8 * Author: Felix Tang (tangf@eyetap.org)
9 * Created at: Sun Mar 31 19:32:12 EST 2002
10 * Based on code by: Dag Brattli <dagb@cs.uit.no>
11 *
12 * Copyright (c) 2002 Felix Tang, All Rights Reserved.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License as
16 * published by the Free Software Foundation; either version 2 of
17 * the License, or (at your option) any later version.
18 *
19 ********************************************************************/
20
21 #include <linux/module.h>
22 #include <linux/delay.h>
23 #include <linux/tty.h>
24 #include <linux/sched.h>
25 #include <linux/init.h>
26
27 #include <net/irda/irda.h>
28 #include <net/irda/irmod.h>
29 #include <net/irda/irda_device.h>
30 #include <net/irda/irtty.h>
31
32 static int mcp2120_reset(struct irda_task *task);
33 static void mcp2120_open(dongle_t *self, struct qos_info *qos);
34 static void mcp2120_close(dongle_t *self);
35 static int mcp2120_change_speed(struct irda_task *task);
36
37 #define MCP2120_9600 0x87
38 #define MCP2120_19200 0x8B
39 #define MCP2120_38400 0x85
40 #define MCP2120_57600 0x83
41 #define MCP2120_115200 0x81
42
43 #define MCP2120_COMMIT 0x11
44
45 static struct dongle_reg dongle = {
46 Q_NULL,
47 IRDA_MCP2120_DONGLE,
48 mcp2120_open,
49 mcp2120_close,
50 mcp2120_reset,
51 mcp2120_change_speed,
52 };
53
mcp2120_init(void)54 int __init mcp2120_init(void)
55 {
56 return irda_device_register_dongle(&dongle);
57 }
58
mcp2120_cleanup(void)59 void mcp2120_cleanup(void)
60 {
61 irda_device_unregister_dongle(&dongle);
62 }
63
mcp2120_open(dongle_t * self,struct qos_info * qos)64 static void mcp2120_open(dongle_t *self, struct qos_info *qos)
65 {
66 qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
67 qos->min_turn_time.bits = 0x01;
68
69 MOD_INC_USE_COUNT;
70 }
71
mcp2120_close(dongle_t * self)72 static void mcp2120_close(dongle_t *self)
73 {
74 /* Power off dongle */
75 /* reset and inhibit mcp2120 */
76 self->set_dtr_rts(self->dev, TRUE, TRUE);
77 //self->set_dtr_rts(self->dev, FALSE, FALSE);
78
79 MOD_DEC_USE_COUNT;
80 }
81
82 /*
83 * Function mcp2120_change_speed (dev, speed)
84 *
85 * Set the speed for the MCP2120.
86 *
87 */
mcp2120_change_speed(struct irda_task * task)88 static int mcp2120_change_speed(struct irda_task *task)
89 {
90 dongle_t *self = (dongle_t *) task->instance;
91 __u32 speed = (__u32) task->param;
92 __u8 control[2];
93 int ret = 0;
94
95 self->speed_task = task;
96
97 switch (task->state) {
98 case IRDA_TASK_INIT:
99 /* Need to reset the dongle and go to 9600 bps before
100 programming */
101 //printk("Dmcp2120_change_speed irda_task_init\n");
102 if (irda_task_execute(self, mcp2120_reset, NULL, task,
103 (void *) speed))
104 {
105 /* Dongle need more time to reset */
106 irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
107
108 /* Give reset 1 sec to finish */
109 ret = MSECS_TO_JIFFIES(1000);
110 }
111 break;
112 case IRDA_TASK_CHILD_WAIT:
113 WARNING("%s(), resetting dongle timed out!\n", __FUNCTION__);
114 ret = -1;
115 break;
116 case IRDA_TASK_CHILD_DONE:
117 /* Set DTR to enter command mode */
118 self->set_dtr_rts(self->dev, TRUE, FALSE);
119 udelay(500);
120
121 switch (speed) {
122 case 9600:
123 default:
124 control[0] = MCP2120_9600;
125 //printk("mcp2120 9600\n");
126 break;
127 case 19200:
128 control[0] = MCP2120_19200;
129 //printk("mcp2120 19200\n");
130 break;
131 case 34800:
132 control[0] = MCP2120_38400;
133 //printk("mcp2120 38400\n");
134 break;
135 case 57600:
136 control[0] = MCP2120_57600;
137 //printk("mcp2120 57600\n");
138 break;
139 case 115200:
140 control[0] = MCP2120_115200;
141 //printk("mcp2120 115200\n");
142 break;
143 }
144 control[1] = MCP2120_COMMIT;
145
146 /* Write control bytes */
147 self->write(self->dev, control, 2);
148
149 irda_task_next_state(task, IRDA_TASK_WAIT);
150 ret = MSECS_TO_JIFFIES(100);
151 //printk("mcp2120_change_speed irda_child_done\n");
152 break;
153 case IRDA_TASK_WAIT:
154 /* Go back to normal mode */
155 self->set_dtr_rts(self->dev, FALSE, FALSE);
156 irda_task_next_state(task, IRDA_TASK_DONE);
157 self->speed_task = NULL;
158 //printk("mcp2120_change_speed irda_task_wait\n");
159 break;
160 default:
161 ERROR("%s(), unknown state %d\n", __FUNCTION__, task->state);
162 irda_task_next_state(task, IRDA_TASK_DONE);
163 self->speed_task = NULL;
164 ret = -1;
165 break;
166 }
167 return ret;
168 }
169
170 /*
171 * Function mcp2120_reset (driver)
172 *
173 * This function resets the mcp2120 dongle.
174 *
175 * Info: -set RTS to reset mcp2120
176 * -set DTR to set mcp2120 software command mode
177 * -mcp2120 defaults to 9600 baud after reset
178 *
179 * Algorithm:
180 * 0. Set RTS to reset mcp2120.
181 * 1. Clear RTS and wait for device reset timer of 30 ms (max).
182 *
183 */
184
185
mcp2120_reset(struct irda_task * task)186 static int mcp2120_reset(struct irda_task *task)
187 {
188 dongle_t *self = (dongle_t *) task->instance;
189 int ret = 0;
190
191 self->reset_task = task;
192
193 switch (task->state) {
194 case IRDA_TASK_INIT:
195 //printk("mcp2120_reset irda_task_init\n");
196 /* Reset dongle by setting RTS*/
197 self->set_dtr_rts(self->dev, TRUE, TRUE);
198 irda_task_next_state(task, IRDA_TASK_WAIT1);
199 ret = MSECS_TO_JIFFIES(50);
200 break;
201 case IRDA_TASK_WAIT1:
202 //printk("mcp2120_reset irda_task_wait1\n");
203 /* clear RTS and wait for at least 30 ms. */
204 self->set_dtr_rts(self->dev, FALSE, FALSE);
205 irda_task_next_state(task, IRDA_TASK_WAIT2);
206 ret = MSECS_TO_JIFFIES(50);
207 break;
208 case IRDA_TASK_WAIT2:
209 //printk("mcp2120_reset irda_task_wait2\n");
210 /* Go back to normal mode */
211 self->set_dtr_rts(self->dev, FALSE, FALSE);
212 irda_task_next_state(task, IRDA_TASK_DONE);
213 self->reset_task = NULL;
214 break;
215 default:
216 ERROR("%s(), unknown state %d\n", __FUNCTION__ , task->state);
217 irda_task_next_state(task, IRDA_TASK_DONE);
218 self->reset_task = NULL;
219 ret = -1;
220 break;
221 }
222 return ret;
223 }
224
225 #ifdef MODULE
226 MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>");
227 MODULE_DESCRIPTION("Microchip MCP2120");
228 MODULE_LICENSE("GPL");
229
230
231 /*
232 * Function init_module (void)
233 *
234 * Initialize MCP2120 module
235 *
236 */
init_module(void)237 int init_module(void)
238 {
239 return mcp2120_init();
240 }
241
242 /*
243 * Function cleanup_module (void)
244 *
245 * Cleanup MCP2120 module
246 *
247 */
cleanup_module(void)248 void cleanup_module(void)
249 {
250 mcp2120_cleanup();
251 }
252 #endif /* MODULE */
253