forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
6.5 KiB
284 lines
6.5 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Driver for the ADB controller in the Mac I/O (Hydra) chip. |
|
*/ |
|
#include <stdarg.h> |
|
#include <linux/types.h> |
|
#include <linux/errno.h> |
|
#include <linux/kernel.h> |
|
#include <linux/delay.h> |
|
#include <linux/spinlock.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/pgtable.h> |
|
#include <asm/prom.h> |
|
#include <linux/adb.h> |
|
#include <asm/io.h> |
|
#include <asm/hydra.h> |
|
#include <asm/irq.h> |
|
#include <linux/init.h> |
|
#include <linux/ioport.h> |
|
|
|
struct preg { |
|
unsigned char r; |
|
char pad[15]; |
|
}; |
|
|
|
struct adb_regs { |
|
struct preg intr; |
|
struct preg data[9]; |
|
struct preg intr_enb; |
|
struct preg dcount; |
|
struct preg error; |
|
struct preg ctrl; |
|
struct preg autopoll; |
|
struct preg active_hi; |
|
struct preg active_lo; |
|
struct preg test; |
|
}; |
|
|
|
/* Bits in intr and intr_enb registers */ |
|
#define DFB 1 /* data from bus */ |
|
#define TAG 2 /* transfer access grant */ |
|
|
|
/* Bits in dcount register */ |
|
#define HMB 0x0f /* how many bytes */ |
|
#define APD 0x10 /* auto-poll data */ |
|
|
|
/* Bits in error register */ |
|
#define NRE 1 /* no response error */ |
|
#define DLE 2 /* data lost error */ |
|
|
|
/* Bits in ctrl register */ |
|
#define TAR 1 /* transfer access request */ |
|
#define DTB 2 /* data to bus */ |
|
#define CRE 4 /* command response expected */ |
|
#define ADB_RST 8 /* ADB reset */ |
|
|
|
/* Bits in autopoll register */ |
|
#define APE 1 /* autopoll enable */ |
|
|
|
static volatile struct adb_regs __iomem *adb; |
|
static struct adb_request *current_req, *last_req; |
|
static DEFINE_SPINLOCK(macio_lock); |
|
|
|
static int macio_probe(void); |
|
static int macio_init(void); |
|
static irqreturn_t macio_adb_interrupt(int irq, void *arg); |
|
static int macio_send_request(struct adb_request *req, int sync); |
|
static int macio_adb_autopoll(int devs); |
|
static void macio_adb_poll(void); |
|
static int macio_adb_reset_bus(void); |
|
|
|
struct adb_driver macio_adb_driver = { |
|
.name = "MACIO", |
|
.probe = macio_probe, |
|
.init = macio_init, |
|
.send_request = macio_send_request, |
|
.autopoll = macio_adb_autopoll, |
|
.poll = macio_adb_poll, |
|
.reset_bus = macio_adb_reset_bus, |
|
}; |
|
|
|
int macio_probe(void) |
|
{ |
|
struct device_node *np; |
|
|
|
np = of_find_compatible_node(NULL, "adb", "chrp,adb0"); |
|
if (np) { |
|
of_node_put(np); |
|
return 0; |
|
} |
|
return -ENODEV; |
|
} |
|
|
|
int macio_init(void) |
|
{ |
|
struct device_node *adbs; |
|
struct resource r; |
|
unsigned int irq; |
|
|
|
adbs = of_find_compatible_node(NULL, "adb", "chrp,adb0"); |
|
if (adbs == 0) |
|
return -ENXIO; |
|
|
|
if (of_address_to_resource(adbs, 0, &r)) { |
|
of_node_put(adbs); |
|
return -ENXIO; |
|
} |
|
adb = ioremap(r.start, sizeof(struct adb_regs)); |
|
|
|
out_8(&adb->ctrl.r, 0); |
|
out_8(&adb->intr.r, 0); |
|
out_8(&adb->error.r, 0); |
|
out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */ |
|
out_8(&adb->active_lo.r, 0xff); |
|
out_8(&adb->autopoll.r, APE); |
|
|
|
irq = irq_of_parse_and_map(adbs, 0); |
|
of_node_put(adbs); |
|
if (request_irq(irq, macio_adb_interrupt, 0, "ADB", (void *)0)) { |
|
printk(KERN_ERR "ADB: can't get irq %d\n", irq); |
|
return -EAGAIN; |
|
} |
|
out_8(&adb->intr_enb.r, DFB | TAG); |
|
|
|
printk("adb: mac-io driver 1.0 for unified ADB\n"); |
|
|
|
return 0; |
|
} |
|
|
|
static int macio_adb_autopoll(int devs) |
|
{ |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&macio_lock, flags); |
|
out_8(&adb->active_hi.r, devs >> 8); |
|
out_8(&adb->active_lo.r, devs); |
|
out_8(&adb->autopoll.r, devs? APE: 0); |
|
spin_unlock_irqrestore(&macio_lock, flags); |
|
return 0; |
|
} |
|
|
|
static int macio_adb_reset_bus(void) |
|
{ |
|
unsigned long flags; |
|
int timeout = 1000000; |
|
|
|
/* Hrm... we may want to not lock interrupts for so |
|
* long ... oh well, who uses that chip anyway ? :) |
|
* That function will be seldom used during boot |
|
* on rare machines, so... |
|
*/ |
|
spin_lock_irqsave(&macio_lock, flags); |
|
out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | ADB_RST); |
|
while ((in_8(&adb->ctrl.r) & ADB_RST) != 0) { |
|
if (--timeout == 0) { |
|
out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) & ~ADB_RST); |
|
spin_unlock_irqrestore(&macio_lock, flags); |
|
return -1; |
|
} |
|
} |
|
spin_unlock_irqrestore(&macio_lock, flags); |
|
return 0; |
|
} |
|
|
|
/* Send an ADB command */ |
|
static int macio_send_request(struct adb_request *req, int sync) |
|
{ |
|
unsigned long flags; |
|
int i; |
|
|
|
if (req->data[0] != ADB_PACKET) |
|
return -EINVAL; |
|
|
|
for (i = 0; i < req->nbytes - 1; ++i) |
|
req->data[i] = req->data[i+1]; |
|
--req->nbytes; |
|
|
|
req->next = NULL; |
|
req->sent = 0; |
|
req->complete = 0; |
|
req->reply_len = 0; |
|
|
|
spin_lock_irqsave(&macio_lock, flags); |
|
if (current_req != 0) { |
|
last_req->next = req; |
|
last_req = req; |
|
} else { |
|
current_req = last_req = req; |
|
out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); |
|
} |
|
spin_unlock_irqrestore(&macio_lock, flags); |
|
|
|
if (sync) { |
|
while (!req->complete) |
|
macio_adb_poll(); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static irqreturn_t macio_adb_interrupt(int irq, void *arg) |
|
{ |
|
int i, n, err; |
|
struct adb_request *req = NULL; |
|
unsigned char ibuf[16]; |
|
int ibuf_len = 0; |
|
int complete = 0; |
|
int autopoll = 0; |
|
int handled = 0; |
|
|
|
spin_lock(&macio_lock); |
|
if (in_8(&adb->intr.r) & TAG) { |
|
handled = 1; |
|
if ((req = current_req) != 0) { |
|
/* put the current request in */ |
|
for (i = 0; i < req->nbytes; ++i) |
|
out_8(&adb->data[i].r, req->data[i]); |
|
out_8(&adb->dcount.r, req->nbytes & HMB); |
|
req->sent = 1; |
|
if (req->reply_expected) { |
|
out_8(&adb->ctrl.r, DTB + CRE); |
|
} else { |
|
out_8(&adb->ctrl.r, DTB); |
|
current_req = req->next; |
|
complete = 1; |
|
if (current_req) |
|
out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); |
|
} |
|
} |
|
out_8(&adb->intr.r, 0); |
|
} |
|
|
|
if (in_8(&adb->intr.r) & DFB) { |
|
handled = 1; |
|
err = in_8(&adb->error.r); |
|
if (current_req && current_req->sent) { |
|
/* this is the response to a command */ |
|
req = current_req; |
|
if (err == 0) { |
|
req->reply_len = in_8(&adb->dcount.r) & HMB; |
|
for (i = 0; i < req->reply_len; ++i) |
|
req->reply[i] = in_8(&adb->data[i].r); |
|
} |
|
current_req = req->next; |
|
complete = 1; |
|
if (current_req) |
|
out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); |
|
} else if (err == 0) { |
|
/* autopoll data */ |
|
n = in_8(&adb->dcount.r) & HMB; |
|
for (i = 0; i < n; ++i) |
|
ibuf[i] = in_8(&adb->data[i].r); |
|
ibuf_len = n; |
|
autopoll = (in_8(&adb->dcount.r) & APD) != 0; |
|
} |
|
out_8(&adb->error.r, 0); |
|
out_8(&adb->intr.r, 0); |
|
} |
|
spin_unlock(&macio_lock); |
|
if (complete && req) { |
|
void (*done)(struct adb_request *) = req->done; |
|
mb(); |
|
req->complete = 1; |
|
/* Here, we assume that if the request has a done member, the |
|
* struct request will survive to setting req->complete to 1 |
|
*/ |
|
if (done) |
|
(*done)(req); |
|
} |
|
if (ibuf_len) |
|
adb_input(ibuf, ibuf_len, autopoll); |
|
|
|
return IRQ_RETVAL(handled); |
|
} |
|
|
|
static void macio_adb_poll(void) |
|
{ |
|
unsigned long flags; |
|
|
|
local_irq_save(flags); |
|
if (in_8(&adb->intr.r) != 0) |
|
macio_adb_interrupt(0, NULL); |
|
local_irq_restore(flags); |
|
}
|
|
|