xref: /OK3568_Linux_fs/kernel/drivers/input/serio/hil_mlc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * HIL MLC state machine and serio interface driver
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (c) 2001 Brian S. Julin
5*4882a593Smuzhiyun  * All rights reserved.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Redistribution and use in source and binary forms, with or without
8*4882a593Smuzhiyun  * modification, are permitted provided that the following conditions
9*4882a593Smuzhiyun  * are met:
10*4882a593Smuzhiyun  * 1. Redistributions of source code must retain the above copyright
11*4882a593Smuzhiyun  *    notice, this list of conditions, and the following disclaimer,
12*4882a593Smuzhiyun  *    without modification.
13*4882a593Smuzhiyun  * 2. The name of the author may not be used to endorse or promote products
14*4882a593Smuzhiyun  *    derived from this software without specific prior written permission.
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * Alternatively, this software may be distributed under the terms of the
17*4882a593Smuzhiyun  * GNU General Public License ("GPL").
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20*4882a593Smuzhiyun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21*4882a593Smuzhiyun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*4882a593Smuzhiyun  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23*4882a593Smuzhiyun  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*4882a593Smuzhiyun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25*4882a593Smuzhiyun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*4882a593Smuzhiyun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27*4882a593Smuzhiyun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * References:
30*4882a593Smuzhiyun  * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
31*4882a593Smuzhiyun  *
32*4882a593Smuzhiyun  *
33*4882a593Smuzhiyun  *	Driver theory of operation:
34*4882a593Smuzhiyun  *
35*4882a593Smuzhiyun  *	Some access methods and an ISR is defined by the sub-driver
36*4882a593Smuzhiyun  *	(e.g. hp_sdc_mlc.c).  These methods are expected to provide a
37*4882a593Smuzhiyun  *	few bits of logic in addition to raw access to the HIL MLC,
38*4882a593Smuzhiyun  *	specifically, the ISR, which is entirely registered by the
39*4882a593Smuzhiyun  *	sub-driver and invoked directly, must check for record
40*4882a593Smuzhiyun  *	termination or packet match, at which point a semaphore must
41*4882a593Smuzhiyun  *	be cleared and then the hil_mlcs_tasklet must be scheduled.
42*4882a593Smuzhiyun  *
43*4882a593Smuzhiyun  *	The hil_mlcs_tasklet processes the state machine for all MLCs
44*4882a593Smuzhiyun  *	each time it runs, checking each MLC's progress at the current
45*4882a593Smuzhiyun  *	node in the state machine, and moving the MLC to subsequent nodes
46*4882a593Smuzhiyun  *	in the state machine when appropriate.  It will reschedule
47*4882a593Smuzhiyun  *	itself if output is pending.  (This rescheduling should be replaced
48*4882a593Smuzhiyun  *	at some point with a sub-driver-specific mechanism.)
49*4882a593Smuzhiyun  *
50*4882a593Smuzhiyun  *	A timer task prods the tasklet once per second to prevent
51*4882a593Smuzhiyun  *	hangups when attached devices do not return expected data
52*4882a593Smuzhiyun  *	and to initiate probes of the loop for new devices.
53*4882a593Smuzhiyun  */
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #include <linux/hil_mlc.h>
56*4882a593Smuzhiyun #include <linux/errno.h>
57*4882a593Smuzhiyun #include <linux/kernel.h>
58*4882a593Smuzhiyun #include <linux/module.h>
59*4882a593Smuzhiyun #include <linux/init.h>
60*4882a593Smuzhiyun #include <linux/interrupt.h>
61*4882a593Smuzhiyun #include <linux/slab.h>
62*4882a593Smuzhiyun #include <linux/timer.h>
63*4882a593Smuzhiyun #include <linux/list.h>
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
66*4882a593Smuzhiyun MODULE_DESCRIPTION("HIL MLC serio");
67*4882a593Smuzhiyun MODULE_LICENSE("Dual BSD/GPL");
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun EXPORT_SYMBOL(hil_mlc_register);
70*4882a593Smuzhiyun EXPORT_SYMBOL(hil_mlc_unregister);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define PREFIX "HIL MLC: "
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun static LIST_HEAD(hil_mlcs);
75*4882a593Smuzhiyun static DEFINE_RWLOCK(hil_mlcs_lock);
76*4882a593Smuzhiyun static struct timer_list	hil_mlcs_kicker;
77*4882a593Smuzhiyun static int			hil_mlcs_probe, hil_mlc_stop;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun static void hil_mlcs_process(unsigned long unused);
80*4882a593Smuzhiyun static DECLARE_TASKLET_DISABLED_OLD(hil_mlcs_tasklet, hil_mlcs_process);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun /* #define HIL_MLC_DEBUG */
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun /********************** Device info/instance management **********************/
86*4882a593Smuzhiyun 
hil_mlc_clear_di_map(hil_mlc * mlc,int val)87*4882a593Smuzhiyun static void hil_mlc_clear_di_map(hil_mlc *mlc, int val)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	int j;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	for (j = val; j < 7 ; j++)
92*4882a593Smuzhiyun 		mlc->di_map[j] = -1;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
hil_mlc_clear_di_scratch(hil_mlc * mlc)95*4882a593Smuzhiyun static void hil_mlc_clear_di_scratch(hil_mlc *mlc)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch));
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
hil_mlc_copy_di_scratch(hil_mlc * mlc,int idx)100*4882a593Smuzhiyun static void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch));
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
hil_mlc_match_di_scratch(hil_mlc * mlc)105*4882a593Smuzhiyun static int hil_mlc_match_di_scratch(hil_mlc *mlc)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	int idx;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
110*4882a593Smuzhiyun 		int j, found = 0;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 		/* In-use slots are not eligible. */
113*4882a593Smuzhiyun 		for (j = 0; j < 7 ; j++)
114*4882a593Smuzhiyun 			if (mlc->di_map[j] == idx)
115*4882a593Smuzhiyun 				found++;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 		if (found)
118*4882a593Smuzhiyun 			continue;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 		if (!memcmp(mlc->di + idx, &mlc->di_scratch,
121*4882a593Smuzhiyun 				sizeof(mlc->di_scratch)))
122*4882a593Smuzhiyun 			break;
123*4882a593Smuzhiyun 	}
124*4882a593Smuzhiyun 	return idx >= HIL_MLC_DEVMEM ? -1 : idx;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
hil_mlc_find_free_di(hil_mlc * mlc)127*4882a593Smuzhiyun static int hil_mlc_find_free_di(hil_mlc *mlc)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	int idx;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	/* TODO: Pick all-zero slots first, failing that,
132*4882a593Smuzhiyun 	 * randomize the slot picked among those eligible.
133*4882a593Smuzhiyun 	 */
134*4882a593Smuzhiyun 	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
135*4882a593Smuzhiyun 		int j, found = 0;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		for (j = 0; j < 7 ; j++)
138*4882a593Smuzhiyun 			if (mlc->di_map[j] == idx)
139*4882a593Smuzhiyun 				found++;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 		if (!found)
142*4882a593Smuzhiyun 			break;
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	return idx; /* Note: It is guaranteed at least one above will match */
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
hil_mlc_clean_serio_map(hil_mlc * mlc)148*4882a593Smuzhiyun static inline void hil_mlc_clean_serio_map(hil_mlc *mlc)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	int idx;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
153*4882a593Smuzhiyun 		int j, found = 0;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 		for (j = 0; j < 7 ; j++)
156*4882a593Smuzhiyun 			if (mlc->di_map[j] == idx)
157*4882a593Smuzhiyun 				found++;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 		if (!found)
160*4882a593Smuzhiyun 			mlc->serio_map[idx].di_revmap = -1;
161*4882a593Smuzhiyun 	}
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
hil_mlc_send_polls(hil_mlc * mlc)164*4882a593Smuzhiyun static void hil_mlc_send_polls(hil_mlc *mlc)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	int did, i, cnt;
167*4882a593Smuzhiyun 	struct serio *serio;
168*4882a593Smuzhiyun 	struct serio_driver *drv;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	i = cnt = 0;
171*4882a593Smuzhiyun 	did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8;
172*4882a593Smuzhiyun 	serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL;
173*4882a593Smuzhiyun 	drv = (serio != NULL) ? serio->drv : NULL;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	while (mlc->icount < 15 - i) {
176*4882a593Smuzhiyun 		hil_packet p;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 		p = mlc->ipacket[i];
179*4882a593Smuzhiyun 		if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {
180*4882a593Smuzhiyun 			if (drv && drv->interrupt) {
181*4882a593Smuzhiyun 				drv->interrupt(serio, 0, 0);
182*4882a593Smuzhiyun 				drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
183*4882a593Smuzhiyun 				drv->interrupt(serio, HIL_PKT_CMD >> 8,  0);
184*4882a593Smuzhiyun 				drv->interrupt(serio, HIL_CMD_POL + cnt, 0);
185*4882a593Smuzhiyun 			}
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 			did = (p & HIL_PKT_ADDR_MASK) >> 8;
188*4882a593Smuzhiyun 			serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL;
189*4882a593Smuzhiyun 			drv = (serio != NULL) ? serio->drv : NULL;
190*4882a593Smuzhiyun 			cnt = 0;
191*4882a593Smuzhiyun 		}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		cnt++;
194*4882a593Smuzhiyun 		i++;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 		if (drv && drv->interrupt) {
197*4882a593Smuzhiyun 			drv->interrupt(serio, (p >> 24), 0);
198*4882a593Smuzhiyun 			drv->interrupt(serio, (p >> 16) & 0xff, 0);
199*4882a593Smuzhiyun 			drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0);
200*4882a593Smuzhiyun 			drv->interrupt(serio, p & 0xff, 0);
201*4882a593Smuzhiyun 		}
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun /*************************** State engine *********************************/
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun #define HILSEN_SCHED	0x000100	/* Schedule the tasklet		*/
208*4882a593Smuzhiyun #define HILSEN_BREAK	0x000200	/* Wait until next pass		*/
209*4882a593Smuzhiyun #define HILSEN_UP	0x000400	/* relative node#, decrement	*/
210*4882a593Smuzhiyun #define HILSEN_DOWN	0x000800	/* relative node#, increment	*/
211*4882a593Smuzhiyun #define HILSEN_FOLLOW	0x001000	/* use retval as next node#	*/
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun #define HILSEN_MASK	0x0000ff
214*4882a593Smuzhiyun #define HILSEN_START	0
215*4882a593Smuzhiyun #define HILSEN_RESTART	1
216*4882a593Smuzhiyun #define HILSEN_DHR	9
217*4882a593Smuzhiyun #define HILSEN_DHR2	10
218*4882a593Smuzhiyun #define HILSEN_IFC	14
219*4882a593Smuzhiyun #define HILSEN_HEAL0	16
220*4882a593Smuzhiyun #define HILSEN_HEAL	18
221*4882a593Smuzhiyun #define HILSEN_ACF      21
222*4882a593Smuzhiyun #define HILSEN_ACF2	22
223*4882a593Smuzhiyun #define HILSEN_DISC0	25
224*4882a593Smuzhiyun #define HILSEN_DISC	27
225*4882a593Smuzhiyun #define HILSEN_MATCH	40
226*4882a593Smuzhiyun #define HILSEN_OPERATE	41
227*4882a593Smuzhiyun #define HILSEN_PROBE	44
228*4882a593Smuzhiyun #define HILSEN_DSR	52
229*4882a593Smuzhiyun #define HILSEN_REPOLL	55
230*4882a593Smuzhiyun #define HILSEN_IFCACF	58
231*4882a593Smuzhiyun #define HILSEN_END	60
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun #define HILSEN_NEXT	(HILSEN_DOWN | 1)
234*4882a593Smuzhiyun #define HILSEN_SAME	(HILSEN_DOWN | 0)
235*4882a593Smuzhiyun #define HILSEN_LAST	(HILSEN_UP | 1)
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun #define HILSEN_DOZE	(HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
238*4882a593Smuzhiyun #define HILSEN_SLEEP	(HILSEN_SAME | HILSEN_BREAK)
239*4882a593Smuzhiyun 
hilse_match(hil_mlc * mlc,int unused)240*4882a593Smuzhiyun static int hilse_match(hil_mlc *mlc, int unused)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	int rc;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	rc = hil_mlc_match_di_scratch(mlc);
245*4882a593Smuzhiyun 	if (rc == -1) {
246*4882a593Smuzhiyun 		rc = hil_mlc_find_free_di(mlc);
247*4882a593Smuzhiyun 		if (rc == -1)
248*4882a593Smuzhiyun 			goto err;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun #ifdef HIL_MLC_DEBUG
251*4882a593Smuzhiyun 		printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);
252*4882a593Smuzhiyun #endif
253*4882a593Smuzhiyun 		hil_mlc_copy_di_scratch(mlc, rc);
254*4882a593Smuzhiyun 		mlc->di_map[mlc->ddi] = rc;
255*4882a593Smuzhiyun 		mlc->serio_map[rc].di_revmap = mlc->ddi;
256*4882a593Smuzhiyun 		hil_mlc_clean_serio_map(mlc);
257*4882a593Smuzhiyun 		serio_rescan(mlc->serio[rc]);
258*4882a593Smuzhiyun 		return -1;
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	mlc->di_map[mlc->ddi] = rc;
262*4882a593Smuzhiyun #ifdef HIL_MLC_DEBUG
263*4882a593Smuzhiyun 	printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);
264*4882a593Smuzhiyun #endif
265*4882a593Smuzhiyun 	mlc->serio_map[rc].di_revmap = mlc->ddi;
266*4882a593Smuzhiyun 	hil_mlc_clean_serio_map(mlc);
267*4882a593Smuzhiyun 	return 0;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun  err:
270*4882a593Smuzhiyun 	printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");
271*4882a593Smuzhiyun 	return 1;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun /* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
hilse_init_lcv(hil_mlc * mlc,int unused)275*4882a593Smuzhiyun static int hilse_init_lcv(hil_mlc *mlc, int unused)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	time64_t now = ktime_get_seconds();
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	if (mlc->lcv && (now - mlc->lcv_time) < 5)
280*4882a593Smuzhiyun 		return -1;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	mlc->lcv_time = now;
283*4882a593Smuzhiyun 	mlc->lcv = 0;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	return 0;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
hilse_inc_lcv(hil_mlc * mlc,int lim)288*4882a593Smuzhiyun static int hilse_inc_lcv(hil_mlc *mlc, int lim)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	return mlc->lcv++ >= lim ? -1 : 0;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun #if 0
294*4882a593Smuzhiyun static int hilse_set_lcv(hil_mlc *mlc, int val)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun 	mlc->lcv = val;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	return 0;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun #endif
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun /* Management of the discovered device index (zero based, -1 means no devs) */
hilse_set_ddi(hil_mlc * mlc,int val)303*4882a593Smuzhiyun static int hilse_set_ddi(hil_mlc *mlc, int val)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun 	mlc->ddi = val;
306*4882a593Smuzhiyun 	hil_mlc_clear_di_map(mlc, val + 1);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	return 0;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun 
hilse_dec_ddi(hil_mlc * mlc,int unused)311*4882a593Smuzhiyun static int hilse_dec_ddi(hil_mlc *mlc, int unused)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun 	mlc->ddi--;
314*4882a593Smuzhiyun 	if (mlc->ddi <= -1) {
315*4882a593Smuzhiyun 		mlc->ddi = -1;
316*4882a593Smuzhiyun 		hil_mlc_clear_di_map(mlc, 0);
317*4882a593Smuzhiyun 		return -1;
318*4882a593Smuzhiyun 	}
319*4882a593Smuzhiyun 	hil_mlc_clear_di_map(mlc, mlc->ddi + 1);
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	return 0;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
hilse_inc_ddi(hil_mlc * mlc,int unused)324*4882a593Smuzhiyun static int hilse_inc_ddi(hil_mlc *mlc, int unused)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	BUG_ON(mlc->ddi >= 6);
327*4882a593Smuzhiyun 	mlc->ddi++;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	return 0;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
hilse_take_idd(hil_mlc * mlc,int unused)332*4882a593Smuzhiyun static int hilse_take_idd(hil_mlc *mlc, int unused)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	int i;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	/* Help the state engine:
337*4882a593Smuzhiyun 	 * Is this a real IDD response or just an echo?
338*4882a593Smuzhiyun 	 *
339*4882a593Smuzhiyun 	 * Real IDD response does not start with a command.
340*4882a593Smuzhiyun 	 */
341*4882a593Smuzhiyun 	if (mlc->ipacket[0] & HIL_PKT_CMD)
342*4882a593Smuzhiyun 		goto bail;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	/* Should have the command echoed further down. */
345*4882a593Smuzhiyun 	for (i = 1; i < 16; i++) {
346*4882a593Smuzhiyun 		if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) ==
347*4882a593Smuzhiyun 		     (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&
348*4882a593Smuzhiyun 		    (mlc->ipacket[i] & HIL_PKT_CMD) &&
349*4882a593Smuzhiyun 		    ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))
350*4882a593Smuzhiyun 			break;
351*4882a593Smuzhiyun 	}
352*4882a593Smuzhiyun 	if (i > 15)
353*4882a593Smuzhiyun 		goto bail;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	/* And the rest of the packets should still be clear. */
356*4882a593Smuzhiyun 	while (++i < 16)
357*4882a593Smuzhiyun 		if (mlc->ipacket[i])
358*4882a593Smuzhiyun 			break;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	if (i < 16)
361*4882a593Smuzhiyun 		goto bail;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	for (i = 0; i < 16; i++)
364*4882a593Smuzhiyun 		mlc->di_scratch.idd[i] =
365*4882a593Smuzhiyun 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	/* Next step is to see if RSC supported */
368*4882a593Smuzhiyun 	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC)
369*4882a593Smuzhiyun 		return HILSEN_NEXT;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
372*4882a593Smuzhiyun 		return HILSEN_DOWN | 4;
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	return 0;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun  bail:
377*4882a593Smuzhiyun 	mlc->ddi--;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	return -1; /* This should send us off to ACF */
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
hilse_take_rsc(hil_mlc * mlc,int unused)382*4882a593Smuzhiyun static int hilse_take_rsc(hil_mlc *mlc, int unused)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	int i;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	for (i = 0; i < 16; i++)
387*4882a593Smuzhiyun 		mlc->di_scratch.rsc[i] =
388*4882a593Smuzhiyun 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	/* Next step is to see if EXD supported (IDD has already been read) */
391*4882a593Smuzhiyun 	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
392*4882a593Smuzhiyun 		return HILSEN_NEXT;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	return 0;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun 
hilse_take_exd(hil_mlc * mlc,int unused)397*4882a593Smuzhiyun static int hilse_take_exd(hil_mlc *mlc, int unused)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun 	int i;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	for (i = 0; i < 16; i++)
402*4882a593Smuzhiyun 		mlc->di_scratch.exd[i] =
403*4882a593Smuzhiyun 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	/* Next step is to see if RNM supported. */
406*4882a593Smuzhiyun 	if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM)
407*4882a593Smuzhiyun 		return HILSEN_NEXT;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun 
hilse_take_rnm(hil_mlc * mlc,int unused)412*4882a593Smuzhiyun static int hilse_take_rnm(hil_mlc *mlc, int unused)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun 	int i;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	for (i = 0; i < 16; i++)
417*4882a593Smuzhiyun 		mlc->di_scratch.rnm[i] =
418*4882a593Smuzhiyun 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	printk(KERN_INFO PREFIX "Device name gotten: %16s\n",
421*4882a593Smuzhiyun 			mlc->di_scratch.rnm);
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	return 0;
424*4882a593Smuzhiyun }
425*4882a593Smuzhiyun 
hilse_operate(hil_mlc * mlc,int repoll)426*4882a593Smuzhiyun static int hilse_operate(hil_mlc *mlc, int repoll)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	if (mlc->opercnt == 0)
430*4882a593Smuzhiyun 		hil_mlcs_probe = 0;
431*4882a593Smuzhiyun 	mlc->opercnt = 1;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	hil_mlc_send_polls(mlc);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	if (!hil_mlcs_probe)
436*4882a593Smuzhiyun 		return 0;
437*4882a593Smuzhiyun 	hil_mlcs_probe = 0;
438*4882a593Smuzhiyun 	mlc->opercnt = 0;
439*4882a593Smuzhiyun 	return 1;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun #define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \
443*4882a593Smuzhiyun { HILSE_FUNC,		{ .func = funct }, funct_arg, zero_rc, neg_rc, pos_rc },
444*4882a593Smuzhiyun #define OUT(pack) \
445*4882a593Smuzhiyun { HILSE_OUT,		{ .packet = pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 },
446*4882a593Smuzhiyun #define CTS \
447*4882a593Smuzhiyun { HILSE_CTS,		{ .packet = 0    }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 },
448*4882a593Smuzhiyun #define EXPECT(comp, to, got, got_wrong, timed_out) \
449*4882a593Smuzhiyun { HILSE_EXPECT,		{ .packet = comp }, to, got, got_wrong, timed_out },
450*4882a593Smuzhiyun #define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \
451*4882a593Smuzhiyun { HILSE_EXPECT_LAST,	{ .packet = comp }, to, got, got_wrong, timed_out },
452*4882a593Smuzhiyun #define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \
453*4882a593Smuzhiyun { HILSE_EXPECT_DISC,	{ .packet = comp }, to, got, got_wrong, timed_out },
454*4882a593Smuzhiyun #define IN(to, got, got_error, timed_out) \
455*4882a593Smuzhiyun { HILSE_IN,		{ .packet = 0    }, to, got, got_error, timed_out },
456*4882a593Smuzhiyun #define OUT_DISC(pack) \
457*4882a593Smuzhiyun { HILSE_OUT_DISC,	{ .packet = pack }, 0, 0, 0, 0 },
458*4882a593Smuzhiyun #define OUT_LAST(pack) \
459*4882a593Smuzhiyun { HILSE_OUT_LAST,	{ .packet = pack }, 0, 0, 0, 0 },
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun static const struct hilse_node hil_mlc_se[HILSEN_END] = {
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	/* 0  HILSEN_START */
464*4882a593Smuzhiyun 	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	/* 1  HILSEN_RESTART */
467*4882a593Smuzhiyun 	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,  0)
468*4882a593Smuzhiyun 	OUT(HIL_CTRL_ONLY)			/* Disable APE */
469*4882a593Smuzhiyun 	CTS
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun #define TEST_PACKET(x) \
472*4882a593Smuzhiyun (HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x)
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5))
475*4882a593Smuzhiyun 	EXPECT(HIL_ERR_INT | TEST_PACKET(0x5),
476*4882a593Smuzhiyun 	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
477*4882a593Smuzhiyun 	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa))
478*4882a593Smuzhiyun 	EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),
479*4882a593Smuzhiyun 	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
480*4882a593Smuzhiyun 	OUT(HIL_CTRL_ONLY | 0)			/* Disable test mode */
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	/* 9  HILSEN_DHR */
483*4882a593Smuzhiyun 	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	/* 10 HILSEN_DHR2 */
486*4882a593Smuzhiyun 	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
487*4882a593Smuzhiyun 	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
488*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_DHR)
489*4882a593Smuzhiyun 	IN(300000,		HILSEN_DHR2,	HILSEN_DHR2,	HILSEN_NEXT)
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	/* 14 HILSEN_IFC */
492*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
493*4882a593Smuzhiyun 	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
494*4882a593Smuzhiyun 	       20000,		HILSEN_DISC,	HILSEN_DHR2,	HILSEN_NEXT )
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	/* If devices are there, they weren't in PUP or other loopback mode.
497*4882a593Smuzhiyun 	 * We're more concerned at this point with restoring operation
498*4882a593Smuzhiyun 	 * to devices than discovering new ones, so we try to salvage
499*4882a593Smuzhiyun 	 * the loop configuration by closing off the loop.
500*4882a593Smuzhiyun 	 */
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	/* 16 HILSEN_HEAL0 */
503*4882a593Smuzhiyun 	FUNC(hilse_dec_ddi, 0,	HILSEN_NEXT,	HILSEN_ACF,	0)
504*4882a593Smuzhiyun 	FUNC(hilse_inc_ddi, 0,	HILSEN_NEXT,	0,		0)
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 	/* 18 HILSEN_HEAL */
507*4882a593Smuzhiyun 	OUT_LAST(HIL_CMD_ELB)
508*4882a593Smuzhiyun 	EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT,
509*4882a593Smuzhiyun 		    20000,	HILSEN_REPOLL,	HILSEN_DSR,	HILSEN_NEXT)
510*4882a593Smuzhiyun 	FUNC(hilse_dec_ddi, 0,	HILSEN_HEAL,	HILSEN_NEXT,	0)
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	/* 21 HILSEN_ACF */
513*4882a593Smuzhiyun 	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_DOZE,	0)
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	/* 22 HILSEN_ACF2 */
516*4882a593Smuzhiyun 	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
517*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
518*4882a593Smuzhiyun 	IN(20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	/* 25 HILSEN_DISC0 */
521*4882a593Smuzhiyun 	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
522*4882a593Smuzhiyun 	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT,
523*4882a593Smuzhiyun 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	/* Only enter here if response just received */
526*4882a593Smuzhiyun 	/* 27 HILSEN_DISC */
527*4882a593Smuzhiyun 	OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD)
528*4882a593Smuzhiyun 	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT,
529*4882a593Smuzhiyun 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_START)
530*4882a593Smuzhiyun 	FUNC(hilse_inc_ddi,  0,	HILSEN_NEXT,	HILSEN_START,	0)
531*4882a593Smuzhiyun 	FUNC(hilse_take_idd, 0,	HILSEN_MATCH,	HILSEN_IFCACF,	HILSEN_FOLLOW)
532*4882a593Smuzhiyun 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC)
533*4882a593Smuzhiyun 	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT,
534*4882a593Smuzhiyun 	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
535*4882a593Smuzhiyun 	FUNC(hilse_take_rsc, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
536*4882a593Smuzhiyun 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD)
537*4882a593Smuzhiyun 	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT,
538*4882a593Smuzhiyun 	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
539*4882a593Smuzhiyun 	FUNC(hilse_take_exd, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
540*4882a593Smuzhiyun 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM)
541*4882a593Smuzhiyun 	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT,
542*4882a593Smuzhiyun 	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
543*4882a593Smuzhiyun 	FUNC(hilse_take_rnm, 0, HILSEN_MATCH,	0,		0)
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	/* 40 HILSEN_MATCH */
546*4882a593Smuzhiyun 	FUNC(hilse_match, 0,	HILSEN_NEXT,	HILSEN_NEXT,	/* TODO */ 0)
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	/* 41 HILSEN_OPERATE */
549*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_POL)
550*4882a593Smuzhiyun 	EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT,
551*4882a593Smuzhiyun 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
552*4882a593Smuzhiyun 	FUNC(hilse_operate, 0,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_NEXT)
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	/* 44 HILSEN_PROBE */
555*4882a593Smuzhiyun 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT)
556*4882a593Smuzhiyun 	IN(10000,		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
557*4882a593Smuzhiyun 	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
558*4882a593Smuzhiyun 	IN(10000,		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
559*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
560*4882a593Smuzhiyun 	IN(10000,		HILSEN_DISC0,	HILSEN_DSR,	HILSEN_NEXT)
561*4882a593Smuzhiyun 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB)
562*4882a593Smuzhiyun 	IN(10000,		HILSEN_OPERATE,	HILSEN_DSR,	HILSEN_DSR)
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	/* 52 HILSEN_DSR */
565*4882a593Smuzhiyun 	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
566*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_DSR)
567*4882a593Smuzhiyun 	IN(20000,		HILSEN_DHR,	HILSEN_DHR,	HILSEN_IFC)
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	/* 55 HILSEN_REPOLL */
570*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_RPL)
571*4882a593Smuzhiyun 	EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT,
572*4882a593Smuzhiyun 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
573*4882a593Smuzhiyun 	FUNC(hilse_operate, 1,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_PROBE)
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	/* 58 HILSEN_IFCACF */
576*4882a593Smuzhiyun 	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
577*4882a593Smuzhiyun 	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
578*4882a593Smuzhiyun 	       20000,		HILSEN_ACF2,	HILSEN_DHR2,	HILSEN_HEAL)
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	/* 60 HILSEN_END */
581*4882a593Smuzhiyun };
582*4882a593Smuzhiyun 
hilse_setup_input(hil_mlc * mlc,const struct hilse_node * node)583*4882a593Smuzhiyun static inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node)
584*4882a593Smuzhiyun {
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	switch (node->act) {
587*4882a593Smuzhiyun 	case HILSE_EXPECT_DISC:
588*4882a593Smuzhiyun 		mlc->imatch = node->object.packet;
589*4882a593Smuzhiyun 		mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
590*4882a593Smuzhiyun 		break;
591*4882a593Smuzhiyun 	case HILSE_EXPECT_LAST:
592*4882a593Smuzhiyun 		mlc->imatch = node->object.packet;
593*4882a593Smuzhiyun 		mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
594*4882a593Smuzhiyun 		break;
595*4882a593Smuzhiyun 	case HILSE_EXPECT:
596*4882a593Smuzhiyun 		mlc->imatch = node->object.packet;
597*4882a593Smuzhiyun 		break;
598*4882a593Smuzhiyun 	case HILSE_IN:
599*4882a593Smuzhiyun 		mlc->imatch = 0;
600*4882a593Smuzhiyun 		break;
601*4882a593Smuzhiyun 	default:
602*4882a593Smuzhiyun 		BUG();
603*4882a593Smuzhiyun 	}
604*4882a593Smuzhiyun 	mlc->istarted = 1;
605*4882a593Smuzhiyun 	mlc->intimeout = usecs_to_jiffies(node->arg);
606*4882a593Smuzhiyun 	mlc->instart = jiffies;
607*4882a593Smuzhiyun 	mlc->icount = 15;
608*4882a593Smuzhiyun 	memset(mlc->ipacket, 0, 16 * sizeof(hil_packet));
609*4882a593Smuzhiyun 	BUG_ON(down_trylock(&mlc->isem));
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun #ifdef HIL_MLC_DEBUG
613*4882a593Smuzhiyun static int doze;
614*4882a593Smuzhiyun static int seidx; /* For debug */
615*4882a593Smuzhiyun #endif
616*4882a593Smuzhiyun 
hilse_donode(hil_mlc * mlc)617*4882a593Smuzhiyun static int hilse_donode(hil_mlc *mlc)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun 	const struct hilse_node *node;
620*4882a593Smuzhiyun 	int nextidx = 0;
621*4882a593Smuzhiyun 	int sched_long = 0;
622*4882a593Smuzhiyun 	unsigned long flags;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun #ifdef HIL_MLC_DEBUG
625*4882a593Smuzhiyun 	if (mlc->seidx && mlc->seidx != seidx &&
626*4882a593Smuzhiyun 	    mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
627*4882a593Smuzhiyun 		printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx);
628*4882a593Smuzhiyun 		doze = 0;
629*4882a593Smuzhiyun 	}
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	seidx = mlc->seidx;
632*4882a593Smuzhiyun #endif
633*4882a593Smuzhiyun 	node = hil_mlc_se + mlc->seidx;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	switch (node->act) {
636*4882a593Smuzhiyun 		int rc;
637*4882a593Smuzhiyun 		hil_packet pack;
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	case HILSE_FUNC:
640*4882a593Smuzhiyun 		BUG_ON(node->object.func == NULL);
641*4882a593Smuzhiyun 		rc = node->object.func(mlc, node->arg);
642*4882a593Smuzhiyun 		nextidx = (rc > 0) ? node->ugly :
643*4882a593Smuzhiyun 			((rc < 0) ? node->bad : node->good);
644*4882a593Smuzhiyun 		if (nextidx == HILSEN_FOLLOW)
645*4882a593Smuzhiyun 			nextidx = rc;
646*4882a593Smuzhiyun 		break;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	case HILSE_EXPECT_LAST:
649*4882a593Smuzhiyun 	case HILSE_EXPECT_DISC:
650*4882a593Smuzhiyun 	case HILSE_EXPECT:
651*4882a593Smuzhiyun 	case HILSE_IN:
652*4882a593Smuzhiyun 		/* Already set up from previous HILSE_OUT_* */
653*4882a593Smuzhiyun 		write_lock_irqsave(&mlc->lock, flags);
654*4882a593Smuzhiyun 		rc = mlc->in(mlc, node->arg);
655*4882a593Smuzhiyun 		if (rc == 2)  {
656*4882a593Smuzhiyun 			nextidx = HILSEN_DOZE;
657*4882a593Smuzhiyun 			sched_long = 1;
658*4882a593Smuzhiyun 			write_unlock_irqrestore(&mlc->lock, flags);
659*4882a593Smuzhiyun 			break;
660*4882a593Smuzhiyun 		}
661*4882a593Smuzhiyun 		if (rc == 1)
662*4882a593Smuzhiyun 			nextidx = node->ugly;
663*4882a593Smuzhiyun 		else if (rc == 0)
664*4882a593Smuzhiyun 			nextidx = node->good;
665*4882a593Smuzhiyun 		else
666*4882a593Smuzhiyun 			nextidx = node->bad;
667*4882a593Smuzhiyun 		mlc->istarted = 0;
668*4882a593Smuzhiyun 		write_unlock_irqrestore(&mlc->lock, flags);
669*4882a593Smuzhiyun 		break;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	case HILSE_OUT_LAST:
672*4882a593Smuzhiyun 		write_lock_irqsave(&mlc->lock, flags);
673*4882a593Smuzhiyun 		pack = node->object.packet;
674*4882a593Smuzhiyun 		pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
675*4882a593Smuzhiyun 		goto out;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	case HILSE_OUT_DISC:
678*4882a593Smuzhiyun 		write_lock_irqsave(&mlc->lock, flags);
679*4882a593Smuzhiyun 		pack = node->object.packet;
680*4882a593Smuzhiyun 		pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
681*4882a593Smuzhiyun 		goto out;
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	case HILSE_OUT:
684*4882a593Smuzhiyun 		write_lock_irqsave(&mlc->lock, flags);
685*4882a593Smuzhiyun 		pack = node->object.packet;
686*4882a593Smuzhiyun 	out:
687*4882a593Smuzhiyun 		if (!mlc->istarted) {
688*4882a593Smuzhiyun 			/* Prepare to receive input */
689*4882a593Smuzhiyun 			if ((node + 1)->act & HILSE_IN)
690*4882a593Smuzhiyun 				hilse_setup_input(mlc, node + 1);
691*4882a593Smuzhiyun 		}
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 		write_unlock_irqrestore(&mlc->lock, flags);
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 		if (down_trylock(&mlc->osem)) {
696*4882a593Smuzhiyun 			nextidx = HILSEN_DOZE;
697*4882a593Smuzhiyun 			break;
698*4882a593Smuzhiyun 		}
699*4882a593Smuzhiyun 		up(&mlc->osem);
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 		write_lock_irqsave(&mlc->lock, flags);
702*4882a593Smuzhiyun 		if (!mlc->ostarted) {
703*4882a593Smuzhiyun 			mlc->ostarted = 1;
704*4882a593Smuzhiyun 			mlc->opacket = pack;
705*4882a593Smuzhiyun 			rc = mlc->out(mlc);
706*4882a593Smuzhiyun 			nextidx = HILSEN_DOZE;
707*4882a593Smuzhiyun 			write_unlock_irqrestore(&mlc->lock, flags);
708*4882a593Smuzhiyun 			if (rc) {
709*4882a593Smuzhiyun 				hil_mlc_stop = 1;
710*4882a593Smuzhiyun 				return 1;
711*4882a593Smuzhiyun 			}
712*4882a593Smuzhiyun 			break;
713*4882a593Smuzhiyun 		}
714*4882a593Smuzhiyun 		mlc->ostarted = 0;
715*4882a593Smuzhiyun 		mlc->instart = jiffies;
716*4882a593Smuzhiyun 		write_unlock_irqrestore(&mlc->lock, flags);
717*4882a593Smuzhiyun 		nextidx = HILSEN_NEXT;
718*4882a593Smuzhiyun 		break;
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	case HILSE_CTS:
721*4882a593Smuzhiyun 		write_lock_irqsave(&mlc->lock, flags);
722*4882a593Smuzhiyun 		rc = mlc->cts(mlc);
723*4882a593Smuzhiyun 		nextidx = rc ? node->bad : node->good;
724*4882a593Smuzhiyun 		write_unlock_irqrestore(&mlc->lock, flags);
725*4882a593Smuzhiyun 		if (rc) {
726*4882a593Smuzhiyun 			hil_mlc_stop = 1;
727*4882a593Smuzhiyun 			return 1;
728*4882a593Smuzhiyun 		}
729*4882a593Smuzhiyun 		break;
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun 	default:
732*4882a593Smuzhiyun 		BUG();
733*4882a593Smuzhiyun 	}
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun #ifdef HIL_MLC_DEBUG
736*4882a593Smuzhiyun 	if (nextidx == HILSEN_DOZE)
737*4882a593Smuzhiyun 		doze++;
738*4882a593Smuzhiyun #endif
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	while (nextidx & HILSEN_SCHED) {
741*4882a593Smuzhiyun 		unsigned long now = jiffies;
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 		if (!sched_long)
744*4882a593Smuzhiyun 			goto sched;
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 		if (time_after(now, mlc->instart + mlc->intimeout))
747*4882a593Smuzhiyun 			 goto sched;
748*4882a593Smuzhiyun 		mod_timer(&hil_mlcs_kicker, mlc->instart + mlc->intimeout);
749*4882a593Smuzhiyun 		break;
750*4882a593Smuzhiyun 	sched:
751*4882a593Smuzhiyun 		tasklet_schedule(&hil_mlcs_tasklet);
752*4882a593Smuzhiyun 		break;
753*4882a593Smuzhiyun 	}
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	if (nextidx & HILSEN_DOWN)
756*4882a593Smuzhiyun 		mlc->seidx += nextidx & HILSEN_MASK;
757*4882a593Smuzhiyun 	else if (nextidx & HILSEN_UP)
758*4882a593Smuzhiyun 		mlc->seidx -= nextidx & HILSEN_MASK;
759*4882a593Smuzhiyun 	else
760*4882a593Smuzhiyun 		mlc->seidx = nextidx & HILSEN_MASK;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	if (nextidx & HILSEN_BREAK)
763*4882a593Smuzhiyun 		return 1;
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	return 0;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun /******************** tasklet context functions **************************/
hil_mlcs_process(unsigned long unused)769*4882a593Smuzhiyun static void hil_mlcs_process(unsigned long unused)
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun 	struct list_head *tmp;
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 	read_lock(&hil_mlcs_lock);
774*4882a593Smuzhiyun 	list_for_each(tmp, &hil_mlcs) {
775*4882a593Smuzhiyun 		struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
776*4882a593Smuzhiyun 		while (hilse_donode(mlc) == 0) {
777*4882a593Smuzhiyun #ifdef HIL_MLC_DEBUG
778*4882a593Smuzhiyun 			if (mlc->seidx != 41 &&
779*4882a593Smuzhiyun 			    mlc->seidx != 42 &&
780*4882a593Smuzhiyun 			    mlc->seidx != 43)
781*4882a593Smuzhiyun 				printk(KERN_DEBUG PREFIX " + ");
782*4882a593Smuzhiyun #endif
783*4882a593Smuzhiyun 		}
784*4882a593Smuzhiyun 	}
785*4882a593Smuzhiyun 	read_unlock(&hil_mlcs_lock);
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun /************************* Keepalive timer task *********************/
789*4882a593Smuzhiyun 
hil_mlcs_timer(struct timer_list * unused)790*4882a593Smuzhiyun static void hil_mlcs_timer(struct timer_list *unused)
791*4882a593Smuzhiyun {
792*4882a593Smuzhiyun 	if (hil_mlc_stop) {
793*4882a593Smuzhiyun 		/* could not send packet - stop immediately. */
794*4882a593Smuzhiyun 		pr_warn(PREFIX "HIL seems stuck - Disabling HIL MLC.\n");
795*4882a593Smuzhiyun 		return;
796*4882a593Smuzhiyun 	}
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 	hil_mlcs_probe = 1;
799*4882a593Smuzhiyun 	tasklet_schedule(&hil_mlcs_tasklet);
800*4882a593Smuzhiyun 	/* Re-insert the periodic task. */
801*4882a593Smuzhiyun 	if (!timer_pending(&hil_mlcs_kicker))
802*4882a593Smuzhiyun 		mod_timer(&hil_mlcs_kicker, jiffies + HZ);
803*4882a593Smuzhiyun }
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun /******************** user/kernel context functions **********************/
806*4882a593Smuzhiyun 
hil_mlc_serio_write(struct serio * serio,unsigned char c)807*4882a593Smuzhiyun static int hil_mlc_serio_write(struct serio *serio, unsigned char c)
808*4882a593Smuzhiyun {
809*4882a593Smuzhiyun 	struct hil_mlc_serio_map *map;
810*4882a593Smuzhiyun 	struct hil_mlc *mlc;
811*4882a593Smuzhiyun 	struct serio_driver *drv;
812*4882a593Smuzhiyun 	uint8_t *idx, *last;
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	map = serio->port_data;
815*4882a593Smuzhiyun 	BUG_ON(map == NULL);
816*4882a593Smuzhiyun 
817*4882a593Smuzhiyun 	mlc = map->mlc;
818*4882a593Smuzhiyun 	BUG_ON(mlc == NULL);
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun 	mlc->serio_opacket[map->didx] |=
821*4882a593Smuzhiyun 		((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx]));
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	if (mlc->serio_oidx[map->didx] >= 3) {
824*4882a593Smuzhiyun 		/* for now only commands */
825*4882a593Smuzhiyun 		if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD))
826*4882a593Smuzhiyun 			return -EIO;
827*4882a593Smuzhiyun 		switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) {
828*4882a593Smuzhiyun 		case HIL_CMD_IDD:
829*4882a593Smuzhiyun 			idx = mlc->di[map->didx].idd;
830*4882a593Smuzhiyun 			goto emu;
831*4882a593Smuzhiyun 		case HIL_CMD_RSC:
832*4882a593Smuzhiyun 			idx = mlc->di[map->didx].rsc;
833*4882a593Smuzhiyun 			goto emu;
834*4882a593Smuzhiyun 		case HIL_CMD_EXD:
835*4882a593Smuzhiyun 			idx = mlc->di[map->didx].exd;
836*4882a593Smuzhiyun 			goto emu;
837*4882a593Smuzhiyun 		case HIL_CMD_RNM:
838*4882a593Smuzhiyun 			idx = mlc->di[map->didx].rnm;
839*4882a593Smuzhiyun 			goto emu;
840*4882a593Smuzhiyun 		default:
841*4882a593Smuzhiyun 			break;
842*4882a593Smuzhiyun 		}
843*4882a593Smuzhiyun 		mlc->serio_oidx[map->didx] = 0;
844*4882a593Smuzhiyun 		mlc->serio_opacket[map->didx] = 0;
845*4882a593Smuzhiyun 	}
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	mlc->serio_oidx[map->didx]++;
848*4882a593Smuzhiyun 	return -EIO;
849*4882a593Smuzhiyun  emu:
850*4882a593Smuzhiyun 	drv = serio->drv;
851*4882a593Smuzhiyun 	BUG_ON(drv == NULL);
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	last = idx + 15;
854*4882a593Smuzhiyun 	while ((last != idx) && (*last == 0))
855*4882a593Smuzhiyun 		last--;
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	while (idx != last) {
858*4882a593Smuzhiyun 		drv->interrupt(serio, 0, 0);
859*4882a593Smuzhiyun 		drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
860*4882a593Smuzhiyun 		drv->interrupt(serio, 0, 0);
861*4882a593Smuzhiyun 		drv->interrupt(serio, *idx, 0);
862*4882a593Smuzhiyun 		idx++;
863*4882a593Smuzhiyun 	}
864*4882a593Smuzhiyun 	drv->interrupt(serio, 0, 0);
865*4882a593Smuzhiyun 	drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
866*4882a593Smuzhiyun 	drv->interrupt(serio, HIL_PKT_CMD >> 8, 0);
867*4882a593Smuzhiyun 	drv->interrupt(serio, *idx, 0);
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	mlc->serio_oidx[map->didx] = 0;
870*4882a593Smuzhiyun 	mlc->serio_opacket[map->didx] = 0;
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	return 0;
873*4882a593Smuzhiyun }
874*4882a593Smuzhiyun 
hil_mlc_serio_open(struct serio * serio)875*4882a593Smuzhiyun static int hil_mlc_serio_open(struct serio *serio)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun 	struct hil_mlc_serio_map *map;
878*4882a593Smuzhiyun 	struct hil_mlc *mlc;
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun 	if (serio_get_drvdata(serio) != NULL)
881*4882a593Smuzhiyun 		return -EBUSY;
882*4882a593Smuzhiyun 
883*4882a593Smuzhiyun 	map = serio->port_data;
884*4882a593Smuzhiyun 	BUG_ON(map == NULL);
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 	mlc = map->mlc;
887*4882a593Smuzhiyun 	BUG_ON(mlc == NULL);
888*4882a593Smuzhiyun 
889*4882a593Smuzhiyun 	return 0;
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun 
hil_mlc_serio_close(struct serio * serio)892*4882a593Smuzhiyun static void hil_mlc_serio_close(struct serio *serio)
893*4882a593Smuzhiyun {
894*4882a593Smuzhiyun 	struct hil_mlc_serio_map *map;
895*4882a593Smuzhiyun 	struct hil_mlc *mlc;
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun 	map = serio->port_data;
898*4882a593Smuzhiyun 	BUG_ON(map == NULL);
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 	mlc = map->mlc;
901*4882a593Smuzhiyun 	BUG_ON(mlc == NULL);
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 	serio_set_drvdata(serio, NULL);
904*4882a593Smuzhiyun 	serio->drv = NULL;
905*4882a593Smuzhiyun 	/* TODO wake up interruptable */
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun static const struct serio_device_id hil_mlc_serio_id = {
909*4882a593Smuzhiyun 	.type = SERIO_HIL_MLC,
910*4882a593Smuzhiyun 	.proto = SERIO_HIL,
911*4882a593Smuzhiyun 	.extra = SERIO_ANY,
912*4882a593Smuzhiyun 	.id = SERIO_ANY,
913*4882a593Smuzhiyun };
914*4882a593Smuzhiyun 
hil_mlc_register(hil_mlc * mlc)915*4882a593Smuzhiyun int hil_mlc_register(hil_mlc *mlc)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun 	int i;
918*4882a593Smuzhiyun 	unsigned long flags;
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 	BUG_ON(mlc == NULL);
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	mlc->istarted = 0;
923*4882a593Smuzhiyun 	mlc->ostarted = 0;
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	rwlock_init(&mlc->lock);
926*4882a593Smuzhiyun 	sema_init(&mlc->osem, 1);
927*4882a593Smuzhiyun 
928*4882a593Smuzhiyun 	sema_init(&mlc->isem, 1);
929*4882a593Smuzhiyun 	mlc->icount = -1;
930*4882a593Smuzhiyun 	mlc->imatch = 0;
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun 	mlc->opercnt = 0;
933*4882a593Smuzhiyun 
934*4882a593Smuzhiyun 	sema_init(&(mlc->csem), 0);
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 	hil_mlc_clear_di_scratch(mlc);
937*4882a593Smuzhiyun 	hil_mlc_clear_di_map(mlc, 0);
938*4882a593Smuzhiyun 	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
939*4882a593Smuzhiyun 		struct serio *mlc_serio;
940*4882a593Smuzhiyun 		hil_mlc_copy_di_scratch(mlc, i);
941*4882a593Smuzhiyun 		mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL);
942*4882a593Smuzhiyun 		mlc->serio[i] = mlc_serio;
943*4882a593Smuzhiyun 		if (!mlc->serio[i]) {
944*4882a593Smuzhiyun 			for (; i >= 0; i--)
945*4882a593Smuzhiyun 				kfree(mlc->serio[i]);
946*4882a593Smuzhiyun 			return -ENOMEM;
947*4882a593Smuzhiyun 		}
948*4882a593Smuzhiyun 		snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
949*4882a593Smuzhiyun 		snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
950*4882a593Smuzhiyun 		mlc_serio->id			= hil_mlc_serio_id;
951*4882a593Smuzhiyun 		mlc_serio->id.id		= i; /* HIL port no. */
952*4882a593Smuzhiyun 		mlc_serio->write		= hil_mlc_serio_write;
953*4882a593Smuzhiyun 		mlc_serio->open			= hil_mlc_serio_open;
954*4882a593Smuzhiyun 		mlc_serio->close		= hil_mlc_serio_close;
955*4882a593Smuzhiyun 		mlc_serio->port_data		= &(mlc->serio_map[i]);
956*4882a593Smuzhiyun 		mlc->serio_map[i].mlc		= mlc;
957*4882a593Smuzhiyun 		mlc->serio_map[i].didx		= i;
958*4882a593Smuzhiyun 		mlc->serio_map[i].di_revmap	= -1;
959*4882a593Smuzhiyun 		mlc->serio_opacket[i]		= 0;
960*4882a593Smuzhiyun 		mlc->serio_oidx[i]		= 0;
961*4882a593Smuzhiyun 		serio_register_port(mlc_serio);
962*4882a593Smuzhiyun 	}
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 	mlc->tasklet = &hil_mlcs_tasklet;
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun 	write_lock_irqsave(&hil_mlcs_lock, flags);
967*4882a593Smuzhiyun 	list_add_tail(&mlc->list, &hil_mlcs);
968*4882a593Smuzhiyun 	mlc->seidx = HILSEN_START;
969*4882a593Smuzhiyun 	write_unlock_irqrestore(&hil_mlcs_lock, flags);
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun 	tasklet_schedule(&hil_mlcs_tasklet);
972*4882a593Smuzhiyun 	return 0;
973*4882a593Smuzhiyun }
974*4882a593Smuzhiyun 
hil_mlc_unregister(hil_mlc * mlc)975*4882a593Smuzhiyun int hil_mlc_unregister(hil_mlc *mlc)
976*4882a593Smuzhiyun {
977*4882a593Smuzhiyun 	struct list_head *tmp;
978*4882a593Smuzhiyun 	unsigned long flags;
979*4882a593Smuzhiyun 	int i;
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	BUG_ON(mlc == NULL);
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	write_lock_irqsave(&hil_mlcs_lock, flags);
984*4882a593Smuzhiyun 	list_for_each(tmp, &hil_mlcs)
985*4882a593Smuzhiyun 		if (list_entry(tmp, hil_mlc, list) == mlc)
986*4882a593Smuzhiyun 			goto found;
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 	/* not found in list */
989*4882a593Smuzhiyun 	write_unlock_irqrestore(&hil_mlcs_lock, flags);
990*4882a593Smuzhiyun 	tasklet_schedule(&hil_mlcs_tasklet);
991*4882a593Smuzhiyun 	return -ENODEV;
992*4882a593Smuzhiyun 
993*4882a593Smuzhiyun  found:
994*4882a593Smuzhiyun 	list_del(tmp);
995*4882a593Smuzhiyun 	write_unlock_irqrestore(&hil_mlcs_lock, flags);
996*4882a593Smuzhiyun 
997*4882a593Smuzhiyun 	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
998*4882a593Smuzhiyun 		serio_unregister_port(mlc->serio[i]);
999*4882a593Smuzhiyun 		mlc->serio[i] = NULL;
1000*4882a593Smuzhiyun 	}
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	tasklet_schedule(&hil_mlcs_tasklet);
1003*4882a593Smuzhiyun 	return 0;
1004*4882a593Smuzhiyun }
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun /**************************** Module interface *************************/
1007*4882a593Smuzhiyun 
hil_mlc_init(void)1008*4882a593Smuzhiyun static int __init hil_mlc_init(void)
1009*4882a593Smuzhiyun {
1010*4882a593Smuzhiyun 	timer_setup(&hil_mlcs_kicker, &hil_mlcs_timer, 0);
1011*4882a593Smuzhiyun 	mod_timer(&hil_mlcs_kicker, jiffies + HZ);
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	tasklet_enable(&hil_mlcs_tasklet);
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	return 0;
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun 
hil_mlc_exit(void)1018*4882a593Smuzhiyun static void __exit hil_mlc_exit(void)
1019*4882a593Smuzhiyun {
1020*4882a593Smuzhiyun 	del_timer_sync(&hil_mlcs_kicker);
1021*4882a593Smuzhiyun 	tasklet_kill(&hil_mlcs_tasklet);
1022*4882a593Smuzhiyun }
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun module_init(hil_mlc_init);
1025*4882a593Smuzhiyun module_exit(hil_mlc_exit);
1026