/*
* Copyright (c) 1999-2002 Eduardo Horvath
* All rights reserved.
*
* 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, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Child devices receive the Sbus interrupt level in their attach
* arguments. We translate these to CPU IPLs using the following
* tables. Note: obio bus interrupt levels are identical to the
* processor IPL.
*
* The second set of tables is used when the Sbus interrupt level
* cannot be had from the PROM as an `interrupt' property. We then
* fall back on the `intr' property which contains the CPU IPL.
*/
/*
* This value is or'ed into the attach args' interrupt level cookie
* if the interrupt level comes from an `intr' property, i.e. it is
* not an Sbus interrupt level.
*/
#define SBUS_INTR_COMPAT 0x80000000
/*
* Print the location of some sbus-attached device (called just
* before attaching that device). If `sbus' is not NULL, the
* device was found but not configured; print the sbus as well.
* Return UNCONF (config_find ignores this if the device was configured).
*/
int
sbus_print(void *args, const char *busname)
{
struct sbus_attach_args *sa = args;
int i;
if (busname)
aprint_normal("%s at %s", sa->sa_name, busname);
aprint_normal(" slot %ld offset 0x%lx", (long)sa->sa_slot,
(u_long)sa->sa_offset);
for (i = 0; i < sa->sa_nintr; i++) {
struct openprom_intr *sbi = &sa->sa_intr[i];
/* XXXX Use sysio PROM mappings for interrupt vector regs. */
sparc_promaddr_to_handle(sc->sc_bustag, ma->ma_address[0], &sc->sc_bh);
sc->sc_sysio = (struct sysioreg *)bus_space_vaddr(sc->sc_bustag,
sc->sc_bh);
#ifdef _LP64
/*
* 32-bit kernels use virtual addresses for bus space operations
* so we may as well use the prom VA.
*
* 64-bit kernels use physical addresses for bus space operations
* so mapping this in again will reduce TLB thrashing.
*/
if (bus_space_map(sc->sc_bustag, ma->ma_reg[0].ur_paddr,
ma->ma_reg[0].ur_len, 0, &sc->sc_bh) != 0) {
aprint_error_dev(self, "cannot map registers\n");
return;
}
#endif
/*
* Record clock frequency for synchronous SCSI.
* IS THIS THE CORRECT DEFAULT??
*/
sc->sc_clockfreq = prom_getpropint(node, "clock-frequency",
25*1000*1000);
printf(": clock = %s MHz\n", clockfreq(sc->sc_clockfreq));
/*
* Note: the stupid SBUS IOMMU ignores the high bits of an address, so a
* NULL DMA pointer will be translated by the first page of the IOTSB.
* To avoid bugs we'll alloc and ignore the first entry in the IOTSB.
*/
if (vmem_xalloc_addr(sc->sc_is.is_dvmamap, sc->sc_is.is_dvmabase,
PAGE_SIZE, VM_NOSLEEP) != 0) {
panic("sbus iommu: can't toss first dvma page");
}
/*
* Loop through ROM children, fixing any relative addresses
* and then configuring each device.
* `specials' is an array of device names that are treated
* specially:
*/
devhandle_t selfh = device_handle(self);
node0 = OF_child(node);
for (node = node0; node; node = OF_peer(node)) {
char *name1 = prom_getpropstring(node, "name");
/*
* Handle an overtemp situation.
*
* SPARCs have temperature sensors which generate interrupts
* if the machine's temperature exceeds a certain threshold.
* This handles the interrupt and powers off the machine.
* The same needs to be done to PCI controller drivers.
*/
int
sbus_overtemp(void *arg)
{
/* Should try a clean shutdown first */
printf("DANGER: OVER TEMPERATURE detected\nShutting down...\n");
delay(20);
kern_reboot(RB_POWERDOWN|RB_HALT, NULL);
}
/*
* Get interrupt attributes for an Sbus device.
*/
int
sbus_get_intr(struct sbus_softc *sc, int node, struct openprom_intr **ipp,
int *np, int slot)
{
int *ipl;
int n, i;
char buf[32];
/*
* The `interrupts' property contains the Sbus interrupt level.
*/
ipl = NULL;
if (prom_getprop(node, "interrupts", sizeof(int), np, &ipl) == 0) {
struct openprom_intr *ip;
int pri;
/* Default to interrupt level 2 -- otherwise unused */
pri = INTLEVENCODE(2);
/* Change format to an `struct sbus_intr' array */
ip = malloc(*np * sizeof(struct openprom_intr), M_DEVBUF,
M_WAITOK);
/*
* Now things get ugly. We need to take this value which is
* the interrupt vector number and encode the IPL into it
* somehow. Luckily, the interrupt vector has lots of free
* space and we can easily stuff the IPL in there for a while.
*/
prom_getpropstringA(node, "device_type", buf, sizeof buf);
if (buf[0] == '\0')
prom_getpropstringA(node, "name", buf, sizeof buf);
for (i = 0; intrmap[i].in_class; i++)
if (strcmp(intrmap[i].in_class, buf) == 0) {
pri = INTLEVENCODE(intrmap[i].in_lev);
break;
}
/*
* Sbus card devices need the slot number encoded into
* the vector as this is generally not done.
*/
if ((ipl[0] & INTMAP_OBIO) == 0)
pri |= slot << 3;
for (n = 0; n < *np; n++) {
/*
* We encode vector and priority into sbi_pri so we
* can pass them as a unit. This will go away if
* sbus_establish ever takes an sbus_intr instead
* of an integer level.
* Stuff the real vector in sbi_vec.
*/