xref: /openbmc/linux/drivers/parport/daisy.c (revision decf26f6)
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 #ifdef DEBUG
341da177e4SLinus Torvalds #define DPRINTK(stuff...) printk(stuff)
351da177e4SLinus Torvalds #else
361da177e4SLinus Torvalds #define DPRINTK(stuff...)
371da177e4SLinus Torvalds #endif
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds static struct daisydev {
401da177e4SLinus Torvalds 	struct daisydev *next;
411da177e4SLinus Torvalds 	struct parport *port;
421da177e4SLinus Torvalds 	int daisy;
431da177e4SLinus Torvalds 	int devnum;
441da177e4SLinus Torvalds } *topology = NULL;
451da177e4SLinus Torvalds static DEFINE_SPINLOCK(topology_lock);
461da177e4SLinus Torvalds 
47df4c756eSCarlos Palminha static int numdevs;
4860f8a59dSSudip Mukherjee static bool daisy_init_done;
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /* Forward-declaration of lower-level functions. */
511da177e4SLinus Torvalds static int mux_present(struct parport *port);
521da177e4SLinus Torvalds static int num_mux_ports(struct parport *port);
531da177e4SLinus Torvalds static int select_port(struct parport *port);
541da177e4SLinus Torvalds static int assign_addrs(struct parport *port);
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* Add a device to the discovered topology. */
571da177e4SLinus Torvalds static void add_dev(int devnum, struct parport *port, int daisy)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	struct daisydev *newdev, **p;
601da177e4SLinus Torvalds 	newdev = kmalloc(sizeof(struct daisydev), GFP_KERNEL);
611da177e4SLinus Torvalds 	if (newdev) {
621da177e4SLinus Torvalds 		newdev->port = port;
631da177e4SLinus Torvalds 		newdev->daisy = daisy;
641da177e4SLinus Torvalds 		newdev->devnum = devnum;
651da177e4SLinus Torvalds 		spin_lock(&topology_lock);
661da177e4SLinus Torvalds 		for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next)
671da177e4SLinus Torvalds 			;
681da177e4SLinus Torvalds 		newdev->next = *p;
691da177e4SLinus Torvalds 		*p = newdev;
701da177e4SLinus Torvalds 		spin_unlock(&topology_lock);
711da177e4SLinus Torvalds 	}
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds /* Clone a parport (actually, make an alias). */
751da177e4SLinus Torvalds static struct parport *clone_parport(struct parport *real, int muxport)
761da177e4SLinus Torvalds {
771da177e4SLinus Torvalds 	struct parport *extra = parport_register_port(real->base,
781da177e4SLinus Torvalds 						       real->irq,
791da177e4SLinus Torvalds 						       real->dma,
801da177e4SLinus Torvalds 						       real->ops);
811da177e4SLinus Torvalds 	if (extra) {
821da177e4SLinus Torvalds 		extra->portnum = real->portnum;
831da177e4SLinus Torvalds 		extra->physport = real;
841da177e4SLinus Torvalds 		extra->muxport = muxport;
851da177e4SLinus Torvalds 		real->slaves[muxport-1] = extra;
861da177e4SLinus Torvalds 	}
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	return extra;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
9160f8a59dSSudip Mukherjee static int daisy_drv_probe(struct pardevice *par_dev)
9260f8a59dSSudip Mukherjee {
9360f8a59dSSudip Mukherjee 	struct device_driver *drv = par_dev->dev.driver;
9460f8a59dSSudip Mukherjee 
9560f8a59dSSudip Mukherjee 	if (strcmp(drv->name, "daisy_drv"))
9660f8a59dSSudip Mukherjee 		return -ENODEV;
9760f8a59dSSudip Mukherjee 	if (strcmp(par_dev->name, daisy_dev_name))
9860f8a59dSSudip Mukherjee 		return -ENODEV;
9960f8a59dSSudip Mukherjee 
10060f8a59dSSudip Mukherjee 	return 0;
10160f8a59dSSudip Mukherjee }
10260f8a59dSSudip Mukherjee 
10360f8a59dSSudip Mukherjee static struct parport_driver daisy_driver = {
10460f8a59dSSudip Mukherjee 	.name = "daisy_drv",
10560f8a59dSSudip Mukherjee 	.probe = daisy_drv_probe,
10660f8a59dSSudip Mukherjee 	.devmodel = true,
10760f8a59dSSudip Mukherjee };
10860f8a59dSSudip Mukherjee 
1091da177e4SLinus Torvalds /* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains.
1101da177e4SLinus Torvalds  * Return value is number of devices actually detected. */
1111da177e4SLinus Torvalds int parport_daisy_init(struct parport *port)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds 	int detected = 0;
1141da177e4SLinus Torvalds 	char *deviceid;
1151da177e4SLinus Torvalds 	static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" };
1161da177e4SLinus Torvalds 	int num_ports;
1171da177e4SLinus Torvalds 	int i;
1181da177e4SLinus Torvalds 	int last_try = 0;
1191da177e4SLinus Torvalds 
12060f8a59dSSudip Mukherjee 	if (!daisy_init_done) {
12160f8a59dSSudip Mukherjee 		/*
12260f8a59dSSudip Mukherjee 		 * flag should be marked true first as
12360f8a59dSSudip Mukherjee 		 * parport_register_driver() might try to load the low
12460f8a59dSSudip Mukherjee 		 * level driver which will lead to announcing new ports
12560f8a59dSSudip Mukherjee 		 * and which will again come back here at
12660f8a59dSSudip Mukherjee 		 * parport_daisy_init()
12760f8a59dSSudip Mukherjee 		 */
12860f8a59dSSudip Mukherjee 		daisy_init_done = true;
12960f8a59dSSudip Mukherjee 		i = parport_register_driver(&daisy_driver);
13060f8a59dSSudip Mukherjee 		if (i) {
13160f8a59dSSudip Mukherjee 			pr_err("daisy registration failed\n");
13260f8a59dSSudip Mukherjee 			daisy_init_done = false;
13360f8a59dSSudip Mukherjee 			return i;
13460f8a59dSSudip Mukherjee 		}
13560f8a59dSSudip Mukherjee 	}
13660f8a59dSSudip Mukherjee 
1371da177e4SLinus Torvalds again:
1381da177e4SLinus Torvalds 	/* Because this is called before any other devices exist,
1391da177e4SLinus Torvalds 	 * we don't have to claim exclusive access.  */
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	/* If mux present on normal port, need to create new
1421da177e4SLinus Torvalds 	 * parports for each extra port. */
1431da177e4SLinus Torvalds 	if (port->muxport < 0 && mux_present(port) &&
1441da177e4SLinus Torvalds 	    /* don't be fooled: a mux must have 2 or 4 ports. */
1451da177e4SLinus Torvalds 	    ((num_ports = num_mux_ports(port)) == 2 || num_ports == 4)) {
1461da177e4SLinus Torvalds 		/* Leave original as port zero. */
1471da177e4SLinus Torvalds 		port->muxport = 0;
148decf26f6SJoe Perches 		pr_info("%s: 1st (default) port of %d-way multiplexor\n",
1491da177e4SLinus Torvalds 			port->name, num_ports);
1501da177e4SLinus Torvalds 		for (i = 1; i < num_ports; i++) {
1511da177e4SLinus Torvalds 			/* Clone the port. */
1521da177e4SLinus Torvalds 			struct parport *extra = clone_parport(port, i);
1531da177e4SLinus Torvalds 			if (!extra) {
1541da177e4SLinus Torvalds 				if (signal_pending(current))
1551da177e4SLinus Torvalds 					break;
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 				schedule();
1581da177e4SLinus Torvalds 				continue;
1591da177e4SLinus Torvalds 			}
1601da177e4SLinus Torvalds 
161decf26f6SJoe Perches 			pr_info("%s: %d%s port of %d-way multiplexor on %s\n",
1621da177e4SLinus Torvalds 				extra->name, i + 1, th[i + 1], num_ports,
1631da177e4SLinus Torvalds 				port->name);
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 			/* Analyse that port too.  We won't recurse
1661da177e4SLinus Torvalds 			   forever because of the 'port->muxport < 0'
1671da177e4SLinus Torvalds 			   test above. */
1681da177e4SLinus Torvalds 			parport_daisy_init(extra);
1691da177e4SLinus Torvalds 		}
1701da177e4SLinus Torvalds 	}
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	if (port->muxport >= 0)
1731da177e4SLinus Torvalds 		select_port(port);
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	parport_daisy_deselect_all(port);
1761da177e4SLinus Torvalds 	detected += assign_addrs(port);
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	/* Count the potential legacy device at the end. */
1791da177e4SLinus Torvalds 	add_dev(numdevs++, port, -1);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	/* Find out the legacy device's IEEE 1284 device ID. */
182b44d3bddSMarko Kohtala 	deviceid = kmalloc(1024, GFP_KERNEL);
1831da177e4SLinus Torvalds 	if (deviceid) {
184b44d3bddSMarko Kohtala 		if (parport_device_id(numdevs - 1, deviceid, 1024) > 2)
1851da177e4SLinus Torvalds 			detected++;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 		kfree(deviceid);
1881da177e4SLinus Torvalds 	}
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds 	if (!detected && !last_try) {
1911da177e4SLinus Torvalds 		/* No devices were detected.  Perhaps they are in some
1921da177e4SLinus Torvalds                    funny state; let's try to reset them and see if
1931da177e4SLinus Torvalds                    they wake up. */
1941da177e4SLinus Torvalds 		parport_daisy_fini(port);
1951da177e4SLinus Torvalds 		parport_write_control(port, PARPORT_CONTROL_SELECT);
1961da177e4SLinus Torvalds 		udelay(50);
1971da177e4SLinus Torvalds 		parport_write_control(port,
1981da177e4SLinus Torvalds 				       PARPORT_CONTROL_SELECT |
1991da177e4SLinus Torvalds 				       PARPORT_CONTROL_INIT);
2001da177e4SLinus Torvalds 		udelay(50);
2011da177e4SLinus Torvalds 		last_try = 1;
2021da177e4SLinus Torvalds 		goto again;
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	return detected;
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds /* Forget about devices on a physical port. */
2091da177e4SLinus Torvalds void parport_daisy_fini(struct parport *port)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	struct daisydev **p;
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 	spin_lock(&topology_lock);
2141da177e4SLinus Torvalds 	p = &topology;
2151da177e4SLinus Torvalds 	while (*p) {
2161da177e4SLinus Torvalds 		struct daisydev *dev = *p;
2171da177e4SLinus Torvalds 		if (dev->port != port) {
2181da177e4SLinus Torvalds 			p = &dev->next;
2191da177e4SLinus Torvalds 			continue;
2201da177e4SLinus Torvalds 		}
2211da177e4SLinus Torvalds 		*p = dev->next;
2221da177e4SLinus Torvalds 		kfree(dev);
2231da177e4SLinus Torvalds 	}
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	/* Gaps in the numbering could be handled better.  How should
2261da177e4SLinus Torvalds            someone enumerate through all IEEE1284.3 devices in the
2271da177e4SLinus Torvalds            topology?. */
2281da177e4SLinus Torvalds 	if (!topology) numdevs = 0;
2291da177e4SLinus Torvalds 	spin_unlock(&topology_lock);
2301da177e4SLinus Torvalds 	return;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds /**
2341da177e4SLinus Torvalds  *	parport_open - find a device by canonical device number
2351da177e4SLinus Torvalds  *	@devnum: canonical device number
2361da177e4SLinus Torvalds  *	@name: name to associate with the device
2371da177e4SLinus Torvalds  *
2381da177e4SLinus Torvalds  *	This function is similar to parport_register_device(), except
2391da177e4SLinus Torvalds  *	that it locates a device by its number rather than by the port
2401da177e4SLinus Torvalds  *	it is attached to.
2411da177e4SLinus Torvalds  *
2421da177e4SLinus Torvalds  *	All parameters except for @devnum are the same as for
2431da177e4SLinus Torvalds  *	parport_register_device().  The return value is the same as
2441da177e4SLinus Torvalds  *	for parport_register_device().
2451da177e4SLinus Torvalds  **/
2461da177e4SLinus Torvalds 
2475712cb3dSJeff Garzik struct pardevice *parport_open(int devnum, const char *name)
2481da177e4SLinus Torvalds {
2491da177e4SLinus Torvalds 	struct daisydev *p = topology;
25060f8a59dSSudip Mukherjee 	struct pardev_cb par_cb;
2511da177e4SLinus Torvalds 	struct parport *port;
2521da177e4SLinus Torvalds 	struct pardevice *dev;
2531da177e4SLinus Torvalds 	int daisy;
2541da177e4SLinus Torvalds 
25560f8a59dSSudip Mukherjee 	memset(&par_cb, 0, sizeof(par_cb));
2561da177e4SLinus Torvalds 	spin_lock(&topology_lock);
2571da177e4SLinus Torvalds 	while (p && p->devnum != devnum)
2581da177e4SLinus Torvalds 		p = p->next;
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 	if (!p) {
2611da177e4SLinus Torvalds 		spin_unlock(&topology_lock);
2621da177e4SLinus Torvalds 		return NULL;
2631da177e4SLinus Torvalds 	}
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	daisy = p->daisy;
2661da177e4SLinus Torvalds 	port = parport_get_port(p->port);
2671da177e4SLinus Torvalds 	spin_unlock(&topology_lock);
2681da177e4SLinus Torvalds 
26960f8a59dSSudip Mukherjee 	dev = parport_register_dev_model(port, name, &par_cb, devnum);
2701da177e4SLinus Torvalds 	parport_put_port(port);
2711da177e4SLinus Torvalds 	if (!dev)
2721da177e4SLinus Torvalds 		return NULL;
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	dev->daisy = daisy;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	/* Check that there really is a device to select. */
2771da177e4SLinus Torvalds 	if (daisy >= 0) {
2781da177e4SLinus Torvalds 		int selected;
2791da177e4SLinus Torvalds 		parport_claim_or_block(dev);
2801da177e4SLinus Torvalds 		selected = port->daisy;
2811da177e4SLinus Torvalds 		parport_release(dev);
2821da177e4SLinus Torvalds 
283c29a75edSMarko Kohtala 		if (selected != daisy) {
2841da177e4SLinus Torvalds 			/* No corresponding device. */
2851da177e4SLinus Torvalds 			parport_unregister_device(dev);
2861da177e4SLinus Torvalds 			return NULL;
2871da177e4SLinus Torvalds 		}
2881da177e4SLinus Torvalds 	}
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	return dev;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds /**
2941da177e4SLinus Torvalds  *	parport_close - close a device opened with parport_open()
2951da177e4SLinus Torvalds  *	@dev: device to close
2961da177e4SLinus Torvalds  *
2971da177e4SLinus Torvalds  *	This is to parport_open() as parport_unregister_device() is to
2981da177e4SLinus Torvalds  *	parport_register_device().
2991da177e4SLinus Torvalds  **/
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds void parport_close(struct pardevice *dev)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	parport_unregister_device(dev);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds /* Send a daisy-chain-style CPP command packet. */
3071da177e4SLinus Torvalds static int cpp_daisy(struct parport *port, int cmd)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	unsigned char s;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	parport_data_forward(port);
3121da177e4SLinus Torvalds 	parport_write_data(port, 0xaa); udelay(2);
3131da177e4SLinus Torvalds 	parport_write_data(port, 0x55); udelay(2);
3141da177e4SLinus Torvalds 	parport_write_data(port, 0x00); udelay(2);
3151da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
3161da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
3171da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
3181da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
3191da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
3201da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_BUSY
3211da177e4SLinus Torvalds 		  | PARPORT_STATUS_PAPEROUT
3221da177e4SLinus Torvalds 		  | PARPORT_STATUS_SELECT
3231da177e4SLinus Torvalds 		  | PARPORT_STATUS_ERROR)) {
3241da177e4SLinus Torvalds 		DPRINTK(KERN_DEBUG "%s: cpp_daisy: aa5500ff(%02x)\n",
3251da177e4SLinus Torvalds 			 port->name, s);
3261da177e4SLinus Torvalds 		return -ENXIO;
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	parport_write_data(port, 0x87); udelay(2);
3301da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
3311da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
3321da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
3331da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
3341da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
3351da177e4SLinus Torvalds 		DPRINTK(KERN_DEBUG "%s: cpp_daisy: aa5500ff87(%02x)\n",
3361da177e4SLinus Torvalds 			 port->name, s);
3371da177e4SLinus Torvalds 		return -ENXIO;
3381da177e4SLinus Torvalds 	}
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	parport_write_data(port, 0x78); udelay(2);
3411da177e4SLinus Torvalds 	parport_write_data(port, cmd); udelay(2);
3421da177e4SLinus Torvalds 	parport_frob_control(port,
3431da177e4SLinus Torvalds 			      PARPORT_CONTROL_STROBE,
3441da177e4SLinus Torvalds 			      PARPORT_CONTROL_STROBE);
3451da177e4SLinus Torvalds 	udelay(1);
3467c9cc3beSMarko Kohtala 	s = parport_read_status(port);
3471da177e4SLinus Torvalds 	parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
3481da177e4SLinus Torvalds 	udelay(1);
3491da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	return s;
3521da177e4SLinus Torvalds }
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds /* Send a mux-style CPP command packet. */
3551da177e4SLinus Torvalds static int cpp_mux(struct parport *port, int cmd)
3561da177e4SLinus Torvalds {
3571da177e4SLinus Torvalds 	unsigned char s;
3581da177e4SLinus Torvalds 	int rc;
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	parport_data_forward(port);
3611da177e4SLinus Torvalds 	parport_write_data(port, 0xaa); udelay(2);
3621da177e4SLinus Torvalds 	parport_write_data(port, 0x55); udelay(2);
3631da177e4SLinus Torvalds 	parport_write_data(port, 0xf0); udelay(2);
3641da177e4SLinus Torvalds 	parport_write_data(port, 0x0f); udelay(2);
3651da177e4SLinus Torvalds 	parport_write_data(port, 0x52); udelay(2);
3661da177e4SLinus Torvalds 	parport_write_data(port, 0xad); udelay(2);
3671da177e4SLinus Torvalds 	parport_write_data(port, cmd); udelay(2);
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	s = parport_read_status(port);
3701da177e4SLinus Torvalds 	if (!(s & PARPORT_STATUS_ACK)) {
3711da177e4SLinus Torvalds 		DPRINTK(KERN_DEBUG "%s: cpp_mux: aa55f00f52ad%02x(%02x)\n",
3721da177e4SLinus Torvalds 			 port->name, cmd, s);
3731da177e4SLinus Torvalds 		return -EIO;
3741da177e4SLinus Torvalds 	}
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	rc = (((s & PARPORT_STATUS_SELECT   ? 1 : 0) << 0) |
3771da177e4SLinus Torvalds 	      ((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) |
3781da177e4SLinus Torvalds 	      ((s & PARPORT_STATUS_BUSY     ? 0 : 1) << 2) |
3791da177e4SLinus Torvalds 	      ((s & PARPORT_STATUS_ERROR    ? 0 : 1) << 3));
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	return rc;
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds void parport_daisy_deselect_all(struct parport *port)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds 	cpp_daisy(port, 0x30);
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds int parport_daisy_select(struct parport *port, int daisy, int mode)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds 	switch (mode)
3921da177e4SLinus Torvalds 	{
3931da177e4SLinus Torvalds 		// For these modes we should switch to EPP mode:
3941da177e4SLinus Torvalds 		case IEEE1284_MODE_EPP:
3951da177e4SLinus Torvalds 		case IEEE1284_MODE_EPPSL:
3961da177e4SLinus Torvalds 		case IEEE1284_MODE_EPPSWE:
3977c9cc3beSMarko Kohtala 			return !(cpp_daisy(port, 0x20 + daisy) &
3981da177e4SLinus Torvalds 				 PARPORT_STATUS_ERROR);
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 		// For these modes we should switch to ECP mode:
4011da177e4SLinus Torvalds 		case IEEE1284_MODE_ECP:
4021da177e4SLinus Torvalds 		case IEEE1284_MODE_ECPRLE:
4031da177e4SLinus Torvalds 		case IEEE1284_MODE_ECPSWE:
4047c9cc3beSMarko Kohtala 			return !(cpp_daisy(port, 0xd0 + daisy) &
4051da177e4SLinus Torvalds 				 PARPORT_STATUS_ERROR);
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 		// Nothing was told for BECP in Daisy chain specification.
4081da177e4SLinus Torvalds 		// May be it's wise to use ECP?
4091da177e4SLinus Torvalds 		case IEEE1284_MODE_BECP:
4101da177e4SLinus Torvalds 		// Others use compat mode
4111da177e4SLinus Torvalds 		case IEEE1284_MODE_NIBBLE:
4121da177e4SLinus Torvalds 		case IEEE1284_MODE_BYTE:
4131da177e4SLinus Torvalds 		case IEEE1284_MODE_COMPAT:
4141da177e4SLinus Torvalds 		default:
4157c9cc3beSMarko Kohtala 			return !(cpp_daisy(port, 0xe0 + daisy) &
4161da177e4SLinus Torvalds 				 PARPORT_STATUS_ERROR);
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds static int mux_present(struct parport *port)
4211da177e4SLinus Torvalds {
4221da177e4SLinus Torvalds 	return cpp_mux(port, 0x51) == 3;
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds static int num_mux_ports(struct parport *port)
4261da177e4SLinus Torvalds {
4271da177e4SLinus Torvalds 	return cpp_mux(port, 0x58);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds static int select_port(struct parport *port)
4311da177e4SLinus Torvalds {
4321da177e4SLinus Torvalds 	int muxport = port->muxport;
4331da177e4SLinus Torvalds 	return cpp_mux(port, 0x60 + muxport) == muxport;
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds static int assign_addrs(struct parport *port)
4371da177e4SLinus Torvalds {
438310c8c32SMarko Kohtala 	unsigned char s;
4391da177e4SLinus Torvalds 	unsigned char daisy;
4401da177e4SLinus Torvalds 	int thisdev = numdevs;
4411da177e4SLinus Torvalds 	int detected;
4421da177e4SLinus Torvalds 	char *deviceid;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	parport_data_forward(port);
4451da177e4SLinus Torvalds 	parport_write_data(port, 0xaa); udelay(2);
4461da177e4SLinus Torvalds 	parport_write_data(port, 0x55); udelay(2);
4471da177e4SLinus Torvalds 	parport_write_data(port, 0x00); udelay(2);
4481da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
4491da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
4501da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
4511da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
4521da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
4531da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_BUSY
4541da177e4SLinus Torvalds 		  | PARPORT_STATUS_PAPEROUT
4551da177e4SLinus Torvalds 		  | PARPORT_STATUS_SELECT
4561da177e4SLinus Torvalds 		  | PARPORT_STATUS_ERROR)) {
4571da177e4SLinus Torvalds 		DPRINTK(KERN_DEBUG "%s: assign_addrs: aa5500ff(%02x)\n",
4581da177e4SLinus Torvalds 			 port->name, s);
4591da177e4SLinus Torvalds 		return 0;
4601da177e4SLinus Torvalds 	}
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	parport_write_data(port, 0x87); udelay(2);
4631da177e4SLinus Torvalds 	s = parport_read_status(port) & (PARPORT_STATUS_BUSY
4641da177e4SLinus Torvalds 					  | PARPORT_STATUS_PAPEROUT
4651da177e4SLinus Torvalds 					  | PARPORT_STATUS_SELECT
4661da177e4SLinus Torvalds 					  | PARPORT_STATUS_ERROR);
4671da177e4SLinus Torvalds 	if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
4681da177e4SLinus Torvalds 		DPRINTK(KERN_DEBUG "%s: assign_addrs: aa5500ff87(%02x)\n",
4691da177e4SLinus Torvalds 			 port->name, s);
4701da177e4SLinus Torvalds 		return 0;
4711da177e4SLinus Torvalds 	}
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 	parport_write_data(port, 0x78); udelay(2);
474310c8c32SMarko Kohtala 	s = parport_read_status(port);
4751da177e4SLinus Torvalds 
476310c8c32SMarko Kohtala 	for (daisy = 0;
477310c8c32SMarko Kohtala 	     (s & (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT))
478310c8c32SMarko Kohtala 		     == (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT)
479310c8c32SMarko Kohtala 		     && daisy < 4;
480310c8c32SMarko Kohtala 	     ++daisy) {
4811da177e4SLinus Torvalds 		parport_write_data(port, daisy);
4821da177e4SLinus Torvalds 		udelay(2);
4831da177e4SLinus Torvalds 		parport_frob_control(port,
4841da177e4SLinus Torvalds 				      PARPORT_CONTROL_STROBE,
4851da177e4SLinus Torvalds 				      PARPORT_CONTROL_STROBE);
4861da177e4SLinus Torvalds 		udelay(1);
4871da177e4SLinus Torvalds 		parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
4881da177e4SLinus Torvalds 		udelay(1);
4891da177e4SLinus Torvalds 
490310c8c32SMarko Kohtala 		add_dev(numdevs++, port, daisy);
491310c8c32SMarko Kohtala 
492310c8c32SMarko Kohtala 		/* See if this device thought it was the last in the
493310c8c32SMarko Kohtala 		 * chain. */
494310c8c32SMarko Kohtala 		if (!(s & PARPORT_STATUS_BUSY))
4951da177e4SLinus Torvalds 			break;
4961da177e4SLinus Torvalds 
497310c8c32SMarko Kohtala 		/* We are seeing pass through status now. We see
498310c8c32SMarko Kohtala 		   last_dev from next device or if last_dev does not
499310c8c32SMarko Kohtala 		   work status lines from some non-daisy chain
500310c8c32SMarko Kohtala 		   device. */
501310c8c32SMarko Kohtala 		s = parport_read_status(port);
5021da177e4SLinus Torvalds 	}
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 	parport_write_data(port, 0xff); udelay(2);
5051da177e4SLinus Torvalds 	detected = numdevs - thisdev;
5061da177e4SLinus Torvalds 	DPRINTK(KERN_DEBUG "%s: Found %d daisy-chained devices\n", port->name,
5071da177e4SLinus Torvalds 		 detected);
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 	/* Ask the new devices to introduce themselves. */
510b44d3bddSMarko Kohtala 	deviceid = kmalloc(1024, GFP_KERNEL);
5111da177e4SLinus Torvalds 	if (!deviceid) return 0;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	for (daisy = 0; thisdev < numdevs; thisdev++, daisy++)
514b44d3bddSMarko Kohtala 		parport_device_id(thisdev, deviceid, 1024);
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	kfree(deviceid);
5171da177e4SLinus Torvalds 	return detected;
5181da177e4SLinus Torvalds }
519