xref: /openbmc/linux/drivers/parport/daisy.c (revision 88c5cbde)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * IEEE 1284.3 Parallel port daisy chain and multiplexor code
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 1999, 2000  Tim Waugh <tim@cyberelk.demon.co.uk>
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * ??-12-1998: Initial implementation.
121da177e4SLinus Torvalds  * 31-01-1999: Make port-cloning transparent.
131da177e4SLinus Torvalds  * 13-02-1999: Move DeviceID technique from parport_probe.
141da177e4SLinus Torvalds  * 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too.
151da177e4SLinus Torvalds  * 22-02-2000: Count devices that are actually detected.
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  * Any part of this program may be used in documents licensed under
181da177e4SLinus Torvalds  * the GNU Free Documentation License, Version 1.1 or any later version
191da177e4SLinus Torvalds  * published by the Free Software Foundation.
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <linux/module.h>
231da177e4SLinus Torvalds #include <linux/parport.h>
241da177e4SLinus Torvalds #include <linux/delay.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
26174cd4b1SIngo Molnar #include <linux/sched/signal.h>
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <asm/current.h>
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #undef DEBUG
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds static struct daisydev {
341da177e4SLinus Torvalds 	struct daisydev *next;
351da177e4SLinus Torvalds 	struct parport *port;
361da177e4SLinus Torvalds 	int daisy;
371da177e4SLinus Torvalds 	int devnum;
381da177e4SLinus Torvalds } *topology = NULL;
391da177e4SLinus Torvalds static DEFINE_SPINLOCK(topology_lock);
401da177e4SLinus Torvalds 
41df4c756eSCarlos Palminha static int numdevs;
4260f8a59dSSudip Mukherjee static bool daisy_init_done;
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds /* Forward-declaration of lower-level functions. */
451da177e4SLinus Torvalds static int mux_present(struct parport *port);
461da177e4SLinus Torvalds static int num_mux_ports(struct parport *port);
471da177e4SLinus Torvalds static int select_port(struct parport *port);
481da177e4SLinus Torvalds static int assign_addrs(struct parport *port);
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /* Add a device to the discovered topology. */
add_dev(int devnum,struct parport * port,int daisy)511da177e4SLinus Torvalds static void add_dev(int devnum, struct parport *port, int daisy)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	struct daisydev *newdev, **p;
541da177e4SLinus Torvalds 	newdev = kmalloc(sizeof(struct daisydev), GFP_KERNEL);
551da177e4SLinus Torvalds 	if (newdev) {
561da177e4SLinus Torvalds 		newdev->port = port;
571da177e4SLinus Torvalds 		newdev->daisy = daisy;
581da177e4SLinus Torvalds 		newdev->devnum = devnum;
591da177e4SLinus Torvalds 		spin_lock(&topology_lock);
601da177e4SLinus Torvalds 		for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next)
611da177e4SLinus Torvalds 			;
621da177e4SLinus Torvalds 		newdev->next = *p;
631da177e4SLinus Torvalds 		*p = newdev;
641da177e4SLinus Torvalds 		spin_unlock(&topology_lock);
651da177e4SLinus Torvalds 	}
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds /* Clone a parport (actually, make an alias). */
clone_parport(struct parport * real,int muxport)691da177e4SLinus Torvalds static struct parport *clone_parport(struct parport *real, int muxport)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	struct parport *extra = parport_register_port(real->base,
721da177e4SLinus Torvalds 						       real->irq,
731da177e4SLinus Torvalds 						       real->dma,
741da177e4SLinus Torvalds 						       real->ops);
751da177e4SLinus Torvalds 	if (extra) {
761da177e4SLinus Torvalds 		extra->portnum = real->portnum;
771da177e4SLinus Torvalds 		extra->physport = real;
781da177e4SLinus Torvalds 		extra->muxport = muxport;
791da177e4SLinus Torvalds 		real->slaves[muxport-1] = extra;
801da177e4SLinus Torvalds 	}
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	return extra;
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds 
daisy_drv_probe(struct pardevice * par_dev)8560f8a59dSSudip Mukherjee static int daisy_drv_probe(struct pardevice *par_dev)
8660f8a59dSSudip Mukherjee {
8760f8a59dSSudip Mukherjee 	struct device_driver *drv = par_dev->dev.driver;
8860f8a59dSSudip Mukherjee 
8960f8a59dSSudip Mukherjee 	if (strcmp(drv->name, "daisy_drv"))
9060f8a59dSSudip Mukherjee 		return -ENODEV;
9160f8a59dSSudip Mukherjee 	if (strcmp(par_dev->name, daisy_dev_name))
9260f8a59dSSudip Mukherjee 		return -ENODEV;
9360f8a59dSSudip Mukherjee 
9460f8a59dSSudip Mukherjee 	return 0;
9560f8a59dSSudip Mukherjee }
9660f8a59dSSudip Mukherjee 
9760f8a59dSSudip Mukherjee static struct parport_driver daisy_driver = {
9860f8a59dSSudip Mukherjee 	.name = "daisy_drv",
9960f8a59dSSudip Mukherjee 	.probe = daisy_drv_probe,
10060f8a59dSSudip Mukherjee 	.devmodel = true,
10160f8a59dSSudip Mukherjee };
10260f8a59dSSudip Mukherjee 
1031da177e4SLinus Torvalds /* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains.
1041da177e4SLinus Torvalds  * Return value is number of devices actually detected. */
parport_daisy_init(struct parport * port)1051da177e4SLinus Torvalds int parport_daisy_init(struct parport *port)
1061da177e4SLinus Torvalds {
1071da177e4SLinus Torvalds 	int detected = 0;
1081da177e4SLinus Torvalds 	char *deviceid;
1091da177e4SLinus Torvalds 	static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" };
1101da177e4SLinus Torvalds 	int num_ports;
1111da177e4SLinus Torvalds 	int i;
1121da177e4SLinus Torvalds 	int last_try = 0;
1131da177e4SLinus Torvalds 
11460f8a59dSSudip Mukherjee 	if (!daisy_init_done) {
11560f8a59dSSudip Mukherjee 		/*
11660f8a59dSSudip Mukherjee 		 * flag should be marked true first as
11760f8a59dSSudip Mukherjee 		 * parport_register_driver() might try to load the low
11860f8a59dSSudip Mukherjee 		 * level driver which will lead to announcing new ports
11960f8a59dSSudip Mukherjee 		 * and which will again come back here at
12060f8a59dSSudip Mukherjee 		 * parport_daisy_init()
12160f8a59dSSudip Mukherjee 		 */
12260f8a59dSSudip Mukherjee 		daisy_init_done = true;
12360f8a59dSSudip Mukherjee 		i = parport_register_driver(&daisy_driver);
12460f8a59dSSudip Mukherjee 		if (i) {
12560f8a59dSSudip Mukherjee 			pr_err("daisy registration failed\n");
12660f8a59dSSudip Mukherjee 			daisy_init_done = false;
12760f8a59dSSudip Mukherjee 			return i;
12860f8a59dSSudip Mukherjee 		}
12960f8a59dSSudip Mukherjee 	}
13060f8a59dSSudip Mukherjee 
1311da177e4SLinus Torvalds again:
1321da177e4SLinus Torvalds 	/* Because this is called before any other devices exist,
1331da177e4SLinus Torvalds 	 * we don't have to claim exclusive access.  */
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	/* If mux present on normal port, need to create new
1361da177e4SLinus Torvalds 	 * parports for each extra port. */
1371da177e4SLinus Torvalds 	if (port->muxport < 0 && mux_present(port) &&
1381da177e4SLinus Torvalds 	    /* don't be fooled: a mux must have 2 or 4 ports. */
1391da177e4SLinus Torvalds 	    ((num_ports = num_mux_ports(port)) == 2 || num_ports == 4)) {
1401da177e4SLinus Torvalds 		/* Leave original as port zero. */
1411da177e4SLinus Torvalds 		port->muxport = 0;
142decf26f6SJoe Perches 		pr_info("%s: 1st (default) port of %d-way multiplexor\n",
1431da177e4SLinus Torvalds 			port->name, num_ports);
1441da177e4SLinus Torvalds 		for (i = 1; i < num_ports; i++) {
1451da177e4SLinus Torvalds 			/* Clone the port. */
1461da177e4SLinus Torvalds 			struct parport *extra = clone_parport(port, i);
1471da177e4SLinus Torvalds 			if (!extra) {
1481da177e4SLinus Torvalds 				if (signal_pending(current))
1491da177e4SLinus Torvalds 					break;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 				schedule();
1521da177e4SLinus Torvalds 				continue;
1531da177e4SLinus Torvalds 			}
1541da177e4SLinus Torvalds 
155decf26f6SJoe Perches 			pr_info("%s: %d%s port of %d-way multiplexor on %s\n",
1561da177e4SLinus Torvalds 				extra->name, i + 1, th[i + 1], num_ports,
1571da177e4SLinus Torvalds 				port->name);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 			/* Analyse that port too.  We won't recurse
1601da177e4SLinus Torvalds 			   forever because of the 'port->muxport < 0'
1611da177e4SLinus Torvalds 			   test above. */
1621da177e4SLinus Torvalds 			parport_daisy_init(extra);
1631da177e4SLinus Torvalds 		}
1641da177e4SLinus Torvalds 	}
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	if (port->muxport >= 0)
1671da177e4SLinus Torvalds 		select_port(port);
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	parport_daisy_deselect_all(port);
1701da177e4SLinus Torvalds 	detected += assign_addrs(port);
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	/* Count the potential legacy device at the end. */
1731da177e4SLinus Torvalds 	add_dev(numdevs++, port, -1);
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	/* Find out the legacy device's IEEE 1284 device ID. */
176b44d3bddSMarko Kohtala 	deviceid = kmalloc(1024, GFP_KERNEL);
1771da177e4SLinus Torvalds 	if (deviceid) {
178b44d3bddSMarko Kohtala 		if (parport_device_id(numdevs - 1, deviceid, 1024) > 2)
1791da177e4SLinus Torvalds 			detected++;
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 		kfree(deviceid);
1821da177e4SLinus Torvalds 	}
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 	if (!detected && !last_try) {
1851da177e4SLinus Torvalds 		/* No devices were detected.  Perhaps they are in some
1861da177e4SLinus Torvalds                    funny state; let's try to reset them and see if
1871da177e4SLinus Torvalds                    they wake up. */
1881da177e4SLinus Torvalds 		parport_daisy_fini(port);
1891da177e4SLinus Torvalds 		parport_write_control(port, PARPORT_CONTROL_SELECT);
1901da177e4SLinus Torvalds 		udelay(50);
1911da177e4SLinus Torvalds 		parport_write_control(port,
1921da177e4SLinus Torvalds 				       PARPORT_CONTROL_SELECT |
1931da177e4SLinus Torvalds 				       PARPORT_CONTROL_INIT);
1941da177e4SLinus Torvalds 		udelay(50);
1951da177e4SLinus Torvalds 		last_try = 1;
1961da177e4SLinus Torvalds 		goto again;
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	return detected;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds /* Forget about devices on a physical port. */
parport_daisy_fini(struct parport * port)2031da177e4SLinus Torvalds void parport_daisy_fini(struct parport *port)
2041da177e4SLinus Torvalds {
2051da177e4SLinus Torvalds 	struct daisydev **p;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	spin_lock(&topology_lock);
2081da177e4SLinus Torvalds 	p = &topology;
2091da177e4SLinus Torvalds 	while (*p) {
2101da177e4SLinus Torvalds 		struct daisydev *dev = *p;
2111da177e4SLinus Torvalds 		if (dev->port != port) {
2121da177e4SLinus Torvalds 			p = &dev->next;
2131da177e4SLinus Torvalds 			continue;
2141da177e4SLinus Torvalds 		}
2151da177e4SLinus Torvalds 		*p = dev->next;
2161da177e4SLinus Torvalds 		kfree(dev);
2171da177e4SLinus Torvalds 	}
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 	/* Gaps in the numbering could be handled better.  How should
2201da177e4SLinus Torvalds            someone enumerate through all IEEE1284.3 devices in the
2211da177e4SLinus Torvalds            topology?. */
2221da177e4SLinus Torvalds 	if (!topology) numdevs = 0;
2231da177e4SLinus Torvalds 	spin_unlock(&topology_lock);
2241da177e4SLinus Torvalds 	return;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds /**
2281da177e4SLinus Torvalds  *	parport_open - find a device by canonical device number
2291da177e4SLinus Torvalds  *	@devnum: canonical device number
2301da177e4SLinus Torvalds  *	@name: name to associate with the device
2311da177e4SLinus Torvalds  *
2321da177e4SLinus Torvalds  *	This function is similar to parport_register_device(), except
2331da177e4SLinus Torvalds  *	that it locates a device by its number rather than by the port
2341da177e4SLinus Torvalds  *	it is attached to.
2351da177e4SLinus Torvalds  *
2361da177e4SLinus Torvalds  *	All parameters except for @devnum are the same as for
2371da177e4SLinus Torvalds  *	parport_register_device().  The return value is the same as
2381da177e4SLinus Torvalds  *	for parport_register_device().
2391da177e4SLinus Torvalds  **/
2401da177e4SLinus Torvalds 
parport_open(int devnum,const char * name)2415712cb3dSJeff Garzik struct pardevice *parport_open(int devnum, const char *name)
2421da177e4SLinus Torvalds {
2431da177e4SLinus Torvalds 	struct daisydev *p = topology;
24460f8a59dSSudip Mukherjee 	struct pardev_cb par_cb;
2451da177e4SLinus Torvalds 	struct parport *port;
2461da177e4SLinus Torvalds 	struct pardevice *dev;
2471da177e4SLinus Torvalds 	int daisy;
2481da177e4SLinus Torvalds 
24960f8a59dSSudip Mukherjee 	memset(&par_cb, 0, sizeof(par_cb));
2501da177e4SLinus Torvalds 	spin_lock(&topology_lock);
2511da177e4SLinus Torvalds 	while (p && p->devnum != devnum)
2521da177e4SLinus Torvalds 		p = p->next;
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	if (!p) {
2551da177e4SLinus Torvalds 		spin_unlock(&topology_lock);
2561da177e4SLinus Torvalds 		return NULL;
2571da177e4SLinus Torvalds 	}
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	daisy = p->daisy;
2601da177e4SLinus Torvalds 	port = parport_get_port(p->port);
2611da177e4SLinus Torvalds 	spin_unlock(&topology_lock);
2621da177e4SLinus Torvalds 
26360f8a59dSSudip Mukherjee 	dev = parport_register_dev_model(port, name, &par_cb, devnum);
2641da177e4SLinus Torvalds 	parport_put_port(port);
2651da177e4SLinus Torvalds 	if (!dev)
2661da177e4SLinus Torvalds 		return NULL;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	dev->daisy = daisy;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	/* Check that there really is a device to select. */
2711da177e4SLinus Torvalds 	if (daisy >= 0) {
2721da177e4SLinus Torvalds 		int selected;
2731da177e4SLinus Torvalds 		parport_claim_or_block(dev);
2741da177e4SLinus Torvalds 		selected = port->daisy;
2751da177e4SLinus Torvalds 		parport_release(dev);
2761da177e4SLinus Torvalds 
277c29a75edSMarko Kohtala 		if (selected != daisy) {
2781da177e4SLinus Torvalds 			/* No corresponding device. */
2791da177e4SLinus Torvalds 			parport_unregister_device(dev);
2801da177e4SLinus Torvalds 			return NULL;
2811da177e4SLinus Torvalds 		}
2821da177e4SLinus Torvalds 	}
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	return dev;
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds /**
2881da177e4SLinus Torvalds  *	parport_close - close a device opened with parport_open()
2891da177e4SLinus Torvalds  *	@dev: device to close
2901da177e4SLinus Torvalds  *
2911da177e4SLinus Torvalds  *	This is to parport_open() as parport_unregister_device() is to
2921da177e4SLinus Torvalds  *	parport_register_device().
2931da177e4SLinus Torvalds  **/
2941da177e4SLinus Torvalds 
parport_close(struct pardevice * dev)2951da177e4SLinus Torvalds void parport_close(struct pardevice *dev)
2961da177e4SLinus Torvalds {
2971da177e4SLinus Torvalds 	parport_unregister_device(dev);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds /* Send a daisy-chain-style CPP command packet. */
cpp_daisy(struct parport * port,int cmd)3011da177e4SLinus Torvalds static int cpp_daisy(struct parport *port, int cmd)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	unsigned char s;
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	parport_data_forward(port);
3061da177e4SLinus Torvalds 	parport_write_data(port, 0xaa); udelay(2);
3071da177e4SLinus Torvalds 	parport_write_data(port, 0x55); udelay(2);
3081da177e4SLinus Torvalds 	parport_write_data(port, 0x00); udelay(2);
3091da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
3101da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
3111da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
3121da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
3131da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
3141da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_BUSY
3151da177e4SLinus Torvalds 		  | PARPORT_STATUS_PAPEROUT
3161da177e4SLinus Torvalds 		  | PARPORT_STATUS_SELECT
3171da177e4SLinus Torvalds 		  | PARPORT_STATUS_ERROR)) {
31888c5cbdeSJoe Perches 		pr_debug("%s: cpp_daisy: aa5500ff(%02x)\n", port->name, s);
3191da177e4SLinus Torvalds 		return -ENXIO;
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	parport_write_data(port, 0x87); udelay(2);
3231da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
3241da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
3251da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
3261da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
3271da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
32888c5cbdeSJoe Perches 		pr_debug("%s: cpp_daisy: aa5500ff87(%02x)\n", port->name, s);
3291da177e4SLinus Torvalds 		return -ENXIO;
3301da177e4SLinus Torvalds 	}
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	parport_write_data(port, 0x78); udelay(2);
3331da177e4SLinus Torvalds 	parport_write_data(port, cmd); udelay(2);
3341da177e4SLinus Torvalds 	parport_frob_control(port,
3351da177e4SLinus Torvalds 			      PARPORT_CONTROL_STROBE,
3361da177e4SLinus Torvalds 			      PARPORT_CONTROL_STROBE);
3371da177e4SLinus Torvalds 	udelay(1);
3387c9cc3beSMarko Kohtala 	s = parport_read_status(port);
3391da177e4SLinus Torvalds 	parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
3401da177e4SLinus Torvalds 	udelay(1);
3411da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	return s;
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds /* Send a mux-style CPP command packet. */
cpp_mux(struct parport * port,int cmd)3471da177e4SLinus Torvalds static int cpp_mux(struct parport *port, int cmd)
3481da177e4SLinus Torvalds {
3491da177e4SLinus Torvalds 	unsigned char s;
3501da177e4SLinus Torvalds 	int rc;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	parport_data_forward(port);
3531da177e4SLinus Torvalds 	parport_write_data(port, 0xaa); udelay(2);
3541da177e4SLinus Torvalds 	parport_write_data(port, 0x55); udelay(2);
3551da177e4SLinus Torvalds 	parport_write_data(port, 0xf0); udelay(2);
3561da177e4SLinus Torvalds 	parport_write_data(port, 0x0f); udelay(2);
3571da177e4SLinus Torvalds 	parport_write_data(port, 0x52); udelay(2);
3581da177e4SLinus Torvalds 	parport_write_data(port, 0xad); udelay(2);
3591da177e4SLinus Torvalds 	parport_write_data(port, cmd); udelay(2);
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	s = parport_read_status(port);
3621da177e4SLinus Torvalds 	if (!(s & PARPORT_STATUS_ACK)) {
36388c5cbdeSJoe Perches 		pr_debug("%s: cpp_mux: aa55f00f52ad%02x(%02x)\n",
3641da177e4SLinus Torvalds 			 port->name, cmd, s);
3651da177e4SLinus Torvalds 		return -EIO;
3661da177e4SLinus Torvalds 	}
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 	rc = (((s & PARPORT_STATUS_SELECT   ? 1 : 0) << 0) |
3691da177e4SLinus Torvalds 	      ((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) |
3701da177e4SLinus Torvalds 	      ((s & PARPORT_STATUS_BUSY     ? 0 : 1) << 2) |
3711da177e4SLinus Torvalds 	      ((s & PARPORT_STATUS_ERROR    ? 0 : 1) << 3));
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	return rc;
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds 
parport_daisy_deselect_all(struct parport * port)3761da177e4SLinus Torvalds void parport_daisy_deselect_all(struct parport *port)
3771da177e4SLinus Torvalds {
3781da177e4SLinus Torvalds 	cpp_daisy(port, 0x30);
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds 
parport_daisy_select(struct parport * port,int daisy,int mode)3811da177e4SLinus Torvalds int parport_daisy_select(struct parport *port, int daisy, int mode)
3821da177e4SLinus Torvalds {
3831da177e4SLinus Torvalds 	switch (mode)
3841da177e4SLinus Torvalds 	{
3851da177e4SLinus Torvalds 		// For these modes we should switch to EPP mode:
3861da177e4SLinus Torvalds 		case IEEE1284_MODE_EPP:
3871da177e4SLinus Torvalds 		case IEEE1284_MODE_EPPSL:
3881da177e4SLinus Torvalds 		case IEEE1284_MODE_EPPSWE:
3897c9cc3beSMarko Kohtala 			return !(cpp_daisy(port, 0x20 + daisy) &
3901da177e4SLinus Torvalds 				 PARPORT_STATUS_ERROR);
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 		// For these modes we should switch to ECP mode:
3931da177e4SLinus Torvalds 		case IEEE1284_MODE_ECP:
3941da177e4SLinus Torvalds 		case IEEE1284_MODE_ECPRLE:
3951da177e4SLinus Torvalds 		case IEEE1284_MODE_ECPSWE:
3967c9cc3beSMarko Kohtala 			return !(cpp_daisy(port, 0xd0 + daisy) &
3971da177e4SLinus Torvalds 				 PARPORT_STATUS_ERROR);
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 		// Nothing was told for BECP in Daisy chain specification.
4001da177e4SLinus Torvalds 		// May be it's wise to use ECP?
4011da177e4SLinus Torvalds 		case IEEE1284_MODE_BECP:
4021da177e4SLinus Torvalds 		// Others use compat mode
4031da177e4SLinus Torvalds 		case IEEE1284_MODE_NIBBLE:
4041da177e4SLinus Torvalds 		case IEEE1284_MODE_BYTE:
4051da177e4SLinus Torvalds 		case IEEE1284_MODE_COMPAT:
4061da177e4SLinus Torvalds 		default:
4077c9cc3beSMarko Kohtala 			return !(cpp_daisy(port, 0xe0 + daisy) &
4081da177e4SLinus Torvalds 				 PARPORT_STATUS_ERROR);
4091da177e4SLinus Torvalds 	}
4101da177e4SLinus Torvalds }
4111da177e4SLinus Torvalds 
mux_present(struct parport * port)4121da177e4SLinus Torvalds static int mux_present(struct parport *port)
4131da177e4SLinus Torvalds {
4141da177e4SLinus Torvalds 	return cpp_mux(port, 0x51) == 3;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
num_mux_ports(struct parport * port)4171da177e4SLinus Torvalds static int num_mux_ports(struct parport *port)
4181da177e4SLinus Torvalds {
4191da177e4SLinus Torvalds 	return cpp_mux(port, 0x58);
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
select_port(struct parport * port)4221da177e4SLinus Torvalds static int select_port(struct parport *port)
4231da177e4SLinus Torvalds {
4241da177e4SLinus Torvalds 	int muxport = port->muxport;
4251da177e4SLinus Torvalds 	return cpp_mux(port, 0x60 + muxport) == muxport;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
assign_addrs(struct parport * port)4281da177e4SLinus Torvalds static int assign_addrs(struct parport *port)
4291da177e4SLinus Torvalds {
430310c8c32SMarko Kohtala 	unsigned char s;
4311da177e4SLinus Torvalds 	unsigned char daisy;
4321da177e4SLinus Torvalds 	int thisdev = numdevs;
4331da177e4SLinus Torvalds 	int detected;
4341da177e4SLinus Torvalds 	char *deviceid;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	parport_data_forward(port);
4371da177e4SLinus Torvalds 	parport_write_data(port, 0xaa); udelay(2);
4381da177e4SLinus Torvalds 	parport_write_data(port, 0x55); udelay(2);
4391da177e4SLinus Torvalds 	parport_write_data(port, 0x00); udelay(2);
4401da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
4411da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
4421da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
4431da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
4441da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
4451da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_BUSY
4461da177e4SLinus Torvalds 		  | PARPORT_STATUS_PAPEROUT
4471da177e4SLinus Torvalds 		  | PARPORT_STATUS_SELECT
4481da177e4SLinus Torvalds 		  | PARPORT_STATUS_ERROR)) {
44988c5cbdeSJoe Perches 		pr_debug("%s: assign_addrs: aa5500ff(%02x)\n", port->name, s);
4501da177e4SLinus Torvalds 		return 0;
4511da177e4SLinus Torvalds 	}
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	parport_write_data(port, 0x87); udelay(2);
4541da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
4551da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
4561da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
4571da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
4581da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
45988c5cbdeSJoe Perches 		pr_debug("%s: assign_addrs: aa5500ff87(%02x)\n", port->name, s);
4601da177e4SLinus Torvalds 		return 0;
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	parport_write_data(port, 0x78); udelay(2);
464310c8c32SMarko Kohtala 	s = parport_read_status(port);
4651da177e4SLinus Torvalds 
466310c8c32SMarko Kohtala 	for (daisy = 0;
467310c8c32SMarko Kohtala 	     (s & (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT))
468310c8c32SMarko Kohtala 		     == (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT)
469310c8c32SMarko Kohtala 		     && daisy < 4;
470310c8c32SMarko Kohtala 	     ++daisy) {
4711da177e4SLinus Torvalds 		parport_write_data(port, daisy);
4721da177e4SLinus Torvalds 		udelay(2);
4731da177e4SLinus Torvalds 		parport_frob_control(port,
4741da177e4SLinus Torvalds 				      PARPORT_CONTROL_STROBE,
4751da177e4SLinus Torvalds 				      PARPORT_CONTROL_STROBE);
4761da177e4SLinus Torvalds 		udelay(1);
4771da177e4SLinus Torvalds 		parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
4781da177e4SLinus Torvalds 		udelay(1);
4791da177e4SLinus Torvalds 
480310c8c32SMarko Kohtala 		add_dev(numdevs++, port, daisy);
481310c8c32SMarko Kohtala 
482310c8c32SMarko Kohtala 		/* See if this device thought it was the last in the
483310c8c32SMarko Kohtala 		 * chain. */
484310c8c32SMarko Kohtala 		if (!(s & PARPORT_STATUS_BUSY))
4851da177e4SLinus Torvalds 			break;
4861da177e4SLinus Torvalds 
487310c8c32SMarko Kohtala 		/* We are seeing pass through status now. We see
488310c8c32SMarko Kohtala 		   last_dev from next device or if last_dev does not
489310c8c32SMarko Kohtala 		   work status lines from some non-daisy chain
490310c8c32SMarko Kohtala 		   device. */
491310c8c32SMarko Kohtala 		s = parport_read_status(port);
4921da177e4SLinus Torvalds 	}
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
4951da177e4SLinus Torvalds 	detected = numdevs - thisdev;
49688c5cbdeSJoe Perches 	pr_debug("%s: Found %d daisy-chained devices\n", port->name, detected);
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds 	/* Ask the new devices to introduce themselves. */
499b44d3bddSMarko Kohtala 	deviceid = kmalloc(1024, GFP_KERNEL);
5001da177e4SLinus Torvalds 	if (!deviceid) return 0;
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	for (daisy = 0; thisdev < numdevs; thisdev++, daisy++)
503b44d3bddSMarko Kohtala 		parport_device_id(thisdev, deviceid, 1024);
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	kfree(deviceid);
5061da177e4SLinus Torvalds 	return detected;
5071da177e4SLinus Torvalds }
508