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