11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Driver for the ADB controller in the Mac I/O (Hydra) chip. 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds #include <stdarg.h> 51da177e4SLinus Torvalds #include <linux/types.h> 61da177e4SLinus Torvalds #include <linux/errno.h> 71da177e4SLinus Torvalds #include <linux/kernel.h> 81da177e4SLinus Torvalds #include <linux/delay.h> 91da177e4SLinus Torvalds #include <linux/sched.h> 101da177e4SLinus Torvalds #include <linux/spinlock.h> 111da177e4SLinus Torvalds #include <linux/interrupt.h> 121da177e4SLinus Torvalds #include <asm/prom.h> 131da177e4SLinus Torvalds #include <linux/adb.h> 141da177e4SLinus Torvalds #include <asm/io.h> 151da177e4SLinus Torvalds #include <asm/pgtable.h> 161da177e4SLinus Torvalds #include <asm/hydra.h> 171da177e4SLinus Torvalds #include <asm/irq.h> 181da177e4SLinus Torvalds #include <asm/system.h> 191da177e4SLinus Torvalds #include <linux/init.h> 20*36874579SDavid Woodhouse #include <linux/ioport.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds struct preg { 231da177e4SLinus Torvalds unsigned char r; 241da177e4SLinus Torvalds char pad[15]; 251da177e4SLinus Torvalds }; 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds struct adb_regs { 281da177e4SLinus Torvalds struct preg intr; 291da177e4SLinus Torvalds struct preg data[9]; 301da177e4SLinus Torvalds struct preg intr_enb; 311da177e4SLinus Torvalds struct preg dcount; 321da177e4SLinus Torvalds struct preg error; 331da177e4SLinus Torvalds struct preg ctrl; 341da177e4SLinus Torvalds struct preg autopoll; 351da177e4SLinus Torvalds struct preg active_hi; 361da177e4SLinus Torvalds struct preg active_lo; 371da177e4SLinus Torvalds struct preg test; 381da177e4SLinus Torvalds }; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds /* Bits in intr and intr_enb registers */ 411da177e4SLinus Torvalds #define DFB 1 /* data from bus */ 421da177e4SLinus Torvalds #define TAG 2 /* transfer access grant */ 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds /* Bits in dcount register */ 451da177e4SLinus Torvalds #define HMB 0x0f /* how many bytes */ 461da177e4SLinus Torvalds #define APD 0x10 /* auto-poll data */ 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds /* Bits in error register */ 491da177e4SLinus Torvalds #define NRE 1 /* no response error */ 501da177e4SLinus Torvalds #define DLE 2 /* data lost error */ 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds /* Bits in ctrl register */ 531da177e4SLinus Torvalds #define TAR 1 /* transfer access request */ 541da177e4SLinus Torvalds #define DTB 2 /* data to bus */ 551da177e4SLinus Torvalds #define CRE 4 /* command response expected */ 561da177e4SLinus Torvalds #define ADB_RST 8 /* ADB reset */ 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* Bits in autopoll register */ 591da177e4SLinus Torvalds #define APE 1 /* autopoll enable */ 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds static volatile struct adb_regs __iomem *adb; 621da177e4SLinus Torvalds static struct adb_request *current_req, *last_req; 631da177e4SLinus Torvalds static DEFINE_SPINLOCK(macio_lock); 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds static int macio_probe(void); 661da177e4SLinus Torvalds static int macio_init(void); 671da177e4SLinus Torvalds static irqreturn_t macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs); 681da177e4SLinus Torvalds static int macio_send_request(struct adb_request *req, int sync); 691da177e4SLinus Torvalds static int macio_adb_autopoll(int devs); 701da177e4SLinus Torvalds static void macio_adb_poll(void); 711da177e4SLinus Torvalds static int macio_adb_reset_bus(void); 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds struct adb_driver macio_adb_driver = { 741da177e4SLinus Torvalds "MACIO", 751da177e4SLinus Torvalds macio_probe, 761da177e4SLinus Torvalds macio_init, 771da177e4SLinus Torvalds macio_send_request, 781da177e4SLinus Torvalds /*macio_write,*/ 791da177e4SLinus Torvalds macio_adb_autopoll, 801da177e4SLinus Torvalds macio_adb_poll, 811da177e4SLinus Torvalds macio_adb_reset_bus 821da177e4SLinus Torvalds }; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds int macio_probe(void) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds int macio_init(void) 901da177e4SLinus Torvalds { 911da177e4SLinus Torvalds struct device_node *adbs; 92*36874579SDavid Woodhouse struct resource r; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds adbs = find_compatible_devices("adb", "chrp,adb0"); 951da177e4SLinus Torvalds if (adbs == 0) 961da177e4SLinus Torvalds return -ENXIO; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds #if 0 99*36874579SDavid Woodhouse { int i = 0; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds printk("macio_adb_init: node = %p, addrs =", adbs->node); 102*36874579SDavid Woodhouse while(!of_address_to_resource(adbs, i, &r)) 103*36874579SDavid Woodhouse printk(" %x(%x)", r.start, r.end - r.start); 1041da177e4SLinus Torvalds printk(", intrs ="); 1051da177e4SLinus Torvalds for (i = 0; i < adbs->n_intrs; ++i) 1061da177e4SLinus Torvalds printk(" %x", adbs->intrs[i].line); 1071da177e4SLinus Torvalds printk("\n"); } 1081da177e4SLinus Torvalds #endif 109*36874579SDavid Woodhouse if (of_address_to_resource(adbs, 0, &r)) 110*36874579SDavid Woodhouse return -ENXIO; 111*36874579SDavid Woodhouse adb = ioremap(r.start, sizeof(struct adb_regs)); 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds out_8(&adb->ctrl.r, 0); 1141da177e4SLinus Torvalds out_8(&adb->intr.r, 0); 1151da177e4SLinus Torvalds out_8(&adb->error.r, 0); 1161da177e4SLinus Torvalds out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */ 1171da177e4SLinus Torvalds out_8(&adb->active_lo.r, 0xff); 1181da177e4SLinus Torvalds out_8(&adb->autopoll.r, APE); 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds if (request_irq(adbs->intrs[0].line, macio_adb_interrupt, 1211da177e4SLinus Torvalds 0, "ADB", (void *)0)) { 1221da177e4SLinus Torvalds printk(KERN_ERR "ADB: can't get irq %d\n", 1231da177e4SLinus Torvalds adbs->intrs[0].line); 1241da177e4SLinus Torvalds return -EAGAIN; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds out_8(&adb->intr_enb.r, DFB | TAG); 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds printk("adb: mac-io driver 1.0 for unified ADB\n"); 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds return 0; 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds static int macio_adb_autopoll(int devs) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds unsigned long flags; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds spin_lock_irqsave(&macio_lock, flags); 1381da177e4SLinus Torvalds out_8(&adb->active_hi.r, devs >> 8); 1391da177e4SLinus Torvalds out_8(&adb->active_lo.r, devs); 1401da177e4SLinus Torvalds out_8(&adb->autopoll.r, devs? APE: 0); 1411da177e4SLinus Torvalds spin_unlock_irqrestore(&macio_lock, flags); 1421da177e4SLinus Torvalds return 0; 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds static int macio_adb_reset_bus(void) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds unsigned long flags; 1481da177e4SLinus Torvalds int timeout = 1000000; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds /* Hrm... we may want to not lock interrupts for so 1511da177e4SLinus Torvalds * long ... oh well, who uses that chip anyway ? :) 1521da177e4SLinus Torvalds * That function will be seldomly used during boot 1531da177e4SLinus Torvalds * on rare machines, so... 1541da177e4SLinus Torvalds */ 1551da177e4SLinus Torvalds spin_lock_irqsave(&macio_lock, flags); 1561da177e4SLinus Torvalds out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | ADB_RST); 1571da177e4SLinus Torvalds while ((in_8(&adb->ctrl.r) & ADB_RST) != 0) { 1581da177e4SLinus Torvalds if (--timeout == 0) { 1591da177e4SLinus Torvalds out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) & ~ADB_RST); 1601da177e4SLinus Torvalds return -1; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds spin_unlock_irqrestore(&macio_lock, flags); 1641da177e4SLinus Torvalds return 0; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds /* Send an ADB command */ 1681da177e4SLinus Torvalds static int macio_send_request(struct adb_request *req, int sync) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds unsigned long flags; 1711da177e4SLinus Torvalds int i; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds if (req->data[0] != ADB_PACKET) 1741da177e4SLinus Torvalds return -EINVAL; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds for (i = 0; i < req->nbytes - 1; ++i) 1771da177e4SLinus Torvalds req->data[i] = req->data[i+1]; 1781da177e4SLinus Torvalds --req->nbytes; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds req->next = NULL; 1811da177e4SLinus Torvalds req->sent = 0; 1821da177e4SLinus Torvalds req->complete = 0; 1831da177e4SLinus Torvalds req->reply_len = 0; 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds spin_lock_irqsave(&macio_lock, flags); 1861da177e4SLinus Torvalds if (current_req != 0) { 1871da177e4SLinus Torvalds last_req->next = req; 1881da177e4SLinus Torvalds last_req = req; 1891da177e4SLinus Torvalds } else { 1901da177e4SLinus Torvalds current_req = last_req = req; 1911da177e4SLinus Torvalds out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds spin_unlock_irqrestore(&macio_lock, flags); 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds if (sync) { 1961da177e4SLinus Torvalds while (!req->complete) 1971da177e4SLinus Torvalds macio_adb_poll(); 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds return 0; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds static irqreturn_t macio_adb_interrupt(int irq, void *arg, 2041da177e4SLinus Torvalds struct pt_regs *regs) 2051da177e4SLinus Torvalds { 2061da177e4SLinus Torvalds int i, n, err; 2071da177e4SLinus Torvalds struct adb_request *req = NULL; 2081da177e4SLinus Torvalds unsigned char ibuf[16]; 2091da177e4SLinus Torvalds int ibuf_len = 0; 2101da177e4SLinus Torvalds int complete = 0; 2111da177e4SLinus Torvalds int autopoll = 0; 2121da177e4SLinus Torvalds int handled = 0; 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds spin_lock(&macio_lock); 2151da177e4SLinus Torvalds if (in_8(&adb->intr.r) & TAG) { 2161da177e4SLinus Torvalds handled = 1; 2171da177e4SLinus Torvalds if ((req = current_req) != 0) { 2181da177e4SLinus Torvalds /* put the current request in */ 2191da177e4SLinus Torvalds for (i = 0; i < req->nbytes; ++i) 2201da177e4SLinus Torvalds out_8(&adb->data[i].r, req->data[i]); 2211da177e4SLinus Torvalds out_8(&adb->dcount.r, req->nbytes & HMB); 2221da177e4SLinus Torvalds req->sent = 1; 2231da177e4SLinus Torvalds if (req->reply_expected) { 2241da177e4SLinus Torvalds out_8(&adb->ctrl.r, DTB + CRE); 2251da177e4SLinus Torvalds } else { 2261da177e4SLinus Torvalds out_8(&adb->ctrl.r, DTB); 2271da177e4SLinus Torvalds current_req = req->next; 2281da177e4SLinus Torvalds complete = 1; 2291da177e4SLinus Torvalds if (current_req) 2301da177e4SLinus Torvalds out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds out_8(&adb->intr.r, 0); 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds if (in_8(&adb->intr.r) & DFB) { 2371da177e4SLinus Torvalds handled = 1; 2381da177e4SLinus Torvalds err = in_8(&adb->error.r); 2391da177e4SLinus Torvalds if (current_req && current_req->sent) { 2401da177e4SLinus Torvalds /* this is the response to a command */ 2411da177e4SLinus Torvalds req = current_req; 2421da177e4SLinus Torvalds if (err == 0) { 2431da177e4SLinus Torvalds req->reply_len = in_8(&adb->dcount.r) & HMB; 2441da177e4SLinus Torvalds for (i = 0; i < req->reply_len; ++i) 2451da177e4SLinus Torvalds req->reply[i] = in_8(&adb->data[i].r); 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds current_req = req->next; 2481da177e4SLinus Torvalds complete = 1; 2491da177e4SLinus Torvalds if (current_req) 2501da177e4SLinus Torvalds out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); 2511da177e4SLinus Torvalds } else if (err == 0) { 2521da177e4SLinus Torvalds /* autopoll data */ 2531da177e4SLinus Torvalds n = in_8(&adb->dcount.r) & HMB; 2541da177e4SLinus Torvalds for (i = 0; i < n; ++i) 2551da177e4SLinus Torvalds ibuf[i] = in_8(&adb->data[i].r); 2561da177e4SLinus Torvalds ibuf_len = n; 2571da177e4SLinus Torvalds autopoll = (in_8(&adb->dcount.r) & APD) != 0; 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds out_8(&adb->error.r, 0); 2601da177e4SLinus Torvalds out_8(&adb->intr.r, 0); 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds spin_unlock(&macio_lock); 2631da177e4SLinus Torvalds if (complete && req) { 2641da177e4SLinus Torvalds void (*done)(struct adb_request *) = req->done; 2651da177e4SLinus Torvalds mb(); 2661da177e4SLinus Torvalds req->complete = 1; 2671da177e4SLinus Torvalds /* Here, we assume that if the request has a done member, the 2681da177e4SLinus Torvalds * struct request will survive to setting req->complete to 1 2691da177e4SLinus Torvalds */ 2701da177e4SLinus Torvalds if (done) 2711da177e4SLinus Torvalds (*done)(req); 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds if (ibuf_len) 2741da177e4SLinus Torvalds adb_input(ibuf, ibuf_len, regs, autopoll); 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds return IRQ_RETVAL(handled); 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static void macio_adb_poll(void) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds unsigned long flags; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds local_irq_save(flags); 2841da177e4SLinus Torvalds if (in_8(&adb->intr.r) != 0) 2851da177e4SLinus Torvalds macio_adb_interrupt(0, NULL, NULL); 2861da177e4SLinus Torvalds local_irq_restore(flags); 2871da177e4SLinus Torvalds } 288