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