blob: 1d0a0277302ea64b80b596e7db5cd90959099469 [file] [log] [blame]
/* $Id: sun3-control.c,v 1.3 2009/08/30 14:20:59 fredette Exp $ */
/* machine/sun3/sun3-control.c - implementation of Sun 3 emulation control space: */
/*
* Copyright (c) 2003, 2004 Matt Fredette
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Matt Fredette.
* 4. 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.
*/
#include <tme/common.h>
_TME_RCSID("$Id: sun3-control.c,v 1.3 2009/08/30 14:20:59 fredette Exp $");
/* includes: */
#include "sun3-impl.h"
/* the bus cycle handler for function code three space: */
int
_tme_sun3_control_cycle_handler(void *_sun3, struct tme_bus_cycle *cycle_init)
{
struct tme_sun3 *sun3;
struct tme_bus_cycle cycle_resp;
tme_uint32_t address, reg;
tme_uint8_t port_data[sizeof(tme_uint32_t)];
tme_uint8_t *port_data8;
tme_uint32_t *port_data32;
tme_uint32_t value32;
tme_uint8_t enable_old, enable_new;
int rc;
/* recover our sun3: */
sun3 = (struct tme_sun3 *) _sun3;
/* get the address, convert it into a register number, then mask
the address: */
address = cycle_init->tme_bus_cycle_address;
reg = TME_SUN3_CONTROL_REG(address);
if (reg != TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_IDPROM)) {
address &= TME_SUN3_CONTROL_MASK_ADDRESS;
}
/* dispatch on the register, to get bytes for our port: */
port_data8 = &port_data[0];
port_data32 = (tme_uint32_t *) &port_data[0];
switch (reg) {
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_IDPROM):
memcpy(port_data,
&sun3->tme_sun3_idprom_contents[(address & (TME_SUN_IDPROM_SIZE - 1))],
TME_MIN(sizeof(port_data),
TME_SUN_IDPROM_SIZE - (address & (TME_SUN_IDPROM_SIZE - 1))));
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_PGMAP):
rc = _tme_sun3_mmu_pte_get(sun3, address, &value32);
assert(rc == TME_OK);
*port_data32 = tme_htobe_u32(value32);
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_SEGMAP):
*port_data8 = tme_sun_mmu_segmap_get(sun3->tme_sun3_mmu, sun3->tme_sun3_context, address);
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_CONTEXT):
*port_data8 = sun3->tme_sun3_context;
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_ENABLE):
*port_data8 = sun3->tme_sun3_enable;
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_UDVMA):
*port_data8 = sun3->tme_sun3_udvma;
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_BUSERR):
*port_data8 = sun3->tme_sun3_buserr;
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_DIAG):
*port_data8 = sun3->tme_sun3_diag;
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_VAC_TAGS):
abort();
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_VAC_DATA):
abort();
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_VAC_FLUSH):
abort();
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_COPY):
abort();
break;
default:
abort();
}
/* run the bus cycle: */
cycle_resp.tme_bus_cycle_buffer = &port_data[0];
cycle_resp.tme_bus_cycle_buffer_increment = 1;
cycle_resp.tme_bus_cycle_lane_routing = cycle_init->tme_bus_cycle_lane_routing;
cycle_resp.tme_bus_cycle_address = 0;
cycle_resp.tme_bus_cycle_type = (cycle_init->tme_bus_cycle_type
^ (TME_BUS_CYCLE_WRITE
| TME_BUS_CYCLE_READ));
cycle_resp.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS32_LOG2);
tme_bus_cycle_xfer(cycle_init, &cycle_resp);
/* whenever the bus error register is read or written, it is
cleared: */
if (reg == TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_BUSERR)) {
sun3->tme_sun3_buserr = 0;
}
/* these registers only need action taken when they're written: */
if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
/* dispatch on the register: */
switch (reg) {
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_PGMAP):
value32 = tme_betoh_u32(*port_data32);
rc = _tme_sun3_mmu_pte_set(sun3, address, value32);
assert (rc == TME_OK);
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_SEGMAP):
tme_sun_mmu_segmap_set(sun3->tme_sun3_mmu, sun3->tme_sun3_context, address, *port_data8 % TME_SUN3_PMEGS);
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_CONTEXT):
sun3->tme_sun3_context = *port_data8;
_tme_sun3_mmu_context_set(sun3);
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_ENABLE):
enable_old = sun3->tme_sun3_enable;
enable_new = (enable_old & TME_SUN3_ENA_DIAG) | (*port_data8 & ~TME_SUN3_ENA_DIAG);
sun3->tme_sun3_enable = enable_new;
/* if we're changing to or from boot state, make a pseudo-context change: */
if ((enable_old ^ enable_new) & TME_SUN3_ENA_NOTBOOT) {
_tme_sun3_mmu_context_set(sun3);
}
/* if we're changing the m6888x enable bit, call out the change: */
if ((enable_old ^ enable_new) & TME_SUN3_ENA_FPP) {
rc = ((*sun3->tme_sun3_m68k->tme_m68k_bus_m6888x_enable)
(sun3->tme_sun3_m68k, (enable_new & TME_SUN3_ENA_FPP)));
assert (rc == TME_OK);
}
/* if we're enabling or disabling system DVMA, call out the change: */
if ((enable_old ^ enable_new) & TME_SUN3_ENA_SDVMA) {
_tme_sun3_mmu_sdvma_change(sun3);
}
/* certain things can't be enabled: */
if (enable_new
& (TME_SUN3_ENA_COPY
| TME_SUN3_ENA_CACHE)) {
abort();
}
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_UDVMA):
if (sun3->tme_sun3_enable & TME_SUN3_ENA_NOTBOOT) {
abort();
}
sun3->tme_sun3_udvma = *port_data8;
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_DIAG):
sun3->tme_sun3_diag = *port_data8;
/* TBD */
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_VAC_TAGS):
abort();
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_VAC_DATA):
abort();
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_VAC_FLUSH):
abort();
break;
case TME_SUN3_CONTROL_REG(TME_SUN3_CONTROL_COPY):
abort();
break;
}
}
return (TME_OK);
}
/* the bus cycle handler for the interrupt register: */
int
_tme_sun3_intreg_cycle_handler(void *_sun3, struct tme_bus_cycle *cycle_init)
{
struct tme_sun3 *sun3;
int rc;
/* recover our sun3: */
sun3 = (struct tme_sun3 *) _sun3;
/* do the transfer: */
tme_bus_cycle_xfer_memory(cycle_init,
&sun3->tme_sun3_ints,
sizeof(sun3->tme_sun3_ints) - 1);
/* if the interrupt register has been written: */
if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
rc = _tme_sun3_ipl_check(sun3);
assert(rc == TME_OK);
}
return (TME_OK);
}
/* this calls out a memory error interrupt change: */
static void
_tme_sun3_memerr_callout(struct tme_sun3 *sun3)
{
unsigned int int_asserted;
struct tme_bus_connection *conn_bus;
int rc;
/* see if the memory error interrupt should be asserted: */
int_asserted
= ((sun3->tme_sun3_memerr_csr
& (TME_SUN3_MEMERR_X_INT_ACTIVE
| TME_SUN3_MEMERR_X_ENABLE_INT))
== (TME_SUN3_MEMERR_X_INT_ACTIVE
| TME_SUN3_MEMERR_X_ENABLE_INT));
/* if we need to call out an interrupt change: */
if (!int_asserted != !sun3->tme_sun3_memerr_int_asserted) {
/* get our bus connection: */
conn_bus = sun3->tme_sun3_memerr_bus;
/* call out the bus interrupt signal edge: */
rc = (*conn_bus->tme_bus_signal)
(conn_bus,
TME_BUS_SIGNAL_INT_UNSPEC
| (int_asserted
? TME_BUS_SIGNAL_LEVEL_ASSERTED
: TME_BUS_SIGNAL_LEVEL_NEGATED));
/* if this callout was successful, note the new state of the
interrupt signal: */
if (rc == TME_OK) {
sun3->tme_sun3_memerr_int_asserted = int_asserted;
}
/* otherwise, abort: */
else {
abort();
}
}
}
/* the bus cycle handler for the memory error register: */
int
_tme_sun3_memerr_cycle_handler(void *_sun3, struct tme_bus_cycle *cycle_init)
{
struct tme_sun3 *sun3;
tme_uint8_t memerr_reg[TME_SUN3_MEMERR_SIZ_REG];
int write_csr, unlatch;
tme_uint8_t csr_old, csr_new;
/* the read-only bits: */
#define TME_SUN3_MEMERR_RO (TME_SUN3_MEMERR_X_INT_ACTIVE \
| TME_SUN3_MEMERR_PAR_ERR_BL3 \
| TME_SUN3_MEMERR_PAR_ERR_BL2 \
| TME_SUN3_MEMERR_PAR_ERR_BL1 \
| TME_SUN3_MEMERR_PAR_ERR_BL0)
/* recover our sun3: */
sun3 = (struct tme_sun3 *) _sun3;
/* fill the memory error register: */
memerr_reg[TME_SUN3_MEMERR_REG_CSR] = sun3->tme_sun3_memerr_csr;
*((tme_uint32_t *) &memerr_reg[TME_SUN3_MEMERR_SIZ_VADDR]) = tme_htobe_u32(sun3->tme_sun3_memerr_vaddr);
/* assume this cycle won't write the CSR or unlatch the register: */
write_csr = FALSE;
unlatch = FALSE;
/* if this is a write: */
if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
/* see if this writes the CSR: */
write_csr = TME_RANGES_OVERLAP(cycle_init->tme_bus_cycle_address,
(cycle_init->tme_bus_cycle_address
+ cycle_init->tme_bus_cycle_size
- 1),
TME_SUN3_MEMERR_REG_CSR,
(TME_SUN3_MEMERR_REG_CSR
+ TME_SUN3_MEMERR_SIZ_CSR
- 1));
/* see if this write unlatches the register: */
unlatch = TME_RANGES_OVERLAP(cycle_init->tme_bus_cycle_address,
(cycle_init->tme_bus_cycle_address
+ cycle_init->tme_bus_cycle_size
- 1),
TME_SUN3_MEMERR_REG_VADDR,
TME_SUN3_MEMERR_REG_VADDR);
}
/* do the transfer: */
tme_bus_cycle_xfer_memory(cycle_init,
memerr_reg,
sizeof(memerr_reg) - 1);
/* if this is a write: */
if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
/* get the old CSR value, and assume that the new CSR value is the
same: */
csr_old = sun3->tme_sun3_memerr_csr;
csr_new = csr_old;
/* if the CSR register has been written: */
if (write_csr) {
/* get the new CSR value, but preserve the read-only bits: */
csr_new = ((csr_new & TME_SUN3_MEMERR_RO)
| (memerr_reg[TME_SUN3_MEMERR_REG_CSR] & ~TME_SUN3_MEMERR_RO));
}
/* if the memory error register has been unlatched: */
if (unlatch) {
/* clear the memory error: */
csr_new &= ~TME_SUN3_MEMERR_RO;
sun3->tme_sun3_memerr_vaddr = 0;
}
/* if the CSR register has changed: */
if (csr_new != csr_old) {
/* set the new CSR value and call out an interrupt change: */
sun3->tme_sun3_memerr_csr = csr_new;
_tme_sun3_memerr_callout(sun3);
/* if the memory error register is being tested: */
if (csr_new & TME_SUN3_MEMERR_PAR_TEST) {
/* if the test is just beginning, invalidate all TLB entries: */
if (!(csr_old & TME_SUN3_MEMERR_PAR_TEST)) {
tme_sun_mmu_tlbs_invalidate(sun3->tme_sun3_mmu);
}
}
}
}
return (TME_OK);
}
/* the bus cycle handler for the memory error register test: */
int
_tme_sun3_memerr_test_cycle_handler(void *_sun3, struct tme_bus_cycle *cycle_init)
{
struct tme_sun3 *sun3;
struct tme_bus_tlb *tlb;
tme_uint32_t vaddr, cycle_align;
tme_uint8_t csr;
int rc;
/* recover our sun3: */
sun3 = (struct tme_sun3 *) _sun3;
/* get the memory error test TLB entry: */
tlb = sun3->tme_sun3_memerr_tlb;
if (tlb == NULL) {
abort();
}
/* recover the virtual address from the TLB entry. this TLB entry
should be for normal memory, which means it should have no shift: */
if (tlb->tme_bus_tlb_addr_shift != 0) {
abort();
}
vaddr = (cycle_init->tme_bus_cycle_address
- tlb->tme_bus_tlb_addr_offset);
/* calculate the number of bytes after the last byte of the cycle
until the next 32-bit boundary. this bus cycle must not cross a
32-bit boundary: */
cycle_align = ((vaddr & (sizeof(tme_uint32_t) - 1))
+ cycle_init->tme_bus_cycle_size);
if (cycle_align > sizeof(tme_uint32_t)) {
abort();
}
cycle_align = sizeof(tme_uint32_t) - cycle_align;
/* make the pending csr value. this calculates a mask of
TME_SUN3_MEMERR_PAR_ERR_BLx bits corresponding to the byte lanes
being read or written in this cycle: */
csr = (1 << cycle_init->tme_bus_cycle_size) - 1;
csr <<= cycle_align;
/* if this cycle is a read: */
if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) {
/* parity checking must be enabled and there must be no memory
error pending: */
if ((sun3->tme_sun3_memerr_csr
& (TME_SUN3_MEMERR_X_INT_ACTIVE
| TME_SUN3_MEMERR_PAR_ENABLE))
!= TME_SUN3_MEMERR_PAR_ENABLE) {
abort();
}
/* this must be a read of the written address: */
if ((sun3->tme_sun3_memerr_pending_csr & csr) == 0
|| ((sun3->tme_sun3_memerr_pending_vaddr ^ vaddr) & -sizeof(tme_uint32_t)) != 0) {
abort();
}
/* do the read: */
rc = ((*sun3->tme_sun3_memerr_cycle)
(sun3->tme_sun3_memerr_cycle_private,
cycle_init));
/* set the memory error control register: */
sun3->tme_sun3_memerr_csr
= ((sun3->tme_sun3_memerr_csr
& ~TME_SUN3_MEMERR_RO)
| TME_SUN3_MEMERR_X_INT_ACTIVE
| (sun3->tme_sun3_memerr_pending_csr & csr));
/* set the memory error address register: */
sun3->tme_sun3_memerr_vaddr
= ((sun3->tme_sun3_context << 28)
| vaddr);
/* call out an interrupt: */
_tme_sun3_memerr_callout(sun3);
/* invalidate the memory test TLB entry: */
tme_token_invalidate(tlb->tme_bus_tlb_token);
sun3->tme_sun3_memerr_tlb = NULL;
/* tell the CPU that a synchronous event of some kind has happened
on the bus: */
return (rc == TME_OK
? TME_BUS_CYCLE_SYNCHRONOUS_EVENT
: rc);
}
/* otherwise, this must be a write: */
if (cycle_init->tme_bus_cycle_type != TME_BUS_CYCLE_WRITE) {
abort();
}
/* this must be the first write: */
if (sun3->tme_sun3_memerr_pending_csr != 0) {
abort();
}
/* remember the pending memory error address and CSR value: */
sun3->tme_sun3_memerr_pending_csr = csr;
sun3->tme_sun3_memerr_pending_vaddr = vaddr;
/* run the cycle normally: */
return ((*sun3->tme_sun3_memerr_cycle)
(sun3->tme_sun3_memerr_cycle_private,
cycle_init));
#undef TME_SUN3_MEMERR_RO
}
#if 1
#include <stdio.h>
/* this dumps out the sun3 state: */
void
tme_sun3_dump(struct tme_sun3 *sun3)
{
/* dump out the page map register: */
fprintf(stderr, "CONTEXT = 0x%02x\n", sun3->tme_sun3_context);
fprintf(stderr, "\n");
fprintf(stderr, "DIAG = 0x%02x\n", sun3->tme_sun3_diag);
fprintf(stderr, "UDVMA = 0x%02x\n", sun3->tme_sun3_udvma);
fprintf(stderr, "BUSERR = 0x%04x\n", sun3->tme_sun3_buserr);
fprintf(stderr, "ENABLE = 0x%02x\n", sun3->tme_sun3_enable);
fprintf(stderr, "INTS = 0x%02x\n", sun3->tme_sun3_ints);
fprintf(stderr, "MEMERR = 0x%02x 0x%04x\n", sun3->tme_sun3_memerr_csr, sun3->tme_sun3_memerr_vaddr);
}
#endif /* 1 */