Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

6
arch/m68k/mac/Makefile Normal file
View File

@@ -0,0 +1,6 @@
#
# Makefile for Linux arch/m68k/mac source directory
#
obj-y := config.o macints.o iop.o via.o oss.o psc.o \
baboon.o macboing.o misc.o

114
arch/m68k/mac/baboon.c Normal file
View File

@@ -0,0 +1,114 @@
/*
* Baboon Custom IC Management
*
* The Baboon custom IC controls the IDE, PCMCIA and media bay on the
* PowerBook 190. It multiplexes multiple interrupt sources onto the
* Nubus slot $C interrupt.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_baboon.h>
/* #define DEBUG_IRQS */
int baboon_present;
static volatile struct baboon *baboon;
#if 0
extern int macide_ack_intr(struct ata_channel *);
#endif
/*
* Baboon initialization.
*/
void __init baboon_init(void)
{
if (macintosh_config->ident != MAC_MODEL_PB190) {
baboon = NULL;
baboon_present = 0;
return;
}
baboon = (struct baboon *) BABOON_BASE;
baboon_present = 1;
printk("Baboon detected at %p\n", baboon);
}
/*
* Baboon interrupt handler. This works a lot like a VIA.
*/
static void baboon_irq(unsigned int irq, struct irq_desc *desc)
{
int irq_bit, irq_num;
unsigned char events;
#ifdef DEBUG_IRQS
printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X\n",
(uint) baboon->mb_control, (uint) baboon->mb_ifr,
(uint) baboon->mb_status);
#endif
events = baboon->mb_ifr & 0x07;
if (!events)
return;
irq_num = IRQ_BABOON_0;
irq_bit = 1;
do {
if (events & irq_bit) {
baboon->mb_ifr &= ~irq_bit;
generic_handle_irq(irq_num);
}
irq_bit <<= 1;
irq_num++;
} while(events >= irq_bit);
#if 0
if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL);
/* for now we need to smash all interrupts */
baboon->mb_ifr &= ~events;
#endif
}
/*
* Register the Baboon interrupt dispatcher on nubus slot $C.
*/
void __init baboon_register_interrupts(void)
{
irq_set_chained_handler(IRQ_NUBUS_C, baboon_irq);
}
/*
* The means for masking individual Baboon interrupts remains a mystery.
* However, since we only use the IDE IRQ, we can just enable/disable all
* Baboon interrupts. If/when we handle more than one Baboon IRQ, we must
* either figure out how to mask them individually or else implement the
* same workaround that's used for NuBus slots (see nubus_disabled and
* via_nubus_irq_shutdown).
*/
void baboon_irq_enable(int irq)
{
#ifdef DEBUG_IRQUSE
printk("baboon_irq_enable(%d)\n", irq);
#endif
mac_irq_enable(irq_get_irq_data(IRQ_NUBUS_C));
}
void baboon_irq_disable(int irq)
{
#ifdef DEBUG_IRQUSE
printk("baboon_irq_disable(%d)\n", irq);
#endif
mac_irq_disable(irq_get_irq_data(IRQ_NUBUS_C));
}

1048
arch/m68k/mac/config.c Normal file

File diff suppressed because it is too large Load Diff

624
arch/m68k/mac/iop.c Normal file
View File

@@ -0,0 +1,624 @@
/*
* I/O Processor (IOP) management
* Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice and this list of conditions.
* 2. Redistributions in binary form must reproduce the above copyright
* notice and this list of conditions in the documentation and/or other
* materials provided with the distribution.
*/
/*
* The IOP chips are used in the IIfx and some Quadras (900, 950) to manage
* serial and ADB. They are actually a 6502 processor and some glue logic.
*
* 990429 (jmt) - Initial implementation, just enough to knock the SCC IOP
* into compatible mode so nobody has to fiddle with the
* Serial Switch control panel anymore.
* 990603 (jmt) - Added code to grab the correct ISM IOP interrupt for OSS
* and non-OSS machines (at least I hope it's correct on a
* non-OSS machine -- someone with a Q900 or Q950 needs to
* check this.)
* 990605 (jmt) - Rearranged things a bit wrt IOP detection; iop_present is
* gone, IOP base addresses are now in an array and the
* globally-visible functions take an IOP number instead of an
* an actual base address.
* 990610 (jmt) - Finished the message passing framework and it seems to work.
* Sending _definitely_ works; my adb-bus.c mods can send
* messages and receive the MSG_COMPLETED status back from the
* IOP. The trick now is figuring out the message formats.
* 990611 (jmt) - More cleanups. Fixed problem where unclaimed messages on a
* receive channel were never properly acknowledged. Bracketed
* the remaining debug printk's with #ifdef's and disabled
* debugging. I can now type on the console.
* 990612 (jmt) - Copyright notice added. Reworked the way replies are handled.
* It turns out that replies are placed back in the send buffer
* for that channel; messages on the receive channels are always
* unsolicited messages from the IOP (and our replies to them
* should go back in the receive channel.) Also added tracking
* of device names to the listener functions ala the interrupt
* handlers.
* 990729 (jmt) - Added passing of pt_regs structure to IOP handlers. This is
* used by the new unified ADB driver.
*
* TODO:
*
* o Something should be periodically checking iop_alive() to make sure the
* IOP hasn't died.
* o Some of the IOP manager routines need better error checking and
* return codes. Nothing major, just prettying up.
*/
/*
* -----------------------
* IOP Message Passing 101
* -----------------------
*
* The host talks to the IOPs using a rather simple message-passing scheme via
* a shared memory area in the IOP RAM. Each IOP has seven "channels"; each
* channel is conneced to a specific software driver on the IOP. For example
* on the SCC IOP there is one channel for each serial port. Each channel has
* an incoming and and outgoing message queue with a depth of one.
*
* A message is 32 bytes plus a state byte for the channel (MSG_IDLE, MSG_NEW,
* MSG_RCVD, MSG_COMPLETE). To send a message you copy the message into the
* buffer, set the state to MSG_NEW and signal the IOP by setting the IRQ flag
* in the IOP control to 1. The IOP will move the state to MSG_RCVD when it
* receives the message and then to MSG_COMPLETE when the message processing
* has completed. It is the host's responsibility at that point to read the
* reply back out of the send channel buffer and reset the channel state back
* to MSG_IDLE.
*
* To receive message from the IOP the same procedure is used except the roles
* are reversed. That is, the IOP puts message in the channel with a state of
* MSG_NEW, and the host receives the message and move its state to MSG_RCVD
* and then to MSG_COMPLETE when processing is completed and the reply (if any)
* has been placed back in the receive channel. The IOP will then reset the
* channel state to MSG_IDLE.
*
* Two sets of host interrupts are provided, INT0 and INT1. Both appear on one
* interrupt level; they are distinguished by a pair of bits in the IOP status
* register. The IOP will raise INT0 when one or more messages in the send
* channels have gone to the MSG_COMPLETE state and it will raise INT1 when one
* or more messages on the receive channels have gone to the MSG_NEW state.
*
* Since each channel handles only one message we have to implement a small
* interrupt-driven queue on our end. Messages to be sent are placed on the
* queue for sending and contain a pointer to an optional callback function.
* The handler for a message is called when the message state goes to
* MSG_COMPLETE.
*
* For receiving message we maintain a list of handler functions to call when
* a message is received on that IOP/channel combination. The handlers are
* called much like an interrupt handler and are passed a copy of the message
* from the IOP. The message state will be in MSG_RCVD while the handler runs;
* it is the handler's responsibility to call iop_complete_message() when
* finished; this function moves the message state to MSG_COMPLETE and signals
* the IOP. This two-step process is provided to allow the handler to defer
* message processing to a bottom-half handler if the processing will take
* a significant amount of time (handlers are called at interrupt time so they
* should execute quickly.)
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_iop.h>
/*#define DEBUG_IOP*/
/* Set to non-zero if the IOPs are present. Set by iop_init() */
int iop_scc_present,iop_ism_present;
/* structure for tracking channel listeners */
struct listener {
const char *devname;
void (*handler)(struct iop_msg *);
};
/*
* IOP structures for the two IOPs
*
* The SCC IOP controls both serial ports (A and B) as its two functions.
* The ISM IOP controls the SWIM (floppy drive) and ADB.
*/
static volatile struct mac_iop *iop_base[NUM_IOPS];
/*
* IOP message queues
*/
static struct iop_msg iop_msg_pool[NUM_IOP_MSGS];
static struct iop_msg *iop_send_queue[NUM_IOPS][NUM_IOP_CHAN];
static struct listener iop_listeners[NUM_IOPS][NUM_IOP_CHAN];
irqreturn_t iop_ism_irq(int, void *);
/*
* Private access functions
*/
static __inline__ void iop_loadaddr(volatile struct mac_iop *iop, __u16 addr)
{
iop->ram_addr_lo = addr;
iop->ram_addr_hi = addr >> 8;
}
static __inline__ __u8 iop_readb(volatile struct mac_iop *iop, __u16 addr)
{
iop->ram_addr_lo = addr;
iop->ram_addr_hi = addr >> 8;
return iop->ram_data;
}
static __inline__ void iop_writeb(volatile struct mac_iop *iop, __u16 addr, __u8 data)
{
iop->ram_addr_lo = addr;
iop->ram_addr_hi = addr >> 8;
iop->ram_data = data;
}
static __inline__ void iop_stop(volatile struct mac_iop *iop)
{
iop->status_ctrl &= ~IOP_RUN;
}
static __inline__ void iop_start(volatile struct mac_iop *iop)
{
iop->status_ctrl = IOP_RUN | IOP_AUTOINC;
}
static __inline__ void iop_bypass(volatile struct mac_iop *iop)
{
iop->status_ctrl |= IOP_BYPASS;
}
static __inline__ void iop_interrupt(volatile struct mac_iop *iop)
{
iop->status_ctrl |= IOP_IRQ;
}
static int iop_alive(volatile struct mac_iop *iop)
{
int retval;
retval = (iop_readb(iop, IOP_ADDR_ALIVE) == 0xFF);
iop_writeb(iop, IOP_ADDR_ALIVE, 0);
return retval;
}
static struct iop_msg *iop_alloc_msg(void)
{
int i;
unsigned long flags;
local_irq_save(flags);
for (i = 0 ; i < NUM_IOP_MSGS ; i++) {
if (iop_msg_pool[i].status == IOP_MSGSTATUS_UNUSED) {
iop_msg_pool[i].status = IOP_MSGSTATUS_WAITING;
local_irq_restore(flags);
return &iop_msg_pool[i];
}
}
local_irq_restore(flags);
return NULL;
}
static void iop_free_msg(struct iop_msg *msg)
{
msg->status = IOP_MSGSTATUS_UNUSED;
}
/*
* This is called by the startup code before anything else. Its purpose
* is to find and initialize the IOPs early in the boot sequence, so that
* the serial IOP can be placed into bypass mode _before_ we try to
* initialize the serial console.
*/
void __init iop_preinit(void)
{
if (macintosh_config->scc_type == MAC_SCC_IOP) {
if (macintosh_config->ident == MAC_MODEL_IIFX) {
iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_IIFX;
} else {
iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_QUADRA;
}
iop_base[IOP_NUM_SCC]->status_ctrl = 0x87;
iop_scc_present = 1;
} else {
iop_base[IOP_NUM_SCC] = NULL;
iop_scc_present = 0;
}
if (macintosh_config->adb_type == MAC_ADB_IOP) {
if (macintosh_config->ident == MAC_MODEL_IIFX) {
iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_IIFX;
} else {
iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA;
}
iop_base[IOP_NUM_ISM]->status_ctrl = 0;
iop_ism_present = 1;
} else {
iop_base[IOP_NUM_ISM] = NULL;
iop_ism_present = 0;
}
}
/*
* Initialize the IOPs, if present.
*/
void __init iop_init(void)
{
int i;
if (iop_scc_present) {
printk("IOP: detected SCC IOP at %p\n", iop_base[IOP_NUM_SCC]);
}
if (iop_ism_present) {
printk("IOP: detected ISM IOP at %p\n", iop_base[IOP_NUM_ISM]);
iop_start(iop_base[IOP_NUM_ISM]);
iop_alive(iop_base[IOP_NUM_ISM]); /* clears the alive flag */
}
/* Make the whole pool available and empty the queues */
for (i = 0 ; i < NUM_IOP_MSGS ; i++) {
iop_msg_pool[i].status = IOP_MSGSTATUS_UNUSED;
}
for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
iop_send_queue[IOP_NUM_SCC][i] = NULL;
iop_send_queue[IOP_NUM_ISM][i] = NULL;
iop_listeners[IOP_NUM_SCC][i].devname = NULL;
iop_listeners[IOP_NUM_SCC][i].handler = NULL;
iop_listeners[IOP_NUM_ISM][i].devname = NULL;
iop_listeners[IOP_NUM_ISM][i].handler = NULL;
}
}
/*
* Register the interrupt handler for the IOPs.
* TODO: might be wrong for non-OSS machines. Anyone?
*/
void __init iop_register_interrupts(void)
{
if (iop_ism_present) {
if (macintosh_config->ident == MAC_MODEL_IIFX) {
if (request_irq(IRQ_MAC_ADB, iop_ism_irq, 0,
"ISM IOP", (void *)IOP_NUM_ISM))
pr_err("Couldn't register ISM IOP interrupt\n");
} else {
if (request_irq(IRQ_VIA2_0, iop_ism_irq, 0, "ISM IOP",
(void *)IOP_NUM_ISM))
pr_err("Couldn't register ISM IOP interrupt\n");
}
if (!iop_alive(iop_base[IOP_NUM_ISM])) {
printk("IOP: oh my god, they killed the ISM IOP!\n");
} else {
printk("IOP: the ISM IOP seems to be alive.\n");
}
}
}
/*
* Register or unregister a listener for a specific IOP and channel
*
* If the handler pointer is NULL the current listener (if any) is
* unregistered. Otherwise the new listener is registered provided
* there is no existing listener registered.
*/
int iop_listen(uint iop_num, uint chan,
void (*handler)(struct iop_msg *),
const char *devname)
{
if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL;
if (chan >= NUM_IOP_CHAN) return -EINVAL;
if (iop_listeners[iop_num][chan].handler && handler) return -EINVAL;
iop_listeners[iop_num][chan].devname = devname;
iop_listeners[iop_num][chan].handler = handler;
return 0;
}
/*
* Complete reception of a message, which just means copying the reply
* into the buffer, setting the channel state to MSG_COMPLETE and
* notifying the IOP.
*/
void iop_complete_message(struct iop_msg *msg)
{
int iop_num = msg->iop_num;
int chan = msg->channel;
int i,offset;
#ifdef DEBUG_IOP
printk("iop_complete(%p): iop %d chan %d\n", msg, msg->iop_num, msg->channel);
#endif
offset = IOP_ADDR_RECV_MSG + (msg->channel * IOP_MSG_LEN);
for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
iop_writeb(iop_base[iop_num], offset, msg->reply[i]);
}
iop_writeb(iop_base[iop_num],
IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE);
iop_interrupt(iop_base[msg->iop_num]);
iop_free_msg(msg);
}
/*
* Actually put a message into a send channel buffer
*/
static void iop_do_send(struct iop_msg *msg)
{
volatile struct mac_iop *iop = iop_base[msg->iop_num];
int i,offset;
offset = IOP_ADDR_SEND_MSG + (msg->channel * IOP_MSG_LEN);
for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
iop_writeb(iop, offset, msg->message[i]);
}
iop_writeb(iop, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW);
iop_interrupt(iop);
}
/*
* Handle sending a message on a channel that
* has gone into the IOP_MSG_COMPLETE state.
*/
static void iop_handle_send(uint iop_num, uint chan)
{
volatile struct mac_iop *iop = iop_base[iop_num];
struct iop_msg *msg,*msg2;
int i,offset;
#ifdef DEBUG_IOP
printk("iop_handle_send: iop %d channel %d\n", iop_num, chan);
#endif
iop_writeb(iop, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE);
if (!(msg = iop_send_queue[iop_num][chan])) return;
msg->status = IOP_MSGSTATUS_COMPLETE;
offset = IOP_ADDR_SEND_MSG + (chan * IOP_MSG_LEN);
for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
msg->reply[i] = iop_readb(iop, offset);
}
if (msg->handler) (*msg->handler)(msg);
msg2 = msg;
msg = msg->next;
iop_free_msg(msg2);
iop_send_queue[iop_num][chan] = msg;
if (msg) iop_do_send(msg);
}
/*
* Handle reception of a message on a channel that has
* gone into the IOP_MSG_NEW state.
*/
static void iop_handle_recv(uint iop_num, uint chan)
{
volatile struct mac_iop *iop = iop_base[iop_num];
int i,offset;
struct iop_msg *msg;
#ifdef DEBUG_IOP
printk("iop_handle_recv: iop %d channel %d\n", iop_num, chan);
#endif
msg = iop_alloc_msg();
msg->iop_num = iop_num;
msg->channel = chan;
msg->status = IOP_MSGSTATUS_UNSOL;
msg->handler = iop_listeners[iop_num][chan].handler;
offset = IOP_ADDR_RECV_MSG + (chan * IOP_MSG_LEN);
for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
msg->message[i] = iop_readb(iop, offset);
}
iop_writeb(iop, IOP_ADDR_RECV_STATE + chan, IOP_MSG_RCVD);
/* If there is a listener, call it now. Otherwise complete */
/* the message ourselves to avoid possible stalls. */
if (msg->handler) {
(*msg->handler)(msg);
} else {
#ifdef DEBUG_IOP
printk("iop_handle_recv: unclaimed message on iop %d channel %d\n", iop_num, chan);
printk("iop_handle_recv:");
for (i = 0 ; i < IOP_MSG_LEN ; i++) {
printk(" %02X", (uint) msg->message[i]);
}
printk("\n");
#endif
iop_complete_message(msg);
}
}
/*
* Send a message
*
* The message is placed at the end of the send queue. Afterwards if the
* channel is idle we force an immediate send of the next message in the
* queue.
*/
int iop_send_message(uint iop_num, uint chan, void *privdata,
uint msg_len, __u8 *msg_data,
void (*handler)(struct iop_msg *))
{
struct iop_msg *msg, *q;
if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL;
if (chan >= NUM_IOP_CHAN) return -EINVAL;
if (msg_len > IOP_MSG_LEN) return -EINVAL;
msg = iop_alloc_msg();
if (!msg) return -ENOMEM;
msg->next = NULL;
msg->status = IOP_MSGSTATUS_WAITING;
msg->iop_num = iop_num;
msg->channel = chan;
msg->caller_priv = privdata;
memcpy(msg->message, msg_data, msg_len);
msg->handler = handler;
if (!(q = iop_send_queue[iop_num][chan])) {
iop_send_queue[iop_num][chan] = msg;
} else {
while (q->next) q = q->next;
q->next = msg;
}
if (iop_readb(iop_base[iop_num],
IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE) {
iop_do_send(msg);
}
return 0;
}
/*
* Upload code to the shared RAM of an IOP.
*/
void iop_upload_code(uint iop_num, __u8 *code_start,
uint code_len, __u16 shared_ram_start)
{
if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return;
iop_loadaddr(iop_base[iop_num], shared_ram_start);
while (code_len--) {
iop_base[iop_num]->ram_data = *code_start++;
}
}
/*
* Download code from the shared RAM of an IOP.
*/
void iop_download_code(uint iop_num, __u8 *code_start,
uint code_len, __u16 shared_ram_start)
{
if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return;
iop_loadaddr(iop_base[iop_num], shared_ram_start);
while (code_len--) {
*code_start++ = iop_base[iop_num]->ram_data;
}
}
/*
* Compare the code in the shared RAM of an IOP with a copy in system memory
* and return 0 on match or the first nonmatching system memory address on
* failure.
*/
__u8 *iop_compare_code(uint iop_num, __u8 *code_start,
uint code_len, __u16 shared_ram_start)
{
if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return code_start;
iop_loadaddr(iop_base[iop_num], shared_ram_start);
while (code_len--) {
if (*code_start != iop_base[iop_num]->ram_data) {
return code_start;
}
code_start++;
}
return (__u8 *) 0;
}
/*
* Handle an ISM IOP interrupt
*/
irqreturn_t iop_ism_irq(int irq, void *dev_id)
{
uint iop_num = (uint) dev_id;
volatile struct mac_iop *iop = iop_base[iop_num];
int i,state;
#ifdef DEBUG_IOP
printk("iop_ism_irq: status = %02X\n", (uint) iop->status_ctrl);
#endif
/* INT0 indicates a state change on an outgoing message channel */
if (iop->status_ctrl & IOP_INT0) {
iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
#ifdef DEBUG_IOP
printk("iop_ism_irq: new status = %02X, send states",
(uint) iop->status_ctrl);
#endif
for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
#ifdef DEBUG_IOP
printk(" %02X", state);
#endif
if (state == IOP_MSG_COMPLETE) {
iop_handle_send(iop_num, i);
}
}
#ifdef DEBUG_IOP
printk("\n");
#endif
}
if (iop->status_ctrl & IOP_INT1) { /* INT1 for incoming msgs */
iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
#ifdef DEBUG_IOP
printk("iop_ism_irq: new status = %02X, recv states",
(uint) iop->status_ctrl);
#endif
for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
#ifdef DEBUG_IOP
printk(" %02X", state);
#endif
if (state == IOP_MSG_NEW) {
iop_handle_recv(iop_num, i);
}
}
#ifdef DEBUG_IOP
printk("\n");
#endif
}
return IRQ_HANDLED;
}

View File

@@ -0,0 +1,75 @@
.byte \
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0xFF,0x0F,0xF0,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x00,0xFF,0xFF,0x0F,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x0F,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xF0,0xFF,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,\
0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0xF0,0x00,0x00,\
0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,\
0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\
0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\
0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,\
0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,\
0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,\
0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,\
0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,\
0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,\
0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,\
0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,\
0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00

309
arch/m68k/mac/macboing.c Normal file
View File

@@ -0,0 +1,309 @@
/*
* Mac bong noise generator. Note - we ought to put a boingy noise
* here 8)
*
* ----------------------------------------------------------------------
* 16.11.98:
* rewrote some functions, added support for Enhanced ASC (Quadras)
* after the NetBSD asc.c console bell patch by Colin Wood/Frederick Bruck
* Juergen Mellinger (juergen.mellinger@t-online.de)
*/
#include <linux/sched.h>
#include <linux/timer.h>
#include <asm/macintosh.h>
#include <asm/mac_asc.h>
static int mac_asc_inited;
/*
* dumb triangular wave table
*/
static __u8 mac_asc_wave_tab[ 0x800 ];
/*
* Alan's original sine table; needs interpolating to 0x800
* (hint: interpolate or hardwire [0 -> Pi/2[, it's symmetric)
*/
static const signed char sine_data[] = {
0, 39, 75, 103, 121, 127, 121, 103, 75, 39,
0, -39, -75, -103, -121, -127, -121, -103, -75, -39
};
/*
* where the ASC hides ...
*/
static volatile __u8* mac_asc_regs = ( void* )0x50F14000;
/*
* sample rate; is this a good default value?
*/
static unsigned long mac_asc_samplespersec = 11050;
static int mac_bell_duration;
static unsigned long mac_bell_phase; /* 0..2*Pi -> 0..0x800 (wavetable size) */
static unsigned long mac_bell_phasepersample;
/*
* some function protos
*/
static void mac_init_asc( void );
static void mac_nosound( unsigned long );
static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int );
static void mac_quadra_ring_bell( unsigned long );
static void mac_av_start_bell( unsigned int, unsigned int, unsigned int );
static void ( *mac_special_bell )( unsigned int, unsigned int, unsigned int );
/*
* our timer to start/continue/stop the bell
*/
static DEFINE_TIMER(mac_sound_timer, mac_nosound, 0, 0);
/*
* Sort of initialize the sound chip (called from mac_mksound on the first
* beep).
*/
static void mac_init_asc( void )
{
int i;
/*
* do some machine specific initialization
* BTW:
* the NetBSD Quadra patch identifies the Enhanced Apple Sound Chip via
* mac_asc_regs[ 0x800 ] & 0xF0 != 0
* this makes no sense here, because we have to set the default sample
* rate anyway if we want correct frequencies
*/
switch ( macintosh_config->ident )
{
case MAC_MODEL_IIFX:
/*
* The IIfx is always special ...
*/
mac_asc_regs = ( void* )0x50010000;
break;
/*
* not sure about how correct this list is
* machines with the EASC enhanced apple sound chip
*/
case MAC_MODEL_Q630:
case MAC_MODEL_P475:
mac_special_bell = mac_quadra_start_bell;
mac_asc_samplespersec = 22150;
break;
case MAC_MODEL_C660:
case MAC_MODEL_Q840:
/*
* The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O.
* It appears to be similar to the "AWACS" custom ASIC in the Power Mac
* [678]100. Because Singer and AWACS may have a similar hardware
* interface, this would imply that the code in drivers/sound/dmasound.c
* for AWACS could be used as a basis for Singer support. All we have to
* do is figure out how to do DMA on the 660AV/840AV through the PSC and
* figure out where the Singer hardware sits in memory. (I'd look in the
* vicinity of the AWACS location in a Power Mac [678]100 first, or the
* current location of the Apple Sound Chip--ASC--in other Macs.) The
* Power Mac [678]100 info can be found in MkLinux Mach kernel sources.
*
* Quoted from Apple's Tech Info Library, article number 16405:
* "Among desktop Macintosh computers, only the 660AV, 840AV, and Power
* Macintosh models have 16-bit audio input and output capability
* because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer
* codec circuitry in the AVs. The Audio Waveform Amplifier and
* Converter (AWAC) chip in the Power Macintosh performs the same
* 16-bit I/O functionality. The PowerBook 500 series computers
* support 16-bit stereo output, but only mono input."
*
* Technical Information Library (TIL) article number 16405.
* http://support.apple.com/kb/TA32601
*
* --David Kilzer
*/
mac_special_bell = mac_av_start_bell;
break;
case MAC_MODEL_Q650:
case MAC_MODEL_Q700:
case MAC_MODEL_Q800:
case MAC_MODEL_Q900:
case MAC_MODEL_Q950:
/*
* Currently not implemented!
*/
mac_special_bell = NULL;
break;
default:
/*
* Every switch needs a default
*/
mac_special_bell = NULL;
break;
}
/*
* init the wave table with a simple triangular wave
* A sine wave would sure be nicer here ...
*/
for ( i = 0; i < 0x400; i++ )
{
mac_asc_wave_tab[ i ] = i / 4;
mac_asc_wave_tab[ i + 0x400 ] = 0xFF - i / 4;
}
mac_asc_inited = 1;
}
/*
* Called to make noise; current single entry to the boing driver.
* Does the job for simple ASC, calls other routines else.
* XXX Fixme:
* Should be split into asc_mksound, easc_mksound, av_mksound and
* function pointer set in mac_init_asc which would be called at
* init time.
* _This_ is rather ugly ...
*/
void mac_mksound( unsigned int freq, unsigned int length )
{
__u32 cfreq = ( freq << 5 ) / 468;
unsigned long flags;
int i;
if ( mac_special_bell == NULL )
{
/* Do nothing */
return;
}
if ( !mac_asc_inited )
mac_init_asc();
if ( mac_special_bell )
{
mac_special_bell( freq, length, 128 );
return;
}
if ( freq < 20 || freq > 20000 || length == 0 )
{
mac_nosound( 0 );
return;
}
local_irq_save(flags);
del_timer( &mac_sound_timer );
for ( i = 0; i < 0x800; i++ )
mac_asc_regs[ i ] = 0;
for ( i = 0; i < 0x800; i++ )
mac_asc_regs[ i ] = mac_asc_wave_tab[ i ];
for ( i = 0; i < 8; i++ )
*( __u32* )( ( __u32 )mac_asc_regs + ASC_CONTROL + 0x814 + 8 * i ) = cfreq;
mac_asc_regs[ 0x807 ] = 0;
mac_asc_regs[ ASC_VOLUME ] = 128;
mac_asc_regs[ 0x805 ] = 0;
mac_asc_regs[ 0x80F ] = 0;
mac_asc_regs[ ASC_MODE ] = ASC_MODE_SAMPLE;
mac_asc_regs[ ASC_ENABLE ] = ASC_ENABLE_SAMPLE;
mac_sound_timer.expires = jiffies + length;
add_timer( &mac_sound_timer );
local_irq_restore(flags);
}
/*
* regular ASC: stop whining ..
*/
static void mac_nosound( unsigned long ignored )
{
mac_asc_regs[ ASC_ENABLE ] = 0;
}
/*
* EASC entry; init EASC, don't load wavetable, schedule 'start whining'.
*/
static void mac_quadra_start_bell( unsigned int freq, unsigned int length, unsigned int volume )
{
unsigned long flags;
/* if the bell is already ringing, ring longer */
if ( mac_bell_duration > 0 )
{
mac_bell_duration += length;
return;
}
mac_bell_duration = length;
mac_bell_phase = 0;
mac_bell_phasepersample = ( freq * sizeof( mac_asc_wave_tab ) ) / mac_asc_samplespersec;
/* this is reasonably big for small frequencies */
local_irq_save(flags);
/* set the volume */
mac_asc_regs[ 0x806 ] = volume;
/* set up the ASC registers */
if ( mac_asc_regs[ 0x801 ] != 1 )
{
/* select mono mode */
mac_asc_regs[ 0x807 ] = 0;
/* select sampled sound mode */
mac_asc_regs[ 0x802 ] = 0;
/* ??? */
mac_asc_regs[ 0x801 ] = 1;
mac_asc_regs[ 0x803 ] |= 0x80;
mac_asc_regs[ 0x803 ] &= 0x7F;
}
mac_sound_timer.function = mac_quadra_ring_bell;
mac_sound_timer.expires = jiffies + 1;
add_timer( &mac_sound_timer );
local_irq_restore(flags);
}
/*
* EASC 'start/continue whining'; I'm not sure why the above function didn't
* already load the wave table, or at least call this one...
* This piece keeps reloading the wave table until done.
*/
static void mac_quadra_ring_bell( unsigned long ignored )
{
int i, count = mac_asc_samplespersec / HZ;
unsigned long flags;
/*
* we neither want a sound buffer overflow nor underflow, so we need to match
* the number of samples per timer interrupt as exactly as possible.
* using the asc interrupt will give better results in the future
* ...and the possibility to use a real sample (a boingy noise, maybe...)
*/
local_irq_save(flags);
del_timer( &mac_sound_timer );
if ( mac_bell_duration-- > 0 )
{
for ( i = 0; i < count; i++ )
{
mac_bell_phase += mac_bell_phasepersample;
mac_asc_regs[ 0 ] = mac_asc_wave_tab[ mac_bell_phase & ( sizeof( mac_asc_wave_tab ) - 1 ) ];
}
mac_sound_timer.expires = jiffies + 1;
add_timer( &mac_sound_timer );
}
else
mac_asc_regs[ 0x801 ] = 0;
local_irq_restore(flags);
}
/*
* AV code - please fill in.
*/
static void mac_av_start_bell( unsigned int freq, unsigned int length, unsigned int volume )
{
}

340
arch/m68k/mac/macints.c Normal file
View File

@@ -0,0 +1,340 @@
/*
* Macintosh interrupts
*
* General design:
* In contrary to the Amiga and Atari platforms, the Mac hardware seems to
* exclusively use the autovector interrupts (the 'generic level0-level7'
* interrupts with exception vectors 0x19-0x1f). The following interrupt levels
* are used:
* 1 - VIA1
* - slot 0: one second interrupt (CA2)
* - slot 1: VBlank (CA1)
* - slot 2: ADB data ready (SR full)
* - slot 3: ADB data (CB2)
* - slot 4: ADB clock (CB1)
* - slot 5: timer 2
* - slot 6: timer 1
* - slot 7: status of IRQ; signals 'any enabled int.'
*
* 2 - VIA2 or RBV
* - slot 0: SCSI DRQ (CA2)
* - slot 1: NUBUS IRQ (CA1) need to read port A to find which
* - slot 2: /EXP IRQ (only on IIci)
* - slot 3: SCSI IRQ (CB2)
* - slot 4: ASC IRQ (CB1)
* - slot 5: timer 2 (not on IIci)
* - slot 6: timer 1 (not on IIci)
* - slot 7: status of IRQ; signals 'any enabled int.'
*
* Levels 3-6 vary by machine type. For VIA or RBV Macintoshes:
*
* 3 - unused (?)
*
* 4 - SCC
*
* 5 - unused (?)
* [serial errors or special conditions seem to raise level 6
* interrupts on some models (LC4xx?)]
*
* 6 - off switch (?)
*
* Machines with Quadra-like VIA hardware, except PSC and PMU machines, support
* an alternate interrupt mapping, as used by A/UX. It spreads ethernet and
* sound out to their own autovector IRQs and gives VIA1 a higher priority:
*
* 1 - unused (?)
*
* 3 - on-board SONIC
*
* 5 - Apple Sound Chip (ASC)
*
* 6 - VIA1
*
* For OSS Macintoshes (IIfx only), we apply an interrupt mapping similar to
* the Quadra (A/UX) mapping:
*
* 1 - ISM IOP (ADB)
*
* 2 - SCSI
*
* 3 - NuBus
*
* 4 - SCC IOP
*
* 6 - VIA1
*
* For PSC Macintoshes (660AV, 840AV):
*
* 3 - PSC level 3
* - slot 0: MACE
*
* 4 - PSC level 4
* - slot 1: SCC channel A interrupt
* - slot 2: SCC channel B interrupt
* - slot 3: MACE DMA
*
* 5 - PSC level 5
*
* 6 - PSC level 6
*
* Finally we have good 'ole level 7, the non-maskable interrupt:
*
* 7 - NMI (programmer's switch on the back of some Macs)
* Also RAM parity error on models which support it (IIc, IIfx?)
*
* The current interrupt logic looks something like this:
*
* - We install dispatchers for the autovector interrupts (1-7). These
* dispatchers are responsible for querying the hardware (the
* VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using
* this information a machspec interrupt number is generated by placing the
* index of the interrupt hardware into the low three bits and the original
* autovector interrupt number in the upper 5 bits. The handlers for the
* resulting machspec interrupt are then called.
*
* - Nubus is a special case because its interrupts are hidden behind two
* layers of hardware. Nubus interrupts come in as index 1 on VIA #2,
* which translates to IRQ number 17. In this spot we install _another_
* dispatcher. This dispatcher finds the interrupting slot number (9-F) and
* then forms a new machspec interrupt number as above with the slot number
* minus 9 in the low three bits and the pseudo-level 7 in the upper five
* bits. The handlers for this new machspec interrupt number are then
* called. This puts Nubus interrupts into the range 56-62.
*
* - The Baboon interrupts (used on some PowerBooks) are an even more special
* case. They're hidden behind the Nubus slot $C interrupt thus adding a
* third layer of indirection. Why oh why did the Apple engineers do that?
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
#include <asm/mac_psc.h>
#include <asm/mac_oss.h>
#include <asm/mac_iop.h>
#include <asm/mac_baboon.h>
#include <asm/hwtest.h>
#include <asm/irq_regs.h>
#define SHUTUP_SONIC
/*
* console_loglevel determines NMI handler function
*/
irqreturn_t mac_nmi_handler(int, void *);
irqreturn_t mac_debug_handler(int, void *);
/* #define DEBUG_MACINTS */
static unsigned int mac_irq_startup(struct irq_data *);
static void mac_irq_shutdown(struct irq_data *);
static struct irq_chip mac_irq_chip = {
.name = "mac",
.irq_enable = mac_irq_enable,
.irq_disable = mac_irq_disable,
.irq_startup = mac_irq_startup,
.irq_shutdown = mac_irq_shutdown,
};
void __init mac_init_IRQ(void)
{
#ifdef DEBUG_MACINTS
printk("mac_init_IRQ(): Setting things up...\n");
#endif
m68k_setup_irq_controller(&mac_irq_chip, handle_simple_irq, IRQ_USER,
NUM_MAC_SOURCES - IRQ_USER);
/* Make sure the SONIC interrupt is cleared or things get ugly */
#ifdef SHUTUP_SONIC
printk("Killing onboard sonic... ");
/* This address should hopefully be mapped already */
if (hwreg_present((void*)(0x50f0a000))) {
*(long *)(0x50f0a014) = 0x7fffL;
*(long *)(0x50f0a010) = 0L;
}
printk("Done.\n");
#endif /* SHUTUP_SONIC */
/*
* Now register the handlers for the master IRQ handlers
* at levels 1-7. Most of the work is done elsewhere.
*/
if (oss_present)
oss_register_interrupts();
else
via_register_interrupts();
if (psc_present)
psc_register_interrupts();
if (baboon_present)
baboon_register_interrupts();
iop_register_interrupts();
if (request_irq(IRQ_AUTO_7, mac_nmi_handler, 0, "NMI",
mac_nmi_handler))
pr_err("Couldn't register NMI\n");
#ifdef DEBUG_MACINTS
printk("mac_init_IRQ(): Done!\n");
#endif
}
/*
* mac_irq_enable - enable an interrupt source
* mac_irq_disable - disable an interrupt source
*
* These routines are just dispatchers to the VIA/OSS/PSC routines.
*/
void mac_irq_enable(struct irq_data *data)
{
int irq = data->irq;
int irq_src = IRQ_SRC(irq);
switch(irq_src) {
case 1:
case 2:
case 7:
if (oss_present)
oss_irq_enable(irq);
else
via_irq_enable(irq);
break;
case 3:
case 4:
case 5:
case 6:
if (psc_present)
psc_irq_enable(irq);
else if (oss_present)
oss_irq_enable(irq);
break;
case 8:
if (baboon_present)
baboon_irq_enable(irq);
break;
}
}
void mac_irq_disable(struct irq_data *data)
{
int irq = data->irq;
int irq_src = IRQ_SRC(irq);
switch(irq_src) {
case 1:
case 2:
case 7:
if (oss_present)
oss_irq_disable(irq);
else
via_irq_disable(irq);
break;
case 3:
case 4:
case 5:
case 6:
if (psc_present)
psc_irq_disable(irq);
else if (oss_present)
oss_irq_disable(irq);
break;
case 8:
if (baboon_present)
baboon_irq_disable(irq);
break;
}
}
static unsigned int mac_irq_startup(struct irq_data *data)
{
int irq = data->irq;
if (IRQ_SRC(irq) == 7 && !oss_present)
via_nubus_irq_startup(irq);
else
mac_irq_enable(data);
return 0;
}
static void mac_irq_shutdown(struct irq_data *data)
{
int irq = data->irq;
if (IRQ_SRC(irq) == 7 && !oss_present)
via_nubus_irq_shutdown(irq);
else
mac_irq_disable(data);
}
static int num_debug[8];
irqreturn_t mac_debug_handler(int irq, void *dev_id)
{
if (num_debug[irq] < 10) {
printk("DEBUG: Unexpected IRQ %d\n", irq);
num_debug[irq]++;
}
return IRQ_HANDLED;
}
static int in_nmi;
static volatile int nmi_hold;
irqreturn_t mac_nmi_handler(int irq, void *dev_id)
{
int i;
/*
* generate debug output on NMI switch if 'debug' kernel option given
* (only works with Penguin!)
*/
in_nmi++;
for (i=0; i<100; i++)
udelay(1000);
if (in_nmi == 1) {
nmi_hold = 1;
printk("... pausing, press NMI to resume ...");
} else {
printk(" ok!\n");
nmi_hold = 0;
}
barrier();
while (nmi_hold == 1)
udelay(1000);
if (console_loglevel >= 8) {
#if 0
struct pt_regs *fp = get_irq_regs();
show_state();
printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp);
printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
fp->d0, fp->d1, fp->d2, fp->d3);
printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
fp->d4, fp->d5, fp->a0, fp->a1);
if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page)
printk("Corrupted stack page\n");
printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, current->kernel_stack_page);
if (intr_count == 1)
dump_stack((struct frame *)fp);
#else
/* printk("NMI "); */
#endif
}
in_nmi--;
return IRQ_HANDLED;
}

771
arch/m68k/mac/misc.c Normal file
View File

@@ -0,0 +1,771 @@
/*
* Miscellaneous Mac68K-specific stuff
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/rtc.h>
#include <linux/mm.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/rtc.h>
#include <asm/segment.h>
#include <asm/setup.h>
#include <asm/macintosh.h>
#include <asm/mac_via.h>
#include <asm/mac_oss.h>
#define BOOTINFO_COMPAT_1_0
#include <asm/bootinfo.h>
#include <asm/machdep.h>
/* Offset between Unix time (1970-based) and Mac time (1904-based) */
#define RTC_OFFSET 2082844800
static void (*rom_reset)(void);
#ifdef CONFIG_ADB_CUDA
static long cuda_read_time(void)
{
struct adb_request req;
long time;
if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
return 0;
while (!req.complete)
cuda_poll();
time = (req.reply[3] << 24) | (req.reply[4] << 16)
| (req.reply[5] << 8) | req.reply[6];
return time - RTC_OFFSET;
}
static void cuda_write_time(long data)
{
struct adb_request req;
data += RTC_OFFSET;
if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
(data >> 24) & 0xFF, (data >> 16) & 0xFF,
(data >> 8) & 0xFF, data & 0xFF) < 0)
return;
while (!req.complete)
cuda_poll();
}
static __u8 cuda_read_pram(int offset)
{
struct adb_request req;
if (cuda_request(&req, NULL, 4, CUDA_PACKET, CUDA_GET_PRAM,
(offset >> 8) & 0xFF, offset & 0xFF) < 0)
return 0;
while (!req.complete)
cuda_poll();
return req.reply[3];
}
static void cuda_write_pram(int offset, __u8 data)
{
struct adb_request req;
if (cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_SET_PRAM,
(offset >> 8) & 0xFF, offset & 0xFF, data) < 0)
return;
while (!req.complete)
cuda_poll();
}
#else
#define cuda_read_time() 0
#define cuda_write_time(n)
#define cuda_read_pram NULL
#define cuda_write_pram NULL
#endif
#ifdef CONFIG_ADB_PMU68K
static long pmu_read_time(void)
{
struct adb_request req;
long time;
if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
return 0;
while (!req.complete)
pmu_poll();
time = (req.reply[1] << 24) | (req.reply[2] << 16)
| (req.reply[3] << 8) | req.reply[4];
return time - RTC_OFFSET;
}
static void pmu_write_time(long data)
{
struct adb_request req;
data += RTC_OFFSET;
if (pmu_request(&req, NULL, 5, PMU_SET_RTC,
(data >> 24) & 0xFF, (data >> 16) & 0xFF,
(data >> 8) & 0xFF, data & 0xFF) < 0)
return;
while (!req.complete)
pmu_poll();
}
static __u8 pmu_read_pram(int offset)
{
struct adb_request req;
if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM,
(offset >> 8) & 0xFF, offset & 0xFF) < 0)
return 0;
while (!req.complete)
pmu_poll();
return req.reply[3];
}
static void pmu_write_pram(int offset, __u8 data)
{
struct adb_request req;
if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM,
(offset >> 8) & 0xFF, offset & 0xFF, data) < 0)
return;
while (!req.complete)
pmu_poll();
}
#else
#define pmu_read_time() 0
#define pmu_write_time(n)
#define pmu_read_pram NULL
#define pmu_write_pram NULL
#endif
#if 0 /* def CONFIG_ADB_MACIISI */
extern int maciisi_request(struct adb_request *req,
void (*done)(struct adb_request *), int nbytes, ...);
static long maciisi_read_time(void)
{
struct adb_request req;
long time;
if (maciisi_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME))
return 0;
time = (req.reply[3] << 24) | (req.reply[4] << 16)
| (req.reply[5] << 8) | req.reply[6];
return time - RTC_OFFSET;
}
static void maciisi_write_time(long data)
{
struct adb_request req;
data += RTC_OFFSET;
maciisi_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
(data >> 24) & 0xFF, (data >> 16) & 0xFF,
(data >> 8) & 0xFF, data & 0xFF);
}
static __u8 maciisi_read_pram(int offset)
{
struct adb_request req;
if (maciisi_request(&req, NULL, 4, CUDA_PACKET, CUDA_GET_PRAM,
(offset >> 8) & 0xFF, offset & 0xFF))
return 0;
return req.reply[3];
}
static void maciisi_write_pram(int offset, __u8 data)
{
struct adb_request req;
maciisi_request(&req, NULL, 5, CUDA_PACKET, CUDA_SET_PRAM,
(offset >> 8) & 0xFF, offset & 0xFF, data);
}
#else
#define maciisi_read_time() 0
#define maciisi_write_time(n)
#define maciisi_read_pram NULL
#define maciisi_write_pram NULL
#endif
/*
* VIA PRAM/RTC access routines
*
* Must be called with interrupts disabled and
* the RTC should be enabled.
*/
static __u8 via_pram_readbyte(void)
{
int i,reg;
__u8 data;
reg = via1[vBufB] & ~VIA1B_vRTCClk;
/* Set the RTC data line to be an input. */
via1[vDirB] &= ~VIA1B_vRTCData;
/* The bits of the byte come out in MSB order */
data = 0;
for (i = 0 ; i < 8 ; i++) {
via1[vBufB] = reg;
via1[vBufB] = reg | VIA1B_vRTCClk;
data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
}
/* Return RTC data line to output state */
via1[vDirB] |= VIA1B_vRTCData;
return data;
}
static void via_pram_writebyte(__u8 data)
{
int i,reg,bit;
reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
/* The bits of the byte go in in MSB order */
for (i = 0 ; i < 8 ; i++) {
bit = data & 0x80? 1 : 0;
data <<= 1;
via1[vBufB] = reg | bit;
via1[vBufB] = reg | bit | VIA1B_vRTCClk;
}
}
/*
* Execute a VIA PRAM/RTC command. For read commands
* data should point to a one-byte buffer for the
* resulting data. For write commands it should point
* to the data byte to for the command.
*
* This function disables all interrupts while running.
*/
static void via_pram_command(int command, __u8 *data)
{
unsigned long flags;
int is_read;
local_irq_save(flags);
/* Enable the RTC and make sure the strobe line is high */
via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
if (command & 0xFF00) { /* extended (two-byte) command */
via_pram_writebyte((command & 0xFF00) >> 8);
via_pram_writebyte(command & 0xFF);
is_read = command & 0x8000;
} else { /* one-byte command */
via_pram_writebyte(command);
is_read = command & 0x80;
}
if (is_read) {
*data = via_pram_readbyte();
} else {
via_pram_writebyte(*data);
}
/* All done, disable the RTC */
via1[vBufB] |= VIA1B_vRTCEnb;
local_irq_restore(flags);
}
static __u8 via_read_pram(int offset)
{
return 0;
}
static void via_write_pram(int offset, __u8 data)
{
}
/*
* Return the current time in seconds since January 1, 1904.
*
* This only works on machines with the VIA-based PRAM/RTC, which
* is basically any machine with Mac II-style ADB.
*/
static long via_read_time(void)
{
union {
__u8 cdata[4];
long idata;
} result, last_result;
int count = 1;
via_pram_command(0x81, &last_result.cdata[3]);
via_pram_command(0x85, &last_result.cdata[2]);
via_pram_command(0x89, &last_result.cdata[1]);
via_pram_command(0x8D, &last_result.cdata[0]);
/*
* The NetBSD guys say to loop until you get the same reading
* twice in a row.
*/
while (1) {
via_pram_command(0x81, &result.cdata[3]);
via_pram_command(0x85, &result.cdata[2]);
via_pram_command(0x89, &result.cdata[1]);
via_pram_command(0x8D, &result.cdata[0]);
if (result.idata == last_result.idata)
return result.idata - RTC_OFFSET;
if (++count > 10)
break;
last_result.idata = result.idata;
}
pr_err("via_read_time: failed to read a stable value; "
"got 0x%08lx then 0x%08lx\n",
last_result.idata, result.idata);
return 0;
}
/*
* Set the current time to a number of seconds since January 1, 1904.
*
* This only works on machines with the VIA-based PRAM/RTC, which
* is basically any machine with Mac II-style ADB.
*/
static void via_write_time(long time)
{
union {
__u8 cdata[4];
long idata;
} data;
__u8 temp;
/* Clear the write protect bit */
temp = 0x55;
via_pram_command(0x35, &temp);
data.idata = time + RTC_OFFSET;
via_pram_command(0x01, &data.cdata[3]);
via_pram_command(0x05, &data.cdata[2]);
via_pram_command(0x09, &data.cdata[1]);
via_pram_command(0x0D, &data.cdata[0]);
/* Set the write protect bit */
temp = 0xD5;
via_pram_command(0x35, &temp);
}
static void via_shutdown(void)
{
if (rbv_present) {
via2[rBufB] &= ~0x04;
} else {
/* Direction of vDirB is output */
via2[vDirB] |= 0x04;
/* Send a value of 0 on that line */
via2[vBufB] &= ~0x04;
mdelay(1000);
}
}
/*
* FIXME: not sure how this is supposed to work exactly...
*/
static void oss_shutdown(void)
{
oss->rom_ctrl = OSS_POWEROFF;
}
#ifdef CONFIG_ADB_CUDA
static void cuda_restart(void)
{
struct adb_request req;
if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM) < 0)
return;
while (!req.complete)
cuda_poll();
}
static void cuda_shutdown(void)
{
struct adb_request req;
if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN) < 0)
return;
while (!req.complete)
cuda_poll();
}
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_ADB_PMU68K
void pmu_restart(void)
{
struct adb_request req;
if (pmu_request(&req, NULL,
2, PMU_SET_INTR_MASK, PMU_INT_ADB|PMU_INT_TICK) < 0)
return;
while (!req.complete)
pmu_poll();
if (pmu_request(&req, NULL, 1, PMU_RESET) < 0)
return;
while (!req.complete)
pmu_poll();
}
void pmu_shutdown(void)
{
struct adb_request req;
if (pmu_request(&req, NULL,
2, PMU_SET_INTR_MASK, PMU_INT_ADB|PMU_INT_TICK) < 0)
return;
while (!req.complete)
pmu_poll();
if (pmu_request(&req, NULL, 5, PMU_SHUTDOWN, 'M', 'A', 'T', 'T') < 0)
return;
while (!req.complete)
pmu_poll();
}
#endif
/*
*-------------------------------------------------------------------
* Below this point are the generic routines; they'll dispatch to the
* correct routine for the hardware on which we're running.
*-------------------------------------------------------------------
*/
void mac_pram_read(int offset, __u8 *buffer, int len)
{
__u8 (*func)(int);
int i;
switch(macintosh_config->adb_type) {
case MAC_ADB_IISI:
func = maciisi_read_pram; break;
case MAC_ADB_PB1:
case MAC_ADB_PB2:
func = pmu_read_pram; break;
case MAC_ADB_CUDA:
func = cuda_read_pram; break;
default:
func = via_read_pram;
}
if (!func)
return;
for (i = 0 ; i < len ; i++) {
buffer[i] = (*func)(offset++);
}
}
void mac_pram_write(int offset, __u8 *buffer, int len)
{
void (*func)(int, __u8);
int i;
switch(macintosh_config->adb_type) {
case MAC_ADB_IISI:
func = maciisi_write_pram; break;
case MAC_ADB_PB1:
case MAC_ADB_PB2:
func = pmu_write_pram; break;
case MAC_ADB_CUDA:
func = cuda_write_pram; break;
default:
func = via_write_pram;
}
if (!func)
return;
for (i = 0 ; i < len ; i++) {
(*func)(offset++, buffer[i]);
}
}
void mac_poweroff(void)
{
/*
* MAC_ADB_IISI may need to be moved up here if it doesn't actually
* work using the ADB packet method. --David Kilzer
*/
if (oss_present) {
oss_shutdown();
} else if (macintosh_config->adb_type == MAC_ADB_II) {
via_shutdown();
#ifdef CONFIG_ADB_CUDA
} else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
cuda_shutdown();
#endif
#ifdef CONFIG_ADB_PMU68K
} else if (macintosh_config->adb_type == MAC_ADB_PB1
|| macintosh_config->adb_type == MAC_ADB_PB2) {
pmu_shutdown();
#endif
}
local_irq_enable();
printk("It is now safe to turn off your Macintosh.\n");
while(1);
}
void mac_reset(void)
{
if (macintosh_config->adb_type == MAC_ADB_II) {
unsigned long flags;
/* need ROMBASE in booter */
/* indeed, plus need to MAP THE ROM !! */
if (mac_bi_data.rombase == 0)
mac_bi_data.rombase = 0x40800000;
/* works on some */
rom_reset = (void *) (mac_bi_data.rombase + 0xa);
if (macintosh_config->ident == MAC_MODEL_SE30) {
/*
* MSch: Machines known to crash on ROM reset ...
*/
} else {
local_irq_save(flags);
rom_reset();
local_irq_restore(flags);
}
#ifdef CONFIG_ADB_CUDA
} else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
cuda_restart();
#endif
#ifdef CONFIG_ADB_PMU68K
} else if (macintosh_config->adb_type == MAC_ADB_PB1
|| macintosh_config->adb_type == MAC_ADB_PB2) {
pmu_restart();
#endif
} else if (CPU_IS_030) {
/* 030-specific reset routine. The idea is general, but the
* specific registers to reset are '030-specific. Until I
* have a non-030 machine, I can't test anything else.
* -- C. Scott Ananian <cananian@alumni.princeton.edu>
*/
unsigned long rombase = 0x40000000;
/* make a 1-to-1 mapping, using the transparent tran. reg. */
unsigned long virt = (unsigned long) mac_reset;
unsigned long phys = virt_to_phys(mac_reset);
unsigned long addr = (phys&0xFF000000)|0x8777;
unsigned long offset = phys-virt;
local_irq_disable(); /* lets not screw this up, ok? */
__asm__ __volatile__(".chip 68030\n\t"
"pmove %0,%/tt0\n\t"
".chip 68k"
: : "m" (addr));
/* Now jump to physical address so we can disable MMU */
__asm__ __volatile__(
".chip 68030\n\t"
"lea %/pc@(1f),%/a0\n\t"
"addl %0,%/a0\n\t"/* fixup target address and stack ptr */
"addl %0,%/sp\n\t"
"pflusha\n\t"
"jmp %/a0@\n\t" /* jump into physical memory */
"0:.long 0\n\t" /* a constant zero. */
/* OK. Now reset everything and jump to reset vector. */
"1:\n\t"
"lea %/pc@(0b),%/a0\n\t"
"pmove %/a0@, %/tc\n\t" /* disable mmu */
"pmove %/a0@, %/tt0\n\t" /* disable tt0 */
"pmove %/a0@, %/tt1\n\t" /* disable tt1 */
"movel #0, %/a0\n\t"
"movec %/a0, %/vbr\n\t" /* clear vector base register */
"movec %/a0, %/cacr\n\t" /* disable caches */
"movel #0x0808,%/a0\n\t"
"movec %/a0, %/cacr\n\t" /* flush i&d caches */
"movew #0x2700,%/sr\n\t" /* set up status register */
"movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
"movec %/a0, %/isp\n\t"
"movel %1@(0x4),%/a0\n\t" /* load reset vector */
"reset\n\t" /* reset external devices */
"jmp %/a0@\n\t" /* jump to the reset vector */
".chip 68k"
: : "r" (offset), "a" (rombase) : "a0");
}
/* should never get here */
local_irq_enable();
printk ("Restart failed. Please restart manually.\n");
while(1);
}
/*
* This function translates seconds since 1970 into a proper date.
*
* Algorithm cribbed from glibc2.1, __offtime().
*/
#define SECS_PER_MINUTE (60)
#define SECS_PER_HOUR (SECS_PER_MINUTE * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
static void unmktime(unsigned long time, long offset,
int *yearp, int *monp, int *dayp,
int *hourp, int *minp, int *secp)
{
/* How many days come before each month (0-12). */
static const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
long int days, rem, y, wday, yday;
const unsigned short int *ip;
days = time / SECS_PER_DAY;
rem = time % SECS_PER_DAY;
rem += offset;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
*hourp = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
*minp = rem / SECS_PER_MINUTE;
*secp = rem % SECS_PER_MINUTE;
/* January 1, 1970 was a Thursday. */
wday = (4 + days) % 7; /* Day in the week. Not currently used */
if (wday < 0) wday += 7;
y = 1970;
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
while (days < 0 || days >= (__isleap (y) ? 366 : 365))
{
/* Guess a corrected year, assuming 365 days per year. */
long int yg = y + days / 365 - (days % 365 < 0);
/* Adjust DAYS and Y to match the guessed year. */
days -= ((yg - y) * 365
+ LEAPS_THRU_END_OF (yg - 1)
- LEAPS_THRU_END_OF (y - 1));
y = yg;
}
*yearp = y - 1900;
yday = days; /* day in the year. Not currently used. */
ip = __mon_yday[__isleap(y)];
for (y = 11; days < (long int) ip[y]; --y)
continue;
days -= ip[y];
*monp = y;
*dayp = days + 1; /* day in the month */
return;
}
/*
* Read/write the hardware clock.
*/
int mac_hwclk(int op, struct rtc_time *t)
{
unsigned long now;
if (!op) { /* read */
switch (macintosh_config->adb_type) {
case MAC_ADB_II:
case MAC_ADB_IOP:
now = via_read_time();
break;
case MAC_ADB_IISI:
now = maciisi_read_time();
break;
case MAC_ADB_PB1:
case MAC_ADB_PB2:
now = pmu_read_time();
break;
case MAC_ADB_CUDA:
now = cuda_read_time();
break;
default:
now = 0;
}
t->tm_wday = 0;
unmktime(now, 0,
&t->tm_year, &t->tm_mon, &t->tm_mday,
&t->tm_hour, &t->tm_min, &t->tm_sec);
#if 0
printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
#endif
} else { /* write */
#if 0
printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
#endif
now = mktime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
switch (macintosh_config->adb_type) {
case MAC_ADB_II:
case MAC_ADB_IOP:
via_write_time(now);
break;
case MAC_ADB_CUDA:
cuda_write_time(now);
break;
case MAC_ADB_PB1:
case MAC_ADB_PB2:
pmu_write_time(now);
break;
case MAC_ADB_IISI:
maciisi_write_time(now);
}
}
return 0;
}
/*
* Set minutes/seconds in the hardware clock
*/
int mac_set_clock_mmss (unsigned long nowtime)
{
struct rtc_time now;
mac_hwclk(0, &now);
now.tm_sec = nowtime % 60;
now.tm_min = (nowtime / 60) % 60;
mac_hwclk(1, &now);
return 0;
}

228
arch/m68k/mac/oss.c Normal file
View File

@@ -0,0 +1,228 @@
/*
* Operating System Services (OSS) chip handling
* Written by Joshua M. Thompson (funaho@jurai.org)
*
*
* This chip is used in the IIfx in place of VIA #2. It acts like a fancy
* VIA chip with prorammable interrupt levels.
*
* 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
* recent insights into OSS operational details.
* 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
* to mostly match the A/UX interrupt scheme supported on the
* VIA side. Also added support for enabling the ISM irq again
* since we now have a functional IOP manager.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
#include <asm/mac_oss.h>
int oss_present;
volatile struct mac_oss *oss;
/*
* Initialize the OSS
*
* The OSS "detection" code is actually in via_init() which is always called
* before us. Thus we can count on oss_present being valid on entry.
*/
void __init oss_init(void)
{
int i;
if (!oss_present) return;
oss = (struct mac_oss *) OSS_BASE;
/* Disable all interrupts. Unlike a VIA it looks like we */
/* do this by setting the source's interrupt level to zero. */
for (i = 0; i <= OSS_NUM_SOURCES; i++) {
oss->irq_level[i] = 0;
}
}
/*
* Initialize OSS for Nubus access
*/
void __init oss_nubus_init(void)
{
}
/*
* Handle miscellaneous OSS interrupts.
*/
static void oss_irq(unsigned int irq, struct irq_desc *desc)
{
int events = oss->irq_pending &
(OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM);
#ifdef DEBUG_IRQS
if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
printk("oss_irq: irq %u events = 0x%04X\n", irq,
(int) oss->irq_pending);
}
#endif
if (events & OSS_IP_IOPSCC) {
oss->irq_pending &= ~OSS_IP_IOPSCC;
generic_handle_irq(IRQ_MAC_SCC);
}
if (events & OSS_IP_SCSI) {
oss->irq_pending &= ~OSS_IP_SCSI;
generic_handle_irq(IRQ_MAC_SCSI);
}
if (events & OSS_IP_IOPISM) {
oss->irq_pending &= ~OSS_IP_IOPISM;
generic_handle_irq(IRQ_MAC_ADB);
}
}
/*
* Nubus IRQ handler, OSS style
*
* Unlike the VIA/RBV this is on its own autovector interrupt level.
*/
static void oss_nubus_irq(unsigned int irq, struct irq_desc *desc)
{
int events, irq_bit, i;
events = oss->irq_pending & OSS_IP_NUBUS;
if (!events)
return;
#ifdef DEBUG_NUBUS_INT
if (console_loglevel > 7) {
printk("oss_nubus_irq: events = 0x%04X\n", events);
}
#endif
/* There are only six slots on the OSS, not seven */
i = 6;
irq_bit = 0x40;
do {
--i;
irq_bit >>= 1;
if (events & irq_bit) {
oss->irq_pending &= ~irq_bit;
generic_handle_irq(NUBUS_SOURCE_BASE + i);
}
} while(events & (irq_bit - 1));
}
/*
* Register the OSS and NuBus interrupt dispatchers.
*
* This IRQ mapping is laid out with two things in mind: first, we try to keep
* things on their own levels to avoid having to do double-dispatches. Second,
* the levels match as closely as possible the alternate IRQ mapping mode (aka
* "A/UX mode") available on some VIA machines.
*/
#define OSS_IRQLEV_IOPISM IRQ_AUTO_1
#define OSS_IRQLEV_SCSI IRQ_AUTO_2
#define OSS_IRQLEV_NUBUS IRQ_AUTO_3
#define OSS_IRQLEV_IOPSCC IRQ_AUTO_4
#define OSS_IRQLEV_VIA1 IRQ_AUTO_6
void __init oss_register_interrupts(void)
{
irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_irq);
irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_irq);
irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq);
irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_irq);
irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq);
/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
oss->irq_level[OSS_VIA1] = IRQ_AUTO_6;
}
/*
* Enable an OSS interrupt
*
* It looks messy but it's rather straightforward. The switch() statement
* just maps the machspec interrupt numbers to the right OSS interrupt
* source (if the OSS handles that interrupt) and then sets the interrupt
* level for that source to nonzero, thus enabling the interrupt.
*/
void oss_irq_enable(int irq) {
#ifdef DEBUG_IRQUSE
printk("oss_irq_enable(%d)\n", irq);
#endif
switch(irq) {
case IRQ_MAC_SCC:
oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
return;
case IRQ_MAC_ADB:
oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
return;
case IRQ_MAC_SCSI:
oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
return;
case IRQ_NUBUS_9:
case IRQ_NUBUS_A:
case IRQ_NUBUS_B:
case IRQ_NUBUS_C:
case IRQ_NUBUS_D:
case IRQ_NUBUS_E:
irq -= NUBUS_SOURCE_BASE;
oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
return;
}
if (IRQ_SRC(irq) == 1)
via_irq_enable(irq);
}
/*
* Disable an OSS interrupt
*
* Same as above except we set the source's interrupt level to zero,
* to disable the interrupt.
*/
void oss_irq_disable(int irq) {
#ifdef DEBUG_IRQUSE
printk("oss_irq_disable(%d)\n", irq);
#endif
switch(irq) {
case IRQ_MAC_SCC:
oss->irq_level[OSS_IOPSCC] = 0;
return;
case IRQ_MAC_ADB:
oss->irq_level[OSS_IOPISM] = 0;
return;
case IRQ_MAC_SCSI:
oss->irq_level[OSS_SCSI] = 0;
return;
case IRQ_NUBUS_9:
case IRQ_NUBUS_A:
case IRQ_NUBUS_B:
case IRQ_NUBUS_C:
case IRQ_NUBUS_D:
case IRQ_NUBUS_E:
irq -= NUBUS_SOURCE_BASE;
oss->irq_level[irq] = 0;
return;
}
if (IRQ_SRC(irq) == 1)
via_irq_disable(irq);
}

182
arch/m68k/mac/psc.c Normal file
View File

@@ -0,0 +1,182 @@
/*
* Apple Peripheral System Controller (PSC)
*
* The PSC is used on the AV Macs to control IO functions not handled
* by the VIAs (Ethernet, DSP, SCC).
*
* TO DO:
*
* Try to figure out what's going on in pIFR5 and pIFR6. There seem to be
* persisant interrupt conditions in those registers and I have no idea what
* they are. Granted it doesn't affect since we're not enabling any interrupts
* on those levels at the moment, but it would be nice to know. I have a feeling
* they aren't actually interrupt lines but data lines (to the DSP?)
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/traps.h>
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_psc.h>
#define DEBUG_PSC
int psc_present;
volatile __u8 *psc;
/*
* Debugging dump, used in various places to see what's going on.
*/
static void psc_debug_dump(void)
{
int i;
if (!psc_present) return;
for (i = 0x30 ; i < 0x70 ; i += 0x10) {
printk("PSC #%d: IFR = 0x%02X IER = 0x%02X\n",
i >> 4,
(int) psc_read_byte(pIFRbase + i),
(int) psc_read_byte(pIERbase + i));
}
}
/*
* Try to kill all DMA channels on the PSC. Not sure how this his
* supposed to work; this is code lifted from macmace.c and then
* expanded to cover what I think are the other 7 channels.
*/
static void psc_dma_die_die_die(void)
{
int i;
printk("Killing all PSC DMA channels...");
for (i = 0 ; i < 9 ; i++) {
psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800);
psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000);
psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100);
psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100);
}
printk("done!\n");
}
/*
* Initialize the PSC. For now this just involves shutting down all
* interrupt sources using the IERs.
*/
void __init psc_init(void)
{
int i;
if (macintosh_config->ident != MAC_MODEL_C660
&& macintosh_config->ident != MAC_MODEL_Q840)
{
psc = NULL;
psc_present = 0;
return;
}
/*
* The PSC is always at the same spot, but using psc
* keeps things consistent with the psc_xxxx functions.
*/
psc = (void *) PSC_BASE;
psc_present = 1;
printk("PSC detected at %p\n", psc);
psc_dma_die_die_die();
#ifdef DEBUG_PSC
psc_debug_dump();
#endif
/*
* Mask and clear all possible interrupts
*/
for (i = 0x30 ; i < 0x70 ; i += 0x10) {
psc_write_byte(pIERbase + i, 0x0F);
psc_write_byte(pIFRbase + i, 0x0F);
}
}
/*
* PSC interrupt handler. It's a lot like the VIA interrupt handler.
*/
static void psc_irq(unsigned int irq, struct irq_desc *desc)
{
unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
int pIFR = pIFRbase + offset;
int pIER = pIERbase + offset;
int irq_num;
unsigned char irq_bit, events;
#ifdef DEBUG_IRQS
printk("psc_irq: irq %u pIFR = 0x%02X pIER = 0x%02X\n",
irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER));
#endif
events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
if (!events)
return;
irq_num = irq << 3;
irq_bit = 1;
do {
if (events & irq_bit) {
psc_write_byte(pIFR, irq_bit);
generic_handle_irq(irq_num);
}
irq_num++;
irq_bit <<= 1;
} while (events >= irq_bit);
}
/*
* Register the PSC interrupt dispatchers for autovector interrupts 3-6.
*/
void __init psc_register_interrupts(void)
{
irq_set_chained_handler(IRQ_AUTO_3, psc_irq);
irq_set_handler_data(IRQ_AUTO_3, (void *)0x30);
irq_set_chained_handler(IRQ_AUTO_4, psc_irq);
irq_set_handler_data(IRQ_AUTO_4, (void *)0x40);
irq_set_chained_handler(IRQ_AUTO_5, psc_irq);
irq_set_handler_data(IRQ_AUTO_5, (void *)0x50);
irq_set_chained_handler(IRQ_AUTO_6, psc_irq);
irq_set_handler_data(IRQ_AUTO_6, (void *)0x60);
}
void psc_irq_enable(int irq) {
int irq_src = IRQ_SRC(irq);
int irq_idx = IRQ_IDX(irq);
int pIER = pIERbase + (irq_src << 4);
#ifdef DEBUG_IRQUSE
printk("psc_irq_enable(%d)\n", irq);
#endif
psc_write_byte(pIER, (1 << irq_idx) | 0x80);
}
void psc_irq_disable(int irq) {
int irq_src = IRQ_SRC(irq);
int irq_idx = IRQ_IDX(irq);
int pIER = pIERbase + (irq_src << 4);
#ifdef DEBUG_IRQUSE
printk("psc_irq_disable(%d)\n", irq);
#endif
psc_write_byte(pIER, 1 << irq_idx);
}

622
arch/m68k/mac/via.c Normal file
View File

@@ -0,0 +1,622 @@
/*
* 6522 Versatile Interface Adapter (VIA)
*
* There are two of these on the Mac II. Some IRQs are vectored
* via them as are assorted bits and bobs - eg RTC, ADB.
*
* CSA: Motorola seems to have removed documentation on the 6522 from
* their web site; try
* http://nerini.drf.com/vectrex/other/text/chips/6522/
* http://www.zymurgy.net/classic/vic20/vicdet1.htm
* and
* http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html
* for info. A full-text web search on 6522 AND VIA will probably also
* net some usefulness. <cananian@alumni.princeton.edu> 20apr1999
*
* Additional data is here (the SY6522 was used in the Mac II etc):
* http://www.6502.org/documents/datasheets/synertek/synertek_sy6522.pdf
* http://www.6502.org/documents/datasheets/synertek/synertek_sy6522_programming_reference.pdf
*
* PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b
* by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org)
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
#include <asm/mac_psc.h>
#include <asm/mac_oss.h>
volatile __u8 *via1, *via2;
int rbv_present;
int via_alt_mapping;
EXPORT_SYMBOL(via_alt_mapping);
static __u8 rbv_clear;
/*
* Globals for accessing the VIA chip registers without having to
* check if we're hitting a real VIA or an RBV. Normally you could
* just hit the combined register (ie, vIER|rIER) but that seems to
* break on AV Macs...probably because they actually decode more than
* eight address bits. Why can't Apple engineers at least be
* _consistently_ lazy? - 1999-05-21 (jmt)
*/
static int gIER,gIFR,gBufA,gBufB;
/*
* Timer defs.
*/
#define TICK_SIZE 10000
#define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */
#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF)
#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8)
/*
* On Macs with a genuine VIA chip there is no way to mask an individual slot
* interrupt. This limitation also seems to apply to VIA clone logic cores in
* Quadra-like ASICs. (RBV and OSS machines don't have this limitation.)
*
* We used to fake it by configuring the relevent VIA pin as an output
* (to mask the interrupt) or input (to unmask). That scheme did not work on
* (at least) the Quadra 700. A NuBus card's /NMRQ signal is an open-collector
* circuit (see Designing Cards and Drivers for Macintosh II and Macintosh SE,
* p. 10-11 etc) but VIA outputs are not (see datasheet).
*
* Driving these outputs high must cause the VIA to source current and the
* card to sink current when it asserts /NMRQ. Current will flow but the pin
* voltage is uncertain and so the /NMRQ condition may still cause a transition
* at the VIA2 CA1 input (which explains the lost interrupts). A side effect
* is that a disabled slot IRQ can never be tested as pending or not.
*
* Driving these outputs low doesn't work either. All the slot /NMRQ lines are
* (active low) OR'd together to generate the CA1 (aka "SLOTS") interrupt (see
* The Guide To Macintosh Family Hardware, 2nd edition p. 167). If we drive a
* disabled /NMRQ line low, the falling edge immediately triggers a CA1
* interrupt and all slot interrupts after that will generate no transition
* and therefore no interrupt, even after being re-enabled.
*
* So we make the VIA port A I/O lines inputs and use nubus_disabled to keep
* track of their states. When any slot IRQ becomes disabled we mask the CA1
* umbrella interrupt. Only when all slot IRQs become enabled do we unmask
* the CA1 interrupt. It must remain enabled even when cards have no interrupt
* handler registered. Drivers must therefore disable a slot interrupt at the
* device before they call free_irq (like shared and autovector interrupts).
*
* There is also a related problem when MacOS is used to boot Linux. A network
* card brought up by a MacOS driver may raise an interrupt while Linux boots.
* This can be fatal since it can't be handled until the right driver loads
* (if such a driver exists at all). Apparently related to this hardware
* limitation, "Designing Cards and Drivers", p. 9-8, says that a slot
* interrupt with no driver would crash MacOS (the book was written before
* the appearance of Macs with RBV or OSS).
*/
static u8 nubus_disabled;
void via_debug_dump(void);
/*
* Initialize the VIAs
*
* First we figure out where they actually _are_ as well as what type of
* VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)
* Then we pretty much clear them out and disable all IRQ sources.
*
* Note: the OSS is actually "detected" here and not in oss_init(). It just
* seems more logical to do it here since via_init() needs to know
* these things anyways.
*/
void __init via_init(void)
{
switch(macintosh_config->via_type) {
/* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
case MAC_VIA_IICI:
via1 = (void *) VIA1_BASE;
if (macintosh_config->ident == MAC_MODEL_IIFX) {
via2 = NULL;
rbv_present = 0;
oss_present = 1;
} else {
via2 = (void *) RBV_BASE;
rbv_present = 1;
oss_present = 0;
}
if (macintosh_config->ident == MAC_MODEL_LCIII) {
rbv_clear = 0x00;
} else {
/* on most RBVs (& unlike the VIAs), you */
/* need to set bit 7 when you write to IFR */
/* in order for your clear to occur. */
rbv_clear = 0x80;
}
gIER = rIER;
gIFR = rIFR;
gBufA = rSIFR;
gBufB = rBufB;
break;
/* Quadra and early MacIIs agree on the VIA locations */
case MAC_VIA_QUADRA:
case MAC_VIA_II:
via1 = (void *) VIA1_BASE;
via2 = (void *) VIA2_BASE;
rbv_present = 0;
oss_present = 0;
rbv_clear = 0x00;
gIER = vIER;
gIFR = vIFR;
gBufA = vBufA;
gBufB = vBufB;
break;
default:
panic("UNKNOWN VIA TYPE");
}
printk(KERN_INFO "VIA1 at %p is a 6522 or clone\n", via1);
printk(KERN_INFO "VIA2 at %p is ", via2);
if (rbv_present) {
printk("an RBV\n");
} else if (oss_present) {
printk("an OSS\n");
} else {
printk("a 6522 or clone\n");
}
#ifdef DEBUG_VIA
via_debug_dump();
#endif
/*
* Shut down all IRQ sources, reset the timers, and
* kill the timer latch on VIA1.
*/
via1[vIER] = 0x7F;
via1[vIFR] = 0x7F;
via1[vT1LL] = 0;
via1[vT1LH] = 0;
via1[vT1CL] = 0;
via1[vT1CH] = 0;
via1[vT2CL] = 0;
via1[vT2CH] = 0;
via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
via1[vACR] &= ~0x03; /* disable port A & B latches */
/*
* SE/30: disable video IRQ
* XXX: testing for SE/30 VBL
*/
if (macintosh_config->ident == MAC_MODEL_SE30) {
via1[vDirB] |= 0x40;
via1[vBufB] |= 0x40;
}
/*
* Set the RTC bits to a known state: all lines to outputs and
* RTC disabled (yes that's 0 to enable and 1 to disable).
*/
via1[vDirB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData);
via1[vBufB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk);
/* Everything below this point is VIA2/RBV only... */
if (oss_present)
return;
if ((macintosh_config->via_type == MAC_VIA_QUADRA) &&
(macintosh_config->adb_type != MAC_ADB_PB1) &&
(macintosh_config->adb_type != MAC_ADB_PB2) &&
(macintosh_config->ident != MAC_MODEL_C660) &&
(macintosh_config->ident != MAC_MODEL_Q840)) {
via_alt_mapping = 1;
via1[vDirB] |= 0x40;
via1[vBufB] &= ~0x40;
} else {
via_alt_mapping = 0;
}
/*
* Now initialize VIA2. For RBV we just kill all interrupts;
* for a regular VIA we also reset the timers and stuff.
*/
via2[gIER] = 0x7F;
via2[gIFR] = 0x7F | rbv_clear;
if (!rbv_present) {
via2[vT1LL] = 0;
via2[vT1LH] = 0;
via2[vT1CL] = 0;
via2[vT1CH] = 0;
via2[vT2CL] = 0;
via2[vT2CH] = 0;
via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
via2[vACR] &= ~0x03; /* disable port A & B latches */
}
/* Everything below this point is VIA2 only... */
if (rbv_present)
return;
/*
* Set vPCR for control line interrupts.
*
* CA1 (SLOTS IRQ), CB1 (ASC IRQ): negative edge trigger.
*
* Macs with ESP SCSI have a negative edge triggered SCSI interrupt.
* Testing reveals that PowerBooks do too. However, the SE/30
* schematic diagram shows an active high NCR5380 IRQ line.
*/
pr_debug("VIA2 vPCR is 0x%02X\n", via2[vPCR]);
if (macintosh_config->via_type == MAC_VIA_II) {
/* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, pos. edge */
via2[vPCR] = 0x66;
} else {
/* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, neg. edge */
via2[vPCR] = 0x22;
}
}
/*
* Start the 100 Hz clock
*/
void __init via_init_clock(irq_handler_t func)
{
via1[vACR] |= 0x40;
via1[vT1LL] = MAC_CLOCK_LOW;
via1[vT1LH] = MAC_CLOCK_HIGH;
via1[vT1CL] = MAC_CLOCK_LOW;
via1[vT1CH] = MAC_CLOCK_HIGH;
if (request_irq(IRQ_MAC_TIMER_1, func, 0, "timer", func))
pr_err("Couldn't register %s interrupt\n", "timer");
}
/*
* Debugging dump, used in various places to see what's going on.
*/
void via_debug_dump(void)
{
printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
(uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);
printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",
(uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);
if (oss_present) {
printk(KERN_DEBUG "VIA2: <OSS>\n");
} else if (rbv_present) {
printk(KERN_DEBUG "VIA2: IFR = 0x%02X IER = 0x%02X\n",
(uint) via2[rIFR], (uint) via2[rIER]);
printk(KERN_DEBUG " SIFR = 0x%02X SIER = 0x%02X\n",
(uint) via2[rSIFR], (uint) via2[rSIER]);
} else {
printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
(uint) via2[vDirA], (uint) via2[vDirB],
(uint) via2[vACR]);
printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n",
(uint) via2[vPCR],
(uint) via2[vIFR], (uint) via2[vIER]);
}
}
/*
* This is always executed with interrupts disabled.
*
* TBI: get time offset between scheduling timer ticks
*/
u32 mac_gettimeoffset(void)
{
unsigned long ticks, offset = 0;
/* read VIA1 timer 2 current value */
ticks = via1[vT1CL] | (via1[vT1CH] << 8);
/* The probability of underflow is less than 2% */
if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50)
/* Check for pending timer interrupt in VIA1 IFR */
if (via1[vIFR] & 0x40) offset = TICK_SIZE;
ticks = MAC_CLOCK_TICK - ticks;
ticks = ticks * 10000L / MAC_CLOCK_TICK;
return (ticks + offset) * 1000;
}
/*
* Flush the L2 cache on Macs that have it by flipping
* the system into 24-bit mode for an instant.
*/
void via_flush_cache(void)
{
via2[gBufB] &= ~VIA2B_vMode32;
via2[gBufB] |= VIA2B_vMode32;
}
/*
* Return the status of the L2 cache on a IIci
*/
int via_get_cache_disable(void)
{
/* Safeguard against being called accidentally */
if (!via2) {
printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n");
return 1;
}
return (int) via2[gBufB] & VIA2B_vCDis;
}
/*
* Initialize VIA2 for Nubus access
*/
void __init via_nubus_init(void)
{
/* unlock nubus transactions */
if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
(macintosh_config->adb_type != MAC_ADB_PB2)) {
/* set the line to be an output on non-RBV machines */
if (!rbv_present)
via2[vDirB] |= 0x02;
/* this seems to be an ADB bit on PMU machines */
/* according to MkLinux. -- jmt */
via2[gBufB] |= 0x02;
}
/*
* Disable the slot interrupts. On some hardware that's not possible.
* On some hardware it's unclear what all of these I/O lines do.
*/
switch (macintosh_config->via_type) {
case MAC_VIA_II:
case MAC_VIA_QUADRA:
pr_debug("VIA2 vDirA is 0x%02X\n", via2[vDirA]);
break;
case MAC_VIA_IICI:
/* RBV. Disable all the slot interrupts. SIER works like IER. */
via2[rSIER] = 0x7F;
break;
}
}
void via_nubus_irq_startup(int irq)
{
int irq_idx = IRQ_IDX(irq);
switch (macintosh_config->via_type) {
case MAC_VIA_II:
case MAC_VIA_QUADRA:
/* Make the port A line an input. Probably redundant. */
if (macintosh_config->via_type == MAC_VIA_II) {
/* The top two bits are RAM size outputs. */
via2[vDirA] &= 0xC0 | ~(1 << irq_idx);
} else {
/* Allow NuBus slots 9 through F. */
via2[vDirA] &= 0x80 | ~(1 << irq_idx);
}
/* fall through */
case MAC_VIA_IICI:
via_irq_enable(irq);
break;
}
}
void via_nubus_irq_shutdown(int irq)
{
switch (macintosh_config->via_type) {
case MAC_VIA_II:
case MAC_VIA_QUADRA:
/* Ensure that the umbrella CA1 interrupt remains enabled. */
via_irq_enable(irq);
break;
case MAC_VIA_IICI:
via_irq_disable(irq);
break;
}
}
/*
* The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
* via6522.c :-), disable/pending masks added.
*/
void via1_irq(unsigned int irq, struct irq_desc *desc)
{
int irq_num;
unsigned char irq_bit, events;
events = via1[vIFR] & via1[vIER] & 0x7F;
if (!events)
return;
irq_num = VIA1_SOURCE_BASE;
irq_bit = 1;
do {
if (events & irq_bit) {
via1[vIFR] = irq_bit;
generic_handle_irq(irq_num);
}
++irq_num;
irq_bit <<= 1;
} while (events >= irq_bit);
}
static void via2_irq(unsigned int irq, struct irq_desc *desc)
{
int irq_num;
unsigned char irq_bit, events;
events = via2[gIFR] & via2[gIER] & 0x7F;
if (!events)
return;
irq_num = VIA2_SOURCE_BASE;
irq_bit = 1;
do {
if (events & irq_bit) {
via2[gIFR] = irq_bit | rbv_clear;
generic_handle_irq(irq_num);
}
++irq_num;
irq_bit <<= 1;
} while (events >= irq_bit);
}
/*
* Dispatch Nubus interrupts. We are called as a secondary dispatch by the
* VIA2 dispatcher as a fast interrupt handler.
*/
void via_nubus_irq(unsigned int irq, struct irq_desc *desc)
{
int slot_irq;
unsigned char slot_bit, events;
events = ~via2[gBufA] & 0x7F;
if (rbv_present)
events &= via2[rSIER];
else
events &= ~via2[vDirA];
if (!events)
return;
do {
slot_irq = IRQ_NUBUS_F;
slot_bit = 0x40;
do {
if (events & slot_bit) {
events &= ~slot_bit;
generic_handle_irq(slot_irq);
}
--slot_irq;
slot_bit >>= 1;
} while (events);
/* clear the CA1 interrupt and make certain there's no more. */
via2[gIFR] = 0x02 | rbv_clear;
events = ~via2[gBufA] & 0x7F;
if (rbv_present)
events &= via2[rSIER];
else
events &= ~via2[vDirA];
} while (events);
}
/*
* Register the interrupt dispatchers for VIA or RBV machines only.
*/
void __init via_register_interrupts(void)
{
if (via_alt_mapping) {
/* software interrupt */
irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
/* via1 interrupt */
irq_set_chained_handler(IRQ_AUTO_6, via1_irq);
} else {
irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
}
irq_set_chained_handler(IRQ_AUTO_2, via2_irq);
irq_set_chained_handler(IRQ_MAC_NUBUS, via_nubus_irq);
}
void via_irq_enable(int irq) {
int irq_src = IRQ_SRC(irq);
int irq_idx = IRQ_IDX(irq);
#ifdef DEBUG_IRQUSE
printk(KERN_DEBUG "via_irq_enable(%d)\n", irq);
#endif
if (irq_src == 1) {
via1[vIER] = IER_SET_BIT(irq_idx);
} else if (irq_src == 2) {
if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)
via2[gIER] = IER_SET_BIT(irq_idx);
} else if (irq_src == 7) {
switch (macintosh_config->via_type) {
case MAC_VIA_II:
case MAC_VIA_QUADRA:
nubus_disabled &= ~(1 << irq_idx);
/* Enable the CA1 interrupt when no slot is disabled. */
if (!nubus_disabled)
via2[gIER] = IER_SET_BIT(1);
break;
case MAC_VIA_IICI:
/* On RBV, enable the slot interrupt.
* SIER works like IER.
*/
via2[rSIER] = IER_SET_BIT(irq_idx);
break;
}
}
}
void via_irq_disable(int irq) {
int irq_src = IRQ_SRC(irq);
int irq_idx = IRQ_IDX(irq);
#ifdef DEBUG_IRQUSE
printk(KERN_DEBUG "via_irq_disable(%d)\n", irq);
#endif
if (irq_src == 1) {
via1[vIER] = IER_CLR_BIT(irq_idx);
} else if (irq_src == 2) {
via2[gIER] = IER_CLR_BIT(irq_idx);
} else if (irq_src == 7) {
switch (macintosh_config->via_type) {
case MAC_VIA_II:
case MAC_VIA_QUADRA:
nubus_disabled |= 1 << irq_idx;
if (nubus_disabled)
via2[gIER] = IER_CLR_BIT(1);
break;
case MAC_VIA_IICI:
via2[rSIER] = IER_CLR_BIT(irq_idx);
break;
}
}
}
void via1_set_head(int head)
{
if (head == 0)
via1[vBufA] &= ~VIA1A_vHeadSel;
else
via1[vBufA] |= VIA1A_vHeadSel;
}
EXPORT_SYMBOL(via1_set_head);
int via2_scsi_drq_pending(void)
{
return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ));
}
EXPORT_SYMBOL(via2_scsi_drq_pending);