Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# 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
+90
View File
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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>
#include "mac.h"
int baboon_present;
static volatile struct baboon *baboon;
/*
* 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;
pr_debug("Baboon detected at %p\n", baboon);
}
/*
* Baboon interrupt handler.
* XXX how do you clear a pending IRQ? is it even necessary?
*/
static void baboon_irq(struct irq_desc *desc)
{
short events, irq_bit;
int irq_num;
events = baboon->mb_ifr & 0x07;
irq_num = IRQ_BABOON_0;
irq_bit = 1;
do {
if (events & irq_bit) {
events &= ~irq_bit;
generic_handle_irq(irq_num);
}
++irq_num;
irq_bit <<= 1;
} while (events);
}
/*
* 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)
{
mac_irq_enable(irq_get_irq_data(IRQ_NUBUS_C));
}
void baboon_irq_disable(int irq)
{
mac_irq_disable(irq_get_irq_data(IRQ_NUBUS_C));
}
File diff suppressed because it is too large Load Diff
+590
View File
@@ -0,0 +1,590 @@
/*
* 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 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 The SCC IOP has to be placed in bypass mode before the serial console
* gets initialized. iop_init() would be one place to do that. Or the
* bootloader could do that. For now, the Serial Switch control panel
* is needed for that -- contrary to the changelog above.
* 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 connected 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 an 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/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_iop.h>
#include "mac.h"
#ifdef DEBUG
#define iop_pr_debug(fmt, ...) \
printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__)
#define iop_pr_cont(fmt, ...) \
printk(KERN_CONT fmt, ##__VA_ARGS__)
#else
#define iop_pr_debug(fmt, ...) \
no_printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__)
#define iop_pr_cont(fmt, ...) \
no_printk(KERN_CONT fmt, ##__VA_ARGS__)
#endif
/* Non-zero if the IOPs are present */
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_AUTOINC;
}
static __inline__ void iop_start(volatile struct mac_iop *iop)
{
iop->status_ctrl = IOP_RUN | IOP_AUTOINC;
}
static __inline__ void iop_interrupt(volatile struct mac_iop *iop)
{
iop->status_ctrl = IOP_IRQ | IOP_RUN | IOP_AUTOINC;
}
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_get_unused_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;
}
/*
* Initialize the IOPs, if present.
*/
void __init iop_init(void)
{
int i;
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_scc_present = 1;
pr_debug("SCC IOP detected at %p\n", iop_base[IOP_NUM_SCC]);
}
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_ism_present = 1;
pr_debug("ISM IOP detected at %p\n", iop_base[IOP_NUM_ISM]);
iop_stop(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.
*/
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])) {
pr_warn("IOP: oh my god, they killed the ISM IOP!\n");
} else {
pr_warn("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;
iop_pr_debug("iop_num %d chan %d reply %*ph\n",
msg->iop_num, msg->channel, IOP_MSG_LEN, msg->reply);
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]);
msg->status = IOP_MSGSTATUS_UNUSED;
}
/*
* 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;
iop_pr_debug("iop_num %d chan %d message %*ph\n",
msg->iop_num, msg->channel, IOP_MSG_LEN, msg->message);
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;
int i,offset;
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);
}
iop_pr_debug("iop_num %d chan %d reply %*ph\n",
iop_num, chan, IOP_MSG_LEN, msg->reply);
if (msg->handler) (*msg->handler)(msg);
msg->status = IOP_MSGSTATUS_UNUSED;
msg = msg->next;
iop_send_queue[iop_num][chan] = msg;
if (msg && iop_readb(iop, IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE)
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;
msg = iop_get_unused_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_pr_debug("iop_num %d chan %d message %*ph\n",
iop_num, chan, IOP_MSG_LEN, msg->message);
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 {
memset(msg->reply, 0, IOP_MSG_LEN);
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_get_unused_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;
iop_do_send(msg);
} else {
while (q->next) q = q->next;
q->next = 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;
u8 events = iop->status_ctrl & (IOP_INT0 | IOP_INT1);
do {
iop_pr_debug("iop_num %d status %02X\n", iop_num,
iop->status_ctrl);
/* INT0 indicates state change on an outgoing message channel */
if (events & IOP_INT0) {
iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
for (i = 0; i < NUM_IOP_CHAN; i++) {
state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
if (state == IOP_MSG_COMPLETE)
iop_handle_send(iop_num, i);
else if (state != IOP_MSG_IDLE)
iop_pr_debug("chan %d send state %02X\n",
i, state);
}
}
/* INT1 for incoming messages */
if (events & IOP_INT1) {
iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
for (i = 0; i < NUM_IOP_CHAN; i++) {
state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
if (state == IOP_MSG_NEW)
iop_handle_recv(iop_num, i);
else if (state != IOP_MSG_IDLE)
iop_pr_debug("chan %d recv state %02X\n",
i, state);
}
}
events = iop->status_ctrl & (IOP_INT0 | IOP_INT1);
} while (events);
return IRQ_HANDLED;
}
void iop_ism_irq_poll(uint iop_num)
{
unsigned long flags;
local_irq_save(flags);
iop_ism_irq(0, (void *)iop_num);
local_irq_restore(flags);
}
+25
View File
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
struct rtc_time;
/* baboon.c */
void baboon_init(void);
/* iop.c */
void iop_init(void);
/* misc.c */
int mac_hwclk(int op, struct rtc_time *t);
/* macboing.c */
void mac_mksound(unsigned int freq, unsigned int length);
/* oss.c */
void oss_init(void);
/* psc.c */
void psc_init(void);
/* via.c */
void via_init(void);
void via_init_clock(void);
+76
View File
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
.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
+303
View File
@@ -0,0 +1,303 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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>
#include "mac.h"
static int mac_asc_inited;
/*
* dumb triangular wave table
*/
static __u8 mac_asc_wave_tab[ 0x800 ];
/*
* 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(struct timer_list *);
static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int );
static void mac_quadra_ring_bell(struct timer_list *);
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);
/*
* 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.
* https://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);
timer_delete(&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(struct timer_list *unused)
{
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(struct timer_list *unused)
{
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);
timer_delete(&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 )
{
}
+268
View File
@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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/sched/debug.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>
#include <asm/processor.h>
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,
};
static irqreturn_t mac_nmi_handler(int irq, void *dev_id)
{
static volatile int in_nmi;
if (in_nmi)
return IRQ_HANDLED;
in_nmi = 1;
pr_info("Non-Maskable Interrupt\n");
show_registers(get_irq_regs());
in_nmi = 0;
return IRQ_HANDLED;
}
void __init mac_init_IRQ(void)
{
m68k_setup_irq_controller(&mac_irq_chip, handle_simple_irq, IRQ_USER,
NUM_MAC_SOURCES - IRQ_USER);
/*
* 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)
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");
}
/*
* 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)
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)
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);
}
+666
View File
@@ -0,0 +1,666 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Miscellaneous Mac68K-specific stuff
*/
#include <linux/types.h>
#include <linux/errno.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 <linux/uaccess.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/macintosh.h>
#include <asm/mac_via.h>
#include <asm/mac_oss.h>
#include <asm/machdep.h>
#include "mac.h"
/*
* Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU
* times wrap in 2040. If we need to handle later times, the read_time functions
* need to be changed to interpret wrapped times as post-2040.
*/
#define RTC_OFFSET 2082844800
static void (*rom_reset)(void);
#if IS_ENABLED(CONFIG_NVRAM)
#ifdef CONFIG_ADB_CUDA
static unsigned char cuda_pram_read_byte(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_pram_write_byte(unsigned char data, int offset)
{
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();
}
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_ADB_PMU
static unsigned char pmu_pram_read_byte(int offset)
{
struct adb_request req;
if (pmu_request(&req, NULL, 3, PMU_READ_XPRAM,
offset & 0xFF, 1) < 0)
return 0;
pmu_wait_complete(&req);
return req.reply[0];
}
static void pmu_pram_write_byte(unsigned char data, int offset)
{
struct adb_request req;
if (pmu_request(&req, NULL, 4, PMU_WRITE_XPRAM,
offset & 0xFF, 1, data) < 0)
return;
pmu_wait_complete(&req);
}
#endif /* CONFIG_ADB_PMU */
#endif /* CONFIG_NVRAM */
/*
* VIA PRAM/RTC access routines
*
* Must be called with interrupts disabled and
* the RTC should be enabled.
*/
static __u8 via_rtc_recv(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_rtc_send(__u8 data)
{
int i, reg, bit;
reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
/* The bits of the byte go into the RTC 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;
}
}
/*
* These values can be found in Inside Macintosh vol. III ch. 2
* which has a description of the RTC chip in the original Mac.
*/
#define RTC_FLG_READ BIT(7)
#define RTC_FLG_WRITE_PROTECT BIT(7)
#define RTC_CMD_READ(r) (RTC_FLG_READ | (r << 2))
#define RTC_CMD_WRITE(r) (r << 2)
#define RTC_REG_SECONDS_0 0
#define RTC_REG_SECONDS_1 1
#define RTC_REG_SECONDS_2 2
#define RTC_REG_SECONDS_3 3
#define RTC_REG_WRITE_PROTECT 13
/*
* Inside Mac has no information about two-byte RTC commands but
* the MAME/MESS source code has the essentials.
*/
#define RTC_REG_XPRAM 14
#define RTC_CMD_XPRAM_READ (RTC_CMD_READ(RTC_REG_XPRAM) << 8)
#define RTC_CMD_XPRAM_WRITE (RTC_CMD_WRITE(RTC_REG_XPRAM) << 8)
#define RTC_CMD_XPRAM_ARG(a) (((a & 0xE0) << 3) | ((a & 0x1F) << 2))
/*
* 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_rtc_command(int command, __u8 *data)
{
unsigned long flags;
int is_read;
local_irq_save(flags);
/* The least significant bits must be 0b01 according to Inside Mac */
command = (command & ~3) | 1;
/* 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_rtc_send((command & 0xFF00) >> 8);
via_rtc_send(command & 0xFF);
is_read = command & (RTC_FLG_READ << 8);
} else { /* one-byte command */
via_rtc_send(command);
is_read = command & RTC_FLG_READ;
}
if (is_read) {
*data = via_rtc_recv();
} else {
via_rtc_send(*data);
}
/* All done, disable the RTC */
via1[vBufB] |= VIA1B_vRTCEnb;
local_irq_restore(flags);
}
#if IS_ENABLED(CONFIG_NVRAM)
static unsigned char via_pram_read_byte(int offset)
{
unsigned char temp;
via_rtc_command(RTC_CMD_XPRAM_READ | RTC_CMD_XPRAM_ARG(offset), &temp);
return temp;
}
static void via_pram_write_byte(unsigned char data, int offset)
{
unsigned char temp;
temp = 0x55;
via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
temp = data;
via_rtc_command(RTC_CMD_XPRAM_WRITE | RTC_CMD_XPRAM_ARG(offset), &temp);
temp = 0x55 | RTC_FLG_WRITE_PROTECT;
via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
}
#endif /* CONFIG_NVRAM */
/*
* 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 time64_t via_read_time(void)
{
union {
__u8 cdata[4];
__u32 idata;
} result, last_result;
int count = 1;
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0), &last_result.cdata[3]);
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1), &last_result.cdata[2]);
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2), &last_result.cdata[1]);
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3), &last_result.cdata[0]);
/*
* The NetBSD guys say to loop until you get the same reading
* twice in a row.
*/
while (1) {
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0),
&result.cdata[3]);
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1),
&result.cdata[2]);
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2),
&result.cdata[1]);
via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3),
&result.cdata[0]);
if (result.idata == last_result.idata)
return (time64_t)result.idata - RTC_OFFSET;
if (++count > 10)
break;
last_result.idata = result.idata;
}
pr_err("%s: failed to read a stable value; got 0x%08x then 0x%08x\n",
__func__, 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_set_rtc_time(struct rtc_time *tm)
{
union {
__u8 cdata[4];
__u32 idata;
} data;
__u8 temp;
time64_t time;
time = mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
/* Clear the write protect bit */
temp = 0x55;
via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
data.idata = lower_32_bits(time + RTC_OFFSET);
via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_0), &data.cdata[3]);
via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_1), &data.cdata[2]);
via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_2), &data.cdata[1]);
via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_3), &data.cdata[0]);
/* Set the write protect bit */
temp = 0x55 | RTC_FLG_WRITE_PROTECT;
via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &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);
}
}
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;
/* Avoid infinite polling loop when PSU is not under Cuda control */
switch (macintosh_config->ident) {
case MAC_MODEL_C660:
case MAC_MODEL_Q605:
case MAC_MODEL_Q605_ACC:
case MAC_MODEL_P475:
case MAC_MODEL_P475F:
return;
}
while (!req.complete)
cuda_poll();
}
#endif /* CONFIG_ADB_CUDA */
/*
*-------------------------------------------------------------------
* Below this point are the generic routines; they'll dispatch to the
* correct routine for the hardware on which we're running.
*-------------------------------------------------------------------
*/
#if IS_ENABLED(CONFIG_NVRAM)
unsigned char mac_pram_read_byte(int addr)
{
switch (macintosh_config->adb_type) {
case MAC_ADB_IOP:
case MAC_ADB_II:
case MAC_ADB_PB1:
return via_pram_read_byte(addr);
#ifdef CONFIG_ADB_CUDA
case MAC_ADB_EGRET:
case MAC_ADB_CUDA:
return cuda_pram_read_byte(addr);
#endif
#ifdef CONFIG_ADB_PMU
case MAC_ADB_PB2:
return pmu_pram_read_byte(addr);
#endif
default:
return 0xFF;
}
}
void mac_pram_write_byte(unsigned char val, int addr)
{
switch (macintosh_config->adb_type) {
case MAC_ADB_IOP:
case MAC_ADB_II:
case MAC_ADB_PB1:
via_pram_write_byte(val, addr);
break;
#ifdef CONFIG_ADB_CUDA
case MAC_ADB_EGRET:
case MAC_ADB_CUDA:
cuda_pram_write_byte(val, addr);
break;
#endif
#ifdef CONFIG_ADB_PMU
case MAC_ADB_PB2:
pmu_pram_write_byte(val, addr);
break;
#endif
default:
break;
}
}
ssize_t mac_pram_get_size(void)
{
return 256;
}
#endif /* CONFIG_NVRAM */
void mac_poweroff(void)
{
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_EGRET ||
macintosh_config->adb_type == MAC_ADB_CUDA) {
cuda_shutdown();
#endif
#ifdef CONFIG_ADB_PMU
} else if (macintosh_config->adb_type == MAC_ADB_PB2) {
pmu_shutdown();
#endif
}
pr_crit("It is now safe to turn off your Macintosh.\n");
local_irq_disable();
while(1);
}
void mac_reset(void)
{
#ifdef CONFIG_ADB_CUDA
if (macintosh_config->adb_type == MAC_ADB_EGRET ||
macintosh_config->adb_type == MAC_ADB_CUDA) {
cuda_restart();
} else
#endif
#ifdef CONFIG_ADB_PMU
if (macintosh_config->adb_type == MAC_ADB_PB2) {
pmu_restart();
} else
#endif
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");
} else {
/* 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);
local_irq_disable();
rom_reset();
}
/* should never get here */
pr_crit("Restart failed. Please restart manually.\n");
local_irq_disable();
while(1);
}
/*
* This function translates seconds since 1970 into a proper date.
*
* Algorithm cribbed from glibc2.1, __offtime().
*
* This is roughly same as rtc_time64_to_tm(), which we should probably
* use here, but it's only available when CONFIG_RTC_LIB is enabled.
*/
#define SECS_PER_MINUTE (60)
#define SECS_PER_HOUR (SECS_PER_MINUTE * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
static void unmktime(time64_t 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 }
};
int days, rem, y, wday;
const unsigned short int *ip;
days = div_u64_rem(time, SECS_PER_DAY, &rem);
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;
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)
{
time64_t now;
if (!op) { /* read */
switch (macintosh_config->adb_type) {
case MAC_ADB_IOP:
case MAC_ADB_II:
case MAC_ADB_PB1:
now = via_read_time();
break;
#ifdef CONFIG_ADB_CUDA
case MAC_ADB_EGRET:
case MAC_ADB_CUDA:
now = cuda_get_time();
break;
#endif
#ifdef CONFIG_ADB_PMU
case MAC_ADB_PB2:
now = pmu_get_time();
break;
#endif
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);
pr_debug("%s: read %ptR\n", __func__, t);
} else { /* write */
pr_debug("%s: tried to write %ptR\n", __func__, t);
switch (macintosh_config->adb_type) {
case MAC_ADB_IOP:
case MAC_ADB_II:
case MAC_ADB_PB1:
via_set_rtc_time(t);
break;
#ifdef CONFIG_ADB_CUDA
case MAC_ADB_EGRET:
case MAC_ADB_CUDA:
cuda_set_rtc_time(t);
break;
#endif
#ifdef CONFIG_ADB_PMU
case MAC_ADB_PB2:
pmu_set_rtc_time(t);
break;
#endif
default:
return -ENODEV;
}
}
return 0;
}
+188
View File
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
#include <asm/mac_oss.h>
#include "mac.h"
int oss_present;
volatile struct mac_oss *oss;
/*
* Initialize the OSS
*/
void __init oss_init(void)
{
int i;
if (macintosh_config->ident != MAC_MODEL_IIFX)
return;
oss = (struct mac_oss *) OSS_BASE;
pr_debug("OSS detected at %p", oss);
oss_present = 1;
/* 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;
}
/*
* Handle OSS interrupts.
* XXX how do you clear a pending IRQ? is it even necessary?
*/
static void oss_iopism_irq(struct irq_desc *desc)
{
generic_handle_irq(IRQ_MAC_ADB);
}
static void oss_scsi_irq(struct irq_desc *desc)
{
generic_handle_irq(IRQ_MAC_SCSI);
}
static void oss_nubus_irq(struct irq_desc *desc)
{
u16 events, irq_bit;
int irq_num;
events = oss->irq_pending & OSS_IP_NUBUS;
irq_num = NUBUS_SOURCE_BASE + 5;
irq_bit = OSS_IP_NUBUS5;
do {
if (events & irq_bit) {
events &= ~irq_bit;
generic_handle_irq(irq_num);
}
--irq_num;
irq_bit >>= 1;
} while (events);
}
static void oss_iopscc_irq(struct irq_desc *desc)
{
generic_handle_irq(IRQ_MAC_SCC);
}
/*
* 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_iopism_irq);
irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_scsi_irq);
irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq);
irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_iopscc_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] = OSS_IRQLEV_VIA1;
}
/*
* 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) {
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) {
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);
}
+168
View File
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_psc.h>
#include "mac.h"
#define DEBUG_PSC
volatile __u8 *psc;
EXPORT_SYMBOL_GPL(psc);
/*
* Debugging dump, used in various places to see what's going on.
*/
static void psc_debug_dump(void)
{
int i;
if (!psc)
return;
for (i = 0x30 ; i < 0x70 ; i += 0x10) {
printk(KERN_DEBUG "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 __init void psc_dma_die_die_die(void)
{
int i;
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);
}
}
/*
* 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;
return;
}
/*
* The PSC is always at the same spot, but using psc
* keeps things consistent with the psc_xxxx functions.
*/
psc = (void *) PSC_BASE;
pr_debug("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(struct irq_desc *desc)
{
unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
unsigned int irq = irq_desc_get_irq(desc);
int pIFR = pIFRbase + offset;
int pIER = pIERbase + offset;
int irq_num;
unsigned char irq_bit, events;
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_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30);
irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40);
irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50);
irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (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);
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);
psc_write_byte(pIER, 1 << irq_idx);
}
+654
View File
@@ -0,0 +1,654 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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/clocksource.h>
#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/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
#include <asm/mac_psc.h>
#include <asm/mac_oss.h>
#include "mac.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;
/*
* 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 relevant 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);
static void via_nubus_init(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.
*/
void __init via_init(void)
{
via1 = (void *)VIA1_BASE;
pr_debug("VIA1 detected at %p\n", via1);
if (oss_present) {
via2 = NULL;
rbv_present = 0;
} else {
switch (macintosh_config->via_type) {
/* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
case MAC_VIA_IICI:
via2 = (void *)RBV_BASE;
pr_debug("VIA2 (RBV) detected at %p\n", via2);
rbv_present = 1;
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:
via2 = (void *) VIA2_BASE;
pr_debug("VIA2 detected at %p\n", via2);
rbv_present = 0;
rbv_clear = 0x00;
gIER = vIER;
gIFR = vIFR;
gBufA = vBufA;
gBufB = vBufB;
break;
default:
panic("UNKNOWN VIA TYPE");
}
}
#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[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
*/
if (macintosh_config->ident == MAC_MODEL_SE30) {
via1[vDirB] |= 0x40;
via1[vBufB] |= 0x40;
}
switch (macintosh_config->adb_type) {
case MAC_ADB_IOP:
case MAC_ADB_II:
case MAC_ADB_PB1:
/*
* 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;
break;
}
/* 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[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 */
}
via_nubus_init();
/* 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;
}
}
/*
* 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 (!via2)
return;
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]);
}
}
/*
* Flush the L2 cache on Macs that have it by flipping
* the system into 24-bit mode for an instant.
*/
void via_l2_flush(int writeback)
{
unsigned long flags;
local_irq_save(flags);
via2[gBufB] &= ~VIA2B_vMode32;
via2[gBufB] |= VIA2B_vMode32;
local_irq_restore(flags);
}
/*
* Initialize VIA2 for Nubus access
*/
static 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);
}
fallthrough;
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.
*/
#define VIA_TIMER_1_INT BIT(6)
void via1_irq(struct irq_desc *desc)
{
int irq_num;
unsigned char irq_bit, events;
events = via1[vIFR] & via1[vIER] & 0x7F;
if (!events)
return;
irq_num = IRQ_MAC_TIMER_1;
irq_bit = VIA_TIMER_1_INT;
if (events & irq_bit) {
unsigned long flags;
local_irq_save(flags);
via1[vIFR] = irq_bit;
generic_handle_irq(irq_num);
local_irq_restore(flags);
events &= ~irq_bit;
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(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.
*/
static void via_nubus_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);
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);
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);
/* timer and clock source */
#define VIA_CLOCK_FREQ 783360 /* VIA "phase 2" clock in Hz */
#define VIA_TIMER_CYCLES (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */
#define VIA_TC (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */
#define VIA_TC_LOW (VIA_TC & 0xFF)
#define VIA_TC_HIGH (VIA_TC >> 8)
static u64 mac_read_clk(struct clocksource *cs);
static struct clocksource mac_clk = {
.name = "via1",
.rating = 250,
.read = mac_read_clk,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static u32 clk_total, clk_offset;
static irqreturn_t via_timer_handler(int irq, void *dev_id)
{
clk_total += VIA_TIMER_CYCLES;
clk_offset = 0;
legacy_timer_tick(1);
return IRQ_HANDLED;
}
void __init via_init_clock(void)
{
if (request_irq(IRQ_MAC_TIMER_1, via_timer_handler, IRQF_TIMER, "timer",
NULL)) {
pr_err("Couldn't register %s interrupt\n", "timer");
return;
}
via1[vT1CL] = VIA_TC_LOW;
via1[vT1CH] = VIA_TC_HIGH;
via1[vACR] |= 0x40;
clocksource_register_hz(&mac_clk, VIA_CLOCK_FREQ);
}
static u64 mac_read_clk(struct clocksource *cs)
{
unsigned long flags;
u8 count_high;
u16 count;
u32 ticks;
/*
* Timer counter wrap-around is detected with the timer interrupt flag
* but reading the counter low byte (vT1CL) would reset the flag.
* Also, accessing both counter registers is essentially a data race.
* These problems are avoided by ignoring the low byte. Clock accuracy
* is 256 times worse (error can reach 0.327 ms) but CPU overhead is
* reduced by avoiding slow VIA register accesses.
*
* The VIA timer counter observably decrements to 0xFFFF before the
* counter reload interrupt gets raised. That complicates things a bit.
*
* State | vT1CH | VIA_TIMER_1_INT | inference drawn
* ------+------------+-----------------+-----------------------------
* i | FE thru 00 | false | counter is decrementing
* ii | FF | false | counter wrapped
* iii | FF | true | wrapped, interrupt raised
* iv | FF | false | wrapped, interrupt handled
* v | FE thru 00 | true | wrapped, interrupt unhandled
*
* State iv is never observed because handling the interrupt involves
* a 6522 register access and every access consumes a "phi 2" clock
* cycle. So 0xFF implies either state ii or state iii, depending on
* the value of the VIA_TIMER_1_INT bit.
*/
local_irq_save(flags);
count_high = via1[vT1CH];
if (count_high == 0xFF)
count_high = 0;
if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT))
clk_offset = VIA_TIMER_CYCLES;
count = count_high << 8;
ticks = VIA_TIMER_CYCLES - count;
ticks += clk_offset + clk_total;
local_irq_restore(flags);
return ticks;
}