Index: head/sys/dev/bhnd/bhnd.h =================================================================== --- head/sys/dev/bhnd/bhnd.h (revision 300627) +++ head/sys/dev/bhnd/bhnd.h (revision 300628) @@ -1,1128 +1,964 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_BHND_H_ #define _BHND_BHND_H_ #include #include #include #include "bhnd_ids.h" #include "bhnd_types.h" #include "bhnd_debug.h" #include "bhnd_bus_if.h" +#include "bhnd_match.h" extern devclass_t bhnd_devclass; extern devclass_t bhnd_hostb_devclass; extern devclass_t bhnd_nvram_devclass; /** * bhnd child instance variables */ enum bhnd_device_vars { BHND_IVAR_VENDOR, /**< Designer's JEP-106 manufacturer ID. */ BHND_IVAR_DEVICE, /**< Part number */ BHND_IVAR_HWREV, /**< Core revision */ BHND_IVAR_DEVICE_CLASS, /**< Core class (@sa bhnd_devclass_t) */ BHND_IVAR_VENDOR_NAME, /**< Core vendor name */ BHND_IVAR_DEVICE_NAME, /**< Core name */ BHND_IVAR_CORE_INDEX, /**< Bus-assigned core number */ BHND_IVAR_CORE_UNIT, /**< Bus-assigned core unit number, assigned sequentially (starting at 0) for each vendor/device pair. */ }; /** * bhnd device probe priority bands. */ enum { BHND_PROBE_ROOT = 0, /**< Nexus or host bridge */ BHND_PROBE_BUS = 1000, /**< Busses and bridges */ BHND_PROBE_CPU = 2000, /**< CPU devices */ BHND_PROBE_INTERRUPT = 3000, /**< Interrupt controllers. */ BHND_PROBE_TIMER = 4000, /**< Timers and clocks. */ BHND_PROBE_RESOURCE = 5000, /**< Resource discovery (including NVRAM/SPROM) */ BHND_PROBE_DEFAULT = 6000, /**< Default device priority */ }; /** * Constants defining fine grained ordering within a BHND_PROBE_* priority band. * * Example: * @code * BHND_PROBE_BUS + BHND_PROBE_ORDER_FIRST * @endcode */ enum { BHND_PROBE_ORDER_FIRST = 0, BHND_PROBE_ORDER_EARLY = 25, BHND_PROBE_ORDER_MIDDLE = 50, BHND_PROBE_ORDER_LATE = 75, BHND_PROBE_ORDER_LAST = 100 }; /* * Simplified accessors for bhnd device ivars */ #define BHND_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(bhnd, var, BHND, ivar, type) BHND_ACCESSOR(vendor, VENDOR, uint16_t); BHND_ACCESSOR(device, DEVICE, uint16_t); BHND_ACCESSOR(hwrev, HWREV, uint8_t); BHND_ACCESSOR(class, DEVICE_CLASS, bhnd_devclass_t); BHND_ACCESSOR(vendor_name, VENDOR_NAME, const char *); BHND_ACCESSOR(device_name, DEVICE_NAME, const char *); BHND_ACCESSOR(core_index, CORE_INDEX, u_int); BHND_ACCESSOR(core_unit, CORE_UNIT, int); #undef BHND_ACCESSOR /** * A bhnd(4) board descriptor. */ struct bhnd_board_info { uint16_t board_vendor; /**< PCI-SIG vendor ID (even on non-PCI * devices). * * On PCI devices, this will generally * be the subsystem vendor ID, but the * value may be overridden in device * NVRAM. */ uint16_t board_type; /**< Board type (See BHND_BOARD_*) * * On PCI devices, this will generally * be the subsystem device ID, but the * value may be overridden in device * NVRAM. */ uint16_t board_rev; /**< Board revision. */ uint8_t board_srom_rev; /**< Board SROM format revision */ uint32_t board_flags; /**< Board flags (see BHND_BFL_*) */ uint32_t board_flags2; /**< Board flags 2 (see BHND_BFL2_*) */ uint32_t board_flags3; /**< Board flags 3 (see BHND_BFL3_*) */ }; /** * Chip Identification * * This is read from the ChipCommon ID register; on earlier bhnd(4) devices * where ChipCommon is unavailable, known values must be supplied. */ struct bhnd_chipid { uint16_t chip_id; /**< chip id (BHND_CHIPID_*) */ uint8_t chip_rev; /**< chip revision */ uint8_t chip_pkg; /**< chip package (BHND_PKGID_*) */ uint8_t chip_type; /**< chip type (BHND_CHIPTYPE_*) */ bhnd_addr_t enum_addr; /**< chip_type-specific enumeration * address; either the siba(4) base * core register block, or the bcma(4) * EROM core address. */ uint8_t ncores; /**< number of cores, if known. 0 if * not available. */ }; /** * A bhnd(4) core descriptor. */ struct bhnd_core_info { uint16_t vendor; /**< JEP-106 vendor (BHND_MFGID_*) */ uint16_t device; /**< device */ uint16_t hwrev; /**< hardware revision */ u_int core_idx; /**< bus-assigned core index */ int unit; /**< bus-assigned core unit */ }; - /** - * A hardware revision match descriptor. - */ -struct bhnd_hwrev_match { - uint16_t start; /**< first revision, or BHND_HWREV_INVALID - to match on any revision. */ - uint16_t end; /**< last revision, or BHND_HWREV_INVALID - to match on any revision. */ -}; - -/** * A bhnd(4) bus resource. * * This provides an abstract interface to per-core resources that may require * bus-level remapping of address windows prior to access. */ struct bhnd_resource { struct resource *res; /**< the system resource. */ bool direct; /**< false if the resource requires * bus window remapping before it * is MMIO accessible. */ }; -/** - * Wildcard hardware revision match descriptor. - */ -#define BHND_HWREV_ANY { BHND_HWREV_INVALID, BHND_HWREV_INVALID } -#define BHND_HWREV_IS_ANY(_m) \ - ((_m)->start == BHND_HWREV_INVALID && (_m)->end == BHND_HWREV_INVALID) - /** - * Hardware revision match descriptor for an inclusive range. - * - * @param _start The first applicable hardware revision. - * @param _end The last applicable hardware revision, or BHND_HWREV_INVALID - * to match on any revision. + * Device quirk table descriptor. */ -#define BHND_HWREV_RANGE(_start, _end) { _start, _end } - -/** - * Hardware revision match descriptor for a single revision. - * - * @param _hwrev The hardware revision to match on. - */ -#define BHND_HWREV_EQ(_hwrev) BHND_HWREV_RANGE(_hwrev, _hwrev) - -/** - * Hardware revision match descriptor for any revision equal to or greater - * than @p _start. - * - * @param _start The first hardware revision to match on. - */ -#define BHND_HWREV_GTE(_start) BHND_HWREV_RANGE(_start, BHND_HWREV_INVALID) - -/** - * Hardware revision match descriptor for any revision equal to or less - * than @p _end. - * - * @param _end The last hardware revision to match on. - */ -#define BHND_HWREV_LTE(_end) BHND_HWREV_RANGE(0, _end) - - -/** A core match descriptor. */ -struct bhnd_core_match { - uint16_t vendor; /**< required JEP106 device vendor or BHND_MFGID_INVALID. */ - uint16_t device; /**< required core ID or BHND_COREID_INVALID */ - struct bhnd_hwrev_match hwrev; /**< matching revisions. */ - bhnd_devclass_t class; /**< required class or BHND_DEVCLASS_INVALID */ - int unit; /**< required core unit, or -1 */ +struct bhnd_device_quirk { + struct bhnd_device_match desc; /**< device match descriptor */ + uint32_t quirks; /**< quirk flags */ }; -/** - * Core match descriptor matching against the given @p _vendor, @p _device, - * and @p _hwrev match descriptors. - */ -#define BHND_CORE_MATCH(_vendor, _device, _hwrev) \ - { _vendor, _device, _hwrev, BHND_DEVCLASS_INVALID, -1 } +#define BHND_CORE_QUIRK(_rev, _flags) \ + {{ BHND_MATCH_CORE_REV(_rev) }, (_flags) } -/** - * Wildcard core match descriptor. - */ -#define BHND_CORE_MATCH_ANY \ - { \ - .vendor = BHND_MFGID_INVALID, \ - .device = BHND_COREID_INVALID, \ - .hwrev = BHND_HWREV_ANY, \ - .class = BHND_DEVCLASS_INVALID, \ - .unit = -1 \ - } +#define BHND_CHIP_QUIRK(_chip, _rev, _flags) \ + {{ BHND_CHIP_IR(BCM ## _chip, _rev) }, (_flags) } -/** - * A chipset match descriptor. - * - * @warning Matching on board/nvram attributes relies on NVRAM access, and will - * fail if a valid NVRAM device cannot be found, or is not yet attached. - */ -struct bhnd_chip_match { - /** Select fields to be matched */ - uint16_t - match_id:1, - match_rev:1, - match_pkg:1, - match_bvendor:1, - match_btype:1, - match_brev:1, - match_srom_rev:1, - match_any:1, - match_flags_unused:8; +#define BHND_PKG_QUIRK(_chip, _pkg, _flags) \ + {{ BHND_CHIP_IP(BCM ## _chip, BCM ## _chip ## _pkg) }, (_flags) } - uint16_t chip_id; /**< required chip id */ - struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */ - uint8_t chip_pkg; /**< required package */ +#define BHND_BOARD_QUIRK(_board, _flags) \ + {{ BHND_MATCH_BOARD_TYPE(_board) }, \ + (_flags) } - uint16_t board_vendor; /**< required board vendor */ - uint16_t board_type; /**< required board type */ - struct bhnd_hwrev_match board_rev; /**< matching board revisions */ - - struct bhnd_hwrev_match board_srom_rev; /**< matching board srom revisions */ -}; - -#define BHND_CHIP_MATCH_ANY \ - { .match_any = 1 } - -#define BHND_CHIP_MATCH_IS_ANY(_m) \ - ((_m)->match_any == 1) - -#define BHND_CHIP_MATCH_REQ_BOARD_INFO(_m) \ - ((_m)->match_srom_rev || (_m)->match_bvendor || \ - (_m)->match_btype || (_m)->match_brev) - -/** Set the required chip ID within a bhnd_chip_match instance */ -#define BHND_CHIP_ID(_cid) \ - .match_id = 1, .chip_id = BHND_CHIPID_BCM ## _cid - -/** Set the required chip revision range within a bhnd_chip_match instance */ -#define BHND_CHIP_REV(_rev) \ - .match_rev = 1, .chip_rev = BHND_ ## _rev - -/** Set the required package ID within a bhnd_chip_match instance */ -#define BHND_CHIP_PKG(_pkg) \ - .match_pkg = 1, .chip_pkg = BHND_PKGID_BCM ## _pkg - -/** Set the required board vendor within a bhnd_chip_match instance */ -#define BHND_CHIP_BVENDOR(_vend) \ - .match_bvendor = 1, .board_vendor = _vend - -/** Set the required board type within a bhnd_chip_match instance */ -#define BHND_CHIP_BTYPE(_btype) \ - .match_btype = 1, .board_type = BHND_BOARD_ ## _btype - -/** Set the required SROM revision range within a bhnd_chip_match instance */ -#define BHND_CHIP_SROMREV(_rev) \ - .match_srom_rev = 1, .board_srom_rev = BHND_ ## _rev - -/** Set the required board revision range within a bhnd_chip_match instance */ -#define BHND_CHIP_BREV(_rev) \ - .match_brev = 1, .board_rev = BHND_ ## _rev - -/** Set the required board vendor and type within a bhnd_chip_match instance */ -#define BHND_CHIP_BVT(_vend, _type) \ - BHND_CHIP_BVENDOR(_vend), BHND_CHIP_BTYPE(_type) - -/** Set the required board vendor, type, and revision within a bhnd_chip_match - * instance */ -#define BHND_CHIP_BVTR(_vend, _type, _rev) \ - BHND_CHIP_BVT(_vend, _type), BHND_CHIP_BREV(_rev) - -/** Set the required chip and package ID within a bhnd_chip_match instance */ -#define BHND_CHIP_IP(_cid, _pkg) \ - BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg) - -/** Set the required chip ID, package ID, and revision within a bhnd_chip_match - * instance */ -#define BHND_CHIP_IPR(_cid, _pkg, _rev) \ - BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg), BHND_CHIP_REV(_rev) - -/** Set the required chip ID and revision within a bhnd_chip_match - * instance */ -#define BHND_CHIP_IR(_cid, _rev) \ - BHND_CHIP_ID(_cid), BHND_CHIP_REV(_rev) - -/** - * Chipset quirk table descriptor. - */ -struct bhnd_chip_quirk { - const struct bhnd_chip_match chip; /**< chip match descriptor */ - uint32_t quirks; /**< quirk flags */ -}; - -#define BHND_CHIP_QUIRK_END { BHND_CHIP_MATCH_ANY, 0 } - -#define BHND_CHIP_QUIRK_IS_END(_q) \ - (BHND_CHIP_MATCH_IS_ANY(&(_q)->chip) && (_q)->quirks == 0) - -/** - * Device quirk table descriptor. - */ -struct bhnd_device_quirk { - struct bhnd_hwrev_match hwrev; /**< applicable hardware revisions */ - uint32_t quirks; /**< quirk flags */ -}; -#define BHND_DEVICE_QUIRK_END { BHND_HWREV_ANY, 0 } +#define BHND_DEVICE_QUIRK_END { { BHND_MATCH_ANY }, 0 } #define BHND_DEVICE_QUIRK_IS_END(_q) \ - (BHND_HWREV_IS_ANY(&(_q)->hwrev) && (_q)->quirks == 0) + (((_q)->desc.m.match_flags == 0) && (_q)->quirks == 0) enum { BHND_DF_ANY = 0, - BHND_DF_HOSTB = (1<<0) /**< core is serving as the bus' - * host bridge */ + BHND_DF_HOSTB = (1<<0), /**< core is serving as the bus' host + * bridge. implies BHND_DF_ADAPTER */ + BHND_DF_SOC = (1<<1), /**< core is attached to a native + bus (BHND_ATTACH_NATIVE) */ + BHND_DF_ADAPTER = (1<<2), /**< core is attached to a bridged + * adapter (BHND_ATTACH_ADAPTER) */ }; /** Device probe table descriptor */ struct bhnd_device { - const struct bhnd_core_match core; /**< core match descriptor */ - const char *desc; /**< device description, or NULL. */ - const struct bhnd_device_quirk *quirks_table; /**< quirks table for this device, or NULL */ - const struct bhnd_chip_quirk *chip_quirks_table; /**< chipset-specific quirks for this device, or NULL */ - uint32_t device_flags; /**< required BHND_DF_* flags */ + const struct bhnd_device_match core; /**< core match descriptor */ + const char *desc; /**< device description, or NULL. */ + const struct bhnd_device_quirk *quirks_table; /**< quirks table for this device, or NULL */ + uint32_t device_flags; /**< required BHND_DF_* flags */ }; -#define _BHND_DEVICE(_vendor, _device, _desc, _quirks, _chip_quirks, \ - _flags, ...) \ - { BHND_CORE_MATCH(BHND_MFGID_ ## _vendor, \ - BHND_COREID_ ## _device, BHND_HWREV_ANY), _desc, _quirks, \ - _chip_quirks, _flags } +#define _BHND_DEVICE(_vendor, _device, _desc, _quirks, \ + _flags, ...) \ + { { BHND_MATCH_CORE(BHND_MFGID_ ## _vendor, \ + BHND_COREID_ ## _device) }, _desc, _quirks, \ + _flags } -#define BHND_MIPS_DEVICE(_device, _desc, _quirks, _chip_quirks, ...) \ - _BHND_DEVICE(MIPS, _device, _desc, _quirks, _chip_quirks, \ +#define BHND_MIPS_DEVICE(_device, _desc, _quirks, ...) \ + _BHND_DEVICE(MIPS, _device, _desc, _quirks, \ ## __VA_ARGS__, 0) -#define BHND_ARM_DEVICE(_device, _desc, _quirks, _chip_quirks, ...) \ - _BHND_DEVICE(ARM, _device, _desc, _quirks, _chip_quirks, \ +#define BHND_ARM_DEVICE(_device, _desc, _quirks, ...) \ + _BHND_DEVICE(ARM, _device, _desc, _quirks, \ ## __VA_ARGS__, 0) -#define BHND_DEVICE(_device, _desc, _quirks, _chip_quirks, ...) \ - _BHND_DEVICE(BCM, _device, _desc, _quirks, _chip_quirks, \ +#define BHND_DEVICE(_device, _desc, _quirks, ...) \ + _BHND_DEVICE(BCM, _device, _desc, _quirks, \ ## __VA_ARGS__, 0) -#define BHND_DEVICE_END { BHND_CORE_MATCH_ANY, NULL, NULL, NULL, 0 } +#define BHND_DEVICE_END { { BHND_MATCH_ANY }, NULL, NULL, 0 } +#define BHND_DEVICE_IS_END(_d) \ + (BHND_MATCH_IS_ANY(&(_d)->core) && (_d)->desc == NULL) const char *bhnd_vendor_name(uint16_t vendor); const char *bhnd_port_type_name(bhnd_port_type port_type); const char *bhnd_find_core_name(uint16_t vendor, uint16_t device); bhnd_devclass_t bhnd_find_core_class(uint16_t vendor, uint16_t device); const char *bhnd_core_name(const struct bhnd_core_info *ci); bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci); device_t bhnd_match_child(device_t dev, const struct bhnd_core_match *desc); device_t bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit); device_t bhnd_find_bridge_root(device_t dev, devclass_t bus_class); const struct bhnd_core_info *bhnd_match_core( const struct bhnd_core_info *cores, u_int num_cores, const struct bhnd_core_match *desc); const struct bhnd_core_info *bhnd_find_core( const struct bhnd_core_info *cores, u_int num_cores, bhnd_devclass_t class); bool bhnd_core_matches( const struct bhnd_core_info *core, const struct bhnd_core_match *desc); bool bhnd_chip_matches( const struct bhnd_chipid *chipid, - const struct bhnd_board_info *binfo, const struct bhnd_chip_match *desc); +bool bhnd_board_matches( + const struct bhnd_board_info *info, + const struct bhnd_board_match *desc); + bool bhnd_hwrev_matches(uint16_t hwrev, const struct bhnd_hwrev_match *desc); -uint32_t bhnd_chip_quirks(device_t dev, - const struct bhnd_chip_quirk *table); - bool bhnd_device_matches(device_t dev, - const struct bhnd_core_match *desc); + const struct bhnd_device_match *desc); const struct bhnd_device *bhnd_device_lookup(device_t dev, const struct bhnd_device *table, size_t entry_size); uint32_t bhnd_device_quirks(device_t dev, const struct bhnd_device *table, size_t entry_size); struct bhnd_core_info bhnd_get_core_info(device_t dev); - int bhnd_alloc_resources(device_t dev, struct resource_spec *rs, struct bhnd_resource **res); void bhnd_release_resources(device_t dev, const struct resource_spec *rs, struct bhnd_resource **res); struct bhnd_chipid bhnd_parse_chipid(uint32_t idreg, bhnd_addr_t enum_addr); int bhnd_read_chipid(device_t dev, struct resource_spec *rs, bus_size_t chipc_offset, struct bhnd_chipid *result); void bhnd_set_custom_core_desc(device_t dev, const char *name); void bhnd_set_default_core_desc(device_t dev); bool bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child); bool bhnd_bus_generic_is_region_valid(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region); int bhnd_bus_generic_read_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size); const struct bhnd_chipid *bhnd_bus_generic_get_chipid(device_t dev, device_t child); int bhnd_bus_generic_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info); int bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size); struct bhnd_resource *bhnd_bus_generic_alloc_resource (device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); int bhnd_bus_generic_release_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); int bhnd_bus_generic_activate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); int bhnd_bus_generic_deactivate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); /** * Return the active host bridge core for the bhnd bus, if any, or NULL if * not found. * * @param dev A bhnd bus device. */ static inline device_t bhnd_find_hostb_device(device_t dev) { return (BHND_BUS_FIND_HOSTB_DEVICE(dev)); } /** * Return true if the hardware components required by @p dev are known to be * unpopulated or otherwise unusable. * * In some cases, enumerated devices may have pins that are left floating, or * the hardware may otherwise be non-functional; this method allows a parent * device to explicitly specify if a successfully enumerated @p dev should * be disabled. * * @param dev A bhnd bus child device. */ static inline bool bhnd_is_hw_disabled(device_t dev) { return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), dev)); } /** * Return the BHND chip identification info for the bhnd bus. * * @param dev A bhnd bus child device. */ static inline const struct bhnd_chipid * bhnd_get_chipid(device_t dev) { return (BHND_BUS_GET_CHIPID(device_get_parent(dev), dev)); }; /** * Return the BHND attachment type of the parent bhnd bus. * * @param dev A bhnd bus child device. * * @retval BHND_ATTACH_ADAPTER if the bus is resident on a bridged adapter, * such as a WiFi chipset. * @retval BHND_ATTACH_NATIVE if the bus provides hardware services (clock, * CPU, etc) to a directly attached native host. */ static inline bhnd_attach_type bhnd_get_attach_type (device_t dev) { return (BHND_BUS_GET_ATTACH_TYPE(device_get_parent(dev), dev)); } /** * Attempt to read the BHND board identification from the bhnd bus. * * This relies on NVRAM access, and will fail if a valid NVRAM device cannot * be found, or is not yet attached. * * @param dev The parent of @p child. * @param child The bhnd device requesting board info. * @param[out] info On success, will be populated with the bhnd(4) device's * board information. * * @retval 0 success * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_read_board_info(device_t dev, struct bhnd_board_info *info) { return (BHND_BUS_READ_BOARD_INFO(device_get_parent(dev), dev, info)); } /** * Determine an NVRAM variable's expected size. * * @param dev A bhnd bus child device. * @param name The variable name. * @param[out] len On success, the variable's size, in bytes. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_nvram_getvarlen(device_t dev, const char *name, size_t *len) { return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, NULL, len)); } /** * Read an NVRAM variable. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param buf A buffer large enough to hold @p len bytes. On success, * the requested value will be written to this buffer. * @param len The required variable length. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval EINVAL If @p len does not match the actual variable size. * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_nvram_getvar(device_t dev, const char *name, void *buf, size_t len) { size_t var_len; int error; if ((error = bhnd_nvram_getvarlen(dev, name, &var_len))) return (error); if (len != var_len) return (EINVAL); return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, buf, &len)); } /** * Allocate a resource from a device's parent bhnd(4) bus. * * @param dev The device requesting resource ownership. * @param type The type of resource to allocate. This may be any type supported * by the standard bus APIs. * @param rid The bus-specific handle identifying the resource being allocated. * @param start The start address of the resource. * @param end The end address of the resource. * @param count The size of the resource. * @param flags The flags for the resource to be allocated. These may be any * values supported by the standard bus APIs. * * To request the resource's default addresses, pass @p start and * @p end values of @c 0 and @c ~0, respectively, and * a @p count of @c 1. * * @retval NULL The resource could not be allocated. * @retval resource The allocated resource. */ static inline struct bhnd_resource * bhnd_alloc_resource(device_t dev, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { return BHND_BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, rid, start, end, count, flags); } /** * Allocate a resource from a device's parent bhnd(4) bus, using the * resource's default start, end, and count values. * * @param dev The device requesting resource ownership. * @param type The type of resource to allocate. This may be any type supported * by the standard bus APIs. * @param rid The bus-specific handle identifying the resource being allocated. * @param flags The flags for the resource to be allocated. These may be any * values supported by the standard bus APIs. * * @retval NULL The resource could not be allocated. * @retval resource The allocated resource. */ static inline struct bhnd_resource * bhnd_alloc_resource_any(device_t dev, int type, int *rid, u_int flags) { return bhnd_alloc_resource(dev, type, rid, 0, ~0, 1, flags); } /** * Activate a previously allocated bhnd resource. * * @param dev The device holding ownership of the allocated resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_BUS_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_activate_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Deactivate a previously activated bhnd resource. * * @param dev The device holding ownership of the activated resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_BUS_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_deactivate_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Free a resource allocated by bhnd_alloc_resource(). * * @param dev The device holding ownership of the resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_release_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Return true if @p region_num is a valid region on @p port_num of * @p type attached to @p dev. * * @param dev A bhnd bus child device. * @param type The port type being queried. * @param port_num The port number being queried. * @param region_num The region number being queried. */ static inline bool bhnd_is_region_valid(device_t dev, bhnd_port_type type, u_int port_num, u_int region_num) { return (BHND_BUS_IS_REGION_VALID(device_get_parent(dev), dev, type, port_num, region_num)); } /** * Return the number of ports of type @p type attached to @p def. * * @param dev A bhnd bus child device. * @param type The port type being queried. */ static inline u_int bhnd_get_port_count(device_t dev, bhnd_port_type type) { return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), dev, type)); } /** * Return the number of memory regions mapped to @p child @p port of * type @p type. * * @param dev A bhnd bus child device. * @param port The port number being queried. * @param type The port type being queried. */ static inline u_int bhnd_get_region_count(device_t dev, bhnd_port_type type, u_int port) { return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), dev, type, port)); } /** * Return the resource-ID for a memory region on the given device port. * * @param dev A bhnd bus child device. * @param type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * * @retval int The RID for the given @p port and @p region on @p device. * @retval -1 No such port/region found. */ static inline int bhnd_get_port_rid(device_t dev, bhnd_port_type type, u_int port, u_int region) { return BHND_BUS_GET_PORT_RID(device_get_parent(dev), dev, type, port, region); } /** * Decode a port / region pair on @p dev defined by @p rid. * * @param dev A bhnd bus child device. * @param type The resource type. * @param rid The resource identifier. * @param[out] port_type The decoded port type. * @param[out] port The decoded port identifier. * @param[out] region The decoded region identifier. * * @retval 0 success * @retval non-zero No matching port/region found. */ static inline int bhnd_decode_port_rid(device_t dev, int type, int rid, bhnd_port_type *port_type, u_int *port, u_int *region) { return BHND_BUS_DECODE_PORT_RID(device_get_parent(dev), dev, type, rid, port_type, port, region); } /** * Get the address and size of @p region on @p port. * * @param dev A bhnd bus child device. * @param port_type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * @param[out] region_addr The region's base address. * @param[out] region_size The region's size. * * @retval 0 success * @retval non-zero No matching port/region found. */ static inline int bhnd_get_region_addr(device_t dev, bhnd_port_type port_type, u_int port, u_int region, bhnd_addr_t *region_addr, bhnd_size_t *region_size) { return BHND_BUS_GET_REGION_ADDR(device_get_parent(dev), dev, port_type, port, region, region_addr, region_size); } /* * bhnd bus-level equivalents of the bus_(read|write|set|barrier|...) * macros (compatible with bhnd_resource). * * Generated with bhnd/tools/bus_macro.sh */ #define bhnd_bus_barrier(r, o, l, f) \ ((r)->direct) ? \ bus_barrier((r)->res, (o), (l), (f)) : \ BHND_BUS_BARRIER( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (l), (f)) #define bhnd_bus_read_1(r, o) \ ((r)->direct) ? \ bus_read_1((r)->res, (o)) : \ BHND_BUS_READ_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_1(r, o, v) \ ((r)->direct) ? \ bus_write_1((r)->res, (o), (v)) : \ BHND_BUS_WRITE_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_1(r, o) \ ((r)->direct) ? \ bus_read_stream_1((r)->res, (o)) : \ BHND_BUS_READ_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_1(r, o, v) \ ((r)->direct) ? \ bus_write_stream_1((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_set_multi_1(r, o, v, c) \ ((r)->direct) ? \ bus_set_multi_1((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_set_region_1(r, o, v, c) \ ((r)->direct) ? \ bus_set_region_1((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_REGION_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_read_2(r, o) \ ((r)->direct) ? \ bus_read_2((r)->res, (o)) : \ BHND_BUS_READ_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_2(r, o, v) \ ((r)->direct) ? \ bus_write_2((r)->res, (o), (v)) : \ BHND_BUS_WRITE_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_2(r, o) \ ((r)->direct) ? \ bus_read_stream_2((r)->res, (o)) : \ BHND_BUS_READ_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_2(r, o, v) \ ((r)->direct) ? \ bus_write_stream_2((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_set_multi_2(r, o, v, c) \ ((r)->direct) ? \ bus_set_multi_2((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_set_region_2(r, o, v, c) \ ((r)->direct) ? \ bus_set_region_2((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_REGION_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_read_4(r, o) \ ((r)->direct) ? \ bus_read_4((r)->res, (o)) : \ BHND_BUS_READ_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_4(r, o, v) \ ((r)->direct) ? \ bus_write_4((r)->res, (o), (v)) : \ BHND_BUS_WRITE_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_4(r, o) \ ((r)->direct) ? \ bus_read_stream_4((r)->res, (o)) : \ BHND_BUS_READ_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_region_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_region_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_REGION_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_4(r, o, v) \ ((r)->direct) ? \ bus_write_stream_4((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_region_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_region_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_REGION_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_set_multi_4(r, o, v, c) \ ((r)->direct) ? \ bus_set_multi_4((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_set_region_4(r, o, v, c) \ ((r)->direct) ? \ bus_set_region_4((r)->res, (o), (v), (c)) : \ BHND_BUS_SET_REGION_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v), (c)) #endif /* _BHND_BHND_H_ */ Index: head/sys/dev/bhnd/bhnd_match.h =================================================================== --- head/sys/dev/bhnd/bhnd_match.h (nonexistent) +++ head/sys/dev/bhnd/bhnd_match.h (revision 300628) @@ -0,0 +1,291 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * $FreeBSD$ + */ + +#ifndef _BHND_BHND_MATCH_H_ +#define _BHND_BHND_MATCH_H_ + +#include "bhnd_types.h" + +/** + * A hardware revision match descriptor. + */ +struct bhnd_hwrev_match { + uint16_t start; /**< first revision, or BHND_HWREV_INVALID + to match on any revision. */ + uint16_t end; /**< last revision, or BHND_HWREV_INVALID + to match on any revision. */ +}; + +/* Copy match field @p _name from @p _src */ +#define _BHND_COPY_MATCH_FIELD(_src, _name) \ + .m.match._name = (_src)->m.match._name, \ + ._name = (_src)->_name + +/* Set match field @p _name with @p _value */ +#define _BHND_SET_MATCH_FIELD(_name, _value) \ + .m.match._name = 1, ._name = _value + +/** + * Wildcard hardware revision match descriptor. + */ +#define BHND_HWREV_ANY { BHND_HWREV_INVALID, BHND_HWREV_INVALID } +#define BHND_HWREV_IS_ANY(_m) \ + ((_m)->start == BHND_HWREV_INVALID && (_m)->end == BHND_HWREV_INVALID) + +/** + * Hardware revision match descriptor for an inclusive range. + * + * @param _start The first applicable hardware revision. + * @param _end The last applicable hardware revision, or BHND_HWREV_INVALID + * to match on any revision. + */ +#define BHND_HWREV_RANGE(_start, _end) { _start, _end } + +/** + * Hardware revision match descriptor for a single revision. + * + * @param _hwrev The hardware revision to match on. + */ +#define BHND_HWREV_EQ(_hwrev) BHND_HWREV_RANGE(_hwrev, _hwrev) + +/** + * Hardware revision match descriptor for any revision equal to or greater + * than @p _start. + * + * @param _start The first hardware revision to match on. + */ +#define BHND_HWREV_GTE(_start) BHND_HWREV_RANGE(_start, BHND_HWREV_INVALID) + +/** + * Hardware revision match descriptor for any revision equal to or less + * than @p _end. + * + * @param _end The last hardware revision to match on. + */ +#define BHND_HWREV_LTE(_end) BHND_HWREV_RANGE(0, _end) + +/** + * A bhnd(4) core match descriptor. + */ +struct bhnd_core_match { + /** Select fields to be matched */ + union { + uint8_t match_flags; + struct { + uint8_t + core_vendor:1, + core_id:1, + core_rev:1, + core_class:1, + core_unit:1, + flags_unused:3; + } match; + } m; + + uint16_t core_vendor; /**< required JEP106 device vendor */ + uint16_t core_id; /**< required core ID */ + struct bhnd_hwrev_match core_rev; /**< matching core revisions. */ + bhnd_devclass_t core_class; /**< required bhnd class */ + int core_unit; /**< required core unit */ +}; + +#define _BHND_CORE_MATCH_COPY(_src) \ + _BHND_COPY_MATCH_FIELD(_src, core_vendor), \ + _BHND_COPY_MATCH_FIELD(_src, core_id), \ + _BHND_COPY_MATCH_FIELD(_src, core_rev), \ + _BHND_COPY_MATCH_FIELD(_src, core_class), \ + _BHND_COPY_MATCH_FIELD(_src, core_unit) \ + +#define BHND_MATCH_CORE_VENDOR(_v) _BHND_SET_MATCH_FIELD(core_vendor, _v) +#define BHND_MATCH_CORE_ID(_id) _BHND_SET_MATCH_FIELD(core_id, _id) +#define BHND_MATCH_CORE_REV(_rev) _BHND_SET_MATCH_FIELD(core_rev, \ + BHND_ ## _rev) +#define BHND_MATCH_CORE_CLASS(_cls) _BHND_SET_MATCH_FIELD(core_class, _cls) +#define BHND_MATCH_CORE_UNIT(_unit) _BHND_SET_MATCH_FIELD(core_unit, _unit) + +/** + * Match against the given @p _vendor and @p _id, + */ +#define BHND_MATCH_CORE(_vendor, _id) \ + BHND_MATCH_CORE_VENDOR(_vendor), \ + BHND_MATCH_CORE_ID(_id) + +/** + * A bhnd(4) chip match descriptor. + */ +struct bhnd_chip_match { + /** Select fields to be matched */ + union { + uint8_t match_flags; + struct { + uint8_t + chip_id:1, + chip_rev:1, + chip_pkg:1, + flags_unused:5; + } match; + + } m; + + uint16_t chip_id; /**< required chip id */ + struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */ + uint8_t chip_pkg; /**< required package */ +}; + +#define _BHND_CHIP_MATCH_COPY(_src) \ + _BHND_COPY_MATCH_FIELD(_src, chip_id), \ + _BHND_COPY_MATCH_FIELD(_src, chip_rev), \ + _BHND_COPY_MATCH_FIELD(_src, chip_pkg) \ + +/** Set the required chip ID within a bhnd match descriptor */ +#define BHND_CHIP_ID(_cid) _BHND_SET_MATCH_FIELD(chip_id, \ + BHND_CHIPID_ ## _cid) + +/** Set the required chip revision range within a bhnd match descriptor */ +#define BHND_CHIP_REV(_rev) _BHND_SET_MATCH_FIELD(chip_rev, \ + BHND_ ## _rev) + +/** Set the required package ID within a bhnd match descriptor */ +#define BHND_CHIP_PKG(_pkg) _BHND_SET_MATCH_FIELD(chip_pkg, \ + BHND_PKGID_ ## _pkg) + +/** Set the required chip and package ID within a bhnd match descriptor */ +#define BHND_CHIP_IP(_cid, _pkg) \ + BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg) + +/** Set the required chip ID, package ID, and revision within a bhnd_device_match + * instance */ +#define BHND_CHIP_IPR(_cid, _pkg, _rev) \ + BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg), BHND_CHIP_REV(_rev) + +/** Set the required chip ID and revision within a bhnd_device_match + * instance */ +#define BHND_CHIP_IR(_cid, _rev) \ + BHND_CHIP_ID(_cid), BHND_CHIP_REV(_rev) + +/** + * A bhnd(4) board match descriptor. + */ +struct bhnd_board_match { + /** Select fields to be matched */ + union { + uint8_t match_flags; + struct { + uint8_t + board_vendor:1, + board_type:1, + board_rev:1, + board_srom_rev:1, + flags_unused:4; + } match; + } m; + + uint16_t board_vendor; /**< required board vendor */ + uint16_t board_type; /**< required board type */ + struct bhnd_hwrev_match board_rev; /**< matching board revisions */ + struct bhnd_hwrev_match board_srom_rev; /**< matching board srom revisions */ +}; + +#define _BHND_BOARD_MATCH_COPY(_src) \ + _BHND_COPY_MATCH_FIELD(_src, board_vendor), \ + _BHND_COPY_MATCH_FIELD(_src, board_type), \ + _BHND_COPY_MATCH_FIELD(_src, board_rev), \ + _BHND_COPY_MATCH_FIELD(_src, board_srom_rev) + +/** Set the required board vendor within a bhnd match descriptor */ +#define BHND_MATCH_BOARD_VENDOR(_v) _BHND_SET_MATCH_FIELD(board_vendor, _v) + +/** Set the required board type within a bhnd match descriptor */ +#define BHND_MATCH_BOARD_TYPE(_type) _BHND_SET_MATCH_FIELD(board_type, \ + BHND_BOARD_ ## _type) +/** Set the required SROM revision range within a bhnd match descriptor */ +#define BHND_MATCH_SROMREV(_rev) _BHND_SET_MATCH_FIELD(board_srom_rev, \ + BHND_HWREV_ ## _rev) + +/** Set the required board revision range within a bhnd match descriptor */ +#define BHND_MATCH_BOARD_REV(_rev) _BHND_SET_MATCH_FIELD(board_rev, \ + BHND_ ## _rev) + +/** Set the required board vendor and type within a bhnd match descriptor */ +#define BHND_MATCH_BOARD(_vend, _type) \ + BHND_MATCH_BOARD_VENDOR(_vend), BHND_MATCH_BOARD_TYPE(_type) + + +/** + * A bhnd(4) device match descriptor. + * + * @warning Matching on board attributes relies on NVRAM access, and will + * fail if a valid NVRAM device cannot be found, or is not yet attached. + */ +struct bhnd_device_match { + /** Select fields to be matched */ + union { + uint16_t match_flags; + struct { + uint16_t + core_vendor:1, + core_id:1, + core_rev:1, + core_class:1, + core_unit:1, + chip_id:1, + chip_rev:1, + chip_pkg:1, + board_vendor:1, + board_type:1, + board_rev:1, + board_srom_rev:1, + flags_unused:2; + } match; + } m; + + uint16_t core_vendor; /**< required JEP106 device vendor */ + uint16_t core_id; /**< required core ID */ + struct bhnd_hwrev_match core_rev; /**< matching core revisions. */ + bhnd_devclass_t core_class; /**< required bhnd class */ + int core_unit; /**< required core unit */ + + uint16_t chip_id; /**< required chip id */ + struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */ + uint8_t chip_pkg; /**< required package */ + + uint16_t board_vendor; /**< required board vendor */ + uint16_t board_type; /**< required board type */ + struct bhnd_hwrev_match board_rev; /**< matching board revisions */ + struct bhnd_hwrev_match board_srom_rev; /**< matching board srom revisions */ +}; + +/** Define a wildcard match requirement (matches on any device). */ +#define BHND_MATCH_ANY .m.match_flags = 0 +#define BHND_MATCH_IS_ANY(_m) \ + ((_m)->m.match_flags == 0) + +#endif /* _BHND_BHND_MATCH_H_ */ Property changes on: head/sys/dev/bhnd/bhnd_match.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/bhnd/bhnd_subr.c =================================================================== --- head/sys/dev/bhnd/bhnd_subr.c (revision 300627) +++ head/sys/dev/bhnd/bhnd_subr.c (revision 300628) @@ -1,1168 +1,1160 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvram/bhnd_nvram.h" #include "bhnd_chipc_if.h" #include "bhnd_nvram_if.h" #include "bhnd_nvram_map.h" #include "bhndreg.h" #include "bhndvar.h" static device_t find_nvram_child(device_t dev); /* BHND core device description table. */ static const struct bhnd_core_desc { uint16_t vendor; uint16_t device; bhnd_devclass_t class; const char *desc; } bhnd_core_descs[] = { #define BHND_CDESC(_mfg, _cid, _cls, _desc) \ { BHND_MFGID_ ## _mfg, BHND_COREID_ ## _cid, \ BHND_DEVCLASS_ ## _cls, _desc } BHND_CDESC(BCM, CC, CC, "ChipCommon I/O Controller"), BHND_CDESC(BCM, ILINE20, OTHER, "iLine20 HPNA"), BHND_CDESC(BCM, SRAM, RAM, "SRAM"), BHND_CDESC(BCM, SDRAM, RAM, "SDRAM"), BHND_CDESC(BCM, PCI, PCI, "PCI Bridge"), BHND_CDESC(BCM, MIPS, CPU, "MIPS Core"), BHND_CDESC(BCM, ENET, ENET_MAC, "Fast Ethernet MAC"), BHND_CDESC(BCM, CODEC, OTHER, "V.90 Modem Codec"), BHND_CDESC(BCM, USB, OTHER, "USB 1.1 Device/Host Controller"), BHND_CDESC(BCM, ADSL, OTHER, "ADSL Core"), BHND_CDESC(BCM, ILINE100, OTHER, "iLine100 HPNA"), BHND_CDESC(BCM, IPSEC, OTHER, "IPsec Accelerator"), BHND_CDESC(BCM, UTOPIA, OTHER, "UTOPIA ATM Core"), BHND_CDESC(BCM, PCMCIA, PCCARD, "PCMCIA Bridge"), BHND_CDESC(BCM, SOCRAM, RAM, "Internal Memory"), BHND_CDESC(BCM, MEMC, MEMC, "MEMC SDRAM Controller"), BHND_CDESC(BCM, OFDM, OTHER, "OFDM PHY"), BHND_CDESC(BCM, EXTIF, OTHER, "External Interface"), BHND_CDESC(BCM, D11, WLAN, "802.11 MAC/PHY/Radio"), BHND_CDESC(BCM, APHY, WLAN_PHY, "802.11a PHY"), BHND_CDESC(BCM, BPHY, WLAN_PHY, "802.11b PHY"), BHND_CDESC(BCM, GPHY, WLAN_PHY, "802.11g PHY"), BHND_CDESC(BCM, MIPS33, CPU, "MIPS 3302 Core"), BHND_CDESC(BCM, USB11H, OTHER, "USB 1.1 Host Controller"), BHND_CDESC(BCM, USB11D, OTHER, "USB 1.1 Device Core"), BHND_CDESC(BCM, USB20H, OTHER, "USB 2.0 Host Controller"), BHND_CDESC(BCM, USB20D, OTHER, "USB 2.0 Device Core"), BHND_CDESC(BCM, SDIOH, OTHER, "SDIO Host Controller"), BHND_CDESC(BCM, ROBO, OTHER, "RoboSwitch"), BHND_CDESC(BCM, ATA100, OTHER, "Parallel ATA Controller"), BHND_CDESC(BCM, SATAXOR, OTHER, "SATA DMA/XOR Controller"), BHND_CDESC(BCM, GIGETH, ENET_MAC, "Gigabit Ethernet MAC"), BHND_CDESC(BCM, PCIE, PCIE, "PCIe Bridge"), BHND_CDESC(BCM, NPHY, WLAN_PHY, "802.11n 2x2 PHY"), BHND_CDESC(BCM, SRAMC, MEMC, "SRAM Controller"), BHND_CDESC(BCM, MINIMAC, OTHER, "MINI MAC/PHY"), BHND_CDESC(BCM, ARM11, CPU, "ARM1176 CPU"), BHND_CDESC(BCM, ARM7S, CPU, "ARM7TDMI-S CPU"), BHND_CDESC(BCM, LPPHY, WLAN_PHY, "802.11a/b/g PHY"), BHND_CDESC(BCM, PMU, PMU, "PMU"), BHND_CDESC(BCM, SSNPHY, WLAN_PHY, "802.11n Single-Stream PHY"), BHND_CDESC(BCM, SDIOD, OTHER, "SDIO Device Core"), BHND_CDESC(BCM, ARMCM3, CPU, "ARM Cortex-M3 CPU"), BHND_CDESC(BCM, HTPHY, WLAN_PHY, "802.11n 4x4 PHY"), BHND_CDESC(BCM, MIPS74K, CPU, "MIPS74k CPU"), BHND_CDESC(BCM, GMAC, ENET_MAC, "Gigabit MAC core"), BHND_CDESC(BCM, DMEMC, MEMC, "DDR1/DDR2 Memory Controller"), BHND_CDESC(BCM, PCIERC, OTHER, "PCIe Root Complex"), BHND_CDESC(BCM, OCP, SOC_BRIDGE, "OCP to OCP Bridge"), BHND_CDESC(BCM, SC, OTHER, "Shared Common Core"), BHND_CDESC(BCM, AHB, SOC_BRIDGE, "OCP to AHB Bridge"), BHND_CDESC(BCM, SPIH, OTHER, "SPI Host Controller"), BHND_CDESC(BCM, I2S, OTHER, "I2S Digital Audio Interface"), BHND_CDESC(BCM, DMEMS, MEMC, "SDR/DDR1 Memory Controller"), BHND_CDESC(BCM, UBUS_SHIM, OTHER, "BCM6362/UBUS WLAN SHIM"), BHND_CDESC(BCM, PCIE2, PCIE, "PCIe Bridge (Gen2)"), BHND_CDESC(ARM, APB_BRIDGE, SOC_BRIDGE, "BP135 AMBA3 AXI to APB Bridge"), BHND_CDESC(ARM, PL301, SOC_ROUTER, "PL301 AMBA3 Interconnect"), BHND_CDESC(ARM, EROM, EROM, "PL366 Device Enumeration ROM"), BHND_CDESC(ARM, OOB_ROUTER, OTHER, "PL367 OOB Interrupt Router"), BHND_CDESC(ARM, AXI_UNMAPPED, OTHER, "Unmapped Address Ranges"), BHND_CDESC(BCM, 4706_CC, CC, "ChipCommon I/O Controller"), BHND_CDESC(BCM, NS_PCIE2, PCIE, "PCIe Bridge (Gen2)"), BHND_CDESC(BCM, NS_DMA, OTHER, "DMA engine"), BHND_CDESC(BCM, NS_SDIO, OTHER, "SDIO 3.0 Host Controller"), BHND_CDESC(BCM, NS_USB20H, OTHER, "USB 2.0 Host Controller"), BHND_CDESC(BCM, NS_USB30H, OTHER, "USB 3.0 Host Controller"), BHND_CDESC(BCM, NS_A9JTAG, OTHER, "ARM Cortex A9 JTAG Interface"), BHND_CDESC(BCM, NS_DDR23_MEMC, MEMC, "Denali DDR2/DD3 Memory Controller"), BHND_CDESC(BCM, NS_ROM, NVRAM, "System ROM"), BHND_CDESC(BCM, NS_NAND, NVRAM, "NAND Flash Controller"), BHND_CDESC(BCM, NS_QSPI, NVRAM, "QSPI Flash Controller"), BHND_CDESC(BCM, NS_CC_B, CC_B, "ChipCommon B Auxiliary I/O Controller"), BHND_CDESC(BCM, 4706_SOCRAM, RAM, "Internal Memory"), BHND_CDESC(BCM, IHOST_ARMCA9, CPU, "ARM Cortex A9 CPU"), BHND_CDESC(BCM, 4706_GMAC_CMN, ENET, "Gigabit MAC (Common)"), BHND_CDESC(BCM, 4706_GMAC, ENET_MAC, "Gigabit MAC"), BHND_CDESC(BCM, AMEMC, MEMC, "Denali DDR1/DDR2 Memory Controller"), #undef BHND_CDESC /* Derived from inspection of the BCM4331 cores that provide PrimeCell * IDs. Due to lack of documentation, the surmised device name/purpose * provided here may be incorrect. */ { BHND_MFGID_ARM, BHND_PRIMEID_EROM, BHND_DEVCLASS_OTHER, "PL364 Device Enumeration ROM" }, { BHND_MFGID_ARM, BHND_PRIMEID_SWRAP, BHND_DEVCLASS_OTHER, "PL368 Device Management Interface" }, { BHND_MFGID_ARM, BHND_PRIMEID_MWRAP, BHND_DEVCLASS_OTHER, "PL369 Device Management Interface" }, { 0, 0, 0, NULL } }; /** * Return the name for a given JEP106 manufacturer ID. * * @param vendor A JEP106 Manufacturer ID, including the non-standard ARM 4-bit * JEP106 continuation code. */ const char * bhnd_vendor_name(uint16_t vendor) { switch (vendor) { case BHND_MFGID_ARM: return "ARM"; case BHND_MFGID_BCM: return "Broadcom"; case BHND_MFGID_MIPS: return "MIPS"; default: return "unknown"; } } /** * Return the name of a port type. */ const char * bhnd_port_type_name(bhnd_port_type port_type) { switch (port_type) { case BHND_PORT_DEVICE: return ("device"); case BHND_PORT_BRIDGE: return ("bridge"); case BHND_PORT_AGENT: return ("agent"); default: return "unknown"; } } static const struct bhnd_core_desc * bhnd_find_core_desc(uint16_t vendor, uint16_t device) { for (u_int i = 0; bhnd_core_descs[i].desc != NULL; i++) { if (bhnd_core_descs[i].vendor != vendor) continue; if (bhnd_core_descs[i].device != device) continue; return (&bhnd_core_descs[i]); } return (NULL); } /** * Return a human-readable name for a BHND core. * * @param vendor The core designer's JEDEC-106 Manufacturer ID * @param device The core identifier. */ const char * bhnd_find_core_name(uint16_t vendor, uint16_t device) { const struct bhnd_core_desc *desc; if ((desc = bhnd_find_core_desc(vendor, device)) == NULL) return ("unknown"); return desc->desc; } /** * Return the device class for a BHND core. * * @param vendor The core designer's JEDEC-106 Manufacturer ID * @param device The core identifier. */ bhnd_devclass_t bhnd_find_core_class(uint16_t vendor, uint16_t device) { const struct bhnd_core_desc *desc; if ((desc = bhnd_find_core_desc(vendor, device)) == NULL) return (BHND_DEVCLASS_OTHER); return desc->class; } /** * Return a human-readable name for a BHND core. * * @param ci The core's info record. */ const char * bhnd_core_name(const struct bhnd_core_info *ci) { return bhnd_find_core_name(ci->vendor, ci->device); } /** * Return the device class for a BHND core. * * @param ci The core's info record. */ bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci) { return bhnd_find_core_class(ci->vendor, ci->device); } /** * Initialize a core info record with data from from a bhnd-attached @p dev. * * @param dev A bhnd device. * @param core The record to be initialized. */ struct bhnd_core_info bhnd_get_core_info(device_t dev) { return (struct bhnd_core_info) { .vendor = bhnd_get_vendor(dev), .device = bhnd_get_device(dev), .hwrev = bhnd_get_hwrev(dev), .core_idx = bhnd_get_core_index(dev), .unit = bhnd_get_core_unit(dev) }; } /** * Find a @p class child device with @p unit on @p dev. * * @param parent The bhnd-compatible bus to be searched. * @param class The device class to match on. * @param unit The device unit number; specify -1 to return the first match * regardless of unit number. * * @retval device_t if a matching child device is found. * @retval NULL if no matching child device is found. */ device_t bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit) { struct bhnd_core_match md = { - .vendor = BHND_MFGID_INVALID, - .device = BHND_COREID_INVALID, - .hwrev.start = BHND_HWREV_INVALID, - .hwrev.end = BHND_HWREV_INVALID, - .class = class, - .unit = unit + BHND_MATCH_CORE_CLASS(class), + BHND_MATCH_CORE_UNIT(unit) }; + if (unit == -1) + md.m.match.core_unit = 0; + return bhnd_match_child(dev, &md); } /** * Find the first child device on @p dev that matches @p desc. * * @param parent The bhnd-compatible bus to be searched. * @param desc A match descriptor. * * @retval device_t if a matching child device is found. * @retval NULL if no matching child device is found. */ device_t bhnd_match_child(device_t dev, const struct bhnd_core_match *desc) { device_t *devlistp; device_t match; int devcnt; int error; error = device_get_children(dev, &devlistp, &devcnt); if (error != 0) return (NULL); match = NULL; for (int i = 0; i < devcnt; i++) { - device_t dev = devlistp[i]; - if (bhnd_device_matches(dev, desc)) { - match = dev; + struct bhnd_core_info ci = bhnd_get_core_info(devlistp[i]); + + if (bhnd_core_matches(&ci, desc)) { + match = devlistp[i]; goto done; } } done: free(devlistp, M_TEMP); return match; } /** * Walk up the bhnd device hierarchy to locate the root device * to which the bhndb bridge is attached. * * This can be used from within bhnd host bridge drivers to locate the * actual upstream host device. * * @param dev A bhnd device. * @param bus_class The expected bus (e.g. "pci") to which the bridge root * should be attached. * * @retval device_t if a matching parent device is found. * @retval NULL @p dev is not attached via a bhndb bus * @retval NULL no parent device is attached via @p bus_class. */ device_t bhnd_find_bridge_root(device_t dev, devclass_t bus_class) { devclass_t bhndb_class; device_t parent; KASSERT(device_get_devclass(device_get_parent(dev)) == bhnd_devclass, ("%s not a bhnd device", device_get_nameunit(dev))); bhndb_class = devclass_find("bhndb"); /* Walk the device tree until we hit a bridge */ parent = dev; while ((parent = device_get_parent(parent)) != NULL) { if (device_get_devclass(parent) == bhndb_class) break; } /* No bridge? */ if (parent == NULL) return (NULL); /* Search for a parent attached to the expected bus class */ while ((parent = device_get_parent(parent)) != NULL) { device_t bus; bus = device_get_parent(parent); if (bus != NULL && device_get_devclass(bus) == bus_class) return (parent); } /* Not found */ return (NULL); } /** * Find the first core in @p cores that matches @p desc. * * @param cores The table to search. * @param num_cores The length of @p cores. * @param desc A match descriptor. * * @retval bhnd_core_info if a matching core is found. * @retval NULL if no matching core is found. */ const struct bhnd_core_info * bhnd_match_core(const struct bhnd_core_info *cores, u_int num_cores, const struct bhnd_core_match *desc) { for (u_int i = 0; i < num_cores; i++) { if (bhnd_core_matches(&cores[i], desc)) return &cores[i]; } return (NULL); } /** * Find the first core in @p cores with the given @p class. * * @param cores The table to search. * @param num_cores The length of @p cores. * @param desc A match descriptor. * * @retval bhnd_core_info if a matching core is found. * @retval NULL if no matching core is found. */ const struct bhnd_core_info * bhnd_find_core(const struct bhnd_core_info *cores, u_int num_cores, bhnd_devclass_t class) { struct bhnd_core_match md = { - .vendor = BHND_MFGID_INVALID, - .device = BHND_COREID_INVALID, - .hwrev.start = BHND_HWREV_INVALID, - .hwrev.end = BHND_HWREV_INVALID, - .class = class, - .unit = -1 + BHND_MATCH_CORE_CLASS(class) }; return bhnd_match_core(cores, num_cores, &md); } /** * Return true if the @p core matches @p desc. * * @param core A bhnd core descriptor. * @param desc A match descriptor to compare against @p core. * * @retval true if @p core matches @p match * @retval false if @p core does not match @p match. */ bool bhnd_core_matches(const struct bhnd_core_info *core, const struct bhnd_core_match *desc) { - if (desc->vendor != BHND_MFGID_INVALID && - desc->vendor != core->vendor) + if (desc->m.match.core_vendor && desc->core_vendor != core->vendor) return (false); - if (desc->device != BHND_COREID_INVALID && - desc->device != core->device) + if (desc->m.match.core_id && desc->core_id != core->device) return (false); - if (desc->unit != -1 && desc->unit != core->unit) + if (desc->m.match.core_unit && desc->core_unit != core->unit) return (false); - if (!bhnd_hwrev_matches(core->hwrev, &desc->hwrev)) + if (desc->m.match.core_rev && + !bhnd_hwrev_matches(core->hwrev, &desc->core_rev)) return (false); - if (desc->class != BHND_DEVCLASS_INVALID && - desc->class != bhnd_core_class(core)) + if (desc->m.match.core_class && + desc->core_class != bhnd_core_class(core)) return (false); return true; } /** * Return true if the @p chip matches @p desc. * * @param chip A bhnd chip identifier. - * @param board The bhnd board info, or NULL if unavailable. * @param desc A match descriptor to compare against @p chip. * * @retval true if @p chip matches @p match * @retval false if @p chip does not match @p match. */ bool bhnd_chip_matches(const struct bhnd_chipid *chip, - const struct bhnd_board_info *board, const struct bhnd_chip_match *desc) { - /* Explicit wildcard match */ - if (desc->match_any) - return (true); - - /* If board_info is missing, but required, we cannot match. */ - if (BHND_CHIP_MATCH_REQ_BOARD_INFO(desc) && board == NULL) + if (desc->m.match.chip_id && chip->chip_id != desc->chip_id) return (false); - - /* Chip matching */ - if (desc->match_id && chip->chip_id != desc->chip_id) + if (desc->m.match.chip_pkg && chip->chip_pkg != desc->chip_pkg) return (false); - if (desc->match_pkg && chip->chip_pkg != desc->chip_pkg) - return (false); - - if (desc->match_rev && + if (desc->m.match.chip_rev && !bhnd_hwrev_matches(chip->chip_rev, &desc->chip_rev)) return (false); + return (true); +} - /* Board info matching */ - if (desc->match_srom_rev && +/** + * Return true if the @p board matches @p desc. + * + * @param board The bhnd board info. + * @param desc A match descriptor to compare against @p board. + * + * @retval true if @p chip matches @p match + * @retval false if @p chip does not match @p match. + */ +bool +bhnd_board_matches(const struct bhnd_board_info *board, + const struct bhnd_board_match *desc) +{ + if (desc->m.match.board_srom_rev && !bhnd_hwrev_matches(board->board_srom_rev, &desc->board_srom_rev)) return (false); - if (desc->match_bvendor && board->board_vendor != desc->board_vendor) + if (desc->m.match.board_vendor && + board->board_vendor != desc->board_vendor) return (false); - if (desc->match_btype && board->board_type != desc->board_type) + if (desc->m.match.board_type && board->board_type != desc->board_type) return (false); - if (desc->match_brev && + if (desc->m.match.board_rev && !bhnd_hwrev_matches(board->board_rev, &desc->board_rev)) return (false); - return (true); } /** * Return true if the @p hwrev matches @p desc. * * @param hwrev A bhnd hardware revision. * @param desc A match descriptor to compare against @p core. * * @retval true if @p hwrev matches @p match * @retval false if @p hwrev does not match @p match. */ bool bhnd_hwrev_matches(uint16_t hwrev, const struct bhnd_hwrev_match *desc) { if (desc->start != BHND_HWREV_INVALID && desc->start > hwrev) return false; if (desc->end != BHND_HWREV_INVALID && desc->end < hwrev) return false; return true; } /** * Return true if the @p dev matches @p desc. * * @param dev A bhnd device. * @param desc A match descriptor to compare against @p dev. * * @retval true if @p dev matches @p match * @retval false if @p dev does not match @p match. */ bool -bhnd_device_matches(device_t dev, const struct bhnd_core_match *desc) +bhnd_device_matches(device_t dev, const struct bhnd_device_match *desc) { - struct bhnd_core_info ci = { - .vendor = bhnd_get_vendor(dev), - .device = bhnd_get_device(dev), - .unit = bhnd_get_core_unit(dev), - .hwrev = bhnd_get_hwrev(dev) - }; + struct bhnd_core_info core; + const struct bhnd_chipid *chip; + struct bhnd_board_info board; + device_t parent; + int error; - return bhnd_core_matches(&ci, desc); + /* Construct individual match descriptors */ + struct bhnd_core_match m_core = { _BHND_CORE_MATCH_COPY(desc) }; + struct bhnd_chip_match m_chip = { _BHND_CHIP_MATCH_COPY(desc) }; + struct bhnd_board_match m_board = { _BHND_BOARD_MATCH_COPY(desc) }; + + /* Fetch and match core info */ + if (m_core.m.match_flags) { + /* Only applicable to bhnd-attached cores */ + parent = device_get_parent(dev); + if (device_get_devclass(parent) != bhnd_devclass) { + device_printf(dev, "attempting to match core " + "attributes against non-core device\n"); + return (false); + } + + core = bhnd_get_core_info(dev); + if (!bhnd_core_matches(&core, &m_core)) + return (false); + } + + /* Fetch and match chip info */ + if (m_chip.m.match_flags) { + chip = bhnd_get_chipid(dev); + + if (!bhnd_chip_matches(chip, &m_chip)) + return (false); + } + + /* Fetch and match board info. + * + * This is not available until after NVRAM is up; earlier device + * matches should not include board requirements */ + if (m_board.m.match_flags) { + if ((error = bhnd_read_board_info(dev, &board))) { + device_printf(dev, "failed to read required board info " + "during device matching: %d\n", error); + return (false); + } + + if (!bhnd_board_matches(&board, &m_board)) + return (false); + } + + /* All matched */ + return (true); } /** * Search @p table for an entry matching @p dev. * * @param dev A bhnd device to match against @p table. * @param table The device table to search. * @param entry_size The @p table entry size, in bytes. * * @retval bhnd_device the first matching device, if any. * @retval NULL if no matching device is found in @p table. */ const struct bhnd_device * bhnd_device_lookup(device_t dev, const struct bhnd_device *table, size_t entry_size) { const struct bhnd_device *entry; device_t hostb, parent; + bhnd_attach_type attach_type; + uint32_t dflags; parent = device_get_parent(dev); hostb = bhnd_find_hostb_device(parent); + attach_type = bhnd_get_attach_type(dev); - for (entry = table; entry->desc != NULL; entry = + for (entry = table; !BHND_DEVICE_IS_END(entry); entry = (const struct bhnd_device *) ((const char *) entry + entry_size)) { /* match core info */ if (!bhnd_device_matches(dev, &entry->core)) continue; /* match device flags */ - if (entry->device_flags & BHND_DF_HOSTB) { + dflags = entry->device_flags; + + /* hostb implies BHND_ATTACH_ADAPTER requirement */ + if (dflags & BHND_DF_HOSTB) + dflags |= BHND_DF_ADAPTER; + + if (dflags & BHND_DF_ADAPTER) + if (attach_type != BHND_ATTACH_ADAPTER) + continue; + + if (dflags & BHND_DF_HOSTB) if (dev != hostb) continue; - } + if (dflags & BHND_DF_SOC) + if (attach_type != BHND_ATTACH_NATIVE) + continue; + /* device found */ return (entry); } /* not found */ return (NULL); } /** - * Scan @p table for all quirk flags applicable to @p dev's chip identifier - * (as returned by bhnd_get_chipid). + * Scan the device @p table for all quirk flags applicable to @p dev. * - * @param dev A bhnd device. - * @param table The chip quirk table to search. - * - * @return returns all matching quirk flags. - */ -uint32_t -bhnd_chip_quirks(device_t dev, const struct bhnd_chip_quirk *table) -{ - struct bhnd_board_info bi, *board; - const struct bhnd_chipid *cid; - const struct bhnd_chip_quirk *qent; - uint32_t quirks; - int error; - bool need_boardinfo; - - cid = bhnd_get_chipid(dev); - quirks = 0; - need_boardinfo = 0; - board = NULL; - - /* Determine whether quirk matching requires board_info; we want to - * avoid fetching board_info for early devices (e.g. ChipCommon) - * that are brought up prior to NVRAM being readable. */ - for (qent = table; !BHND_CHIP_QUIRK_IS_END(qent); qent++) { - if (!BHND_CHIP_MATCH_REQ_BOARD_INFO(&qent->chip)) - continue; - - need_boardinfo = true; - break; - } - - /* If required, fetch board info */ - if (need_boardinfo) { - error = bhnd_read_board_info(dev, &bi); - if (!error) { - board = &bi; - } else { - device_printf(dev, "failed to read required board info " - "during quirk matching: %d\n", error); - } - } - - /* Apply all matching quirk flags */ - for (qent = table; !BHND_CHIP_QUIRK_IS_END(qent); qent++) { - if (bhnd_chip_matches(cid, board, &qent->chip)) - quirks |= qent->quirks; - } - - return (quirks); -} - -/** - * Scan @p table for all quirk flags applicable to @p dev. - * * @param dev A bhnd device to match against @p table. * @param table The device table to search. - * @param entry_size The @p table entry size, in bytes. * * @return returns all matching quirk flags. */ uint32_t bhnd_device_quirks(device_t dev, const struct bhnd_device *table, size_t entry_size) { const struct bhnd_device *dent; - const struct bhnd_device_quirk *qtable, *qent; + const struct bhnd_device_quirk *qent, *qtable; uint32_t quirks; - uint16_t hwrev; - hwrev = bhnd_get_hwrev(dev); - quirks = 0; + /* Locate the device entry */ + if ((dent = bhnd_device_lookup(dev, table, entry_size)) == NULL) + return (0); - /* Find the quirk table */ - if ((dent = bhnd_device_lookup(dev, table, entry_size)) == NULL) { - /* This is almost certainly a (caller) implementation bug */ - device_printf(dev, "quirk lookup did not match any device\n"); + /* Quirks table is optional */ + qtable = dent->quirks_table; + if (qtable == NULL) return (0); - } /* Collect matching device quirk entries */ - if ((qtable = dent->quirks_table) != NULL) { - for (qent = qtable; !BHND_DEVICE_QUIRK_IS_END(qent); qent++) { - if (bhnd_hwrev_matches(hwrev, &qent->hwrev)) - quirks |= qent->quirks; - } + quirks = 0; + for (qent = qtable; !BHND_DEVICE_QUIRK_IS_END(qent); qent++) { + if (bhnd_device_matches(dev, &qent->desc)) + quirks |= qent->quirks; } - - /* Collect matching chip quirk entries */ - if (dent->chip_quirks_table != NULL) - quirks |= bhnd_chip_quirks(dev, dent->chip_quirks_table); return (quirks); } /** * Allocate bhnd(4) resources defined in @p rs from a parent bus. * * @param dev The device requesting ownership of the resources. * @param rs A standard bus resource specification. This will be updated * with the allocated resource's RIDs. * @param res On success, the allocated bhnd resources. * * @retval 0 success * @retval non-zero if allocation of any non-RF_OPTIONAL resource fails, * all allocated resources will be released and a regular * unix error code will be returned. */ int bhnd_alloc_resources(device_t dev, struct resource_spec *rs, struct bhnd_resource **res) { /* Initialize output array */ for (u_int i = 0; rs[i].type != -1; i++) res[i] = NULL; for (u_int i = 0; rs[i].type != -1; i++) { res[i] = bhnd_alloc_resource_any(dev, rs[i].type, &rs[i].rid, rs[i].flags); /* Clean up all allocations on failure */ if (res[i] == NULL && !(rs[i].flags & RF_OPTIONAL)) { bhnd_release_resources(dev, rs, res); return (ENXIO); } } return (0); }; /** * Release bhnd(4) resources defined in @p rs from a parent bus. * * @param dev The device that owns the resources. * @param rs A standard bus resource specification previously initialized * by @p bhnd_alloc_resources. * @param res The bhnd resources to be released. */ void bhnd_release_resources(device_t dev, const struct resource_spec *rs, struct bhnd_resource **res) { for (u_int i = 0; rs[i].type != -1; i++) { if (res[i] == NULL) continue; bhnd_release_resource(dev, rs[i].type, rs[i].rid, res[i]); res[i] = NULL; } } /** * Parse the CHIPC_ID_* fields from the ChipCommon CHIPC_ID * register, returning its bhnd_chipid representation. * * @param idreg The CHIPC_ID register value. * @param enum_addr The enumeration address to include in the result. * * @warning * On early siba(4) devices, the ChipCommon core does not provide * a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions * (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return * an invalid `ncores` value. */ struct bhnd_chipid bhnd_parse_chipid(uint32_t idreg, bhnd_addr_t enum_addr) { struct bhnd_chipid result; /* Fetch the basic chip info */ result.chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP); result.chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG); result.chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV); result.chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS); result.ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE); result.enum_addr = enum_addr; return (result); } /** * Allocate the resource defined by @p rs via @p dev, use it * to read the ChipCommon ID register relative to @p chipc_offset, * then release the resource. * * @param dev The device owning @p rs. * @param rs A resource spec that encompasses the ChipCommon register block. * @param chipc_offset The offset of the ChipCommon registers within @p rs. * @param[out] result the chip identification data. * * @retval 0 success * @retval non-zero if the ChipCommon identification data could not be read. */ int bhnd_read_chipid(device_t dev, struct resource_spec *rs, bus_size_t chipc_offset, struct bhnd_chipid *result) { struct resource *res; uint32_t reg; int error, rid, rtype; /* Allocate the ChipCommon window resource and fetch the chipid data */ rid = rs->rid; rtype = rs->type; res = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE); if (res == NULL) { device_printf(dev, "failed to allocate bhnd chipc resource\n"); return (ENXIO); } /* Fetch the basic chip info */ reg = bus_read_4(res, chipc_offset + CHIPC_ID); *result = bhnd_parse_chipid(reg, 0x0); /* Fetch the enum base address */ error = 0; switch (result->chip_type) { case BHND_CHIPTYPE_SIBA: result->enum_addr = BHND_DEFAULT_CHIPC_ADDR; break; case BHND_CHIPTYPE_BCMA: case BHND_CHIPTYPE_BCMA_ALT: result->enum_addr = bus_read_4(res, chipc_offset + CHIPC_EROMPTR); break; case BHND_CHIPTYPE_UBUS: device_printf(dev, "unsupported ubus/bcm63xx chip type"); error = ENODEV; goto cleanup; default: device_printf(dev, "unknown chip type %hhu\n", result->chip_type); error = ENODEV; goto cleanup; } cleanup: /* Clean up */ bus_release_resource(dev, rtype, rid, res); return (error); } /** * Using the bhnd(4) bus-level core information and a custom core name, * populate @p dev's device description. * * @param dev A bhnd-bus attached device. * @param dev_name The core's name (e.g. "SDIO Device Core") */ void bhnd_set_custom_core_desc(device_t dev, const char *dev_name) { const char *vendor_name; char *desc; vendor_name = bhnd_get_vendor_name(dev); asprintf(&desc, M_BHND, "%s %s, rev %hhu", vendor_name, dev_name, bhnd_get_hwrev(dev)); if (desc != NULL) { device_set_desc_copy(dev, desc); free(desc, M_BHND); } else { device_set_desc(dev, dev_name); } } /** * Using the bhnd(4) bus-level core information, populate @p dev's device * description. * * @param dev A bhnd-bus attached device. */ void bhnd_set_default_core_desc(device_t dev) { bhnd_set_custom_core_desc(dev, bhnd_get_device_name(dev)); } /** * Helper function for implementing BHND_BUS_IS_HW_DISABLED(). * * If a parent device is available, this implementation delegates the * request to the BHND_BUS_IS_HW_DISABLED() method on the parent of @p dev. * * If no parent device is available (i.e. on a the bus root), the hardware * is assumed to be usable and false is returned. */ bool bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child) { if (device_get_parent(dev) != NULL) return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); return (false); } /** * Helper function for implementing BHND_BUS_GET_CHIPID(). * * This implementation delegates the request to the BHND_BUS_GET_CHIPID() * method on the parent of @p dev. If no parent exists, the implementation * will panic. */ const struct bhnd_chipid * bhnd_bus_generic_get_chipid(device_t dev, device_t child) { if (device_get_parent(dev) != NULL) return (BHND_BUS_GET_CHIPID(device_get_parent(dev), child)); panic("missing BHND_BUS_GET_CHIPID()"); } /* nvram board_info population macros for bhnd_bus_generic_read_board_info() */ #define BHND_GV(_dest, _name) \ bhnd_nvram_getvar(child, BHND_NVAR_ ## _name, &_dest, sizeof(_dest)) #define REQ_BHND_GV(_dest, _name) do { \ if ((error = BHND_GV(_dest, _name))) { \ device_printf(dev, \ "error reading " __STRING(_name) ": %d\n", error); \ return (error); \ } \ } while(0) #define OPT_BHND_GV(_dest, _name, _default) do { \ if ((error = BHND_GV(_dest, _name))) { \ if (error != ENOENT) { \ device_printf(dev, \ "error reading " \ __STRING(_name) ": %d\n", error); \ return (error); \ } \ _dest = _default; \ } \ } while(0) /** * Helper function for implementing BHND_BUS_READ_BOARDINFO(). * * This implementation populates @p info with information from NVRAM, * defaulting board_vendor and board_type fields to 0 if the * requested variables cannot be found. * * This behavior is correct for most SoCs, but must be overridden on * bridged (PCI, PCMCIA, etc) devices to produce a complete bhnd_board_info * result. */ int bhnd_bus_generic_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { int error; OPT_BHND_GV(info->board_vendor, BOARDVENDOR, 0); OPT_BHND_GV(info->board_type, BOARDTYPE, 0); /* srom >= 2 */ REQ_BHND_GV(info->board_rev, BOARDREV); REQ_BHND_GV(info->board_srom_rev,SROMREV); REQ_BHND_GV(info->board_flags, BOARDFLAGS); OPT_BHND_GV(info->board_flags2, BOARDFLAGS2, 0); /* srom >= 4 */ OPT_BHND_GV(info->board_flags3, BOARDFLAGS3, 0); /* srom >= 11 */ return (0); } #undef BHND_GV #undef BHND_GV_REQ #undef BHND_GV_OPT /** * Find an NVRAM child device on @p dev, if any. * * @retval device_t An NVRAM device. * @retval NULL If no NVRAM device is found. */ static device_t find_nvram_child(device_t dev) { device_t chipc, nvram; /* Look for a directly-attached NVRAM child */ nvram = device_find_child(dev, "bhnd_nvram", 0); if (nvram != NULL) return (nvram); /* Remaining checks are only applicable when searching a bhnd(4) * bus. */ if (device_get_devclass(dev) != bhnd_devclass) return (NULL); /* Look for a ChipCommon-attached NVRAM device */ if ((chipc = bhnd_find_child(dev, BHND_DEVCLASS_CC, -1)) != NULL) { nvram = device_find_child(chipc, "bhnd_nvram", 0); if (nvram != NULL) return (nvram); } /* Not found */ return (NULL); } /** * Helper function for implementing BHND_BUS_GET_NVRAM_VAR(). * * This implementation searches @p dev for a usable NVRAM child device: * - The first child device implementing the bhnd_nvram devclass is * returned, otherwise * - If @p dev is a bhnd(4) bus, a ChipCommon core that advertises an * attached NVRAM source. * * If no usable child device is found on @p dev, the request is delegated to * the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev. */ int bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size) { device_t nvram; device_t parent; /* Try to find an NVRAM device applicable to @p child */ if ((nvram = find_nvram_child(dev)) != NULL) return BHND_NVRAM_GETVAR(nvram, name, buf, size); /* Try to delegate to parent */ if ((parent = device_get_parent(dev)) == NULL) return (ENODEV); return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), child, name, buf, size)); } /** * Helper function for implementing BHND_BUS_ALLOC_RESOURCE(). * * This implementation of BHND_BUS_ALLOC_RESOURCE() delegates allocation * of the underlying resource to BUS_ALLOC_RESOURCE(), and activation * to @p dev's BHND_BUS_ACTIVATE_RESOURCE(). */ struct bhnd_resource * bhnd_bus_generic_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct bhnd_resource *br; struct resource *res; int error; br = NULL; res = NULL; /* Allocate the real bus resource (without activating it) */ res = BUS_ALLOC_RESOURCE(dev, child, type, rid, start, end, count, (flags & ~RF_ACTIVE)); if (res == NULL) return (NULL); /* Allocate our bhnd resource wrapper. */ br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT); if (br == NULL) goto failed; br->direct = false; br->res = res; /* Attempt activation */ if (flags & RF_ACTIVE) { error = BHND_BUS_ACTIVATE_RESOURCE(dev, child, type, *rid, br); if (error) goto failed; } return (br); failed: if (res != NULL) BUS_RELEASE_RESOURCE(dev, child, type, *rid, res); free(br, M_BHND); return (NULL); } /** * Helper function for implementing BHND_BUS_RELEASE_RESOURCE(). * * This implementation of BHND_BUS_RELEASE_RESOURCE() delegates release of * the backing resource to BUS_RELEASE_RESOURCE(). */ int bhnd_bus_generic_release_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; if ((error = BUS_RELEASE_RESOURCE(dev, child, type, rid, r->res))) return (error); free(r, M_BHND); return (0); } /** * Helper function for implementing BHND_BUS_ACTIVATE_RESOURCE(). * * This implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev. */ int bhnd_bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { /* Try to delegate to the parent */ if (device_get_parent(dev) != NULL) return (BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev), child, type, rid, r)); return (EINVAL); }; /** * Helper function for implementing BHND_BUS_DEACTIVATE_RESOURCE(). * * This implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev. */ int bhnd_bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { if (device_get_parent(dev) != NULL) return (BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), child, type, rid, r)); return (EINVAL); }; Index: head/sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c (revision 300627) +++ head/sys/dev/bhnd/bhndb/bhndb.c (revision 300628) @@ -1,2017 +1,2016 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Abstract BHND Bridge Device Driver * * Provides generic support for bridging from a parent bus (such as PCI) to * a BHND-compatible bus (e.g. bcma or siba). */ #include #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_chipc_if.h" #include "bhnd_nvram_if.h" #include "bhndbvar.h" #include "bhndb_bus_if.h" #include "bhndb_hwdata.h" #include "bhndb_private.h" /* Debugging flags */ static u_long bhndb_debug = 0; TUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug); enum { BHNDB_DEBUG_PRIO = 1 << 0, }; #define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug) static bool bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw); static int bhndb_initialize_region_cfg( struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw_priority *table, struct bhndb_resources *r); static int bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw **hw); static int bhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, struct bhnd_chipid *result); bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, device_t child); static struct rman *bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type); static int bhndb_init_child_resource(struct resource *r, struct resource *parent, bhnd_size_t offset, bhnd_size_t size); static int bhndb_activate_static_region( struct bhndb_softc *sc, struct bhndb_region *region, device_t child, int type, int rid, struct resource *r); static int bhndb_try_activate_resource( struct bhndb_softc *sc, device_t child, int type, int rid, struct resource *r, bool *indirect); /** * Default bhndb(4) implementation of DEVICE_PROBE(). * * This function provides the default bhndb implementation of DEVICE_PROBE(), * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge(). */ int bhndb_generic_probe(device_t dev) { return (BUS_PROBE_NOWILDCARD); } static void bhndb_probe_nomatch(device_t dev, device_t child) { const char *name; name = device_get_name(child); if (name == NULL) name = "unknown device"; device_printf(dev, "<%s> (no driver attached)\n", name); } static int bhndb_print_child(device_t dev, device_t child) { struct bhndb_softc *sc; struct resource_list *rl; int retval = 0; sc = device_get_softc(dev); retval += bus_print_child_header(dev, child); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); } retval += bus_print_child_domain(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } static int bhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static int bhndb_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { struct bhndb_softc *sc; sc = device_get_softc(dev); snprintf(buf, buflen, "base=0x%llx", (unsigned long long) sc->chipid.enum_addr); return (0); } /** * Return true if @p devlist matches the @p hw specification. * * @param devlist A device table to match against. * @param num_devs The number of devices in @p devlist. * @param hw The hardware description to be matched against. */ static bool bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw) { for (u_int i = 0; i < hw->num_hw_reqs; i++) { const struct bhnd_core_match *match; + struct bhnd_core_info ci; bool found; match = &hw->hw_reqs[i]; found = false; for (int d = 0; d < num_devs; d++) { - if (!bhnd_device_matches(devlist[d], match)) + ci = bhnd_get_core_info(devlist[d]); + if (!bhnd_core_matches(&ci, match)) continue; found = true; break; } if (!found) return (false); } return (true); } /** * Initialize the region maps and priority configuration in @p r using * the provided priority @p table and the set of devices attached to * the bridged @p bus_dev . * * @param sc The bhndb device state. * @param devs All devices enumerated on the bridged bhnd bus. * @param ndevs The length of @p devs. * @param table Hardware priority table to be used to determine the relative * priorities of per-core port resources. * @param r The resource state to be configured. */ static int bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw_priority *table, struct bhndb_resources *r) { const struct bhndb_hw_priority *hp; bhnd_addr_t addr; bhnd_size_t size; size_t prio_low, prio_default, prio_high; int error; /* The number of port regions per priority band that must be accessible * via dynamic register windows */ prio_low = 0; prio_default = 0; prio_high = 0; /* * Register bridge regions covering all statically mapped ports. */ for (int i = 0; i < ndevs; i++) { const struct bhndb_regwin *regw; device_t child; child = devs[i]; for (regw = r->cfg->register_windows; regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) { /* Only core windows are supported */ if (regw->win_type != BHNDB_REGWIN_T_CORE) continue; /* Skip non-applicable register windows. */ if (!bhndb_regwin_matches_device(regw, child)) continue; /* Fetch the base address of the mapped port. */ error = bhnd_get_region_addr(child, regw->d.core.port_type, regw->d.core.port, regw->d.core.region, &addr, &size); if (error) return (error); /* * Always defer to the register window's size. * * If the port size is smaller than the window size, * this ensures that we fully utilize register windows * larger than the referenced port. * * If the port size is larger than the window size, this * ensures that we do not directly map the allocations * within the region to a too-small window. */ size = regw->win_size; /* * Add to the bus region list. * * The window priority for a statically mapped * region is always HIGH. */ error = bhndb_add_resource_region(r, addr, size, BHNDB_PRIORITY_HIGH, regw); if (error) return (error); } } /* * Perform priority accounting and register bridge regions for all * ports defined in the priority table */ for (int i = 0; i < ndevs; i++) { struct bhndb_region *region; device_t child; child = devs[i]; /* * Skip priority accounting for cores that ... */ /* ... do not require bridge resources */ if (bhnd_is_hw_disabled(child) || !device_is_enabled(child)) continue; /* ... do not have a priority table entry */ hp = bhndb_hw_priority_find_device(table, child); if (hp == NULL) continue; /* ... are explicitly disabled in the priority table. */ if (hp->priority == BHNDB_PRIORITY_NONE) continue; /* Determine the number of dynamic windows required and * register their bus_region entries. */ for (u_int i = 0; i < hp->num_ports; i++) { const struct bhndb_port_priority *pp; pp = &hp->ports[i]; /* Skip ports not defined on this device */ if (!bhnd_is_region_valid(child, pp->type, pp->port, pp->region)) { continue; } /* Fetch the address+size of the mapped port. */ error = bhnd_get_region_addr(child, pp->type, pp->port, pp->region, &addr, &size); if (error) return (error); /* Skip ports with an existing static mapping */ region = bhndb_find_resource_region(r, addr, size); if (region != NULL && region->static_regwin != NULL) continue; /* Define a dynamic region for this port */ error = bhndb_add_resource_region(r, addr, size, pp->priority, NULL); if (error) return (error); /* Update port mapping counts */ switch (pp->priority) { case BHNDB_PRIORITY_NONE: break; case BHNDB_PRIORITY_LOW: prio_low++; break; case BHNDB_PRIORITY_DEFAULT: prio_default++; break; case BHNDB_PRIORITY_HIGH: prio_high++; break; } } } /* Determine the minimum priority at which we'll allocate direct * register windows from our dynamic pool */ size_t prio_total = prio_low + prio_default + prio_high; if (prio_total <= r->dwa_count) { /* low+default+high priority regions get windows */ r->min_prio = BHNDB_PRIORITY_LOW; } else if (prio_default + prio_high <= r->dwa_count) { /* default+high priority regions get windows */ r->min_prio = BHNDB_PRIORITY_DEFAULT; } else { /* high priority regions get windows */ r->min_prio = BHNDB_PRIORITY_HIGH; } if (BHNDB_DEBUG(PRIO)) { struct bhndb_region *region; const char *direct_msg, *type_msg; bhndb_priority_t prio, prio_min; prio_min = r->min_prio; device_printf(sc->dev, "min_prio: %d\n", prio_min); STAILQ_FOREACH(region, &r->bus_regions, link) { prio = region->priority; direct_msg = prio >= prio_min ? "direct" : "indirect"; type_msg = region->static_regwin ? "static" : "dynamic"; device_printf(sc->dev, "region 0x%llx+0x%llx priority " "%u %s/%s\n", (unsigned long long) region->addr, (unsigned long long) region->size, region->priority, direct_msg, type_msg); } } return (0); } /** * Find a hardware specification for @p dev. * * @param sc The bhndb device state. * @param devs All devices enumerated on the bridged bhnd bus. * @param ndevs The length of @p devs. * @param[out] hw On success, the matched hardware specification. * with @p dev. * * @retval 0 success * @retval non-zero if an error occurs fetching device info for comparison. */ static int bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw **hw) { const struct bhndb_hw *next, *hw_table; /* Search for the first matching hardware config. */ hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev); for (next = hw_table; next->hw_reqs != NULL; next++) { if (!bhndb_hw_matches(devs, ndevs, next)) continue; /* Found */ *hw = next; return (0); } return (ENOENT); } /** * Read the ChipCommon identification data for this device. * * @param sc bhndb device state. * @param cfg The hardware configuration to use when mapping the ChipCommon * registers. * @param[out] result the chip identification data. * * @retval 0 success * @retval non-zero if the ChipCommon identification data could not be read. */ static int bhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, struct bhnd_chipid *result) { const struct bhnd_chipid *parent_cid; const struct bhndb_regwin *cc_win; struct resource_spec rs; int error; /* Let our parent device override the discovery process */ parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); if (parent_cid != NULL) { *result = *parent_cid; return (0); } /* Find a register window we can use to map the first CHIPC_CHIPID_SIZE * of ChipCommon registers. */ cc_win = bhndb_regwin_find_best(cfg->register_windows, BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE); if (cc_win == NULL) { device_printf(sc->dev, "no chipcommon register window\n"); return (0); } /* We can assume a device without a static ChipCommon window uses the * default ChipCommon address. */ if (cc_win->win_type == BHNDB_REGWIN_T_DYN) { error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win, BHND_DEFAULT_CHIPC_ADDR); if (error) { device_printf(sc->dev, "failed to set chipcommon " "register window\n"); return (error); } } /* Let the default bhnd implemenation alloc/release the resource and * perform the read */ rs.type = cc_win->res.type; rs.rid = cc_win->res.rid; rs.flags = RF_ACTIVE; return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset, result)); } /** * Helper function that must be called by subclass bhndb(4) drivers * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4) * APIs on the bridge device. * * @param dev The bridge device to attach. * @param bridge_devclass The device class of the bridging core. This is used * to automatically detect the bridge core, and to disable additional bridge * cores (e.g. PCMCIA on a PCIe device). */ int bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass) { struct bhndb_devinfo *dinfo; struct bhndb_softc *sc; const struct bhndb_hwcfg *cfg; int error; sc = device_get_softc(dev); sc->dev = dev; sc->parent_dev = device_get_parent(dev); sc->bridge_class = bridge_devclass; BHNDB_LOCK_INIT(sc); /* Read our chip identification data */ cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid))) return (error); /* Populate generic resource allocation state. */ sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); if (sc->bus_res == NULL) { return (ENXIO); } /* Attach our bridged bus device */ sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1); if (sc->bus_dev == NULL) { error = ENXIO; goto failed; } /* Configure address space */ dinfo = device_get_ivars(sc->bus_dev); dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED; /* Finish attach */ return (bus_generic_attach(dev)); failed: BHNDB_LOCK_DESTROY(sc); if (sc->bus_res != NULL) bhndb_free_resources(sc->bus_res); return (error); } /** * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG(). * * This function provides the default bhndb implementation of * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver * overriding BHNDB_INIT_FULL_CONFIG(). * * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final * bridge configuration based on the hardware information enumerated by the * child bus, and will reset all resource allocation state on the bridge. * * When calling this method: * - Any bus resources previously allocated by @p child must be deallocated. * - The @p child bus must have performed initial enumeration -- but not * probe or attachment -- of its children. */ int bhndb_generic_init_full_config(device_t dev, device_t child, const struct bhndb_hw_priority *hw_prio_table) { struct bhndb_softc *sc; const struct bhndb_hw *hw; struct bhndb_resources *r; device_t *devs; device_t hostb; int ndevs; int error; sc = device_get_softc(dev); hostb = NULL; /* Fetch the full set of bhnd-attached cores */ if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) return (error); /* Find our host bridge device */ hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child); if (hostb == NULL) { device_printf(sc->dev, "no host bridge core found\n"); error = ENODEV; goto cleanup; } /* Find our full register window configuration */ if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) { device_printf(sc->dev, "unable to identify device, " " using generic bridge resource definitions\n"); error = 0; goto cleanup; } if (bootverbose || BHNDB_DEBUG(PRIO)) device_printf(sc->dev, "%s resource configuration\n", hw->name); /* Release existing resource state */ BHNDB_LOCK(sc); bhndb_free_resources(sc->bus_res); sc->bus_res = NULL; BHNDB_UNLOCK(sc); /* Allocate new resource state */ r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg); if (r == NULL) { error = ENXIO; goto cleanup; } /* Initialize our resource priority configuration */ error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r); if (error) { bhndb_free_resources(r); goto cleanup; } /* Update our bridge state */ BHNDB_LOCK(sc); sc->bus_res = r; sc->hostb_dev = hostb; BHNDB_UNLOCK(sc); cleanup: free(devs, M_TEMP); return (error); } /** * Default bhndb(4) implementation of DEVICE_DETACH(). * * This function detaches any child devices, and if successful, releases all * resources held by the bridge device. */ int bhndb_generic_detach(device_t dev) { struct bhndb_softc *sc; int error; sc = device_get_softc(dev); /* Detach children */ if ((error = bus_generic_detach(dev))) return (error); /* Clean up our driver state. */ bhndb_free_resources(sc->bus_res); BHNDB_LOCK_DESTROY(sc); return (0); } /** * Default bhndb(4) implementation of DEVICE_SUSPEND(). * * This function calls bus_generic_suspend() (or implements equivalent * behavior). */ int bhndb_generic_suspend(device_t dev) { return (bus_generic_suspend(dev)); } /** * Default bhndb(4) implementation of DEVICE_RESUME(). * * This function calls bus_generic_resume() (or implements equivalent * behavior). */ int bhndb_generic_resume(device_t dev) { struct bhndb_softc *sc; struct bhndb_resources *bus_res; struct bhndb_dw_alloc *dwa; int error; sc = device_get_softc(dev); bus_res = sc->bus_res; /* Guarantee that all in-use dynamic register windows are mapped to * their previously configured target address. */ BHNDB_LOCK(sc); for (size_t i = 0; i < bus_res->dwa_count; i++) { dwa = &bus_res->dw_alloc[i]; /* Skip regions that were not previously used */ if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0) continue; /* Otherwise, ensure the register window is correct before * any children attempt MMIO */ error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); if (error) break; } BHNDB_UNLOCK(sc); /* Error restoring hardware state; children cannot be safely resumed */ if (error) { device_printf(dev, "Unable to restore hardware configuration; " "cannot resume: %d\n", error); return (error); } return (bus_generic_resume(dev)); } /** * Default implementation of BHNDB_SUSPEND_RESOURCE. */ static void bhndb_suspend_resource(device_t dev, device_t child, int type, struct resource *r) { struct bhndb_softc *sc; struct bhndb_dw_alloc *dwa; sc = device_get_softc(dev); // TODO: IRQs? if (type != SYS_RES_MEMORY) return; BHNDB_LOCK(sc); dwa = bhndb_dw_find_resource(sc->bus_res, r); if (dwa == NULL) { BHNDB_UNLOCK(sc); return; } if (BHNDB_DEBUG(PRIO)) device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n", type, rman_get_start(r), rman_get_size(r)); /* Release the resource's window reference */ bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); } /** * Default implementation of BHNDB_RESUME_RESOURCE. */ static int bhndb_resume_resource(device_t dev, device_t child, int type, struct resource *r) { struct bhndb_softc *sc; sc = device_get_softc(dev); // TODO: IRQs? if (type != SYS_RES_MEMORY) return (0); /* Inactive resources don't require reallocation of bridge resources */ if (!(rman_get_flags(r) & RF_ACTIVE)) return (0); if (BHNDB_DEBUG(PRIO)) device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n", type, rman_get_start(r), rman_get_size(r)); return (bhndb_try_activate_resource(sc, rman_get_device(r), type, rman_get_rid(r), r, NULL)); } /** * Default bhndb(4) implementation of BUS_READ_IVAR(). */ static int bhndb_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { return (ENOENT); } /** * Default bhndb(4) implementation of BUS_WRITE_IVAR(). */ static int bhndb_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { return (ENOENT); } /** * Return the address space for the given @p child device. */ bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, device_t child) { struct bhndb_devinfo *dinfo; device_t imd_dev; /* Find the directly attached parent of the requesting device */ imd_dev = child; while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev) imd_dev = device_get_parent(imd_dev); if (imd_dev == NULL) panic("bhndb address space request for non-child device %s\n", device_get_nameunit(child)); dinfo = device_get_ivars(imd_dev); return (dinfo->addrspace); } /** * Return the rman instance for a given resource @p type, if any. * * @param sc The bhndb device state. * @param child The requesting child. * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) */ static struct rman * bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type) { switch (bhndb_get_addrspace(sc, child)) { case BHNDB_ADDRSPACE_NATIVE: switch (type) { case SYS_RES_MEMORY: return (&sc->bus_res->ht_mem_rman); case SYS_RES_IRQ: return (NULL); default: return (NULL); }; case BHNDB_ADDRSPACE_BRIDGED: switch (type) { case SYS_RES_MEMORY: return (&sc->bus_res->br_mem_rman); case SYS_RES_IRQ: // TODO // return &sc->irq_rman; return (NULL); default: return (NULL); }; } /* Quieten gcc */ return (NULL); } /** * Default implementation of BUS_ADD_CHILD() */ static device_t bhndb_add_child(device_t dev, u_int order, const char *name, int unit) { struct bhndb_devinfo *dinfo; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (NULL); dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT); if (dinfo == NULL) { device_delete_child(dev, child); return (NULL); } dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); return (child); } /** * Default implementation of BUS_CHILD_DELETED(). */ static void bhndb_child_deleted(device_t dev, device_t child) { struct bhndb_devinfo *dinfo = device_get_ivars(child); if (dinfo != NULL) { resource_list_free(&dinfo->resources); free(dinfo, M_BHND); } device_set_ivars(child, NULL); } /** * Default implementation of BHNDB_GET_CHIPID(). */ static const struct bhnd_chipid * bhndb_get_chipid(device_t dev, device_t child) { struct bhndb_softc *sc = device_get_softc(dev); return (&sc->chipid); } /** * Default implementation of BHNDB_IS_HW_DISABLED(). */ static bool bhndb_is_hw_disabled(device_t dev, device_t child) { struct bhndb_softc *sc; struct bhnd_core_info core; sc = device_get_softc(dev); /* Requestor must be attached to the bhnd bus */ if (device_get_parent(child) != sc->bus_dev) { return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); } /* Fetch core info */ core = bhnd_get_core_info(child); /* Try to defer to the bhndb bus parent */ if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core)) return (true); /* Otherwise, we treat bridge-capable cores as unpopulated if they're * not the configured host bridge */ if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core))) return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child); /* Otherwise, assume the core is populated */ return (false); } /* ascending core index comparison used by bhndb_find_hostb_device() */ static int compare_core_index(const void *lhs, const void *rhs) { u_int left = bhnd_get_core_index(*(const device_t *) lhs); u_int right = bhnd_get_core_index(*(const device_t *) rhs); if (left < right) return (-1); else if (left > right) return (1); else return (0); } /** * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE(). * * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged * bhnd(4) devices to determine the hostb core: * * - The core must have a Broadcom vendor ID. * - The core devclass must match the bridge type. * - The core must be the first device on the bus with the bridged device * class. * * @param dev The bhndb device * @param child The requesting bhnd bus. */ static device_t bhndb_find_hostb_device(device_t dev, device_t child) { - struct bhndb_softc *sc; - struct bhnd_core_match md; - device_t hostb_dev, *devlist; - int devcnt, error; + struct bhndb_softc *sc; + struct bhnd_device_match md; + device_t hostb_dev, *devlist; + int devcnt, error; sc = device_get_softc(dev); - /* Determine required device class and set up a match descriptor. */ - md = (struct bhnd_core_match) { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_INVALID, - .hwrev = { BHND_HWREV_INVALID, BHND_HWREV_INVALID }, - .class = sc->bridge_class, - .unit = 0 + /* Set up a match descriptor for the required device class. */ + md = (struct bhnd_device_match) { + BHND_MATCH_CORE_CLASS(sc->bridge_class), + BHND_MATCH_CORE_UNIT(0) }; /* Must be the absolute first matching device on the bus. */ if ((error = device_get_children(child, &devlist, &devcnt))) return (false); /* Sort by core index value, ascending */ qsort(devlist, devcnt, sizeof(*devlist), compare_core_index); /* Find the hostb device */ hostb_dev = NULL; for (int i = 0; i < devcnt; i++) { if (bhnd_device_matches(devlist[i], &md)) { hostb_dev = devlist[i]; break; } } /* Clean up */ free(devlist, M_TEMP); return (hostb_dev); } /** * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). */ static struct resource * bhndb_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct bhndb_softc *sc; struct resource_list_entry *rle; struct resource *rv; struct rman *rm; int error; bool passthrough, isdefault; sc = device_get_softc(dev); passthrough = (device_get_parent(child) != dev); isdefault = RMAN_IS_DEFAULT_RANGE(start, end); rle = NULL; /* Populate defaults */ if (!passthrough && isdefault) { /* Fetch the resource list entry. */ rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), type, *rid); if (rle == NULL) { device_printf(dev, "default resource %#x type %d for child %s " "not found\n", *rid, type, device_get_nameunit(child)); return (NULL); } if (rle->res != NULL) { device_printf(dev, "resource entry %#x type %d for child %s is busy\n", *rid, type, device_get_nameunit(child)); return (NULL); } start = rle->start; end = rle->end; count = ulmax(count, rle->count); } /* Validate resource addresses */ if (start > end || count > ((end - start) + 1)) return (NULL); /* Fetch the resource manager */ rm = bhndb_get_rman(sc, child, type); if (rm == NULL) return (NULL); /* Make our reservation */ rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); /* Activate */ if (flags & RF_ACTIVE) { error = bus_activate_resource(child, type, *rid, rv); if (error) { device_printf(dev, "failed to activate entry %#x type %d for " "child %s: %d\n", *rid, type, device_get_nameunit(child), error); rman_release_resource(rv); return (NULL); } } /* Update child's resource list entry */ if (rle != NULL) { rle->res = rv; rle->start = rman_get_start(rv); rle->end = rman_get_end(rv); rle->count = rman_get_size(rv); } return (rv); } /** * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE(). */ static int bhndb_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { int error; /* Deactivate resources */ if (rman_get_flags(r) & RF_ACTIVE) { error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); if (error) return (error); } if ((error = rman_release_resource(r))) return (error); return (0); } /** * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE(). */ static int bhndb_adjust_resource(device_t dev, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end) { struct bhndb_softc *sc; struct rman *rm; rman_res_t mstart, mend; int error; sc = device_get_softc(dev); error = 0; /* Verify basic constraints */ if (end <= start) return (EINVAL); /* Fetch resource manager */ rm = bhndb_get_rman(sc, child, type); if (rm == NULL) return (ENXIO); if (!rman_is_region_manager(r, rm)) return (ENXIO); BHNDB_LOCK(sc); /* If not active, allow any range permitted by the resource manager */ if (!(rman_get_flags(r) & RF_ACTIVE)) goto done; /* Otherwise, the range is limited to the existing register window * mapping */ error = bhndb_find_resource_limits(sc->bus_res, r, &mstart, &mend); if (error) goto done; if (start < mstart || end > mend) { error = EINVAL; goto done; } /* Fall through */ done: if (!error) error = rman_adjust_resource(r, start, end); BHNDB_UNLOCK(sc); return (error); } /** * Initialize child resource @p r with a virtual address, tag, and handle * copied from @p parent, adjusted to contain only the range defined by * @p offsize and @p size. * * @param r The register to be initialized. * @param parent The parent bus resource that fully contains the subregion. * @param offset The subregion offset within @p parent. * @param size The subregion size. * @p r. */ static int bhndb_init_child_resource(struct resource *r, struct resource *parent, bhnd_size_t offset, bhnd_size_t size) { bus_space_handle_t bh, child_bh; bus_space_tag_t bt; uintptr_t vaddr; int error; /* Fetch the parent resource's real bus values */ vaddr = (uintptr_t) rman_get_virtual(parent); bt = rman_get_bustag(parent); bh = rman_get_bushandle(parent); /* Configure child resource with window-adjusted real bus values */ vaddr += offset; error = bus_space_subregion(bt, bh, offset, size, &child_bh); if (error) return (error); rman_set_virtual(r, (void *) vaddr); rman_set_bustag(r, bt); rman_set_bushandle(r, child_bh); return (0); } /** * Attempt activation of a fixed register window mapping for @p child. * * @param sc BHNDB device state. * @param region The static region definition capable of mapping @p r. * @param child A child requesting resource activation. * @param type Resource type. * @param rid Resource identifier. * @param r Resource to be activated. * * @retval 0 if @p r was activated successfully * @retval ENOENT if no fixed register window was found. * @retval non-zero if @p r could not be activated. */ static int bhndb_activate_static_region(struct bhndb_softc *sc, struct bhndb_region *region, device_t child, int type, int rid, struct resource *r) { struct resource *bridge_res; const struct bhndb_regwin *win; bhnd_size_t parent_offset; rman_res_t r_start, r_size; int error; win = region->static_regwin; KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type), ("can't activate non-static region")); r_start = rman_get_start(r); r_size = rman_get_size(r); /* Find the corresponding bridge resource */ bridge_res = bhndb_find_regwin_resource(sc->bus_res, win); if (bridge_res == NULL) return (ENXIO); /* Calculate subregion offset within the parent resource */ parent_offset = r_start - region->addr; parent_offset += win->win_offset; /* Configure resource with its real bus values. */ error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size); if (error) return (error); /* Mark active */ if ((error = rman_activate_resource(r))) return (error); return (0); } /** * Attempt to allocate/retain a dynamic register window for @p r, returning * the retained window. * * @param sc The bhndb driver state. * @param r The resource for which a window will be retained. */ static struct bhndb_dw_alloc * bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r) { struct bhndb_dw_alloc *dwa; rman_res_t r_start, r_size; int error; BHNDB_LOCK_ASSERT(sc, MA_OWNED); r_start = rman_get_start(r); r_size = rman_get_size(r); /* Look for an existing dynamic window we can reference */ dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size); if (dwa != NULL) { if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0) return (dwa); return (NULL); } /* Otherwise, try to reserve a free window */ dwa = bhndb_dw_next_free(sc->bus_res); if (dwa == NULL) { /* No free windows */ return (NULL); } /* Set the window target */ error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r), rman_get_size(r)); if (error) { device_printf(sc->dev, "dynamic window initialization " "for 0x%llx-0x%llx failed: %d\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1, error); return (NULL); } /* Add our reservation */ if (bhndb_dw_retain(sc->bus_res, dwa, r)) return (NULL); return (dwa); } /** * Activate a resource using any viable static or dynamic register window. * * @param sc The bhndb driver state. * @param child The child holding ownership of @p r. * @param type The type of the resource to be activated. * @param rid The resource ID of @p r. * @param r The resource to be activated * @param[out] indirect On error and if not NULL, will be set to 'true' if * the caller should instead use an indirect resource mapping. * * @retval 0 success * @retval non-zero activation failed. */ static int bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type, int rid, struct resource *r, bool *indirect) { struct bhndb_region *region; struct bhndb_dw_alloc *dwa; bhndb_priority_t dw_priority; rman_res_t r_start, r_size; rman_res_t parent_offset; int error; BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); // TODO - IRQs if (type != SYS_RES_MEMORY) return (ENXIO); if (indirect) *indirect = false; r_start = rman_get_start(r); r_size = rman_get_size(r); /* Activate native addrspace resources using the host address space */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) { struct resource *parent; /* Find the bridge resource referenced by the child */ parent = bhndb_find_resource_range(sc->bus_res, r_start, r_size); if (parent == NULL) { device_printf(sc->dev, "host resource not found " "for 0x%llx-0x%llx\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1); return (ENOENT); } /* Initialize child resource with the real bus values */ error = bhndb_init_child_resource(r, parent, r_start - rman_get_start(parent), r_size); if (error) return (error); /* Try to activate child resource */ return (rman_activate_resource(r)); } /* Default to low priority */ dw_priority = BHNDB_PRIORITY_LOW; /* Look for a bus region matching the resource's address range */ region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); if (region != NULL) dw_priority = region->priority; /* Prefer static mappings over consuming a dynamic windows. */ if (region && region->static_regwin) { error = bhndb_activate_static_region(sc, region, child, type, rid, r); if (error) device_printf(sc->dev, "static window allocation " "for 0x%llx-0x%llx failed\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1); return (error); } /* A dynamic window will be required; is this resource high enough * priority to be reserved a dynamic window? */ if (dw_priority < sc->bus_res->min_prio) { if (indirect) *indirect = true; return (ENOMEM); } /* Find and retain a usable window */ BHNDB_LOCK(sc); { dwa = bhndb_retain_dynamic_window(sc, r); } BHNDB_UNLOCK(sc); if (dwa == NULL) { if (indirect) *indirect = true; return (ENOMEM); } /* Configure resource with its real bus values. */ parent_offset = dwa->win->win_offset; parent_offset += r_start - dwa->target; error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset, dwa->win->win_size); if (error) goto failed; /* Mark active */ if ((error = rman_activate_resource(r))) goto failed; return (0); failed: /* Release our region allocation. */ BHNDB_LOCK(sc); bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); return (error); } /** * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE(). * * Maps resource activation requests to a viable static or dynamic * register window, if any. */ static int bhndb_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct bhndb_softc *sc = device_get_softc(dev); return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); } /** * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). */ static int bhndb_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct bhndb_dw_alloc *dwa; struct bhndb_softc *sc; struct rman *rm; int error; sc = device_get_softc(dev); if ((rm = bhndb_get_rman(sc, child, type)) == NULL) return (EINVAL); /* Mark inactive */ if ((error = rman_deactivate_resource(r))) return (error); /* Free any dynamic window allocation. */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { BHNDB_LOCK(sc); dwa = bhndb_dw_find_resource(sc->bus_res, r); if (dwa != NULL) bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); } return (0); } /** * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST(). */ static struct resource_list * bhndb_get_resource_list(device_t dev, device_t child) { struct bhndb_devinfo *dinfo = device_get_ivars(child); return (&dinfo->resources); } /** * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE(). * * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to * be activated by the bridge. * * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register * window, a dynamic register window, or configures @p r as an indirect * resource -- in that order. */ static int bhndb_activate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { struct bhndb_softc *sc; struct bhndb_region *region; rman_res_t r_start, r_size; int error; bool indirect; KASSERT(!r->direct, ("direct flag set on inactive resource")); KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE), ("RF_ACTIVE set on inactive resource")); sc = device_get_softc(dev); r_start = rman_get_start(r->res); r_size = rman_get_size(r->res); /* Verify bridged address range's resource priority, and skip direct * allocation if the priority is too low. */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { bhndb_priority_t r_prio; region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); if (region != NULL) r_prio = region->priority; else r_prio = BHNDB_PRIORITY_NONE; /* If less than the minimum dynamic window priority, this * resource should always be indirect. */ if (r_prio < sc->bus_res->min_prio) return (0); } /* Attempt direct activation */ error = bhndb_try_activate_resource(sc, child, type, rid, r->res, &indirect); if (!error) { r->direct = true; } else if (indirect) { /* The request was valid, but no viable register window is * available; indirection must be employed. */ error = 0; r->direct = false; } if (BHNDB_DEBUG(PRIO) && bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { device_printf(child, "activated 0x%llx-0x%llx as %s " "resource\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1, r->direct ? "direct" : "indirect"); } return (error); }; /** * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE(). */ static int bhndb_deactivate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; /* Indirect resources don't require activation */ if (!r->direct) return (0); KASSERT(rman_get_flags(r->res) & RF_ACTIVE, ("RF_ACTIVE not set on direct resource")); /* Perform deactivation */ error = bus_deactivate_resource(child, type, rid, r->res); if (!error) r->direct = false; return (error); }; /** * Slow path for bhndb_io_resource(). * * Iterates over the existing allocated dynamic windows looking for a viable * in-use region; the first matching region is returned. */ static struct bhndb_dw_alloc * bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, bus_size_t *offset) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; BHNDB_LOCK_ASSERT(sc, MA_OWNED); br = sc->bus_res; /* Search for an existing dynamic mapping of this address range. * Static regions are not searched, as a statically mapped * region would never be allocated as an indirect resource. */ for (size_t i = 0; i < br->dwa_count; i++) { const struct bhndb_regwin *win; dwa = &br->dw_alloc[i]; win = dwa->win; KASSERT(win->win_type == BHNDB_REGWIN_T_DYN, ("invalid register window type")); /* Verify the range */ if (addr < dwa->target) continue; if (addr + size > dwa->target + win->win_size) continue; /* Found */ *offset = dwa->win->win_offset; *offset += addr - dwa->target; return (dwa); } /* not found */ return (NULL); } /** * Find the bridge resource to be used for I/O requests. * * @param sc Bridge driver state. * @param addr The I/O target address. * @param size The size of the I/O operation to be performed at @p addr. * @param[out] offset The offset within the returned resource at which * to perform the I/O request. */ static inline struct bhndb_dw_alloc * bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, bus_size_t *offset) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; int error; BHNDB_LOCK_ASSERT(sc, MA_OWNED); br = sc->bus_res; /* Try to fetch a free window */ dwa = bhndb_dw_next_free(br); /* * If no dynamic windows are available, look for an existing * region that maps the target range. * * If none are found, this is a child driver bug -- our window * over-commit should only fail in the case where a child driver leaks * resources, or perform operations out-of-order. * * Broadcom HND chipsets are designed to not require register window * swapping during execution; as long as the child devices are * attached/detached correctly, using the hardware's required order * of operations, there should always be a window available for the * current operation. */ if (dwa == NULL) { dwa = bhndb_io_resource_slow(sc, addr, size, offset); if (dwa == NULL) { panic("register windows exhausted attempting to map " "0x%llx-0x%llx\n", (unsigned long long) addr, (unsigned long long) addr+size-1); } return (dwa); } /* Adjust the window if the I/O request won't fit in the current * target range. */ if (addr < dwa->target || (dwa->target + dwa->win->win_size) - addr < size) { error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr, size); if (error) { panic("failed to set register window target mapping " "0x%llx-0x%llx\n", (unsigned long long) addr, (unsigned long long) addr+size-1); } } /* Calculate the offset and return */ *offset = (addr - dwa->target) + dwa->win->win_offset; return (dwa); } /* * BHND_BUS_(READ|WRITE_* implementations */ /* bhndb_bus_(read|write) common implementation */ #define BHNDB_IO_COMMON_SETUP(_io_size) \ struct bhndb_softc *sc; \ struct bhndb_dw_alloc *dwa; \ struct resource *io_res; \ bus_size_t io_offset; \ \ sc = device_get_softc(dev); \ \ BHNDB_LOCK(sc); \ dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ offset, _io_size, &io_offset); \ io_res = dwa->parent_res; \ \ KASSERT(!r->direct, \ ("bhnd_bus slow path used for direct resource")); \ \ KASSERT(rman_get_flags(io_res) & RF_ACTIVE, \ ("i/o resource is not active")); #define BHNDB_IO_COMMON_TEARDOWN() \ BHNDB_UNLOCK(sc); /* Defines a bhndb_bus_read_* method implementation */ #define BHNDB_IO_READ(_type, _name) \ static _type \ bhndb_bus_read_ ## _name (device_t dev, device_t child, \ struct bhnd_resource *r, bus_size_t offset) \ { \ _type v; \ BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ v = bus_read_ ## _name (io_res, io_offset); \ BHNDB_IO_COMMON_TEARDOWN(); \ \ return (v); \ } /* Defines a bhndb_bus_write_* method implementation */ #define BHNDB_IO_WRITE(_type, _name) \ static void \ bhndb_bus_write_ ## _name (device_t dev, device_t child, \ struct bhnd_resource *r, bus_size_t offset, _type value) \ { \ BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ bus_write_ ## _name (io_res, io_offset, value); \ BHNDB_IO_COMMON_TEARDOWN(); \ } /* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */ #define BHNDB_IO_MISC(_type, _ptr, _op, _size) \ static void \ bhndb_bus_ ## _op ## _ ## _size (device_t dev, \ device_t child, struct bhnd_resource *r, bus_size_t offset, \ _type _ptr datap, bus_size_t count) \ { \ BHNDB_IO_COMMON_SETUP(sizeof(_type) * count); \ bus_ ## _op ## _ ## _size (io_res, io_offset, \ datap, count); \ BHNDB_IO_COMMON_TEARDOWN(); \ } /* Defines a complete set of read/write methods */ #define BHNDB_IO_METHODS(_type, _size) \ BHNDB_IO_READ(_type, _size) \ BHNDB_IO_WRITE(_type, _size) \ \ BHNDB_IO_READ(_type, stream_ ## _size) \ BHNDB_IO_WRITE(_type, stream_ ## _size) \ \ BHNDB_IO_MISC(_type, *, read_multi, _size) \ BHNDB_IO_MISC(_type, *, write_multi, _size) \ \ BHNDB_IO_MISC(_type, *, read_multi_stream, _size) \ BHNDB_IO_MISC(_type, *, write_multi_stream, _size) \ \ BHNDB_IO_MISC(_type, , set_multi, _size) \ BHNDB_IO_MISC(_type, , set_region, _size) \ BHNDB_IO_MISC(_type, *, read_region, _size) \ BHNDB_IO_MISC(_type, *, write_region, _size) \ \ BHNDB_IO_MISC(_type, *, read_region_stream, _size) \ BHNDB_IO_MISC(_type, *, write_region_stream, _size) BHNDB_IO_METHODS(uint8_t, 1); BHNDB_IO_METHODS(uint16_t, 2); BHNDB_IO_METHODS(uint32_t, 4); /** * Default bhndb(4) implementation of BHND_BUS_BARRIER(). */ static void bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r, bus_size_t offset, bus_size_t length, int flags) { BHNDB_IO_COMMON_SETUP(length); bus_barrier(io_res, io_offset + offset, length, flags); BHNDB_IO_COMMON_TEARDOWN(); } /** * Default bhndb(4) implementation of BUS_SETUP_INTR(). */ static int bhndb_setup_intr(device_t dev, device_t child, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). */ static int bhndb_teardown_intr(device_t dev, device_t child, struct resource *r, void *cookie) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_CONFIG_INTR(). */ static int bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_BIND_INTR(). */ static int bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). */ static int bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, const char *descr) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). */ static bus_dma_tag_t bhndb_get_dma_tag(device_t dev, device_t child) { // TODO return (NULL); } static device_method_t bhndb_methods[] = { /* Device interface */ \ DEVMETHOD(device_probe, bhndb_generic_probe), DEVMETHOD(device_detach, bhndb_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bhndb_generic_suspend), DEVMETHOD(device_resume, bhndb_generic_resume), /* Bus interface */ DEVMETHOD(bus_probe_nomatch, bhndb_probe_nomatch), DEVMETHOD(bus_print_child, bhndb_print_child), DEVMETHOD(bus_child_pnpinfo_str, bhndb_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, bhndb_child_location_str), DEVMETHOD(bus_add_child, bhndb_add_child), DEVMETHOD(bus_child_deleted, bhndb_child_deleted), DEVMETHOD(bus_alloc_resource, bhndb_alloc_resource), DEVMETHOD(bus_release_resource, bhndb_release_resource), DEVMETHOD(bus_activate_resource, bhndb_activate_resource), DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), DEVMETHOD(bus_setup_intr, bhndb_setup_intr), DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), DEVMETHOD(bus_config_intr, bhndb_config_intr), DEVMETHOD(bus_bind_intr, bhndb_bind_intr), DEVMETHOD(bus_describe_intr, bhndb_describe_intr), DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), DEVMETHOD(bus_adjust_resource, bhndb_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_get_resource_list, bhndb_get_resource_list), DEVMETHOD(bus_read_ivar, bhndb_read_ivar), DEVMETHOD(bus_write_ivar, bhndb_write_ivar), /* BHNDB interface */ DEVMETHOD(bhndb_get_chipid, bhndb_get_chipid), DEVMETHOD(bhndb_init_full_config, bhndb_generic_init_full_config), DEVMETHOD(bhndb_find_hostb_device, bhndb_find_hostb_device), DEVMETHOD(bhndb_suspend_resource, bhndb_suspend_resource), DEVMETHOD(bhndb_resume_resource, bhndb_resume_resource), /* BHND interface */ DEVMETHOD(bhnd_bus_is_hw_disabled, bhndb_is_hw_disabled), DEVMETHOD(bhnd_bus_get_chipid, bhndb_get_chipid), DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource), DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource), DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1), DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), DEVMETHOD(bhnd_bus_write_1, bhndb_bus_write_1), DEVMETHOD(bhnd_bus_write_2, bhndb_bus_write_2), DEVMETHOD(bhnd_bus_write_4, bhndb_bus_write_4), DEVMETHOD(bhnd_bus_read_stream_1, bhndb_bus_read_stream_1), DEVMETHOD(bhnd_bus_read_stream_2, bhndb_bus_read_stream_2), DEVMETHOD(bhnd_bus_read_stream_4, bhndb_bus_read_stream_4), DEVMETHOD(bhnd_bus_write_stream_1, bhndb_bus_write_stream_1), DEVMETHOD(bhnd_bus_write_stream_2, bhndb_bus_write_stream_2), DEVMETHOD(bhnd_bus_write_stream_4, bhndb_bus_write_stream_4), DEVMETHOD(bhnd_bus_read_multi_1, bhndb_bus_read_multi_1), DEVMETHOD(bhnd_bus_read_multi_2, bhndb_bus_read_multi_2), DEVMETHOD(bhnd_bus_read_multi_4, bhndb_bus_read_multi_4), DEVMETHOD(bhnd_bus_write_multi_1, bhndb_bus_write_multi_1), DEVMETHOD(bhnd_bus_write_multi_2, bhndb_bus_write_multi_2), DEVMETHOD(bhnd_bus_write_multi_4, bhndb_bus_write_multi_4), DEVMETHOD(bhnd_bus_read_multi_stream_1, bhndb_bus_read_multi_stream_1), DEVMETHOD(bhnd_bus_read_multi_stream_2, bhndb_bus_read_multi_stream_2), DEVMETHOD(bhnd_bus_read_multi_stream_4, bhndb_bus_read_multi_stream_4), DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1), DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2), DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4), DEVMETHOD(bhnd_bus_set_multi_1, bhndb_bus_set_multi_1), DEVMETHOD(bhnd_bus_set_multi_2, bhndb_bus_set_multi_2), DEVMETHOD(bhnd_bus_set_multi_4, bhndb_bus_set_multi_4), DEVMETHOD(bhnd_bus_set_region_1, bhndb_bus_set_region_1), DEVMETHOD(bhnd_bus_set_region_2, bhndb_bus_set_region_2), DEVMETHOD(bhnd_bus_set_region_4, bhndb_bus_set_region_4), DEVMETHOD(bhnd_bus_read_region_1, bhndb_bus_read_region_1), DEVMETHOD(bhnd_bus_read_region_2, bhndb_bus_read_region_2), DEVMETHOD(bhnd_bus_read_region_4, bhndb_bus_read_region_4), DEVMETHOD(bhnd_bus_write_region_1, bhndb_bus_write_region_1), DEVMETHOD(bhnd_bus_write_region_2, bhndb_bus_write_region_2), DEVMETHOD(bhnd_bus_write_region_4, bhndb_bus_write_region_4), DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1), DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2), DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4), DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1), DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2), DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4), DEVMETHOD(bhnd_bus_barrier, bhndb_bus_barrier), DEVMETHOD_END }; devclass_t bhndb_devclass; DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc)); MODULE_VERSION(bhndb, 1); MODULE_DEPEND(bhndb, bhnd, 1, 1, 1); MODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1); Index: head/sys/dev/bhnd/bhndb/bhndb_hwdata.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_hwdata.c (revision 300627) +++ head/sys/dev/bhnd/bhndb/bhndb_hwdata.c (revision 300628) @@ -1,195 +1,196 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "bhndb_hwdata.h" /* * Resource priority specifications shared by all bhndb(4) bridge * implementations. */ /* * Define a bhndb_port_priority table. */ #define BHNDB_PORTS(...) \ .ports = _BHNDB_PORT_ARRAY(__VA_ARGS__), \ .num_ports = nitems(_BHNDB_PORT_ARRAY(__VA_ARGS__)) #define _BHNDB_PORT_ARRAY(...) (const struct bhndb_port_priority[]) { \ __VA_ARGS__ \ } /* - * Define a core priority record for all cores matching @p devclass and - * @p unit. - * - * If a devclass of BHNDB_DEVCLASS_INVALID is specified, this will match - * on all device classes. - * - * If a unit number of -1 is specified, this will match on all units. + * Define a core priority record for all cores matching @p devclass */ -#define BHNDB_CLASS_PRIO(_devclass, _unit, _priority, ...) { \ +#define BHNDB_CLASS_PRIO(_devclass, _priority, ...) { \ .match = { \ - .vendor = BHND_MFGID_INVALID, \ - .device = BHND_COREID_INVALID, \ - .hwrev = { BHND_HWREV_INVALID, BHND_HWREV_INVALID }, \ - .class = (BHND_DEVCLASS_ ## _devclass), \ - .unit = (_unit) \ + BHND_MATCH_CORE_CLASS(BHND_DEVCLASS_ ## _devclass), \ }, \ .priority = (BHNDB_PRIORITY_ ## _priority), \ BHNDB_PORTS(__VA_ARGS__) \ } +/* + * Define a default core priority record + */ +#define BHNDB_DEFAULT_PRIO(...) { \ + .match = { \ + BHND_MATCH_ANY , \ + }, \ + .priority = (BHNDB_PRIORITY_DEFAULT), \ + BHNDB_PORTS(__VA_ARGS__) \ +} + /* Define a port priority record for the type/port/region * triplet. */ #define BHNDB_PORT_PRIO(_type, _port, _region, _priority) { \ .type = (BHND_PORT_ ## _type), \ .port = _port, \ .region = _region, \ .priority = (BHNDB_PRIORITY_ ## _priority) \ } /* Define a port priority record for the default (_type, 0, 0) type/port/region * triplet. */ #define BHNDB_PORT0_PRIO(_type, _priority) \ BHNDB_PORT_PRIO(_type, 0, 0, _priority) /** * Generic resource priority configuration usable with all currently supported * bcma(4)-based PCI devices. */ const struct bhndb_hw_priority bhndb_bcma_priority_table[] = { /* * Ignorable device classes. * * Runtime access to these cores is not required, and no register * windows should be reserved for these device types. */ - BHNDB_CLASS_PRIO(SOC_ROUTER, -1, NONE), - BHNDB_CLASS_PRIO(SOC_BRIDGE, -1, NONE), - BHNDB_CLASS_PRIO(EROM, -1, NONE), - BHNDB_CLASS_PRIO(OTHER, -1, NONE), + BHNDB_CLASS_PRIO(SOC_ROUTER, NONE), + BHNDB_CLASS_PRIO(SOC_BRIDGE, NONE), + BHNDB_CLASS_PRIO(EROM, NONE), + BHNDB_CLASS_PRIO(OTHER, NONE), /* * Low priority device classes. * * These devices do not sit in a performance-critical path and can be * treated as a low allocation priority. */ - BHNDB_CLASS_PRIO(CC, -1, LOW, + BHNDB_CLASS_PRIO(CC, LOW, /* Device Block */ BHNDB_PORT0_PRIO(DEVICE, LOW), /* CC agent registers are not accessed via the bridge. */ BHNDB_PORT0_PRIO(AGENT, NONE) ), - BHNDB_CLASS_PRIO(PMU, -1, LOW, + BHNDB_CLASS_PRIO(PMU, LOW, /* Device Block */ BHNDB_PORT0_PRIO(DEVICE, LOW), /* PMU agent registers are not accessed via the bridge. */ BHNDB_PORT0_PRIO(AGENT, NONE) ), /* * Default Core Behavior * * All other cores are assumed to require efficient runtime access to * the default device port, and if supported by the bus, an agent port. */ - BHNDB_CLASS_PRIO(INVALID, -1, DEFAULT, + BHNDB_DEFAULT_PRIO( /* Device Block */ BHNDB_PORT0_PRIO(DEVICE, HIGH), /* Agent Block */ BHNDB_PORT0_PRIO(AGENT, DEFAULT) ), BHNDB_HW_PRIORITY_TABLE_END }; /** * Generic resource priority configuration usable with all currently supported * siba(4)-based PCI devices. */ const struct bhndb_hw_priority bhndb_siba_priority_table[] = { /* * Ignorable device classes. * * Runtime access to these cores is not required, and no register * windows should be reserved for these device types. */ - BHNDB_CLASS_PRIO(SOC_ROUTER, -1, NONE), - BHNDB_CLASS_PRIO(SOC_BRIDGE, -1, NONE), - BHNDB_CLASS_PRIO(EROM, -1, NONE), - BHNDB_CLASS_PRIO(OTHER, -1, NONE), + BHNDB_CLASS_PRIO(SOC_ROUTER, NONE), + BHNDB_CLASS_PRIO(SOC_BRIDGE, NONE), + BHNDB_CLASS_PRIO(EROM, NONE), + BHNDB_CLASS_PRIO(OTHER, NONE), /* * Low priority device classes. * * These devices do not sit in a performance-critical path and can be * treated as a low allocation priority. * * Agent ports are marked as 'NONE' on siba(4) devices, as they * will be fully mappable via register windows shared with the * device0.0 port. */ - BHNDB_CLASS_PRIO(CC, -1, LOW, + BHNDB_CLASS_PRIO(CC, LOW, /* Device Block */ BHNDB_PORT_PRIO(DEVICE, 0, 0, LOW) ), - BHNDB_CLASS_PRIO(PMU, -1, LOW, + BHNDB_CLASS_PRIO(PMU, LOW, /* Device Block */ BHNDB_PORT_PRIO(DEVICE, 0, 0, LOW) ), /* * Default Core Behavior * * All other cores are assumed to require efficient runtime access to * the device port. */ - BHNDB_CLASS_PRIO(INVALID, -1, DEFAULT, + BHNDB_DEFAULT_PRIO( /* Device Block */ BHNDB_PORT_PRIO(DEVICE, 0, 0, HIGH) ), BHNDB_HW_PRIORITY_TABLE_END }; \ No newline at end of file Index: head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c (revision 300627) +++ head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c (revision 300628) @@ -1,634 +1,592 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Resource specifications and register maps for Broadcom PCI/PCIe cores * configured as PCI-BHND bridges. */ #include #include #include #include #include #include #include #include "bhndbvar.h" #include "bhndb_pcireg.h" static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0; static const struct bhndb_hwcfg bhndb_pci_hwcfg_v1_pci; static const struct bhndb_hwcfg bhndb_pci_hwcfg_v1_pcie; static const struct bhndb_hwcfg bhndb_pci_hwcfg_v2; static const struct bhndb_hwcfg bhndb_pci_hwcfg_v3; /** * Define a bhndb_hw match entry. * * @param _name The entry name. * @param _vers The configuration version associated with this entry. */ #define BHNDB_HW_MATCH(_name, _vers, ...) { \ .name = _name, \ .hw_reqs = _BHNDB_HW_REQ_ARRAY(__VA_ARGS__), \ .num_hw_reqs = (sizeof(_BHNDB_HW_REQ_ARRAY(__VA_ARGS__)) / \ sizeof(_BHNDB_HW_REQ_ARRAY(__VA_ARGS__)[0])), \ .cfg = &bhndb_pci_hwcfg_ ## _vers \ } #define _BHNDB_HW_REQ_ARRAY(...) (struct bhnd_core_match[]) { __VA_ARGS__ } /** * Generic PCI-SIBA bridge configuration usable with all known siba(4)-based * PCI devices; this configuration is adequate for enumerating a bridged * siba(4) bus to determine the full hardware configuration. * * @par Compatibility * - Compatible with PCI_V0, PCI_V1, PCI_V2, and PCI_V3 devices. * - Compatible with siba(4) bus enumeration. * - Compatible with bcma(4) bus enumeration if the ChipCommon core is mapped * at the default enumeration address (0x18000000). */ const struct bhndb_hwcfg bhndb_pci_siba_generic_hwcfg = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V1_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V1_BAR0_WIN0_CONTROL }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; /** * Generic PCI-BCMA bridge configuration usable with all known bcma(4)-based * PCI devices; this configuration is adequate for enumerating a bridged * bcma(4) bus to determine the full hardware configuration. * * @par Compatibility * - Compatible with PCI_V1, PCI_V2, and PCI_V3 devices. * - Compatible with both siba(4) and bcma(4) bus enumeration. */ const struct bhndb_hwcfg bhndb_pci_bcma_generic_hwcfg = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V1_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V1_BAR0_WIN0_CONTROL, }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x3000: chipc core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V1_BAR0_CCREGS_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_CCREGS_SIZE, .d.core = { .class = BHND_DEVCLASS_CC, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; /** * Hardware configuration tables for Broadcom HND PCI NICs. */ const struct bhndb_hw bhndb_pci_generic_hw_table[] = { /* PCI/V0 WLAN */ BHNDB_HW_MATCH("PCI/v0 WLAN", v0, /* PCI Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_PCI, - .hwrev = { - .start = 0, - .end = BHNDB_PCI_V0_MAX_PCI_HWREV - }, - .class = BHND_DEVCLASS_PCI, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_PCI), + BHND_MATCH_CORE_REV( + HWREV_LTE (BHNDB_PCI_V0_MAX_PCI_HWREV)), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_PCI), + BHND_MATCH_CORE_UNIT (0) }, /* 802.11 Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_INVALID, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_WLAN, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_WLAN), + BHND_MATCH_CORE_UNIT (0) } ), /* PCI/V1 WLAN */ BHNDB_HW_MATCH("PCI/v1 WLAN", v1_pci, /* PCI Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_PCI, - .hwrev = { - .start = BHNDB_PCI_V1_MIN_PCI_HWREV, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_PCI, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_PCI), + BHND_MATCH_CORE_REV( + HWREV_GTE (BHNDB_PCI_V1_MIN_PCI_HWREV)), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_PCI), + BHND_MATCH_CORE_UNIT (0) }, /* 802.11 Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_INVALID, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_WLAN, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_WLAN), + BHND_MATCH_CORE_UNIT (0) } ), /* PCIE/V1 WLAN */ BHNDB_HW_MATCH("PCIe/v1 WLAN", v1_pcie, /* PCIe Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_PCIE, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_PCIE, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_PCIE), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_PCIE), + BHND_MATCH_CORE_UNIT (0) }, /* ChipCommon (revision <= 31) */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_CC, - .hwrev = { - .start = 0, - .end = BHNDB_PCI_V1_MAX_CHIPC_HWREV - }, - .class = BHND_DEVCLASS_CC, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_CC), + BHND_MATCH_CORE_REV( + HWREV_LTE (BHNDB_PCI_V1_MAX_CHIPC_HWREV)), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_CC), + BHND_MATCH_CORE_UNIT (0) }, /* 802.11 Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_INVALID, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_WLAN, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_WLAN), + BHND_MATCH_CORE_UNIT (0) } ), /* PCIE/V2 WLAN */ BHNDB_HW_MATCH("PCIe/v2 WLAN", v2, /* PCIe Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_PCIE, - .hwrev = { 0, BHND_HWREV_INVALID }, - .class = BHND_DEVCLASS_PCIE, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_PCIE), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_PCIE), + BHND_MATCH_CORE_UNIT (0) }, /* ChipCommon (revision >= 32) */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_CC, - .hwrev = { - .start = BHNDB_PCI_V2_MIN_CHIPC_HWREV, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_CC, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_CC), + BHND_MATCH_CORE_REV( + HWREV_GTE (BHNDB_PCI_V2_MIN_CHIPC_HWREV)), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_CC), + BHND_MATCH_CORE_UNIT (0) }, /* 802.11 Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_INVALID, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_WLAN, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_WLAN), + BHND_MATCH_CORE_UNIT (0) } ), /* PCIE/V3 WLAN */ BHNDB_HW_MATCH("PCIe-Gen2/v3 WLAN", v3, /* PCIe Gen2 Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_PCIE2, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_PCIE, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_ID (BHND_COREID_PCIE2), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_PCIE), + BHND_MATCH_CORE_UNIT (0) }, /* 802.11 Core */ { - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_INVALID, - .hwrev = { - .start = 0, - .end = BHND_HWREV_INVALID - }, - .class = BHND_DEVCLASS_WLAN, - .unit = 0 + BHND_MATCH_CORE_VENDOR (BHND_MFGID_BCM), + BHND_MATCH_CORE_CLASS (BHND_DEVCLASS_WLAN), + BHND_MATCH_CORE_UNIT (0) } ), { NULL, NULL, 0, NULL } }; /** * PCI_V0 hardware configuration. * * Applies to: * - PCI (cid=0x804, revision <= 12) */ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V0_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V0_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V0_BAR0_WIN0_CONTROL }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x1000: sprom shadow */ { .win_type = BHNDB_REGWIN_T_SPROM, .win_offset = BHNDB_PCI_V0_BAR0_SPROM_OFFSET, .win_size = BHNDB_PCI_V0_BAR0_SPROM_SIZE, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x1800: pci core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V0_BAR0_PCIREG_OFFSET, .win_size = BHNDB_PCI_V0_BAR0_PCIREG_SIZE, .d.core = { .class = BHND_DEVCLASS_PCI, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; /** * PCI_V1 (PCI-only) hardware configuration (PCI version) * * Applies to: * - PCI (cid=0x804, revision >= 13) */ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v1_pci = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V1_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V1_BAR0_WIN0_CONTROL }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x1000: sprom shadow */ { .win_type = BHNDB_REGWIN_T_SPROM, .win_offset = BHNDB_PCI_V1_BAR0_SPROM_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_SPROM_SIZE, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x2000: pci core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V1_BAR0_PCIREG_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_PCIREG_SIZE, .d.core = { .class = BHND_DEVCLASS_PCI, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x3000: chipc core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V1_BAR0_CCREGS_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_CCREGS_SIZE, .d.core = { .class = BHND_DEVCLASS_CC, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; /** * PCI_V1 hardware configuration (PCIE version). * * Applies to: * - PCIE (cid=0x820) with ChipCommon (revision <= 31) */ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v1_pcie = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V1_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V1_BAR0_WIN0_CONTROL }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x1000: sprom shadow */ { .win_type = BHNDB_REGWIN_T_SPROM, .win_offset = BHNDB_PCI_V1_BAR0_SPROM_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_SPROM_SIZE, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x2000: pci core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V1_BAR0_PCIREG_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_PCIREG_SIZE, .d.core = { .class = BHND_DEVCLASS_PCIE, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x3000: chipc core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V1_BAR0_CCREGS_OFFSET, .win_size = BHNDB_PCI_V1_BAR0_CCREGS_SIZE, .d.core = { .class = BHND_DEVCLASS_CC, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; /** * PCI_V2 hardware configuration. * * Applies to: * - PCIE (cid=0x820) with ChipCommon (revision >= 32) */ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v2 = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V2_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V2_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V2_BAR0_WIN0_CONTROL, }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x1000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V2_BAR0_WIN1_OFFSET, .win_size = BHNDB_PCI_V2_BAR0_WIN1_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V2_BAR0_WIN1_CONTROL, }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x2000: pcie core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V2_BAR0_PCIREG_OFFSET, .win_size = BHNDB_PCI_V2_BAR0_PCIREG_SIZE, .d.core = { .class = BHND_DEVCLASS_PCIE, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x3000: chipc core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V2_BAR0_CCREGS_OFFSET, .win_size = BHNDB_PCI_V2_BAR0_CCREGS_SIZE, .d.core = { .class = BHND_DEVCLASS_CC, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; /** * PCI_V3 hardware configuration. * * Applies to: * - PCIE2 (cid=0x83c) */ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v3 = { .resource_specs = (const struct resource_spec[]) { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }, .register_windows = (const struct bhndb_regwin[]) { /* bar0+0x0000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V3_BAR0_WIN0_OFFSET, .win_size = BHNDB_PCI_V3_BAR0_WIN0_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V3_BAR0_WIN0_CONTROL, }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x1000: configurable backplane window */ { .win_type = BHNDB_REGWIN_T_DYN, .win_offset = BHNDB_PCI_V3_BAR0_WIN1_OFFSET, .win_size = BHNDB_PCI_V3_BAR0_WIN1_SIZE, .d.dyn = { .cfg_offset = BHNDB_PCI_V3_BAR0_WIN1_CONTROL, }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x2000: pcie core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V3_BAR0_PCIREG_OFFSET, .win_size = BHNDB_PCI_V3_BAR0_PCIREG_SIZE, .d.core = { .class = BHND_DEVCLASS_PCIE, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, /* bar0+0x3000: chipc core registers */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V3_BAR0_CCREGS_OFFSET, .win_size = BHNDB_PCI_V3_BAR0_CCREGS_SIZE, .d.core = { .class = BHND_DEVCLASS_CC, .unit = 0, .port = 0, .region = 0, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, BHNDB_REGWIN_TABLE_END }, }; Index: head/sys/dev/bhnd/bhndb/bhndb_subr.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_subr.c (revision 300627) +++ head/sys/dev/bhnd/bhndb/bhndb_subr.c (revision 300628) @@ -1,1029 +1,1032 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include "bhndb_private.h" #include "bhndbvar.h" /** * Attach a BHND bridge device to @p parent. * * @param parent A parent PCI device. * @param[out] bhndb On success, the probed and attached bhndb bridge device. * @param unit The device unit number, or -1 to select the next available unit * number. * * @retval 0 success * @retval non-zero Failed to attach the bhndb device. */ int bhndb_attach_bridge(device_t parent, device_t *bhndb, int unit) { int error; *bhndb = device_add_child(parent, "bhndb", unit); if (*bhndb == NULL) return (ENXIO); if (!(error = device_probe_and_attach(*bhndb))) return (0); if ((device_delete_child(parent, *bhndb))) device_printf(parent, "failed to detach bhndb child\n"); return (error); } /* * Call BHNDB_SUSPEND_RESOURCE() for all resources in @p rl. */ static void bhndb_do_suspend_resources(device_t dev, struct resource_list *rl) { struct resource_list_entry *rle; /* Suspend all child resources. */ STAILQ_FOREACH(rle, rl, link) { /* Skip non-allocated resources */ if (rle->res == NULL) continue; BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, rle->type, rle->res); } } /** * Helper function for implementing BUS_RESUME_CHILD() on bridged * bhnd(4) buses. * * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST() * to find the child's resources and call BHNDB_SUSPEND_RESOURCE() for all * child resources, ensuring that the device's allocated bridge resources * will be available to other devices during bus resumption. * * Before suspending any resources, @p child is suspended by * calling bhnd_generic_suspend_child(). * * If @p child is not a direct child of @p dev, suspension is delegated to * the @p dev parent. */ int bhnd_generic_br_suspend_child(device_t dev, device_t child) { struct resource_list *rl; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (device_is_suspended(child)) return (EBUSY); /* Suspend the child device */ if ((error = bhnd_generic_suspend_child(dev, child))) return (error); /* Fetch the resource list. If none, there's nothing else to do */ rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child); if (rl == NULL) return (0); /* Suspend all child resources. */ bhndb_do_suspend_resources(dev, rl); return (0); } /** * Helper function for implementing BUS_RESUME_CHILD() on bridged * bhnd(4) bus devices. * * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST() * to find the child's resources and call BHNDB_RESUME_RESOURCE() for all * child resources, before delegating to bhnd_generic_resume_child(). * * If resource resumption fails, @p child will not be resumed. * * If @p child is not a direct child of @p dev, suspension is delegated to * the @p dev parent. */ int bhnd_generic_br_resume_child(device_t dev, device_t child) { struct resource_list *rl; struct resource_list_entry *rle; int error; if (device_get_parent(child) != dev) BUS_RESUME_CHILD(device_get_parent(dev), child); if (!device_is_suspended(child)) return (EBUSY); /* Fetch the resource list. If none, there's nothing else to do */ rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child); if (rl == NULL) return (bhnd_generic_resume_child(dev, child)); /* Resume all resources */ STAILQ_FOREACH(rle, rl, link) { /* Skip non-allocated resources */ if (rle->res == NULL) continue; error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, rle->type, rle->res); if (error) { /* Put all resources back into a suspend state */ bhndb_do_suspend_resources(dev, rl); return (error); } } /* Now that all resources are resumed, resume child */ if ((error = bhnd_generic_resume_child(dev, child))) { /* Put all resources back into a suspend state */ bhndb_do_suspend_resources(dev, rl); } return (error); } /** * Find a SYS_RES_MEMORY resource containing the given address range. * * @param br The bhndb resource state to search. * @param start The start address of the range to search for. * @param count The size of the range to search for. * * @retval resource the host resource containing the requested range. * @retval NULL if no resource containing the requested range can be found. */ struct resource * bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start, rman_res_t count) { for (u_int i = 0; br->res_spec[i].type != -1; i++) { struct resource *r = br->res[i]; if (br->res_spec->type != SYS_RES_MEMORY) continue; /* Verify range */ if (rman_get_start(r) > start) continue; if (rman_get_end(r) < (start + count - 1)) continue; return (r); } return (NULL); } /** * Find the resource containing @p win. * * @param br The bhndb resource state to search. * @param win A register window. * * @retval resource the resource containing @p win. * @retval NULL if no resource containing @p win can be found. */ struct resource * bhndb_find_regwin_resource(struct bhndb_resources *br, const struct bhndb_regwin *win) { const struct resource_spec *rspecs; rspecs = br->cfg->resource_specs; for (u_int i = 0; rspecs[i].type != -1; i++) { if (win->res.type != rspecs[i].type) continue; if (win->res.rid != rspecs[i].rid) continue; /* Found declared resource */ return (br->res[i]); } device_printf(br->dev, "missing regwin resource spec (type=%d, rid=%d)\n", win->res.type, win->res.rid); return (NULL); } /** * Allocate and initialize a new resource state structure, allocating * bus resources from @p parent_dev according to @p cfg. * * @param dev The bridge device. * @param parent_dev The parent device from which resources will be allocated. * @param cfg The hardware configuration to be used. */ struct bhndb_resources * bhndb_alloc_resources(device_t dev, device_t parent_dev, const struct bhndb_hwcfg *cfg) { struct bhndb_resources *r; const struct bhndb_regwin *win; bus_size_t last_window_size; size_t res_num; u_int rnid; int error; bool free_parent_res; bool free_ht_mem, free_br_mem; free_parent_res = false; free_ht_mem = false; free_br_mem = false; r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO); if (r == NULL) return (NULL); /* Basic initialization */ r->dev = dev; r->parent_dev = parent_dev; r->cfg = cfg; r->min_prio = BHNDB_PRIORITY_NONE; STAILQ_INIT(&r->bus_regions); /* Initialize host address space resource manager. */ r->ht_mem_rman.rm_start = 0; r->ht_mem_rman.rm_end = ~0; r->ht_mem_rman.rm_type = RMAN_ARRAY; r->ht_mem_rman.rm_descr = "BHNDB host memory"; if ((error = rman_init(&r->ht_mem_rman))) { device_printf(r->dev, "could not initialize ht_mem_rman\n"); goto failed; } free_ht_mem = true; /* Initialize resource manager for the bridged address space. */ r->br_mem_rman.rm_start = 0; r->br_mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT; r->br_mem_rman.rm_type = RMAN_ARRAY; r->br_mem_rman.rm_descr = "BHNDB bridged memory"; if ((error = rman_init(&r->br_mem_rman))) { device_printf(r->dev, "could not initialize br_mem_rman\n"); goto failed; } free_br_mem = true; error = rman_manage_region(&r->br_mem_rman, 0, BUS_SPACE_MAXADDR_32BIT); if (error) { device_printf(r->dev, "could not configure br_mem_rman\n"); goto failed; } /* Determine our bridge resource count from the hardware config. */ res_num = 0; for (size_t i = 0; cfg->resource_specs[i].type != -1; i++) res_num++; /* Allocate space for a non-const copy of our resource_spec * table; this will be updated with the RIDs assigned by * bus_alloc_resources. */ r->res_spec = malloc(sizeof(r->res_spec[0]) * (res_num + 1), M_BHND, M_NOWAIT); if (r->res_spec == NULL) goto failed; /* Initialize and terminate the table */ for (size_t i = 0; i < res_num; i++) r->res_spec[i] = cfg->resource_specs[i]; r->res_spec[res_num].type = -1; /* Allocate space for our resource references */ r->res = malloc(sizeof(r->res[0]) * res_num, M_BHND, M_NOWAIT); if (r->res == NULL) goto failed; /* Allocate resources */ error = bus_alloc_resources(r->parent_dev, r->res_spec, r->res); if (error) { device_printf(r->dev, "could not allocate bridge resources on %s: %d\n", device_get_nameunit(r->parent_dev), error); goto failed; } else { free_parent_res = true; } /* Add allocated memory resources to our host memory resource manager */ for (u_int i = 0; r->res_spec[i].type != -1; i++) { struct resource *res; /* skip non-memory resources */ if (r->res_spec[i].type != SYS_RES_MEMORY) continue; /* add host resource to set of managed regions */ res = r->res[i]; error = rman_manage_region(&r->ht_mem_rman, rman_get_start(res), rman_get_end(res)); if (error) { device_printf(r->dev, "could not register host memory region with " "ht_mem_rman: %d\n", error); goto failed; } } /* Fetch the dynamic regwin count and verify that it does not exceed * what is representable via our freelist bitmask. */ r->dwa_count = bhndb_regwin_count(cfg->register_windows, BHNDB_REGWIN_T_DYN); if (r->dwa_count >= (8 * sizeof(r->dwa_freelist))) { device_printf(r->dev, "max dynamic regwin count exceeded\n"); goto failed; } /* Allocate the dynamic window allocation table. */ r->dw_alloc = malloc(sizeof(r->dw_alloc[0]) * r->dwa_count, M_BHND, M_NOWAIT); if (r->dw_alloc == NULL) goto failed; /* Initialize the dynamic window table and freelist. */ r->dwa_freelist = 0; rnid = 0; last_window_size = 0; for (win = cfg->register_windows; win->win_type != BHNDB_REGWIN_T_INVALID; win++) { struct bhndb_dw_alloc *dwa; /* Skip non-DYN windows */ if (win->win_type != BHNDB_REGWIN_T_DYN) continue; /* Validate the window size */ if (win->win_size == 0) { device_printf(r->dev, "ignoring zero-length dynamic " "register window\n"); continue; } else if (last_window_size == 0) { last_window_size = win->win_size; } else if (last_window_size != win->win_size) { /* * No existing hardware should trigger this. * * If you run into this in the future, the dynamic * window allocator and the resource priority system * will need to be extended to support multiple register * window allocation pools. */ device_printf(r->dev, "devices that vend multiple " "dynamic register window sizes are not currently " "supported\n"); goto failed; } dwa = &r->dw_alloc[rnid]; dwa->win = win; dwa->parent_res = NULL; dwa->rnid = rnid; dwa->target = 0x0; LIST_INIT(&dwa->refs); /* Find and validate corresponding resource. */ dwa->parent_res = bhndb_find_regwin_resource(r, win); if (dwa->parent_res == NULL) goto failed; if (rman_get_size(dwa->parent_res) < win->win_offset + win->win_size) { device_printf(r->dev, "resource %d too small for " "register window with offset %llx and size %llx\n", rman_get_rid(dwa->parent_res), (unsigned long long) win->win_offset, (unsigned long long) win->win_size); error = EINVAL; goto failed; } /* Add to freelist */ r->dwa_freelist |= (1 << rnid); rnid++; } return (r); failed: if (free_parent_res) bus_release_resources(r->parent_dev, r->res_spec, r->res); if (free_ht_mem) rman_fini(&r->ht_mem_rman); if (free_br_mem) rman_fini(&r->br_mem_rman); if (r->res != NULL) free(r->res, M_BHND); if (r->res_spec != NULL) free(r->res_spec, M_BHND); if (r->dw_alloc != NULL) free(r->dw_alloc, M_BHND); free (r, M_BHND); return (NULL); } /** * Deallocate the given bridge resource structure and any associated resources. * * @param br Resource state to be deallocated. */ void bhndb_free_resources(struct bhndb_resources *br) { struct bhndb_region *region, *r_next; struct bhndb_dw_alloc *dwa; struct bhndb_dw_rentry *dwr, *dwr_next; /* No window regions may still be held */ if (__builtin_popcount(br->dwa_freelist) != br->dwa_count) { device_printf(br->dev, "leaked %llu dynamic register regions\n", (unsigned long long) br->dwa_count - br->dwa_freelist); } /* Release resources allocated through our parent. */ bus_release_resources(br->parent_dev, br->res_spec, br->res); /* Clean up resource reservations */ for (size_t i = 0; i < br->dwa_count; i++) { dwa = &br->dw_alloc[i]; LIST_FOREACH_SAFE(dwr, &dwa->refs, dw_link, dwr_next) { LIST_REMOVE(dwr, dw_link); free(dwr, M_BHND); } } /* Release bus regions */ STAILQ_FOREACH_SAFE(region, &br->bus_regions, link, r_next) { STAILQ_REMOVE(&br->bus_regions, region, bhndb_region, link); free(region, M_BHND); } /* Release our resource managers */ rman_fini(&br->ht_mem_rman); rman_fini(&br->br_mem_rman); /* Free backing resource state structures */ free(br->res, M_BHND); free(br->res_spec, M_BHND); free(br->dw_alloc, M_BHND); } /** * Add a bus region entry to @p r for the given base @p addr and @p size. * * @param br The resource state to which the bus region entry will be added. * @param addr The base address of this region. * @param size The size of this region. * @param priority The resource priority to be assigned to allocations * made within this bus region. * @param static_regwin If available, a static register window mapping this * bus region entry. If not available, NULL. * * @retval 0 success * @retval non-zero if adding the bus region fails. */ int bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size, bhndb_priority_t priority, const struct bhndb_regwin *static_regwin) { struct bhndb_region *reg; /* Insert in the bus resource list */ reg = malloc(sizeof(*reg), M_BHND, M_NOWAIT); if (reg == NULL) return (ENOMEM); *reg = (struct bhndb_region) { .addr = addr, .size = size, .priority = priority, .static_regwin = static_regwin }; STAILQ_INSERT_HEAD(&br->bus_regions, reg, link); return (0); } /** * Find the maximum start and end limits of the register window mapping * resource @p r. * * If the memory range is not mapped by an existing dynamic or static register * window, ENOENT will be returned. * * @param br The resource state to search. * @param r The resource to search for in @p br. * @param addr The requested starting address. * @param size The requested size. * * @retval bhndb_region A region that fully contains the requested range. * @retval NULL If no mapping region can be found. */ int bhndb_find_resource_limits(struct bhndb_resources *br, struct resource *r, rman_res_t *start, rman_res_t *end) { struct bhndb_dw_alloc *dynamic; struct bhndb_region *sregion; /* Check for an enclosing dynamic register window */ if ((dynamic = bhndb_dw_find_resource(br, r))) { *start = dynamic->target; *end = dynamic->target + dynamic->win->win_size - 1; return (0); } /* Check for a static region */ sregion = bhndb_find_resource_region(br, rman_get_start(r), rman_get_size(r)); if (sregion != NULL && sregion->static_regwin != NULL) { *start = sregion->addr; *end = sregion->addr + sregion->size - 1; return (0); } /* Not found */ return (ENOENT); } /** * Find the bus region that maps @p size bytes at @p addr. * * @param br The resource state to search. * @param addr The requested starting address. * @param size The requested size. * * @retval bhndb_region A region that fully contains the requested range. * @retval NULL If no mapping region can be found. */ struct bhndb_region * bhndb_find_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size) { struct bhndb_region *region; STAILQ_FOREACH(region, &br->bus_regions, link) { /* Request must fit within the region's mapping */ if (addr < region->addr) continue; if (addr + size > region->addr + region->size) continue; return (region); } /* Not found */ return (NULL); } /** * Find the entry matching @p r in @p dwa's references, if any. * * @param dwa The dynamic window allocation to search * @param r The resource to search for in @p dwa. */ static struct bhndb_dw_rentry * bhndb_dw_find_resource_entry(struct bhndb_dw_alloc *dwa, struct resource *r) { struct bhndb_dw_rentry *rentry; LIST_FOREACH(rentry, &dwa->refs, dw_link) { struct resource *dw_res = rentry->dw_res; /* Match dev/rid/addr/size */ if (rman_get_device(dw_res) != rman_get_device(r) || rman_get_rid(dw_res) != rman_get_rid(r) || rman_get_start(dw_res) != rman_get_start(r) || rman_get_size(dw_res) != rman_get_size(r)) { continue; } /* Matching allocation found */ return (rentry); } return (NULL); } /** * Find the dynamic region allocated for @p r, if any. * * @param br The resource state to search. * @param r The resource to search for. * * @retval bhndb_dw_alloc The allocation record for @p r. * @retval NULL if no dynamic window is allocated for @p r. */ struct bhndb_dw_alloc * bhndb_dw_find_resource(struct bhndb_resources *br, struct resource *r) { struct bhndb_dw_alloc *dwa; for (size_t i = 0; i < br->dwa_count; i++) { dwa = &br->dw_alloc[i]; /* Skip free dynamic windows */ if (bhndb_dw_is_free(br, dwa)) continue; /* Matching allocation found? */ if (bhndb_dw_find_resource_entry(dwa, r) != NULL) return (dwa); } return (NULL); } /** * Find an existing dynamic window mapping @p size bytes * at @p addr. The window may or may not be free. * * @param br The resource state to search. * @param addr The requested starting address. * @param size The requested size. * * @retval bhndb_dw_alloc A window allocation that fully contains the requested * range. * @retval NULL If no mapping region can be found. */ struct bhndb_dw_alloc * bhndb_dw_find_mapping(struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size) { struct bhndb_dw_alloc *dwr; const struct bhndb_regwin *win; /* Search for an existing dynamic mapping of this address range. */ for (size_t i = 0; i < br->dwa_count; i++) { dwr = &br->dw_alloc[i]; win = dwr->win; /* Verify the range */ if (addr < dwr->target) continue; if (addr + size > dwr->target + win->win_size) continue; /* Found a usable mapping */ return (dwr); } /* not found */ return (NULL); } /** * Retain a reference to @p dwa for use by @p res. * * @param br The resource state owning @p dwa. * @param dwa The allocation record to be retained. * @param res The resource that will own a reference to @p dwa. * * @retval 0 success * @retval ENOMEM Failed to allocate a new reference structure. */ int bhndb_dw_retain(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, struct resource *res) { struct bhndb_dw_rentry *rentry; KASSERT(bhndb_dw_find_resource_entry(dwa, res) == NULL, ("double-retain of dynamic window for same resource")); /* Insert a reference entry; we use M_NOWAIT to allow use from * within a non-sleepable lock */ rentry = malloc(sizeof(*rentry), M_BHND, M_NOWAIT); if (rentry == NULL) return (ENOMEM); rentry->dw_res = res; LIST_INSERT_HEAD(&dwa->refs, rentry, dw_link); /* Update the free list */ br->dwa_freelist &= ~(1 << (dwa->rnid)); return (0); } /** * Release a reference to @p dwa previously retained by @p res. If the * reference count of @p dwa reaches zero, it will be added to the * free list. * * @param br The resource state owning @p dwa. * @param dwa The allocation record to be released. * @param res The resource that currently owns a reference to @p dwa. */ void bhndb_dw_release(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, struct resource *r) { struct bhndb_dw_rentry *rentry; /* Find the rentry */ rentry = bhndb_dw_find_resource_entry(dwa, r); KASSERT(rentry != NULL, ("over release of resource entry")); LIST_REMOVE(rentry, dw_link); free(rentry, M_BHND); /* If this was the last reference, update the free list */ if (LIST_EMPTY(&dwa->refs)) br->dwa_freelist |= (1 << (dwa->rnid)); } /** * Attempt to set (or reset) the target address of @p dwa to map @p size bytes * at @p addr. * * This will apply any necessary window alignment and verify that * the window is capable of mapping the requested range prior to modifying * therecord. * * @param dev The device on which to issue the BHNDB_SET_WINDOW_ADDR() request. * @param br The resource state owning @p dwa. * @param dwa The allocation record to be configured. * @param addr The address to be mapped via @p dwa. * @param size The number of bytes to be mapped at @p addr. * * @retval 0 success * @retval non-zero no usable register window available. */ int bhndb_dw_set_addr(device_t dev, struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size) { const struct bhndb_regwin *rw; bus_addr_t offset; int error; rw = dwa->win; KASSERT(bhndb_dw_is_free(br, dwa), ("attempting to set the target address on an in-use window")); /* Page-align the target address */ offset = addr % rw->win_size; dwa->target = addr - offset; /* Verify that the window is large enough for the full target */ if (rw->win_size - offset < size) return (ENOMEM); /* Update the window target */ error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); if (error) { dwa->target = 0x0; return (error); } return (0); } /** * Return the count of @p type register windows in @p table. * * @param table The table to search. * @param type The required window type, or BHNDB_REGWIN_T_INVALID to * count all register window types. */ size_t bhndb_regwin_count(const struct bhndb_regwin *table, bhndb_regwin_type_t type) { const struct bhndb_regwin *rw; size_t count; count = 0; for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { if (type == BHNDB_REGWIN_T_INVALID || rw->win_type == type) count++; } return (count); } /** * Search @p table for the first window with the given @p type. * * @param table The table to search. * @param type The required window type. * @param min_size The minimum window size. * * @retval bhndb_regwin The first matching window. * @retval NULL If no window of the requested type could be found. */ const struct bhndb_regwin * bhndb_regwin_find_type(const struct bhndb_regwin *table, bhndb_regwin_type_t type, bus_size_t min_size) { const struct bhndb_regwin *rw; for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { if (rw->win_type == type && rw->win_size >= min_size) return (rw); } return (NULL); } /** * Search @p windows for the first matching core window. * * @param table The table to search. * @param class The required core class. * @param unit The required core unit, or -1. * @param port_type The required port type. * @param port The required port. * @param region The required region. * * @retval bhndb_regwin The first matching window. * @retval NULL If no matching window was found. */ const struct bhndb_regwin * bhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, u_int region) { const struct bhndb_regwin *rw; for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { if (rw->win_type != BHNDB_REGWIN_T_CORE) continue; if (rw->d.core.class != class) continue; if (unit != -1 && rw->d.core.unit != unit) continue; if (rw->d.core.port_type != port_type) continue; if (rw->d.core.port != port) continue; if (rw->d.core.region != region) continue; return (rw); } return (NULL); } /** * Search @p windows for the best available window of at least @p min_size. * * Search order: * - BHND_REGWIN_T_CORE * - BHND_REGWIN_T_DYN * * @param table The table to search. * @param class The required core class. * @param unit The required core unit, or -1. * @param port_type The required port type. * @param port The required port. * @param region The required region. * @param min_size The minimum window size. * * @retval bhndb_regwin The first matching window. * @retval NULL If no matching window was found. */ const struct bhndb_regwin * bhndb_regwin_find_best(const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, u_int region, bus_size_t min_size) { const struct bhndb_regwin *rw; /* Prefer a fixed core mapping */ rw = bhndb_regwin_find_core(table, class, unit, port_type, port, region); if (rw != NULL) return (rw); /* Fall back on a generic dynamic window */ return (bhndb_regwin_find_type(table, BHNDB_REGWIN_T_DYN, min_size)); } /** * Return true if @p regw defines a static port register window, and * the mapped port is actually defined on @p dev. * * @param regw A register window to match against. * @param dev A bhnd(4) bus device. */ bool bhndb_regwin_matches_device(const struct bhndb_regwin *regw, device_t dev) { /* Only core windows are supported */ if (regw->win_type != BHNDB_REGWIN_T_CORE) return (false); /* Device class must match */ if (bhnd_get_class(dev) != regw->d.core.class) return (false); /* Device unit must match */ if (bhnd_get_core_unit(dev) != regw->d.core.unit) return (false); /* The regwin port/region must be defined. */ if (!bhnd_is_region_valid(dev, regw->d.core.port_type, regw->d.core.port, regw->d.core.region)) { return (false); } /* Matches */ return (true); } /** * Search for a core resource priority descriptor in @p table that matches * @p device. * * @param table The table to search. * @param device A bhnd(4) bus device. */ const struct bhndb_hw_priority * bhndb_hw_priority_find_device(const struct bhndb_hw_priority *table, device_t device) { - const struct bhndb_hw_priority *hp; + const struct bhndb_hw_priority *hp; + struct bhnd_core_info ci; + ci = bhnd_get_core_info(device); + for (hp = table; hp->ports != NULL; hp++) { - if (bhnd_device_matches(device, &hp->match)) + if (bhnd_core_matches(&ci, &hp->match)) return (hp); } /* not found */ return (NULL); } Index: head/sys/dev/bhnd/cores/chipc/chipc.c =================================================================== --- head/sys/dev/bhnd/cores/chipc/chipc.c (revision 300627) +++ head/sys/dev/bhnd/cores/chipc/chipc.c (revision 300628) @@ -1,1260 +1,1238 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2016 Michael Zhilin * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Broadcom ChipCommon driver. * * With the exception of some very early chipsets, the ChipCommon core * has been included in all HND SoCs and chipsets based on the siba(4) * and bcma(4) interconnects, providing a common interface to chipset * identification, bus enumeration, UARTs, clocks, watchdog interrupts, GPIO, * flash, etc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chipcreg.h" #include "chipcvar.h" #include "chipc_private.h" devclass_t bhnd_chipc_devclass; /**< bhnd(4) chipcommon device class */ static struct bhnd_device_quirk chipc_quirks[]; -static struct bhnd_chip_quirk chipc_chip_quirks[]; /* Supported device identifiers */ static const struct bhnd_device chipc_devices[] = { - BHND_DEVICE(CC, "CC", chipc_quirks, chipc_chip_quirks), + BHND_DEVICE(CC, NULL, chipc_quirks), BHND_DEVICE_END }; /* Device quirks table */ static struct bhnd_device_quirk chipc_quirks[] = { - { BHND_HWREV_GTE (32), CHIPC_QUIRK_SUPPORTS_SPROM }, - { BHND_HWREV_GTE (35), CHIPC_QUIRK_SUPPORTS_CAP_EXT }, - { BHND_HWREV_EQ (38), CHIPC_QUIRK_4706_NFLASH }, /*BCM5357 ?*/ - { BHND_HWREV_GTE (49), CHIPC_QUIRK_IPX_OTPLAYOUT_SIZE }, + /* core revision quirks */ + BHND_CORE_QUIRK (HWREV_GTE(32), CHIPC_QUIRK_SUPPORTS_SPROM), + BHND_CORE_QUIRK (HWREV_GTE(35), CHIPC_QUIRK_SUPPORTS_CAP_EXT), + BHND_CORE_QUIRK (HWREV_GTE(49), CHIPC_QUIRK_IPX_OTPLAYOUT_SIZE), - BHND_DEVICE_QUIRK_END -}; + /* 4706 variant quirks */ + BHND_CORE_QUIRK (HWREV_EQ (38), CHIPC_QUIRK_4706_NFLASH), /* BCM5357? */ + BHND_CHIP_QUIRK (4706, HWREV_ANY, CHIPC_QUIRK_4706_NFLASH), -/* Chip-specific quirks table */ -static struct bhnd_chip_quirk chipc_chip_quirks[] = { - /* 4331 12x9 packages */ - {{ BHND_CHIP_IP(4331, 4331TN) }, - CHIPC_QUIRK_4331_GPIO2_5_MUX_SPROM - }, - {{ BHND_CHIP_IP(4331, 4331TNA0) }, - CHIPC_QUIRK_4331_GPIO2_5_MUX_SPROM - }, + /* 4331 quirks*/ + BHND_CHIP_QUIRK (4331, HWREV_ANY, CHIPC_QUIRK_4331_EXTPA_MUX_SPROM), + BHND_PKG_QUIRK (4331, TN, CHIPC_QUIRK_4331_GPIO2_5_MUX_SPROM), + BHND_PKG_QUIRK (4331, TNA0, CHIPC_QUIRK_4331_GPIO2_5_MUX_SPROM), + BHND_PKG_QUIRK (4331, TT, CHIPC_QUIRK_4331_EXTPA2_MUX_SPROM), - /* 4331 12x12 packages */ - {{ BHND_CHIP_IPR(4331, 4331TT, HWREV_GTE(1)) }, - CHIPC_QUIRK_4331_EXTPA2_MUX_SPROM - }, + /* 4360 quirks */ + BHND_CHIP_QUIRK (4352, HWREV_LTE(2), CHIPC_QUIRK_4360_FEM_MUX_SPROM), + BHND_CHIP_QUIRK (43460, HWREV_LTE(2), CHIPC_QUIRK_4360_FEM_MUX_SPROM), + BHND_CHIP_QUIRK (43462, HWREV_LTE(2), CHIPC_QUIRK_4360_FEM_MUX_SPROM), + BHND_CHIP_QUIRK (43602, HWREV_LTE(2), CHIPC_QUIRK_4360_FEM_MUX_SPROM), - /* 4331 (all packages/revisions) */ - {{ BHND_CHIP_ID(4331) }, - CHIPC_QUIRK_4331_EXTPA_MUX_SPROM - }, - - /* 4360 family (all revs <= 2) */ - {{ BHND_CHIP_IR(4352, HWREV_LTE(2)) }, - CHIPC_QUIRK_4360_FEM_MUX_SPROM }, - {{ BHND_CHIP_IR(43460, HWREV_LTE(2)) }, - CHIPC_QUIRK_4360_FEM_MUX_SPROM }, - {{ BHND_CHIP_IR(43462, HWREV_LTE(2)) }, - CHIPC_QUIRK_4360_FEM_MUX_SPROM }, - {{ BHND_CHIP_IR(43602, HWREV_LTE(2)) }, - CHIPC_QUIRK_4360_FEM_MUX_SPROM }, - - /* BCM4706 */ - {{ BHND_CHIP_ID(4306) }, - CHIPC_QUIRK_4706_NFLASH }, - - BHND_CHIP_QUIRK_END + BHND_DEVICE_QUIRK_END }; static int chipc_try_activate_resource( struct chipc_softc *sc, device_t child, int type, int rid, struct resource *r, bool req_direct); static int chipc_read_caps(struct chipc_softc *sc, struct chipc_caps *caps); static int chipc_nvram_attach(struct chipc_softc *sc); static bhnd_nvram_src_t chipc_nvram_identify(struct chipc_softc *sc); static bool chipc_should_enable_sprom( struct chipc_softc *sc); static int chipc_init_rman(struct chipc_softc *sc); static void chipc_free_rman(struct chipc_softc *sc); static struct rman *chipc_get_rman(struct chipc_softc *sc, int type); /* quirk and capability flag convenience macros */ #define CHIPC_QUIRK(_sc, _name) \ ((_sc)->quirks & CHIPC_QUIRK_ ## _name) #define CHIPC_CAP(_sc, _name) \ ((_sc)->caps._name) #define CHIPC_ASSERT_QUIRK(_sc, name) \ KASSERT(CHIPC_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set")) #define CHIPC_ASSERT_CAP(_sc, name) \ KASSERT(CHIPC_CAP((_sc), name), ("capability " __STRING(_name) " not set")) static int chipc_probe(device_t dev) { const struct bhnd_device *id; id = bhnd_device_lookup(dev, chipc_devices, sizeof(chipc_devices[0])); if (id == NULL) return (ENXIO); bhnd_set_default_core_desc(dev); return (BUS_PROBE_DEFAULT); } static int chipc_attach(device_t dev) { struct chipc_softc *sc; bhnd_addr_t enum_addr; uint32_t ccid_reg; uint8_t chip_type; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, chipc_devices, sizeof(chipc_devices[0])); sc->sprom_refcnt = 0; CHIPC_LOCK_INIT(sc); STAILQ_INIT(&sc->mem_regions); /* Set up resource management */ if ((error = chipc_init_rman(sc))) { device_printf(sc->dev, "failed to initialize chipc resource state: %d\n", error); goto failed; } /* Allocate the region containing our core registers */ if ((sc->core_region = chipc_find_region_by_rid(sc, 0)) == NULL) { error = ENXIO; goto failed; } error = chipc_retain_region(sc, sc->core_region, RF_ALLOCATED|RF_ACTIVE); if (error) { sc->core_region = NULL; goto failed; } else { sc->core = sc->core_region->cr_res; } /* Fetch our chipset identification data */ ccid_reg = bhnd_bus_read_4(sc->core, CHIPC_ID); chip_type = CHIPC_GET_BITS(ccid_reg, CHIPC_ID_BUS); switch (chip_type) { case BHND_CHIPTYPE_SIBA: /* enumeration space starts at the ChipCommon register base. */ enum_addr = rman_get_start(sc->core->res); break; case BHND_CHIPTYPE_BCMA: case BHND_CHIPTYPE_BCMA_ALT: enum_addr = bhnd_bus_read_4(sc->core, CHIPC_EROMPTR); break; default: device_printf(dev, "unsupported chip type %hhu\n", chip_type); error = ENODEV; goto failed; } sc->ccid = bhnd_parse_chipid(ccid_reg, enum_addr); /* Fetch and parse capability register(s) */ if ((error = chipc_read_caps(sc, &sc->caps))) goto failed; if (bootverbose) chipc_print_caps(sc->dev, &sc->caps); /* Identify NVRAM source and add child device. */ sc->nvram_src = chipc_nvram_identify(sc); if ((error = chipc_nvram_attach(sc))) goto failed; /* Standard bus probe */ if ((error = bus_generic_attach(dev))) goto failed; return (0); failed: if (sc->core_region != NULL) { chipc_release_region(sc, sc->core_region, RF_ALLOCATED|RF_ACTIVE); } chipc_free_rman(sc); CHIPC_LOCK_DESTROY(sc); return (error); } static int chipc_detach(device_t dev) { struct chipc_softc *sc; int error; sc = device_get_softc(dev); if ((error = bus_generic_detach(dev))) return (error); chipc_release_region(sc, sc->core_region, RF_ALLOCATED|RF_ACTIVE); chipc_free_rman(sc); bhnd_sprom_fini(&sc->sprom); CHIPC_LOCK_DESTROY(sc); return (0); } /* Read and parse chipc capabilities */ static int chipc_read_caps(struct chipc_softc *sc, struct chipc_caps *caps) { uint32_t cap_reg; uint32_t cap_ext_reg; uint32_t regval; /* Fetch cap registers */ cap_reg = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); cap_ext_reg = 0; if (CHIPC_QUIRK(sc, SUPPORTS_CAP_EXT)) cap_ext_reg = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES_EXT); /* Extract values */ caps->num_uarts = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_NUM_UART); caps->mipseb = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_MIPSEB); caps->uart_gpio = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_UARTGPIO); caps->uart_clock = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_UCLKSEL); caps->extbus_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_EXTBUS); caps->power_control = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PWR_CTL); caps->jtag_master = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_JTAGP); caps->pll_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_PLL); caps->backplane_64 = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_BKPLN64); caps->boot_rom = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_ROM); caps->pmu = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PMU); caps->eci = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_ECI); caps->sprom = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_SPROM); caps->otp_size = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_OTP_SIZE); caps->seci = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_SECI); caps->gsio = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_GSIO); caps->aob = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_AOB); /* Fetch OTP size for later IPX controller revisions */ if (CHIPC_QUIRK(sc, IPX_OTPLAYOUT_SIZE)) { regval = bhnd_bus_read_4(sc->core, CHIPC_OTPLAYOUT); caps->otp_size = CHIPC_GET_BITS(regval, CHIPC_OTPL_SIZE); } - /* Determine flash type and paramters */ + /* Determine flash type and parameters */ caps->cfi_width = 0; switch (CHIPC_GET_BITS(cap_reg, CHIPC_CAP_FLASH)) { case CHIPC_CAP_SFLASH_ST: caps->flash_type = CHIPC_SFLASH_ST; break; case CHIPC_CAP_SFLASH_AT: caps->flash_type = CHIPC_SFLASH_AT; break; case CHIPC_CAP_NFLASH: caps->flash_type = CHIPC_NFLASH; break; case CHIPC_CAP_PFLASH: caps->flash_type = CHIPC_PFLASH_CFI; /* determine cfi width */ regval = bhnd_bus_read_4(sc->core, CHIPC_FLASH_CFG); if (CHIPC_GET_FLAG(regval, CHIPC_FLASH_CFG_DS)) caps->cfi_width = 2; else caps->cfi_width = 1; break; case CHIPC_CAP_FLASH_NONE: caps->flash_type = CHIPC_FLASH_NONE; break; } /* Handle 4706_NFLASH fallback */ if (CHIPC_QUIRK(sc, 4706_NFLASH) && CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_4706_NFLASH)) { caps->flash_type = CHIPC_NFLASH_4706; } return (0); } /** * If supported, add an appropriate NVRAM child device. */ static int chipc_nvram_attach(struct chipc_softc *sc) { device_t nvram_dev; rman_res_t start; int error; switch (sc->nvram_src) { case BHND_NVRAM_SRC_OTP: // TODO OTP support device_printf(sc->dev, "OTP nvram source unsupported\n"); return (0); case BHND_NVRAM_SRC_SPROM: /* Add OTP/SPROM device */ nvram_dev = BUS_ADD_CHILD(sc->dev, 0, "bhnd_nvram", -1); if (nvram_dev == NULL) { device_printf(sc->dev, "failed to add NVRAM device\n"); return (ENXIO); } start = rman_get_start(sc->core->res) + CHIPC_SPROM_OTP; error = bus_set_resource(nvram_dev, SYS_RES_MEMORY, 0, start, CHIPC_SPROM_OTP_SIZE); return (error); case BHND_NVRAM_SRC_FLASH: // TODO flash support device_printf(sc->dev, "flash nvram source unsupported\n"); return (0); case BHND_NVRAM_SRC_UNKNOWN: /* Handled externally */ return (0); default: device_printf(sc->dev, "invalid nvram source: %u\n", sc->nvram_src); return (ENXIO); } } /** * Determine the NVRAM data source for this device. * * @param sc chipc driver state. */ static bhnd_nvram_src_t chipc_nvram_identify(struct chipc_softc *sc) { uint32_t srom_ctrl; /* Very early devices vend SPROM/OTP/CIS (if at all) via the * host bridge interface instead of ChipCommon. */ if (!CHIPC_QUIRK(sc, SUPPORTS_SPROM)) return (BHND_NVRAM_SRC_UNKNOWN); /* * Later chipset revisions standardized the SPROM capability flags and * register interfaces. * * We check for hardware presence in order of precedence. For example, * SPROM is is always used in preference to internal OTP if found. */ if (CHIPC_CAP(sc, sprom)) { srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL); if (srom_ctrl & CHIPC_SRC_PRESENT) return (BHND_NVRAM_SRC_SPROM); } /* Check for OTP */ if (CHIPC_CAP(sc, otp_size) != 0) return (BHND_NVRAM_SRC_OTP); /* Check for flash */ if (CHIPC_CAP(sc, flash_type) != CHIPC_FLASH_NONE) return (BHND_NVRAM_SRC_FLASH); /* No NVRAM hardware capability declared */ return (BHND_NVRAM_SRC_UNKNOWN); } static int chipc_suspend(device_t dev) { return (bus_generic_suspend(dev)); } static int chipc_resume(device_t dev) { return (bus_generic_resume(dev)); } static void chipc_probe_nomatch(device_t dev, device_t child) { struct resource_list *rl; const char *name; name = device_get_name(child); if (name == NULL) name = "unknown device"; device_printf(dev, "<%s> at", name); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); } printf(" (no driver attached)\n"); } static int chipc_print_child(device_t dev, device_t child) { struct resource_list *rl; int retval = 0; retval += bus_print_child_header(dev, child); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); } retval += bus_print_child_domain(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } static int chipc_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) { if (buflen == 0) return (EOVERFLOW); *buf = '\0'; return (0); } static int chipc_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { if (buflen == 0) return (EOVERFLOW); *buf = '\0'; return (ENXIO); } static device_t chipc_add_child(device_t dev, u_int order, const char *name, int unit) { struct chipc_devinfo *dinfo; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (NULL); dinfo = malloc(sizeof(struct chipc_devinfo), M_BHND, M_NOWAIT); if (dinfo == NULL) { device_delete_child(dev, child); return (NULL); } resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); return (child); } static void chipc_child_deleted(device_t dev, device_t child) { struct chipc_devinfo *dinfo = device_get_ivars(child); if (dinfo != NULL) { resource_list_free(&dinfo->resources); free(dinfo, M_BHND); } device_set_ivars(child, NULL); } static struct resource_list * chipc_get_resource_list(device_t dev, device_t child) { struct chipc_devinfo *dinfo = device_get_ivars(child); return (&dinfo->resources); } /* Allocate region records for the given port, and add the port's memory * range to the mem_rman */ static int chipc_rman_init_regions (struct chipc_softc *sc, bhnd_port_type type, u_int port) { struct chipc_region *cr; rman_res_t start, end; u_int num_regions; int error; num_regions = bhnd_get_region_count(sc->dev, port, port); for (u_int region = 0; region < num_regions; region++) { /* Allocate new region record */ cr = chipc_alloc_region(sc, type, port, region); if (cr == NULL) return (ENODEV); /* Can't manage regions that cannot be allocated */ if (cr->cr_rid < 0) { BHND_DEBUG_DEV(sc->dev, "no rid for chipc region " "%s%u.%u", bhnd_port_type_name(type), port, region); chipc_free_region(sc, cr); continue; } /* Add to rman's managed range */ start = cr->cr_addr; end = cr->cr_end; if ((error = rman_manage_region(&sc->mem_rman, start, end))) { chipc_free_region(sc, cr); return (error); } /* Add to region list */ STAILQ_INSERT_TAIL(&sc->mem_regions, cr, cr_link); } return (0); } /* Initialize memory state for all chipc port regions */ static int chipc_init_rman(struct chipc_softc *sc) { u_int num_ports; int error; /* Port types for which we'll register chipc_region mappings */ bhnd_port_type types[] = { BHND_PORT_DEVICE }; /* Initialize resource manager */ sc->mem_rman.rm_start = 0; sc->mem_rman.rm_end = BUS_SPACE_MAXADDR; sc->mem_rman.rm_type = RMAN_ARRAY; sc->mem_rman.rm_descr = "ChipCommon Device Memory"; if ((error = rman_init(&sc->mem_rman))) { device_printf(sc->dev, "could not initialize mem_rman: %d\n", error); return (error); } /* Populate per-port-region state */ for (u_int i = 0; i < nitems(types); i++) { num_ports = bhnd_get_port_count(sc->dev, types[i]); for (u_int port = 0; port < num_ports; port++) { error = chipc_rman_init_regions(sc, types[i], port); if (error) { device_printf(sc->dev, "region init failed for %s%u: %d\n", bhnd_port_type_name(types[i]), port, error); goto failed; } } } return (0); failed: chipc_free_rman(sc); return (error); } /* Free memory management state */ static void chipc_free_rman(struct chipc_softc *sc) { struct chipc_region *cr, *cr_next; STAILQ_FOREACH_SAFE(cr, &sc->mem_regions, cr_link, cr_next) chipc_free_region(sc, cr); rman_fini(&sc->mem_rman); } /** * Return the rman instance for a given resource @p type, if any. * * @param sc The chipc device state. * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) */ static struct rman * chipc_get_rman(struct chipc_softc *sc, int type) { switch (type) { case SYS_RES_MEMORY: return (&sc->mem_rman); case SYS_RES_IRQ: /* IRQs can be used with RF_SHAREABLE, so we don't perform * any local proxying of resource requests. */ return (NULL); default: return (NULL); }; } static struct resource * chipc_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct chipc_softc *sc; struct chipc_region *cr; struct resource_list_entry *rle; struct resource *rv; struct rman *rm; int error; bool passthrough, isdefault; sc = device_get_softc(dev); passthrough = (device_get_parent(child) != dev); isdefault = RMAN_IS_DEFAULT_RANGE(start, end); rle = NULL; /* Fetch the resource manager, delegate request if necessary */ rm = chipc_get_rman(sc, type); if (rm == NULL) { /* Requested resource type is delegated to our parent */ rv = bus_generic_rl_alloc_resource(dev, child, type, rid, start, end, count, flags); return (rv); } /* Populate defaults */ if (!passthrough && isdefault) { /* Fetch the resource list entry. */ rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), type, *rid); if (rle == NULL) { device_printf(dev, "default resource %#x type %d for child %s " "not found\n", *rid, type, device_get_nameunit(child)); return (NULL); } if (rle->res != NULL) { device_printf(dev, "resource entry %#x type %d for child %s is busy\n", *rid, type, device_get_nameunit(child)); return (NULL); } start = rle->start; end = rle->end; count = ulmax(count, rle->count); } /* Locate a mapping region */ if ((cr = chipc_find_region(sc, start, end)) == NULL) { /* Resource requests outside our shared port regions can be * delegated to our parent. */ rv = bus_generic_rl_alloc_resource(dev, child, type, rid, start, end, count, flags); return (rv); } /* Try to retain a region reference */ if ((error = chipc_retain_region(sc, cr, RF_ALLOCATED))) { CHIPC_UNLOCK(sc); return (NULL); } /* Make our rman reservation */ rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, child); if (rv == NULL) { chipc_release_region(sc, cr, RF_ALLOCATED); return (NULL); } rman_set_rid(rv, *rid); /* Activate */ if (flags & RF_ACTIVE) { error = bus_activate_resource(child, type, *rid, rv); if (error) { device_printf(dev, "failed to activate entry %#x type %d for " "child %s: %d\n", *rid, type, device_get_nameunit(child), error); chipc_release_region(sc, cr, RF_ALLOCATED); rman_release_resource(rv); return (NULL); } } /* Update child's resource list entry */ if (rle != NULL) { rle->res = rv; rle->start = rman_get_start(rv); rle->end = rman_get_end(rv); rle->count = rman_get_size(rv); } return (rv); } static int chipc_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct chipc_softc *sc; struct chipc_region *cr; struct rman *rm; int error; sc = device_get_softc(dev); /* Handled by parent bus? */ rm = chipc_get_rman(sc, type); if (rm == NULL || !rman_is_region_manager(r, rm)) { return (bus_generic_rl_release_resource(dev, child, type, rid, r)); } /* Locate the mapping region */ cr = chipc_find_region(sc, rman_get_start(r), rman_get_end(r)); if (cr == NULL) return (EINVAL); /* Deactivate resources */ if (rman_get_flags(r) & RF_ACTIVE) { error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); if (error) return (error); } if ((error = rman_release_resource(r))) return (error); /* Drop allocation reference */ chipc_release_region(sc, cr, RF_ALLOCATED); return (0); } static int chipc_adjust_resource(device_t dev, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end) { struct chipc_softc *sc; struct chipc_region *cr; struct rman *rm; sc = device_get_softc(dev); /* Handled by parent bus? */ rm = chipc_get_rman(sc, type); if (rm == NULL || !rman_is_region_manager(r, rm)) { return (bus_generic_adjust_resource(dev, child, type, r, start, end)); } /* The range is limited to the existing region mapping */ cr = chipc_find_region(sc, rman_get_start(r), rman_get_end(r)); if (cr == NULL) return (EINVAL); if (end <= start) return (EINVAL); if (start < cr->cr_addr || end > cr->cr_end) return (EINVAL); /* Range falls within the existing region */ return (rman_adjust_resource(r, start, end)); } /** * Retain an RF_ACTIVE reference to the region mapping @p r, and * configure @p r with its subregion values. * * @param sc Driver instance state. * @param child Requesting child device. * @param type resource type of @p r. * @param rid resource id of @p r * @param r resource to be activated. * @param req_direct If true, failure to allocate a direct bhnd resource * will be treated as an error. If false, the resource will not be marked * as RF_ACTIVE if bhnd direct resource allocation fails. */ static int chipc_try_activate_resource(struct chipc_softc *sc, device_t child, int type, int rid, struct resource *r, bool req_direct) { struct rman *rm; struct chipc_region *cr; bhnd_size_t cr_offset; rman_res_t r_start, r_end, r_size; int error; rm = chipc_get_rman(sc, type); if (rm == NULL || !rman_is_region_manager(r, rm)) return (EINVAL); r_start = rman_get_start(r); r_end = rman_get_end(r); r_size = rman_get_size(r); /* Find the corresponding chipc region */ cr = chipc_find_region(sc, r_start, r_end); if (cr == NULL) return (EINVAL); /* Calculate subregion offset within the chipc region */ cr_offset = r_start - cr->cr_addr; /* Retain (and activate, if necessary) the chipc region */ if ((error = chipc_retain_region(sc, cr, RF_ACTIVE))) return (error); /* Configure child resource with its subregion values. */ if (cr->cr_res->direct) { error = chipc_init_child_resource(r, cr->cr_res->res, cr_offset, r_size); if (error) goto cleanup; /* Mark active */ if ((error = rman_activate_resource(r))) goto cleanup; } else if (req_direct) { error = ENOMEM; goto cleanup; } return (0); cleanup: chipc_release_region(sc, cr, RF_ACTIVE); return (error); } static int chipc_activate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { struct chipc_softc *sc; struct rman *rm; int error; sc = device_get_softc(dev); /* Delegate non-locally managed resources to parent */ rm = chipc_get_rman(sc, type); if (rm == NULL || !rman_is_region_manager(r->res, rm)) { return (bhnd_bus_generic_activate_resource(dev, child, type, rid, r)); } /* Try activating the chipc region resource */ error = chipc_try_activate_resource(sc, child, type, rid, r->res, false); if (error) return (error); /* Mark the child resource as direct according to the returned resource * state */ if (rman_get_flags(r->res) & RF_ACTIVE) r->direct = true; return (0); } static int chipc_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct chipc_softc *sc; struct rman *rm; sc = device_get_softc(dev); /* Delegate non-locally managed resources to parent */ rm = chipc_get_rman(sc, type); if (rm == NULL || !rman_is_region_manager(r, rm)) { return (bus_generic_activate_resource(dev, child, type, rid, r)); } /* Try activating the chipc region-based resource */ return (chipc_try_activate_resource(sc, child, type, rid, r, true)); } /** * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). */ static int chipc_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct chipc_softc *sc; struct chipc_region *cr; struct rman *rm; int error; sc = device_get_softc(dev); /* Handled by parent bus? */ rm = chipc_get_rman(sc, type); if (rm == NULL || !rman_is_region_manager(r, rm)) { return (bus_generic_deactivate_resource(dev, child, type, rid, r)); } /* Find the corresponding chipc region */ cr = chipc_find_region(sc, rman_get_start(r), rman_get_end(r)); if (cr == NULL) return (EINVAL); /* Mark inactive */ if ((error = rman_deactivate_resource(r))) return (error); /* Drop associated RF_ACTIVE reference */ chipc_release_region(sc, cr, RF_ACTIVE); return (0); } /** * Examine bus state and make a best effort determination of whether it's * likely safe to enable the muxed SPROM pins. * * On devices that do not use SPROM pin muxing, always returns true. * * @param sc chipc driver state. */ static bool chipc_should_enable_sprom(struct chipc_softc *sc) { device_t *devs; device_t hostb; device_t parent; int devcount; int error; bool result; mtx_assert(&Giant, MA_OWNED); /* for newbus */ /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return (true); parent = device_get_parent(sc->dev); hostb = bhnd_find_hostb_device(parent); if ((error = device_get_children(parent, &devs, &devcount))) return (false); /* Reject any active devices other than ChipCommon, or the * host bridge (if any). */ result = true; for (int i = 0; i < devcount; i++) { if (devs[i] == hostb || devs[i] == sc->dev) continue; if (!device_is_attached(devs[i])) continue; if (device_is_suspended(devs[i])) continue; /* Active device; assume SPROM is busy */ result = false; break; } free(devs, M_TEMP); return (result); } /** * If required by this device, enable access to the SPROM. * * @param sc chipc driver state. */ static int chipc_enable_sprom_pins(device_t dev) { struct chipc_softc *sc; uint32_t cctrl; int error; sc = device_get_softc(dev); /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return (0); /* Make sure we're holding Giant for newbus */ mtx_lock(&Giant); CHIPC_LOCK(sc); /* Already enabled? */ if (sc->sprom_refcnt >= 1) { error = 0; goto finished; } /* Check whether bus is busy */ if (!chipc_should_enable_sprom(sc)) { error = EBUSY; goto finished; } cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); /* 4331 devices */ if (CHIPC_QUIRK(sc, 4331_EXTPA_MUX_SPROM)) { cctrl &= ~CHIPC_CCTRL4331_EXTPA_EN; if (CHIPC_QUIRK(sc, 4331_GPIO2_5_MUX_SPROM)) cctrl &= ~CHIPC_CCTRL4331_EXTPA_ON_GPIO2_5; if (CHIPC_QUIRK(sc, 4331_EXTPA2_MUX_SPROM)) cctrl &= ~CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); error = 0; goto finished; } /* 4360 devices */ if (CHIPC_QUIRK(sc, 4360_FEM_MUX_SPROM)) { /* Unimplemented */ } /* Refuse to proceed on unsupported devices with muxed SPROM pins */ device_printf(sc->dev, "muxed sprom lines on unrecognized device\n"); error = ENXIO; finished: /* Bump the reference count */ if (error == 0) sc->sprom_refcnt++; CHIPC_UNLOCK(sc); mtx_unlock(&Giant); return (error); } /** * If required by this device, revert any GPIO/pin configuration applied * to allow SPROM access. * * @param sc chipc driver state. */ static void chipc_disable_sprom_pins(device_t dev) { struct chipc_softc *sc; uint32_t cctrl; sc = device_get_softc(dev); /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return; CHIPC_LOCK(sc); /* Check reference count, skip disable if in-use. */ KASSERT(sc->sprom_refcnt > 0, ("sprom refcnt overrelease")); sc->sprom_refcnt--; if (sc->sprom_refcnt > 0) goto finished; cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); /* 4331 devices */ if (CHIPC_QUIRK(sc, 4331_EXTPA_MUX_SPROM)) { cctrl |= CHIPC_CCTRL4331_EXTPA_EN; if (CHIPC_QUIRK(sc, 4331_GPIO2_5_MUX_SPROM)) cctrl |= CHIPC_CCTRL4331_EXTPA_ON_GPIO2_5; if (CHIPC_QUIRK(sc, 4331_EXTPA2_MUX_SPROM)) cctrl |= CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); goto finished; } /* 4360 devices */ if (CHIPC_QUIRK(sc, 4360_FEM_MUX_SPROM)) { /* Unimplemented */ } finished: CHIPC_UNLOCK(sc); } static bhnd_nvram_src_t chipc_nvram_src(device_t dev) { struct chipc_softc *sc = device_get_softc(dev); return (sc->nvram_src); } static void chipc_write_chipctrl(device_t dev, uint32_t value, uint32_t mask) { struct chipc_softc *sc; uint32_t cctrl; sc = device_get_softc(dev); CHIPC_LOCK(sc); cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); cctrl = (cctrl & ~mask) | (value | mask); bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); CHIPC_UNLOCK(sc); } static device_method_t chipc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, chipc_probe), DEVMETHOD(device_attach, chipc_attach), DEVMETHOD(device_detach, chipc_detach), DEVMETHOD(device_suspend, chipc_suspend), DEVMETHOD(device_resume, chipc_resume), /* Bus interface */ DEVMETHOD(bus_probe_nomatch, chipc_probe_nomatch), DEVMETHOD(bus_print_child, chipc_print_child), DEVMETHOD(bus_child_pnpinfo_str, chipc_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, chipc_child_location_str), DEVMETHOD(bus_add_child, chipc_add_child), DEVMETHOD(bus_child_deleted, chipc_child_deleted), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_alloc_resource, chipc_alloc_resource), DEVMETHOD(bus_release_resource, chipc_release_resource), DEVMETHOD(bus_adjust_resource, chipc_adjust_resource), DEVMETHOD(bus_activate_resource, chipc_activate_resource), DEVMETHOD(bus_deactivate_resource, chipc_deactivate_resource), DEVMETHOD(bus_get_resource_list, chipc_get_resource_list), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_config_intr, bus_generic_config_intr), DEVMETHOD(bus_bind_intr, bus_generic_bind_intr), DEVMETHOD(bus_describe_intr, bus_generic_describe_intr), /* BHND bus inteface */ DEVMETHOD(bhnd_bus_activate_resource, chipc_activate_bhnd_resource), /* ChipCommon interface */ DEVMETHOD(bhnd_chipc_nvram_src, chipc_nvram_src), DEVMETHOD(bhnd_chipc_write_chipctrl, chipc_write_chipctrl), DEVMETHOD(bhnd_chipc_enable_sprom, chipc_enable_sprom_pins), DEVMETHOD(bhnd_chipc_disable_sprom, chipc_disable_sprom_pins), DEVMETHOD_END }; DEFINE_CLASS_0(bhnd_chipc, chipc_driver, chipc_methods, sizeof(struct chipc_softc)); DRIVER_MODULE(bhnd_chipc, bhnd, chipc_driver, bhnd_chipc_devclass, 0, 0); MODULE_DEPEND(bhnd_chipc, bhnd, 1, 1, 1); MODULE_VERSION(bhnd_chipc, 1); Index: head/sys/dev/bhnd/cores/pci/bhnd_pci.c =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pci.c (revision 300627) +++ head/sys/dev/bhnd/cores/pci/bhnd_pci.c (revision 300628) @@ -1,537 +1,538 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Broadcom Common PCI/PCIe Support. * * This base driver implementation is shared by the bhnd_pcib (root complex) * and bhnd_pci_hostb (host bridge) drivers. */ #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_pcireg.h" #include "bhnd_pcivar.h" static int bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc); static int bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd); static int bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc); static void bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc); static int bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd); static int bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd, uint16_t *data_read); static struct bhnd_device_quirk bhnd_pci_quirks[]; static struct bhnd_device_quirk bhnd_pcie_quirks[]; #define BHND_PCI_QUIRKS bhnd_pci_quirks #define BHND_PCIE_QUIRKS bhnd_pcie_quirks -#define BHND_PCI_DEV(_core, _desc, ...) \ - { BHND_DEVICE(_core, _desc, BHND_ ## _core ## _QUIRKS, NULL, \ +#define BHND_PCI_DEV(_core, _desc, ...) \ + { BHND_DEVICE(_core, _desc, BHND_ ## _core ## _QUIRKS, \ ## __VA_ARGS__), BHND_PCI_REGFMT_ ## _core } static const struct bhnd_pci_device { struct bhnd_device device; bhnd_pci_regfmt_t regfmt; /**< register format */ } bhnd_pci_devs[] = { BHND_PCI_DEV(PCI, "Host-PCI bridge", BHND_DF_HOSTB), - BHND_PCI_DEV(PCI, "PCI-BHND bridge"), + BHND_PCI_DEV(PCI, "PCI-BHND bridge", BHND_DF_SOC), BHND_PCI_DEV(PCIE, "PCIe-G1 Host-PCI bridge", BHND_DF_HOSTB), - BHND_PCI_DEV(PCIE, "PCIe-G1 PCI-BHND bridge"), + BHND_PCI_DEV(PCIE, "PCIe-G1 PCI-BHND bridge", BHND_DF_SOC), { BHND_DEVICE_END, 0 } }; /* Device quirks tables */ static struct bhnd_device_quirk bhnd_pci_quirks[] = { BHND_DEVICE_QUIRK_END }; static struct bhnd_device_quirk bhnd_pcie_quirks[] = { - { BHND_HWREV_GTE (10), BHND_PCI_QUIRK_SD_C22_EXTADDR }, + BHND_CORE_QUIRK(HWREV_GTE(10), BHND_PCI_QUIRK_SD_C22_EXTADDR), + BHND_DEVICE_QUIRK_END }; #define BHND_PCIE_MDIO_CTL_DELAY 10 /**< usec delay required between * MDIO_CTL/MDIO_DATA accesses. */ #define BHND_PCIE_MDIO_RETRY_DELAY 2000 /**< usec delay before retrying * BHND_PCIE_MDIOCTL_DONE. */ #define BHND_PCIE_MDIO_RETRY_COUNT 200 /**< number of times to loop waiting * for BHND_PCIE_MDIOCTL_DONE. */ #define BHND_PCI_READ_4(_sc, _reg) \ bhnd_bus_read_4((_sc)->mem_res, (_reg)) #define BHND_PCI_WRITE_4(_sc, _reg, _val) \ bhnd_bus_write_4((_sc)->mem_res, (_reg), (_val)) #define BHND_PCIE_ASSERT(sc) \ KASSERT(bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE, \ ("not a pcie device!")); int bhnd_pci_generic_probe(device_t dev) { const struct bhnd_device *id; id = bhnd_device_lookup(dev, &bhnd_pci_devs[0].device, sizeof(bhnd_pci_devs[0])); if (id == NULL) return (ENXIO); bhnd_set_custom_core_desc(dev, id->desc); return (BUS_PROBE_DEFAULT); } int bhnd_pci_generic_attach(device_t dev) { struct bhnd_pci_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, &bhnd_pci_devs[0].device, sizeof(bhnd_pci_devs[0])); /* Allocate bus resources */ sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) return (ENXIO); BHND_PCI_LOCK_INIT(sc); /* Probe and attach children */ if ((error = bus_generic_attach(dev))) goto cleanup; return (0); cleanup: bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); BHND_PCI_LOCK_DESTROY(sc); return (error); } int bhnd_pci_generic_detach(device_t dev) { struct bhnd_pci_softc *sc; int error; sc = device_get_softc(dev); if ((error = bus_generic_detach(dev))) return (error); bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); BHND_PCI_LOCK_DESTROY(sc); return (0); } static struct resource_list * bhnd_pci_get_resource_list(device_t dev, device_t child) { struct bhnd_pci_devinfo *dinfo; if (device_get_parent(child) != dev) return (NULL); dinfo = device_get_ivars(child); return (&dinfo->resources); } static device_t bhnd_pci_add_child(device_t dev, u_int order, const char *name, int unit) { struct bhnd_pci_devinfo *dinfo; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (NULL); dinfo = malloc(sizeof(struct bhnd_pci_devinfo), M_DEVBUF, M_NOWAIT); if (dinfo == NULL) { device_delete_child(dev, child); return (NULL); } resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); return (child); } static void bhnd_pci_child_deleted(device_t dev, device_t child) { struct bhnd_pci_devinfo *dinfo; if (device_get_parent(child) != dev) return; dinfo = device_get_ivars(child); if (dinfo != NULL) { resource_list_free(&dinfo->resources); free(dinfo, M_DEVBUF); } device_set_ivars(child, NULL); } int bhnd_pci_generic_suspend(device_t dev) { return (bus_generic_suspend(dev)); } int bhnd_pci_generic_resume(device_t dev) { return (bus_generic_resume(dev)); } /** * Read a 32-bit PCIe TLP/DLLP/PLP protocol register. * * @param sc The bhndb_pci driver state. * @param addr The protocol register offset. */ uint32_t bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr) { uint32_t val; BHND_PCIE_ASSERT(sc); BHND_PCI_LOCK(sc); BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr); val = BHND_PCI_READ_4(sc, BHND_PCIE_IND_DATA); BHND_PCI_UNLOCK(sc); return (val); } /** * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value. * * @param sc The bhndb_pci driver state. * @param addr The protocol register offset. * @param val The value to write to @p addr. */ void bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr, uint32_t val) { BHND_PCIE_ASSERT(sc); BHND_PCI_LOCK(sc); BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr); BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val); BHND_PCI_UNLOCK(sc); } /* Spin until the MDIO device reports itself as idle, or timeout is reached. */ static int bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc) { uint32_t ctl; /* Spin waiting for the BUSY flag to clear */ for (int i = 0; i < BHND_PCIE_MDIO_RETRY_COUNT; i++) { ctl = BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_CTL); if ((ctl & BHND_PCIE_MDIOCTL_DONE)) return (0); DELAY(BHND_PCIE_MDIO_RETRY_DELAY); } return (ETIMEDOUT); } /** * Write an MDIO IOCTL and wait for completion. */ static int bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd) { BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_CTL, cmd); DELAY(BHND_PCIE_MDIO_CTL_DELAY); return (0); } /** * Enable MDIO device */ static int bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc) { uint32_t ctl; BHND_PCIE_ASSERT(sc); /* Enable MDIO clock and preamble mode */ ctl = BHND_PCIE_MDIOCTL_PREAM_EN|BHND_PCIE_MDIOCTL_DIVISOR_VAL; return (bhnd_pcie_mdio_ioctl(sc, ctl)); } /** * Disable MDIO device. */ static void bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc) { if (bhnd_pcie_mdio_ioctl(sc, 0)) device_printf(sc->dev, "failed to disable MDIO clock\n"); } /** * Issue a write command and wait for completion */ static int bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd) { int error; BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_WRITE; BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd); DELAY(BHND_PCIE_MDIO_CTL_DELAY); if ((error = bhnd_pcie_mdio_wait_idle(sc))) return (error); return (0); } /** * Issue an an MDIO read command, wait for completion, and return * the result in @p data_read. */ static int bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd, uint16_t *data_read) { int error; BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_READ; BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd); DELAY(BHND_PCIE_MDIO_CTL_DELAY); if ((error = bhnd_pcie_mdio_wait_idle(sc))) return (error); *data_read = (BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_DATA) & BHND_PCIE_MDIODATA_DATA_MASK); return (0); } int bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy, int reg) { uint32_t cmd; uint16_t val; int error; /* Enable MDIO access */ BHND_PCI_LOCK(sc); bhnd_pcie_mdio_enable(sc); /* Issue the read */ cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg); error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val); /* Disable MDIO access */ bhnd_pcie_mdio_disable(sc); BHND_PCI_UNLOCK(sc); if (error) return (~0U); return (val); } int bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy, int reg, int val) { uint32_t cmd; int error; /* Enable MDIO access */ BHND_PCI_LOCK(sc); bhnd_pcie_mdio_enable(sc); /* Issue the write */ cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK); error = bhnd_pcie_mdio_cmd_write(sc, cmd); /* Disable MDIO access */ bhnd_pcie_mdio_disable(sc); BHND_PCI_UNLOCK(sc); return (error); } int bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy, int devaddr, int reg) { uint32_t cmd; uint16_t val; int error; if (devaddr == MDIO_DEVADDR_NONE) return (bhnd_pcie_mdio_read(sc, phy, reg)); /* Extended register access is only supported for the SerDes device, * using the non-standard C22 extended address mechanism */ if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) || phy != BHND_PCIE_PHYADDR_SD) { return (~0U); } /* Enable MDIO access */ BHND_PCI_LOCK(sc); bhnd_pcie_mdio_enable(sc); /* Write the block address to the address extension register */ cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr; if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd))) goto cleanup; /* Issue the read */ cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg); error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val); cleanup: bhnd_pcie_mdio_disable(sc); BHND_PCI_UNLOCK(sc); if (error) return (~0U); return (val); } int bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy, int devaddr, int reg, int val) { uint32_t cmd; int error; if (devaddr == MDIO_DEVADDR_NONE) return (bhnd_pcie_mdio_write(sc, phy, reg, val)); /* Extended register access is only supported for the SerDes device, * using the non-standard C22 extended address mechanism */ if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) || phy != BHND_PCIE_PHYADDR_SD) { return (~0U); } /* Enable MDIO access */ BHND_PCI_LOCK(sc); bhnd_pcie_mdio_enable(sc); /* Write the block address to the address extension register */ cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr; if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd))) goto cleanup; /* Issue the write */ cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK); error = bhnd_pcie_mdio_cmd_write(sc, cmd); cleanup: bhnd_pcie_mdio_disable(sc); BHND_PCI_UNLOCK(sc); return (error); } static device_method_t bhnd_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhnd_pci_generic_probe), DEVMETHOD(device_attach, bhnd_pci_generic_attach), DEVMETHOD(device_detach, bhnd_pci_generic_detach), DEVMETHOD(device_suspend, bhnd_pci_generic_suspend), DEVMETHOD(device_resume, bhnd_pci_generic_resume), /* Bus interface */ DEVMETHOD(bus_add_child, bhnd_pci_add_child), DEVMETHOD(bus_child_deleted, bhnd_pci_child_deleted), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_get_resource_list, bhnd_pci_get_resource_list), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD_END }; DEFINE_CLASS_0(bhnd_pci, bhnd_pci_driver, bhnd_pci_methods, sizeof(struct bhnd_pci_softc)); MODULE_DEPEND(bhnd_pci, bhnd, 1, 1, 1); MODULE_DEPEND(bhnd_pci, pci, 1, 1, 1); MODULE_VERSION(bhnd_pci, 1); Index: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c (revision 300627) +++ head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c (revision 300628) @@ -1,661 +1,658 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Broadcom BHND PCI/PCIe-Gen1 PCI-Host Bridge. * * This driver handles all interactions with PCI bridge cores operating in * endpoint mode. * * Host-level PCI operations are handled at the bhndb bridge level by the * bhndb_pci driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_pcireg.h" #include "bhnd_pci_hostbvar.h" static const struct bhnd_device_quirk bhnd_pci_quirks[]; static const struct bhnd_device_quirk bhnd_pcie_quirks[]; -static const struct bhnd_chip_quirk bhnd_pci_chip_quirks[]; -static const struct bhnd_chip_quirk bhnd_pcie_chip_quirks[]; /* Device driver work-around variations */ typedef enum { BHND_PCI_WAR_ATTACH, /**< apply attach workarounds */ BHND_PCI_WAR_RESUME, /**< apply resume workarounds */ BHND_PCI_WAR_SUSPEND, /**< apply suspend workarounds */ BHND_PCI_WAR_DETACH /**< apply detach workarounds */ } bhnd_pci_war_state; static int bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc); static int bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state); static int bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state); /* * device/quirk tables */ -#define BHND_PCI_DEV(_core, _quirks, _chip_quirks) \ - BHND_DEVICE(_core, "", _quirks, _chip_quirks, BHND_DF_HOSTB) +#define BHND_PCI_DEV(_core, _quirks) \ + BHND_DEVICE(_core, NULL, _quirks, BHND_DF_HOSTB) static const struct bhnd_device bhnd_pci_devs[] = { - BHND_PCI_DEV(PCI, bhnd_pci_quirks, bhnd_pci_chip_quirks), - BHND_PCI_DEV(PCIE, bhnd_pcie_quirks, bhnd_pcie_chip_quirks), + BHND_PCI_DEV(PCI, bhnd_pci_quirks), + BHND_PCI_DEV(PCIE, bhnd_pcie_quirks), BHND_DEVICE_END }; static const struct bhnd_device_quirk bhnd_pci_quirks[] = { - { BHND_HWREV_ANY, BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST }, - { BHND_HWREV_GTE(11), BHND_PCI_QUIRK_SBTOPCI2_READMULTI | - BHND_PCI_QUIRK_CLKRUN_DSBL }, - BHND_DEVICE_QUIRK_END -}; + /* core revision quirks */ + BHND_CORE_QUIRK (HWREV_ANY, BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST), + BHND_CORE_QUIRK (HWREV_GTE(11), BHND_PCI_QUIRK_SBTOPCI2_READMULTI | + BHND_PCI_QUIRK_CLKRUN_DSBL), -static const struct bhnd_chip_quirk bhnd_pci_chip_quirks[] = { /* BCM4321CB2 boards that require 960ns latency timer override */ - {{ BHND_CHIP_BTYPE(BCM4321CB2) }, - BHND_PCI_QUIRK_960NS_LATTIM_OVR }, - {{ BHND_CHIP_BTYPE(BCM4321CB2_AG) }, - BHND_PCI_QUIRK_960NS_LATTIM_OVR }, + BHND_BOARD_QUIRK(BCM4321CB2, BHND_PCI_QUIRK_960NS_LATTIM_OVR), + BHND_BOARD_QUIRK(BCM4321CB2_AG, BHND_PCI_QUIRK_960NS_LATTIM_OVR), - BHND_CHIP_QUIRK_END + BHND_DEVICE_QUIRK_END }; static const struct bhnd_device_quirk bhnd_pcie_quirks[] = { - { BHND_HWREV_EQ (0), BHND_PCIE_QUIRK_SDR9_L0s_HANG }, - { BHND_HWREV_RANGE (0,1), BHND_PCIE_QUIRK_UR_STATUS_FIX }, - { BHND_HWREV_EQ (1), BHND_PCIE_QUIRK_PCIPM_REQEN }, + /* core revision quirks */ + BHND_CORE_QUIRK (HWREV_EQ (0), BHND_PCIE_QUIRK_SDR9_L0s_HANG), + BHND_CORE_QUIRK (HWREV_RANGE(0,1), + BHND_PCIE_QUIRK_UR_STATUS_FIX), - { BHND_HWREV_RANGE (3,5), BHND_PCIE_QUIRK_ASPM_OVR | - BHND_PCIE_QUIRK_SDR9_POLARITY | - BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY }, + BHND_CORE_QUIRK (HWREV_EQ (1), BHND_PCIE_QUIRK_PCIPM_REQEN), - { BHND_HWREV_LTE (6), BHND_PCIE_QUIRK_L1_IDLE_THRESH }, - { BHND_HWREV_GTE (6), BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET }, - { BHND_HWREV_EQ (7), BHND_PCIE_QUIRK_SERDES_NOPLLDOWN }, - { BHND_HWREV_GTE (8), BHND_PCIE_QUIRK_L1_TIMER_PERF }, + BHND_CORE_QUIRK (HWREV_RANGE(3,5), + BHND_PCIE_QUIRK_ASPM_OVR | BHND_PCIE_QUIRK_SDR9_POLARITY | + BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY), - { BHND_HWREV_LTE (17), BHND_PCIE_QUIRK_MAX_MRRS_128 }, + BHND_CORE_QUIRK (HWREV_LTE(6), BHND_PCIE_QUIRK_L1_IDLE_THRESH), + BHND_CORE_QUIRK (HWREV_GTE(6), BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET), + BHND_CORE_QUIRK (HWREV_EQ (7), BHND_PCIE_QUIRK_SERDES_NOPLLDOWN), + BHND_CORE_QUIRK (HWREV_GTE(8), BHND_PCIE_QUIRK_L1_TIMER_PERF), - BHND_DEVICE_QUIRK_END -}; + BHND_CORE_QUIRK (HWREV_LTE(17), BHND_PCIE_QUIRK_MAX_MRRS_128), -static const struct bhnd_chip_quirk bhnd_pcie_chip_quirks[] = { /* Apple boards on which BHND_BFL2_PCIEWAR_OVR should be assumed * to be set. */ - {{ BHND_CHIP_BVENDOR (PCI_VENDOR_APPLE), - BHND_CHIP_SROMREV (HWREV_EQ(4)), - BHND_CHIP_BREV (HWREV_LTE(0x71)) }, - BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN }, + {{ BHND_MATCH_BOARD_VENDOR (PCI_VENDOR_APPLE), + BHND_MATCH_BOARD_REV (HWREV_LTE(0x71)), + BHND_MATCH_SROMREV (EQ(4)) }, + BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN }, /* Apple BCM4322 boards that require 700mV SerDes TX drive strength. */ - {{ BHND_CHIP_BVT (PCI_VENDOR_APPLE, BCM94322X9) }, - BHND_PCIE_QUIRK_SERDES_TXDRV_700MV }, + {{ BHND_CHIP_ID(BCM4322), + BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94322X9), }, + BHND_PCIE_QUIRK_SERDES_TXDRV_700MV }, /* Apple BCM4331 board-specific quirks */ -#define BHND_APPLE_4331_QUIRK(_board, ...) \ - {{ BHND_CHIP_ID (4331), \ - BHND_CHIP_BVT (PCI_VENDOR_APPLE, _board), }, \ - __VA_ARGS__ } +#define BHND_A4331_QUIRK(_board, ...) \ + {{ BHND_CHIP_ID(BCM4331), \ + BHND_MATCH_BOARD(PCI_VENDOR_APPLE, _board) }, __VA_ARGS__ } - BHND_APPLE_4331_QUIRK(BCM94331X19, - BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512), + BHND_A4331_QUIRK(BCM94331X19, BHND_PCIE_QUIRK_SERDES_TXDRV_MAX | + BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - BHND_APPLE_4331_QUIRK(BCM94331X28, - BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - BHND_APPLE_4331_QUIRK(BCM94331X28B, BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - - BHND_APPLE_4331_QUIRK(BCM94331X29B, - BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512), + BHND_A4331_QUIRK(BCM94331X28, BHND_PCIE_QUIRK_SERDES_TXDRV_MAX | + BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - BHND_APPLE_4331_QUIRK(BCM94331X19C, - BHND_PCIE_QUIRK_SERDES_TXDRV_MAX|BHND_PCIE_QUIRK_DEFAULT_MRRS_512), + BHND_A4331_QUIRK(BCM94331X28B, BHND_PCIE_QUIRK_DEFAULT_MRRS_512), + + BHND_A4331_QUIRK(BCM94331X29B, BHND_PCIE_QUIRK_SERDES_TXDRV_MAX | + BHND_PCIE_QUIRK_DEFAULT_MRRS_512), + + BHND_A4331_QUIRK(BCM94331X19C, BHND_PCIE_QUIRK_SERDES_TXDRV_MAX | + BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - BHND_APPLE_4331_QUIRK(BCM94331X29D, BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - BHND_APPLE_4331_QUIRK(BCM94331X33, BHND_PCIE_QUIRK_DEFAULT_MRRS_512), -#undef BHND_APPLE_4331_QUIRK + BHND_A4331_QUIRK(BCM94331X29D, BHND_PCIE_QUIRK_DEFAULT_MRRS_512), - BHND_CHIP_QUIRK_END + BHND_A4331_QUIRK(BCM94331X33, BHND_PCIE_QUIRK_DEFAULT_MRRS_512), + +#undef BHND_A4331_QUIRK + + BHND_DEVICE_QUIRK_END }; + #define BHND_PCI_SOFTC(_sc) (&((_sc)->common)) #define BHND_PCI_READ_2(_sc, _reg) \ bhnd_bus_read_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg)) #define BHND_PCI_READ_4(_sc, _reg) \ bhnd_bus_read_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg)) #define BHND_PCI_WRITE_2(_sc, _reg, _val) \ bhnd_bus_write_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val)) #define BHND_PCI_WRITE_4(_sc, _reg, _val) \ bhnd_bus_write_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val)) #define BHND_PCI_PROTO_READ_4(_sc, _reg) \ bhnd_pcie_read_proto_reg(BHND_PCI_SOFTC(_sc), (_reg)) #define BHND_PCI_PROTO_WRITE_4(_sc, _reg, _val) \ bhnd_pcie_write_proto_reg(BHND_PCI_SOFTC(_sc), (_reg), (_val)) #define BHND_PCI_MDIO_READ(_sc, _phy, _reg) \ bhnd_pcie_mdio_read(BHND_PCI_SOFTC(_sc), (_phy), (_reg)) #define BHND_PCI_MDIO_WRITE(_sc, _phy, _reg, _val) \ bhnd_pcie_mdio_write(BHND_PCI_SOFTC(_sc), (_phy), (_reg), (_val)) #define BHND_PCI_MDIO_READ_EXT(_sc, _phy, _devaddr, _reg) \ bhnd_pcie_mdio_read_ext(BHND_PCI_SOFTC(_sc), (_phy), (_devaddr), (_reg)) #define BHND_PCI_MDIO_WRITE_EXT(_sc, _phy, _devaddr, _reg, _val) \ bhnd_pcie_mdio_write_ext(BHND_PCI_SOFTC(_sc), (_phy), \ (_devaddr), (_reg), (_val)) #define BPCI_REG_SET(_regv, _attr, _val) \ BHND_PCI_REG_SET((_regv), BHND_ ## _attr, (_val)) #define BPCI_REG_GET(_regv, _attr) \ BHND_PCI_REG_GET((_regv), BHND_ ## _attr) #define BPCI_CMN_REG_SET(_regv, _attr, _val) \ BHND_PCI_CMN_REG_SET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv), \ BHND_ ## _attr, (_val)) #define BPCI_CMN_REG_GET(_regv, _attr) \ BHND_PCI_CMN_REG_GET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv), \ BHND_ ## _attr) static int bhnd_pci_hostb_attach(device_t dev) { struct bhnd_pcihb_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, bhnd_pci_devs, sizeof(bhnd_pci_devs[0])); /* Find the host PCI bridge device */ sc->pci_dev = bhnd_find_bridge_root(dev, devclass_find("pci")); if (sc->pci_dev == NULL) { device_printf(dev, "parent pci bridge device not found\n"); return (ENXIO); } /* Common setup */ if ((error = bhnd_pci_generic_attach(dev))) return (error); /* Apply early single-shot work-arounds */ if ((error = bhnd_pci_wars_early_once(sc))) goto failed; /* Apply attach/resume work-arounds */ if ((error = bhnd_pci_wars_hwup(sc, BHND_PCI_WAR_ATTACH))) goto failed; return (0); failed: bhnd_pci_generic_detach(dev); return (error); } static int bhnd_pci_hostb_detach(device_t dev) { struct bhnd_pcihb_softc *sc; int error; sc = device_get_softc(dev); /* Apply suspend/detach work-arounds */ if ((error = bhnd_pci_wars_hwdown(sc, BHND_PCI_WAR_DETACH))) return (error); return (bhnd_pci_generic_detach(dev)); } static int bhnd_pci_hostb_suspend(device_t dev) { struct bhnd_pcihb_softc *sc; int error; sc = device_get_softc(dev); /* Apply suspend/detach work-arounds */ if ((error = bhnd_pci_wars_hwdown(sc, BHND_PCI_WAR_SUSPEND))) return (error); return (bhnd_pci_generic_suspend(dev)); } static int bhnd_pci_hostb_resume(device_t dev) { struct bhnd_pcihb_softc *sc; int error; sc = device_get_softc(dev); if ((error = bhnd_pci_generic_resume(dev))) return (error); /* Apply attach/resume work-arounds */ if ((error = bhnd_pci_wars_hwup(sc, BHND_PCI_WAR_RESUME))) { bhnd_pci_generic_detach(dev); return (error); } return (0); } /** * Apply any hardware work-arounds that must be executed exactly once, early in * the attach process. * * This must be called after core enumeration and discovery of all applicable * quirks, but prior to probe/attach of any cores, parsing of * SPROM, etc. */ static int bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc) { int error; /* Set PCI latency timer */ if (sc->quirks & BHND_PCI_QUIRK_960NS_LATTIM_OVR) { pci_write_config(sc->pci_dev, PCIR_LATTIMER, 0x20 /* 960ns */, 1); } /* Determine whether ASPM/CLKREQ should be forced on, or forced off. */ if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) { struct bhnd_board_info board; bool aspm_en; /* Fetch board info */ if ((error = bhnd_read_board_info(sc->dev, &board))) return (error); /* Check board flags */ aspm_en = true; if (board.board_flags2 & BHND_BFL2_PCIEWAR_OVR) aspm_en = false; /* Early Apple devices did not (but should have) set * BHND_BFL2_PCIEWAR_OVR in SPROM. */ if (sc->quirks & BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN) aspm_en = false; sc->aspm_quirk_override.aspm_en = aspm_en; } /* Determine correct polarity by observing the attach-time PCIe PHY * link status. This is used later to reset/force the SerDes * polarity */ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) { uint32_t st; bool inv; st = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_PLP_STATUSREG); inv = ((st & BHND_PCIE_PLP_POLARITY_INV) != 0); sc->sdr9_quirk_polarity.inv = inv; } /* Override maximum read request size */ if (bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE) { int msize; msize = 128; /* compatible with all PCIe-G1 core revisions */ if (sc->quirks & BHND_PCIE_QUIRK_DEFAULT_MRRS_512) msize = 512; if (pci_set_max_read_req(sc->pci_dev, msize) == 0) panic("set mrrs on non-PCIe device"); } return (0); } /** * Apply any hardware workarounds that are required upon attach or resume * of the bridge device. */ static int bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state) { /* Note that the order here matters; these work-arounds * should not be re-ordered without careful review of their * interdependencies */ /* Enable PCI prefetch/burst/readmulti flags */ if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST || sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI) { uint32_t sbp2; sbp2 = BHND_PCI_READ_4(sc, BHND_PCI_SBTOPCI2); if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST) sbp2 |= (BHND_PCI_SBTOPCI_PREF|BHND_PCI_SBTOPCI_BURST); if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI) sbp2 |= BHND_PCI_SBTOPCI_RC_READMULTI; BHND_PCI_WRITE_4(sc, BHND_PCI_SBTOPCI2, sbp2); } /* Disable PCI CLKRUN# */ if (sc->quirks & BHND_PCI_QUIRK_CLKRUN_DSBL) { uint32_t ctl; ctl = BHND_PCI_READ_4(sc, BHND_PCI_CLKRUN_CTL); ctl |= BHND_PCI_CLKRUN_DSBL; BHND_PCI_WRITE_4(sc, BHND_PCI_CLKRUN_CTL, ctl); } /* Enable TLP unmatched address handling work-around */ if (sc->quirks & BHND_PCIE_QUIRK_UR_STATUS_FIX) { uint32_t wrs; wrs = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG); wrs |= BHND_PCIE_TLP_WORKAROUND_URBIT; BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG, wrs); } /* Adjust SerDes CDR tuning to ensure that CDR is stable before sending * data during L0s to L0 exit transitions. */ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_L0s_HANG) { uint16_t sdv; /* Set RX track/acquire timers to 2.064us/40.96us */ sdv = BPCI_REG_SET(0, PCIE_SDR9_RX_TIMER1_LKTRK, (2064/16)); sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_TIMER1_LKACQ, (40960/1024)); BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX, BHND_PCIE_SDR9_RX_TIMER1, sdv); /* Apply CDR frequency workaround */ sdv = BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN; sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDR_FREQ_OVR, 0x0); BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX, BHND_PCIE_SDR9_RX_CDR, sdv); /* Apply CDR BW tunings */ sdv = 0; sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGTRK, 0x2); sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGACQ, 0x4); sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPTRK, 0x6); sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPACQ, 0x6); BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX, BHND_PCIE_SDR9_RX_CDRBW, sdv); } /* Force correct SerDes polarity */ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) { uint16_t rxctl; rxctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_TXRX, BHND_PCIE_SDR9_RX_CTRL); rxctl |= BHND_PCIE_SDR9_RX_CTRL_FORCE; if (sc->sdr9_quirk_polarity.inv) rxctl |= BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV; else rxctl &= ~BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV; BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX, BHND_PCIE_SDR9_RX_CTRL, rxctl); } /* Disable startup retry on PLL frequency detection failure */ if (sc->quirks & BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY) { uint16_t pctl; pctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_PLL, BHND_PCIE_SDR9_PLL_CTRL); pctl &= ~BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN; BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_PLL, BHND_PCIE_SDR9_PLL_CTRL, pctl); } /* Explicitly enable PCI-PM */ if (sc->quirks & BHND_PCIE_QUIRK_PCIPM_REQEN) { uint32_t lcreg; lcreg = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_LCREG); lcreg |= BHND_PCIE_DLLP_LCREG_PCIPM_EN; BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_LCREG, lcreg); } /* Adjust L1 timer to fix slow L1->L0 transitions */ if (sc->quirks & BHND_PCIE_QUIRK_L1_IDLE_THRESH) { uint32_t pmt; pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG); pmt = BPCI_REG_SET(pmt, PCIE_L1THRESHOLDTIME, BHND_PCIE_L1THRESHOLD_WARVAL); BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt); } /* Extend L1 timer for better performance. * TODO: We could enable/disable this on demand for better power * savings if we tie this to HT clock request handling */ if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) { uint32_t pmt; pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG); pmt |= BHND_PCIE_ASPMTIMER_EXTEND; BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt); } /* Override ASPM/ECPM settings in SPROM shadow and PCIER_LINK_CTL */ if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) { bus_size_t reg; uint16_t cfg; /* Set ASPM L1/L0s flags in SPROM shadow */ reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_ASPM_OFFSET; cfg = BHND_PCI_READ_2(sc, reg); if (sc->aspm_quirk_override.aspm_en) cfg |= BHND_PCIE_SRSH_ASPM_ENB; else cfg &= ~BHND_PCIE_SRSH_ASPM_ENB; BHND_PCI_WRITE_2(sc, reg, cfg); /* Set ASPM/ECPM (CLKREQ) flags in PCIe link control register */ cfg = pcie_read_config(sc->pci_dev, PCIER_LINK_CTL, 2); if (sc->aspm_quirk_override.aspm_en) cfg |= PCIEM_LINK_CTL_ASPMC; else cfg &= ~PCIEM_LINK_CTL_ASPMC; cfg &= ~PCIEM_LINK_CTL_ECPM; /* CLKREQ# */ pcie_write_config(sc->pci_dev, PCIER_LINK_CTL, cfg, 2); /* Set CLKREQ (ECPM) flags in SPROM shadow */ reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_CLKREQ_OFFSET_R5; cfg = BHND_PCI_READ_2(sc, reg); if (sc->aspm_quirk_override.aspm_en) cfg |= BHND_PCIE_SRSH_CLKREQ_ENB; else cfg &= ~BHND_PCIE_SRSH_CLKREQ_ENB; BHND_PCI_WRITE_2(sc, reg, cfg); } /* Enable L23READY_EXIT_NOPRST if not already set in SPROM. */ if (sc->quirks & BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET) { bus_size_t reg; uint16_t cfg; /* Fetch the misc cfg flags from SPROM */ reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_PCIE_MISC_CONFIG; cfg = BHND_PCI_READ_2(sc, reg); /* Write EXIT_NOPRST flag if not already set in SPROM */ if (!(cfg & BHND_PCIE_SRSH_L23READY_EXIT_NOPRST)) { cfg |= BHND_PCIE_SRSH_L23READY_EXIT_NOPRST; BHND_PCI_WRITE_2(sc, reg, cfg); } } /* Disable SerDes PLL down */ if (sc->quirks & BHND_PCIE_QUIRK_SERDES_NOPLLDOWN) { device_t bhnd, chipc; bus_size_t reg; bhnd = device_get_parent(sc->dev); chipc = bhnd_find_child(bhnd, BHND_DEVCLASS_CC, 0); KASSERT(chipc != NULL, ("missing chipcommon device")); /* Write SerDes PLL disable flag to the ChipCommon core */ BHND_CHIPC_WRITE_CHIPCTRL(chipc, CHIPCTRL_4321_PLL_DOWN, CHIPCTRL_4321_PLL_DOWN); /* Clear SPROM shadow backdoor register */ reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_BD_OFFSET; BHND_PCI_WRITE_2(sc, reg, 0); } /* Adjust TX drive strength and pre-emphasis coefficient */ if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_ADJUST) { uint16_t txdrv; /* Fetch current TX driver parameters */ txdrv = BHND_PCI_MDIO_READ_EXT(sc, BHND_PCIE_PHYADDR_SD, BHND_PCIE_SD_REGS_TX0, BHND_PCIE_SD_TX_DRIVER); /* Set 700mV drive strength */ if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_700MV) { txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_P2_COEFF, BHND_PCIE_APPLE_TX_P2_COEFF_700MV); txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_IDRIVER, BHND_PCIE_APPLE_TX_IDRIVER_700MV); } /* ... or, set max drive strength */ if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_MAX) { txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_P2_COEFF, BHND_PCIE_APPLE_TX_P2_COEFF_MAX); txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_IDRIVER, BHND_PCIE_APPLE_TX_IDRIVER_MAX); } BHND_PCI_MDIO_WRITE_EXT(sc, BHND_PCIE_PHYADDR_SD, BHND_PCIE_SD_REGS_TX0, BHND_PCIE_SD_TX_DRIVER, txdrv); } return (0); } /** * Apply any hardware workarounds that are required upon detach or suspend * of the bridge device. */ static int bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state) { /* Reduce L1 timer for better power savings. * TODO: We could enable/disable this on demand for better power * savings if we tie this to HT clock request handling */ if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) { uint32_t pmt; pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG); pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND; BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt); } /* Enable CLKREQ (ECPM). If suspending, also disable ASPM L1 entry */ if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) { uint16_t lcreg; lcreg = pcie_read_config(sc->pci_dev, PCIER_LINK_CTL, 2); lcreg |= PCIEM_LINK_CTL_ECPM; /* CLKREQ# */ if (state == BHND_PCI_WAR_SUSPEND) lcreg &= ~PCIEM_LINK_CTL_ASPMC_L1; pcie_write_config(sc->pci_dev, PCIER_LINK_CTL, lcreg, 2); } return (0); } static device_method_t bhnd_pci_hostb_methods[] = { /* Device interface */ DEVMETHOD(device_attach, bhnd_pci_hostb_attach), DEVMETHOD(device_detach, bhnd_pci_hostb_detach), DEVMETHOD(device_suspend, bhnd_pci_hostb_suspend), DEVMETHOD(device_resume, bhnd_pci_hostb_resume), DEVMETHOD_END }; DEFINE_CLASS_1(bhnd_hostb, bhnd_pci_hostb_driver, bhnd_pci_hostb_methods, sizeof(struct bhnd_pcihb_softc), bhnd_pci_driver); DRIVER_MODULE(bhnd_pci_hostb, bhnd, bhnd_pci_hostb_driver, bhnd_hostb_devclass, 0, 0); MODULE_VERSION(bhnd_pci_hostb, 1); MODULE_DEPEND(bhnd_pci_hostb, bhnd, 1, 1, 1); MODULE_DEPEND(bhnd_pci_hostb, bhnd_pci, 1, 1, 1); Index: head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2.c =================================================================== --- head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2.c (revision 300627) +++ head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2.c (revision 300628) @@ -1,289 +1,289 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Broadcom Common PCIe-G2 Support. * * This base driver implementation is shared by the bhnd_pcib_g2 (root complex) * and bhnd_pci_hostb_g2 (host bridge) drivers. */ #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_pcie2_reg.h" #include "bhnd_pcie2_var.h" static struct bhnd_device_quirk bhnd_pcie2_quirks[]; #define BHND_PCIE_DEV(_core, _desc, ...) \ - BHND_DEVICE(_core, _desc, bhnd_pcie2_quirks, NULL, ## __VA_ARGS__) + BHND_DEVICE(_core, _desc, bhnd_pcie2_quirks, ## __VA_ARGS__) static const struct bhnd_device bhnd_pcie2_devs[] = { BHND_PCIE_DEV(PCIE2, "PCIe-G2 Host-PCI bridge", BHND_DF_HOSTB), - BHND_PCIE_DEV(PCIE2, "PCIe-G2 PCI-BHND bridge"), + BHND_PCIE_DEV(PCIE2, "PCIe-G2 PCI-BHND bridge", BHND_DF_SOC), BHND_DEVICE_END }; /* Device quirks tables */ static struct bhnd_device_quirk bhnd_pcie2_quirks[] = { BHND_DEVICE_QUIRK_END }; int bhnd_pcie2_generic_probe(device_t dev) { const struct bhnd_device *id; id = bhnd_device_lookup(dev, bhnd_pcie2_devs, sizeof(bhnd_pcie2_devs[0])); if (id == NULL) return (ENXIO); bhnd_set_custom_core_desc(dev, id->desc); return (BUS_PROBE_DEFAULT); } int bhnd_pcie2_generic_attach(device_t dev) { struct bhnd_pcie2_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, bhnd_pcie2_devs, sizeof(bhnd_pcie2_devs[0])); /* Allocate bus resources */ sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) return (ENXIO); BHND_PCIE2_LOCK_INIT(sc); /* Probe and attach children */ if ((error = bus_generic_attach(dev))) goto cleanup; return (0); cleanup: bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); BHND_PCIE2_LOCK_DESTROY(sc); return (error); } int bhnd_pcie2_generic_detach(device_t dev) { struct bhnd_pcie2_softc *sc; int error; sc = device_get_softc(dev); if ((error = bus_generic_detach(dev))) return (error); bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); BHND_PCIE2_LOCK_DESTROY(sc); return (0); } static struct resource_list * bhnd_pcie2_get_resource_list(device_t dev, device_t child) { struct bhnd_pcie2_devinfo *dinfo; if (device_get_parent(child) != dev) return (NULL); dinfo = device_get_ivars(child); return (&dinfo->resources); } static device_t bhnd_pcie2_add_child(device_t dev, u_int order, const char *name, int unit) { struct bhnd_pcie2_devinfo *dinfo; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (NULL); dinfo = malloc(sizeof(struct bhnd_pcie2_devinfo), M_DEVBUF, M_NOWAIT); if (dinfo == NULL) { device_delete_child(dev, child); return (NULL); } resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); return (child); } static void bhnd_pcie2_child_deleted(device_t dev, device_t child) { struct bhnd_pcie2_devinfo *dinfo; if (device_get_parent(child) != dev) return; dinfo = device_get_ivars(child); if (dinfo != NULL) { resource_list_free(&dinfo->resources); free(dinfo, M_DEVBUF); } device_set_ivars(child, NULL); } int bhnd_pcie2_generic_suspend(device_t dev) { return (bus_generic_suspend(dev)); } int bhnd_pcie2_generic_resume(device_t dev) { return (bus_generic_resume(dev)); } /** * Read a 32-bit PCIe TLP/DLLP/PLP protocol register. * * @param sc The bhndb_pci driver state. * @param addr The protocol register offset. */ uint32_t bhnd_pcie2_read_proto_reg(struct bhnd_pcie2_softc *sc, uint32_t addr) { // TODO return (ENXIO); } /** * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value. * * @param sc The bhndb_pci driver state. * @param addr The protocol register offset. * @param val The value to write to @p addr. */ void bhnd_pcie2_write_proto_reg(struct bhnd_pcie2_softc *sc, uint32_t addr, uint32_t val) { // TODO panic("unimplemented"); } int bhnd_pcie2_mdio_read(struct bhnd_pcie2_softc *sc, int phy, int reg) { // TODO return (ENXIO); } int bhnd_pcie2_mdio_write(struct bhnd_pcie2_softc *sc, int phy, int reg, int val) { // TODO return (ENXIO); } int bhnd_pcie2_mdio_read_ext(struct bhnd_pcie2_softc *sc, int phy, int devaddr, int reg) { // TODO return (ENXIO); } int bhnd_pcie2_mdio_write_ext(struct bhnd_pcie2_softc *sc, int phy, int devaddr, int reg, int val) { // TODO return (ENXIO); } static device_method_t bhnd_pcie2_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhnd_pcie2_generic_probe), DEVMETHOD(device_attach, bhnd_pcie2_generic_attach), DEVMETHOD(device_detach, bhnd_pcie2_generic_detach), DEVMETHOD(device_suspend, bhnd_pcie2_generic_suspend), DEVMETHOD(device_resume, bhnd_pcie2_generic_resume), /* Bus interface */ DEVMETHOD(bus_add_child, bhnd_pcie2_add_child), DEVMETHOD(bus_child_deleted, bhnd_pcie2_child_deleted), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_get_resource_list, bhnd_pcie2_get_resource_list), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD_END }; DEFINE_CLASS_0(bhnd_pcie2, bhnd_pcie2_driver, bhnd_pcie2_methods, sizeof(struct bhnd_pcie2_softc)); MODULE_DEPEND(bhnd_pcie2, bhnd, 1, 1, 1); MODULE_DEPEND(bhnd_pcie2, pci, 1, 1, 1); MODULE_VERSION(bhnd_pcie2, 1); Index: head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c =================================================================== --- head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c (revision 300627) +++ head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c (revision 300628) @@ -1,254 +1,249 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Broadcom BHND PCIe-Gen2 PCI-Host Bridge. * * This driver handles all interactions with PCIe-G2 bridge cores operating in * endpoint mode. * * Host-level PCI operations are handled at the bhndb bridge level by the * bhndb_pci driver. */ // TODO // // A full survey of known quirks/work-arounds has not been completed. // // Work-arounds for the following are not yet implemented: // - BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH // 4360 PCIe SerDes Tx amplitude/deemphasis (vendor Apple, boards // BCM94360X51P2, BCM94360X51A) #include #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_pcie2_reg.h" #include "bhnd_pcie2_hostbvar.h" static const struct bhnd_device_quirk bhnd_pcie2_quirks[]; -static const struct bhnd_chip_quirk bhnd_pcie2_chip_quirks[]; static int bhnd_pcie2_wars_early_once(struct bhnd_pcie2hb_softc *sc); static int bhnd_pcie2_wars_hwup(struct bhnd_pcie2hb_softc *sc); static int bhnd_pcie2_wars_hwdown(struct bhnd_pcie2hb_softc *sc); /* * device/quirk tables */ -#define BHND_PCI_DEV(_core, _quirks, _chip_quirks) \ - BHND_DEVICE(_core, "", _quirks, _chip_quirks, BHND_DF_HOSTB) +#define BHND_PCI_DEV(_core, _quirks) \ + BHND_DEVICE(_core, NULL, _quirks, BHND_DF_HOSTB) static const struct bhnd_device bhnd_pcie2_devs[] = { - BHND_PCI_DEV(PCIE2, bhnd_pcie2_quirks, bhnd_pcie2_chip_quirks), + BHND_PCI_DEV(PCIE2, bhnd_pcie2_quirks), BHND_DEVICE_END }; static const struct bhnd_device_quirk bhnd_pcie2_quirks[] = { - BHND_DEVICE_QUIRK_END -}; - -static const struct bhnd_chip_quirk bhnd_pcie2_chip_quirks[] = { /* Apple BCM4360 boards that require adjusting TX amplitude and * differential output de-emphasis of the PCIe SerDes */ - {{ BHND_CHIP_BVT (PCI_VENDOR_APPLE, BCM94360X51P2) }, - BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH }, - {{ BHND_CHIP_BVT (PCI_VENDOR_APPLE, BCM94360X51A) }, - BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH }, + {{ BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94360X51P2), }, + BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH }, + {{ BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94360X51A), }, + BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH }, - BHND_CHIP_QUIRK_END + BHND_DEVICE_QUIRK_END }; static int bhnd_pcie2_hostb_attach(device_t dev) { struct bhnd_pcie2hb_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, bhnd_pcie2_devs, sizeof(bhnd_pcie2_devs[0])); /* Find the host PCI bridge device */ sc->pci_dev = bhnd_find_bridge_root(dev, devclass_find("pci")); if (sc->pci_dev == NULL) { device_printf(dev, "parent pci bridge device not found\n"); return (ENXIO); } /* Common setup */ if ((error = bhnd_pcie2_generic_attach(dev))) return (error); /* Apply early single-shot work-arounds */ if ((error = bhnd_pcie2_wars_early_once(sc))) goto failed; /* Apply attach/resume work-arounds */ if ((error = bhnd_pcie2_wars_hwup(sc))) goto failed; return (0); failed: bhnd_pcie2_generic_detach(dev); return (error); } static int bhnd_pcie2_hostb_detach(device_t dev) { struct bhnd_pcie2hb_softc *sc; int error; sc = device_get_softc(dev); /* Apply suspend/detach work-arounds */ if ((error = bhnd_pcie2_wars_hwdown(sc))) return (error); return (bhnd_pcie2_generic_detach(dev)); } static int bhnd_pcie2_hostb_suspend(device_t dev) { struct bhnd_pcie2hb_softc *sc; int error; sc = device_get_softc(dev); /* Apply suspend/detach work-arounds */ if ((error = bhnd_pcie2_wars_hwdown(sc))) return (error); return (bhnd_pcie2_generic_suspend(dev)); } static int bhnd_pcie2_hostb_resume(device_t dev) { struct bhnd_pcie2hb_softc *sc; int error; sc = device_get_softc(dev); if ((error = bhnd_pcie2_generic_resume(dev))) return (error); /* Apply attach/resume work-arounds */ if ((error = bhnd_pcie2_wars_hwup(sc))) { bhnd_pcie2_generic_detach(dev); return (error); } return (0); } /** * Apply any hardware work-arounds that must be executed exactly once, early in * the attach process. * * This must be called after core enumeration and discovery of all applicable * quirks, but prior to probe/attach of any cores, parsing of * SPROM, etc. */ static int bhnd_pcie2_wars_early_once(struct bhnd_pcie2hb_softc *sc) { // TODO return (ENXIO); } /** * Apply any hardware workarounds that are required upon attach or resume * of the bridge device. */ static int bhnd_pcie2_wars_hwup(struct bhnd_pcie2hb_softc *sc) { // TODO return (ENXIO); } /** * Apply any hardware workarounds that are required upon detach or suspend * of the bridge device. */ static int bhnd_pcie2_wars_hwdown(struct bhnd_pcie2hb_softc *sc) { // TODO return (ENXIO); } static device_method_t bhnd_pcie2_hostb_methods[] = { /* Device interface */ DEVMETHOD(device_attach, bhnd_pcie2_hostb_attach), DEVMETHOD(device_detach, bhnd_pcie2_hostb_detach), DEVMETHOD(device_suspend, bhnd_pcie2_hostb_suspend), DEVMETHOD(device_resume, bhnd_pcie2_hostb_resume), DEVMETHOD_END }; DEFINE_CLASS_1(bhnd_hostb, bhnd_pcie2_hostb_driver, bhnd_pcie2_hostb_methods, sizeof(struct bhnd_pcie2hb_softc), bhnd_pcie2_driver); DRIVER_MODULE(bhnd_pcie2_hostb, bhnd, bhnd_pcie2_hostb_driver, bhnd_hostb_devclass, 0, 0); MODULE_VERSION(bhnd_pcie2_hostb, 1); MODULE_DEPEND(bhnd_pcie2_hostb, bhnd, 1, 1, 1); MODULE_DEPEND(bhnd_pcie2_hostb, bhnd_pcie2, 1, 1, 1); Index: head/sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- head/sys/dev/bhnd/siba/siba_bhndb.c (revision 300627) +++ head/sys/dev/bhnd/siba/siba_bhndb.c (revision 300628) @@ -1,285 +1,288 @@ /*- * Copyright (c) 2015 Landon Fuller * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "sibareg.h" #include "sibavar.h" /* * Supports attachment of siba(4) bus devices via a bhndb bridge. */ // // TODO: PCI rev < 6 interrupt handling // // On early PCI cores (rev < 6) interrupt masking is handled via interconnect // configuration registers (SBINTVEC), rather than the PCI_INT_MASK // config register. // // On those devices, we should handle interrupts locally using SBINTVEC, rather // than delegating to our parent bhndb device. // static int siba_bhndb_wars_hwup(struct siba_softc *sc); +/* Bridge-specific core device quirks */ enum { /** When PCIe-bridged, the D11 core's initiator request * timeout must be disabled to prevent D11 from entering a * RESP_TIMEOUT error state. */ SIBA_QUIRK_PCIE_D11_SB_TIMEOUT = (1<<0) }; -static struct bhnd_chip_quirk chip_quirks[] = { - {{ BHND_CHIP_IR(4311, HWREV_EQ(2)) }, SIBA_QUIRK_PCIE_D11_SB_TIMEOUT }, - {{ BHND_CHIP_IR(4312, HWREV_EQ(0)) }, SIBA_QUIRK_PCIE_D11_SB_TIMEOUT }, +static struct bhnd_device_quirk bridge_quirks[] = { + BHND_CHIP_QUIRK(4311, HWREV_EQ(2), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT), + BHND_CHIP_QUIRK(4312, HWREV_EQ(0), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT), }; +static struct bhnd_device bridge_devs[] = { + BHND_DEVICE(PCI, NULL, bridge_quirks), +}; + static int siba_bhndb_probe(device_t dev) { const struct bhnd_chipid *cid; /* Check bus type */ cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); if (cid->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); /* Delegate to default probe implementation */ return (siba_probe(dev)); } static int siba_bhndb_attach(device_t dev) { struct siba_softc *sc; const struct bhnd_chipid *chipid; int error; sc = device_get_softc(dev); /* Enumerate our children. */ chipid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); if ((error = siba_add_children(dev, chipid))) return (error); /* Initialize full bridge configuration */ error = BHNDB_INIT_FULL_CONFIG(device_get_parent(dev), dev, bhndb_siba_priority_table); if (error) return (error); /* Ask our parent bridge to find the corresponding bridge core */ sc->hostb_dev = BHNDB_FIND_HOSTB_DEVICE(device_get_parent(dev), dev); /* Call our superclass' implementation */ if ((error = siba_attach(dev))) return (error); /* Apply attach/resume work-arounds */ if ((error = siba_bhndb_wars_hwup(sc))) return (error); return (0); } static int siba_bhndb_resume(device_t dev) { struct siba_softc *sc; int error; sc = device_get_softc(dev); /* Apply attach/resume work-arounds */ if ((error = siba_bhndb_wars_hwup(sc))) return (error); /* Call our superclass' implementation */ return (siba_resume(dev)); } /* Suspend all references to the device's cfg register blocks */ static void siba_bhndb_suspend_cfgblocks(device_t dev, struct siba_devinfo *dinfo) { for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { if (dinfo->cfg[i] == NULL) continue; BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->cfg[i]->res); } } static int siba_bhndb_suspend_child(device_t dev, device_t child) { struct siba_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); dinfo = device_get_ivars(child); /* Suspend the child */ if ((error = bhnd_generic_br_suspend_child(dev, child))) return (error); /* Suspend resource references to the child's config registers */ siba_bhndb_suspend_cfgblocks(dev, dinfo); return (0); } static int siba_bhndb_resume_child(device_t dev, device_t child) { struct siba_devinfo *dinfo; int error; if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); if (!device_is_suspended(child)) return (EBUSY); dinfo = device_get_ivars(child); /* Resume all resource references to the child's config registers */ for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { if (dinfo->cfg[i] == NULL) continue; error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, SYS_RES_MEMORY, dinfo->cfg[i]->res); if (error) { siba_bhndb_suspend_cfgblocks(dev, dinfo); return (error); } } /* Resume the child */ if ((error = bhnd_generic_br_resume_child(dev, child))) { siba_bhndb_suspend_cfgblocks(dev, dinfo); return (error); } return (0); } /* Work-around implementation for SIBA_QUIRK_PCIE_D11_SB_TIMEOUT */ static int siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc) { struct siba_devinfo *dinfo; device_t d11; uint32_t imcfg; /* Only applies when bridged by PCIe */ if (bhnd_get_class(sc->hostb_dev) != BHND_DEVCLASS_PCIE) return (0); /* Only applies if there's a D11 core */ - d11 = bhnd_match_child(sc->dev, &(struct bhnd_core_match){ - .vendor = BHND_MFGID_BCM, - .device = BHND_COREID_D11, - .hwrev = BHND_HWREV_ANY, - .class = BHND_DEVCLASS_INVALID, - .unit = 0 + d11 = bhnd_match_child(sc->dev, &(struct bhnd_core_match) { + BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11), + BHND_MATCH_CORE_UNIT(0) }); if (d11 == NULL) return (0); /* Clear initiator timeout in D11's CFG0 block */ dinfo = device_get_ivars(d11); KASSERT(dinfo->cfg[0] != NULL, ("missing core config mapping")); imcfg = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW); imcfg &= ~SIBA_IMCL_RTO_MASK; bhnd_bus_write_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW, imcfg); return (0); } /** * Apply any hardware workarounds that are required upon attach or resume * of the bus. */ static int siba_bhndb_wars_hwup(struct siba_softc *sc) { uint32_t quirks; int error; - quirks = bhnd_chip_quirks(sc->hostb_dev, chip_quirks); + quirks = bhnd_device_quirks(sc->hostb_dev, bridge_devs, + sizeof(bridge_devs[0])); if (quirks & SIBA_QUIRK_PCIE_D11_SB_TIMEOUT) { if ((error = siba_bhndb_wars_pcie_clear_d11_timeout(sc))) return (error); } return (0); } static device_method_t siba_bhndb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, siba_bhndb_probe), DEVMETHOD(device_attach, siba_bhndb_attach), DEVMETHOD(device_resume, siba_bhndb_resume), /* Bus interface */ DEVMETHOD(bus_suspend_child, siba_bhndb_suspend_child), DEVMETHOD(bus_resume_child, siba_bhndb_resume_child), DEVMETHOD_END }; DEFINE_CLASS_2(bhnd, siba_bhndb_driver, siba_bhndb_methods, sizeof(struct siba_softc), bhnd_bhndb_driver, siba_driver); DRIVER_MODULE(siba_bhndb, bhndb, siba_bhndb_driver, bhnd_devclass, NULL, NULL); MODULE_VERSION(siba_bhndb, 1); MODULE_DEPEND(siba_bhndb, siba, 1, 1, 1); MODULE_DEPEND(siba_bhndb, bhnd, 1, 1, 1); MODULE_DEPEND(siba_bhndb, bhndb, 1, 1, 1);