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