Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/bhnd/siba/siba.c
/*- | /*- | ||||
* Copyright (c) 2015 Landon Fuller <landon@landonf.org> | * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> | ||||
* Copyright (c) 2017 The FreeBSD Foundation | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Portions of this software were developed by Landon Fuller | |||||
* under sponsorship from the FreeBSD Foundation. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer, | * notice, this list of conditions and the following disclaimer, | ||||
* without modification. | * without modification. | ||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | ||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any | * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any | ||||
▲ Show 20 Lines • Show All 196 Lines • ▼ Show 20 Lines | siba_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask) | ||||
struct bhnd_resource *r; | struct bhnd_resource *r; | ||||
uint32_t ts_low, ts_mask; | uint32_t ts_low, ts_mask; | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* Fetch CFG0 mapping */ | /* Fetch CFG0 mapping */ | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
if ((r = dinfo->cfg[0]) == NULL) | if ((r = dinfo->cfg_res[0]) == NULL) | ||||
return (ENODEV); | return (ENODEV); | ||||
/* Mask and set TMSTATELOW core flag bits */ | /* Mask and set TMSTATELOW core flag bits */ | ||||
ts_mask = (mask << SIBA_TML_SICF_SHIFT) & SIBA_TML_SICF_MASK; | ts_mask = (mask << SIBA_TML_SICF_SHIFT) & SIBA_TML_SICF_MASK; | ||||
ts_low = (value << SIBA_TML_SICF_SHIFT) & ts_mask; | ts_low = (value << SIBA_TML_SICF_SHIFT) & ts_mask; | ||||
return (siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, | return (siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, | ||||
ts_low, ts_mask)); | ts_low, ts_mask)); | ||||
Show All 35 Lines | siba_reset_hw(device_t dev, device_t child, uint16_t ioctl) | ||||
int error; | int error; | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (EINVAL); | return (EINVAL); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
/* Can't suspend the core without access to the CFG0 registers */ | /* Can't suspend the core without access to the CFG0 registers */ | ||||
if ((r = dinfo->cfg[0]) == NULL) | if ((r = dinfo->cfg_res[0]) == NULL) | ||||
return (ENODEV); | return (ENODEV); | ||||
/* We require exclusive control over BHND_IOCTL_CLK_EN and | /* We require exclusive control over BHND_IOCTL_CLK_EN and | ||||
* BHND_IOCTL_CLK_FORCE. */ | * BHND_IOCTL_CLK_FORCE. */ | ||||
if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE)) | if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE)) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* Place core into known RESET state */ | /* Place core into known RESET state */ | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | siba_suspend_hw(device_t dev, device_t child) | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (EINVAL); | return (EINVAL); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
pm = dinfo->pmu_info; | pm = dinfo->pmu_info; | ||||
/* Can't suspend the core without access to the CFG0 registers */ | /* Can't suspend the core without access to the CFG0 registers */ | ||||
if ((r = dinfo->cfg[0]) == NULL) | if ((r = dinfo->cfg_res[0]) == NULL) | ||||
return (ENODEV); | return (ENODEV); | ||||
/* Already in RESET? */ | /* Already in RESET? */ | ||||
ts_low = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATELOW); | ts_low = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATELOW); | ||||
if (ts_low & SIBA_TML_RESET) { | if (ts_low & SIBA_TML_RESET) { | ||||
/* Clear IOCTL flags, ensuring the clock is disabled */ | /* Clear IOCTL flags, ensuring the clock is disabled */ | ||||
return (siba_write_target_state(child, dinfo, | return (siba_write_target_state(child, dinfo, | ||||
SIBA_CFG0_TMSTATELOW, 0x0, SIBA_TML_SICF_MASK)); | SIBA_CFG0_TMSTATELOW, 0x0, SIBA_TML_SICF_MASK)); | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | siba_read_config(device_t dev, device_t child, bus_size_t offset, void *value, | ||||
rman_res_t r_size; | rman_res_t r_size; | ||||
/* Must be directly attached */ | /* Must be directly attached */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* CFG0 registers must be available */ | /* CFG0 registers must be available */ | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
if (dinfo->cfg[0] == NULL) | if (dinfo->cfg_res[0] == NULL) | ||||
return (ENODEV); | return (ENODEV); | ||||
/* Offset must fall within CFG0 */ | /* Offset must fall within CFG0 */ | ||||
r_size = rman_get_size(dinfo->cfg[0]->res); | r_size = rman_get_size(dinfo->cfg_res[0]->res); | ||||
if (r_size < offset || r_size - offset < width) | if (r_size < offset || r_size - offset < width) | ||||
return (EFAULT); | return (EFAULT); | ||||
switch (width) { | switch (width) { | ||||
case 1: | case 1: | ||||
*((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg[0], offset); | *((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg_res[0], | ||||
offset); | |||||
return (0); | return (0); | ||||
case 2: | case 2: | ||||
*((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg[0], offset); | *((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg_res[0], | ||||
offset); | |||||
return (0); | return (0); | ||||
case 4: | case 4: | ||||
*((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg[0], offset); | *((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg_res[0], | ||||
offset); | |||||
return (0); | return (0); | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
siba_write_config(device_t dev, device_t child, bus_size_t offset, | siba_write_config(device_t dev, device_t child, bus_size_t offset, | ||||
const void *value, u_int width) | const void *value, u_int width) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
struct bhnd_resource *r; | struct bhnd_resource *r; | ||||
rman_res_t r_size; | rman_res_t r_size; | ||||
/* Must be directly attached */ | /* Must be directly attached */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* CFG0 registers must be available */ | /* CFG0 registers must be available */ | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
if ((r = dinfo->cfg[0]) == NULL) | if ((r = dinfo->cfg_res[0]) == NULL) | ||||
return (ENODEV); | return (ENODEV); | ||||
/* Offset must fall within CFG0 */ | /* Offset must fall within CFG0 */ | ||||
r_size = rman_get_size(r->res); | r_size = rman_get_size(r->res); | ||||
if (r_size < offset || r_size - offset < width) | if (r_size < offset || r_size - offset < width) | ||||
return (EFAULT); | return (EFAULT); | ||||
switch (width) { | switch (width) { | ||||
Show All 17 Lines | siba_get_port_count(device_t dev, device_t child, bhnd_port_type type) | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, | return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, | ||||
type)); | type)); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
return (siba_addrspace_port_count(dinfo->core_id.num_addrspace)); | return (siba_port_count(&dinfo->core_id, type)); | ||||
} | } | ||||
static u_int | static u_int | ||||
siba_get_region_count(device_t dev, device_t child, bhnd_port_type type, | siba_get_region_count(device_t dev, device_t child, bhnd_port_type type, | ||||
u_int port) | u_int port) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, | return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, | ||||
type, port)); | type, port)); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
if (!siba_is_port_valid(dinfo->core_id.num_addrspace, type, port)) | return (siba_port_region_count(&dinfo->core_id, type, port)); | ||||
return (0); | |||||
return (siba_addrspace_region_count(dinfo->core_id.num_addrspace, | |||||
port)); | |||||
} | } | ||||
static int | static int | ||||
siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, | siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, | ||||
u_int port_num, u_int region_num) | u_int port_num, u_int region_num) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
struct siba_addrspace *addrspace; | struct siba_addrspace *addrspace; | ||||
struct siba_cfg_block *cfg; | |||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (BHND_BUS_GET_PORT_RID(device_get_parent(dev), child, | return (BHND_BUS_GET_PORT_RID(device_get_parent(dev), child, | ||||
port_type, port_num, region_num)); | port_type, port_num, region_num)); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num); | |||||
if (addrspace == NULL) | |||||
return (-1); | |||||
/* Look for a matching addrspace entry */ | |||||
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num); | |||||
if (addrspace != NULL) | |||||
return (addrspace->sa_rid); | return (addrspace->sa_rid); | ||||
/* Try the config blocks */ | |||||
cfg = siba_find_cfg_block(dinfo, port_type, port_num, region_num); | |||||
if (cfg != NULL) | |||||
return (cfg->cb_rid); | |||||
/* Not found */ | |||||
return (-1); | |||||
} | } | ||||
static int | static int | ||||
siba_decode_port_rid(device_t dev, device_t child, int type, int rid, | siba_decode_port_rid(device_t dev, device_t child, int type, int rid, | ||||
bhnd_port_type *port_type, u_int *port_num, u_int *region_num) | bhnd_port_type *port_type, u_int *port_num, u_int *region_num) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (BHND_BUS_DECODE_PORT_RID(device_get_parent(dev), child, | return (BHND_BUS_DECODE_PORT_RID(device_get_parent(dev), child, | ||||
type, rid, port_type, port_num, region_num)); | type, rid, port_type, port_num, region_num)); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
/* Ports are always memory mapped */ | /* Ports are always memory mapped */ | ||||
if (type != SYS_RES_MEMORY) | if (type != SYS_RES_MEMORY) | ||||
return (EINVAL); | return (EINVAL); | ||||
for (int i = 0; i < dinfo->core_id.num_addrspace; i++) { | /* Look for a matching addrspace entry */ | ||||
for (u_int i = 0; i < dinfo->core_id.num_addrspace; i++) { | |||||
if (dinfo->addrspace[i].sa_rid != rid) | if (dinfo->addrspace[i].sa_rid != rid) | ||||
continue; | continue; | ||||
*port_type = BHND_PORT_DEVICE; | *port_type = BHND_PORT_DEVICE; | ||||
*port_num = siba_addrspace_port(i); | *port_num = siba_addrspace_device_port(i); | ||||
*region_num = siba_addrspace_region(i); | *region_num = siba_addrspace_device_region(i); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Try the config blocks */ | |||||
for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { | |||||
if (dinfo->cfg[i].cb_rid != rid) | |||||
continue; | |||||
*port_type = BHND_PORT_AGENT; | |||||
*port_num = siba_cfg_agent_port(i); | |||||
*region_num = siba_cfg_agent_region(i); | |||||
return (0); | |||||
} | |||||
/* Not found */ | /* Not found */ | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
static int | static int | ||||
siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, | siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, | ||||
u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) | u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
struct siba_addrspace *addrspace; | struct siba_addrspace *addrspace; | ||||
struct siba_cfg_block *cfg; | |||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) { | if (device_get_parent(child) != dev) { | ||||
return (BHND_BUS_GET_REGION_ADDR(device_get_parent(dev), child, | return (BHND_BUS_GET_REGION_ADDR(device_get_parent(dev), child, | ||||
port_type, port_num, region_num, addr, size)); | port_type, port_num, region_num, addr, size)); | ||||
} | } | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num); | |||||
if (addrspace == NULL) | |||||
return (ENOENT); | |||||
/* Look for a matching addrspace */ | |||||
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num); | |||||
if (addrspace != NULL) { | |||||
*addr = addrspace->sa_base; | *addr = addrspace->sa_base; | ||||
*size = addrspace->sa_size - addrspace->sa_bus_reserved; | *size = addrspace->sa_size - addrspace->sa_bus_reserved; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Look for a matching cfg block */ | |||||
cfg = siba_find_cfg_block(dinfo, port_type, port_num, region_num); | |||||
if (cfg != NULL) { | |||||
*addr = cfg->cb_base; | |||||
*size = cfg->cb_size; | |||||
return (0); | |||||
} | |||||
/* Not found */ | |||||
return (ENOENT); | |||||
} | |||||
/** | /** | ||||
* Default siba(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT(). | * Default siba(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT(). | ||||
* | |||||
* This implementation consults @p child's configuration block mapping, | |||||
* returning SIBA_CORE_NUM_INTR if a valid CFG0 block is mapped. | |||||
*/ | */ | ||||
int | u_int | ||||
siba_get_intr_count(device_t dev, device_t child) | siba_get_intr_count(device_t dev, device_t child) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child)); | return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child)); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
if (!dinfo->intr_en) { | |||||
/* We can get/set interrupt sbflags on any core with a valid cfg0 | /* No interrupts */ | ||||
* block; whether the core actually makes use of it is another matter | |||||
* entirely */ | |||||
if (dinfo->cfg[0] == NULL) | |||||
return (0); | return (0); | ||||
} else { | |||||
return (SIBA_CORE_NUM_INTR); | /* One assigned interrupt */ | ||||
return (1); | |||||
} | } | ||||
} | |||||
/** | /** | ||||
* Default siba(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC(). | * Default siba(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC(). | ||||
* | |||||
* This implementation consults @p child's CFG0 register block, | |||||
* returning the interrupt flag assigned to @p child. | |||||
*/ | */ | ||||
int | int | ||||
siba_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec) | siba_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec) | ||||
{ | { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
uint32_t tpsflag; | |||||
/* delegate non-bus-attached devices to our parent */ | /* delegate non-bus-attached devices to our parent */ | ||||
if (device_get_parent(child) != dev) | if (device_get_parent(child) != dev) | ||||
return (BHND_BUS_GET_CORE_IVEC(device_get_parent(dev), child, | return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child, | ||||
intr, ivec)); | intr, ivec)); | ||||
/* Must be a valid interrupt ID */ | /* Must be a valid interrupt ID */ | ||||
if (intr >= siba_get_intr_count(dev, child)) | if (intr >= siba_get_intr_count(dev, child)) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* Fetch sbflag number */ | KASSERT(intr == 0, ("invalid ivec %u", intr)); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
tpsflag = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_TPSFLAG); | |||||
*ivec = SIBA_REG_GET(tpsflag, TPS_NUM0); | |||||
KASSERT(dinfo->intr_en, ("core does not have an interrupt assigned")); | |||||
*ivec = dinfo->intr.flag; | |||||
return (0); | return (0); | ||||
} | } | ||||
/** | /** | ||||
* Register all address space mappings for @p di. | * Register all address space mappings for @p di. | ||||
* | * | ||||
* @param dev The siba bus device. | * @param dev The siba bus device. | ||||
* @param di The device info instance on which to register all address | * @param di The device info instance on which to register all address | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | error = siba_append_dinfo_region(di, i, addr, size, | ||||
bus_reserved); | bus_reserved); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/** | /** | ||||
* Register all interrupt descriptors for @p dinfo. Must be called after | |||||
* configuration blocks have been mapped. | |||||
* | |||||
* @param dev The siba bus device. | |||||
* @param child The siba child device. | |||||
* @param dinfo The device info instance on which to register all interrupt | |||||
* descriptor entries. | |||||
* @param r A resource mapping the enumeration table block for @p di. | |||||
*/ | |||||
static int | |||||
siba_register_interrupts(device_t dev, device_t child, | |||||
struct siba_devinfo *dinfo, struct bhnd_resource *r) | |||||
{ | |||||
uint32_t tpsflag; | |||||
int error; | |||||
/* Is backplane interrupt distribution enabled for this core? */ | |||||
tpsflag = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_TPSFLAG)); | |||||
if ((tpsflag & SIBA_TPS_F0EN0) == 0) { | |||||
dinfo->intr_en = false; | |||||
return (0); | |||||
} | |||||
/* Have one interrupt */ | |||||
dinfo->intr_en = true; | |||||
dinfo->intr.flag = SIBA_REG_GET(tpsflag, TPS_NUM0); | |||||
dinfo->intr.mapped = false; | |||||
dinfo->intr.irq = 0; | |||||
dinfo->intr.rid = -1; | |||||
/* Map the interrupt */ | |||||
error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */, | |||||
&dinfo->intr.irq); | |||||
if (error) { | |||||
device_printf(dev, "failed mapping interrupt line for core %u: " | |||||
"%d\n", dinfo->core_id.core_info.core_idx, error); | |||||
return (error); | |||||
} | |||||
dinfo->intr.mapped = true; | |||||
/* Update the resource list */ | |||||
dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ, | |||||
dinfo->intr.irq, dinfo->intr.irq, 1); | |||||
return (0); | |||||
} | |||||
/** | |||||
* Map per-core configuration blocks for @p dinfo. | * Map per-core configuration blocks for @p dinfo. | ||||
* | * | ||||
* @param dev The siba bus device. | * @param dev The siba bus device. | ||||
* @param dinfo The device info instance on which to map all per-core | * @param dinfo The device info instance on which to map all per-core | ||||
* configuration blocks. | * configuration blocks. | ||||
*/ | */ | ||||
static int | static int | ||||
siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo) | siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo) | ||||
{ | { | ||||
struct siba_addrspace *addrspace; | struct siba_addrspace *addrspace; | ||||
rman_res_t r_start, r_count, r_end; | rman_res_t r_start, r_count, r_end; | ||||
uint8_t num_cfg; | uint8_t num_cfg; | ||||
int rid; | |||||
num_cfg = dinfo->core_id.num_cfg_blocks; | num_cfg = dinfo->core_id.num_cfg_blocks; | ||||
if (num_cfg > SIBA_MAX_CFG) { | if (num_cfg > SIBA_MAX_CFG) { | ||||
device_printf(dev, "config block count %hhu out of range\n", | device_printf(dev, "config block count %hhu out of range\n", | ||||
num_cfg); | num_cfg); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Fetch the core register address space */ | /* Fetch the core register address space */ | ||||
addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0); | addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0); | ||||
if (addrspace == NULL) { | if (addrspace == NULL) { | ||||
device_printf(dev, "missing device registers\n"); | device_printf(dev, "missing device registers\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* | /* | ||||
* Map the per-core configuration blocks | * Map the per-core configuration blocks | ||||
*/ | */ | ||||
for (uint8_t i = 0; i < num_cfg; i++) { | for (uint8_t i = 0; i < num_cfg; i++) { | ||||
/* Determine the config block's address range; configuration | /* Add to child's resource list */ | ||||
* blocks are allocated starting at SIBA_CFG0_OFFSET, | r_start = addrspace->sa_base + SIBA_CFG_OFFSET(i); | ||||
* growing downwards. */ | |||||
r_start = addrspace->sa_base + SIBA_CFG0_OFFSET; | |||||
r_start -= i * SIBA_CFG_SIZE; | |||||
r_count = SIBA_CFG_SIZE; | r_count = SIBA_CFG_SIZE; | ||||
r_end = r_start + r_count - 1; | r_end = r_start + r_count - 1; | ||||
/* Allocate the config resource */ | rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY, | ||||
r_start, r_end, r_count); | |||||
/* Initialize config block descriptor */ | |||||
dinfo->cfg[i] = ((struct siba_cfg_block) { | |||||
.cb_base = r_start, | |||||
.cb_size = SIBA_CFG_SIZE, | |||||
.cb_rid = rid | |||||
}); | |||||
/* Map the config resource for bus-level access */ | |||||
dinfo->cfg_rid[i] = SIBA_CFG_RID(dinfo, i); | dinfo->cfg_rid[i] = SIBA_CFG_RID(dinfo, i); | ||||
dinfo->cfg[i] = BHND_BUS_ALLOC_RESOURCE(dev, dev, | dinfo->cfg_res[i] = BHND_BUS_ALLOC_RESOURCE(dev, dev, | ||||
SYS_RES_MEMORY, &dinfo->cfg_rid[i], r_start, r_end, | SYS_RES_MEMORY, &dinfo->cfg_rid[i], r_start, r_end, | ||||
r_count, RF_ACTIVE); | r_count, RF_ACTIVE|RF_SHAREABLE); | ||||
if (dinfo->cfg[i] == NULL) { | if (dinfo->cfg_res[i] == NULL) { | ||||
device_printf(dev, "failed to allocate SIBA_CFG%hhu\n", | device_printf(dev, "failed to allocate SIBA_CFG%hhu\n", | ||||
i); | i); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
Show All 26 Lines | siba_child_deleted(device_t dev, device_t child) | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
/* Call required bhnd(4) implementation */ | /* Call required bhnd(4) implementation */ | ||||
bhnd_generic_child_deleted(dev, child); | bhnd_generic_child_deleted(dev, child); | ||||
/* Free siba device info */ | /* Free siba device info */ | ||||
if ((dinfo = device_get_ivars(child)) != NULL) | if ((dinfo = device_get_ivars(child)) != NULL) | ||||
siba_free_dinfo(dev, dinfo); | siba_free_dinfo(dev, child, dinfo); | ||||
device_set_ivars(child, NULL); | device_set_ivars(child, NULL); | ||||
} | } | ||||
/** | /** | ||||
* Scan the core table and add all valid discovered cores to | * Scan the core table and add all valid discovered cores to | ||||
* the bus. | * the bus. | ||||
* | * | ||||
Show All 24 Lines | siba_add_children(device_t dev) | ||||
* | * | ||||
* On bridged devices, we'll exhaust our available register windows if | * On bridged devices, we'll exhaust our available register windows if | ||||
* we map config blocks on unpopulated/disabled cores. To avoid this, we | * we map config blocks on unpopulated/disabled cores. To avoid this, we | ||||
* defer mapping of the per-core siba(4) config blocks until all cores | * defer mapping of the per-core siba(4) config blocks until all cores | ||||
* have been enumerated and otherwise configured. | * have been enumerated and otherwise configured. | ||||
*/ | */ | ||||
for (u_int i = 0; i < chipid->ncores; i++) { | for (u_int i = 0; i < chipid->ncores; i++) { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
device_t child; | |||||
uint32_t idhigh, idlow; | uint32_t idhigh, idlow; | ||||
rman_res_t r_count, r_end, r_start; | rman_res_t r_count, r_end, r_start; | ||||
/* Map the core's register block */ | /* Map the core's register block */ | ||||
rid = 0; | rid = 0; | ||||
r_start = SIBA_CORE_ADDR(i); | r_start = SIBA_CORE_ADDR(i); | ||||
r_count = SIBA_CORE_SIZE; | r_count = SIBA_CORE_SIZE; | ||||
r_end = r_start + SIBA_CORE_SIZE - 1; | r_end = r_start + SIBA_CORE_SIZE - 1; | ||||
Show All 16 Lines | for (u_int j = 0; j < i; j++) { | ||||
struct bhnd_core_info *prev = &cores[j].core_info; | struct bhnd_core_info *prev = &cores[j].core_info; | ||||
if (prev->vendor == cur->vendor && | if (prev->vendor == cur->vendor && | ||||
prev->device == cur->device) | prev->device == cur->device) | ||||
cur->unit++; | cur->unit++; | ||||
} | } | ||||
/* Add the child device */ | /* Add the child device */ | ||||
children[i] = BUS_ADD_CHILD(dev, 0, NULL, -1); | child = BUS_ADD_CHILD(dev, 0, NULL, -1); | ||||
if (children[i] == NULL) { | if (child == NULL) { | ||||
error = ENXIO; | error = ENXIO; | ||||
goto failed; | goto failed; | ||||
} | } | ||||
children[i] = child; | |||||
/* Initialize per-device bus info */ | /* Initialize per-device bus info */ | ||||
if ((dinfo = device_get_ivars(children[i])) == NULL) { | if ((dinfo = device_get_ivars(child)) == NULL) { | ||||
error = ENXIO; | error = ENXIO; | ||||
goto failed; | goto failed; | ||||
} | } | ||||
if ((error = siba_init_dinfo(dev, dinfo, &cores[i]))) | if ((error = siba_init_dinfo(dev, dinfo, &cores[i]))) | ||||
goto failed; | goto failed; | ||||
/* Register the core's address space(s). */ | /* Register the core's address space(s). */ | ||||
if ((error = siba_register_addrspaces(dev, dinfo, r))) | if ((error = siba_register_addrspaces(dev, dinfo, r))) | ||||
goto failed; | goto failed; | ||||
/* Register the core's interrupts */ | |||||
if ((error = siba_register_interrupts(dev, child, dinfo, r))) | |||||
goto failed; | |||||
/* Unmap the core's register block */ | /* Unmap the core's register block */ | ||||
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); | bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r); | ||||
r = NULL; | r = NULL; | ||||
/* If pins are floating or the hardware is otherwise | /* If pins are floating or the hardware is otherwise | ||||
* unpopulated, the device shouldn't be used. */ | * unpopulated, the device shouldn't be used. */ | ||||
if (bhnd_is_hw_disabled(children[i])) | if (bhnd_is_hw_disabled(child)) | ||||
device_disable(children[i]); | device_disable(child); | ||||
} | } | ||||
/* Map all valid core's config register blocks and perform interrupt | /* Map all valid core's config register blocks and perform interrupt | ||||
* assignment */ | * assignment */ | ||||
for (u_int i = 0; i < chipid->ncores; i++) { | for (u_int i = 0; i < chipid->ncores; i++) { | ||||
struct siba_devinfo *dinfo; | struct siba_devinfo *dinfo; | ||||
device_t child; | device_t child; | ||||
int nintr; | |||||
child = children[i]; | child = children[i]; | ||||
/* Skip if core is disabled */ | /* Skip if core is disabled */ | ||||
if (bhnd_is_hw_disabled(child)) | if (bhnd_is_hw_disabled(child)) | ||||
continue; | continue; | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
/* Map the core's config blocks */ | /* Map the core's config blocks */ | ||||
if ((error = siba_map_cfg_resources(dev, dinfo))) | if ((error = siba_map_cfg_resources(dev, dinfo))) | ||||
goto failed; | goto failed; | ||||
/* Assign interrupts */ | |||||
nintr = bhnd_get_intr_count(child); | |||||
for (int rid = 0; rid < nintr; rid++) { | |||||
error = BHND_BUS_ASSIGN_INTR(dev, child, rid); | |||||
if (error) { | |||||
device_printf(dev, "failed to assign interrupt " | |||||
"%d to core %u: %d\n", rid, i, error); | |||||
} | |||||
} | |||||
/* Issue bus callback for fully initialized child. */ | /* Issue bus callback for fully initialized child. */ | ||||
BHND_BUS_CHILD_ADDED(dev, child); | BHND_BUS_CHILD_ADDED(dev, child); | ||||
} | } | ||||
free(cores, M_BHND); | free(cores, M_BHND); | ||||
free(children, M_BHND); | free(children, M_BHND); | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | static device_method_t siba_methods[] = { | ||||
DEVMETHOD(bhnd_bus_read_config, siba_read_config), | DEVMETHOD(bhnd_bus_read_config, siba_read_config), | ||||
DEVMETHOD(bhnd_bus_write_config, siba_write_config), | DEVMETHOD(bhnd_bus_write_config, siba_write_config), | ||||
DEVMETHOD(bhnd_bus_get_port_count, siba_get_port_count), | DEVMETHOD(bhnd_bus_get_port_count, siba_get_port_count), | ||||
DEVMETHOD(bhnd_bus_get_region_count, siba_get_region_count), | DEVMETHOD(bhnd_bus_get_region_count, siba_get_region_count), | ||||
DEVMETHOD(bhnd_bus_get_port_rid, siba_get_port_rid), | DEVMETHOD(bhnd_bus_get_port_rid, siba_get_port_rid), | ||||
DEVMETHOD(bhnd_bus_decode_port_rid, siba_decode_port_rid), | DEVMETHOD(bhnd_bus_decode_port_rid, siba_decode_port_rid), | ||||
DEVMETHOD(bhnd_bus_get_region_addr, siba_get_region_addr), | DEVMETHOD(bhnd_bus_get_region_addr, siba_get_region_addr), | ||||
DEVMETHOD(bhnd_bus_get_intr_count, siba_get_intr_count), | DEVMETHOD(bhnd_bus_get_intr_count, siba_get_intr_count), | ||||
DEVMETHOD(bhnd_bus_get_core_ivec, siba_get_core_ivec), | DEVMETHOD(bhnd_bus_get_intr_ivec, siba_get_intr_ivec), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
DEFINE_CLASS_1(bhnd, siba_driver, siba_methods, sizeof(struct siba_softc), bhnd_driver); | DEFINE_CLASS_1(bhnd, siba_driver, siba_methods, sizeof(struct siba_softc), bhnd_driver); | ||||
MODULE_VERSION(siba, 1); | MODULE_VERSION(siba, 1); | ||||
MODULE_DEPEND(siba, bhnd, 1, 1, 1); | MODULE_DEPEND(siba, bhnd, 1, 1, 1); |