162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for the ADB controller in the Mac I/O (Hydra) chip.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/spinlock.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/pgtable.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/of_irq.h>
1562306a36Sopenharmony_ci#include <linux/adb.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/io.h>
1862306a36Sopenharmony_ci#include <asm/hydra.h>
1962306a36Sopenharmony_ci#include <asm/irq.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/ioport.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct preg {
2462306a36Sopenharmony_ci	unsigned char r;
2562306a36Sopenharmony_ci	char pad[15];
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct adb_regs {
2962306a36Sopenharmony_ci	struct preg intr;
3062306a36Sopenharmony_ci	struct preg data[9];
3162306a36Sopenharmony_ci	struct preg intr_enb;
3262306a36Sopenharmony_ci	struct preg dcount;
3362306a36Sopenharmony_ci	struct preg error;
3462306a36Sopenharmony_ci	struct preg ctrl;
3562306a36Sopenharmony_ci	struct preg autopoll;
3662306a36Sopenharmony_ci	struct preg active_hi;
3762306a36Sopenharmony_ci	struct preg active_lo;
3862306a36Sopenharmony_ci	struct preg test;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Bits in intr and intr_enb registers */
4262306a36Sopenharmony_ci#define DFB	1		/* data from bus */
4362306a36Sopenharmony_ci#define TAG	2		/* transfer access grant */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Bits in dcount register */
4662306a36Sopenharmony_ci#define HMB	0x0f		/* how many bytes */
4762306a36Sopenharmony_ci#define APD	0x10		/* auto-poll data */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Bits in error register */
5062306a36Sopenharmony_ci#define NRE	1		/* no response error */
5162306a36Sopenharmony_ci#define DLE	2		/* data lost error */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Bits in ctrl register */
5462306a36Sopenharmony_ci#define TAR	1		/* transfer access request */
5562306a36Sopenharmony_ci#define DTB	2		/* data to bus */
5662306a36Sopenharmony_ci#define CRE	4		/* command response expected */
5762306a36Sopenharmony_ci#define ADB_RST	8		/* ADB reset */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Bits in autopoll register */
6062306a36Sopenharmony_ci#define APE	1		/* autopoll enable */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic volatile struct adb_regs __iomem *adb;
6362306a36Sopenharmony_cistatic struct adb_request *current_req, *last_req;
6462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(macio_lock);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int macio_probe(void);
6762306a36Sopenharmony_cistatic int macio_init(void);
6862306a36Sopenharmony_cistatic irqreturn_t macio_adb_interrupt(int irq, void *arg);
6962306a36Sopenharmony_cistatic int macio_send_request(struct adb_request *req, int sync);
7062306a36Sopenharmony_cistatic int macio_adb_autopoll(int devs);
7162306a36Sopenharmony_cistatic void macio_adb_poll(void);
7262306a36Sopenharmony_cistatic int macio_adb_reset_bus(void);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistruct adb_driver macio_adb_driver = {
7562306a36Sopenharmony_ci	.name         = "MACIO",
7662306a36Sopenharmony_ci	.probe        = macio_probe,
7762306a36Sopenharmony_ci	.init         = macio_init,
7862306a36Sopenharmony_ci	.send_request = macio_send_request,
7962306a36Sopenharmony_ci	.autopoll     = macio_adb_autopoll,
8062306a36Sopenharmony_ci	.poll         = macio_adb_poll,
8162306a36Sopenharmony_ci	.reset_bus    = macio_adb_reset_bus,
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciint macio_probe(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct device_node *np;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, "adb", "chrp,adb0");
8962306a36Sopenharmony_ci	if (np) {
9062306a36Sopenharmony_ci		of_node_put(np);
9162306a36Sopenharmony_ci		return 0;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci	return -ENODEV;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciint macio_init(void)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct device_node *adbs;
9962306a36Sopenharmony_ci	struct resource r;
10062306a36Sopenharmony_ci	unsigned int irq;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	adbs = of_find_compatible_node(NULL, "adb", "chrp,adb0");
10362306a36Sopenharmony_ci	if (!adbs)
10462306a36Sopenharmony_ci		return -ENXIO;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (of_address_to_resource(adbs, 0, &r)) {
10762306a36Sopenharmony_ci		of_node_put(adbs);
10862306a36Sopenharmony_ci		return -ENXIO;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	adb = ioremap(r.start, sizeof(struct adb_regs));
11162306a36Sopenharmony_ci	if (!adb) {
11262306a36Sopenharmony_ci		of_node_put(adbs);
11362306a36Sopenharmony_ci		return -ENOMEM;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	out_8(&adb->ctrl.r, 0);
11762306a36Sopenharmony_ci	out_8(&adb->intr.r, 0);
11862306a36Sopenharmony_ci	out_8(&adb->error.r, 0);
11962306a36Sopenharmony_ci	out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */
12062306a36Sopenharmony_ci	out_8(&adb->active_lo.r, 0xff);
12162306a36Sopenharmony_ci	out_8(&adb->autopoll.r, APE);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	irq = irq_of_parse_and_map(adbs, 0);
12462306a36Sopenharmony_ci	of_node_put(adbs);
12562306a36Sopenharmony_ci	if (request_irq(irq, macio_adb_interrupt, 0, "ADB", (void *)0)) {
12662306a36Sopenharmony_ci		printk(KERN_ERR "ADB: can't get irq %d\n", irq);
12762306a36Sopenharmony_ci		return -EAGAIN;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	out_8(&adb->intr_enb.r, DFB | TAG);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	printk("adb: mac-io driver 1.0 for unified ADB\n");
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int macio_adb_autopoll(int devs)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	unsigned long flags;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	spin_lock_irqsave(&macio_lock, flags);
14162306a36Sopenharmony_ci	out_8(&adb->active_hi.r, devs >> 8);
14262306a36Sopenharmony_ci	out_8(&adb->active_lo.r, devs);
14362306a36Sopenharmony_ci	out_8(&adb->autopoll.r, devs? APE: 0);
14462306a36Sopenharmony_ci	spin_unlock_irqrestore(&macio_lock, flags);
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int macio_adb_reset_bus(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	unsigned long flags;
15162306a36Sopenharmony_ci	int timeout = 1000000;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* Hrm... we may want to not lock interrupts for so
15462306a36Sopenharmony_ci	 * long ... oh well, who uses that chip anyway ? :)
15562306a36Sopenharmony_ci	 * That function will be seldom used during boot
15662306a36Sopenharmony_ci	 * on rare machines, so...
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	spin_lock_irqsave(&macio_lock, flags);
15962306a36Sopenharmony_ci	out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | ADB_RST);
16062306a36Sopenharmony_ci	while ((in_8(&adb->ctrl.r) & ADB_RST) != 0) {
16162306a36Sopenharmony_ci		if (--timeout == 0) {
16262306a36Sopenharmony_ci			out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) & ~ADB_RST);
16362306a36Sopenharmony_ci			spin_unlock_irqrestore(&macio_lock, flags);
16462306a36Sopenharmony_ci			return -1;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci	spin_unlock_irqrestore(&macio_lock, flags);
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* Send an ADB command */
17262306a36Sopenharmony_cistatic int macio_send_request(struct adb_request *req, int sync)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	unsigned long flags;
17562306a36Sopenharmony_ci	int i;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (req->data[0] != ADB_PACKET)
17862306a36Sopenharmony_ci		return -EINVAL;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	for (i = 0; i < req->nbytes - 1; ++i)
18162306a36Sopenharmony_ci		req->data[i] = req->data[i+1];
18262306a36Sopenharmony_ci	--req->nbytes;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	req->next = NULL;
18562306a36Sopenharmony_ci	req->sent = 0;
18662306a36Sopenharmony_ci	req->complete = 0;
18762306a36Sopenharmony_ci	req->reply_len = 0;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	spin_lock_irqsave(&macio_lock, flags);
19062306a36Sopenharmony_ci	if (current_req) {
19162306a36Sopenharmony_ci		last_req->next = req;
19262306a36Sopenharmony_ci		last_req = req;
19362306a36Sopenharmony_ci	} else {
19462306a36Sopenharmony_ci		current_req = last_req = req;
19562306a36Sopenharmony_ci		out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci	spin_unlock_irqrestore(&macio_lock, flags);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (sync) {
20062306a36Sopenharmony_ci		while (!req->complete)
20162306a36Sopenharmony_ci			macio_adb_poll();
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic irqreturn_t macio_adb_interrupt(int irq, void *arg)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int i, n, err;
21062306a36Sopenharmony_ci	struct adb_request *req = NULL;
21162306a36Sopenharmony_ci	unsigned char ibuf[16];
21262306a36Sopenharmony_ci	int ibuf_len = 0;
21362306a36Sopenharmony_ci	int complete = 0;
21462306a36Sopenharmony_ci	int autopoll = 0;
21562306a36Sopenharmony_ci	int handled = 0;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	spin_lock(&macio_lock);
21862306a36Sopenharmony_ci	if (in_8(&adb->intr.r) & TAG) {
21962306a36Sopenharmony_ci		handled = 1;
22062306a36Sopenharmony_ci		req = current_req;
22162306a36Sopenharmony_ci		if (req) {
22262306a36Sopenharmony_ci			/* put the current request in */
22362306a36Sopenharmony_ci			for (i = 0; i < req->nbytes; ++i)
22462306a36Sopenharmony_ci				out_8(&adb->data[i].r, req->data[i]);
22562306a36Sopenharmony_ci			out_8(&adb->dcount.r, req->nbytes & HMB);
22662306a36Sopenharmony_ci			req->sent = 1;
22762306a36Sopenharmony_ci			if (req->reply_expected) {
22862306a36Sopenharmony_ci				out_8(&adb->ctrl.r, DTB + CRE);
22962306a36Sopenharmony_ci			} else {
23062306a36Sopenharmony_ci				out_8(&adb->ctrl.r, DTB);
23162306a36Sopenharmony_ci				current_req = req->next;
23262306a36Sopenharmony_ci				complete = 1;
23362306a36Sopenharmony_ci				if (current_req)
23462306a36Sopenharmony_ci					out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
23562306a36Sopenharmony_ci			}
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci		out_8(&adb->intr.r, 0);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (in_8(&adb->intr.r) & DFB) {
24162306a36Sopenharmony_ci		handled = 1;
24262306a36Sopenharmony_ci		err = in_8(&adb->error.r);
24362306a36Sopenharmony_ci		if (current_req && current_req->sent) {
24462306a36Sopenharmony_ci			/* this is the response to a command */
24562306a36Sopenharmony_ci			req = current_req;
24662306a36Sopenharmony_ci			if (err == 0) {
24762306a36Sopenharmony_ci				req->reply_len = in_8(&adb->dcount.r) & HMB;
24862306a36Sopenharmony_ci				for (i = 0; i < req->reply_len; ++i)
24962306a36Sopenharmony_ci					req->reply[i] = in_8(&adb->data[i].r);
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci			current_req = req->next;
25262306a36Sopenharmony_ci			complete = 1;
25362306a36Sopenharmony_ci			if (current_req)
25462306a36Sopenharmony_ci				out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
25562306a36Sopenharmony_ci		} else if (err == 0) {
25662306a36Sopenharmony_ci			/* autopoll data */
25762306a36Sopenharmony_ci			n = in_8(&adb->dcount.r) & HMB;
25862306a36Sopenharmony_ci			for (i = 0; i < n; ++i)
25962306a36Sopenharmony_ci				ibuf[i] = in_8(&adb->data[i].r);
26062306a36Sopenharmony_ci			ibuf_len = n;
26162306a36Sopenharmony_ci			autopoll = (in_8(&adb->dcount.r) & APD) != 0;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci		out_8(&adb->error.r, 0);
26462306a36Sopenharmony_ci		out_8(&adb->intr.r, 0);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	spin_unlock(&macio_lock);
26762306a36Sopenharmony_ci	if (complete && req) {
26862306a36Sopenharmony_ci	    void (*done)(struct adb_request *) = req->done;
26962306a36Sopenharmony_ci	    mb();
27062306a36Sopenharmony_ci	    req->complete = 1;
27162306a36Sopenharmony_ci	    /* Here, we assume that if the request has a done member, the
27262306a36Sopenharmony_ci    	     * struct request will survive to setting req->complete to 1
27362306a36Sopenharmony_ci	     */
27462306a36Sopenharmony_ci	    if (done)
27562306a36Sopenharmony_ci		(*done)(req);
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci	if (ibuf_len)
27862306a36Sopenharmony_ci		adb_input(ibuf, ibuf_len, autopoll);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void macio_adb_poll(void)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	unsigned long flags;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	local_irq_save(flags);
28862306a36Sopenharmony_ci	if (in_8(&adb->intr.r) != 0)
28962306a36Sopenharmony_ci		macio_adb_interrupt(0, NULL);
29062306a36Sopenharmony_ci	local_irq_restore(flags);
29162306a36Sopenharmony_ci}
292