#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sunddi.h>
#include <sys/esunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi.h>
#include <sys/modctl.h>
#include <sys/sysmacros.h>
#include <sys/note.h>
#include <sys/platform_module.h>
#include <sys/errno.h>
#include <sys/i2c/clients/i2c_client.h>
#include <sys/cherrystone.h>
#include <sys/machsystm.h>
#include <sys/promif.h>
#include <vm/page.h>
#include <sys/memnode.h>
#include <vm/vm_dep.h>
#define CHERRY_KEY_POLL_PORT 3
#define CHERRY_KEY_POLL_BIT 2
#define CHERRY_KEY_POLL_INTVL 10
#define SHARED_PCF8584_PATH "/pci@9,700000/ebus@1/i2c@1,2e/nvram@4,a4"
static dev_info_t *shared_pcf8584_dip;
static kmutex_t cherry_pcf8584_mutex;
static boolean_t key_locked_bit;
static clock_t keypoll_timeout_hz;
int slice_to_memnode[CHERRYSTONE_MAX_SLICE];
static void update_mem_bounds(int, int, int, uint64_t, uint64_t);
static uint64_t
slice_table[CHERRYSTONE_SBD_SLOTS][CHERRYSTONE_CPUS_PER_BOARD]
[CHERRYSTONE_BANKS_PER_MC][2];
#define SLICE_PA 0
#define SLICE_SPAN 1
int (*p2get_mem_unum)(int, uint64_t, char *, int, int *);
int (*cherry_ssc050_get_port_bit) (dev_info_t *, int, int, uint8_t *, int);
extern void (*abort_seq_handler)();
static int cherry_dev_search(dev_info_t *, void *);
static void keyswitch_poll(void *);
static void cherry_abort_seq_handler(char *msg);
int
set_platform_tsb_spares()
{
return (0);
}
void
startup_platform(void)
{
extern int disable_watchdog_on_exit;
disable_watchdog_on_exit = 1;
mutex_init(&cherry_pcf8584_mutex, NULL, MUTEX_ADAPTIVE, NULL);
}
#pragma weak mmu_init_large_pages
void
set_platform_defaults(void)
{
extern void mmu_init_large_pages(size_t);
if ((mmu_page_sizes == max_mmu_page_sizes) &&
(mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) {
if (&mmu_init_large_pages)
mmu_init_large_pages(mmu_ism_pagesize);
}
}
void
load_platform_modules(void)
{
if (modload("drv", "pmc") < 0) {
cmn_err(CE_NOTE, "pmc driver failed to load");
}
}
void
load_platform_drivers(void)
{
char **drv;
dev_info_t *i2cnexus_dip;
dev_info_t *keysw_dip = NULL;
static char *boot_time_drivers[] = {
"todds1287",
"mc-us3",
"ssc050",
NULL
};
for (drv = boot_time_drivers; *drv; drv++) {
if (i_ddi_attach_hw_nodes(*drv) != DDI_SUCCESS)
cmn_err(CE_WARN, "Failed to install \"%s\" driver.",
*drv);
}
(void) ddi_hold_driver(ddi_name_to_major("mc-us3"));
(void) ddi_hold_driver(ddi_name_to_major("ssc050"));
cherry_ssc050_get_port_bit = (int (*) (dev_info_t *, int, int,
uint8_t *, int)) modgetsymvalue("ssc050_get_port_bit", 0);
if (cherry_ssc050_get_port_bit == NULL) {
cmn_err(CE_WARN, "cannot find ssc050_get_port_bit");
return;
}
e_ddi_walk_driver("i2c-ssc050", cherry_dev_search, (void *)&keysw_dip);
ASSERT(keysw_dip != NULL);
e_ddi_hold_devi(keysw_dip);
keypoll_timeout_hz = drv_usectohz(10 * MICROSEC);
keyswitch_poll(keysw_dip);
abort_seq_handler = cherry_abort_seq_handler;
i2cnexus_dip = e_ddi_hold_devi_by_path(SHARED_PCF8584_PATH, 0);
ASSERT(i2cnexus_dip != NULL);
shared_pcf8584_dip = ddi_get_parent(i2cnexus_dip);
ndi_hold_devi(shared_pcf8584_dip);
ndi_rele_devi(i2cnexus_dip);
}
static int
cherry_dev_search(dev_info_t *dip, void *arg)
{
int *dev_regs;
uint_t len;
int err;
if (strcmp(ddi_binding_name(dip), "i2c-ssc050") != 0)
return (DDI_WALK_CONTINUE);
err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "reg", &dev_regs, &len);
if (err != DDI_PROP_SUCCESS) {
return (DDI_WALK_CONTINUE);
}
if (dev_regs[0] == 0 && dev_regs[1] == 0x82) {
*((dev_info_t **)arg) = dip;
ddi_prop_free(dev_regs);
return (DDI_WALK_TERMINATE);
}
ddi_prop_free(dev_regs);
return (DDI_WALK_CONTINUE);
}
static void
keyswitch_poll(void *arg)
{
dev_info_t *dip = arg;
uchar_t port_byte;
int port = CHERRY_KEY_POLL_PORT;
int bit = CHERRY_KEY_POLL_BIT;
int err;
err = cherry_ssc050_get_port_bit(dip, port, bit,
&port_byte, I2C_NOSLEEP);
if (err != 0) {
cmn_err(CE_WARN, "keyswitch polling disabled: "
"errno=%d while reading ssc050", err);
return;
}
key_locked_bit = (boolean_t)((port_byte & 0x1));
(void) timeout(keyswitch_poll, (caddr_t)dip, keypoll_timeout_hz);
}
static void
cherry_abort_seq_handler(char *msg)
{
if (key_locked_bit == 0)
cmn_err(CE_CONT, "KEY in LOCKED position, "
"ignoring debug enter sequence");
else {
debug_enter(msg);
}
}
int
plat_cpu_poweron(struct cpu *cp)
{
return (ENOTSUP);
}
int
plat_cpu_poweroff(struct cpu *cp)
{
return (ENOTSUP);
}
static int
plat_discover_slice(pfn_t pfn, pfn_t *first, pfn_t *last)
{
int bd, cpu, bank;
for (bd = 0; bd < CHERRYSTONE_SBD_SLOTS; bd++) {
for (cpu = 0; cpu < CHERRYSTONE_CPUS_PER_BOARD; cpu++) {
for (bank = 0; bank < CHERRYSTONE_BANKS_PER_MC;
bank++) {
uint64_t *slice = slice_table[bd][cpu][bank];
uint64_t base = btop(slice[SLICE_PA]);
uint64_t len = btop(slice[SLICE_SPAN]);
if (len && pfn >= base && pfn < (base + len)) {
*first = base;
*last = base + len - 1;
return (bd);
}
}
}
}
panic("plat_discover_slice: no slice for pfn 0x%lx\n", pfn);
}
void
plat_freelist_process(int mnode)
{
}
static void
update_mem_bounds(int boardid, int cpuid, int bankid,
uint64_t base, uint64_t size)
{
uint64_t end;
int mnode;
slice_table[boardid][cpuid][bankid][SLICE_PA] = base;
slice_table[boardid][cpuid][bankid][SLICE_SPAN] = size;
end = base + size - 1;
if ((mnode = plat_lgrphand_to_mem_node(boardid)) == -1) {
if ((mnode = slice_to_memnode[PA_2_SLICE(base)]) == -1)
mnode = mem_node_alloc();
ASSERT(mnode >= 0);
ASSERT(mnode < MAX_MEM_NODES);
plat_assign_lgrphand_to_mem_node(boardid, mnode);
}
base = P2ALIGN(base, (1ul << PA_SLICE_SHIFT));
while (base < end) {
slice_to_memnode[PA_2_SLICE(base)] = mnode;
base += (1ul << PA_SLICE_SHIFT);
}
}
void
plat_fill_mc(pnode_t nodeid)
{
uint64_t mc_addr, saf_addr;
uint64_t mc_decode[CHERRYSTONE_BANKS_PER_MC];
uint64_t base, size;
uint64_t saf_mask;
uint64_t offset;
uint32_t regs[4];
int len;
int local_mc;
int portid;
int boardid;
int cpuid;
int i;
if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
(portid == -1))
return;
boardid = CHERRYSTONE_GETSLOT(portid);
cpuid = CHERRYSTONE_GETSID(portid);
len = prom_getproplen(nodeid, "reg");
if (len != (sizeof (uint32_t) * 4)) {
prom_printf("Warning: malformed 'reg' property\n");
return;
}
if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0)
return;
mc_addr = ((uint64_t)regs[0]) << 32;
mc_addr |= (uint64_t)regs[1];
saf_addr = lddsafaddr(8);
saf_mask = (uint64_t)SAF_MASK;
if ((mc_addr & saf_mask) == saf_addr)
local_mc = 1;
else
local_mc = 0;
for (i = 0; i < CHERRYSTONE_BANKS_PER_MC; i++) {
offset = 0x10 + (i << 3);
if (local_mc)
mc_decode[i] = lddmcdecode(offset);
else
mc_decode[i] = lddphysio(mc_addr | offset);
if ((int64_t)mc_decode[i] < 0) {
base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
size = MC_UK2SPAN(mc_decode[i]);
update_mem_bounds(boardid, cpuid, i, base, size);
}
}
}
void
plat_build_mem_nodes(prom_memlist_t *list, size_t nelems)
{
int slice;
pfn_t basepfn;
pgcnt_t npgs;
mem_node_pfn_shift = PFN_SLICE_SHIFT;
mem_node_physalign = (1ull << PA_SLICE_SHIFT);
npgs = 1ull << PFN_SLICE_SHIFT;
for (slice = 0; slice < CHERRYSTONE_MAX_SLICE; slice++) {
if (slice_to_memnode[slice] == -1)
continue;
basepfn = (uint64_t)slice << PFN_SLICE_SHIFT;
mem_node_add_slice(basepfn, basepfn + npgs - 1);
}
}
int
plat_pfn_to_mem_node(pfn_t pfn)
{
return (slice_to_memnode[PFN_2_SLICE(pfn)]);
}
lgrp_handle_t
plat_lgrp_cpu_to_hand(processorid_t id)
{
return (CHERRYSTONE_GETSLOT(id));
}
void
plat_lgrp_init(void)
{
int i;
for (i = 0; i < CHERRYSTONE_MAX_SLICE; i++) {
slice_to_memnode[i] = -1;
}
}
int
plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to)
{
if (lgrp_optimizations() && (from != to ||
from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE))
return (21);
else
return (19);
}
char *platform_module_list[] = {
(char *)0
};
void
plat_tod_fault(enum tod_fault_type tod_bad)
{
}
int
plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id,
int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp)
{
if (flt_in_memory && (p2get_mem_unum != NULL))
return (p2get_mem_unum(synd_code, P2ALIGN(flt_addr, 8),
buf, buflen, lenp));
else
return (ENOTSUP);
}
void
plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm)
{
_NOTE(ARGUNUSED(bank, dimm))
char board = CHERRYSTONE_GETSLOT_LABEL(mcid);
char old_unum[UNUM_NAMLEN];
(void) strcpy(old_unum, unum);
(void) snprintf(unum, UNUM_NAMLEN, "Slot %c: %s", board, old_unum);
}
int
plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp)
{
char board = CHERRYSTONE_GETSLOT_LABEL(cpuid);
if (snprintf(buf, buflen, "Slot %c", board) >= buflen) {
return (ENOSPC);
} else {
*lenp = strlen(buf);
return (0);
}
}
void
plat_setprop_enter(void)
{
mutex_enter(&cherry_pcf8584_mutex);
}
void
plat_setprop_exit(void)
{
mutex_exit(&cherry_pcf8584_mutex);
}
void
plat_shared_i2c_enter(dev_info_t *i2cnexus_dip)
{
if (i2cnexus_dip == shared_pcf8584_dip) {
plat_setprop_enter();
}
}
void
plat_shared_i2c_exit(dev_info_t *i2cnexus_dip)
{
if (i2cnexus_dip == shared_pcf8584_dip) {
plat_setprop_exit();
}
}