* ========== Copyright Header Begin ==========================================
* OpenSPARC T2 Processor File: sam_dev.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
* ========== Copyright Header End ============================================
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
#pragma ident "@(#)sam_dev.c 1.12 07/10/12 SMI"
#include "tsparcv9internal.h"
void sam_parse_dev(domain_t
* domainp
, config_dev_t
*config_devp
);
void sam_init_device(char *dev_namep
, config_dev_t
*config_devp
);
static void sam_set_default_args(mmi_data_t
*mmi_datap
);
static void sam_parse_args(mmi_data_t
*mmi_datap
);
static void sam_init_dev(config_dev_t
*config_devp
);
static void sam_dump_dev(config_dev_t
*config_devp
);
static tpaddr_t
sam_dev_non_cacheable(config_addr_t
*config_addrp
, dev_access_t type
,
tpaddr_t offset
, uint8_t **blockp
);
static bool_t
sam_dev_cpu_access(simcpu_t
*sp
, config_addr_t
*cap
, tpaddr_t offset
,
maccess_t op
, uint64_t *regp
);
static simcpu_t
*sam_find_simcpu(int cpuid
);
* Maps an address range in the domain to a SAM device, checking for overlap
sam_insert_address_range(domain_t
* domainp
, config_dev_t
*config_devp
,
tpaddr_t baseaddr
, tpaddr_t topaddr
) {
insert_domain_address(domainp
, config_devp
, baseaddr
, topaddr
);
overlapp
= insert_domain_device(domainp
, config_devp
);
lex_fatal("device \"%s\" @ 0x%llx overlaps with device \"%s\" @ 0x%llx",
overlapp
->dev_typep
->dev_type_namep
,
overlapp
->addrp
->baseaddr
,
config_devp
->dev_typep
->dev_type_namep
,
config_devp
->addrp
->baseaddr
);
* The SAM MMI device is parsed by the 'mmi_device' directive in
* mmi_device <dev_name> <base_addr> + <size> {
* - use SAM MMI NIU device model for Niagara 2:
* mmi_device "n2niu" 0x8100000000 + 0x100000000;
* - use SAM MMI dumbserial device model:
* mmi_device "dumbserial-mmi" 0x1f10000000 + 0x50 {
sam_parse_dev(domain_t
* domainp
, config_dev_t
*config_devp
)
dev_type_t
*dev_typep
= (dev_type_t
*)config_devp
->dev_typep
;
tpaddr_t baseaddr
, size
, topaddr
;
char *type
, argv
[BUFSIZ
];
DBG(printf("sam_parse_dev: parsing device %d\n", config_devp
->device_id
); );
sam_devp
= Xcalloc(1, sam_device_t
);
mmi_datap
= Xcalloc(1, mmi_data_t
);
sam_devp
->config_devp
= config_devp
;
sam_devp
->mmi_datap
= mmi_datap
;
mmi_datap
->modname
= (char *)Xstrdup(dev_typep
->dev_type_namep
);
mmi_datap
->argv
= (char**) Xcalloc(MMI_MAX_ARGC
, char**);
mmi_datap
->argc
= 0; /* Initialize argument count to 0 */
mmi_datap
->scx
= NULL
; /* Initialize to NULL for most devices */
config_devp
->devp
= sam_devp
;
pthread_mutex_init(&sam_devp
->mmi_lock
, NULL
);
/* Next value is end address */
sprintf(argv
, "base=0x%llx", baseaddr
);
mmi_datap
->argv
[0] = Xstrdup(argv
);
topaddr
= baseaddr
+ lex
.val
;
sprintf(argv
, "size=0x%llx", topaddr
- baseaddr
);
mmi_datap
->argv
[1] = Xstrdup(argv
);
lex_fatal("top address <= base address with device %s",
/* Add the current address range into the domain address map */
sam_insert_address_range(domainp
, config_devp
, baseaddr
, topaddr
);
if (lex_get_token() == T_Comma
) {
* More address ranges to map to device.
* Allocate new control struct, but share device entry.
config_devp
= Xmalloc( sizeof(config_dev_t
) );
config_devp
->dev_typep
= dev_typep
;
config_devp
->is_implied
= false;
config_devp
->addrp
= NULL
;
config_devp
->devp
= sam_devp
;
sam_set_default_args(mmi_datap
);
lex_fatal("sam_parse_dev: unexpected token encountered");
while ( (tok
= lex_get_token()) != T_R_Brace
) {
sam_parse_args(mmi_datap
);
static void sam_parse_args(mmi_data_t
*mmi_datap
)
char libname
[MAXPATHLEN
];
* This code only applies to Rock setups at the moment
* necessitating some complex linking code.
if (strstr(mmi_datap
->modname
, HH
)) {
scx_handle_t (*find_scx_device_fn
)(char*);
if (streq(lex
.strp
, "scx")) {
/* Try to get function pointer for find_scx_device */
(scx_handle_t (*)(char*)) dlopen_lib("rock",
scx_ops
= *((scx_api
**) dlopen_lib("rock", "scx_ops",
if (find_scx_device_fn
== NULL
|| scx_ops
== NULL
) {
lex_fatal("Attempted to define SCX Hammerhead "
"interface in non-Rock SCX simulation");
mmi_datap
->scx
= find_scx_device_fn(lex
.strp
);
mmi_datap
->scx_ops
= scx_ops
;
if (strstr(mmi_datap
->modname
, N2NIU
)) {
if (streq(lex
.strp
, "type")) {
type
= Xstrdup(lex
.strp
);
sprintf(argv
, "type=%s", type
);
mmi_datap
->argv
[mmi_datap
->argc
] = Xstrdup(argv
);
if (strstr(mmi_datap
->modname
, PARROT
)) {
if (streq(lex
.strp
, "bus")) {
sprintf(argv
, "bus=%s", bus
);
mmi_datap
->argv
[mmi_datap
->argc
] = Xstrdup(argv
);
if (streq(lex
.strp
, "dev")) {
sprintf(argv
, "dev=%x", dev
);
mmi_datap
->argv
[mmi_datap
->argc
] = Xstrdup(argv
);
if (streq(lex
.strp
, "func")) {
if (7 < func
|| func
< 0)
sprintf(argv
, "fun=%x", func
);
mmi_datap
->argv
[mmi_datap
->argc
] = Xstrdup(argv
);
if (strstr(mmi_datap
->modname
, PCIE_BUS
)) {
if (streq(lex
.strp
, "bus")) {
mmi_datap
->instance_name
= Xstrdup(lex
.strp
);
lex_fatal("sam_parse_args: unexpected token encountered");
static void sam_set_default_args(mmi_data_t
*mmi_datap
)
uint8_t siu
, hh
, pcie
, parrot
= 0;
if (strstr(mmi_datap
->modname
, ROCK_SIU
) ||
strstr(mmi_datap
->modname
, HH
) ||
strstr(mmi_datap
->modname
, PCIE_BUS
) ||
strstr(mmi_datap
->modname
, PARROT
)) {
mmi_datap
->argv
[mmi_datap
->argc
] = DEBUG_L2
;
#endif /* DEBUG_SAM_MODULES */
sam_get_instance(ROCK_SIU
, &siu
);
sam_get_instance(HH
, &hh
);
sam_get_instance(PCIE_BUS
, &pcie
);
sam_get_instance(PARROT
, &parrot
);
if (strstr(mmi_datap
->modname
, ROCK_SIU
)) {
sprintf(str
, "rock_chip_id=%d", (siu
-1));
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
sprintf(str
, "hh=hh%d", siu
);
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
if (strstr(mmi_datap
->modname
, HH
)) {
sprintf(str
, "pciA=pci%d", hh
);
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
sprintf(str
, "pciB=pci%d", (hh
+ 1));
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
sprintf(str
, "rock_siu=rock_siu%d", hh
);
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
if (strstr(mmi_datap
->modname
, PCIE_BUS
)) {
sprintf(str
, "bridge=hh%d", pcie
);
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
sprintf(str
, "busnum=1");
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
sprintf(str
, "pci%d", pcie
);
mmi_datap
->instance_name
= strdup(str
);
if (strstr(mmi_datap
->modname
, PARROT
)) {
sprintf(str
, "bus=pci%d", (pcie
&& (parrot
/ pcie
)) ?
(pcie
) : (parrot
% pcie
));
mmi_datap
->argv
[mmi_datap
->argc
] = strdup(str
);
static void sam_parse_dev_stub(config_dev_t
*config_devp
) {
* Complete initialization of the SAM MMI device
static void sam_init_dev_stub(config_dev_t
*config_devp
)
* SAM MMI device configuration dump
static void sam_dump_dev_stub(config_dev_t
*config_devp
)
static tpaddr_t
sam_dev_non_cacheable(config_addr_t
*config_addrp
, dev_access_t type
,
tpaddr_t offset
, uint8_t **blockp
)
return ((tpaddr_t
)0); /* no cacheable memory at all */
* Legion CPU accessing registers of the SAM MMI device
static bool_t
sam_dev_cpu_access(simcpu_t
*sp
, config_addr_t
*cap
, tpaddr_t offset
,
maccess_t op
, uint64_t *regp
)
size
= op
& MA_Size_Mask
;
cpuid
= sp
->config_procp
->proc_typep
->get_cpuid(sp
);
wr
= (op
== MA_St
) ? mmi_true
: mmi_false
;
end
= cap
->baseaddr
+ size
;
for (iomap
= mmi_iomap_head
; iomap
!= NULL
; iomap
= iomap
->next
) {
if ((iomap
->base
<= cap
->baseaddr
+ offset
) &&
(cap
->baseaddr
+ offset
<= iomap
->end
)) {
* access SAM MMI device (the full PA is expected, instead of the offset)
tpaddr_t pa
= offset
+ cap
->baseaddr
;
status
= iomap
->access(cpuid
, iomap
->obj
, pa
, wr
, count
, regp
, bytemask
);
DBGDEV(lprintf(sp
->gid
, "sam_dev_cpu_access: %s pa=0x%llx "
"data=0x%llx count=0x%x pc=0x%llx status=%d\n",
wr
? "WRITE" : "READ", pa
, *regp
, count
,
DBGDEV(lprintf(sp
->gid
, "sam_dev_cpu_access: FAILED to %s @ "
"pa=0x%llx pc=0x%llx status=%d\n",
wr
? "WRITE" : "READ", pa
, sp
->pc
, status
); );
* SAM MMI device accessing Legion main memory
bool_t
sam_mem_access(uint64_t paddr
, uint8_t *datap
, uint64_t size
, dev_access_t type
)
config_dev_t
*config_devp
;
config_addr_t
* target_cap
;
/* XXX - Assumes single address domain per simulation */
config_devp
= sam_dev_list_head
->config_devp
;
ASSERT(config_devp
!= NULL
);
domainp
= config_devp
->domainp
;
target_cap
= find_domain_address(domainp
, paddr
);
if (target_cap
== NULL
) {
/* OK it's a bus error there was no backing store */
extent
= target_cap
->config_devp
->dev_typep
->dev_cacheable(target_cap
, type
,
paddr
-target_cap
->baseaddr
, &bufp
);
* Bufp points to the physical memory, datap points to the device memory.
bcopy(bufp
, datap
, size
);
bcopy(datap
, bufp
, size
);
* Deliver internal on chip I/O interrupts generated by I/O devices such as the
* NIU on the Niagara 2 chip. Each of such interrupts comes with a hardwired
* interrupt number (no addtitional data payload contained), which is used to
* index a table of interrupt information (INT_MAN in NCU for Niagara 2 for
void sam_internal_intr(int dest_cpuid
, sam_device_t
*sam_devp
, int src_iscpu
,
uint32_t vnum
, int traptype
)
config_dev_t
*config_devp
;
config_proc_t
*config_procp
;
DBGDEV(lprintf(-1, "sam_internal_intr: dest_cpuid = %d vnum = 0x%lx\n",
config_devp
= sam_devp
->config_devp
;
* get the config_proc_t pointer, assuming the configuration defines
* a single chip domain XXX
config_procp
= LIST_ENTRY(config_devp
->domainp
->procs
, 0);
* setup ext_sig_t type in terms of the device type (such as ES_NIU
* for interrupts handled by Niagara 2 NCU)
if (strcmp(sam_devp
->mmi_datap
->modname
, "n2niu") == 0)
* call Legion external interrupt handler
pthread_mutex_lock(&sam_devp
->mmi_lock
);
config_procp
->proc_typep
->ext_signal(config_procp
, sigtype
,
pthread_mutex_unlock(&sam_devp
->mmi_lock
);
sp
= sam_find_simcpu(dest_cpuid
);
DBGDEV(lprintf(-1, "sam_internal_intr: Invalid "\
"target cpuid = %d \n", dest_cpuid
););
* call Legion external interrupt handler
config_procp
= sp
->config_procp
;
config_procp
->proc_typep
->ext_signal(config_procp
, sigtype
,
static simcpu_t
*sam_find_simcpu (int cpuid
)
for (i
= 0; i
< simcpu_list
.count
; i
++) {
sp
= LIST_ENTRY(simcpu_list
, i
);
cur_cpuid
= sp
->config_procp
->proc_typep
->get_cpuid(sp
);
if (cpuid
== cur_cpuid
) {
* Deliver external I/O mondo interrupts such as those generated through the
* PCI-express DMU. Those interrupts are contained with a mondo data payload,
* and serviced by the "mondo" interrupt ACK/NACK standard.
void sam_mondo_intr(int dest_cpuid
, void *src
, int src_iscpu
, uint64_t *idata
)
* Create a new Legion device entry to interface with each SAM MMI device.
void sam_init_device(char *dev_namep
, config_dev_t
*config_devp
)
dev_typep
= Xcalloc(1, dev_type_t
);
dev_typep
->dev_type_namep
= (char *)Xstrdup(dev_namep
);
dev_typep
->parse_dev
= sam_parse_dev_stub
;
dev_typep
->init_dev
= sam_init_dev_stub
;
dev_typep
->dump_dev
= sam_dump_dev_stub
;
dev_typep
->dev_cacheable
= sam_dev_non_cacheable
;
dev_typep
->dev_cpu_access
= sam_dev_cpu_access
;
dev_typep
->dev_magic
= DEV_MAGIC
;
config_devp
->dev_typep
= dev_typep
;
config_devp
->is_implied
= false; /* this is a real device */
config_devp
->addrp
= NULL
;
* Load SAM MMI device model (per MMI spec, it's coded with the explicit
* use of the special symbols "_init" and "_fini").
dev_type_t
*sam_load_device(config_dev_t
*config_devp
)
sam_device_t
*sam_devp
, *first_sam_devp
;
char symname
[BUFSIZ
], libname
[MAXPATHLEN
], *rv
, *lv
, *marker
;
* link up the new MMI device
sam_devp
= (sam_device_t
*)config_devp
->devp
;
sam_register_device(sam_devp
);
* Dlopen the SAM MMI device loadable module
sprintf(symname
, "_init");
dev_namep
= config_devp
->dev_typep
->dev_type_namep
;
first_sam_devp
= sam_find_device(dev_namep
);
if (first_sam_devp
->mmi_datap
->create_instance
) {
mmi_register_instance_creator (dev_namep
,
first_sam_devp
->mmi_datap
->create_instance
);
if (!dlopen_lib(dev_namep
, symname
, libname
))
* If this is a Hammerhead device, and we are using it with SCX
* assign the scx handle and pointer to SCX api structure.
if (strstr(sam_devp
->mmi_datap
->modname
, HH
) &&
sam_devp
->mmi_datap
->scx
!= NULL
&&
sam_devp
->mmi_datap
->scx_ops
!= NULL
) {
void (*hh_set_scx
)(void*, scx_handle_t
, scx_api
*);
void *hhObj
= mmi_get_interface(sam_devp
->mmi_datap
, "HH");
fatal("WARNING: Unable to get Hammerhead instance"
hh_set_scx
= (void (*)(void*, scx_handle_t
, scx_api
*))
mmi_get_interface(sam_devp
->mmi_datap
,
if (hh_set_scx
== NULL
) {
fatal("This version of Hammerhead module does"
hh_set_scx(hhObj
, sam_devp
->mmi_datap
->scx
,
sam_devp
->mmi_datap
->scx_ops
);
return(config_devp
->dev_typep
);
* Add the new SAM MMI device pointer into the linked list.
void sam_register_device(sam_device_t
*new)
if (sam_dev_list_head
== NULL
) {
temp
= sam_dev_list_head
;
while (temp
->next
!= NULL
)
* Find a SAM MMI device pointer by its name.
sam_device_t
*sam_find_device(const char *dev_namep
)
sam_devp
= sam_dev_list_head
;
if (sam_devp
->mmi_datap
->instance_name
&& strcmp(sam_devp
->mmi_datap
->instance_name
,
if ( strcmp(sam_devp
->mmi_datap
->modname
, dev_namep
) == 0) {
sam_devp
= sam_devp
->next
;
sam_device_t
*sam_get_instance(const char *dev_namep
, uint8_t *instance
)
sam_device_t
*rsam_devp
= NULL
;
sam_devp
= sam_dev_list_head
;
if (strcmp(sam_devp
->mmi_datap
->modname
,
sam_devp
= sam_devp
->next
;
* Add a new physio map to the linked list.
void sam_register_iomap(mmi_iomap_t
*new)
if (mmi_iomap_head
== NULL
) {
while (temp
->next
!= NULL
)
* Remove a physio map from the linked list.
void sam_unregister_iomap(uint64_t base
, uint64_t size
, void *obj
)
mmi_iomap_t
*iomap
, *prev
, *temp
;
for (prev
= NULL
, iomap
= mmi_iomap_head
; iomap
!= NULL
; prev
= iomap
, iomap
= iomap
->next
) {
if ((iomap
->obj
== obj
) && (iomap
->base
== base
) && (iomap
->size
== size
))
mmi_iomap_head
= temp
->next
;
* Add a new cb_cycle to the linked list.
void sam_register_cb_cycle(sam_cycle_t
*new)
if (cb_cycle_head
== NULL
) {
while (temp
->next
!= NULL
)
* Remove a cb_cycle from the linked list.
void sam_unregister_cb_cycle(void *obj
)
sam_cycle_t
*cb_cycle
, *prev
, *temp
;
for (prev
= NULL
, cb_cycle
= cb_cycle_head
; cb_cycle
!= NULL
;
prev
= cb_cycle
, cb_cycle
= cb_cycle
->next
) {
cb_cycle_head
= temp
->next
;
sam_cycle_t
*sam_find_cb_cycle(void *obj
)
cb_cycle
= cb_cycle_head
;
cb_cycle
= cb_cycle
->next
;
void *sam_start_dma(void *arg
)
cb_cycle
= cb_cycle_head
;
cb_cycle
->handler(cb_cycle
->cb_data
,
cb_cycle
= cb_cycle
->next
;
int sam_asi_ld_handler(uint32_t asi
, uint64_t vaddr
, uint64_t *buf
,
int size
, uint32_t cpuid
)
if (idx
>= sam_asi_dev_list
.count
)
sam_devp
= LIST_ENTRY(sam_asi_dev_list
, idx
);
if (sam_devp
&& sam_devp
->mmi_datap
->asi_ld_handler(
sam_devp
->mmi_datap
->asi_cb_data
, asi
, vaddr
, buf
,
int sam_asi_st_handler(uint32_t asi
, uint64_t vaddr
, uint64_t buf
,
int size
, uint32_t cpuid
)
if (idx
>= sam_asi_dev_list
.count
)
sam_devp
= LIST_ENTRY(sam_asi_dev_list
, idx
);
if (sam_devp
&& sam_devp
->mmi_datap
->asi_st_handler(
sam_devp
->mmi_datap
->asi_cb_data
, asi
, vaddr
, buf
,