162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * HIL MLC state machine and serio interface driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2001 Brian S. Julin 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 862306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 962306a36Sopenharmony_ci * are met: 1062306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1162306a36Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 1262306a36Sopenharmony_ci * without modification. 1362306a36Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products 1462306a36Sopenharmony_ci * derived from this software without specific prior written permission. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 1762306a36Sopenharmony_ci * GNU General Public License ("GPL"). 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2062306a36Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2162306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2262306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 2362306a36Sopenharmony_ci * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2462306a36Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2562306a36Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2662306a36Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2762306a36Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * References: 3062306a36Sopenharmony_ci * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Driver theory of operation: 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Some access methods and an ISR is defined by the sub-driver 3662306a36Sopenharmony_ci * (e.g. hp_sdc_mlc.c). These methods are expected to provide a 3762306a36Sopenharmony_ci * few bits of logic in addition to raw access to the HIL MLC, 3862306a36Sopenharmony_ci * specifically, the ISR, which is entirely registered by the 3962306a36Sopenharmony_ci * sub-driver and invoked directly, must check for record 4062306a36Sopenharmony_ci * termination or packet match, at which point a semaphore must 4162306a36Sopenharmony_ci * be cleared and then the hil_mlcs_tasklet must be scheduled. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * The hil_mlcs_tasklet processes the state machine for all MLCs 4462306a36Sopenharmony_ci * each time it runs, checking each MLC's progress at the current 4562306a36Sopenharmony_ci * node in the state machine, and moving the MLC to subsequent nodes 4662306a36Sopenharmony_ci * in the state machine when appropriate. It will reschedule 4762306a36Sopenharmony_ci * itself if output is pending. (This rescheduling should be replaced 4862306a36Sopenharmony_ci * at some point with a sub-driver-specific mechanism.) 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * A timer task prods the tasklet once per second to prevent 5162306a36Sopenharmony_ci * hangups when attached devices do not return expected data 5262306a36Sopenharmony_ci * and to initiate probes of the loop for new devices. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#include <linux/hil_mlc.h> 5662306a36Sopenharmony_ci#include <linux/errno.h> 5762306a36Sopenharmony_ci#include <linux/kernel.h> 5862306a36Sopenharmony_ci#include <linux/module.h> 5962306a36Sopenharmony_ci#include <linux/init.h> 6062306a36Sopenharmony_ci#include <linux/interrupt.h> 6162306a36Sopenharmony_ci#include <linux/slab.h> 6262306a36Sopenharmony_ci#include <linux/timer.h> 6362306a36Sopenharmony_ci#include <linux/list.h> 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ciMODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); 6662306a36Sopenharmony_ciMODULE_DESCRIPTION("HIL MLC serio"); 6762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciEXPORT_SYMBOL(hil_mlc_register); 7062306a36Sopenharmony_ciEXPORT_SYMBOL(hil_mlc_unregister); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define PREFIX "HIL MLC: " 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic LIST_HEAD(hil_mlcs); 7562306a36Sopenharmony_cistatic DEFINE_RWLOCK(hil_mlcs_lock); 7662306a36Sopenharmony_cistatic struct timer_list hil_mlcs_kicker; 7762306a36Sopenharmony_cistatic int hil_mlcs_probe, hil_mlc_stop; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void hil_mlcs_process(unsigned long unused); 8062306a36Sopenharmony_cistatic DECLARE_TASKLET_DISABLED_OLD(hil_mlcs_tasklet, hil_mlcs_process); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* #define HIL_MLC_DEBUG */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/********************** Device info/instance management **********************/ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void hil_mlc_clear_di_map(hil_mlc *mlc, int val) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int j; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci for (j = val; j < 7 ; j++) 9262306a36Sopenharmony_ci mlc->di_map[j] = -1; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void hil_mlc_clear_di_scratch(hil_mlc *mlc) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch)); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch)); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int hil_mlc_match_di_scratch(hil_mlc *mlc) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int idx; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { 11062306a36Sopenharmony_ci int j, found = 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* In-use slots are not eligible. */ 11362306a36Sopenharmony_ci for (j = 0; j < 7 ; j++) 11462306a36Sopenharmony_ci if (mlc->di_map[j] == idx) 11562306a36Sopenharmony_ci found++; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (found) 11862306a36Sopenharmony_ci continue; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!memcmp(mlc->di + idx, &mlc->di_scratch, 12162306a36Sopenharmony_ci sizeof(mlc->di_scratch))) 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci return idx >= HIL_MLC_DEVMEM ? -1 : idx; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int hil_mlc_find_free_di(hil_mlc *mlc) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int idx; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* TODO: Pick all-zero slots first, failing that, 13262306a36Sopenharmony_ci * randomize the slot picked among those eligible. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { 13562306a36Sopenharmony_ci int j, found = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (j = 0; j < 7 ; j++) 13862306a36Sopenharmony_ci if (mlc->di_map[j] == idx) 13962306a36Sopenharmony_ci found++; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!found) 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return idx; /* Note: It is guaranteed at least one above will match */ 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic inline void hil_mlc_clean_serio_map(hil_mlc *mlc) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int idx; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { 15362306a36Sopenharmony_ci int j, found = 0; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci for (j = 0; j < 7 ; j++) 15662306a36Sopenharmony_ci if (mlc->di_map[j] == idx) 15762306a36Sopenharmony_ci found++; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!found) 16062306a36Sopenharmony_ci mlc->serio_map[idx].di_revmap = -1; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void hil_mlc_send_polls(hil_mlc *mlc) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int did, i, cnt; 16762306a36Sopenharmony_ci struct serio *serio; 16862306a36Sopenharmony_ci struct serio_driver *drv; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci i = cnt = 0; 17162306a36Sopenharmony_ci did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8; 17262306a36Sopenharmony_ci serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL; 17362306a36Sopenharmony_ci drv = (serio != NULL) ? serio->drv : NULL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci while (mlc->icount < 15 - i) { 17662306a36Sopenharmony_ci hil_packet p; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci p = mlc->ipacket[i]; 17962306a36Sopenharmony_ci if (did != (p & HIL_PKT_ADDR_MASK) >> 8) { 18062306a36Sopenharmony_ci if (drv && drv->interrupt) { 18162306a36Sopenharmony_ci drv->interrupt(serio, 0, 0); 18262306a36Sopenharmony_ci drv->interrupt(serio, HIL_ERR_INT >> 16, 0); 18362306a36Sopenharmony_ci drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); 18462306a36Sopenharmony_ci drv->interrupt(serio, HIL_CMD_POL + cnt, 0); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci did = (p & HIL_PKT_ADDR_MASK) >> 8; 18862306a36Sopenharmony_ci serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL; 18962306a36Sopenharmony_ci drv = (serio != NULL) ? serio->drv : NULL; 19062306a36Sopenharmony_ci cnt = 0; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci cnt++; 19462306a36Sopenharmony_ci i++; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (drv && drv->interrupt) { 19762306a36Sopenharmony_ci drv->interrupt(serio, (p >> 24), 0); 19862306a36Sopenharmony_ci drv->interrupt(serio, (p >> 16) & 0xff, 0); 19962306a36Sopenharmony_ci drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0); 20062306a36Sopenharmony_ci drv->interrupt(serio, p & 0xff, 0); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/*************************** State engine *********************************/ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#define HILSEN_SCHED 0x000100 /* Schedule the tasklet */ 20862306a36Sopenharmony_ci#define HILSEN_BREAK 0x000200 /* Wait until next pass */ 20962306a36Sopenharmony_ci#define HILSEN_UP 0x000400 /* relative node#, decrement */ 21062306a36Sopenharmony_ci#define HILSEN_DOWN 0x000800 /* relative node#, increment */ 21162306a36Sopenharmony_ci#define HILSEN_FOLLOW 0x001000 /* use retval as next node# */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci#define HILSEN_MASK 0x0000ff 21462306a36Sopenharmony_ci#define HILSEN_START 0 21562306a36Sopenharmony_ci#define HILSEN_RESTART 1 21662306a36Sopenharmony_ci#define HILSEN_DHR 9 21762306a36Sopenharmony_ci#define HILSEN_DHR2 10 21862306a36Sopenharmony_ci#define HILSEN_IFC 14 21962306a36Sopenharmony_ci#define HILSEN_HEAL0 16 22062306a36Sopenharmony_ci#define HILSEN_HEAL 18 22162306a36Sopenharmony_ci#define HILSEN_ACF 21 22262306a36Sopenharmony_ci#define HILSEN_ACF2 22 22362306a36Sopenharmony_ci#define HILSEN_DISC0 25 22462306a36Sopenharmony_ci#define HILSEN_DISC 27 22562306a36Sopenharmony_ci#define HILSEN_MATCH 40 22662306a36Sopenharmony_ci#define HILSEN_OPERATE 41 22762306a36Sopenharmony_ci#define HILSEN_PROBE 44 22862306a36Sopenharmony_ci#define HILSEN_DSR 52 22962306a36Sopenharmony_ci#define HILSEN_REPOLL 55 23062306a36Sopenharmony_ci#define HILSEN_IFCACF 58 23162306a36Sopenharmony_ci#define HILSEN_END 60 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#define HILSEN_NEXT (HILSEN_DOWN | 1) 23462306a36Sopenharmony_ci#define HILSEN_SAME (HILSEN_DOWN | 0) 23562306a36Sopenharmony_ci#define HILSEN_LAST (HILSEN_UP | 1) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define HILSEN_DOZE (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK) 23862306a36Sopenharmony_ci#define HILSEN_SLEEP (HILSEN_SAME | HILSEN_BREAK) 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int hilse_match(hil_mlc *mlc, int unused) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int rc; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci rc = hil_mlc_match_di_scratch(mlc); 24562306a36Sopenharmony_ci if (rc == -1) { 24662306a36Sopenharmony_ci rc = hil_mlc_find_free_di(mlc); 24762306a36Sopenharmony_ci if (rc == -1) 24862306a36Sopenharmony_ci goto err; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci#ifdef HIL_MLC_DEBUG 25162306a36Sopenharmony_ci printk(KERN_DEBUG PREFIX "new in slot %i\n", rc); 25262306a36Sopenharmony_ci#endif 25362306a36Sopenharmony_ci hil_mlc_copy_di_scratch(mlc, rc); 25462306a36Sopenharmony_ci mlc->di_map[mlc->ddi] = rc; 25562306a36Sopenharmony_ci mlc->serio_map[rc].di_revmap = mlc->ddi; 25662306a36Sopenharmony_ci hil_mlc_clean_serio_map(mlc); 25762306a36Sopenharmony_ci serio_rescan(mlc->serio[rc]); 25862306a36Sopenharmony_ci return -1; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci mlc->di_map[mlc->ddi] = rc; 26262306a36Sopenharmony_ci#ifdef HIL_MLC_DEBUG 26362306a36Sopenharmony_ci printk(KERN_DEBUG PREFIX "same in slot %i\n", rc); 26462306a36Sopenharmony_ci#endif 26562306a36Sopenharmony_ci mlc->serio_map[rc].di_revmap = mlc->ddi; 26662306a36Sopenharmony_ci hil_mlc_clean_serio_map(mlc); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci err: 27062306a36Sopenharmony_ci printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n"); 27162306a36Sopenharmony_ci return 1; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */ 27562306a36Sopenharmony_cistatic int hilse_init_lcv(hil_mlc *mlc, int unused) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci time64_t now = ktime_get_seconds(); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (mlc->lcv && (now - mlc->lcv_time) < 5) 28062306a36Sopenharmony_ci return -1; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci mlc->lcv_time = now; 28362306a36Sopenharmony_ci mlc->lcv = 0; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int hilse_inc_lcv(hil_mlc *mlc, int lim) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci return mlc->lcv++ >= lim ? -1 : 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci#if 0 29462306a36Sopenharmony_cistatic int hilse_set_lcv(hil_mlc *mlc, int val) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci mlc->lcv = val; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci#endif 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* Management of the discovered device index (zero based, -1 means no devs) */ 30362306a36Sopenharmony_cistatic int hilse_set_ddi(hil_mlc *mlc, int val) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci mlc->ddi = val; 30662306a36Sopenharmony_ci hil_mlc_clear_di_map(mlc, val + 1); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int hilse_dec_ddi(hil_mlc *mlc, int unused) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci mlc->ddi--; 31462306a36Sopenharmony_ci if (mlc->ddi <= -1) { 31562306a36Sopenharmony_ci mlc->ddi = -1; 31662306a36Sopenharmony_ci hil_mlc_clear_di_map(mlc, 0); 31762306a36Sopenharmony_ci return -1; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci hil_mlc_clear_di_map(mlc, mlc->ddi + 1); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int hilse_inc_ddi(hil_mlc *mlc, int unused) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci BUG_ON(mlc->ddi >= 6); 32762306a36Sopenharmony_ci mlc->ddi++; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int hilse_take_idd(hil_mlc *mlc, int unused) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int i; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Help the state engine: 33762306a36Sopenharmony_ci * Is this a real IDD response or just an echo? 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * Real IDD response does not start with a command. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci if (mlc->ipacket[0] & HIL_PKT_CMD) 34262306a36Sopenharmony_ci goto bail; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Should have the command echoed further down. */ 34562306a36Sopenharmony_ci for (i = 1; i < 16; i++) { 34662306a36Sopenharmony_ci if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == 34762306a36Sopenharmony_ci (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) && 34862306a36Sopenharmony_ci (mlc->ipacket[i] & HIL_PKT_CMD) && 34962306a36Sopenharmony_ci ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD)) 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci if (i > 15) 35362306a36Sopenharmony_ci goto bail; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* And the rest of the packets should still be clear. */ 35662306a36Sopenharmony_ci while (++i < 16) 35762306a36Sopenharmony_ci if (mlc->ipacket[i]) 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (i < 16) 36162306a36Sopenharmony_ci goto bail; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci for (i = 0; i < 16; i++) 36462306a36Sopenharmony_ci mlc->di_scratch.idd[i] = 36562306a36Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Next step is to see if RSC supported */ 36862306a36Sopenharmony_ci if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) 36962306a36Sopenharmony_ci return HILSEN_NEXT; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 37262306a36Sopenharmony_ci return HILSEN_DOWN | 4; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci bail: 37762306a36Sopenharmony_ci mlc->ddi--; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return -1; /* This should send us off to ACF */ 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int hilse_take_rsc(hil_mlc *mlc, int unused) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int i; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (i = 0; i < 16; i++) 38762306a36Sopenharmony_ci mlc->di_scratch.rsc[i] = 38862306a36Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Next step is to see if EXD supported (IDD has already been read) */ 39162306a36Sopenharmony_ci if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 39262306a36Sopenharmony_ci return HILSEN_NEXT; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int hilse_take_exd(hil_mlc *mlc, int unused) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci int i; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (i = 0; i < 16; i++) 40262306a36Sopenharmony_ci mlc->di_scratch.exd[i] = 40362306a36Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* Next step is to see if RNM supported. */ 40662306a36Sopenharmony_ci if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) 40762306a36Sopenharmony_ci return HILSEN_NEXT; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int hilse_take_rnm(hil_mlc *mlc, int unused) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int i; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci for (i = 0; i < 16; i++) 41762306a36Sopenharmony_ci mlc->di_scratch.rnm[i] = 41862306a36Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci printk(KERN_INFO PREFIX "Device name gotten: %16s\n", 42162306a36Sopenharmony_ci mlc->di_scratch.rnm); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int hilse_operate(hil_mlc *mlc, int repoll) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (mlc->opercnt == 0) 43062306a36Sopenharmony_ci hil_mlcs_probe = 0; 43162306a36Sopenharmony_ci mlc->opercnt = 1; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci hil_mlc_send_polls(mlc); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (!hil_mlcs_probe) 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci hil_mlcs_probe = 0; 43862306a36Sopenharmony_ci mlc->opercnt = 0; 43962306a36Sopenharmony_ci return 1; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \ 44362306a36Sopenharmony_ci{ HILSE_FUNC, { .func = funct }, funct_arg, zero_rc, neg_rc, pos_rc }, 44462306a36Sopenharmony_ci#define OUT(pack) \ 44562306a36Sopenharmony_ci{ HILSE_OUT, { .packet = pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 }, 44662306a36Sopenharmony_ci#define CTS \ 44762306a36Sopenharmony_ci{ HILSE_CTS, { .packet = 0 }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 }, 44862306a36Sopenharmony_ci#define EXPECT(comp, to, got, got_wrong, timed_out) \ 44962306a36Sopenharmony_ci{ HILSE_EXPECT, { .packet = comp }, to, got, got_wrong, timed_out }, 45062306a36Sopenharmony_ci#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \ 45162306a36Sopenharmony_ci{ HILSE_EXPECT_LAST, { .packet = comp }, to, got, got_wrong, timed_out }, 45262306a36Sopenharmony_ci#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \ 45362306a36Sopenharmony_ci{ HILSE_EXPECT_DISC, { .packet = comp }, to, got, got_wrong, timed_out }, 45462306a36Sopenharmony_ci#define IN(to, got, got_error, timed_out) \ 45562306a36Sopenharmony_ci{ HILSE_IN, { .packet = 0 }, to, got, got_error, timed_out }, 45662306a36Sopenharmony_ci#define OUT_DISC(pack) \ 45762306a36Sopenharmony_ci{ HILSE_OUT_DISC, { .packet = pack }, 0, 0, 0, 0 }, 45862306a36Sopenharmony_ci#define OUT_LAST(pack) \ 45962306a36Sopenharmony_ci{ HILSE_OUT_LAST, { .packet = pack }, 0, 0, 0, 0 }, 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic const struct hilse_node hil_mlc_se[HILSEN_END] = { 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 0 HILSEN_START */ 46462306a36Sopenharmony_ci FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* 1 HILSEN_RESTART */ 46762306a36Sopenharmony_ci FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) 46862306a36Sopenharmony_ci OUT(HIL_CTRL_ONLY) /* Disable APE */ 46962306a36Sopenharmony_ci CTS 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci#define TEST_PACKET(x) \ 47262306a36Sopenharmony_ci(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x) 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5)) 47562306a36Sopenharmony_ci EXPECT(HIL_ERR_INT | TEST_PACKET(0x5), 47662306a36Sopenharmony_ci 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) 47762306a36Sopenharmony_ci OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa)) 47862306a36Sopenharmony_ci EXPECT(HIL_ERR_INT | TEST_PACKET(0xa), 47962306a36Sopenharmony_ci 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) 48062306a36Sopenharmony_ci OUT(HIL_CTRL_ONLY | 0) /* Disable test mode */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 9 HILSEN_DHR */ 48362306a36Sopenharmony_ci FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 10 HILSEN_DHR2 */ 48662306a36Sopenharmony_ci FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) 48762306a36Sopenharmony_ci FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) 48862306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_DHR) 48962306a36Sopenharmony_ci IN(300000, HILSEN_DHR2, HILSEN_DHR2, HILSEN_NEXT) 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* 14 HILSEN_IFC */ 49262306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_IFC) 49362306a36Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, 49462306a36Sopenharmony_ci 20000, HILSEN_DISC, HILSEN_DHR2, HILSEN_NEXT ) 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* If devices are there, they weren't in PUP or other loopback mode. 49762306a36Sopenharmony_ci * We're more concerned at this point with restoring operation 49862306a36Sopenharmony_ci * to devices than discovering new ones, so we try to salvage 49962306a36Sopenharmony_ci * the loop configuration by closing off the loop. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 16 HILSEN_HEAL0 */ 50362306a36Sopenharmony_ci FUNC(hilse_dec_ddi, 0, HILSEN_NEXT, HILSEN_ACF, 0) 50462306a36Sopenharmony_ci FUNC(hilse_inc_ddi, 0, HILSEN_NEXT, 0, 0) 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* 18 HILSEN_HEAL */ 50762306a36Sopenharmony_ci OUT_LAST(HIL_CMD_ELB) 50862306a36Sopenharmony_ci EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, 50962306a36Sopenharmony_ci 20000, HILSEN_REPOLL, HILSEN_DSR, HILSEN_NEXT) 51062306a36Sopenharmony_ci FUNC(hilse_dec_ddi, 0, HILSEN_HEAL, HILSEN_NEXT, 0) 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* 21 HILSEN_ACF */ 51362306a36Sopenharmony_ci FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_DOZE, 0) 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* 22 HILSEN_ACF2 */ 51662306a36Sopenharmony_ci FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) 51762306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) 51862306a36Sopenharmony_ci IN(20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* 25 HILSEN_DISC0 */ 52162306a36Sopenharmony_ci OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) 52262306a36Sopenharmony_ci EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT, 52362306a36Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Only enter here if response just received */ 52662306a36Sopenharmony_ci /* 27 HILSEN_DISC */ 52762306a36Sopenharmony_ci OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD) 52862306a36Sopenharmony_ci EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT, 52962306a36Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_START) 53062306a36Sopenharmony_ci FUNC(hilse_inc_ddi, 0, HILSEN_NEXT, HILSEN_START, 0) 53162306a36Sopenharmony_ci FUNC(hilse_take_idd, 0, HILSEN_MATCH, HILSEN_IFCACF, HILSEN_FOLLOW) 53262306a36Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC) 53362306a36Sopenharmony_ci EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT, 53462306a36Sopenharmony_ci 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 53562306a36Sopenharmony_ci FUNC(hilse_take_rsc, 0, HILSEN_MATCH, 0, HILSEN_FOLLOW) 53662306a36Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD) 53762306a36Sopenharmony_ci EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT, 53862306a36Sopenharmony_ci 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 53962306a36Sopenharmony_ci FUNC(hilse_take_exd, 0, HILSEN_MATCH, 0, HILSEN_FOLLOW) 54062306a36Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM) 54162306a36Sopenharmony_ci EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT, 54262306a36Sopenharmony_ci 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 54362306a36Sopenharmony_ci FUNC(hilse_take_rnm, 0, HILSEN_MATCH, 0, 0) 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 40 HILSEN_MATCH */ 54662306a36Sopenharmony_ci FUNC(hilse_match, 0, HILSEN_NEXT, HILSEN_NEXT, /* TODO */ 0) 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 41 HILSEN_OPERATE */ 54962306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_POL) 55062306a36Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT, 55162306a36Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) 55262306a36Sopenharmony_ci FUNC(hilse_operate, 0, HILSEN_OPERATE, HILSEN_IFC, HILSEN_NEXT) 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* 44 HILSEN_PROBE */ 55562306a36Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT) 55662306a36Sopenharmony_ci IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) 55762306a36Sopenharmony_ci OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) 55862306a36Sopenharmony_ci IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) 55962306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) 56062306a36Sopenharmony_ci IN(10000, HILSEN_DISC0, HILSEN_DSR, HILSEN_NEXT) 56162306a36Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB) 56262306a36Sopenharmony_ci IN(10000, HILSEN_OPERATE, HILSEN_DSR, HILSEN_DSR) 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* 52 HILSEN_DSR */ 56562306a36Sopenharmony_ci FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) 56662306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_DSR) 56762306a36Sopenharmony_ci IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC) 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 55 HILSEN_REPOLL */ 57062306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_RPL) 57162306a36Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT, 57262306a36Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) 57362306a36Sopenharmony_ci FUNC(hilse_operate, 1, HILSEN_OPERATE, HILSEN_IFC, HILSEN_PROBE) 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 58 HILSEN_IFCACF */ 57662306a36Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_IFC) 57762306a36Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, 57862306a36Sopenharmony_ci 20000, HILSEN_ACF2, HILSEN_DHR2, HILSEN_HEAL) 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 60 HILSEN_END */ 58162306a36Sopenharmony_ci}; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci switch (node->act) { 58762306a36Sopenharmony_ci case HILSE_EXPECT_DISC: 58862306a36Sopenharmony_ci mlc->imatch = node->object.packet; 58962306a36Sopenharmony_ci mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci case HILSE_EXPECT_LAST: 59262306a36Sopenharmony_ci mlc->imatch = node->object.packet; 59362306a36Sopenharmony_ci mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci case HILSE_EXPECT: 59662306a36Sopenharmony_ci mlc->imatch = node->object.packet; 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci case HILSE_IN: 59962306a36Sopenharmony_ci mlc->imatch = 0; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci BUG(); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci mlc->istarted = 1; 60562306a36Sopenharmony_ci mlc->intimeout = usecs_to_jiffies(node->arg); 60662306a36Sopenharmony_ci mlc->instart = jiffies; 60762306a36Sopenharmony_ci mlc->icount = 15; 60862306a36Sopenharmony_ci memset(mlc->ipacket, 0, 16 * sizeof(hil_packet)); 60962306a36Sopenharmony_ci BUG_ON(down_trylock(&mlc->isem)); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci#ifdef HIL_MLC_DEBUG 61362306a36Sopenharmony_cistatic int doze; 61462306a36Sopenharmony_cistatic int seidx; /* For debug */ 61562306a36Sopenharmony_ci#endif 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int hilse_donode(hil_mlc *mlc) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci const struct hilse_node *node; 62062306a36Sopenharmony_ci int nextidx = 0; 62162306a36Sopenharmony_ci int sched_long = 0; 62262306a36Sopenharmony_ci unsigned long flags; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci#ifdef HIL_MLC_DEBUG 62562306a36Sopenharmony_ci if (mlc->seidx && mlc->seidx != seidx && 62662306a36Sopenharmony_ci mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) { 62762306a36Sopenharmony_ci printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx); 62862306a36Sopenharmony_ci doze = 0; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci seidx = mlc->seidx; 63262306a36Sopenharmony_ci#endif 63362306a36Sopenharmony_ci node = hil_mlc_se + mlc->seidx; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci switch (node->act) { 63662306a36Sopenharmony_ci int rc; 63762306a36Sopenharmony_ci hil_packet pack; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci case HILSE_FUNC: 64062306a36Sopenharmony_ci BUG_ON(node->object.func == NULL); 64162306a36Sopenharmony_ci rc = node->object.func(mlc, node->arg); 64262306a36Sopenharmony_ci nextidx = (rc > 0) ? node->ugly : 64362306a36Sopenharmony_ci ((rc < 0) ? node->bad : node->good); 64462306a36Sopenharmony_ci if (nextidx == HILSEN_FOLLOW) 64562306a36Sopenharmony_ci nextidx = rc; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci case HILSE_EXPECT_LAST: 64962306a36Sopenharmony_ci case HILSE_EXPECT_DISC: 65062306a36Sopenharmony_ci case HILSE_EXPECT: 65162306a36Sopenharmony_ci case HILSE_IN: 65262306a36Sopenharmony_ci /* Already set up from previous HILSE_OUT_* */ 65362306a36Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 65462306a36Sopenharmony_ci rc = mlc->in(mlc, node->arg); 65562306a36Sopenharmony_ci if (rc == 2) { 65662306a36Sopenharmony_ci nextidx = HILSEN_DOZE; 65762306a36Sopenharmony_ci sched_long = 1; 65862306a36Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci if (rc == 1) 66262306a36Sopenharmony_ci nextidx = node->ugly; 66362306a36Sopenharmony_ci else if (rc == 0) 66462306a36Sopenharmony_ci nextidx = node->good; 66562306a36Sopenharmony_ci else 66662306a36Sopenharmony_ci nextidx = node->bad; 66762306a36Sopenharmony_ci mlc->istarted = 0; 66862306a36Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci case HILSE_OUT_LAST: 67262306a36Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 67362306a36Sopenharmony_ci pack = node->object.packet; 67462306a36Sopenharmony_ci pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); 67562306a36Sopenharmony_ci goto out; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci case HILSE_OUT_DISC: 67862306a36Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 67962306a36Sopenharmony_ci pack = node->object.packet; 68062306a36Sopenharmony_ci pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); 68162306a36Sopenharmony_ci goto out; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci case HILSE_OUT: 68462306a36Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 68562306a36Sopenharmony_ci pack = node->object.packet; 68662306a36Sopenharmony_ci out: 68762306a36Sopenharmony_ci if (!mlc->istarted) { 68862306a36Sopenharmony_ci /* Prepare to receive input */ 68962306a36Sopenharmony_ci if ((node + 1)->act & HILSE_IN) 69062306a36Sopenharmony_ci hilse_setup_input(mlc, node + 1); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (down_trylock(&mlc->osem)) { 69662306a36Sopenharmony_ci nextidx = HILSEN_DOZE; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci up(&mlc->osem); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 70262306a36Sopenharmony_ci if (!mlc->ostarted) { 70362306a36Sopenharmony_ci mlc->ostarted = 1; 70462306a36Sopenharmony_ci mlc->opacket = pack; 70562306a36Sopenharmony_ci rc = mlc->out(mlc); 70662306a36Sopenharmony_ci nextidx = HILSEN_DOZE; 70762306a36Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 70862306a36Sopenharmony_ci if (rc) { 70962306a36Sopenharmony_ci hil_mlc_stop = 1; 71062306a36Sopenharmony_ci return 1; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci mlc->ostarted = 0; 71562306a36Sopenharmony_ci mlc->instart = jiffies; 71662306a36Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 71762306a36Sopenharmony_ci nextidx = HILSEN_NEXT; 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci case HILSE_CTS: 72162306a36Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 72262306a36Sopenharmony_ci rc = mlc->cts(mlc); 72362306a36Sopenharmony_ci nextidx = rc ? node->bad : node->good; 72462306a36Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 72562306a36Sopenharmony_ci if (rc) { 72662306a36Sopenharmony_ci hil_mlc_stop = 1; 72762306a36Sopenharmony_ci return 1; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci default: 73262306a36Sopenharmony_ci BUG(); 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci#ifdef HIL_MLC_DEBUG 73662306a36Sopenharmony_ci if (nextidx == HILSEN_DOZE) 73762306a36Sopenharmony_ci doze++; 73862306a36Sopenharmony_ci#endif 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci while (nextidx & HILSEN_SCHED) { 74162306a36Sopenharmony_ci unsigned long now = jiffies; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!sched_long) 74462306a36Sopenharmony_ci goto sched; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (time_after(now, mlc->instart + mlc->intimeout)) 74762306a36Sopenharmony_ci goto sched; 74862306a36Sopenharmony_ci mod_timer(&hil_mlcs_kicker, mlc->instart + mlc->intimeout); 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci sched: 75162306a36Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (nextidx & HILSEN_DOWN) 75662306a36Sopenharmony_ci mlc->seidx += nextidx & HILSEN_MASK; 75762306a36Sopenharmony_ci else if (nextidx & HILSEN_UP) 75862306a36Sopenharmony_ci mlc->seidx -= nextidx & HILSEN_MASK; 75962306a36Sopenharmony_ci else 76062306a36Sopenharmony_ci mlc->seidx = nextidx & HILSEN_MASK; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (nextidx & HILSEN_BREAK) 76362306a36Sopenharmony_ci return 1; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/******************** tasklet context functions **************************/ 76962306a36Sopenharmony_cistatic void hil_mlcs_process(unsigned long unused) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct list_head *tmp; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci read_lock(&hil_mlcs_lock); 77462306a36Sopenharmony_ci list_for_each(tmp, &hil_mlcs) { 77562306a36Sopenharmony_ci struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list); 77662306a36Sopenharmony_ci while (hilse_donode(mlc) == 0) { 77762306a36Sopenharmony_ci#ifdef HIL_MLC_DEBUG 77862306a36Sopenharmony_ci if (mlc->seidx != 41 && 77962306a36Sopenharmony_ci mlc->seidx != 42 && 78062306a36Sopenharmony_ci mlc->seidx != 43) 78162306a36Sopenharmony_ci printk(KERN_DEBUG PREFIX " + "); 78262306a36Sopenharmony_ci#endif 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci read_unlock(&hil_mlcs_lock); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/************************* Keepalive timer task *********************/ 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic void hil_mlcs_timer(struct timer_list *unused) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci if (hil_mlc_stop) { 79362306a36Sopenharmony_ci /* could not send packet - stop immediately. */ 79462306a36Sopenharmony_ci pr_warn(PREFIX "HIL seems stuck - Disabling HIL MLC.\n"); 79562306a36Sopenharmony_ci return; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci hil_mlcs_probe = 1; 79962306a36Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 80062306a36Sopenharmony_ci /* Re-insert the periodic task. */ 80162306a36Sopenharmony_ci if (!timer_pending(&hil_mlcs_kicker)) 80262306a36Sopenharmony_ci mod_timer(&hil_mlcs_kicker, jiffies + HZ); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/******************** user/kernel context functions **********************/ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic int hil_mlc_serio_write(struct serio *serio, unsigned char c) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct hil_mlc_serio_map *map; 81062306a36Sopenharmony_ci struct hil_mlc *mlc; 81162306a36Sopenharmony_ci struct serio_driver *drv; 81262306a36Sopenharmony_ci uint8_t *idx, *last; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci map = serio->port_data; 81562306a36Sopenharmony_ci BUG_ON(map == NULL); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci mlc = map->mlc; 81862306a36Sopenharmony_ci BUG_ON(mlc == NULL); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci mlc->serio_opacket[map->didx] |= 82162306a36Sopenharmony_ci ((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx])); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (mlc->serio_oidx[map->didx] >= 3) { 82462306a36Sopenharmony_ci /* for now only commands */ 82562306a36Sopenharmony_ci if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) 82662306a36Sopenharmony_ci return -EIO; 82762306a36Sopenharmony_ci switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) { 82862306a36Sopenharmony_ci case HIL_CMD_IDD: 82962306a36Sopenharmony_ci idx = mlc->di[map->didx].idd; 83062306a36Sopenharmony_ci goto emu; 83162306a36Sopenharmony_ci case HIL_CMD_RSC: 83262306a36Sopenharmony_ci idx = mlc->di[map->didx].rsc; 83362306a36Sopenharmony_ci goto emu; 83462306a36Sopenharmony_ci case HIL_CMD_EXD: 83562306a36Sopenharmony_ci idx = mlc->di[map->didx].exd; 83662306a36Sopenharmony_ci goto emu; 83762306a36Sopenharmony_ci case HIL_CMD_RNM: 83862306a36Sopenharmony_ci idx = mlc->di[map->didx].rnm; 83962306a36Sopenharmony_ci goto emu; 84062306a36Sopenharmony_ci default: 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci mlc->serio_oidx[map->didx] = 0; 84462306a36Sopenharmony_ci mlc->serio_opacket[map->didx] = 0; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci mlc->serio_oidx[map->didx]++; 84862306a36Sopenharmony_ci return -EIO; 84962306a36Sopenharmony_ci emu: 85062306a36Sopenharmony_ci drv = serio->drv; 85162306a36Sopenharmony_ci BUG_ON(drv == NULL); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci last = idx + 15; 85462306a36Sopenharmony_ci while ((last != idx) && (*last == 0)) 85562306a36Sopenharmony_ci last--; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci while (idx != last) { 85862306a36Sopenharmony_ci drv->interrupt(serio, 0, 0); 85962306a36Sopenharmony_ci drv->interrupt(serio, HIL_ERR_INT >> 16, 0); 86062306a36Sopenharmony_ci drv->interrupt(serio, 0, 0); 86162306a36Sopenharmony_ci drv->interrupt(serio, *idx, 0); 86262306a36Sopenharmony_ci idx++; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci drv->interrupt(serio, 0, 0); 86562306a36Sopenharmony_ci drv->interrupt(serio, HIL_ERR_INT >> 16, 0); 86662306a36Sopenharmony_ci drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); 86762306a36Sopenharmony_ci drv->interrupt(serio, *idx, 0); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci mlc->serio_oidx[map->didx] = 0; 87062306a36Sopenharmony_ci mlc->serio_opacket[map->didx] = 0; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return 0; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int hil_mlc_serio_open(struct serio *serio) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct hil_mlc_serio_map *map; 87862306a36Sopenharmony_ci struct hil_mlc *mlc; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (serio_get_drvdata(serio) != NULL) 88162306a36Sopenharmony_ci return -EBUSY; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci map = serio->port_data; 88462306a36Sopenharmony_ci BUG_ON(map == NULL); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci mlc = map->mlc; 88762306a36Sopenharmony_ci BUG_ON(mlc == NULL); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return 0; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic void hil_mlc_serio_close(struct serio *serio) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct hil_mlc_serio_map *map; 89562306a36Sopenharmony_ci struct hil_mlc *mlc; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci map = serio->port_data; 89862306a36Sopenharmony_ci BUG_ON(map == NULL); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci mlc = map->mlc; 90162306a36Sopenharmony_ci BUG_ON(mlc == NULL); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci serio_set_drvdata(serio, NULL); 90462306a36Sopenharmony_ci serio->drv = NULL; 90562306a36Sopenharmony_ci /* TODO wake up interruptable */ 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic const struct serio_device_id hil_mlc_serio_id = { 90962306a36Sopenharmony_ci .type = SERIO_HIL_MLC, 91062306a36Sopenharmony_ci .proto = SERIO_HIL, 91162306a36Sopenharmony_ci .extra = SERIO_ANY, 91262306a36Sopenharmony_ci .id = SERIO_ANY, 91362306a36Sopenharmony_ci}; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ciint hil_mlc_register(hil_mlc *mlc) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci int i; 91862306a36Sopenharmony_ci unsigned long flags; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci BUG_ON(mlc == NULL); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci mlc->istarted = 0; 92362306a36Sopenharmony_ci mlc->ostarted = 0; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci rwlock_init(&mlc->lock); 92662306a36Sopenharmony_ci sema_init(&mlc->osem, 1); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci sema_init(&mlc->isem, 1); 92962306a36Sopenharmony_ci mlc->icount = -1; 93062306a36Sopenharmony_ci mlc->imatch = 0; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci mlc->opercnt = 0; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci sema_init(&(mlc->csem), 0); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci hil_mlc_clear_di_scratch(mlc); 93762306a36Sopenharmony_ci hil_mlc_clear_di_map(mlc, 0); 93862306a36Sopenharmony_ci for (i = 0; i < HIL_MLC_DEVMEM; i++) { 93962306a36Sopenharmony_ci struct serio *mlc_serio; 94062306a36Sopenharmony_ci hil_mlc_copy_di_scratch(mlc, i); 94162306a36Sopenharmony_ci mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); 94262306a36Sopenharmony_ci mlc->serio[i] = mlc_serio; 94362306a36Sopenharmony_ci if (!mlc->serio[i]) { 94462306a36Sopenharmony_ci for (; i >= 0; i--) 94562306a36Sopenharmony_ci kfree(mlc->serio[i]); 94662306a36Sopenharmony_ci return -ENOMEM; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i); 94962306a36Sopenharmony_ci snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i); 95062306a36Sopenharmony_ci mlc_serio->id = hil_mlc_serio_id; 95162306a36Sopenharmony_ci mlc_serio->id.id = i; /* HIL port no. */ 95262306a36Sopenharmony_ci mlc_serio->write = hil_mlc_serio_write; 95362306a36Sopenharmony_ci mlc_serio->open = hil_mlc_serio_open; 95462306a36Sopenharmony_ci mlc_serio->close = hil_mlc_serio_close; 95562306a36Sopenharmony_ci mlc_serio->port_data = &(mlc->serio_map[i]); 95662306a36Sopenharmony_ci mlc->serio_map[i].mlc = mlc; 95762306a36Sopenharmony_ci mlc->serio_map[i].didx = i; 95862306a36Sopenharmony_ci mlc->serio_map[i].di_revmap = -1; 95962306a36Sopenharmony_ci mlc->serio_opacket[i] = 0; 96062306a36Sopenharmony_ci mlc->serio_oidx[i] = 0; 96162306a36Sopenharmony_ci serio_register_port(mlc_serio); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci mlc->tasklet = &hil_mlcs_tasklet; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci write_lock_irqsave(&hil_mlcs_lock, flags); 96762306a36Sopenharmony_ci list_add_tail(&mlc->list, &hil_mlcs); 96862306a36Sopenharmony_ci mlc->seidx = HILSEN_START; 96962306a36Sopenharmony_ci write_unlock_irqrestore(&hil_mlcs_lock, flags); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ciint hil_mlc_unregister(hil_mlc *mlc) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct list_head *tmp; 97862306a36Sopenharmony_ci unsigned long flags; 97962306a36Sopenharmony_ci int i; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci BUG_ON(mlc == NULL); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci write_lock_irqsave(&hil_mlcs_lock, flags); 98462306a36Sopenharmony_ci list_for_each(tmp, &hil_mlcs) 98562306a36Sopenharmony_ci if (list_entry(tmp, hil_mlc, list) == mlc) 98662306a36Sopenharmony_ci goto found; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* not found in list */ 98962306a36Sopenharmony_ci write_unlock_irqrestore(&hil_mlcs_lock, flags); 99062306a36Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 99162306a36Sopenharmony_ci return -ENODEV; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci found: 99462306a36Sopenharmony_ci list_del(tmp); 99562306a36Sopenharmony_ci write_unlock_irqrestore(&hil_mlcs_lock, flags); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci for (i = 0; i < HIL_MLC_DEVMEM; i++) { 99862306a36Sopenharmony_ci serio_unregister_port(mlc->serio[i]); 99962306a36Sopenharmony_ci mlc->serio[i] = NULL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci/**************************** Module interface *************************/ 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int __init hil_mlc_init(void) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci timer_setup(&hil_mlcs_kicker, &hil_mlcs_timer, 0); 101162306a36Sopenharmony_ci mod_timer(&hil_mlcs_kicker, jiffies + HZ); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci tasklet_enable(&hil_mlcs_tasklet); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic void __exit hil_mlc_exit(void) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci del_timer_sync(&hil_mlcs_kicker); 102162306a36Sopenharmony_ci tasklet_kill(&hil_mlcs_tasklet); 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cimodule_init(hil_mlc_init); 102562306a36Sopenharmony_cimodule_exit(hil_mlc_exit); 1026