18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * HIL MLC state machine and serio interface driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2001 Brian S. Julin 58c2ecf20Sopenharmony_ci * All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 98c2ecf20Sopenharmony_ci * are met: 108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 118c2ecf20Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 128c2ecf20Sopenharmony_ci * without modification. 138c2ecf20Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products 148c2ecf20Sopenharmony_ci * derived from this software without specific prior written permission. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 178c2ecf20Sopenharmony_ci * GNU General Public License ("GPL"). 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 208c2ecf20Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 238c2ecf20Sopenharmony_ci * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 248c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 258c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 268c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 278c2ecf20Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * References: 308c2ecf20Sopenharmony_ci * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Driver theory of operation: 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Some access methods and an ISR is defined by the sub-driver 368c2ecf20Sopenharmony_ci * (e.g. hp_sdc_mlc.c). These methods are expected to provide a 378c2ecf20Sopenharmony_ci * few bits of logic in addition to raw access to the HIL MLC, 388c2ecf20Sopenharmony_ci * specifically, the ISR, which is entirely registered by the 398c2ecf20Sopenharmony_ci * sub-driver and invoked directly, must check for record 408c2ecf20Sopenharmony_ci * termination or packet match, at which point a semaphore must 418c2ecf20Sopenharmony_ci * be cleared and then the hil_mlcs_tasklet must be scheduled. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * The hil_mlcs_tasklet processes the state machine for all MLCs 448c2ecf20Sopenharmony_ci * each time it runs, checking each MLC's progress at the current 458c2ecf20Sopenharmony_ci * node in the state machine, and moving the MLC to subsequent nodes 468c2ecf20Sopenharmony_ci * in the state machine when appropriate. It will reschedule 478c2ecf20Sopenharmony_ci * itself if output is pending. (This rescheduling should be replaced 488c2ecf20Sopenharmony_ci * at some point with a sub-driver-specific mechanism.) 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * A timer task prods the tasklet once per second to prevent 518c2ecf20Sopenharmony_ci * hangups when attached devices do not return expected data 528c2ecf20Sopenharmony_ci * and to initiate probes of the loop for new devices. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <linux/hil_mlc.h> 568c2ecf20Sopenharmony_ci#include <linux/errno.h> 578c2ecf20Sopenharmony_ci#include <linux/kernel.h> 588c2ecf20Sopenharmony_ci#include <linux/module.h> 598c2ecf20Sopenharmony_ci#include <linux/init.h> 608c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 618c2ecf20Sopenharmony_ci#include <linux/slab.h> 628c2ecf20Sopenharmony_ci#include <linux/timer.h> 638c2ecf20Sopenharmony_ci#include <linux/list.h> 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); 668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HIL MLC serio"); 678c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hil_mlc_register); 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hil_mlc_unregister); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define PREFIX "HIL MLC: " 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic LIST_HEAD(hil_mlcs); 758c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(hil_mlcs_lock); 768c2ecf20Sopenharmony_cistatic struct timer_list hil_mlcs_kicker; 778c2ecf20Sopenharmony_cistatic int hil_mlcs_probe, hil_mlc_stop; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void hil_mlcs_process(unsigned long unused); 808c2ecf20Sopenharmony_cistatic DECLARE_TASKLET_DISABLED_OLD(hil_mlcs_tasklet, hil_mlcs_process); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* #define HIL_MLC_DEBUG */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/********************** Device info/instance management **********************/ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void hil_mlc_clear_di_map(hil_mlc *mlc, int val) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int j; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (j = val; j < 7 ; j++) 928c2ecf20Sopenharmony_ci mlc->di_map[j] = -1; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void hil_mlc_clear_di_scratch(hil_mlc *mlc) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch)); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch)); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int hil_mlc_match_di_scratch(hil_mlc *mlc) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int idx; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { 1108c2ecf20Sopenharmony_ci int j, found = 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* In-use slots are not eligible. */ 1138c2ecf20Sopenharmony_ci for (j = 0; j < 7 ; j++) 1148c2ecf20Sopenharmony_ci if (mlc->di_map[j] == idx) 1158c2ecf20Sopenharmony_ci found++; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (found) 1188c2ecf20Sopenharmony_ci continue; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!memcmp(mlc->di + idx, &mlc->di_scratch, 1218c2ecf20Sopenharmony_ci sizeof(mlc->di_scratch))) 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci return idx >= HIL_MLC_DEVMEM ? -1 : idx; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int hil_mlc_find_free_di(hil_mlc *mlc) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int idx; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* TODO: Pick all-zero slots first, failing that, 1328c2ecf20Sopenharmony_ci * randomize the slot picked among those eligible. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { 1358c2ecf20Sopenharmony_ci int j, found = 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (j = 0; j < 7 ; j++) 1388c2ecf20Sopenharmony_ci if (mlc->di_map[j] == idx) 1398c2ecf20Sopenharmony_ci found++; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!found) 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return idx; /* Note: It is guaranteed at least one above will match */ 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic inline void hil_mlc_clean_serio_map(hil_mlc *mlc) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int idx; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { 1538c2ecf20Sopenharmony_ci int j, found = 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for (j = 0; j < 7 ; j++) 1568c2ecf20Sopenharmony_ci if (mlc->di_map[j] == idx) 1578c2ecf20Sopenharmony_ci found++; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!found) 1608c2ecf20Sopenharmony_ci mlc->serio_map[idx].di_revmap = -1; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void hil_mlc_send_polls(hil_mlc *mlc) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int did, i, cnt; 1678c2ecf20Sopenharmony_ci struct serio *serio; 1688c2ecf20Sopenharmony_ci struct serio_driver *drv; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci i = cnt = 0; 1718c2ecf20Sopenharmony_ci did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8; 1728c2ecf20Sopenharmony_ci serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL; 1738c2ecf20Sopenharmony_ci drv = (serio != NULL) ? serio->drv : NULL; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci while (mlc->icount < 15 - i) { 1768c2ecf20Sopenharmony_ci hil_packet p; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci p = mlc->ipacket[i]; 1798c2ecf20Sopenharmony_ci if (did != (p & HIL_PKT_ADDR_MASK) >> 8) { 1808c2ecf20Sopenharmony_ci if (drv && drv->interrupt) { 1818c2ecf20Sopenharmony_ci drv->interrupt(serio, 0, 0); 1828c2ecf20Sopenharmony_ci drv->interrupt(serio, HIL_ERR_INT >> 16, 0); 1838c2ecf20Sopenharmony_ci drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); 1848c2ecf20Sopenharmony_ci drv->interrupt(serio, HIL_CMD_POL + cnt, 0); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci did = (p & HIL_PKT_ADDR_MASK) >> 8; 1888c2ecf20Sopenharmony_ci serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL; 1898c2ecf20Sopenharmony_ci drv = (serio != NULL) ? serio->drv : NULL; 1908c2ecf20Sopenharmony_ci cnt = 0; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci cnt++; 1948c2ecf20Sopenharmony_ci i++; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (drv && drv->interrupt) { 1978c2ecf20Sopenharmony_ci drv->interrupt(serio, (p >> 24), 0); 1988c2ecf20Sopenharmony_ci drv->interrupt(serio, (p >> 16) & 0xff, 0); 1998c2ecf20Sopenharmony_ci drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0); 2008c2ecf20Sopenharmony_ci drv->interrupt(serio, p & 0xff, 0); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/*************************** State engine *********************************/ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#define HILSEN_SCHED 0x000100 /* Schedule the tasklet */ 2088c2ecf20Sopenharmony_ci#define HILSEN_BREAK 0x000200 /* Wait until next pass */ 2098c2ecf20Sopenharmony_ci#define HILSEN_UP 0x000400 /* relative node#, decrement */ 2108c2ecf20Sopenharmony_ci#define HILSEN_DOWN 0x000800 /* relative node#, increment */ 2118c2ecf20Sopenharmony_ci#define HILSEN_FOLLOW 0x001000 /* use retval as next node# */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#define HILSEN_MASK 0x0000ff 2148c2ecf20Sopenharmony_ci#define HILSEN_START 0 2158c2ecf20Sopenharmony_ci#define HILSEN_RESTART 1 2168c2ecf20Sopenharmony_ci#define HILSEN_DHR 9 2178c2ecf20Sopenharmony_ci#define HILSEN_DHR2 10 2188c2ecf20Sopenharmony_ci#define HILSEN_IFC 14 2198c2ecf20Sopenharmony_ci#define HILSEN_HEAL0 16 2208c2ecf20Sopenharmony_ci#define HILSEN_HEAL 18 2218c2ecf20Sopenharmony_ci#define HILSEN_ACF 21 2228c2ecf20Sopenharmony_ci#define HILSEN_ACF2 22 2238c2ecf20Sopenharmony_ci#define HILSEN_DISC0 25 2248c2ecf20Sopenharmony_ci#define HILSEN_DISC 27 2258c2ecf20Sopenharmony_ci#define HILSEN_MATCH 40 2268c2ecf20Sopenharmony_ci#define HILSEN_OPERATE 41 2278c2ecf20Sopenharmony_ci#define HILSEN_PROBE 44 2288c2ecf20Sopenharmony_ci#define HILSEN_DSR 52 2298c2ecf20Sopenharmony_ci#define HILSEN_REPOLL 55 2308c2ecf20Sopenharmony_ci#define HILSEN_IFCACF 58 2318c2ecf20Sopenharmony_ci#define HILSEN_END 60 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define HILSEN_NEXT (HILSEN_DOWN | 1) 2348c2ecf20Sopenharmony_ci#define HILSEN_SAME (HILSEN_DOWN | 0) 2358c2ecf20Sopenharmony_ci#define HILSEN_LAST (HILSEN_UP | 1) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#define HILSEN_DOZE (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK) 2388c2ecf20Sopenharmony_ci#define HILSEN_SLEEP (HILSEN_SAME | HILSEN_BREAK) 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int hilse_match(hil_mlc *mlc, int unused) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int rc; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci rc = hil_mlc_match_di_scratch(mlc); 2458c2ecf20Sopenharmony_ci if (rc == -1) { 2468c2ecf20Sopenharmony_ci rc = hil_mlc_find_free_di(mlc); 2478c2ecf20Sopenharmony_ci if (rc == -1) 2488c2ecf20Sopenharmony_ci goto err; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#ifdef HIL_MLC_DEBUG 2518c2ecf20Sopenharmony_ci printk(KERN_DEBUG PREFIX "new in slot %i\n", rc); 2528c2ecf20Sopenharmony_ci#endif 2538c2ecf20Sopenharmony_ci hil_mlc_copy_di_scratch(mlc, rc); 2548c2ecf20Sopenharmony_ci mlc->di_map[mlc->ddi] = rc; 2558c2ecf20Sopenharmony_ci mlc->serio_map[rc].di_revmap = mlc->ddi; 2568c2ecf20Sopenharmony_ci hil_mlc_clean_serio_map(mlc); 2578c2ecf20Sopenharmony_ci serio_rescan(mlc->serio[rc]); 2588c2ecf20Sopenharmony_ci return -1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mlc->di_map[mlc->ddi] = rc; 2628c2ecf20Sopenharmony_ci#ifdef HIL_MLC_DEBUG 2638c2ecf20Sopenharmony_ci printk(KERN_DEBUG PREFIX "same in slot %i\n", rc); 2648c2ecf20Sopenharmony_ci#endif 2658c2ecf20Sopenharmony_ci mlc->serio_map[rc].di_revmap = mlc->ddi; 2668c2ecf20Sopenharmony_ci hil_mlc_clean_serio_map(mlc); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci err: 2708c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n"); 2718c2ecf20Sopenharmony_ci return 1; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */ 2758c2ecf20Sopenharmony_cistatic int hilse_init_lcv(hil_mlc *mlc, int unused) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci time64_t now = ktime_get_seconds(); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (mlc->lcv && (now - mlc->lcv_time) < 5) 2808c2ecf20Sopenharmony_ci return -1; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci mlc->lcv_time = now; 2838c2ecf20Sopenharmony_ci mlc->lcv = 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int hilse_inc_lcv(hil_mlc *mlc, int lim) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci return mlc->lcv++ >= lim ? -1 : 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#if 0 2948c2ecf20Sopenharmony_cistatic int hilse_set_lcv(hil_mlc *mlc, int val) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci mlc->lcv = val; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci#endif 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* Management of the discovered device index (zero based, -1 means no devs) */ 3038c2ecf20Sopenharmony_cistatic int hilse_set_ddi(hil_mlc *mlc, int val) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci mlc->ddi = val; 3068c2ecf20Sopenharmony_ci hil_mlc_clear_di_map(mlc, val + 1); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int hilse_dec_ddi(hil_mlc *mlc, int unused) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci mlc->ddi--; 3148c2ecf20Sopenharmony_ci if (mlc->ddi <= -1) { 3158c2ecf20Sopenharmony_ci mlc->ddi = -1; 3168c2ecf20Sopenharmony_ci hil_mlc_clear_di_map(mlc, 0); 3178c2ecf20Sopenharmony_ci return -1; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci hil_mlc_clear_di_map(mlc, mlc->ddi + 1); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int hilse_inc_ddi(hil_mlc *mlc, int unused) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci BUG_ON(mlc->ddi >= 6); 3278c2ecf20Sopenharmony_ci mlc->ddi++; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int hilse_take_idd(hil_mlc *mlc, int unused) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int i; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Help the state engine: 3378c2ecf20Sopenharmony_ci * Is this a real IDD response or just an echo? 3388c2ecf20Sopenharmony_ci * 3398c2ecf20Sopenharmony_ci * Real IDD response does not start with a command. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (mlc->ipacket[0] & HIL_PKT_CMD) 3428c2ecf20Sopenharmony_ci goto bail; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Should have the command echoed further down. */ 3458c2ecf20Sopenharmony_ci for (i = 1; i < 16; i++) { 3468c2ecf20Sopenharmony_ci if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == 3478c2ecf20Sopenharmony_ci (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) && 3488c2ecf20Sopenharmony_ci (mlc->ipacket[i] & HIL_PKT_CMD) && 3498c2ecf20Sopenharmony_ci ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD)) 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci if (i > 15) 3538c2ecf20Sopenharmony_ci goto bail; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* And the rest of the packets should still be clear. */ 3568c2ecf20Sopenharmony_ci while (++i < 16) 3578c2ecf20Sopenharmony_ci if (mlc->ipacket[i]) 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (i < 16) 3618c2ecf20Sopenharmony_ci goto bail; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 3648c2ecf20Sopenharmony_ci mlc->di_scratch.idd[i] = 3658c2ecf20Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Next step is to see if RSC supported */ 3688c2ecf20Sopenharmony_ci if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) 3698c2ecf20Sopenharmony_ci return HILSEN_NEXT; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 3728c2ecf20Sopenharmony_ci return HILSEN_DOWN | 4; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci bail: 3778c2ecf20Sopenharmony_ci mlc->ddi--; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return -1; /* This should send us off to ACF */ 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int hilse_take_rsc(hil_mlc *mlc, int unused) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int i; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 3878c2ecf20Sopenharmony_ci mlc->di_scratch.rsc[i] = 3888c2ecf20Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Next step is to see if EXD supported (IDD has already been read) */ 3918c2ecf20Sopenharmony_ci if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 3928c2ecf20Sopenharmony_ci return HILSEN_NEXT; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int hilse_take_exd(hil_mlc *mlc, int unused) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci int i; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 4028c2ecf20Sopenharmony_ci mlc->di_scratch.exd[i] = 4038c2ecf20Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Next step is to see if RNM supported. */ 4068c2ecf20Sopenharmony_ci if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) 4078c2ecf20Sopenharmony_ci return HILSEN_NEXT; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int hilse_take_rnm(hil_mlc *mlc, int unused) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci int i; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 4178c2ecf20Sopenharmony_ci mlc->di_scratch.rnm[i] = 4188c2ecf20Sopenharmony_ci mlc->ipacket[i] & HIL_PKT_DATA_MASK; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "Device name gotten: %16s\n", 4218c2ecf20Sopenharmony_ci mlc->di_scratch.rnm); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int hilse_operate(hil_mlc *mlc, int repoll) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (mlc->opercnt == 0) 4308c2ecf20Sopenharmony_ci hil_mlcs_probe = 0; 4318c2ecf20Sopenharmony_ci mlc->opercnt = 1; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci hil_mlc_send_polls(mlc); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (!hil_mlcs_probe) 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci hil_mlcs_probe = 0; 4388c2ecf20Sopenharmony_ci mlc->opercnt = 0; 4398c2ecf20Sopenharmony_ci return 1; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \ 4438c2ecf20Sopenharmony_ci{ HILSE_FUNC, { .func = funct }, funct_arg, zero_rc, neg_rc, pos_rc }, 4448c2ecf20Sopenharmony_ci#define OUT(pack) \ 4458c2ecf20Sopenharmony_ci{ HILSE_OUT, { .packet = pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 }, 4468c2ecf20Sopenharmony_ci#define CTS \ 4478c2ecf20Sopenharmony_ci{ HILSE_CTS, { .packet = 0 }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 }, 4488c2ecf20Sopenharmony_ci#define EXPECT(comp, to, got, got_wrong, timed_out) \ 4498c2ecf20Sopenharmony_ci{ HILSE_EXPECT, { .packet = comp }, to, got, got_wrong, timed_out }, 4508c2ecf20Sopenharmony_ci#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \ 4518c2ecf20Sopenharmony_ci{ HILSE_EXPECT_LAST, { .packet = comp }, to, got, got_wrong, timed_out }, 4528c2ecf20Sopenharmony_ci#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \ 4538c2ecf20Sopenharmony_ci{ HILSE_EXPECT_DISC, { .packet = comp }, to, got, got_wrong, timed_out }, 4548c2ecf20Sopenharmony_ci#define IN(to, got, got_error, timed_out) \ 4558c2ecf20Sopenharmony_ci{ HILSE_IN, { .packet = 0 }, to, got, got_error, timed_out }, 4568c2ecf20Sopenharmony_ci#define OUT_DISC(pack) \ 4578c2ecf20Sopenharmony_ci{ HILSE_OUT_DISC, { .packet = pack }, 0, 0, 0, 0 }, 4588c2ecf20Sopenharmony_ci#define OUT_LAST(pack) \ 4598c2ecf20Sopenharmony_ci{ HILSE_OUT_LAST, { .packet = pack }, 0, 0, 0, 0 }, 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic const struct hilse_node hil_mlc_se[HILSEN_END] = { 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* 0 HILSEN_START */ 4648c2ecf20Sopenharmony_ci FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* 1 HILSEN_RESTART */ 4678c2ecf20Sopenharmony_ci FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) 4688c2ecf20Sopenharmony_ci OUT(HIL_CTRL_ONLY) /* Disable APE */ 4698c2ecf20Sopenharmony_ci CTS 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci#define TEST_PACKET(x) \ 4728c2ecf20Sopenharmony_ci(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x) 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5)) 4758c2ecf20Sopenharmony_ci EXPECT(HIL_ERR_INT | TEST_PACKET(0x5), 4768c2ecf20Sopenharmony_ci 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) 4778c2ecf20Sopenharmony_ci OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa)) 4788c2ecf20Sopenharmony_ci EXPECT(HIL_ERR_INT | TEST_PACKET(0xa), 4798c2ecf20Sopenharmony_ci 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) 4808c2ecf20Sopenharmony_ci OUT(HIL_CTRL_ONLY | 0) /* Disable test mode */ 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* 9 HILSEN_DHR */ 4838c2ecf20Sopenharmony_ci FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 10 HILSEN_DHR2 */ 4868c2ecf20Sopenharmony_ci FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) 4878c2ecf20Sopenharmony_ci FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) 4888c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_DHR) 4898c2ecf20Sopenharmony_ci IN(300000, HILSEN_DHR2, HILSEN_DHR2, HILSEN_NEXT) 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* 14 HILSEN_IFC */ 4928c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_IFC) 4938c2ecf20Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, 4948c2ecf20Sopenharmony_ci 20000, HILSEN_DISC, HILSEN_DHR2, HILSEN_NEXT ) 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* If devices are there, they weren't in PUP or other loopback mode. 4978c2ecf20Sopenharmony_ci * We're more concerned at this point with restoring operation 4988c2ecf20Sopenharmony_ci * to devices than discovering new ones, so we try to salvage 4998c2ecf20Sopenharmony_ci * the loop configuration by closing off the loop. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 16 HILSEN_HEAL0 */ 5038c2ecf20Sopenharmony_ci FUNC(hilse_dec_ddi, 0, HILSEN_NEXT, HILSEN_ACF, 0) 5048c2ecf20Sopenharmony_ci FUNC(hilse_inc_ddi, 0, HILSEN_NEXT, 0, 0) 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 18 HILSEN_HEAL */ 5078c2ecf20Sopenharmony_ci OUT_LAST(HIL_CMD_ELB) 5088c2ecf20Sopenharmony_ci EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, 5098c2ecf20Sopenharmony_ci 20000, HILSEN_REPOLL, HILSEN_DSR, HILSEN_NEXT) 5108c2ecf20Sopenharmony_ci FUNC(hilse_dec_ddi, 0, HILSEN_HEAL, HILSEN_NEXT, 0) 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* 21 HILSEN_ACF */ 5138c2ecf20Sopenharmony_ci FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_DOZE, 0) 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 22 HILSEN_ACF2 */ 5168c2ecf20Sopenharmony_ci FUNC(hilse_inc_lcv, 10, HILSEN_NEXT, HILSEN_START, 0) 5178c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) 5188c2ecf20Sopenharmony_ci IN(20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* 25 HILSEN_DISC0 */ 5218c2ecf20Sopenharmony_ci OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) 5228c2ecf20Sopenharmony_ci EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT, 5238c2ecf20Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Only enter here if response just received */ 5268c2ecf20Sopenharmony_ci /* 27 HILSEN_DISC */ 5278c2ecf20Sopenharmony_ci OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD) 5288c2ecf20Sopenharmony_ci EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT, 5298c2ecf20Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_START) 5308c2ecf20Sopenharmony_ci FUNC(hilse_inc_ddi, 0, HILSEN_NEXT, HILSEN_START, 0) 5318c2ecf20Sopenharmony_ci FUNC(hilse_take_idd, 0, HILSEN_MATCH, HILSEN_IFCACF, HILSEN_FOLLOW) 5328c2ecf20Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC) 5338c2ecf20Sopenharmony_ci EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT, 5348c2ecf20Sopenharmony_ci 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 5358c2ecf20Sopenharmony_ci FUNC(hilse_take_rsc, 0, HILSEN_MATCH, 0, HILSEN_FOLLOW) 5368c2ecf20Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD) 5378c2ecf20Sopenharmony_ci EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT, 5388c2ecf20Sopenharmony_ci 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 5398c2ecf20Sopenharmony_ci FUNC(hilse_take_exd, 0, HILSEN_MATCH, 0, HILSEN_FOLLOW) 5408c2ecf20Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM) 5418c2ecf20Sopenharmony_ci EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT, 5428c2ecf20Sopenharmony_ci 30000, HILSEN_NEXT, HILSEN_DSR, HILSEN_DSR) 5438c2ecf20Sopenharmony_ci FUNC(hilse_take_rnm, 0, HILSEN_MATCH, 0, 0) 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* 40 HILSEN_MATCH */ 5468c2ecf20Sopenharmony_ci FUNC(hilse_match, 0, HILSEN_NEXT, HILSEN_NEXT, /* TODO */ 0) 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 41 HILSEN_OPERATE */ 5498c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_POL) 5508c2ecf20Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT, 5518c2ecf20Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) 5528c2ecf20Sopenharmony_ci FUNC(hilse_operate, 0, HILSEN_OPERATE, HILSEN_IFC, HILSEN_NEXT) 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* 44 HILSEN_PROBE */ 5558c2ecf20Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT) 5568c2ecf20Sopenharmony_ci IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) 5578c2ecf20Sopenharmony_ci OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) 5588c2ecf20Sopenharmony_ci IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) 5598c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) 5608c2ecf20Sopenharmony_ci IN(10000, HILSEN_DISC0, HILSEN_DSR, HILSEN_NEXT) 5618c2ecf20Sopenharmony_ci OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB) 5628c2ecf20Sopenharmony_ci IN(10000, HILSEN_OPERATE, HILSEN_DSR, HILSEN_DSR) 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* 52 HILSEN_DSR */ 5658c2ecf20Sopenharmony_ci FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) 5668c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_DSR) 5678c2ecf20Sopenharmony_ci IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC) 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* 55 HILSEN_REPOLL */ 5708c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_RPL) 5718c2ecf20Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT, 5728c2ecf20Sopenharmony_ci 20000, HILSEN_NEXT, HILSEN_DSR, HILSEN_NEXT) 5738c2ecf20Sopenharmony_ci FUNC(hilse_operate, 1, HILSEN_OPERATE, HILSEN_IFC, HILSEN_PROBE) 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* 58 HILSEN_IFCACF */ 5768c2ecf20Sopenharmony_ci OUT(HIL_PKT_CMD | HIL_CMD_IFC) 5778c2ecf20Sopenharmony_ci EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, 5788c2ecf20Sopenharmony_ci 20000, HILSEN_ACF2, HILSEN_DHR2, HILSEN_HEAL) 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* 60 HILSEN_END */ 5818c2ecf20Sopenharmony_ci}; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci switch (node->act) { 5878c2ecf20Sopenharmony_ci case HILSE_EXPECT_DISC: 5888c2ecf20Sopenharmony_ci mlc->imatch = node->object.packet; 5898c2ecf20Sopenharmony_ci mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci case HILSE_EXPECT_LAST: 5928c2ecf20Sopenharmony_ci mlc->imatch = node->object.packet; 5938c2ecf20Sopenharmony_ci mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci case HILSE_EXPECT: 5968c2ecf20Sopenharmony_ci mlc->imatch = node->object.packet; 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case HILSE_IN: 5998c2ecf20Sopenharmony_ci mlc->imatch = 0; 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci BUG(); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci mlc->istarted = 1; 6058c2ecf20Sopenharmony_ci mlc->intimeout = usecs_to_jiffies(node->arg); 6068c2ecf20Sopenharmony_ci mlc->instart = jiffies; 6078c2ecf20Sopenharmony_ci mlc->icount = 15; 6088c2ecf20Sopenharmony_ci memset(mlc->ipacket, 0, 16 * sizeof(hil_packet)); 6098c2ecf20Sopenharmony_ci BUG_ON(down_trylock(&mlc->isem)); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci#ifdef HIL_MLC_DEBUG 6138c2ecf20Sopenharmony_cistatic int doze; 6148c2ecf20Sopenharmony_cistatic int seidx; /* For debug */ 6158c2ecf20Sopenharmony_ci#endif 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int hilse_donode(hil_mlc *mlc) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci const struct hilse_node *node; 6208c2ecf20Sopenharmony_ci int nextidx = 0; 6218c2ecf20Sopenharmony_ci int sched_long = 0; 6228c2ecf20Sopenharmony_ci unsigned long flags; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci#ifdef HIL_MLC_DEBUG 6258c2ecf20Sopenharmony_ci if (mlc->seidx && mlc->seidx != seidx && 6268c2ecf20Sopenharmony_ci mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) { 6278c2ecf20Sopenharmony_ci printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx); 6288c2ecf20Sopenharmony_ci doze = 0; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci seidx = mlc->seidx; 6328c2ecf20Sopenharmony_ci#endif 6338c2ecf20Sopenharmony_ci node = hil_mlc_se + mlc->seidx; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci switch (node->act) { 6368c2ecf20Sopenharmony_ci int rc; 6378c2ecf20Sopenharmony_ci hil_packet pack; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci case HILSE_FUNC: 6408c2ecf20Sopenharmony_ci BUG_ON(node->object.func == NULL); 6418c2ecf20Sopenharmony_ci rc = node->object.func(mlc, node->arg); 6428c2ecf20Sopenharmony_ci nextidx = (rc > 0) ? node->ugly : 6438c2ecf20Sopenharmony_ci ((rc < 0) ? node->bad : node->good); 6448c2ecf20Sopenharmony_ci if (nextidx == HILSEN_FOLLOW) 6458c2ecf20Sopenharmony_ci nextidx = rc; 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci case HILSE_EXPECT_LAST: 6498c2ecf20Sopenharmony_ci case HILSE_EXPECT_DISC: 6508c2ecf20Sopenharmony_ci case HILSE_EXPECT: 6518c2ecf20Sopenharmony_ci case HILSE_IN: 6528c2ecf20Sopenharmony_ci /* Already set up from previous HILSE_OUT_* */ 6538c2ecf20Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 6548c2ecf20Sopenharmony_ci rc = mlc->in(mlc, node->arg); 6558c2ecf20Sopenharmony_ci if (rc == 2) { 6568c2ecf20Sopenharmony_ci nextidx = HILSEN_DOZE; 6578c2ecf20Sopenharmony_ci sched_long = 1; 6588c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci if (rc == 1) 6628c2ecf20Sopenharmony_ci nextidx = node->ugly; 6638c2ecf20Sopenharmony_ci else if (rc == 0) 6648c2ecf20Sopenharmony_ci nextidx = node->good; 6658c2ecf20Sopenharmony_ci else 6668c2ecf20Sopenharmony_ci nextidx = node->bad; 6678c2ecf20Sopenharmony_ci mlc->istarted = 0; 6688c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci case HILSE_OUT_LAST: 6728c2ecf20Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 6738c2ecf20Sopenharmony_ci pack = node->object.packet; 6748c2ecf20Sopenharmony_ci pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); 6758c2ecf20Sopenharmony_ci goto out; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci case HILSE_OUT_DISC: 6788c2ecf20Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 6798c2ecf20Sopenharmony_ci pack = node->object.packet; 6808c2ecf20Sopenharmony_ci pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); 6818c2ecf20Sopenharmony_ci goto out; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci case HILSE_OUT: 6848c2ecf20Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 6858c2ecf20Sopenharmony_ci pack = node->object.packet; 6868c2ecf20Sopenharmony_ci out: 6878c2ecf20Sopenharmony_ci if (!mlc->istarted) { 6888c2ecf20Sopenharmony_ci /* Prepare to receive input */ 6898c2ecf20Sopenharmony_ci if ((node + 1)->act & HILSE_IN) 6908c2ecf20Sopenharmony_ci hilse_setup_input(mlc, node + 1); 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (down_trylock(&mlc->osem)) { 6968c2ecf20Sopenharmony_ci nextidx = HILSEN_DOZE; 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci up(&mlc->osem); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 7028c2ecf20Sopenharmony_ci if (!mlc->ostarted) { 7038c2ecf20Sopenharmony_ci mlc->ostarted = 1; 7048c2ecf20Sopenharmony_ci mlc->opacket = pack; 7058c2ecf20Sopenharmony_ci rc = mlc->out(mlc); 7068c2ecf20Sopenharmony_ci nextidx = HILSEN_DOZE; 7078c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 7088c2ecf20Sopenharmony_ci if (rc) { 7098c2ecf20Sopenharmony_ci hil_mlc_stop = 1; 7108c2ecf20Sopenharmony_ci return 1; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci mlc->ostarted = 0; 7158c2ecf20Sopenharmony_ci mlc->instart = jiffies; 7168c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 7178c2ecf20Sopenharmony_ci nextidx = HILSEN_NEXT; 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci case HILSE_CTS: 7218c2ecf20Sopenharmony_ci write_lock_irqsave(&mlc->lock, flags); 7228c2ecf20Sopenharmony_ci rc = mlc->cts(mlc); 7238c2ecf20Sopenharmony_ci nextidx = rc ? node->bad : node->good; 7248c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mlc->lock, flags); 7258c2ecf20Sopenharmony_ci if (rc) { 7268c2ecf20Sopenharmony_ci hil_mlc_stop = 1; 7278c2ecf20Sopenharmony_ci return 1; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci default: 7328c2ecf20Sopenharmony_ci BUG(); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci#ifdef HIL_MLC_DEBUG 7368c2ecf20Sopenharmony_ci if (nextidx == HILSEN_DOZE) 7378c2ecf20Sopenharmony_ci doze++; 7388c2ecf20Sopenharmony_ci#endif 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci while (nextidx & HILSEN_SCHED) { 7418c2ecf20Sopenharmony_ci unsigned long now = jiffies; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (!sched_long) 7448c2ecf20Sopenharmony_ci goto sched; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (time_after(now, mlc->instart + mlc->intimeout)) 7478c2ecf20Sopenharmony_ci goto sched; 7488c2ecf20Sopenharmony_ci mod_timer(&hil_mlcs_kicker, mlc->instart + mlc->intimeout); 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci sched: 7518c2ecf20Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 7528c2ecf20Sopenharmony_ci break; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (nextidx & HILSEN_DOWN) 7568c2ecf20Sopenharmony_ci mlc->seidx += nextidx & HILSEN_MASK; 7578c2ecf20Sopenharmony_ci else if (nextidx & HILSEN_UP) 7588c2ecf20Sopenharmony_ci mlc->seidx -= nextidx & HILSEN_MASK; 7598c2ecf20Sopenharmony_ci else 7608c2ecf20Sopenharmony_ci mlc->seidx = nextidx & HILSEN_MASK; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (nextidx & HILSEN_BREAK) 7638c2ecf20Sopenharmony_ci return 1; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/******************** tasklet context functions **************************/ 7698c2ecf20Sopenharmony_cistatic void hil_mlcs_process(unsigned long unused) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct list_head *tmp; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci read_lock(&hil_mlcs_lock); 7748c2ecf20Sopenharmony_ci list_for_each(tmp, &hil_mlcs) { 7758c2ecf20Sopenharmony_ci struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list); 7768c2ecf20Sopenharmony_ci while (hilse_donode(mlc) == 0) { 7778c2ecf20Sopenharmony_ci#ifdef HIL_MLC_DEBUG 7788c2ecf20Sopenharmony_ci if (mlc->seidx != 41 && 7798c2ecf20Sopenharmony_ci mlc->seidx != 42 && 7808c2ecf20Sopenharmony_ci mlc->seidx != 43) 7818c2ecf20Sopenharmony_ci printk(KERN_DEBUG PREFIX " + "); 7828c2ecf20Sopenharmony_ci#endif 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci read_unlock(&hil_mlcs_lock); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci/************************* Keepalive timer task *********************/ 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic void hil_mlcs_timer(struct timer_list *unused) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci if (hil_mlc_stop) { 7938c2ecf20Sopenharmony_ci /* could not send packet - stop immediately. */ 7948c2ecf20Sopenharmony_ci pr_warn(PREFIX "HIL seems stuck - Disabling HIL MLC.\n"); 7958c2ecf20Sopenharmony_ci return; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci hil_mlcs_probe = 1; 7998c2ecf20Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 8008c2ecf20Sopenharmony_ci /* Re-insert the periodic task. */ 8018c2ecf20Sopenharmony_ci if (!timer_pending(&hil_mlcs_kicker)) 8028c2ecf20Sopenharmony_ci mod_timer(&hil_mlcs_kicker, jiffies + HZ); 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci/******************** user/kernel context functions **********************/ 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic int hil_mlc_serio_write(struct serio *serio, unsigned char c) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct hil_mlc_serio_map *map; 8108c2ecf20Sopenharmony_ci struct hil_mlc *mlc; 8118c2ecf20Sopenharmony_ci struct serio_driver *drv; 8128c2ecf20Sopenharmony_ci uint8_t *idx, *last; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci map = serio->port_data; 8158c2ecf20Sopenharmony_ci BUG_ON(map == NULL); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci mlc = map->mlc; 8188c2ecf20Sopenharmony_ci BUG_ON(mlc == NULL); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci mlc->serio_opacket[map->didx] |= 8218c2ecf20Sopenharmony_ci ((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx])); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (mlc->serio_oidx[map->didx] >= 3) { 8248c2ecf20Sopenharmony_ci /* for now only commands */ 8258c2ecf20Sopenharmony_ci if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) 8268c2ecf20Sopenharmony_ci return -EIO; 8278c2ecf20Sopenharmony_ci switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) { 8288c2ecf20Sopenharmony_ci case HIL_CMD_IDD: 8298c2ecf20Sopenharmony_ci idx = mlc->di[map->didx].idd; 8308c2ecf20Sopenharmony_ci goto emu; 8318c2ecf20Sopenharmony_ci case HIL_CMD_RSC: 8328c2ecf20Sopenharmony_ci idx = mlc->di[map->didx].rsc; 8338c2ecf20Sopenharmony_ci goto emu; 8348c2ecf20Sopenharmony_ci case HIL_CMD_EXD: 8358c2ecf20Sopenharmony_ci idx = mlc->di[map->didx].exd; 8368c2ecf20Sopenharmony_ci goto emu; 8378c2ecf20Sopenharmony_ci case HIL_CMD_RNM: 8388c2ecf20Sopenharmony_ci idx = mlc->di[map->didx].rnm; 8398c2ecf20Sopenharmony_ci goto emu; 8408c2ecf20Sopenharmony_ci default: 8418c2ecf20Sopenharmony_ci break; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci mlc->serio_oidx[map->didx] = 0; 8448c2ecf20Sopenharmony_ci mlc->serio_opacket[map->didx] = 0; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci mlc->serio_oidx[map->didx]++; 8488c2ecf20Sopenharmony_ci return -EIO; 8498c2ecf20Sopenharmony_ci emu: 8508c2ecf20Sopenharmony_ci drv = serio->drv; 8518c2ecf20Sopenharmony_ci BUG_ON(drv == NULL); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci last = idx + 15; 8548c2ecf20Sopenharmony_ci while ((last != idx) && (*last == 0)) 8558c2ecf20Sopenharmony_ci last--; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci while (idx != last) { 8588c2ecf20Sopenharmony_ci drv->interrupt(serio, 0, 0); 8598c2ecf20Sopenharmony_ci drv->interrupt(serio, HIL_ERR_INT >> 16, 0); 8608c2ecf20Sopenharmony_ci drv->interrupt(serio, 0, 0); 8618c2ecf20Sopenharmony_ci drv->interrupt(serio, *idx, 0); 8628c2ecf20Sopenharmony_ci idx++; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci drv->interrupt(serio, 0, 0); 8658c2ecf20Sopenharmony_ci drv->interrupt(serio, HIL_ERR_INT >> 16, 0); 8668c2ecf20Sopenharmony_ci drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); 8678c2ecf20Sopenharmony_ci drv->interrupt(serio, *idx, 0); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci mlc->serio_oidx[map->didx] = 0; 8708c2ecf20Sopenharmony_ci mlc->serio_opacket[map->didx] = 0; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int hil_mlc_serio_open(struct serio *serio) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct hil_mlc_serio_map *map; 8788c2ecf20Sopenharmony_ci struct hil_mlc *mlc; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (serio_get_drvdata(serio) != NULL) 8818c2ecf20Sopenharmony_ci return -EBUSY; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci map = serio->port_data; 8848c2ecf20Sopenharmony_ci BUG_ON(map == NULL); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci mlc = map->mlc; 8878c2ecf20Sopenharmony_ci BUG_ON(mlc == NULL); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci return 0; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic void hil_mlc_serio_close(struct serio *serio) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct hil_mlc_serio_map *map; 8958c2ecf20Sopenharmony_ci struct hil_mlc *mlc; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci map = serio->port_data; 8988c2ecf20Sopenharmony_ci BUG_ON(map == NULL); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci mlc = map->mlc; 9018c2ecf20Sopenharmony_ci BUG_ON(mlc == NULL); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 9048c2ecf20Sopenharmony_ci serio->drv = NULL; 9058c2ecf20Sopenharmony_ci /* TODO wake up interruptable */ 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic const struct serio_device_id hil_mlc_serio_id = { 9098c2ecf20Sopenharmony_ci .type = SERIO_HIL_MLC, 9108c2ecf20Sopenharmony_ci .proto = SERIO_HIL, 9118c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 9128c2ecf20Sopenharmony_ci .id = SERIO_ANY, 9138c2ecf20Sopenharmony_ci}; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ciint hil_mlc_register(hil_mlc *mlc) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci int i; 9188c2ecf20Sopenharmony_ci unsigned long flags; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci BUG_ON(mlc == NULL); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci mlc->istarted = 0; 9238c2ecf20Sopenharmony_ci mlc->ostarted = 0; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci rwlock_init(&mlc->lock); 9268c2ecf20Sopenharmony_ci sema_init(&mlc->osem, 1); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci sema_init(&mlc->isem, 1); 9298c2ecf20Sopenharmony_ci mlc->icount = -1; 9308c2ecf20Sopenharmony_ci mlc->imatch = 0; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci mlc->opercnt = 0; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci sema_init(&(mlc->csem), 0); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci hil_mlc_clear_di_scratch(mlc); 9378c2ecf20Sopenharmony_ci hil_mlc_clear_di_map(mlc, 0); 9388c2ecf20Sopenharmony_ci for (i = 0; i < HIL_MLC_DEVMEM; i++) { 9398c2ecf20Sopenharmony_ci struct serio *mlc_serio; 9408c2ecf20Sopenharmony_ci hil_mlc_copy_di_scratch(mlc, i); 9418c2ecf20Sopenharmony_ci mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); 9428c2ecf20Sopenharmony_ci mlc->serio[i] = mlc_serio; 9438c2ecf20Sopenharmony_ci if (!mlc->serio[i]) { 9448c2ecf20Sopenharmony_ci for (; i >= 0; i--) 9458c2ecf20Sopenharmony_ci kfree(mlc->serio[i]); 9468c2ecf20Sopenharmony_ci return -ENOMEM; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i); 9498c2ecf20Sopenharmony_ci snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i); 9508c2ecf20Sopenharmony_ci mlc_serio->id = hil_mlc_serio_id; 9518c2ecf20Sopenharmony_ci mlc_serio->id.id = i; /* HIL port no. */ 9528c2ecf20Sopenharmony_ci mlc_serio->write = hil_mlc_serio_write; 9538c2ecf20Sopenharmony_ci mlc_serio->open = hil_mlc_serio_open; 9548c2ecf20Sopenharmony_ci mlc_serio->close = hil_mlc_serio_close; 9558c2ecf20Sopenharmony_ci mlc_serio->port_data = &(mlc->serio_map[i]); 9568c2ecf20Sopenharmony_ci mlc->serio_map[i].mlc = mlc; 9578c2ecf20Sopenharmony_ci mlc->serio_map[i].didx = i; 9588c2ecf20Sopenharmony_ci mlc->serio_map[i].di_revmap = -1; 9598c2ecf20Sopenharmony_ci mlc->serio_opacket[i] = 0; 9608c2ecf20Sopenharmony_ci mlc->serio_oidx[i] = 0; 9618c2ecf20Sopenharmony_ci serio_register_port(mlc_serio); 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci mlc->tasklet = &hil_mlcs_tasklet; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci write_lock_irqsave(&hil_mlcs_lock, flags); 9678c2ecf20Sopenharmony_ci list_add_tail(&mlc->list, &hil_mlcs); 9688c2ecf20Sopenharmony_ci mlc->seidx = HILSEN_START; 9698c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hil_mlcs_lock, flags); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 9728c2ecf20Sopenharmony_ci return 0; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ciint hil_mlc_unregister(hil_mlc *mlc) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct list_head *tmp; 9788c2ecf20Sopenharmony_ci unsigned long flags; 9798c2ecf20Sopenharmony_ci int i; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci BUG_ON(mlc == NULL); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci write_lock_irqsave(&hil_mlcs_lock, flags); 9848c2ecf20Sopenharmony_ci list_for_each(tmp, &hil_mlcs) 9858c2ecf20Sopenharmony_ci if (list_entry(tmp, hil_mlc, list) == mlc) 9868c2ecf20Sopenharmony_ci goto found; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* not found in list */ 9898c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hil_mlcs_lock, flags); 9908c2ecf20Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 9918c2ecf20Sopenharmony_ci return -ENODEV; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci found: 9948c2ecf20Sopenharmony_ci list_del(tmp); 9958c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hil_mlcs_lock, flags); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci for (i = 0; i < HIL_MLC_DEVMEM; i++) { 9988c2ecf20Sopenharmony_ci serio_unregister_port(mlc->serio[i]); 9998c2ecf20Sopenharmony_ci mlc->serio[i] = NULL; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci tasklet_schedule(&hil_mlcs_tasklet); 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/**************************** Module interface *************************/ 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic int __init hil_mlc_init(void) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci timer_setup(&hil_mlcs_kicker, &hil_mlcs_timer, 0); 10118c2ecf20Sopenharmony_ci mod_timer(&hil_mlcs_kicker, jiffies + HZ); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci tasklet_enable(&hil_mlcs_tasklet); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic void __exit hil_mlc_exit(void) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci del_timer_sync(&hil_mlcs_kicker); 10218c2ecf20Sopenharmony_ci tasklet_kill(&hil_mlcs_tasklet); 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cimodule_init(hil_mlc_init); 10258c2ecf20Sopenharmony_cimodule_exit(hil_mlc_exit); 1026