18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * IEEE 1284.3 Parallel port daisy chain and multiplexor code 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License 88c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 98c2ecf20Sopenharmony_ci * 2 of the License, or (at your option) any later version. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * ??-12-1998: Initial implementation. 128c2ecf20Sopenharmony_ci * 31-01-1999: Make port-cloning transparent. 138c2ecf20Sopenharmony_ci * 13-02-1999: Move DeviceID technique from parport_probe. 148c2ecf20Sopenharmony_ci * 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too. 158c2ecf20Sopenharmony_ci * 22-02-2000: Count devices that are actually detected. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Any part of this program may be used in documents licensed under 188c2ecf20Sopenharmony_ci * the GNU Free Documentation License, Version 1.1 or any later version 198c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/parport.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/current.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#undef DEBUG 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct daisydev { 348c2ecf20Sopenharmony_ci struct daisydev *next; 358c2ecf20Sopenharmony_ci struct parport *port; 368c2ecf20Sopenharmony_ci int daisy; 378c2ecf20Sopenharmony_ci int devnum; 388c2ecf20Sopenharmony_ci} *topology = NULL; 398c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(topology_lock); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int numdevs; 428c2ecf20Sopenharmony_cistatic bool daisy_init_done; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Forward-declaration of lower-level functions. */ 458c2ecf20Sopenharmony_cistatic int mux_present(struct parport *port); 468c2ecf20Sopenharmony_cistatic int num_mux_ports(struct parport *port); 478c2ecf20Sopenharmony_cistatic int select_port(struct parport *port); 488c2ecf20Sopenharmony_cistatic int assign_addrs(struct parport *port); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Add a device to the discovered topology. */ 518c2ecf20Sopenharmony_cistatic void add_dev(int devnum, struct parport *port, int daisy) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct daisydev *newdev, **p; 548c2ecf20Sopenharmony_ci newdev = kmalloc(sizeof(struct daisydev), GFP_KERNEL); 558c2ecf20Sopenharmony_ci if (newdev) { 568c2ecf20Sopenharmony_ci newdev->port = port; 578c2ecf20Sopenharmony_ci newdev->daisy = daisy; 588c2ecf20Sopenharmony_ci newdev->devnum = devnum; 598c2ecf20Sopenharmony_ci spin_lock(&topology_lock); 608c2ecf20Sopenharmony_ci for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next) 618c2ecf20Sopenharmony_ci ; 628c2ecf20Sopenharmony_ci newdev->next = *p; 638c2ecf20Sopenharmony_ci *p = newdev; 648c2ecf20Sopenharmony_ci spin_unlock(&topology_lock); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Clone a parport (actually, make an alias). */ 698c2ecf20Sopenharmony_cistatic struct parport *clone_parport(struct parport *real, int muxport) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct parport *extra = parport_register_port(real->base, 728c2ecf20Sopenharmony_ci real->irq, 738c2ecf20Sopenharmony_ci real->dma, 748c2ecf20Sopenharmony_ci real->ops); 758c2ecf20Sopenharmony_ci if (extra) { 768c2ecf20Sopenharmony_ci extra->portnum = real->portnum; 778c2ecf20Sopenharmony_ci extra->physport = real; 788c2ecf20Sopenharmony_ci extra->muxport = muxport; 798c2ecf20Sopenharmony_ci real->slaves[muxport-1] = extra; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return extra; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int daisy_drv_probe(struct pardevice *par_dev) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct device_driver *drv = par_dev->dev.driver; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (strcmp(drv->name, "daisy_drv")) 908c2ecf20Sopenharmony_ci return -ENODEV; 918c2ecf20Sopenharmony_ci if (strcmp(par_dev->name, daisy_dev_name)) 928c2ecf20Sopenharmony_ci return -ENODEV; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct parport_driver daisy_driver = { 988c2ecf20Sopenharmony_ci .name = "daisy_drv", 998c2ecf20Sopenharmony_ci .probe = daisy_drv_probe, 1008c2ecf20Sopenharmony_ci .devmodel = true, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains. 1048c2ecf20Sopenharmony_ci * Return value is number of devices actually detected. */ 1058c2ecf20Sopenharmony_ciint parport_daisy_init(struct parport *port) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int detected = 0; 1088c2ecf20Sopenharmony_ci char *deviceid; 1098c2ecf20Sopenharmony_ci static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" }; 1108c2ecf20Sopenharmony_ci int num_ports; 1118c2ecf20Sopenharmony_ci int i; 1128c2ecf20Sopenharmony_ci int last_try = 0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!daisy_init_done) { 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * flag should be marked true first as 1178c2ecf20Sopenharmony_ci * parport_register_driver() might try to load the low 1188c2ecf20Sopenharmony_ci * level driver which will lead to announcing new ports 1198c2ecf20Sopenharmony_ci * and which will again come back here at 1208c2ecf20Sopenharmony_ci * parport_daisy_init() 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci daisy_init_done = true; 1238c2ecf20Sopenharmony_ci i = parport_register_driver(&daisy_driver); 1248c2ecf20Sopenharmony_ci if (i) { 1258c2ecf20Sopenharmony_ci pr_err("daisy registration failed\n"); 1268c2ecf20Sopenharmony_ci daisy_init_done = false; 1278c2ecf20Sopenharmony_ci return i; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciagain: 1328c2ecf20Sopenharmony_ci /* Because this is called before any other devices exist, 1338c2ecf20Sopenharmony_ci * we don't have to claim exclusive access. */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* If mux present on normal port, need to create new 1368c2ecf20Sopenharmony_ci * parports for each extra port. */ 1378c2ecf20Sopenharmony_ci if (port->muxport < 0 && mux_present(port) && 1388c2ecf20Sopenharmony_ci /* don't be fooled: a mux must have 2 or 4 ports. */ 1398c2ecf20Sopenharmony_ci ((num_ports = num_mux_ports(port)) == 2 || num_ports == 4)) { 1408c2ecf20Sopenharmony_ci /* Leave original as port zero. */ 1418c2ecf20Sopenharmony_ci port->muxport = 0; 1428c2ecf20Sopenharmony_ci pr_info("%s: 1st (default) port of %d-way multiplexor\n", 1438c2ecf20Sopenharmony_ci port->name, num_ports); 1448c2ecf20Sopenharmony_ci for (i = 1; i < num_ports; i++) { 1458c2ecf20Sopenharmony_ci /* Clone the port. */ 1468c2ecf20Sopenharmony_ci struct parport *extra = clone_parport(port, i); 1478c2ecf20Sopenharmony_ci if (!extra) { 1488c2ecf20Sopenharmony_ci if (signal_pending(current)) 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci schedule(); 1528c2ecf20Sopenharmony_ci continue; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pr_info("%s: %d%s port of %d-way multiplexor on %s\n", 1568c2ecf20Sopenharmony_ci extra->name, i + 1, th[i + 1], num_ports, 1578c2ecf20Sopenharmony_ci port->name); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Analyse that port too. We won't recurse 1608c2ecf20Sopenharmony_ci forever because of the 'port->muxport < 0' 1618c2ecf20Sopenharmony_ci test above. */ 1628c2ecf20Sopenharmony_ci parport_daisy_init(extra); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (port->muxport >= 0) 1678c2ecf20Sopenharmony_ci select_port(port); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci parport_daisy_deselect_all(port); 1708c2ecf20Sopenharmony_ci detected += assign_addrs(port); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Count the potential legacy device at the end. */ 1738c2ecf20Sopenharmony_ci add_dev(numdevs++, port, -1); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Find out the legacy device's IEEE 1284 device ID. */ 1768c2ecf20Sopenharmony_ci deviceid = kmalloc(1024, GFP_KERNEL); 1778c2ecf20Sopenharmony_ci if (deviceid) { 1788c2ecf20Sopenharmony_ci if (parport_device_id(numdevs - 1, deviceid, 1024) > 2) 1798c2ecf20Sopenharmony_ci detected++; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci kfree(deviceid); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!detected && !last_try) { 1858c2ecf20Sopenharmony_ci /* No devices were detected. Perhaps they are in some 1868c2ecf20Sopenharmony_ci funny state; let's try to reset them and see if 1878c2ecf20Sopenharmony_ci they wake up. */ 1888c2ecf20Sopenharmony_ci parport_daisy_fini(port); 1898c2ecf20Sopenharmony_ci parport_write_control(port, PARPORT_CONTROL_SELECT); 1908c2ecf20Sopenharmony_ci udelay(50); 1918c2ecf20Sopenharmony_ci parport_write_control(port, 1928c2ecf20Sopenharmony_ci PARPORT_CONTROL_SELECT | 1938c2ecf20Sopenharmony_ci PARPORT_CONTROL_INIT); 1948c2ecf20Sopenharmony_ci udelay(50); 1958c2ecf20Sopenharmony_ci last_try = 1; 1968c2ecf20Sopenharmony_ci goto again; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return detected; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* Forget about devices on a physical port. */ 2038c2ecf20Sopenharmony_civoid parport_daisy_fini(struct parport *port) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct daisydev **p; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci spin_lock(&topology_lock); 2088c2ecf20Sopenharmony_ci p = &topology; 2098c2ecf20Sopenharmony_ci while (*p) { 2108c2ecf20Sopenharmony_ci struct daisydev *dev = *p; 2118c2ecf20Sopenharmony_ci if (dev->port != port) { 2128c2ecf20Sopenharmony_ci p = &dev->next; 2138c2ecf20Sopenharmony_ci continue; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci *p = dev->next; 2168c2ecf20Sopenharmony_ci kfree(dev); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Gaps in the numbering could be handled better. How should 2208c2ecf20Sopenharmony_ci someone enumerate through all IEEE1284.3 devices in the 2218c2ecf20Sopenharmony_ci topology?. */ 2228c2ecf20Sopenharmony_ci if (!topology) numdevs = 0; 2238c2ecf20Sopenharmony_ci spin_unlock(&topology_lock); 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * parport_open - find a device by canonical device number 2298c2ecf20Sopenharmony_ci * @devnum: canonical device number 2308c2ecf20Sopenharmony_ci * @name: name to associate with the device 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * This function is similar to parport_register_device(), except 2338c2ecf20Sopenharmony_ci * that it locates a device by its number rather than by the port 2348c2ecf20Sopenharmony_ci * it is attached to. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * All parameters except for @devnum are the same as for 2378c2ecf20Sopenharmony_ci * parport_register_device(). The return value is the same as 2388c2ecf20Sopenharmony_ci * for parport_register_device(). 2398c2ecf20Sopenharmony_ci **/ 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistruct pardevice *parport_open(int devnum, const char *name) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct daisydev *p = topology; 2448c2ecf20Sopenharmony_ci struct pardev_cb par_cb; 2458c2ecf20Sopenharmony_ci struct parport *port; 2468c2ecf20Sopenharmony_ci struct pardevice *dev; 2478c2ecf20Sopenharmony_ci int daisy; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci memset(&par_cb, 0, sizeof(par_cb)); 2508c2ecf20Sopenharmony_ci spin_lock(&topology_lock); 2518c2ecf20Sopenharmony_ci while (p && p->devnum != devnum) 2528c2ecf20Sopenharmony_ci p = p->next; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!p) { 2558c2ecf20Sopenharmony_ci spin_unlock(&topology_lock); 2568c2ecf20Sopenharmony_ci return NULL; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci daisy = p->daisy; 2608c2ecf20Sopenharmony_ci port = parport_get_port(p->port); 2618c2ecf20Sopenharmony_ci spin_unlock(&topology_lock); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci dev = parport_register_dev_model(port, name, &par_cb, devnum); 2648c2ecf20Sopenharmony_ci parport_put_port(port); 2658c2ecf20Sopenharmony_ci if (!dev) 2668c2ecf20Sopenharmony_ci return NULL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci dev->daisy = daisy; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Check that there really is a device to select. */ 2718c2ecf20Sopenharmony_ci if (daisy >= 0) { 2728c2ecf20Sopenharmony_ci int selected; 2738c2ecf20Sopenharmony_ci parport_claim_or_block(dev); 2748c2ecf20Sopenharmony_ci selected = port->daisy; 2758c2ecf20Sopenharmony_ci parport_release(dev); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (selected != daisy) { 2788c2ecf20Sopenharmony_ci /* No corresponding device. */ 2798c2ecf20Sopenharmony_ci parport_unregister_device(dev); 2808c2ecf20Sopenharmony_ci return NULL; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return dev; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/** 2888c2ecf20Sopenharmony_ci * parport_close - close a device opened with parport_open() 2898c2ecf20Sopenharmony_ci * @dev: device to close 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * This is to parport_open() as parport_unregister_device() is to 2928c2ecf20Sopenharmony_ci * parport_register_device(). 2938c2ecf20Sopenharmony_ci **/ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_civoid parport_close(struct pardevice *dev) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci parport_unregister_device(dev); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* Send a daisy-chain-style CPP command packet. */ 3018c2ecf20Sopenharmony_cistatic int cpp_daisy(struct parport *port, int cmd) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci unsigned char s; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci parport_data_forward(port); 3068c2ecf20Sopenharmony_ci parport_write_data(port, 0xaa); udelay(2); 3078c2ecf20Sopenharmony_ci parport_write_data(port, 0x55); udelay(2); 3088c2ecf20Sopenharmony_ci parport_write_data(port, 0x00); udelay(2); 3098c2ecf20Sopenharmony_ci parport_write_data(port, 0xff); udelay(2); 3108c2ecf20Sopenharmony_ci s = parport_read_status(port) & (PARPORT_STATUS_BUSY 3118c2ecf20Sopenharmony_ci | PARPORT_STATUS_PAPEROUT 3128c2ecf20Sopenharmony_ci | PARPORT_STATUS_SELECT 3138c2ecf20Sopenharmony_ci | PARPORT_STATUS_ERROR); 3148c2ecf20Sopenharmony_ci if (s != (PARPORT_STATUS_BUSY 3158c2ecf20Sopenharmony_ci | PARPORT_STATUS_PAPEROUT 3168c2ecf20Sopenharmony_ci | PARPORT_STATUS_SELECT 3178c2ecf20Sopenharmony_ci | PARPORT_STATUS_ERROR)) { 3188c2ecf20Sopenharmony_ci pr_debug("%s: cpp_daisy: aa5500ff(%02x)\n", port->name, s); 3198c2ecf20Sopenharmony_ci return -ENXIO; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci parport_write_data(port, 0x87); udelay(2); 3238c2ecf20Sopenharmony_ci s = parport_read_status(port) & (PARPORT_STATUS_BUSY 3248c2ecf20Sopenharmony_ci | PARPORT_STATUS_PAPEROUT 3258c2ecf20Sopenharmony_ci | PARPORT_STATUS_SELECT 3268c2ecf20Sopenharmony_ci | PARPORT_STATUS_ERROR); 3278c2ecf20Sopenharmony_ci if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) { 3288c2ecf20Sopenharmony_ci pr_debug("%s: cpp_daisy: aa5500ff87(%02x)\n", port->name, s); 3298c2ecf20Sopenharmony_ci return -ENXIO; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci parport_write_data(port, 0x78); udelay(2); 3338c2ecf20Sopenharmony_ci parport_write_data(port, cmd); udelay(2); 3348c2ecf20Sopenharmony_ci parport_frob_control(port, 3358c2ecf20Sopenharmony_ci PARPORT_CONTROL_STROBE, 3368c2ecf20Sopenharmony_ci PARPORT_CONTROL_STROBE); 3378c2ecf20Sopenharmony_ci udelay(1); 3388c2ecf20Sopenharmony_ci s = parport_read_status(port); 3398c2ecf20Sopenharmony_ci parport_frob_control(port, PARPORT_CONTROL_STROBE, 0); 3408c2ecf20Sopenharmony_ci udelay(1); 3418c2ecf20Sopenharmony_ci parport_write_data(port, 0xff); udelay(2); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return s; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* Send a mux-style CPP command packet. */ 3478c2ecf20Sopenharmony_cistatic int cpp_mux(struct parport *port, int cmd) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci unsigned char s; 3508c2ecf20Sopenharmony_ci int rc; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci parport_data_forward(port); 3538c2ecf20Sopenharmony_ci parport_write_data(port, 0xaa); udelay(2); 3548c2ecf20Sopenharmony_ci parport_write_data(port, 0x55); udelay(2); 3558c2ecf20Sopenharmony_ci parport_write_data(port, 0xf0); udelay(2); 3568c2ecf20Sopenharmony_ci parport_write_data(port, 0x0f); udelay(2); 3578c2ecf20Sopenharmony_ci parport_write_data(port, 0x52); udelay(2); 3588c2ecf20Sopenharmony_ci parport_write_data(port, 0xad); udelay(2); 3598c2ecf20Sopenharmony_ci parport_write_data(port, cmd); udelay(2); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci s = parport_read_status(port); 3628c2ecf20Sopenharmony_ci if (!(s & PARPORT_STATUS_ACK)) { 3638c2ecf20Sopenharmony_ci pr_debug("%s: cpp_mux: aa55f00f52ad%02x(%02x)\n", 3648c2ecf20Sopenharmony_ci port->name, cmd, s); 3658c2ecf20Sopenharmony_ci return -EIO; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci rc = (((s & PARPORT_STATUS_SELECT ? 1 : 0) << 0) | 3698c2ecf20Sopenharmony_ci ((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) | 3708c2ecf20Sopenharmony_ci ((s & PARPORT_STATUS_BUSY ? 0 : 1) << 2) | 3718c2ecf20Sopenharmony_ci ((s & PARPORT_STATUS_ERROR ? 0 : 1) << 3)); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return rc; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_civoid parport_daisy_deselect_all(struct parport *port) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci cpp_daisy(port, 0x30); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciint parport_daisy_select(struct parport *port, int daisy, int mode) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci switch (mode) 3848c2ecf20Sopenharmony_ci { 3858c2ecf20Sopenharmony_ci // For these modes we should switch to EPP mode: 3868c2ecf20Sopenharmony_ci case IEEE1284_MODE_EPP: 3878c2ecf20Sopenharmony_ci case IEEE1284_MODE_EPPSL: 3888c2ecf20Sopenharmony_ci case IEEE1284_MODE_EPPSWE: 3898c2ecf20Sopenharmony_ci return !(cpp_daisy(port, 0x20 + daisy) & 3908c2ecf20Sopenharmony_ci PARPORT_STATUS_ERROR); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci // For these modes we should switch to ECP mode: 3938c2ecf20Sopenharmony_ci case IEEE1284_MODE_ECP: 3948c2ecf20Sopenharmony_ci case IEEE1284_MODE_ECPRLE: 3958c2ecf20Sopenharmony_ci case IEEE1284_MODE_ECPSWE: 3968c2ecf20Sopenharmony_ci return !(cpp_daisy(port, 0xd0 + daisy) & 3978c2ecf20Sopenharmony_ci PARPORT_STATUS_ERROR); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci // Nothing was told for BECP in Daisy chain specification. 4008c2ecf20Sopenharmony_ci // May be it's wise to use ECP? 4018c2ecf20Sopenharmony_ci case IEEE1284_MODE_BECP: 4028c2ecf20Sopenharmony_ci // Others use compat mode 4038c2ecf20Sopenharmony_ci case IEEE1284_MODE_NIBBLE: 4048c2ecf20Sopenharmony_ci case IEEE1284_MODE_BYTE: 4058c2ecf20Sopenharmony_ci case IEEE1284_MODE_COMPAT: 4068c2ecf20Sopenharmony_ci default: 4078c2ecf20Sopenharmony_ci return !(cpp_daisy(port, 0xe0 + daisy) & 4088c2ecf20Sopenharmony_ci PARPORT_STATUS_ERROR); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int mux_present(struct parport *port) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci return cpp_mux(port, 0x51) == 3; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int num_mux_ports(struct parport *port) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci return cpp_mux(port, 0x58); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int select_port(struct parport *port) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci int muxport = port->muxport; 4258c2ecf20Sopenharmony_ci return cpp_mux(port, 0x60 + muxport) == muxport; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int assign_addrs(struct parport *port) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci unsigned char s; 4318c2ecf20Sopenharmony_ci unsigned char daisy; 4328c2ecf20Sopenharmony_ci int thisdev = numdevs; 4338c2ecf20Sopenharmony_ci int detected; 4348c2ecf20Sopenharmony_ci char *deviceid; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci parport_data_forward(port); 4378c2ecf20Sopenharmony_ci parport_write_data(port, 0xaa); udelay(2); 4388c2ecf20Sopenharmony_ci parport_write_data(port, 0x55); udelay(2); 4398c2ecf20Sopenharmony_ci parport_write_data(port, 0x00); udelay(2); 4408c2ecf20Sopenharmony_ci parport_write_data(port, 0xff); udelay(2); 4418c2ecf20Sopenharmony_ci s = parport_read_status(port) & (PARPORT_STATUS_BUSY 4428c2ecf20Sopenharmony_ci | PARPORT_STATUS_PAPEROUT 4438c2ecf20Sopenharmony_ci | PARPORT_STATUS_SELECT 4448c2ecf20Sopenharmony_ci | PARPORT_STATUS_ERROR); 4458c2ecf20Sopenharmony_ci if (s != (PARPORT_STATUS_BUSY 4468c2ecf20Sopenharmony_ci | PARPORT_STATUS_PAPEROUT 4478c2ecf20Sopenharmony_ci | PARPORT_STATUS_SELECT 4488c2ecf20Sopenharmony_ci | PARPORT_STATUS_ERROR)) { 4498c2ecf20Sopenharmony_ci pr_debug("%s: assign_addrs: aa5500ff(%02x)\n", port->name, s); 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci parport_write_data(port, 0x87); udelay(2); 4548c2ecf20Sopenharmony_ci s = parport_read_status(port) & (PARPORT_STATUS_BUSY 4558c2ecf20Sopenharmony_ci | PARPORT_STATUS_PAPEROUT 4568c2ecf20Sopenharmony_ci | PARPORT_STATUS_SELECT 4578c2ecf20Sopenharmony_ci | PARPORT_STATUS_ERROR); 4588c2ecf20Sopenharmony_ci if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) { 4598c2ecf20Sopenharmony_ci pr_debug("%s: assign_addrs: aa5500ff87(%02x)\n", port->name, s); 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci parport_write_data(port, 0x78); udelay(2); 4648c2ecf20Sopenharmony_ci s = parport_read_status(port); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for (daisy = 0; 4678c2ecf20Sopenharmony_ci (s & (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT)) 4688c2ecf20Sopenharmony_ci == (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT) 4698c2ecf20Sopenharmony_ci && daisy < 4; 4708c2ecf20Sopenharmony_ci ++daisy) { 4718c2ecf20Sopenharmony_ci parport_write_data(port, daisy); 4728c2ecf20Sopenharmony_ci udelay(2); 4738c2ecf20Sopenharmony_ci parport_frob_control(port, 4748c2ecf20Sopenharmony_ci PARPORT_CONTROL_STROBE, 4758c2ecf20Sopenharmony_ci PARPORT_CONTROL_STROBE); 4768c2ecf20Sopenharmony_ci udelay(1); 4778c2ecf20Sopenharmony_ci parport_frob_control(port, PARPORT_CONTROL_STROBE, 0); 4788c2ecf20Sopenharmony_ci udelay(1); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci add_dev(numdevs++, port, daisy); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* See if this device thought it was the last in the 4838c2ecf20Sopenharmony_ci * chain. */ 4848c2ecf20Sopenharmony_ci if (!(s & PARPORT_STATUS_BUSY)) 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* We are seeing pass through status now. We see 4888c2ecf20Sopenharmony_ci last_dev from next device or if last_dev does not 4898c2ecf20Sopenharmony_ci work status lines from some non-daisy chain 4908c2ecf20Sopenharmony_ci device. */ 4918c2ecf20Sopenharmony_ci s = parport_read_status(port); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci parport_write_data(port, 0xff); udelay(2); 4958c2ecf20Sopenharmony_ci detected = numdevs - thisdev; 4968c2ecf20Sopenharmony_ci pr_debug("%s: Found %d daisy-chained devices\n", port->name, detected); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Ask the new devices to introduce themselves. */ 4998c2ecf20Sopenharmony_ci deviceid = kmalloc(1024, GFP_KERNEL); 5008c2ecf20Sopenharmony_ci if (!deviceid) return 0; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci for (daisy = 0; thisdev < numdevs; thisdev++, daisy++) 5038c2ecf20Sopenharmony_ci parport_device_id(thisdev, deviceid, 1024); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci kfree(deviceid); 5068c2ecf20Sopenharmony_ci return detected; 5078c2ecf20Sopenharmony_ci} 508