Index: head/sys/arm/nvidia/tegra_uart.c =================================================================== --- head/sys/arm/nvidia/tegra_uart.c (revision 338073) +++ head/sys/arm/nvidia/tegra_uart.c (revision 338074) @@ -1,250 +1,250 @@ /*- * Copyright (c) 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * UART driver for Tegra SoCs. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * High-level UART interface. */ struct tegra_softc { struct ns8250_softc ns8250_base; clk_t clk; hwreset_t reset; }; /* * UART class interface. */ static int tegra_uart_attach(struct uart_softc *sc) { int rv; struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; rv = ns8250_bus_attach(sc); if (rv != 0) return (rv); ns8250->ier_rxbits = 0x1d; ns8250->ier_mask = 0xc0; ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; ns8250->ier |= ns8250->ier_rxbits; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); return (0); } static void tegra_uart_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; u_char ier; /* * turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. * All pending interrupt signals are reset when IER is set to 0. */ uart_lock(sc->sc_hwmtx); ier = uart_getreg(bas, REG_IER); uart_setreg(bas, REG_IER, ier & ns8250->ier_mask); uart_setreg(bas, REG_FCR, 0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void tegra_uart_ungrab(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_FCR, ns8250->fcr); uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static kobj_method_t tegra_methods[] = { KOBJMETHOD(uart_probe, ns8250_bus_probe), KOBJMETHOD(uart_attach, tegra_uart_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD(uart_grab, tegra_uart_grab), KOBJMETHOD(uart_ungrab, tegra_uart_ungrab), KOBJMETHOD_END }; static struct uart_class tegra_uart_class = { "tegra class", tegra_methods, sizeof(struct tegra_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, .uc_rclk = 0, }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS(compat_data); /* * UART Driver interface. */ static int uart_fdt_get_shift1(phandle_t node) { pcell_t shift; if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) shift = 2; return ((int)shift); } static int tegra_uart_probe(device_t dev) { struct tegra_softc *sc; phandle_t node; uint64_t freq; int shift; int rv; const struct ofw_compat_data *cd; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); cd = ofw_bus_search_compatible(dev, compat_data); if (cd->ocd_data == 0) return (ENXIO); sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; rv = hwreset_get_by_ofw_name(dev, 0, "serial", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get 'serial' reset\n"); return (ENXIO); } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(dev, "Cannot unreset 'serial' reset\n"); return (ENXIO); } node = ofw_bus_get_node(dev); shift = uart_fdt_get_shift1(node); rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get UART clock: %d\n", rv); return (ENXIO); } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable UART clock: %d\n", rv); return (ENXIO); } rv = clk_get_freq(sc->clk, &freq); if (rv != 0) { device_printf(dev, "Cannot enable UART clock: %d\n", rv); return (ENXIO); } - return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0)); + return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0)); } static int tegra_uart_detach(device_t dev) { struct tegra_softc *sc; sc = device_get_softc(dev); if (sc->clk != NULL) { clk_release(sc->clk); } return (uart_bus_detach(dev)); } static device_method_t tegra_uart_bus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_uart_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, tegra_uart_detach), { 0, 0 } }; static driver_t tegra_uart_driver = { uart_driver_name, tegra_uart_bus_methods, sizeof(struct tegra_softc), }; DRIVER_MODULE(tegra_uart, simplebus, tegra_uart_driver, uart_devclass, 0, 0); Index: head/sys/dev/acpica/acpi.c =================================================================== --- head/sys/dev/acpica/acpi.c (revision 338073) +++ head/sys/dev/acpica/acpi.c (revision 338074) @@ -1,4192 +1,4201 @@ /*- * Copyright (c) 2000 Takanori Watanabe * Copyright (c) 2000 Mitsuru IWASAKI * Copyright (c) 2000, 2001 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices"); /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_BUS ACPI_MODULE_NAME("ACPI") static d_open_t acpiopen; static d_close_t acpiclose; static d_ioctl_t acpiioctl; static struct cdevsw acpi_cdevsw = { .d_version = D_VERSION, .d_open = acpiopen, .d_close = acpiclose, .d_ioctl = acpiioctl, .d_name = "acpi", }; struct acpi_interface { ACPI_STRING *data; int num; }; static char *sysres_ids[] = { "PNP0C01", "PNP0C02", NULL }; static char *pcilink_ids[] = { "PNP0C0F", NULL }; /* Global mutex for locking access to the ACPI subsystem. */ struct mtx acpi_mutex; struct callout acpi_sleep_timer; /* Bitmap of device quirks. */ int acpi_quirks; /* Supported sleep states. */ static BOOLEAN acpi_sleep_states[ACPI_S_STATE_COUNT]; static void acpi_lookup(void *arg, const char *name, device_t *dev); static int acpi_modevent(struct module *mod, int event, void *junk); static int acpi_probe(device_t dev); static int acpi_attach(device_t dev); static int acpi_suspend(device_t dev); static int acpi_resume(device_t dev); static int acpi_shutdown(device_t dev); static device_t acpi_add_child(device_t bus, u_int order, const char *name, int unit); static int acpi_print_child(device_t bus, device_t child); static void acpi_probe_nomatch(device_t bus, device_t child); static void acpi_driver_added(device_t dev, driver_t *driver); static int acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result); static int acpi_write_ivar(device_t dev, device_t child, int index, uintptr_t value); static struct resource_list *acpi_get_rlist(device_t dev, device_t child); static void acpi_reserve_resources(device_t dev); static int acpi_sysres_alloc(device_t dev); static int acpi_set_resource(device_t dev, device_t child, int type, int rid, rman_res_t start, rman_res_t count); static struct resource *acpi_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); static int acpi_adjust_resource(device_t bus, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end); static int acpi_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static void acpi_delete_resource(device_t bus, device_t child, int type, int rid); static uint32_t acpi_isa_get_logicalid(device_t dev); static int acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count); static char *acpi_device_id_probe(device_t bus, device_t dev, char **ids); static ACPI_STATUS acpi_device_eval_obj(device_t bus, device_t dev, ACPI_STRING pathname, ACPI_OBJECT_LIST *parameters, ACPI_BUFFER *ret); static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level, void *context, void **retval); static ACPI_STATUS acpi_device_scan_children(device_t bus, device_t dev, int max_depth, acpi_scan_cb_t user_fn, void *arg); static int acpi_set_powerstate(device_t child, int state); static int acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids); static void acpi_probe_children(device_t bus); static void acpi_probe_order(ACPI_HANDLE handle, int *order); static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status); static void acpi_sleep_enable(void *arg); static ACPI_STATUS acpi_sleep_disable(struct acpi_softc *sc); static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, int state); static void acpi_shutdown_final(void *arg, int howto); static void acpi_enable_fixed_events(struct acpi_softc *sc); static BOOLEAN acpi_has_hid(ACPI_HANDLE handle); static void acpi_resync_clock(struct acpi_softc *sc); static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate); static int acpi_wake_run_prep(ACPI_HANDLE handle, int sstate); static int acpi_wake_prep_walk(int sstate); static int acpi_wake_sysctl_walk(device_t dev); static int acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_system_eventhandler_sleep(void *arg, int state); static void acpi_system_eventhandler_wakeup(void *arg, int state); static int acpi_sname2sstate(const char *sname); static const char *acpi_sstate2sname(int sstate); static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_pm_func(u_long cmd, void *arg, ...); static int acpi_child_location_str_method(device_t acdev, device_t child, char *buf, size_t buflen); static int acpi_child_pnpinfo_str_method(device_t acdev, device_t child, char *buf, size_t buflen); #if defined(__i386__) || defined(__amd64__) static void acpi_enable_pcie(void); #endif static void acpi_hint_device_unit(device_t acdev, device_t child, const char *name, int *unitp); static void acpi_reset_interfaces(device_t dev); static device_method_t acpi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_probe), DEVMETHOD(device_attach, acpi_attach), DEVMETHOD(device_shutdown, acpi_shutdown), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_suspend, acpi_suspend), DEVMETHOD(device_resume, acpi_resume), /* Bus interface */ DEVMETHOD(bus_add_child, acpi_add_child), DEVMETHOD(bus_print_child, acpi_print_child), DEVMETHOD(bus_probe_nomatch, acpi_probe_nomatch), DEVMETHOD(bus_driver_added, acpi_driver_added), DEVMETHOD(bus_read_ivar, acpi_read_ivar), DEVMETHOD(bus_write_ivar, acpi_write_ivar), DEVMETHOD(bus_get_resource_list, acpi_get_rlist), DEVMETHOD(bus_set_resource, acpi_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_alloc_resource, acpi_alloc_resource), DEVMETHOD(bus_adjust_resource, acpi_adjust_resource), DEVMETHOD(bus_release_resource, acpi_release_resource), DEVMETHOD(bus_delete_resource, acpi_delete_resource), DEVMETHOD(bus_child_pnpinfo_str, acpi_child_pnpinfo_str_method), DEVMETHOD(bus_child_location_str, acpi_child_location_str_method), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_hint_device_unit, acpi_hint_device_unit), DEVMETHOD(bus_get_cpus, acpi_get_cpus), DEVMETHOD(bus_get_domain, acpi_get_domain), /* ACPI bus */ DEVMETHOD(acpi_id_probe, acpi_device_id_probe), DEVMETHOD(acpi_evaluate_object, acpi_device_eval_obj), DEVMETHOD(acpi_pwr_for_sleep, acpi_device_pwr_for_sleep), DEVMETHOD(acpi_scan_children, acpi_device_scan_children), /* ISA emulation */ DEVMETHOD(isa_pnp_probe, acpi_isa_pnp_probe), DEVMETHOD_END }; static driver_t acpi_driver = { "acpi", acpi_methods, sizeof(struct acpi_softc), }; static devclass_t acpi_devclass; DRIVER_MODULE(acpi, nexus, acpi_driver, acpi_devclass, acpi_modevent, 0); MODULE_VERSION(acpi, 1); ACPI_SERIAL_DECL(acpi, "ACPI root bus"); /* Local pools for managing system resources for ACPI child devices. */ static struct rman acpi_rman_io, acpi_rman_mem; #define ACPI_MINIMUM_AWAKETIME 5 /* Holds the description of the acpi0 device. */ static char acpi_desc[ACPI_OEM_ID_SIZE + ACPI_OEM_TABLE_ID_SIZE + 2]; SYSCTL_NODE(_debug, OID_AUTO, acpi, CTLFLAG_RD, NULL, "ACPI debugging"); static char acpi_ca_version[12]; SYSCTL_STRING(_debug_acpi, OID_AUTO, acpi_ca_version, CTLFLAG_RD, acpi_ca_version, 0, "Version of Intel ACPI-CA"); /* * Allow overriding _OSI methods. */ static char acpi_install_interface[256]; TUNABLE_STR("hw.acpi.install_interface", acpi_install_interface, sizeof(acpi_install_interface)); static char acpi_remove_interface[256]; TUNABLE_STR("hw.acpi.remove_interface", acpi_remove_interface, sizeof(acpi_remove_interface)); /* Allow users to dump Debug objects without ACPI debugger. */ static int acpi_debug_objects; TUNABLE_INT("debug.acpi.enable_debug_objects", &acpi_debug_objects); SYSCTL_PROC(_debug_acpi, OID_AUTO, enable_debug_objects, CTLFLAG_RW | CTLTYPE_INT, NULL, 0, acpi_debug_objects_sysctl, "I", "Enable Debug objects"); /* Allow the interpreter to ignore common mistakes in BIOS. */ static int acpi_interpreter_slack = 1; TUNABLE_INT("debug.acpi.interpreter_slack", &acpi_interpreter_slack); SYSCTL_INT(_debug_acpi, OID_AUTO, interpreter_slack, CTLFLAG_RDTUN, &acpi_interpreter_slack, 1, "Turn on interpreter slack mode."); /* Ignore register widths set by FADT and use default widths instead. */ static int acpi_ignore_reg_width = 1; TUNABLE_INT("debug.acpi.default_register_width", &acpi_ignore_reg_width); SYSCTL_INT(_debug_acpi, OID_AUTO, default_register_width, CTLFLAG_RDTUN, &acpi_ignore_reg_width, 1, "Ignore register widths set by FADT"); /* Allow users to override quirks. */ TUNABLE_INT("debug.acpi.quirks", &acpi_quirks); int acpi_susp_bounce; SYSCTL_INT(_debug_acpi, OID_AUTO, suspend_bounce, CTLFLAG_RW, &acpi_susp_bounce, 0, "Don't actually suspend, just test devices."); /* * ACPI can only be loaded as a module by the loader; activating it after * system bootstrap time is not useful, and can be fatal to the system. * It also cannot be unloaded, since the entire system bus hierarchy hangs * off it. */ static int acpi_modevent(struct module *mod, int event, void *junk) { switch (event) { case MOD_LOAD: if (!cold) { printf("The ACPI driver cannot be loaded after boot.\n"); return (EPERM); } break; case MOD_UNLOAD: if (!cold && power_pm_get_type() == POWER_PM_TYPE_ACPI) return (EBUSY); break; default: break; } return (0); } /* * Perform early initialization. */ ACPI_STATUS acpi_Startup(void) { static int started = 0; ACPI_STATUS status; int val; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* Only run the startup code once. The MADT driver also calls this. */ if (started) return_VALUE (AE_OK); started = 1; /* * Initialize the ACPICA subsystem. */ if (ACPI_FAILURE(status = AcpiInitializeSubsystem())) { printf("ACPI: Could not initialize Subsystem: %s\n", AcpiFormatException(status)); return_VALUE (status); } /* * Pre-allocate space for RSDT/XSDT and DSDT tables and allow resizing * if more tables exist. */ if (ACPI_FAILURE(status = AcpiInitializeTables(NULL, 2, TRUE))) { printf("ACPI: Table initialisation failed: %s\n", AcpiFormatException(status)); return_VALUE (status); } /* Set up any quirks we have for this system. */ if (acpi_quirks == ACPI_Q_OK) acpi_table_quirks(&acpi_quirks); /* If the user manually set the disabled hint to 0, force-enable ACPI. */ if (resource_int_value("acpi", 0, "disabled", &val) == 0 && val == 0) acpi_quirks &= ~ACPI_Q_BROKEN; if (acpi_quirks & ACPI_Q_BROKEN) { printf("ACPI disabled by blacklist. Contact your BIOS vendor.\n"); status = AE_SUPPORT; } return_VALUE (status); } /* * Detect ACPI and perform early initialisation. */ int acpi_identify(void) { ACPI_TABLE_RSDP *rsdp; ACPI_TABLE_HEADER *rsdt; ACPI_PHYSICAL_ADDRESS paddr; struct sbuf sb; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (!cold) return (ENXIO); /* Check that we haven't been disabled with a hint. */ if (resource_disabled("acpi", 0)) return (ENXIO); /* Check for other PM systems. */ if (power_pm_get_type() != POWER_PM_TYPE_NONE && power_pm_get_type() != POWER_PM_TYPE_ACPI) { printf("ACPI identify failed, other PM system enabled.\n"); return (ENXIO); } /* Initialize root tables. */ if (ACPI_FAILURE(acpi_Startup())) { printf("ACPI: Try disabling either ACPI or apic support.\n"); return (ENXIO); } if ((paddr = AcpiOsGetRootPointer()) == 0 || (rsdp = AcpiOsMapMemory(paddr, sizeof(ACPI_TABLE_RSDP))) == NULL) return (ENXIO); if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress != 0) paddr = (ACPI_PHYSICAL_ADDRESS)rsdp->XsdtPhysicalAddress; else paddr = (ACPI_PHYSICAL_ADDRESS)rsdp->RsdtPhysicalAddress; AcpiOsUnmapMemory(rsdp, sizeof(ACPI_TABLE_RSDP)); if ((rsdt = AcpiOsMapMemory(paddr, sizeof(ACPI_TABLE_HEADER))) == NULL) return (ENXIO); sbuf_new(&sb, acpi_desc, sizeof(acpi_desc), SBUF_FIXEDLEN); sbuf_bcat(&sb, rsdt->OemId, ACPI_OEM_ID_SIZE); sbuf_trim(&sb); sbuf_putc(&sb, ' '); sbuf_bcat(&sb, rsdt->OemTableId, ACPI_OEM_TABLE_ID_SIZE); sbuf_trim(&sb); sbuf_finish(&sb); sbuf_delete(&sb); AcpiOsUnmapMemory(rsdt, sizeof(ACPI_TABLE_HEADER)); snprintf(acpi_ca_version, sizeof(acpi_ca_version), "%x", ACPI_CA_VERSION); return (0); } /* * Fetch some descriptive data from ACPI to put in our attach message. */ static int acpi_probe(device_t dev) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); device_set_desc(dev, acpi_desc); return_VALUE (BUS_PROBE_NOWILDCARD); } static int acpi_attach(device_t dev) { struct acpi_softc *sc; ACPI_STATUS status; int error, state; UINT32 flags; UINT8 TypeA, TypeB; char *env; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc(dev); sc->acpi_dev = dev; callout_init(&sc->susp_force_to, 1); error = ENXIO; /* Initialize resource manager. */ acpi_rman_io.rm_type = RMAN_ARRAY; acpi_rman_io.rm_start = 0; acpi_rman_io.rm_end = 0xffff; acpi_rman_io.rm_descr = "ACPI I/O ports"; if (rman_init(&acpi_rman_io) != 0) panic("acpi rman_init IO ports failed"); acpi_rman_mem.rm_type = RMAN_ARRAY; acpi_rman_mem.rm_descr = "ACPI I/O memory addresses"; if (rman_init(&acpi_rman_mem) != 0) panic("acpi rman_init memory failed"); /* Initialise the ACPI mutex */ mtx_init(&acpi_mutex, "ACPI global lock", NULL, MTX_DEF); /* * Set the globals from our tunables. This is needed because ACPI-CA * uses UINT8 for some values and we have no tunable_byte. */ AcpiGbl_EnableInterpreterSlack = acpi_interpreter_slack ? TRUE : FALSE; AcpiGbl_EnableAmlDebugObject = acpi_debug_objects ? TRUE : FALSE; AcpiGbl_UseDefaultRegisterWidths = acpi_ignore_reg_width ? TRUE : FALSE; #ifndef ACPI_DEBUG /* * Disable all debugging layers and levels. */ AcpiDbgLayer = 0; AcpiDbgLevel = 0; #endif /* Override OS interfaces if the user requested. */ acpi_reset_interfaces(dev); /* Load ACPI name space. */ status = AcpiLoadTables(); if (ACPI_FAILURE(status)) { device_printf(dev, "Could not load Namespace: %s\n", AcpiFormatException(status)); goto out; } #if defined(__i386__) || defined(__amd64__) /* Handle MCFG table if present. */ acpi_enable_pcie(); #endif /* * Note that some systems (specifically, those with namespace evaluation * issues that require the avoidance of parts of the namespace) must * avoid running _INI and _STA on everything, as well as dodging the final * object init pass. * * For these devices, we set ACPI_NO_DEVICE_INIT and ACPI_NO_OBJECT_INIT). * * XXX We should arrange for the object init pass after we have attached * all our child devices, but on many systems it works here. */ flags = 0; if (testenv("debug.acpi.avoid")) flags = ACPI_NO_DEVICE_INIT | ACPI_NO_OBJECT_INIT; /* Bring the hardware and basic handlers online. */ if (ACPI_FAILURE(status = AcpiEnableSubsystem(flags))) { device_printf(dev, "Could not enable ACPI: %s\n", AcpiFormatException(status)); goto out; } /* * Call the ECDT probe function to provide EC functionality before * the namespace has been evaluated. * * XXX This happens before the sysresource devices have been probed and * attached so its resources come from nexus0. In practice, this isn't * a problem but should be addressed eventually. */ acpi_ec_ecdt_probe(dev); /* Bring device objects and regions online. */ if (ACPI_FAILURE(status = AcpiInitializeObjects(flags))) { device_printf(dev, "Could not initialize ACPI objects: %s\n", AcpiFormatException(status)); goto out; } /* * Setup our sysctl tree. * * XXX: This doesn't check to make sure that none of these fail. */ sysctl_ctx_init(&sc->acpi_sysctl_ctx); sc->acpi_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, device_get_name(dev), CTLFLAG_RD, 0, ""); SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "supported_sleep_state", CTLTYPE_STRING | CTLFLAG_RD, 0, 0, acpi_supported_sleep_state_sysctl, "A", "List supported ACPI sleep states."); SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "power_button_state", CTLTYPE_STRING | CTLFLAG_RW, &sc->acpi_power_button_sx, 0, acpi_sleep_state_sysctl, "A", "Power button ACPI sleep state."); SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "sleep_button_state", CTLTYPE_STRING | CTLFLAG_RW, &sc->acpi_sleep_button_sx, 0, acpi_sleep_state_sysctl, "A", "Sleep button ACPI sleep state."); SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "lid_switch_state", CTLTYPE_STRING | CTLFLAG_RW, &sc->acpi_lid_switch_sx, 0, acpi_sleep_state_sysctl, "A", "Lid ACPI sleep state. Set to S3 if you want to suspend your laptop when close the Lid."); SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "standby_state", CTLTYPE_STRING | CTLFLAG_RW, &sc->acpi_standby_sx, 0, acpi_sleep_state_sysctl, "A", ""); SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "suspend_state", CTLTYPE_STRING | CTLFLAG_RW, &sc->acpi_suspend_sx, 0, acpi_sleep_state_sysctl, "A", ""); SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "sleep_delay", CTLFLAG_RW, &sc->acpi_sleep_delay, 0, "sleep delay in seconds"); SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "s4bios", CTLFLAG_RW, &sc->acpi_s4bios, 0, "S4BIOS mode"); SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "verbose", CTLFLAG_RW, &sc->acpi_verbose, 0, "verbose mode"); SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "disable_on_reboot", CTLFLAG_RW, &sc->acpi_do_disable, 0, "Disable ACPI when rebooting/halting system"); SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "handle_reboot", CTLFLAG_RW, &sc->acpi_handle_reboot, 0, "Use ACPI Reset Register to reboot"); /* * Default to 1 second before sleeping to give some machines time to * stabilize. */ sc->acpi_sleep_delay = 1; if (bootverbose) sc->acpi_verbose = 1; if ((env = kern_getenv("hw.acpi.verbose")) != NULL) { if (strcmp(env, "0") != 0) sc->acpi_verbose = 1; freeenv(env); } /* Only enable reboot by default if the FADT says it is available. */ if (AcpiGbl_FADT.Flags & ACPI_FADT_RESET_REGISTER) sc->acpi_handle_reboot = 1; #if !ACPI_REDUCED_HARDWARE /* Only enable S4BIOS by default if the FACS says it is available. */ if (AcpiGbl_FACS != NULL && AcpiGbl_FACS->Flags & ACPI_FACS_S4_BIOS_PRESENT) sc->acpi_s4bios = 1; #endif /* Probe all supported sleep states. */ acpi_sleep_states[ACPI_STATE_S0] = TRUE; for (state = ACPI_STATE_S1; state < ACPI_S_STATE_COUNT; state++) if (ACPI_SUCCESS(AcpiEvaluateObject(ACPI_ROOT_OBJECT, __DECONST(char *, AcpiGbl_SleepStateNames[state]), NULL, NULL)) && ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) acpi_sleep_states[state] = TRUE; /* * Dispatch the default sleep state to devices. The lid switch is set * to UNKNOWN by default to avoid surprising users. */ sc->acpi_power_button_sx = acpi_sleep_states[ACPI_STATE_S5] ? ACPI_STATE_S5 : ACPI_STATE_UNKNOWN; sc->acpi_lid_switch_sx = ACPI_STATE_UNKNOWN; sc->acpi_standby_sx = acpi_sleep_states[ACPI_STATE_S1] ? ACPI_STATE_S1 : ACPI_STATE_UNKNOWN; sc->acpi_suspend_sx = acpi_sleep_states[ACPI_STATE_S3] ? ACPI_STATE_S3 : ACPI_STATE_UNKNOWN; /* Pick the first valid sleep state for the sleep button default. */ sc->acpi_sleep_button_sx = ACPI_STATE_UNKNOWN; for (state = ACPI_STATE_S1; state <= ACPI_STATE_S4; state++) if (acpi_sleep_states[state]) { sc->acpi_sleep_button_sx = state; break; } acpi_enable_fixed_events(sc); /* * Scan the namespace and attach/initialise children. */ /* Register our shutdown handler. */ EVENTHANDLER_REGISTER(shutdown_final, acpi_shutdown_final, sc, SHUTDOWN_PRI_LAST); /* * Register our acpi event handlers. * XXX should be configurable eg. via userland policy manager. */ EVENTHANDLER_REGISTER(acpi_sleep_event, acpi_system_eventhandler_sleep, sc, ACPI_EVENT_PRI_LAST); EVENTHANDLER_REGISTER(acpi_wakeup_event, acpi_system_eventhandler_wakeup, sc, ACPI_EVENT_PRI_LAST); /* Flag our initial states. */ sc->acpi_enabled = TRUE; sc->acpi_sstate = ACPI_STATE_S0; sc->acpi_sleep_disabled = TRUE; /* Create the control device */ sc->acpi_dev_t = make_dev(&acpi_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664, "acpi"); sc->acpi_dev_t->si_drv1 = sc; if ((error = acpi_machdep_init(dev))) goto out; /* Register ACPI again to pass the correct argument of pm_func. */ power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, sc); if (!acpi_disabled("bus")) { EVENTHANDLER_REGISTER(dev_lookup, acpi_lookup, NULL, 1000); acpi_probe_children(dev); } /* Update all GPEs and enable runtime GPEs. */ status = AcpiUpdateAllGpes(); if (ACPI_FAILURE(status)) device_printf(dev, "Could not update all GPEs: %s\n", AcpiFormatException(status)); /* Allow sleep request after a while. */ callout_init_mtx(&acpi_sleep_timer, &acpi_mutex, 0); callout_reset(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME, acpi_sleep_enable, sc); error = 0; out: return_VALUE (error); } static void acpi_set_power_children(device_t dev, int state) { device_t child; device_t *devlist; int dstate, i, numdevs; if (device_get_children(dev, &devlist, &numdevs) != 0) return; /* * Retrieve and set D-state for the sleep state if _SxD is present. * Skip children who aren't attached since they are handled separately. */ for (i = 0; i < numdevs; i++) { child = devlist[i]; dstate = state; if (device_is_attached(child) && acpi_device_pwr_for_sleep(dev, child, &dstate) == 0) acpi_set_powerstate(child, dstate); } free(devlist, M_TEMP); } static int acpi_suspend(device_t dev) { int error; GIANT_REQUIRED; error = bus_generic_suspend(dev); if (error == 0) acpi_set_power_children(dev, ACPI_STATE_D3); return (error); } static int acpi_resume(device_t dev) { GIANT_REQUIRED; acpi_set_power_children(dev, ACPI_STATE_D0); return (bus_generic_resume(dev)); } static int acpi_shutdown(device_t dev) { GIANT_REQUIRED; /* Allow children to shutdown first. */ bus_generic_shutdown(dev); /* * Enable any GPEs that are able to power-on the system (i.e., RTC). * Also, disable any that are not valid for this state (most). */ acpi_wake_prep_walk(ACPI_STATE_S5); return (0); } /* * Handle a new device being added */ static device_t acpi_add_child(device_t bus, u_int order, const char *name, int unit) { struct acpi_device *ad; device_t child; if ((ad = malloc(sizeof(*ad), M_ACPIDEV, M_NOWAIT | M_ZERO)) == NULL) return (NULL); resource_list_init(&ad->ad_rl); child = device_add_child_ordered(bus, order, name, unit); if (child != NULL) device_set_ivars(child, ad); else free(ad, M_ACPIDEV); return (child); } static int acpi_print_child(device_t bus, device_t child) { struct acpi_device *adev = device_get_ivars(child); struct resource_list *rl = &adev->ad_rl; int retval = 0; retval += bus_print_child_header(bus, child); retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%jd"); if (device_get_flags(child)) retval += printf(" flags %#x", device_get_flags(child)); retval += bus_print_child_domain(bus, child); retval += bus_print_child_footer(bus, child); return (retval); } /* * If this device is an ACPI child but no one claimed it, attempt * to power it off. We'll power it back up when a driver is added. * * XXX Disabled for now since many necessary devices (like fdc and * ATA) don't claim the devices we created for them but still expect * them to be powered up. */ static void acpi_probe_nomatch(device_t bus, device_t child) { #ifdef ACPI_ENABLE_POWERDOWN_NODRIVER acpi_set_powerstate(child, ACPI_STATE_D3); #endif } /* * If a new driver has a chance to probe a child, first power it up. * * XXX Disabled for now (see acpi_probe_nomatch for details). */ static void acpi_driver_added(device_t dev, driver_t *driver) { device_t child, *devlist; int i, numdevs; DEVICE_IDENTIFY(driver, dev); if (device_get_children(dev, &devlist, &numdevs)) return; for (i = 0; i < numdevs; i++) { child = devlist[i]; if (device_get_state(child) == DS_NOTPRESENT) { #ifdef ACPI_ENABLE_POWERDOWN_NODRIVER acpi_set_powerstate(child, ACPI_STATE_D0); if (device_probe_and_attach(child) != 0) acpi_set_powerstate(child, ACPI_STATE_D3); #else device_probe_and_attach(child); #endif } } free(devlist, M_TEMP); } /* Location hint for devctl(8) */ static int acpi_child_location_str_method(device_t cbdev, device_t child, char *buf, size_t buflen) { struct acpi_device *dinfo = device_get_ivars(child); char buf2[32]; int pxm; if (dinfo->ad_handle) { snprintf(buf, buflen, "handle=%s", acpi_name(dinfo->ad_handle)); if (ACPI_SUCCESS(acpi_GetInteger(dinfo->ad_handle, "_PXM", &pxm))) { snprintf(buf2, 32, " _PXM=%d", pxm); strlcat(buf, buf2, buflen); } } else { snprintf(buf, buflen, "unknown"); } return (0); } /* PnP information for devctl(8) */ static int acpi_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen) { struct acpi_device *dinfo = device_get_ivars(child); ACPI_DEVICE_INFO *adinfo; if (ACPI_FAILURE(AcpiGetObjectInfo(dinfo->ad_handle, &adinfo))) { snprintf(buf, buflen, "unknown"); return (0); } snprintf(buf, buflen, "_HID=%s _UID=%lu", (adinfo->Valid & ACPI_VALID_HID) ? adinfo->HardwareId.String : "none", (adinfo->Valid & ACPI_VALID_UID) ? strtoul(adinfo->UniqueId.String, NULL, 10) : 0UL); AcpiOsFree(adinfo); return (0); } /* * Handle per-device ivars */ static int acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { struct acpi_device *ad; if ((ad = device_get_ivars(child)) == NULL) { device_printf(child, "device has no ivars\n"); return (ENOENT); } /* ACPI and ISA compatibility ivars */ switch(index) { case ACPI_IVAR_HANDLE: *(ACPI_HANDLE *)result = ad->ad_handle; break; case ACPI_IVAR_PRIVATE: *(void **)result = ad->ad_private; break; case ACPI_IVAR_FLAGS: *(int *)result = ad->ad_flags; break; case ISA_IVAR_VENDORID: case ISA_IVAR_SERIAL: case ISA_IVAR_COMPATID: *(int *)result = -1; break; case ISA_IVAR_LOGICALID: *(int *)result = acpi_isa_get_logicalid(child); break; case PCI_IVAR_CLASS: *(uint8_t*)result = (ad->ad_cls_class >> 16) & 0xff; break; case PCI_IVAR_SUBCLASS: *(uint8_t*)result = (ad->ad_cls_class >> 8) & 0xff; break; case PCI_IVAR_PROGIF: *(uint8_t*)result = (ad->ad_cls_class >> 0) & 0xff; break; default: return (ENOENT); } return (0); } static int acpi_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { struct acpi_device *ad; if ((ad = device_get_ivars(child)) == NULL) { device_printf(child, "device has no ivars\n"); return (ENOENT); } switch(index) { case ACPI_IVAR_HANDLE: ad->ad_handle = (ACPI_HANDLE)value; break; case ACPI_IVAR_PRIVATE: ad->ad_private = (void *)value; break; case ACPI_IVAR_FLAGS: ad->ad_flags = (int)value; break; default: panic("bad ivar write request (%d)", index); return (ENOENT); } return (0); } /* * Handle child resource allocation/removal */ static struct resource_list * acpi_get_rlist(device_t dev, device_t child) { struct acpi_device *ad; ad = device_get_ivars(child); return (&ad->ad_rl); } static int acpi_match_resource_hint(device_t dev, int type, long value) { struct acpi_device *ad = device_get_ivars(dev); struct resource_list *rl = &ad->ad_rl; struct resource_list_entry *rle; STAILQ_FOREACH(rle, rl, link) { if (rle->type != type) continue; if (rle->start <= value && rle->end >= value) return (1); } return (0); } /* * Wire device unit numbers based on resource matches in hints. */ static void acpi_hint_device_unit(device_t acdev, device_t child, const char *name, int *unitp) { const char *s; long value; int line, matches, unit; /* * Iterate over all the hints for the devices with the specified * name to see if one's resources are a subset of this device. */ line = 0; while (resource_find_dev(&line, name, &unit, "at", NULL) == 0) { /* Must have an "at" for acpi or isa. */ resource_string_value(name, unit, "at", &s); if (!(strcmp(s, "acpi0") == 0 || strcmp(s, "acpi") == 0 || strcmp(s, "isa0") == 0 || strcmp(s, "isa") == 0)) continue; /* * Check for matching resources. We must have at least one match. * Since I/O and memory resources cannot be shared, if we get a * match on either of those, ignore any mismatches in IRQs or DRQs. * * XXX: We may want to revisit this to be more lenient and wire * as long as it gets one match. */ matches = 0; if (resource_long_value(name, unit, "port", &value) == 0) { /* * Floppy drive controllers are notorious for having a * wide variety of resources not all of which include the * first port that is specified by the hint (typically * 0x3f0) (see the comment above fdc_isa_alloc_resources() * in fdc_isa.c). However, they do all seem to include * port + 2 (e.g. 0x3f2) so for a floppy device, look for * 'value + 2' in the port resources instead of the hint * value. */ if (strcmp(name, "fdc") == 0) value += 2; if (acpi_match_resource_hint(child, SYS_RES_IOPORT, value)) matches++; else continue; } if (resource_long_value(name, unit, "maddr", &value) == 0) { if (acpi_match_resource_hint(child, SYS_RES_MEMORY, value)) matches++; else continue; } if (matches > 0) goto matched; if (resource_long_value(name, unit, "irq", &value) == 0) { if (acpi_match_resource_hint(child, SYS_RES_IRQ, value)) matches++; else continue; } if (resource_long_value(name, unit, "drq", &value) == 0) { if (acpi_match_resource_hint(child, SYS_RES_DRQ, value)) matches++; else continue; } matched: if (matches > 0) { /* We have a winner! */ *unitp = unit; break; } } } /* * Fetch the NUMA domain for a device by mapping the value returned by * _PXM to a NUMA domain. If the device does not have a _PXM method, * -2 is returned. If any other error occurs, -1 is returned. */ static int acpi_parse_pxm(device_t dev) { #ifdef NUMA #if defined(__i386__) || defined(__amd64__) ACPI_HANDLE handle; ACPI_STATUS status; int pxm; handle = acpi_get_handle(dev); if (handle == NULL) return (-2); status = acpi_GetInteger(handle, "_PXM", &pxm); if (ACPI_SUCCESS(status)) return (acpi_map_pxm_to_vm_domainid(pxm)); if (status == AE_NOT_FOUND) return (-2); #endif #endif return (-1); } int acpi_get_cpus(device_t dev, device_t child, enum cpu_sets op, size_t setsize, cpuset_t *cpuset) { int d, error; d = acpi_parse_pxm(child); if (d < 0) return (bus_generic_get_cpus(dev, child, op, setsize, cpuset)); switch (op) { case LOCAL_CPUS: if (setsize != sizeof(cpuset_t)) return (EINVAL); *cpuset = cpuset_domain[d]; return (0); case INTR_CPUS: error = bus_generic_get_cpus(dev, child, op, setsize, cpuset); if (error != 0) return (error); if (setsize != sizeof(cpuset_t)) return (EINVAL); CPU_AND(cpuset, &cpuset_domain[d]); return (0); default: return (bus_generic_get_cpus(dev, child, op, setsize, cpuset)); } } /* * Fetch the NUMA domain for the given device 'dev'. * * If a device has a _PXM method, map that to a NUMA domain. * Otherwise, pass the request up to the parent. * If there's no matching domain or the domain cannot be * determined, return ENOENT. */ int acpi_get_domain(device_t dev, device_t child, int *domain) { int d; d = acpi_parse_pxm(child); if (d >= 0) { *domain = d; return (0); } if (d == -1) return (ENOENT); /* No _PXM node; go up a level */ return (bus_generic_get_domain(dev, child, domain)); } /* * Pre-allocate/manage all memory and IO resources. Since rman can't handle * duplicates, we merge any in the sysresource attach routine. */ static int acpi_sysres_alloc(device_t dev) { struct resource *res; struct resource_list *rl; struct resource_list_entry *rle; struct rman *rm; device_t *children; int child_count, i; /* * Probe/attach any sysresource devices. This would be unnecessary if we * had multi-pass probe/attach. */ if (device_get_children(dev, &children, &child_count) != 0) return (ENXIO); for (i = 0; i < child_count; i++) { if (ACPI_ID_PROBE(dev, children[i], sysres_ids) != NULL) device_probe_and_attach(children[i]); } free(children, M_TEMP); rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); STAILQ_FOREACH(rle, rl, link) { if (rle->res != NULL) { device_printf(dev, "duplicate resource for %jx\n", rle->start); continue; } /* Only memory and IO resources are valid here. */ switch (rle->type) { case SYS_RES_IOPORT: rm = &acpi_rman_io; break; case SYS_RES_MEMORY: rm = &acpi_rman_mem; break; default: continue; } /* Pre-allocate resource and add to our rman pool. */ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, rle->type, &rle->rid, rle->start, rle->start + rle->count - 1, rle->count, 0); if (res != NULL) { rman_manage_region(rm, rman_get_start(res), rman_get_end(res)); rle->res = res; } else if (bootverbose) device_printf(dev, "reservation of %jx, %jx (%d) failed\n", rle->start, rle->count, rle->type); } return (0); } /* * Reserve declared resources for devices found during attach once system * resources have been allocated. */ static void acpi_reserve_resources(device_t dev) { struct resource_list_entry *rle; struct resource_list *rl; struct acpi_device *ad; struct acpi_softc *sc; device_t *children; int child_count, i; sc = device_get_softc(dev); if (device_get_children(dev, &children, &child_count) != 0) return; for (i = 0; i < child_count; i++) { ad = device_get_ivars(children[i]); rl = &ad->ad_rl; /* Don't reserve system resources. */ if (ACPI_ID_PROBE(dev, children[i], sysres_ids) != NULL) continue; STAILQ_FOREACH(rle, rl, link) { /* * Don't reserve IRQ resources. There are many sticky things * to get right otherwise (e.g. IRQs for psm, atkbd, and HPET * when using legacy routing). */ if (rle->type == SYS_RES_IRQ) continue; /* * Don't reserve the resource if it is already allocated. * The acpi_ec(4) driver can allocate its resources early * if ECDT is present. */ if (rle->res != NULL) continue; /* * Try to reserve the resource from our parent. If this * fails because the resource is a system resource, just * let it be. The resource range is already reserved so * that other devices will not use it. If the driver * needs to allocate the resource, then * acpi_alloc_resource() will sub-alloc from the system * resource. */ resource_list_reserve(rl, dev, children[i], rle->type, &rle->rid, rle->start, rle->end, rle->count, 0); } } free(children, M_TEMP); sc->acpi_resources_reserved = 1; } static int acpi_set_resource(device_t dev, device_t child, int type, int rid, rman_res_t start, rman_res_t count) { struct acpi_softc *sc = device_get_softc(dev); struct acpi_device *ad = device_get_ivars(child); struct resource_list *rl = &ad->ad_rl; #if defined(__i386__) || defined(__amd64__) ACPI_DEVICE_INFO *devinfo; #endif rman_res_t end; /* Ignore IRQ resources for PCI link devices. */ if (type == SYS_RES_IRQ && ACPI_ID_PROBE(dev, child, pcilink_ids) != NULL) return (0); /* * Ignore most resources for PCI root bridges. Some BIOSes * incorrectly enumerate the memory ranges they decode as plain * memory resources instead of as ResourceProducer ranges. Other * BIOSes incorrectly list system resource entries for I/O ranges * under the PCI bridge. Do allow the one known-correct case on * x86 of a PCI bridge claiming the I/O ports used for PCI config * access. */ #if defined(__i386__) || defined(__amd64__) if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { if (ACPI_SUCCESS(AcpiGetObjectInfo(ad->ad_handle, &devinfo))) { if ((devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0) { if (!(type == SYS_RES_IOPORT && start == CONF1_ADDR_PORT)) { AcpiOsFree(devinfo); return (0); } } AcpiOsFree(devinfo); } } #endif /* If the resource is already allocated, fail. */ if (resource_list_busy(rl, type, rid)) return (EBUSY); /* If the resource is already reserved, release it. */ if (resource_list_reserved(rl, type, rid)) resource_list_unreserve(rl, dev, child, type, rid); /* Add the resource. */ end = (start + count - 1); resource_list_add(rl, type, rid, start, end, count); /* Don't reserve resources until the system resources are allocated. */ if (!sc->acpi_resources_reserved) return (0); /* Don't reserve system resources. */ if (ACPI_ID_PROBE(dev, child, sysres_ids) != NULL) return (0); /* * Don't reserve IRQ resources. There are many sticky things to * get right otherwise (e.g. IRQs for psm, atkbd, and HPET when * using legacy routing). */ if (type == SYS_RES_IRQ) return (0); /* * Reserve the resource. * * XXX: Ignores failure for now. Failure here is probably a * BIOS/firmware bug? */ resource_list_reserve(rl, dev, child, type, &rid, start, end, count, 0); return (0); } static struct resource * acpi_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { #ifndef INTRNG ACPI_RESOURCE ares; #endif struct acpi_device *ad; struct resource_list_entry *rle; struct resource_list *rl; struct resource *res; int isdefault = RMAN_IS_DEFAULT_RANGE(start, end); /* * First attempt at allocating the resource. For direct children, * use resource_list_alloc() to handle reserved resources. For * other devices, pass the request up to our parent. */ if (bus == device_get_parent(child)) { ad = device_get_ivars(child); rl = &ad->ad_rl; /* * Simulate the behavior of the ISA bus for direct children * devices. That is, if a non-default range is specified for * a resource that doesn't exist, use bus_set_resource() to * add the resource before allocating it. Note that these * resources will not be reserved. */ if (!isdefault && resource_list_find(rl, type, *rid) == NULL) resource_list_add(rl, type, *rid, start, end, count); res = resource_list_alloc(rl, bus, child, type, rid, start, end, count, flags); #ifndef INTRNG if (res != NULL && type == SYS_RES_IRQ) { /* * Since bus_config_intr() takes immediate effect, we cannot * configure the interrupt associated with a device when we * parse the resources but have to defer it until a driver * actually allocates the interrupt via bus_alloc_resource(). * * XXX: Should we handle the lookup failing? */ if (ACPI_SUCCESS(acpi_lookup_irq_resource(child, *rid, res, &ares))) acpi_config_intr(child, &ares); } #endif /* * If this is an allocation of the "default" range for a given * RID, fetch the exact bounds for this resource from the * resource list entry to try to allocate the range from the * system resource regions. */ if (res == NULL && isdefault) { rle = resource_list_find(rl, type, *rid); if (rle != NULL) { start = rle->start; end = rle->end; count = rle->count; } } } else res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); /* * If the first attempt failed and this is an allocation of a * specific range, try to satisfy the request via a suballocation * from our system resource regions. */ if (res == NULL && start + count - 1 == end) res = acpi_alloc_sysres(child, type, rid, start, end, count, flags); return (res); } /* * Attempt to allocate a specific resource range from the system * resource ranges. Note that we only handle memory and I/O port * system resources. */ struct resource * acpi_alloc_sysres(device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct rman *rm; struct resource *res; switch (type) { case SYS_RES_IOPORT: rm = &acpi_rman_io; break; case SYS_RES_MEMORY: rm = &acpi_rman_mem; break; default: return (NULL); } KASSERT(start + count - 1 == end, ("wildcard resource range")); res = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, child); if (res == NULL) return (NULL); rman_set_rid(res, *rid); /* If requested, activate the resource using the parent's method. */ if (flags & RF_ACTIVE) if (bus_activate_resource(child, type, *rid, res) != 0) { rman_release_resource(res); return (NULL); } return (res); } static int acpi_is_resource_managed(int type, struct resource *r) { /* We only handle memory and IO resources through rman. */ switch (type) { case SYS_RES_IOPORT: return (rman_is_region_manager(r, &acpi_rman_io)); case SYS_RES_MEMORY: return (rman_is_region_manager(r, &acpi_rman_mem)); } return (0); } static int acpi_adjust_resource(device_t bus, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end) { if (acpi_is_resource_managed(type, r)) return (rman_adjust_resource(r, start, end)); return (bus_generic_adjust_resource(bus, child, type, r, start, end)); } static int acpi_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int ret; /* * If this resource belongs to one of our internal managers, * deactivate it and release it to the local pool. */ if (acpi_is_resource_managed(type, r)) { if (rman_get_flags(r) & RF_ACTIVE) { ret = bus_deactivate_resource(child, type, rid, r); if (ret != 0) return (ret); } return (rman_release_resource(r)); } return (bus_generic_rl_release_resource(bus, child, type, rid, r)); } static void acpi_delete_resource(device_t bus, device_t child, int type, int rid) { struct resource_list *rl; rl = acpi_get_rlist(bus, child); if (resource_list_busy(rl, type, rid)) { device_printf(bus, "delete_resource: Resource still owned by child" " (type=%d, rid=%d)\n", type, rid); return; } resource_list_unreserve(rl, bus, child, type, rid); resource_list_delete(rl, type, rid); } /* Allocate an IO port or memory resource, given its GAS. */ int acpi_bus_alloc_gas(device_t dev, int *type, int *rid, ACPI_GENERIC_ADDRESS *gas, struct resource **res, u_int flags) { int error, res_type; error = ENOMEM; if (type == NULL || rid == NULL || gas == NULL || res == NULL) return (EINVAL); /* We only support memory and IO spaces. */ switch (gas->SpaceId) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: res_type = SYS_RES_MEMORY; break; case ACPI_ADR_SPACE_SYSTEM_IO: res_type = SYS_RES_IOPORT; break; default: return (EOPNOTSUPP); } /* * If the register width is less than 8, assume the BIOS author means * it is a bit field and just allocate a byte. */ if (gas->BitWidth && gas->BitWidth < 8) gas->BitWidth = 8; /* Validate the address after we're sure we support the space. */ if (gas->Address == 0 || gas->BitWidth == 0) return (EINVAL); bus_set_resource(dev, res_type, *rid, gas->Address, gas->BitWidth / 8); *res = bus_alloc_resource_any(dev, res_type, rid, RF_ACTIVE | flags); if (*res != NULL) { *type = res_type; error = 0; } else bus_delete_resource(dev, res_type, *rid); return (error); } /* Probe _HID and _CID for compatible ISA PNP ids. */ static uint32_t acpi_isa_get_logicalid(device_t dev) { ACPI_DEVICE_INFO *devinfo; ACPI_HANDLE h; uint32_t pnpid; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* Fetch and validate the HID. */ if ((h = acpi_get_handle(dev)) == NULL || ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) return_VALUE (0); pnpid = (devinfo->Valid & ACPI_VALID_HID) != 0 && devinfo->HardwareId.Length >= ACPI_EISAID_STRING_SIZE ? PNP_EISAID(devinfo->HardwareId.String) : 0; AcpiOsFree(devinfo); return_VALUE (pnpid); } static int acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count) { ACPI_DEVICE_INFO *devinfo; ACPI_PNP_DEVICE_ID *ids; ACPI_HANDLE h; uint32_t *pnpid; int i, valid; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); pnpid = cids; /* Fetch and validate the CID */ if ((h = acpi_get_handle(dev)) == NULL || ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) return_VALUE (0); if ((devinfo->Valid & ACPI_VALID_CID) == 0) { AcpiOsFree(devinfo); return_VALUE (0); } if (devinfo->CompatibleIdList.Count < count) count = devinfo->CompatibleIdList.Count; ids = devinfo->CompatibleIdList.Ids; for (i = 0, valid = 0; i < count; i++) if (ids[i].Length >= ACPI_EISAID_STRING_SIZE && strncmp(ids[i].String, "PNP", 3) == 0) { *pnpid++ = PNP_EISAID(ids[i].String); valid++; } AcpiOsFree(devinfo); return_VALUE (valid); } static char * acpi_device_id_probe(device_t bus, device_t dev, char **ids) { ACPI_HANDLE h; ACPI_OBJECT_TYPE t; int i; h = acpi_get_handle(dev); if (ids == NULL || h == NULL) return (NULL); t = acpi_get_type(dev); if (t != ACPI_TYPE_DEVICE && t != ACPI_TYPE_PROCESSOR) return (NULL); /* Try to match one of the array of IDs with a HID or CID. */ for (i = 0; ids[i] != NULL; i++) { if (acpi_MatchHid(h, ids[i])) return (ids[i]); } return (NULL); } static ACPI_STATUS acpi_device_eval_obj(device_t bus, device_t dev, ACPI_STRING pathname, ACPI_OBJECT_LIST *parameters, ACPI_BUFFER *ret) { ACPI_HANDLE h; if (dev == NULL) h = ACPI_ROOT_OBJECT; else if ((h = acpi_get_handle(dev)) == NULL) return (AE_BAD_PARAMETER); return (AcpiEvaluateObject(h, pathname, parameters, ret)); } int acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate) { struct acpi_softc *sc; ACPI_HANDLE handle; ACPI_STATUS status; char sxd[8]; handle = acpi_get_handle(dev); /* * XXX If we find these devices, don't try to power them down. * The serial and IRDA ports on my T23 hang the system when * set to D3 and it appears that such legacy devices may * need special handling in their drivers. */ if (dstate == NULL || handle == NULL || acpi_MatchHid(handle, "PNP0500") || acpi_MatchHid(handle, "PNP0501") || acpi_MatchHid(handle, "PNP0502") || acpi_MatchHid(handle, "PNP0510") || acpi_MatchHid(handle, "PNP0511")) return (ENXIO); /* * Override next state with the value from _SxD, if present. * Note illegal _S0D is evaluated because some systems expect this. */ sc = device_get_softc(bus); snprintf(sxd, sizeof(sxd), "_S%dD", sc->acpi_sstate); status = acpi_GetInteger(handle, sxd, dstate); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { device_printf(dev, "failed to get %s on %s: %s\n", sxd, acpi_name(handle), AcpiFormatException(status)); return (ENXIO); } return (0); } /* Callback arg for our implementation of walking the namespace. */ struct acpi_device_scan_ctx { acpi_scan_cb_t user_fn; void *arg; ACPI_HANDLE parent; }; static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level, void *arg, void **retval) { struct acpi_device_scan_ctx *ctx; device_t dev, old_dev; ACPI_STATUS status; ACPI_OBJECT_TYPE type; /* * Skip this device if we think we'll have trouble with it or it is * the parent where the scan began. */ ctx = (struct acpi_device_scan_ctx *)arg; if (acpi_avoid(h) || h == ctx->parent) return (AE_OK); /* If this is not a valid device type (e.g., a method), skip it. */ if (ACPI_FAILURE(AcpiGetType(h, &type))) return (AE_OK); if (type != ACPI_TYPE_DEVICE && type != ACPI_TYPE_PROCESSOR && type != ACPI_TYPE_THERMAL && type != ACPI_TYPE_POWER) return (AE_OK); /* * Call the user function with the current device. If it is unchanged * afterwards, return. Otherwise, we update the handle to the new dev. */ old_dev = acpi_get_device(h); dev = old_dev; status = ctx->user_fn(h, &dev, level, ctx->arg); if (ACPI_FAILURE(status) || old_dev == dev) return (status); /* Remove the old child and its connection to the handle. */ if (old_dev != NULL) { device_delete_child(device_get_parent(old_dev), old_dev); AcpiDetachData(h, acpi_fake_objhandler); } /* Recreate the handle association if the user created a device. */ if (dev != NULL) AcpiAttachData(h, acpi_fake_objhandler, dev); return (AE_OK); } static ACPI_STATUS acpi_device_scan_children(device_t bus, device_t dev, int max_depth, acpi_scan_cb_t user_fn, void *arg) { ACPI_HANDLE h; struct acpi_device_scan_ctx ctx; if (acpi_disabled("children")) return (AE_OK); if (dev == NULL) h = ACPI_ROOT_OBJECT; else if ((h = acpi_get_handle(dev)) == NULL) return (AE_BAD_PARAMETER); ctx.user_fn = user_fn; ctx.arg = arg; ctx.parent = h; return (AcpiWalkNamespace(ACPI_TYPE_ANY, h, max_depth, acpi_device_scan_cb, NULL, &ctx, NULL)); } /* * Even though ACPI devices are not PCI, we use the PCI approach for setting * device power states since it's close enough to ACPI. */ static int acpi_set_powerstate(device_t child, int state) { ACPI_HANDLE h; ACPI_STATUS status; h = acpi_get_handle(child); if (state < ACPI_STATE_D0 || state > ACPI_D_STATES_MAX) return (EINVAL); if (h == NULL) return (0); /* Ignore errors if the power methods aren't present. */ status = acpi_pwr_switch_consumer(h, state); if (ACPI_SUCCESS(status)) { if (bootverbose) device_printf(child, "set ACPI power state D%d on %s\n", state, acpi_name(h)); } else if (status != AE_NOT_FOUND) device_printf(child, "failed to set ACPI power state D%d on %s: %s\n", state, acpi_name(h), AcpiFormatException(status)); return (0); } static int acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids) { int result, cid_count, i; uint32_t lid, cids[8]; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* * ISA-style drivers attached to ACPI may persist and * probe manually if we return ENOENT. We never want * that to happen, so don't ever return it. */ result = ENXIO; /* Scan the supplied IDs for a match */ lid = acpi_isa_get_logicalid(child); cid_count = acpi_isa_get_compatid(child, cids, 8); while (ids && ids->ip_id) { if (lid == ids->ip_id) { result = 0; goto out; } for (i = 0; i < cid_count; i++) { if (cids[i] == ids->ip_id) { result = 0; goto out; } } ids++; } out: if (result == 0 && ids->ip_desc) device_set_desc(child, ids->ip_desc); return_VALUE (result); } #if defined(__i386__) || defined(__amd64__) /* * Look for a MCFG table. If it is present, use the settings for * domain (segment) 0 to setup PCI config space access via the memory * map. */ static void acpi_enable_pcie(void) { ACPI_TABLE_HEADER *hdr; ACPI_MCFG_ALLOCATION *alloc, *end; ACPI_STATUS status; status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr); if (ACPI_FAILURE(status)) return; end = (ACPI_MCFG_ALLOCATION *)((char *)hdr + hdr->Length); alloc = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)hdr + 1); while (alloc < end) { if (alloc->PciSegment == 0) { pcie_cfgregopen(alloc->Address, alloc->StartBusNumber, alloc->EndBusNumber); return; } alloc++; } } #elif defined(__aarch64__) static void acpi_enable_pcie(device_t child, int segment) { ACPI_TABLE_HEADER *hdr; ACPI_MCFG_ALLOCATION *alloc, *end; ACPI_STATUS status; status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr); if (ACPI_FAILURE(status)) return; end = (ACPI_MCFG_ALLOCATION *)((char *)hdr + hdr->Length); alloc = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)hdr + 1); while (alloc < end) { if (alloc->PciSegment == segment) { bus_set_resource(child, SYS_RES_MEMORY, 0, alloc->Address, 0x10000000); return; } alloc++; } } #endif /* * Scan all of the ACPI namespace and attach child devices. * * We should only expect to find devices in the \_PR, \_TZ, \_SI, and * \_SB scopes, and \_PR and \_TZ became obsolete in the ACPI 2.0 spec. * However, in violation of the spec, some systems place their PCI link * devices in \, so we have to walk the whole namespace. We check the * type of namespace nodes, so this should be ok. */ static void acpi_probe_children(device_t bus) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* * Scan the namespace and insert placeholders for all the devices that * we find. We also probe/attach any early devices. * * Note that we use AcpiWalkNamespace rather than AcpiGetDevices because * we want to create nodes for all devices, not just those that are * currently present. (This assumes that we don't want to create/remove * devices as they appear, which might be smarter.) */ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "namespace scan\n")); AcpiWalkNamespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, 100, acpi_probe_child, NULL, bus, NULL); /* Pre-allocate resources for our rman from any sysresource devices. */ acpi_sysres_alloc(bus); /* Reserve resources already allocated to children. */ acpi_reserve_resources(bus); /* Create any static children by calling device identify methods. */ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "device identify routines\n")); bus_generic_probe(bus); /* Probe/attach all children, created statically and from the namespace. */ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "acpi bus_generic_attach\n")); bus_generic_attach(bus); /* Attach wake sysctls. */ acpi_wake_sysctl_walk(bus); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "done attaching children\n")); return_VOID; } /* * Determine the probe order for a given device. */ static void acpi_probe_order(ACPI_HANDLE handle, int *order) { ACPI_OBJECT_TYPE type; /* * 0. CPUs * 1. I/O port and memory system resource holders * 2. Clocks and timers (to handle early accesses) * 3. Embedded controllers (to handle early accesses) * 4. PCI Link Devices */ AcpiGetType(handle, &type); if (type == ACPI_TYPE_PROCESSOR) *order = 0; else if (acpi_MatchHid(handle, "PNP0C01") || acpi_MatchHid(handle, "PNP0C02")) *order = 1; else if (acpi_MatchHid(handle, "PNP0100") || acpi_MatchHid(handle, "PNP0103") || acpi_MatchHid(handle, "PNP0B00")) *order = 2; else if (acpi_MatchHid(handle, "PNP0C09")) *order = 3; else if (acpi_MatchHid(handle, "PNP0C0F")) *order = 4; } /* * Evaluate a child device and determine whether we might attach a device to * it. */ static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) { ACPI_DEVICE_INFO *devinfo; struct acpi_device *ad; #ifdef __aarch64__ int segment; #endif struct acpi_prw_data prw; ACPI_OBJECT_TYPE type; ACPI_HANDLE h; device_t bus, child; char *handle_str; int order; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (acpi_disabled("children")) return_ACPI_STATUS (AE_OK); /* Skip this device if we think we'll have trouble with it. */ if (acpi_avoid(handle)) return_ACPI_STATUS (AE_OK); bus = (device_t)context; if (ACPI_SUCCESS(AcpiGetType(handle, &type))) { handle_str = acpi_name(handle); switch (type) { case ACPI_TYPE_DEVICE: /* * Since we scan from \, be sure to skip system scope objects. * \_SB_ and \_TZ_ are defined in ACPICA as devices to work around * BIOS bugs. For example, \_SB_ is to allow \_SB_._INI to be run * during the initialization and \_TZ_ is to support Notify() on it. */ if (strcmp(handle_str, "\\_SB_") == 0 || strcmp(handle_str, "\\_TZ_") == 0) break; if (acpi_parse_prw(handle, &prw) == 0) AcpiSetupGpeForWake(handle, prw.gpe_handle, prw.gpe_bit); /* * Ignore devices that do not have a _HID or _CID. They should * be discovered by other buses (e.g. the PCI bus driver). */ if (!acpi_has_hid(handle)) break; /* FALLTHROUGH */ case ACPI_TYPE_PROCESSOR: case ACPI_TYPE_THERMAL: case ACPI_TYPE_POWER: /* * Create a placeholder device for this node. Sort the * placeholder so that the probe/attach passes will run * breadth-first. Orders less than ACPI_DEV_BASE_ORDER * are reserved for special objects (i.e., system * resources). */ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "scanning '%s'\n", handle_str)); order = level * 10 + ACPI_DEV_BASE_ORDER; acpi_probe_order(handle, &order); child = BUS_ADD_CHILD(bus, order, NULL, -1); if (child == NULL) break; /* Associate the handle with the device_t and vice versa. */ acpi_set_handle(child, handle); AcpiAttachData(handle, acpi_fake_objhandler, child); /* * Check that the device is present. If it's not present, * leave it disabled (so that we have a device_t attached to * the handle, but we don't probe it). * * XXX PCI link devices sometimes report "present" but not * "functional" (i.e. if disabled). Go ahead and probe them * anyway since we may enable them later. */ if (type == ACPI_TYPE_DEVICE && !acpi_DeviceIsPresent(child)) { /* Never disable PCI link devices. */ if (acpi_MatchHid(handle, "PNP0C0F")) break; /* * Docking stations should remain enabled since the system * may be undocked at boot. */ if (ACPI_SUCCESS(AcpiGetHandle(handle, "_DCK", &h))) break; device_disable(child); break; } /* * Get the device's resource settings and attach them. * Note that if the device has _PRS but no _CRS, we need * to decide when it's appropriate to try to configure the * device. Ignore the return value here; it's OK for the * device not to have any resources. */ acpi_parse_resources(child, handle, &acpi_res_parse_set, NULL); ad = device_get_ivars(child); ad->ad_cls_class = 0xffffff; if (ACPI_SUCCESS(AcpiGetObjectInfo(handle, &devinfo))) { if ((devinfo->Valid & ACPI_VALID_CLS) != 0 && devinfo->ClassCode.Length >= ACPI_PCICLS_STRING_SIZE) { ad->ad_cls_class = strtoul(devinfo->ClassCode.String, NULL, 16); } #ifdef __aarch64__ if ((devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0) { if (ACPI_SUCCESS(acpi_GetInteger(handle, "_SEG", &segment))) { acpi_enable_pcie(child, segment); } } #endif AcpiOsFree(devinfo); } break; } } return_ACPI_STATUS (AE_OK); } /* * AcpiAttachData() requires an object handler but never uses it. This is a * placeholder object handler so we can store a device_t in an ACPI_HANDLE. */ void acpi_fake_objhandler(ACPI_HANDLE h, void *data) { } static void acpi_shutdown_final(void *arg, int howto) { struct acpi_softc *sc = (struct acpi_softc *)arg; register_t intr; ACPI_STATUS status; /* * XXX Shutdown code should only run on the BSP (cpuid 0). * Some chipsets do not power off the system correctly if called from * an AP. */ if ((howto & RB_POWEROFF) != 0) { status = AcpiEnterSleepStatePrep(ACPI_STATE_S5); if (ACPI_FAILURE(status)) { device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n", AcpiFormatException(status)); return; } device_printf(sc->acpi_dev, "Powering system off\n"); intr = intr_disable(); status = AcpiEnterSleepState(ACPI_STATE_S5); if (ACPI_FAILURE(status)) { intr_restore(intr); device_printf(sc->acpi_dev, "power-off failed - %s\n", AcpiFormatException(status)); } else { DELAY(1000000); intr_restore(intr); device_printf(sc->acpi_dev, "power-off failed - timeout\n"); } } else if ((howto & RB_HALT) == 0 && sc->acpi_handle_reboot) { /* Reboot using the reset register. */ status = AcpiReset(); if (ACPI_SUCCESS(status)) { DELAY(1000000); device_printf(sc->acpi_dev, "reset failed - timeout\n"); } else if (status != AE_NOT_EXIST) device_printf(sc->acpi_dev, "reset failed - %s\n", AcpiFormatException(status)); } else if (sc->acpi_do_disable && panicstr == NULL) { /* * Only disable ACPI if the user requested. On some systems, writing * the disable value to SMI_CMD hangs the system. */ device_printf(sc->acpi_dev, "Shutting down\n"); AcpiTerminate(); } } static void acpi_enable_fixed_events(struct acpi_softc *sc) { static int first_time = 1; /* Enable and clear fixed events and install handlers. */ if ((AcpiGbl_FADT.Flags & ACPI_FADT_POWER_BUTTON) == 0) { AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, acpi_event_power_button_sleep, sc); if (first_time) device_printf(sc->acpi_dev, "Power Button (fixed)\n"); } if ((AcpiGbl_FADT.Flags & ACPI_FADT_SLEEP_BUTTON) == 0) { AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON); AcpiInstallFixedEventHandler(ACPI_EVENT_SLEEP_BUTTON, acpi_event_sleep_button_sleep, sc); if (first_time) device_printf(sc->acpi_dev, "Sleep Button (fixed)\n"); } first_time = 0; } /* * Returns true if the device is actually present and should * be attached to. This requires the present, enabled, UI-visible * and diagnostics-passed bits to be set. */ BOOLEAN acpi_DeviceIsPresent(device_t dev) { ACPI_HANDLE h; UINT32 s; ACPI_STATUS status; h = acpi_get_handle(dev); if (h == NULL) return (FALSE); status = acpi_GetInteger(h, "_STA", &s); + /* + * Onboard serial ports on certain AMD motherboards have an invalid _STA + * method that always returns 0. Force them to always be treated as present. + * + * This may solely be a quirk of a preproduction BIOS. + */ + if (acpi_MatchHid(h, "AMDI0020") || acpi_MatchHid(h, "AMDI0010")) + return (TRUE); + /* If no _STA method, must be present */ if (ACPI_FAILURE(status)) return (status == AE_NOT_FOUND ? TRUE : FALSE); return (ACPI_DEVICE_PRESENT(s) ? TRUE : FALSE); } /* * Returns true if the battery is actually present and inserted. */ BOOLEAN acpi_BatteryIsPresent(device_t dev) { ACPI_HANDLE h; UINT32 s; ACPI_STATUS status; h = acpi_get_handle(dev); if (h == NULL) return (FALSE); status = acpi_GetInteger(h, "_STA", &s); /* If no _STA method, must be present */ if (ACPI_FAILURE(status)) return (status == AE_NOT_FOUND ? TRUE : FALSE); return (ACPI_BATTERY_PRESENT(s) ? TRUE : FALSE); } /* * Returns true if a device has at least one valid device ID. */ static BOOLEAN acpi_has_hid(ACPI_HANDLE h) { ACPI_DEVICE_INFO *devinfo; BOOLEAN ret; if (h == NULL || ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) return (FALSE); ret = FALSE; if ((devinfo->Valid & ACPI_VALID_HID) != 0) ret = TRUE; else if ((devinfo->Valid & ACPI_VALID_CID) != 0) if (devinfo->CompatibleIdList.Count > 0) ret = TRUE; AcpiOsFree(devinfo); return (ret); } /* * Match a HID string against a handle */ BOOLEAN acpi_MatchHid(ACPI_HANDLE h, const char *hid) { ACPI_DEVICE_INFO *devinfo; BOOLEAN ret; int i; if (hid == NULL || h == NULL || ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) return (FALSE); ret = FALSE; if ((devinfo->Valid & ACPI_VALID_HID) != 0 && strcmp(hid, devinfo->HardwareId.String) == 0) ret = TRUE; else if ((devinfo->Valid & ACPI_VALID_CID) != 0) for (i = 0; i < devinfo->CompatibleIdList.Count; i++) { if (strcmp(hid, devinfo->CompatibleIdList.Ids[i].String) == 0) { ret = TRUE; break; } } AcpiOsFree(devinfo); return (ret); } /* * Return the handle of a named object within our scope, ie. that of (parent) * or one if its parents. */ ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result) { ACPI_HANDLE r; ACPI_STATUS status; /* Walk back up the tree to the root */ for (;;) { status = AcpiGetHandle(parent, path, &r); if (ACPI_SUCCESS(status)) { *result = r; return (AE_OK); } /* XXX Return error here? */ if (status != AE_NOT_FOUND) return (AE_OK); if (ACPI_FAILURE(AcpiGetParent(parent, &r))) return (AE_NOT_FOUND); parent = r; } } /* * Allocate a buffer with a preset data size. */ ACPI_BUFFER * acpi_AllocBuffer(int size) { ACPI_BUFFER *buf; if ((buf = malloc(size + sizeof(*buf), M_ACPIDEV, M_NOWAIT)) == NULL) return (NULL); buf->Length = size; buf->Pointer = (void *)(buf + 1); return (buf); } ACPI_STATUS acpi_SetInteger(ACPI_HANDLE handle, char *path, UINT32 number) { ACPI_OBJECT arg1; ACPI_OBJECT_LIST args; arg1.Type = ACPI_TYPE_INTEGER; arg1.Integer.Value = number; args.Count = 1; args.Pointer = &arg1; return (AcpiEvaluateObject(handle, path, &args, NULL)); } /* * Evaluate a path that should return an integer. */ ACPI_STATUS acpi_GetInteger(ACPI_HANDLE handle, char *path, UINT32 *number) { ACPI_STATUS status; ACPI_BUFFER buf; ACPI_OBJECT param; if (handle == NULL) handle = ACPI_ROOT_OBJECT; /* * Assume that what we've been pointed at is an Integer object, or * a method that will return an Integer. */ buf.Pointer = ¶m; buf.Length = sizeof(param); status = AcpiEvaluateObject(handle, path, NULL, &buf); if (ACPI_SUCCESS(status)) { if (param.Type == ACPI_TYPE_INTEGER) *number = param.Integer.Value; else status = AE_TYPE; } /* * In some applications, a method that's expected to return an Integer * may instead return a Buffer (probably to simplify some internal * arithmetic). We'll try to fetch whatever it is, and if it's a Buffer, * convert it into an Integer as best we can. * * This is a hack. */ if (status == AE_BUFFER_OVERFLOW) { if ((buf.Pointer = AcpiOsAllocate(buf.Length)) == NULL) { status = AE_NO_MEMORY; } else { status = AcpiEvaluateObject(handle, path, NULL, &buf); if (ACPI_SUCCESS(status)) status = acpi_ConvertBufferToInteger(&buf, number); AcpiOsFree(buf.Pointer); } } return (status); } ACPI_STATUS acpi_ConvertBufferToInteger(ACPI_BUFFER *bufp, UINT32 *number) { ACPI_OBJECT *p; UINT8 *val; int i; p = (ACPI_OBJECT *)bufp->Pointer; if (p->Type == ACPI_TYPE_INTEGER) { *number = p->Integer.Value; return (AE_OK); } if (p->Type != ACPI_TYPE_BUFFER) return (AE_TYPE); if (p->Buffer.Length > sizeof(int)) return (AE_BAD_DATA); *number = 0; val = p->Buffer.Pointer; for (i = 0; i < p->Buffer.Length; i++) *number += val[i] << (i * 8); return (AE_OK); } /* * Iterate over the elements of an a package object, calling the supplied * function for each element. * * XXX possible enhancement might be to abort traversal on error. */ ACPI_STATUS acpi_ForeachPackageObject(ACPI_OBJECT *pkg, void (*func)(ACPI_OBJECT *comp, void *arg), void *arg) { ACPI_OBJECT *comp; int i; if (pkg == NULL || pkg->Type != ACPI_TYPE_PACKAGE) return (AE_BAD_PARAMETER); /* Iterate over components */ i = 0; comp = pkg->Package.Elements; for (; i < pkg->Package.Count; i++, comp++) func(comp, arg); return (AE_OK); } /* * Find the (index)th resource object in a set. */ ACPI_STATUS acpi_FindIndexedResource(ACPI_BUFFER *buf, int index, ACPI_RESOURCE **resp) { ACPI_RESOURCE *rp; int i; rp = (ACPI_RESOURCE *)buf->Pointer; i = index; while (i-- > 0) { /* Range check */ if (rp > (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer + buf->Length)) return (AE_BAD_PARAMETER); /* Check for terminator */ if (rp->Type == ACPI_RESOURCE_TYPE_END_TAG || rp->Length == 0) return (AE_NOT_FOUND); rp = ACPI_NEXT_RESOURCE(rp); } if (resp != NULL) *resp = rp; return (AE_OK); } /* * Append an ACPI_RESOURCE to an ACPI_BUFFER. * * Given a pointer to an ACPI_RESOURCE structure, expand the ACPI_BUFFER * provided to contain it. If the ACPI_BUFFER is empty, allocate a sensible * backing block. If the ACPI_RESOURCE is NULL, return an empty set of * resources. */ #define ACPI_INITIAL_RESOURCE_BUFFER_SIZE 512 ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res) { ACPI_RESOURCE *rp; void *newp; /* Initialise the buffer if necessary. */ if (buf->Pointer == NULL) { buf->Length = ACPI_INITIAL_RESOURCE_BUFFER_SIZE; if ((buf->Pointer = AcpiOsAllocate(buf->Length)) == NULL) return (AE_NO_MEMORY); rp = (ACPI_RESOURCE *)buf->Pointer; rp->Type = ACPI_RESOURCE_TYPE_END_TAG; rp->Length = ACPI_RS_SIZE_MIN; } if (res == NULL) return (AE_OK); /* * Scan the current buffer looking for the terminator. * This will either find the terminator or hit the end * of the buffer and return an error. */ rp = (ACPI_RESOURCE *)buf->Pointer; for (;;) { /* Range check, don't go outside the buffer */ if (rp >= (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer + buf->Length)) return (AE_BAD_PARAMETER); if (rp->Type == ACPI_RESOURCE_TYPE_END_TAG || rp->Length == 0) break; rp = ACPI_NEXT_RESOURCE(rp); } /* * Check the size of the buffer and expand if required. * * Required size is: * size of existing resources before terminator + * size of new resource and header + * size of terminator. * * Note that this loop should really only run once, unless * for some reason we are stuffing a *really* huge resource. */ while ((((u_int8_t *)rp - (u_int8_t *)buf->Pointer) + res->Length + ACPI_RS_SIZE_NO_DATA + ACPI_RS_SIZE_MIN) >= buf->Length) { if ((newp = AcpiOsAllocate(buf->Length * 2)) == NULL) return (AE_NO_MEMORY); bcopy(buf->Pointer, newp, buf->Length); rp = (ACPI_RESOURCE *)((u_int8_t *)newp + ((u_int8_t *)rp - (u_int8_t *)buf->Pointer)); AcpiOsFree(buf->Pointer); buf->Pointer = newp; buf->Length += buf->Length; } /* Insert the new resource. */ bcopy(res, rp, res->Length + ACPI_RS_SIZE_NO_DATA); /* And add the terminator. */ rp = ACPI_NEXT_RESOURCE(rp); rp->Type = ACPI_RESOURCE_TYPE_END_TAG; rp->Length = ACPI_RS_SIZE_MIN; return (AE_OK); } ACPI_STATUS acpi_EvaluateOSC(ACPI_HANDLE handle, uint8_t *uuid, int revision, int count, uint32_t *caps_in, uint32_t *caps_out, bool query) { ACPI_OBJECT arg[4], *ret; ACPI_OBJECT_LIST arglist; ACPI_BUFFER buf; ACPI_STATUS status; arglist.Pointer = arg; arglist.Count = 4; arg[0].Type = ACPI_TYPE_BUFFER; arg[0].Buffer.Length = ACPI_UUID_LENGTH; arg[0].Buffer.Pointer = uuid; arg[1].Type = ACPI_TYPE_INTEGER; arg[1].Integer.Value = revision; arg[2].Type = ACPI_TYPE_INTEGER; arg[2].Integer.Value = count; arg[3].Type = ACPI_TYPE_BUFFER; arg[3].Buffer.Length = count * sizeof(*caps_in); arg[3].Buffer.Pointer = (uint8_t *)caps_in; caps_in[0] = query ? 1 : 0; buf.Pointer = NULL; buf.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObjectTyped(handle, "_OSC", &arglist, &buf, ACPI_TYPE_BUFFER); if (ACPI_FAILURE(status)) return (status); if (caps_out != NULL) { ret = buf.Pointer; if (ret->Buffer.Length != count * sizeof(*caps_out)) { AcpiOsFree(buf.Pointer); return (AE_BUFFER_OVERFLOW); } bcopy(ret->Buffer.Pointer, caps_out, ret->Buffer.Length); } AcpiOsFree(buf.Pointer); return (status); } /* * Set interrupt model. */ ACPI_STATUS acpi_SetIntrModel(int model) { return (acpi_SetInteger(ACPI_ROOT_OBJECT, "_PIC", model)); } /* * Walk subtables of a table and call a callback routine for each * subtable. The caller should provide the first subtable and a * pointer to the end of the table. This can be used to walk tables * such as MADT and SRAT that use subtable entries. */ void acpi_walk_subtables(void *first, void *end, acpi_subtable_handler *handler, void *arg) { ACPI_SUBTABLE_HEADER *entry; for (entry = first; (void *)entry < end; ) { /* Avoid an infinite loop if we hit a bogus entry. */ if (entry->Length < sizeof(ACPI_SUBTABLE_HEADER)) return; handler(entry, arg); entry = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, entry, entry->Length); } } /* * DEPRECATED. This interface has serious deficiencies and will be * removed. * * Immediately enter the sleep state. In the old model, acpiconf(8) ran * rc.suspend and rc.resume so we don't have to notify devd(8) to do this. */ ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state) { static int once; if (!once) { device_printf(sc->acpi_dev, "warning: acpi_SetSleepState() deprecated, need to update your software\n"); once = 1; } return (acpi_EnterSleepState(sc, state)); } #if defined(__amd64__) || defined(__i386__) static void acpi_sleep_force_task(void *context) { struct acpi_softc *sc = (struct acpi_softc *)context; if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate))) device_printf(sc->acpi_dev, "force sleep state S%d failed\n", sc->acpi_next_sstate); } static void acpi_sleep_force(void *arg) { struct acpi_softc *sc = (struct acpi_softc *)arg; device_printf(sc->acpi_dev, "suspend request timed out, forcing sleep now\n"); /* * XXX Suspending from callout causes freezes in DEVICE_SUSPEND(). * Suspend from acpi_task thread instead. */ if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_sleep_force_task, sc))) device_printf(sc->acpi_dev, "AcpiOsExecute() for sleeping failed\n"); } #endif /* * Request that the system enter the given suspend state. All /dev/apm * devices and devd(8) will be notified. Userland then has a chance to * save state and acknowledge the request. The system sleeps once all * acks are in. */ int acpi_ReqSleepState(struct acpi_softc *sc, int state) { #if defined(__amd64__) || defined(__i386__) struct apm_clone_data *clone; ACPI_STATUS status; if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX) return (EINVAL); if (!acpi_sleep_states[state]) return (EOPNOTSUPP); /* * If a reboot/shutdown/suspend request is already in progress or * suspend is blocked due to an upcoming shutdown, just return. */ if (rebooting || sc->acpi_next_sstate != 0 || suspend_blocked) { return (0); } /* Wait until sleep is enabled. */ while (sc->acpi_sleep_disabled) { AcpiOsSleep(1000); } ACPI_LOCK(acpi); sc->acpi_next_sstate = state; /* S5 (soft-off) should be entered directly with no waiting. */ if (state == ACPI_STATE_S5) { ACPI_UNLOCK(acpi); status = acpi_EnterSleepState(sc, state); return (ACPI_SUCCESS(status) ? 0 : ENXIO); } /* Record the pending state and notify all apm devices. */ STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) { clone->notify_status = APM_EV_NONE; if ((clone->flags & ACPI_EVF_DEVD) == 0) { selwakeuppri(&clone->sel_read, PZERO); KNOTE_LOCKED(&clone->sel_read.si_note, 0); } } /* If devd(8) is not running, immediately enter the sleep state. */ if (!devctl_process_running()) { ACPI_UNLOCK(acpi); status = acpi_EnterSleepState(sc, state); return (ACPI_SUCCESS(status) ? 0 : ENXIO); } /* * Set a timeout to fire if userland doesn't ack the suspend request * in time. This way we still eventually go to sleep if we were * overheating or running low on battery, even if userland is hung. * We cancel this timeout once all userland acks are in or the * suspend request is aborted. */ callout_reset(&sc->susp_force_to, 10 * hz, acpi_sleep_force, sc); ACPI_UNLOCK(acpi); /* Now notify devd(8) also. */ acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, state); return (0); #else /* This platform does not support acpi suspend/resume. */ return (EOPNOTSUPP); #endif } /* * Acknowledge (or reject) a pending sleep state. The caller has * prepared for suspend and is now ready for it to proceed. If the * error argument is non-zero, it indicates suspend should be cancelled * and gives an errno value describing why. Once all votes are in, * we suspend the system. */ int acpi_AckSleepState(struct apm_clone_data *clone, int error) { #if defined(__amd64__) || defined(__i386__) struct acpi_softc *sc; int ret, sleeping; /* If no pending sleep state, return an error. */ ACPI_LOCK(acpi); sc = clone->acpi_sc; if (sc->acpi_next_sstate == 0) { ACPI_UNLOCK(acpi); return (ENXIO); } /* Caller wants to abort suspend process. */ if (error) { sc->acpi_next_sstate = 0; callout_stop(&sc->susp_force_to); device_printf(sc->acpi_dev, "listener on %s cancelled the pending suspend\n", devtoname(clone->cdev)); ACPI_UNLOCK(acpi); return (0); } /* * Mark this device as acking the suspend request. Then, walk through * all devices, seeing if they agree yet. We only count devices that * are writable since read-only devices couldn't ack the request. */ sleeping = TRUE; clone->notify_status = APM_EV_ACKED; STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) { if ((clone->flags & ACPI_EVF_WRITE) != 0 && clone->notify_status != APM_EV_ACKED) { sleeping = FALSE; break; } } /* If all devices have voted "yes", we will suspend now. */ if (sleeping) callout_stop(&sc->susp_force_to); ACPI_UNLOCK(acpi); ret = 0; if (sleeping) { if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate))) ret = ENODEV; } return (ret); #else /* This platform does not support acpi suspend/resume. */ return (EOPNOTSUPP); #endif } static void acpi_sleep_enable(void *arg) { struct acpi_softc *sc = (struct acpi_softc *)arg; ACPI_LOCK_ASSERT(acpi); /* Reschedule if the system is not fully up and running. */ if (!AcpiGbl_SystemAwakeAndRunning) { callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME); return; } sc->acpi_sleep_disabled = FALSE; } static ACPI_STATUS acpi_sleep_disable(struct acpi_softc *sc) { ACPI_STATUS status; /* Fail if the system is not fully up and running. */ if (!AcpiGbl_SystemAwakeAndRunning) return (AE_ERROR); ACPI_LOCK(acpi); status = sc->acpi_sleep_disabled ? AE_ERROR : AE_OK; sc->acpi_sleep_disabled = TRUE; ACPI_UNLOCK(acpi); return (status); } enum acpi_sleep_state { ACPI_SS_NONE, ACPI_SS_GPE_SET, ACPI_SS_DEV_SUSPEND, ACPI_SS_SLP_PREP, ACPI_SS_SLEPT, }; /* * Enter the desired system sleep state. * * Currently we support S1-S5 but S4 is only S4BIOS */ static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, int state) { register_t intr; ACPI_STATUS status; ACPI_EVENT_STATUS power_button_status; enum acpi_sleep_state slp_state; int sleep_result; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state); if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX) return_ACPI_STATUS (AE_BAD_PARAMETER); if (!acpi_sleep_states[state]) { device_printf(sc->acpi_dev, "Sleep state S%d not supported by BIOS\n", state); return (AE_SUPPORT); } /* Re-entry once we're suspending is not allowed. */ status = acpi_sleep_disable(sc); if (ACPI_FAILURE(status)) { device_printf(sc->acpi_dev, "suspend request ignored (not ready yet)\n"); return (status); } if (state == ACPI_STATE_S5) { /* * Shut down cleanly and power off. This will call us back through the * shutdown handlers. */ shutdown_nice(RB_POWEROFF); return_ACPI_STATUS (AE_OK); } EVENTHANDLER_INVOKE(power_suspend_early); stop_all_proc(); EVENTHANDLER_INVOKE(power_suspend); #ifdef EARLY_AP_STARTUP MPASS(mp_ncpus == 1 || smp_started); thread_lock(curthread); sched_bind(curthread, 0); thread_unlock(curthread); #else if (smp_started) { thread_lock(curthread); sched_bind(curthread, 0); thread_unlock(curthread); } #endif /* * Be sure to hold Giant across DEVICE_SUSPEND/RESUME since non-MPSAFE * drivers need this. */ mtx_lock(&Giant); slp_state = ACPI_SS_NONE; sc->acpi_sstate = state; /* Enable any GPEs as appropriate and requested by the user. */ acpi_wake_prep_walk(state); slp_state = ACPI_SS_GPE_SET; /* * Inform all devices that we are going to sleep. If at least one * device fails, DEVICE_SUSPEND() automatically resumes the tree. * * XXX Note that a better two-pass approach with a 'veto' pass * followed by a "real thing" pass would be better, but the current * bus interface does not provide for this. */ if (DEVICE_SUSPEND(root_bus) != 0) { device_printf(sc->acpi_dev, "device_suspend failed\n"); goto backout; } slp_state = ACPI_SS_DEV_SUSPEND; status = AcpiEnterSleepStatePrep(state); if (ACPI_FAILURE(status)) { device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n", AcpiFormatException(status)); goto backout; } slp_state = ACPI_SS_SLP_PREP; if (sc->acpi_sleep_delay > 0) DELAY(sc->acpi_sleep_delay * 1000000); suspendclock(); intr = intr_disable(); if (state != ACPI_STATE_S1) { sleep_result = acpi_sleep_machdep(sc, state); acpi_wakeup_machdep(sc, state, sleep_result, 0); /* * XXX According to ACPI specification SCI_EN bit should be restored * by ACPI platform (BIOS, firmware) to its pre-sleep state. * Unfortunately some BIOSes fail to do that and that leads to * unexpected and serious consequences during wake up like a system * getting stuck in SMI handlers. * This hack is picked up from Linux, which claims that it follows * Windows behavior. */ if (sleep_result == 1 && state != ACPI_STATE_S4) AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, ACPI_ENABLE_EVENT); if (sleep_result == 1 && state == ACPI_STATE_S3) { /* * Prevent mis-interpretation of the wakeup by power button * as a request for power off. * Ideally we should post an appropriate wakeup event, * perhaps using acpi_event_power_button_wake or alike. * * Clearing of power button status after wakeup is mandated * by ACPI specification in section "Fixed Power Button". * * XXX As of ACPICA 20121114 AcpiGetEventStatus provides * status as 0/1 corressponding to inactive/active despite * its type being ACPI_EVENT_STATUS. In other words, * we should not test for ACPI_EVENT_FLAG_SET for time being. */ if (ACPI_SUCCESS(AcpiGetEventStatus(ACPI_EVENT_POWER_BUTTON, &power_button_status)) && power_button_status != 0) { AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); device_printf(sc->acpi_dev, "cleared fixed power button status\n"); } } intr_restore(intr); /* call acpi_wakeup_machdep() again with interrupt enabled */ acpi_wakeup_machdep(sc, state, sleep_result, 1); AcpiLeaveSleepStatePrep(state); if (sleep_result == -1) goto backout; /* Re-enable ACPI hardware on wakeup from sleep state 4. */ if (state == ACPI_STATE_S4) AcpiEnable(); } else { status = AcpiEnterSleepState(state); intr_restore(intr); AcpiLeaveSleepStatePrep(state); if (ACPI_FAILURE(status)) { device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n", AcpiFormatException(status)); goto backout; } } slp_state = ACPI_SS_SLEPT; /* * Back out state according to how far along we got in the suspend * process. This handles both the error and success cases. */ backout: if (slp_state >= ACPI_SS_SLP_PREP) resumeclock(); if (slp_state >= ACPI_SS_GPE_SET) { acpi_wake_prep_walk(state); sc->acpi_sstate = ACPI_STATE_S0; } if (slp_state >= ACPI_SS_DEV_SUSPEND) DEVICE_RESUME(root_bus); if (slp_state >= ACPI_SS_SLP_PREP) AcpiLeaveSleepState(state); if (slp_state >= ACPI_SS_SLEPT) { #if defined(__i386__) || defined(__amd64__) /* NB: we are still using ACPI timecounter at this point. */ resume_TSC(); #endif acpi_resync_clock(sc); acpi_enable_fixed_events(sc); } sc->acpi_next_sstate = 0; mtx_unlock(&Giant); #ifdef EARLY_AP_STARTUP thread_lock(curthread); sched_unbind(curthread); thread_unlock(curthread); #else if (smp_started) { thread_lock(curthread); sched_unbind(curthread); thread_unlock(curthread); } #endif resume_all_proc(); EVENTHANDLER_INVOKE(power_resume); /* Allow another sleep request after a while. */ callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME); /* Run /etc/rc.resume after we are back. */ if (devctl_process_running()) acpi_UserNotify("Resume", ACPI_ROOT_OBJECT, state); return_ACPI_STATUS (status); } static void acpi_resync_clock(struct acpi_softc *sc) { /* * Warm up timecounter again and reset system clock. */ (void)timecounter->tc_get_timecount(timecounter); (void)timecounter->tc_get_timecount(timecounter); inittodr(time_second + sc->acpi_sleep_delay); } /* Enable or disable the device's wake GPE. */ int acpi_wake_set_enable(device_t dev, int enable) { struct acpi_prw_data prw; ACPI_STATUS status; int flags; /* Make sure the device supports waking the system and get the GPE. */ if (acpi_parse_prw(acpi_get_handle(dev), &prw) != 0) return (ENXIO); flags = acpi_get_flags(dev); if (enable) { status = AcpiSetGpeWakeMask(prw.gpe_handle, prw.gpe_bit, ACPI_GPE_ENABLE); if (ACPI_FAILURE(status)) { device_printf(dev, "enable wake failed\n"); return (ENXIO); } acpi_set_flags(dev, flags | ACPI_FLAG_WAKE_ENABLED); } else { status = AcpiSetGpeWakeMask(prw.gpe_handle, prw.gpe_bit, ACPI_GPE_DISABLE); if (ACPI_FAILURE(status)) { device_printf(dev, "disable wake failed\n"); return (ENXIO); } acpi_set_flags(dev, flags & ~ACPI_FLAG_WAKE_ENABLED); } return (0); } static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate) { struct acpi_prw_data prw; device_t dev; /* Check that this is a wake-capable device and get its GPE. */ if (acpi_parse_prw(handle, &prw) != 0) return (ENXIO); dev = acpi_get_device(handle); /* * The destination sleep state must be less than (i.e., higher power) * or equal to the value specified by _PRW. If this GPE cannot be * enabled for the next sleep state, then disable it. If it can and * the user requested it be enabled, turn on any required power resources * and set _PSW. */ if (sstate > prw.lowest_wake) { AcpiSetGpeWakeMask(prw.gpe_handle, prw.gpe_bit, ACPI_GPE_DISABLE); if (bootverbose) device_printf(dev, "wake_prep disabled wake for %s (S%d)\n", acpi_name(handle), sstate); } else if (dev && (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) != 0) { acpi_pwr_wake_enable(handle, 1); acpi_SetInteger(handle, "_PSW", 1); if (bootverbose) device_printf(dev, "wake_prep enabled for %s (S%d)\n", acpi_name(handle), sstate); } return (0); } static int acpi_wake_run_prep(ACPI_HANDLE handle, int sstate) { struct acpi_prw_data prw; device_t dev; /* * Check that this is a wake-capable device and get its GPE. Return * now if the user didn't enable this device for wake. */ if (acpi_parse_prw(handle, &prw) != 0) return (ENXIO); dev = acpi_get_device(handle); if (dev == NULL || (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) == 0) return (0); /* * If this GPE couldn't be enabled for the previous sleep state, it was * disabled before going to sleep so re-enable it. If it was enabled, * clear _PSW and turn off any power resources it used. */ if (sstate > prw.lowest_wake) { AcpiSetGpeWakeMask(prw.gpe_handle, prw.gpe_bit, ACPI_GPE_ENABLE); if (bootverbose) device_printf(dev, "run_prep re-enabled %s\n", acpi_name(handle)); } else { acpi_SetInteger(handle, "_PSW", 0); acpi_pwr_wake_enable(handle, 0); if (bootverbose) device_printf(dev, "run_prep cleaned up for %s\n", acpi_name(handle)); } return (0); } static ACPI_STATUS acpi_wake_prep(ACPI_HANDLE handle, UINT32 level, void *context, void **status) { int sstate; /* If suspending, run the sleep prep function, otherwise wake. */ sstate = *(int *)context; if (AcpiGbl_SystemAwakeAndRunning) acpi_wake_sleep_prep(handle, sstate); else acpi_wake_run_prep(handle, sstate); return (AE_OK); } /* Walk the tree rooted at acpi0 to prep devices for suspend/resume. */ static int acpi_wake_prep_walk(int sstate) { ACPI_HANDLE sb_handle; if (ACPI_SUCCESS(AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle))) AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 100, acpi_wake_prep, NULL, &sstate, NULL); return (0); } /* Walk the tree rooted at acpi0 to attach per-device wake sysctls. */ static int acpi_wake_sysctl_walk(device_t dev) { int error, i, numdevs; device_t *devlist; device_t child; ACPI_STATUS status; error = device_get_children(dev, &devlist, &numdevs); if (error != 0 || numdevs == 0) { if (numdevs == 0) free(devlist, M_TEMP); return (error); } for (i = 0; i < numdevs; i++) { child = devlist[i]; acpi_wake_sysctl_walk(child); if (!device_is_attached(child)) continue; status = AcpiEvaluateObject(acpi_get_handle(child), "_PRW", NULL, NULL); if (ACPI_SUCCESS(status)) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(child), SYSCTL_CHILDREN(device_get_sysctl_tree(child)), OID_AUTO, "wake", CTLTYPE_INT | CTLFLAG_RW, child, 0, acpi_wake_set_sysctl, "I", "Device set to wake the system"); } } free(devlist, M_TEMP); return (0); } /* Enable or disable wake from userland. */ static int acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS) { int enable, error; device_t dev; dev = (device_t)arg1; enable = (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) ? 1 : 0; error = sysctl_handle_int(oidp, &enable, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (enable != 0 && enable != 1) return (EINVAL); return (acpi_wake_set_enable(dev, enable)); } /* Parse a device's _PRW into a structure. */ int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw) { ACPI_STATUS status; ACPI_BUFFER prw_buffer; ACPI_OBJECT *res, *res2; int error, i, power_count; if (h == NULL || prw == NULL) return (EINVAL); /* * The _PRW object (7.2.9) is only required for devices that have the * ability to wake the system from a sleeping state. */ error = EINVAL; prw_buffer.Pointer = NULL; prw_buffer.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(h, "_PRW", NULL, &prw_buffer); if (ACPI_FAILURE(status)) return (ENOENT); res = (ACPI_OBJECT *)prw_buffer.Pointer; if (res == NULL) return (ENOENT); if (!ACPI_PKG_VALID(res, 2)) goto out; /* * Element 1 of the _PRW object: * The lowest power system sleeping state that can be entered while still * providing wake functionality. The sleeping state being entered must * be less than (i.e., higher power) or equal to this value. */ if (acpi_PkgInt32(res, 1, &prw->lowest_wake) != 0) goto out; /* * Element 0 of the _PRW object: */ switch (res->Package.Elements[0].Type) { case ACPI_TYPE_INTEGER: /* * If the data type of this package element is numeric, then this * _PRW package element is the bit index in the GPEx_EN, in the * GPE blocks described in the FADT, of the enable bit that is * enabled for the wake event. */ prw->gpe_handle = NULL; prw->gpe_bit = res->Package.Elements[0].Integer.Value; error = 0; break; case ACPI_TYPE_PACKAGE: /* * If the data type of this package element is a package, then this * _PRW package element is itself a package containing two * elements. The first is an object reference to the GPE Block * device that contains the GPE that will be triggered by the wake * event. The second element is numeric and it contains the bit * index in the GPEx_EN, in the GPE Block referenced by the * first element in the package, of the enable bit that is enabled for * the wake event. * * For example, if this field is a package then it is of the form: * Package() {\_SB.PCI0.ISA.GPE, 2} */ res2 = &res->Package.Elements[0]; if (!ACPI_PKG_VALID(res2, 2)) goto out; prw->gpe_handle = acpi_GetReference(NULL, &res2->Package.Elements[0]); if (prw->gpe_handle == NULL) goto out; if (acpi_PkgInt32(res2, 1, &prw->gpe_bit) != 0) goto out; error = 0; break; default: goto out; } /* Elements 2 to N of the _PRW object are power resources. */ power_count = res->Package.Count - 2; if (power_count > ACPI_PRW_MAX_POWERRES) { printf("ACPI device %s has too many power resources\n", acpi_name(h)); power_count = 0; } prw->power_res_count = power_count; for (i = 0; i < power_count; i++) prw->power_res[i] = res->Package.Elements[i]; out: if (prw_buffer.Pointer != NULL) AcpiOsFree(prw_buffer.Pointer); return (error); } /* * ACPI Event Handlers */ /* System Event Handlers (registered by EVENTHANDLER_REGISTER) */ static void acpi_system_eventhandler_sleep(void *arg, int state) { struct acpi_softc *sc = (struct acpi_softc *)arg; int ret; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state); /* Check if button action is disabled or unknown. */ if (state == ACPI_STATE_UNKNOWN) return; /* Request that the system prepare to enter the given suspend state. */ ret = acpi_ReqSleepState(sc, state); if (ret != 0) device_printf(sc->acpi_dev, "request to enter state S%d failed (err %d)\n", state, ret); return_VOID; } static void acpi_system_eventhandler_wakeup(void *arg, int state) { ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state); /* Currently, nothing to do for wakeup. */ return_VOID; } /* * ACPICA Event Handlers (FixedEvent, also called from button notify handler) */ static void acpi_invoke_sleep_eventhandler(void *context) { EVENTHANDLER_INVOKE(acpi_sleep_event, *(int *)context); } static void acpi_invoke_wake_eventhandler(void *context) { EVENTHANDLER_INVOKE(acpi_wakeup_event, *(int *)context); } UINT32 acpi_event_power_button_sleep(void *context) { struct acpi_softc *sc = (struct acpi_softc *)context; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_invoke_sleep_eventhandler, &sc->acpi_power_button_sx))) return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); return_VALUE (ACPI_INTERRUPT_HANDLED); } UINT32 acpi_event_power_button_wake(void *context) { struct acpi_softc *sc = (struct acpi_softc *)context; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_invoke_wake_eventhandler, &sc->acpi_power_button_sx))) return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); return_VALUE (ACPI_INTERRUPT_HANDLED); } UINT32 acpi_event_sleep_button_sleep(void *context) { struct acpi_softc *sc = (struct acpi_softc *)context; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button_sx))) return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); return_VALUE (ACPI_INTERRUPT_HANDLED); } UINT32 acpi_event_sleep_button_wake(void *context) { struct acpi_softc *sc = (struct acpi_softc *)context; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_invoke_wake_eventhandler, &sc->acpi_sleep_button_sx))) return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); return_VALUE (ACPI_INTERRUPT_HANDLED); } /* * XXX This static buffer is suboptimal. There is no locking so only * use this for single-threaded callers. */ char * acpi_name(ACPI_HANDLE handle) { ACPI_BUFFER buf; static char data[256]; buf.Length = sizeof(data); buf.Pointer = data; if (handle && ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf))) return (data); return ("(unknown)"); } /* * Debugging/bug-avoidance. Avoid trying to fetch info on various * parts of the namespace. */ int acpi_avoid(ACPI_HANDLE handle) { char *cp, *env, *np; int len; np = acpi_name(handle); if (*np == '\\') np++; if ((env = kern_getenv("debug.acpi.avoid")) == NULL) return (0); /* Scan the avoid list checking for a match */ cp = env; for (;;) { while (*cp != 0 && isspace(*cp)) cp++; if (*cp == 0) break; len = 0; while (cp[len] != 0 && !isspace(cp[len])) len++; if (!strncmp(cp, np, len)) { freeenv(env); return(1); } cp += len; } freeenv(env); return (0); } /* * Debugging/bug-avoidance. Disable ACPI subsystem components. */ int acpi_disabled(char *subsys) { char *cp, *env; int len; if ((env = kern_getenv("debug.acpi.disabled")) == NULL) return (0); if (strcmp(env, "all") == 0) { freeenv(env); return (1); } /* Scan the disable list, checking for a match. */ cp = env; for (;;) { while (*cp != '\0' && isspace(*cp)) cp++; if (*cp == '\0') break; len = 0; while (cp[len] != '\0' && !isspace(cp[len])) len++; if (strncmp(cp, subsys, len) == 0) { freeenv(env); return (1); } cp += len; } freeenv(env); return (0); } static void acpi_lookup(void *arg, const char *name, device_t *dev) { ACPI_HANDLE handle; if (*dev != NULL) return; /* * Allow any handle name that is specified as an absolute path and * starts with '\'. We could restrict this to \_SB and friends, * but see acpi_probe_children() for notes on why we scan the entire * namespace for devices. * * XXX: The pathname argument to AcpiGetHandle() should be fixed to * be const. */ if (name[0] != '\\') return; if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, __DECONST(char *, name), &handle))) return; *dev = acpi_get_device(handle); } /* * Control interface. * * We multiplex ioctls for all participating ACPI devices here. Individual * drivers wanting to be accessible via /dev/acpi should use the * register/deregister interface to make their handlers visible. */ struct acpi_ioctl_hook { TAILQ_ENTRY(acpi_ioctl_hook) link; u_long cmd; acpi_ioctl_fn fn; void *arg; }; static TAILQ_HEAD(,acpi_ioctl_hook) acpi_ioctl_hooks; static int acpi_ioctl_hooks_initted; int acpi_register_ioctl(u_long cmd, acpi_ioctl_fn fn, void *arg) { struct acpi_ioctl_hook *hp; if ((hp = malloc(sizeof(*hp), M_ACPIDEV, M_NOWAIT)) == NULL) return (ENOMEM); hp->cmd = cmd; hp->fn = fn; hp->arg = arg; ACPI_LOCK(acpi); if (acpi_ioctl_hooks_initted == 0) { TAILQ_INIT(&acpi_ioctl_hooks); acpi_ioctl_hooks_initted = 1; } TAILQ_INSERT_TAIL(&acpi_ioctl_hooks, hp, link); ACPI_UNLOCK(acpi); return (0); } void acpi_deregister_ioctl(u_long cmd, acpi_ioctl_fn fn) { struct acpi_ioctl_hook *hp; ACPI_LOCK(acpi); TAILQ_FOREACH(hp, &acpi_ioctl_hooks, link) if (hp->cmd == cmd && hp->fn == fn) break; if (hp != NULL) { TAILQ_REMOVE(&acpi_ioctl_hooks, hp, link); free(hp, M_ACPIDEV); } ACPI_UNLOCK(acpi); } static int acpiopen(struct cdev *dev, int flag, int fmt, struct thread *td) { return (0); } static int acpiclose(struct cdev *dev, int flag, int fmt, struct thread *td) { return (0); } static int acpiioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct acpi_softc *sc; struct acpi_ioctl_hook *hp; int error, state; error = 0; hp = NULL; sc = dev->si_drv1; /* * Scan the list of registered ioctls, looking for handlers. */ ACPI_LOCK(acpi); if (acpi_ioctl_hooks_initted) TAILQ_FOREACH(hp, &acpi_ioctl_hooks, link) { if (hp->cmd == cmd) break; } ACPI_UNLOCK(acpi); if (hp) return (hp->fn(cmd, addr, hp->arg)); /* * Core ioctls are not permitted for non-writable user. * Currently, other ioctls just fetch information. * Not changing system behavior. */ if ((flag & FWRITE) == 0) return (EPERM); /* Core system ioctls. */ switch (cmd) { case ACPIIO_REQSLPSTATE: state = *(int *)addr; if (state != ACPI_STATE_S5) return (acpi_ReqSleepState(sc, state)); device_printf(sc->acpi_dev, "power off via acpi ioctl not supported\n"); error = EOPNOTSUPP; break; case ACPIIO_ACKSLPSTATE: error = *(int *)addr; error = acpi_AckSleepState(sc->acpi_clone, error); break; case ACPIIO_SETSLPSTATE: /* DEPRECATED */ state = *(int *)addr; if (state < ACPI_STATE_S0 || state > ACPI_S_STATES_MAX) return (EINVAL); if (!acpi_sleep_states[state]) return (EOPNOTSUPP); if (ACPI_FAILURE(acpi_SetSleepState(sc, state))) error = ENXIO; break; default: error = ENXIO; break; } return (error); } static int acpi_sname2sstate(const char *sname) { int sstate; if (toupper(sname[0]) == 'S') { sstate = sname[1] - '0'; if (sstate >= ACPI_STATE_S0 && sstate <= ACPI_STATE_S5 && sname[2] == '\0') return (sstate); } else if (strcasecmp(sname, "NONE") == 0) return (ACPI_STATE_UNKNOWN); return (-1); } static const char * acpi_sstate2sname(int sstate) { static const char *snames[] = { "S0", "S1", "S2", "S3", "S4", "S5" }; if (sstate >= ACPI_STATE_S0 && sstate <= ACPI_STATE_S5) return (snames[sstate]); else if (sstate == ACPI_STATE_UNKNOWN) return ("NONE"); return (NULL); } static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS) { int error; struct sbuf sb; UINT8 state; sbuf_new(&sb, NULL, 32, SBUF_AUTOEXTEND); for (state = ACPI_STATE_S1; state < ACPI_S_STATE_COUNT; state++) if (acpi_sleep_states[state]) sbuf_printf(&sb, "%s ", acpi_sstate2sname(state)); sbuf_trim(&sb); sbuf_finish(&sb); error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); sbuf_delete(&sb); return (error); } static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS) { char sleep_state[10]; int error, new_state, old_state; old_state = *(int *)oidp->oid_arg1; strlcpy(sleep_state, acpi_sstate2sname(old_state), sizeof(sleep_state)); error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req); if (error == 0 && req->newptr != NULL) { new_state = acpi_sname2sstate(sleep_state); if (new_state < ACPI_STATE_S1) return (EINVAL); if (new_state < ACPI_S_STATE_COUNT && !acpi_sleep_states[new_state]) return (EOPNOTSUPP); if (new_state != old_state) *(int *)oidp->oid_arg1 = new_state; } return (error); } /* Inform devctl(4) when we receive a Notify. */ void acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify) { char notify_buf[16]; ACPI_BUFFER handle_buf; ACPI_STATUS status; if (subsystem == NULL) return; handle_buf.Pointer = NULL; handle_buf.Length = ACPI_ALLOCATE_BUFFER; status = AcpiNsHandleToPathname(h, &handle_buf, FALSE); if (ACPI_FAILURE(status)) return; snprintf(notify_buf, sizeof(notify_buf), "notify=0x%02x", notify); devctl_notify("ACPI", subsystem, handle_buf.Pointer, notify_buf); AcpiOsFree(handle_buf.Pointer); } #ifdef ACPI_DEBUG /* * Support for parsing debug options from the kernel environment. * * Bits may be set in the AcpiDbgLayer and AcpiDbgLevel debug registers * by specifying the names of the bits in the debug.acpi.layer and * debug.acpi.level environment variables. Bits may be unset by * prefixing the bit name with !. */ struct debugtag { char *name; UINT32 value; }; static struct debugtag dbg_layer[] = { {"ACPI_UTILITIES", ACPI_UTILITIES}, {"ACPI_HARDWARE", ACPI_HARDWARE}, {"ACPI_EVENTS", ACPI_EVENTS}, {"ACPI_TABLES", ACPI_TABLES}, {"ACPI_NAMESPACE", ACPI_NAMESPACE}, {"ACPI_PARSER", ACPI_PARSER}, {"ACPI_DISPATCHER", ACPI_DISPATCHER}, {"ACPI_EXECUTER", ACPI_EXECUTER}, {"ACPI_RESOURCES", ACPI_RESOURCES}, {"ACPI_CA_DEBUGGER", ACPI_CA_DEBUGGER}, {"ACPI_OS_SERVICES", ACPI_OS_SERVICES}, {"ACPI_CA_DISASSEMBLER", ACPI_CA_DISASSEMBLER}, {"ACPI_ALL_COMPONENTS", ACPI_ALL_COMPONENTS}, {"ACPI_AC_ADAPTER", ACPI_AC_ADAPTER}, {"ACPI_BATTERY", ACPI_BATTERY}, {"ACPI_BUS", ACPI_BUS}, {"ACPI_BUTTON", ACPI_BUTTON}, {"ACPI_EC", ACPI_EC}, {"ACPI_FAN", ACPI_FAN}, {"ACPI_POWERRES", ACPI_POWERRES}, {"ACPI_PROCESSOR", ACPI_PROCESSOR}, {"ACPI_THERMAL", ACPI_THERMAL}, {"ACPI_TIMER", ACPI_TIMER}, {"ACPI_ALL_DRIVERS", ACPI_ALL_DRIVERS}, {NULL, 0} }; static struct debugtag dbg_level[] = { {"ACPI_LV_INIT", ACPI_LV_INIT}, {"ACPI_LV_DEBUG_OBJECT", ACPI_LV_DEBUG_OBJECT}, {"ACPI_LV_INFO", ACPI_LV_INFO}, {"ACPI_LV_REPAIR", ACPI_LV_REPAIR}, {"ACPI_LV_ALL_EXCEPTIONS", ACPI_LV_ALL_EXCEPTIONS}, /* Trace verbosity level 1 [Standard Trace Level] */ {"ACPI_LV_INIT_NAMES", ACPI_LV_INIT_NAMES}, {"ACPI_LV_PARSE", ACPI_LV_PARSE}, {"ACPI_LV_LOAD", ACPI_LV_LOAD}, {"ACPI_LV_DISPATCH", ACPI_LV_DISPATCH}, {"ACPI_LV_EXEC", ACPI_LV_EXEC}, {"ACPI_LV_NAMES", ACPI_LV_NAMES}, {"ACPI_LV_OPREGION", ACPI_LV_OPREGION}, {"ACPI_LV_BFIELD", ACPI_LV_BFIELD}, {"ACPI_LV_TABLES", ACPI_LV_TABLES}, {"ACPI_LV_VALUES", ACPI_LV_VALUES}, {"ACPI_LV_OBJECTS", ACPI_LV_OBJECTS}, {"ACPI_LV_RESOURCES", ACPI_LV_RESOURCES}, {"ACPI_LV_USER_REQUESTS", ACPI_LV_USER_REQUESTS}, {"ACPI_LV_PACKAGE", ACPI_LV_PACKAGE}, {"ACPI_LV_VERBOSITY1", ACPI_LV_VERBOSITY1}, /* Trace verbosity level 2 [Function tracing and memory allocation] */ {"ACPI_LV_ALLOCATIONS", ACPI_LV_ALLOCATIONS}, {"ACPI_LV_FUNCTIONS", ACPI_LV_FUNCTIONS}, {"ACPI_LV_OPTIMIZATIONS", ACPI_LV_OPTIMIZATIONS}, {"ACPI_LV_VERBOSITY2", ACPI_LV_VERBOSITY2}, {"ACPI_LV_ALL", ACPI_LV_ALL}, /* Trace verbosity level 3 [Threading, I/O, and Interrupts] */ {"ACPI_LV_MUTEX", ACPI_LV_MUTEX}, {"ACPI_LV_THREADS", ACPI_LV_THREADS}, {"ACPI_LV_IO", ACPI_LV_IO}, {"ACPI_LV_INTERRUPTS", ACPI_LV_INTERRUPTS}, {"ACPI_LV_VERBOSITY3", ACPI_LV_VERBOSITY3}, /* Exceptionally verbose output -- also used in the global "DebugLevel" */ {"ACPI_LV_AML_DISASSEMBLE", ACPI_LV_AML_DISASSEMBLE}, {"ACPI_LV_VERBOSE_INFO", ACPI_LV_VERBOSE_INFO}, {"ACPI_LV_FULL_TABLES", ACPI_LV_FULL_TABLES}, {"ACPI_LV_EVENTS", ACPI_LV_EVENTS}, {"ACPI_LV_VERBOSE", ACPI_LV_VERBOSE}, {NULL, 0} }; static void acpi_parse_debug(char *cp, struct debugtag *tag, UINT32 *flag) { char *ep; int i, l; int set; while (*cp) { if (isspace(*cp)) { cp++; continue; } ep = cp; while (*ep && !isspace(*ep)) ep++; if (*cp == '!') { set = 0; cp++; if (cp == ep) continue; } else { set = 1; } l = ep - cp; for (i = 0; tag[i].name != NULL; i++) { if (!strncmp(cp, tag[i].name, l)) { if (set) *flag |= tag[i].value; else *flag &= ~tag[i].value; } } cp = ep; } } static void acpi_set_debugging(void *junk) { char *layer, *level; if (cold) { AcpiDbgLayer = 0; AcpiDbgLevel = 0; } layer = kern_getenv("debug.acpi.layer"); level = kern_getenv("debug.acpi.level"); if (layer == NULL && level == NULL) return; printf("ACPI set debug"); if (layer != NULL) { if (strcmp("NONE", layer) != 0) printf(" layer '%s'", layer); acpi_parse_debug(layer, &dbg_layer[0], &AcpiDbgLayer); freeenv(layer); } if (level != NULL) { if (strcmp("NONE", level) != 0) printf(" level '%s'", level); acpi_parse_debug(level, &dbg_level[0], &AcpiDbgLevel); freeenv(level); } printf("\n"); } SYSINIT(acpi_debugging, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_set_debugging, NULL); static int acpi_debug_sysctl(SYSCTL_HANDLER_ARGS) { int error, *dbg; struct debugtag *tag; struct sbuf sb; char temp[128]; if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) return (ENOMEM); if (strcmp(oidp->oid_arg1, "debug.acpi.layer") == 0) { tag = &dbg_layer[0]; dbg = &AcpiDbgLayer; } else { tag = &dbg_level[0]; dbg = &AcpiDbgLevel; } /* Get old values if this is a get request. */ ACPI_SERIAL_BEGIN(acpi); if (*dbg == 0) { sbuf_cpy(&sb, "NONE"); } else if (req->newptr == NULL) { for (; tag->name != NULL; tag++) { if ((*dbg & tag->value) == tag->value) sbuf_printf(&sb, "%s ", tag->name); } } sbuf_trim(&sb); sbuf_finish(&sb); strlcpy(temp, sbuf_data(&sb), sizeof(temp)); sbuf_delete(&sb); error = sysctl_handle_string(oidp, temp, sizeof(temp), req); /* Check for error or no change */ if (error == 0 && req->newptr != NULL) { *dbg = 0; kern_setenv((char *)oidp->oid_arg1, temp); acpi_set_debugging(NULL); } ACPI_SERIAL_END(acpi); return (error); } SYSCTL_PROC(_debug_acpi, OID_AUTO, layer, CTLFLAG_RW | CTLTYPE_STRING, "debug.acpi.layer", 0, acpi_debug_sysctl, "A", ""); SYSCTL_PROC(_debug_acpi, OID_AUTO, level, CTLFLAG_RW | CTLTYPE_STRING, "debug.acpi.level", 0, acpi_debug_sysctl, "A", ""); #endif /* ACPI_DEBUG */ static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS) { int error; int old; old = acpi_debug_objects; error = sysctl_handle_int(oidp, &acpi_debug_objects, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (old == acpi_debug_objects || (old && acpi_debug_objects)) return (0); ACPI_SERIAL_BEGIN(acpi); AcpiGbl_EnableAmlDebugObject = acpi_debug_objects ? TRUE : FALSE; ACPI_SERIAL_END(acpi); return (0); } static int acpi_parse_interfaces(char *str, struct acpi_interface *iface) { char *p; size_t len; int i, j; p = str; while (isspace(*p) || *p == ',') p++; len = strlen(p); if (len == 0) return (0); p = strdup(p, M_TEMP); for (i = 0; i < len; i++) if (p[i] == ',') p[i] = '\0'; i = j = 0; while (i < len) if (isspace(p[i]) || p[i] == '\0') i++; else { i += strlen(p + i) + 1; j++; } if (j == 0) { free(p, M_TEMP); return (0); } iface->data = malloc(sizeof(*iface->data) * j, M_TEMP, M_WAITOK); iface->num = j; i = j = 0; while (i < len) if (isspace(p[i]) || p[i] == '\0') i++; else { iface->data[j] = p + i; i += strlen(p + i) + 1; j++; } return (j); } static void acpi_free_interfaces(struct acpi_interface *iface) { free(iface->data[0], M_TEMP); free(iface->data, M_TEMP); } static void acpi_reset_interfaces(device_t dev) { struct acpi_interface list; ACPI_STATUS status; int i; if (acpi_parse_interfaces(acpi_install_interface, &list) > 0) { for (i = 0; i < list.num; i++) { status = AcpiInstallInterface(list.data[i]); if (ACPI_FAILURE(status)) device_printf(dev, "failed to install _OSI(\"%s\"): %s\n", list.data[i], AcpiFormatException(status)); else if (bootverbose) device_printf(dev, "installed _OSI(\"%s\")\n", list.data[i]); } acpi_free_interfaces(&list); } if (acpi_parse_interfaces(acpi_remove_interface, &list) > 0) { for (i = 0; i < list.num; i++) { status = AcpiRemoveInterface(list.data[i]); if (ACPI_FAILURE(status)) device_printf(dev, "failed to remove _OSI(\"%s\"): %s\n", list.data[i], AcpiFormatException(status)); else if (bootverbose) device_printf(dev, "removed _OSI(\"%s\")\n", list.data[i]); } acpi_free_interfaces(&list); } } static int acpi_pm_func(u_long cmd, void *arg, ...) { int state, acpi_state; int error; struct acpi_softc *sc; va_list ap; error = 0; switch (cmd) { case POWER_CMD_SUSPEND: sc = (struct acpi_softc *)arg; if (sc == NULL) { error = EINVAL; goto out; } va_start(ap, arg); state = va_arg(ap, int); va_end(ap); switch (state) { case POWER_SLEEP_STATE_STANDBY: acpi_state = sc->acpi_standby_sx; break; case POWER_SLEEP_STATE_SUSPEND: acpi_state = sc->acpi_suspend_sx; break; case POWER_SLEEP_STATE_HIBERNATE: acpi_state = ACPI_STATE_S4; break; default: error = EINVAL; goto out; } if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state))) error = ENXIO; break; default: error = EINVAL; goto out; } out: return (error); } static void acpi_pm_register(void *arg) { if (!cold || resource_disabled("acpi", 0)) return; power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, NULL); } SYSINIT(power, SI_SUB_KLD, SI_ORDER_ANY, acpi_pm_register, NULL); Index: head/sys/dev/uart/uart_bus.h =================================================================== --- head/sys/dev/uart/uart_bus.h (revision 338073) +++ head/sys/dev/uart/uart_bus.h (revision 338074) @@ -1,219 +1,222 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_UART_BUS_H_ #define _DEV_UART_BUS_H_ #ifndef KLD_MODULE #include "opt_uart.h" #endif #include #include /* Drain and flush targets. */ #define UART_DRAIN_RECEIVER 0x0001 #define UART_DRAIN_TRANSMITTER 0x0002 #define UART_FLUSH_RECEIVER UART_DRAIN_RECEIVER #define UART_FLUSH_TRANSMITTER UART_DRAIN_TRANSMITTER /* Received character status bits. */ #define UART_STAT_BREAK 0x0100 #define UART_STAT_FRAMERR 0x0200 #define UART_STAT_OVERRUN 0x0400 #define UART_STAT_PARERR 0x0800 /* UART_IOCTL() requests */ #define UART_IOCTL_BREAK 1 #define UART_IOCTL_IFLOW 2 #define UART_IOCTL_OFLOW 3 #define UART_IOCTL_BAUD 4 +/* UART quirk flags */ +#define UART_F_BUSY_DETECT 0x1 + /* * UART class & instance (=softc) */ struct uart_class { KOBJ_CLASS_FIELDS; struct uart_ops *uc_ops; /* Low-level console operations. */ u_int uc_range; /* Bus space address range. */ u_int uc_rclk; /* Default rclk for this device. */ u_int uc_rshift; /* Default regshift for this device. */ u_int uc_riowidth; /* Default reg io width for this device. */ }; struct uart_softc { KOBJ_FIELDS; struct uart_class *sc_class; struct uart_bas sc_bas; device_t sc_dev; struct mtx sc_hwmtx_s; /* Spinlock protecting hardware. */ struct mtx *sc_hwmtx; struct resource *sc_rres; /* Register resource. */ int sc_rrid; int sc_rtype; /* SYS_RES_{IOPORT|MEMORY}. */ struct resource *sc_ires; /* Interrupt resource. */ void *sc_icookie; int sc_irid; struct callout sc_timer; int sc_callout:1; /* This UART is opened for callout. */ int sc_fastintr:1; /* This UART uses fast interrupts. */ int sc_hwiflow:1; /* This UART has HW input flow ctl. */ int sc_hwoflow:1; /* This UART has HW output flow ctl. */ int sc_leaving:1; /* This UART is going away. */ int sc_opened:1; /* This UART is open for business. */ int sc_polled:1; /* This UART has no interrupts. */ int sc_txbusy:1; /* This UART is transmitting. */ int sc_isquelch:1; /* This UART has input squelched. */ int sc_testintr:1; /* This UART is under int. testing. */ struct uart_devinfo *sc_sysdev; /* System device (or NULL). */ int sc_altbrk; /* State for alt break sequence. */ uint32_t sc_hwsig; /* Signal state. Used by HW driver. */ /* Receiver data. */ uint16_t *sc_rxbuf; int sc_rxbufsz; int sc_rxput; int sc_rxget; int sc_rxfifosz; /* Size of RX FIFO. */ /* Transmitter data. */ uint8_t *sc_txbuf; int sc_txdatasz; int sc_txfifosz; /* Size of TX FIFO and buffer. */ /* Pulse capturing support (PPS). */ struct pps_state sc_pps; int sc_pps_mode; sbintime_t sc_pps_captime; /* Upper layer data. */ void *sc_softih; uint32_t sc_ttypend; union { /* TTY specific data. */ struct { struct tty *tp; } u_tty; /* Keyboard specific data. */ struct { } u_kbd; } sc_u; }; extern devclass_t uart_devclass; extern const char uart_driver_name[]; int uart_bus_attach(device_t dev); int uart_bus_detach(device_t dev); int uart_bus_resume(device_t dev); serdev_intr_t *uart_bus_ihand(device_t dev, int ipend); int uart_bus_ipend(device_t dev); -int uart_bus_probe(device_t dev, int regshft, int regiowidth, int rclk, int rid, int chan); +int uart_bus_probe(device_t dev, int regshft, int regiowidth, int rclk, int rid, int chan, int quirks); int uart_bus_sysdev(device_t dev); void uart_sched_softih(struct uart_softc *, uint32_t); int uart_tty_attach(struct uart_softc *); int uart_tty_detach(struct uart_softc *); struct mtx *uart_tty_getlock(struct uart_softc *); void uart_tty_intr(void *arg); /* * Receive buffer operations. */ static __inline int uart_rx_empty(struct uart_softc *sc) { return ((sc->sc_rxget == sc->sc_rxput) ? 1 : 0); } static __inline int uart_rx_full(struct uart_softc *sc) { return ((sc->sc_rxput + 1 < sc->sc_rxbufsz) ? (sc->sc_rxput + 1 == sc->sc_rxget) : (sc->sc_rxget == 0)); } static __inline int uart_rx_get(struct uart_softc *sc) { int ptr, xc; ptr = sc->sc_rxget; if (ptr == sc->sc_rxput) return (-1); xc = sc->sc_rxbuf[ptr++]; sc->sc_rxget = (ptr < sc->sc_rxbufsz) ? ptr : 0; return (xc); } static __inline int uart_rx_next(struct uart_softc *sc) { int ptr; ptr = sc->sc_rxget; if (ptr == sc->sc_rxput) return (-1); ptr += 1; sc->sc_rxget = (ptr < sc->sc_rxbufsz) ? ptr : 0; return (0); } static __inline int uart_rx_peek(struct uart_softc *sc) { int ptr; ptr = sc->sc_rxget; return ((ptr == sc->sc_rxput) ? -1 : sc->sc_rxbuf[ptr]); } static __inline int uart_rx_put(struct uart_softc *sc, int xc) { int ptr; ptr = (sc->sc_rxput + 1 < sc->sc_rxbufsz) ? sc->sc_rxput + 1 : 0; if (ptr == sc->sc_rxget) return (ENOSPC); sc->sc_rxbuf[sc->sc_rxput] = xc; sc->sc_rxput = ptr; return (0); } #endif /* _DEV_UART_BUS_H_ */ Index: head/sys/dev/uart/uart_bus_acpi.c =================================================================== --- head/sys/dev/uart/uart_bus_acpi.c (revision 338073) +++ head/sys/dev/uart/uart_bus_acpi.c (revision 338074) @@ -1,125 +1,102 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 M. Warner Losh. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#include - #include #include #include - -#ifdef __aarch64__ #include #include #include -#endif + static int uart_acpi_probe(device_t dev); static device_method_t uart_acpi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_acpi_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), DEVMETHOD(device_resume, uart_bus_resume), { 0, 0 } }; static driver_t uart_acpi_driver = { uart_driver_name, uart_acpi_methods, sizeof(struct uart_softc), }; -#if defined(__i386__) || defined(__amd64__) -static struct isa_pnp_id acpi_ns8250_ids[] = { - {0x0005d041, "Standard PC COM port"}, /* PNP0500 */ - {0x0105d041, "16550A-compatible COM port"}, /* PNP0501 */ - {0x0205d041, "Multiport serial device (non-intelligent 16550)"}, /* PNP0502 */ - {0x1005d041, "Generic IRDA-compatible device"}, /* PNP0510 */ - {0x1105d041, "Generic IRDA-compatible device"}, /* PNP0511 */ - {0x04f0235c, "Wacom Tablet PC Screen"}, /* WACF004 */ - {0x0ef0235c, "Wacom Tablet PC Screen 00e"}, /* WACF00e */ - {0xe502aa1a, "Wacom Tablet at FuS Lifebook T"}, /* FUJ02E5 */ - {0} -}; -#endif - -#ifdef __aarch64__ -static struct uart_class * +static struct acpi_uart_compat_data * uart_acpi_find_device(device_t dev) { - struct acpi_uart_compat_data **cd; + struct acpi_uart_compat_data **cd, *cd_it; ACPI_HANDLE h; if ((h = acpi_get_handle(dev)) == NULL) return (NULL); SET_FOREACH(cd, uart_acpi_class_and_device_set) { - if (acpi_MatchHid(h, (*cd)->hid)) { - return ((*cd)->clas); + for (cd_it = *cd; cd_it->cd_hid != NULL; cd_it++) { + if (acpi_MatchHid(h, cd_it->cd_hid)) + return (cd_it); } } return (NULL); } -#endif static int uart_acpi_probe(device_t dev) { struct uart_softc *sc; + struct acpi_uart_compat_data *cd; sc = device_get_softc(dev); -#if defined(__i386__) || defined(__amd64__) - if (!ISA_PNP_PROBE(device_get_parent(dev), dev, acpi_ns8250_ids)) { - sc->sc_class = &uart_ns8250_class; - return (uart_bus_probe(dev, 0, 0, 0, 0, 0)); + if ((cd = uart_acpi_find_device(dev)) != NULL) { + sc->sc_class = cd->cd_class; + if (cd->cd_desc != NULL) + device_set_desc(dev, cd->cd_desc); + return (uart_bus_probe(dev, cd->cd_regshft, cd->cd_regiowidth, + cd->cd_rclk, 0, 0, cd->cd_quirks)); } - - /* Add checks for non-ns8250 IDs here. */ -#elif defined(__aarch64__) - if ((sc->sc_class = uart_acpi_find_device(dev)) != NULL) - return (uart_bus_probe(dev, 2, 0, 0, 0, 0)); -#endif - return (ENXIO); } DRIVER_MODULE(uart, acpi, uart_acpi_driver, uart_devclass, 0, 0); Index: head/sys/dev/uart/uart_bus_ebus.c =================================================================== --- head/sys/dev/uart/uart_bus_ebus.c (revision 338073) +++ head/sys/dev/uart/uart_bus_ebus.c (revision 338074) @@ -1,108 +1,108 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 by Thomas Moestl . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include static int uart_ebus_probe(device_t dev); static device_method_t uart_ebus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_ebus_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_ebus_driver = { uart_driver_name, uart_ebus_methods, sizeof(struct uart_softc), }; static int uart_ebus_probe(device_t dev) { const char *nm, *cmpt; struct uart_softc *sc; struct uart_devinfo dummy; sc = device_get_softc(dev); sc->sc_class = NULL; nm = ofw_bus_get_name(dev); cmpt = ofw_bus_get_compat(dev); if (cmpt == NULL) cmpt = ""; if (!strcmp(nm, "lom-console") || !strcmp(nm, "su") || !strcmp(nm, "su_pnp") || !strcmp(cmpt, "rsc-console") || !strcmp(cmpt, "rsc-control") || !strcmp(cmpt, "su") || !strcmp(cmpt, "su16550") || !strcmp(cmpt, "su16552")) { /* * On AXi and AXmp boards the NS16550 (used to connect * keyboard/mouse) share their IRQ lines with the i8042. * Any IRQ activity (typically during attach) of the * NS16550 used to connect the keyboard when actually the * PS/2 keyboard is selected in OFW causes interaction * with the OBP i8042 driver resulting in a hang and vice * versa. As RS232 keyboards and mice obviously aren't * meant to be used in parallel with PS/2 ones on these * boards don't attach to the NS16550 in case the RS232 * keyboard isn't selected in order to prevent such hangs. */ if ((!strcmp(sparc64_model, "SUNW,UltraAX-MP") || !strcmp(sparc64_model, "SUNW,UltraSPARC-IIi-Engine")) && uart_cpu_getdev(UART_DEV_KEYBOARD, &dummy)) { device_disable(dev); return (ENXIO); } sc->sc_class = &uart_ns8250_class; - return (uart_bus_probe(dev, 0, 0, 0, 0, 0)); + return (uart_bus_probe(dev, 0, 0, 0, 0, 0, 0)); } return (ENXIO); } DRIVER_MODULE(uart, ebus, uart_ebus_driver, uart_devclass, 0, 0); Index: head/sys/dev/uart/uart_bus_fdt.c =================================================================== --- head/sys/dev/uart/uart_bus_fdt.c (revision 338073) +++ head/sys/dev/uart/uart_bus_fdt.c (revision 338074) @@ -1,285 +1,285 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009-2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static int uart_fdt_probe(device_t); static device_method_t uart_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_fdt_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_fdt_driver = { uart_driver_name, uart_fdt_methods, sizeof(struct uart_softc), }; int uart_fdt_get_clock(phandle_t node, pcell_t *cell) { /* clock-frequency is a FreeBSD-only extension. */ if ((OF_getencprop(node, "clock-frequency", cell, sizeof(*cell))) <= 0) { /* Try to retrieve parent 'bus-frequency' */ /* XXX this should go to simple-bus fixup or so */ if ((OF_getencprop(OF_parent(node), "bus-frequency", cell, sizeof(*cell))) <= 0) *cell = 0; } return (0); } int uart_fdt_get_shift(phandle_t node, pcell_t *cell) { if ((OF_getencprop(node, "reg-shift", cell, sizeof(*cell))) <= 0) return (-1); return (0); } int uart_fdt_get_io_width(phandle_t node, pcell_t *cell) { if ((OF_getencprop(node, "reg-io-width", cell, sizeof(*cell))) <= 0) return (-1); return (0); } static uintptr_t uart_fdt_find_device(device_t dev) { struct ofw_compat_data **cd; const struct ofw_compat_data *ocd; SET_FOREACH(cd, uart_fdt_class_and_device_set) { ocd = ofw_bus_search_compatible(dev, *cd); if (ocd->ocd_data != 0) return (ocd->ocd_data); } return (0); } static int phandle_chosen_propdev(phandle_t chosen, const char *name, phandle_t *node) { char buf[64]; char *sep; if (OF_getprop(chosen, name, buf, sizeof(buf)) <= 0) return (ENXIO); /* * stdout-path may have a ':' to separate the device from the * connection settings. Split the string so we just pass the former * to OF_finddevice. */ sep = strchr(buf, ':'); if (sep != NULL) *sep = '\0'; if ((*node = OF_finddevice(buf)) == -1) return (ENXIO); return (0); } static const struct ofw_compat_data * uart_fdt_find_compatible(phandle_t node, const struct ofw_compat_data *cd) { const struct ofw_compat_data *ocd; for (ocd = cd; ocd->ocd_str != NULL; ocd++) { if (ofw_bus_node_is_compatible(node, ocd->ocd_str)) return (ocd); } return (NULL); } static uintptr_t uart_fdt_find_by_node(phandle_t node, int class_list) { struct ofw_compat_data **cd; const struct ofw_compat_data *ocd; if (class_list) { SET_FOREACH(cd, uart_fdt_class_set) { ocd = uart_fdt_find_compatible(node, *cd); if ((ocd != NULL) && (ocd->ocd_data != 0)) return (ocd->ocd_data); } } else { SET_FOREACH(cd, uart_fdt_class_and_device_set) { ocd = uart_fdt_find_compatible(node, *cd); if ((ocd != NULL) && (ocd->ocd_data != 0)) return (ocd->ocd_data); } } return (0); } int uart_cpu_fdt_probe(struct uart_class **classp, bus_space_tag_t *bst, bus_space_handle_t *bsh, int *baud, u_int *rclk, u_int *shiftp, u_int *iowidthp) { const char *propnames[] = {"stdout-path", "linux,stdout-path", "stdout", "stdin-path", "stdin", NULL}; const char **name; struct uart_class *class; phandle_t node, chosen; pcell_t br, clk, shift, iowidth; char *cp; int err; /* Has the user forced a specific device node? */ cp = kern_getenv("hw.fdt.console"); if (cp == NULL) { /* * Retrieve /chosen/std{in,out}. */ node = -1; if ((chosen = OF_finddevice("/chosen")) != -1) { for (name = propnames; *name != NULL; name++) { if (phandle_chosen_propdev(chosen, *name, &node) == 0) break; } } if (chosen == -1 || *name == NULL) node = OF_finddevice("serial0"); /* Last ditch */ } else { node = OF_finddevice(cp); } if (node == -1) return (ENXIO); /* * Check old style of UART definition first. Unfortunately, the common * FDT processing is not possible if we have clock, power domains and * pinmux stuff. */ class = (struct uart_class *)uart_fdt_find_by_node(node, 0); if (class != NULL) { if ((err = uart_fdt_get_clock(node, &clk)) != 0) return (err); } else { /* Check class only linker set */ class = (struct uart_class *)uart_fdt_find_by_node(node, 1); if (class == NULL) return (ENXIO); clk = 0; } /* * Retrieve serial attributes. */ if (uart_fdt_get_shift(node, &shift) != 0) shift = uart_getregshift(class); if (uart_fdt_get_io_width(node, &iowidth) != 0) iowidth = uart_getregiowidth(class); if (OF_getencprop(node, "current-speed", &br, sizeof(br)) <= 0) br = 0; err = OF_decode_addr(node, 0, bst, bsh, NULL); if (err != 0) return (err); *classp = class; *baud = br; *rclk = clk; *shiftp = shift; *iowidthp = iowidth; return (0); } static int uart_fdt_probe(device_t dev) { struct uart_softc *sc; phandle_t node; pcell_t clock, shift, iowidth; int err; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); sc->sc_class = (struct uart_class *)uart_fdt_find_device(dev); if (sc->sc_class == NULL) return (ENXIO); node = ofw_bus_get_node(dev); if ((err = uart_fdt_get_clock(node, &clock)) != 0) return (err); if (uart_fdt_get_shift(node, &shift) != 0) shift = uart_getregshift(sc->sc_class); if (uart_fdt_get_io_width(node, &iowidth) != 0) iowidth = uart_getregiowidth(sc->sc_class); - return (uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0)); + return (uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0, 0)); } DRIVER_MODULE(uart, simplebus, uart_fdt_driver, uart_devclass, 0, 0); DRIVER_MODULE(uart, ofwbus, uart_fdt_driver, uart_devclass, 0, 0); Index: head/sys/dev/uart/uart_bus_isa.c =================================================================== --- head/sys/dev/uart/uart_bus_isa.c (revision 338073) +++ head/sys/dev/uart/uart_bus_isa.c (revision 338074) @@ -1,175 +1,175 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 TAKAHASHI Yoshihiro * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2001 M. Warner Losh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static int uart_isa_probe(device_t dev); static device_method_t uart_isa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_isa_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), DEVMETHOD(device_resume, uart_bus_resume), { 0, 0 } }; static driver_t uart_isa_driver = { uart_driver_name, uart_isa_methods, sizeof(struct uart_softc), }; static struct isa_pnp_id isa_ns8250_ids[] = { {0x0005d041, "Standard PC COM port"}, /* PNP0500 */ {0x0105d041, "16550A-compatible COM port"}, /* PNP0501 */ {0x0205d041, "Multiport serial device (non-intelligent 16550)"}, /* PNP0502 */ {0x1005d041, "Generic IRDA-compatible device"}, /* PNP0510 */ {0x1105d041, "Generic IRDA-compatible device"}, /* PNP0511 */ /* Devices that do not have a compatid */ {0x12206804, NULL}, /* ACH2012 - 5634BTS 56K Video Ready Modem */ {0x7602a904, NULL}, /* AEI0276 - 56K v.90 Fax Modem (LKT) */ {0x00007905, NULL}, /* AKY0000 - 56K Plug&Play Modem */ {0x21107905, NULL}, /* AKY1021 - 56K Plug&Play Modem */ {0x01405407, NULL}, /* AZT4001 - AZT3000 PnP SOUND DEVICE, MODEM */ {0x56039008, NULL}, /* BDP0356 - Best Data 56x2 */ {0x56159008, NULL}, /* BDP1556 - B.D. Smart One 56SPS,Voice Modem*/ {0x36339008, NULL}, /* BDP3336 - Best Data Prods. 336F */ {0x0014490a, NULL}, /* BRI1400 - Boca 33.6 PnP */ {0x0015490a, NULL}, /* BRI1500 - Internal Fax Data */ {0x0034490a, NULL}, /* BRI3400 - Internal ACF Modem */ {0x0094490a, NULL}, /* BRI9400 - Boca K56Flex PnP */ {0x00b4490a, NULL}, /* BRIB400 - Boca 56k PnP */ {0x0010320d, NULL}, /* CIR1000 - Cirrus Logic V34 */ {0x0030320d, NULL}, /* CIR3000 - Cirrus Logic V43 */ {0x0100440e, NULL}, /* CRD0001 - Cardinal MVP288IV ? */ {0x01308c0e, NULL}, /* CTL3001 - Creative Labs Phoneblaster */ {0x36033610, NULL}, /* DAV0336 - DAVICOM 336PNP MODEM */ {0x01009416, NULL}, /* ETT0001 - E-Tech Bullet 33k6 PnP */ {0x0000aa1a, NULL}, /* FUJ0000 - FUJITSU Modem 33600 PNP/I2 */ {0x1200c31e, NULL}, /* GVC0012 - VF1128HV-R9 (win modem?) */ {0x0303c31e, NULL}, /* GVC0303 - MaxTech 33.6 PnP D/F/V */ {0x0505c31e, NULL}, /* GVC0505 - GVC 56k Faxmodem */ {0x0116c31e, NULL}, /* GVC1601 - Rockwell V.34 Plug & Play Modem */ {0x0050c31e, NULL}, /* GVC5000 - some GVC modem */ {0x3800f91e, NULL}, /* GWY0038 - Telepath with v.90 */ {0x9062f91e, NULL}, /* GWY6290 - Telepath with x2 Technology */ {0x8100e425, NULL}, /* IOD0081 - I-O DATA DEVICE,INC. IFML-560 */ {0x71004d24, NULL}, /* IBM0071 - IBM ThinkPad 240 IrDA controller*/ {0x21002534, NULL}, /* MAE0021 - Jetstream Int V.90 56k Voice Series 2*/ {0x0000f435, NULL}, /* MOT0000 - Motorola ModemSURFR 33.6 Intern */ {0x5015f435, NULL}, /* MOT1550 - Motorola ModemSURFR 56K Modem */ {0xf015f435, NULL}, /* MOT15F0 - Motorola VoiceSURFR 56K Modem */ {0x6045f435, NULL}, /* MOT4560 - Motorola ? */ {0x61e7a338, NULL}, /* NECE761 - 33.6Modem */ {0x0160633a, NULL}, /* NSC6001 - National Semi's IrDA Controller*/ {0x08804f3f, NULL}, /* OZO8008 - Zoom (33.6k Modem) */ {0x0f804f3f, NULL}, /* OZO800f - Zoom 2812 (56k Modem) */ {0x39804f3f, NULL}, /* OZO8039 - Zoom 56k flex */ {0x00914f3f, NULL}, /* OZO9100 - Zoom 2919 (K56 Faxmodem) */ {0x3024a341, NULL}, /* PMC2430 - Pace 56 Voice Internal Modem */ {0x1000eb49, NULL}, /* ROK0010 - Rockwell ? */ {0x1200b23d, NULL}, /* RSS0012 - OMRON ME5614ISA */ {0x5002734a, NULL}, /* RSS0250 - 5614Jx3(G) Internal Modem */ {0x6202734a, NULL}, /* RSS0262 - 5614Jx3[G] V90+K56Flex Modem */ {0x1010104d, NULL}, /* SHP1010 - Rockwell 33600bps Modem */ {0x10f0a34d, NULL}, /* SMCF010 - SMC IrCC*/ {0xc100ad4d, NULL}, /* SMM00C1 - Leopard 56k PnP */ {0x9012b04e, NULL}, /* SUP1290 - Supra ? */ {0x1013b04e, NULL}, /* SUP1310 - SupraExpress 336i PnP */ {0x8013b04e, NULL}, /* SUP1380 - SupraExpress 288i PnP Voice */ {0x8113b04e, NULL}, /* SUP1381 - SupraExpress 336i PnP Voice */ {0x5016b04e, NULL}, /* SUP1650 - Supra 336i Sp Intl */ {0x7016b04e, NULL}, /* SUP1670 - Supra 336i V+ Intl */ {0x7420b04e, NULL}, /* SUP2070 - Supra ? */ {0x8020b04e, NULL}, /* SUP2080 - Supra ? */ {0x8420b04e, NULL}, /* SUP2084 - SupraExpress 56i PnP */ {0x7121b04e, NULL}, /* SUP2171 - SupraExpress 56i Sp? */ {0x8024b04e, NULL}, /* SUP2480 - Supra ? */ {0x01007256, NULL}, /* USR0001 - U.S. Robotics Inc., Sportster W */ {0x02007256, NULL}, /* USR0002 - U.S. Robotics Inc. Sportster 33. */ {0x04007256, NULL}, /* USR0004 - USR Sportster 14.4k */ {0x06007256, NULL}, /* USR0006 - USR Sportster 33.6k */ {0x11007256, NULL}, /* USR0011 - USR ? */ {0x01017256, NULL}, /* USR0101 - USR ? */ {0x30207256, NULL}, /* USR2030 - U.S.Robotics Inc. Sportster 560 */ {0x50207256, NULL}, /* USR2050 - U.S.Robotics Inc. Sportster 33. */ {0x70207256, NULL}, /* USR2070 - U.S.Robotics Inc. Sportster 560 */ {0x30307256, NULL}, /* USR3030 - U.S. Robotics 56K FAX INT */ {0x31307256, NULL}, /* USR3031 - U.S. Robotics 56K FAX INT */ {0x50307256, NULL}, /* USR3050 - U.S. Robotics 56K FAX INT */ {0x70307256, NULL}, /* USR3070 - U.S. Robotics 56K Voice INT */ {0x90307256, NULL}, /* USR3090 - USR ? */ {0x70917256, NULL}, /* USR9170 - U.S. Robotics 56K FAX INT */ {0x90917256, NULL}, /* USR9190 - USR 56k Voice INT */ {0x04f0235c, NULL}, /* WACF004 - Wacom Tablet PC Screen */ {0x0ef0235c, NULL}, /* WACF00e - Wacom Tablet PC Screen 00e */ {0x0300695c, NULL}, /* WCI0003 - Fax/Voice/Modem/Speakphone/Asvd */ {0x01a0896a, NULL}, /* ZTIA001 - Zoom Internal V90 Faxmodem */ {0x61f7896a, NULL}, /* ZTIF761 - Zoom ComStar 33.6 */ {0} }; static int uart_isa_probe(device_t dev) { struct uart_softc *sc; device_t parent; parent = device_get_parent(dev); sc = device_get_softc(dev); /* Check PnP IDs */ if (ISA_PNP_PROBE(parent, dev, isa_ns8250_ids) == ENXIO) return (ENXIO); /* Probe PnP _and_ non-PnP ns8250 here. */ sc->sc_class = &uart_ns8250_class; - return (uart_bus_probe(dev, 0, 0, 0, 0, 0)); + return (uart_bus_probe(dev, 0, 0, 0, 0, 0, 0)); } DRIVER_MODULE(uart, isa, uart_isa_driver, uart_devclass, 0, 0); ISA_PNP_INFO(isa_ns8250_ids); Index: head/sys/dev/uart/uart_bus_pccard.c =================================================================== --- head/sys/dev/uart/uart_bus_pccard.c (revision 338073) +++ head/sys/dev/uart/uart_bus_pccard.c (revision 338074) @@ -1,106 +1,106 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 M. Warner Losh. All rights reserved. * Copyright (c) 2003 Norikatsu Shigemura, Takenori Watanabe All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "pccarddevs.h" static int uart_pccard_probe(device_t dev); static int uart_pccard_attach(device_t dev); static device_method_t uart_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_pccard_probe), DEVMETHOD(device_attach, uart_pccard_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static uint32_t uart_pccard_function = PCCARD_FUNCTION_SERIAL; static driver_t uart_pccard_driver = { uart_driver_name, uart_pccard_methods, sizeof(struct uart_softc), }; static int uart_pccard_probe(device_t dev) { int error; uint32_t fcn; fcn = PCCARD_FUNCTION_UNSPEC; error = pccard_get_function(dev, &fcn); if (error != 0) return (error); /* * If a serial card, we are likely the right driver. However, * some serial cards are better serviced by other drivers, so * allow other drivers to claim it, if they want. */ if (fcn == uart_pccard_function) return (BUS_PROBE_GENERIC); return (ENXIO); } static int uart_pccard_attach(device_t dev) { struct uart_softc *sc; int error; sc = device_get_softc(dev); sc->sc_class = &uart_ns8250_class; - error = uart_bus_probe(dev, 0, 0, 0, 0, 0); + error = uart_bus_probe(dev, 0, 0, 0, 0, 0, 0); if (error > 0) return (error); return (uart_bus_attach(dev)); } DRIVER_MODULE(uart, pccard, uart_pccard_driver, uart_devclass, 0, 0); MODULE_PNP_INFO("U32:function_type;", pccard, uart, &uart_pccard_function, sizeof(uart_pccard_function), 1); Index: head/sys/dev/uart/uart_bus_pci.c =================================================================== --- head/sys/dev/uart/uart_bus_pci.c (revision 338073) +++ head/sys/dev/uart/uart_bus_pci.c (revision 338074) @@ -1,255 +1,255 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Marcel Moolenaar * Copyright (c) 2001 M. Warner Losh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_RCLK 1843200 static int uart_pci_probe(device_t dev); static int uart_pci_attach(device_t dev); static int uart_pci_detach(device_t dev); static device_method_t uart_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_pci_probe), DEVMETHOD(device_attach, uart_pci_attach), DEVMETHOD(device_detach, uart_pci_detach), DEVMETHOD(device_resume, uart_bus_resume), DEVMETHOD_END }; static driver_t uart_pci_driver = { uart_driver_name, uart_pci_methods, sizeof(struct uart_softc), }; struct pci_id { uint16_t vendor; uint16_t device; uint16_t subven; uint16_t subdev; const char *desc; int rid; int rclk; int regshft; }; static const struct pci_id pci_ns8250_ids[] = { { 0x1028, 0x0008, 0xffff, 0, "Dell Remote Access Card III", 0x14, 128 * DEFAULT_RCLK }, { 0x1028, 0x0012, 0xffff, 0, "Dell RAC 4 Daughter Card Virtual UART", 0x14, 128 * DEFAULT_RCLK }, { 0x1033, 0x0074, 0x1033, 0x8014, "NEC RCV56ACF 56k Voice Modem", 0x10 }, { 0x1033, 0x007d, 0x1033, 0x8012, "NEC RS232C", 0x10 }, { 0x103c, 0x1048, 0x103c, 0x1227, "HP Diva Serial [GSP] UART - Powerbar SP2", 0x10 }, { 0x103c, 0x1048, 0x103c, 0x1301, "HP Diva RMP3", 0x14 }, { 0x103c, 0x1290, 0xffff, 0, "HP Auxiliary Diva Serial Port", 0x18 }, { 0x103c, 0x3301, 0xffff, 0, "HP iLO serial port", 0x10 }, { 0x11c1, 0x0480, 0xffff, 0, "Agere Systems Venus Modem (V90, 56KFlex)", 0x14 }, { 0x115d, 0x0103, 0xffff, 0, "Xircom Cardbus Ethernet + 56k Modem", 0x10 }, { 0x125b, 0x9100, 0xa000, 0x1000, "ASIX AX99100 PCIe 1/2/3/4-port RS-232/422/485", 0x10 }, { 0x1282, 0x6585, 0xffff, 0, "Davicom 56PDV PCI Modem", 0x10 }, { 0x12b9, 0x1008, 0xffff, 0, "3Com 56K FaxModem Model 5610", 0x10 }, { 0x131f, 0x1000, 0xffff, 0, "Siig CyberSerial (1-port) 16550", 0x18 }, { 0x131f, 0x1001, 0xffff, 0, "Siig CyberSerial (1-port) 16650", 0x18 }, { 0x131f, 0x1002, 0xffff, 0, "Siig CyberSerial (1-port) 16850", 0x18 }, { 0x131f, 0x2000, 0xffff, 0, "Siig CyberSerial (1-port) 16550", 0x10 }, { 0x131f, 0x2001, 0xffff, 0, "Siig CyberSerial (1-port) 16650", 0x10 }, { 0x131f, 0x2002, 0xffff, 0, "Siig CyberSerial (1-port) 16850", 0x10 }, { 0x135c, 0x0190, 0xffff, 0, "Quatech SSCLP-100", 0x18 }, { 0x135c, 0x01c0, 0xffff, 0, "Quatech SSCLP-200/300", 0x18 }, { 0x135e, 0x7101, 0xffff, 0, "Sealevel Systems Single Port RS-232/422/485/530", 0x18 }, { 0x1407, 0x0110, 0xffff, 0, "Lava Computer mfg DSerial-PCI Port A", 0x10 }, { 0x1407, 0x0111, 0xffff, 0, "Lava Computer mfg DSerial-PCI Port B", 0x10 }, { 0x1407, 0x0510, 0xffff, 0, "Lava SP Serial 550 PCI", 0x10 }, { 0x1409, 0x7168, 0x1409, 0x4025, "Timedia Technology Serial Port", 0x10, 8 * DEFAULT_RCLK }, { 0x1409, 0x7168, 0x1409, 0x4027, "Timedia Technology Serial Port", 0x10, 8 * DEFAULT_RCLK }, { 0x1409, 0x7168, 0x1409, 0x4028, "Timedia Technology Serial Port", 0x10, 8 * DEFAULT_RCLK }, { 0x1409, 0x7168, 0x1409, 0x5025, "Timedia Technology Serial Port", 0x10, 8 * DEFAULT_RCLK }, { 0x1409, 0x7168, 0x1409, 0x5027, "Timedia Technology Serial Port", 0x10, 8 * DEFAULT_RCLK }, { 0x1415, 0x950b, 0xffff, 0, "Oxford Semiconductor OXCB950 Cardbus 16950 UART", 0x10, 16384000 }, { 0x1415, 0xc120, 0xffff, 0, "Oxford Semiconductor OXPCIe952 PCIe 16950 UART", 0x10 }, { 0x14e4, 0x160a, 0xffff, 0, "Broadcom TruManage UART", 0x10, 128 * DEFAULT_RCLK, 2}, { 0x14e4, 0x4344, 0xffff, 0, "Sony Ericsson GC89 PC Card", 0x10}, { 0x151f, 0x0000, 0xffff, 0, "TOPIC Semiconductor TP560 56k modem", 0x10 }, { 0x1fd4, 0x1999, 0x1fd4, 0x0001, "Sunix SER5xxxx Serial Port", 0x10, 8 * DEFAULT_RCLK }, { 0x8086, 0x0f0a, 0xffff, 0, "Intel ValleyView LPIO1 HSUART#1", 0x10, 24 * DEFAULT_RCLK, 2 }, { 0x8086, 0x0f0c, 0xffff, 0, "Intel ValleyView LPIO1 HSUART#2", 0x10, 24 * DEFAULT_RCLK, 2 }, { 0x8086, 0x108f, 0xffff, 0, "Intel AMT - SOL", 0x10 }, { 0x8086, 0x1c3d, 0xffff, 0, "Intel AMT - KT Controller", 0x10 }, { 0x8086, 0x1d3d, 0xffff, 0, "Intel C600/X79 Series Chipset KT Controller", 0x10 }, { 0x8086, 0x1e3d, 0xffff, 0, "Intel Panther Point KT Controller", 0x10 }, { 0x8086, 0x228a, 0xffff, 0, "Intel Cherryview SIO HSUART#1", 0x10, 24 * DEFAULT_RCLK, 2 }, { 0x8086, 0x228c, 0xffff, 0, "Intel Cherryview SIO HSUART#2", 0x10, 24 * DEFAULT_RCLK, 2 }, { 0x8086, 0x2a07, 0xffff, 0, "Intel AMT - PM965/GM965 KT Controller", 0x10 }, { 0x8086, 0x2a47, 0xffff, 0, "Mobile 4 Series Chipset KT Controller", 0x10 }, { 0x8086, 0x2e17, 0xffff, 0, "4 Series Chipset Serial KT Controller", 0x10 }, { 0x8086, 0x3b67, 0xffff, 0, "5 Series/3400 Series Chipset KT Controller", 0x10 }, { 0x8086, 0x8811, 0xffff, 0, "Intel EG20T Serial Port 0", 0x10 }, { 0x8086, 0x8812, 0xffff, 0, "Intel EG20T Serial Port 1", 0x10 }, { 0x8086, 0x8813, 0xffff, 0, "Intel EG20T Serial Port 2", 0x10 }, { 0x8086, 0x8814, 0xffff, 0, "Intel EG20T Serial Port 3", 0x10 }, { 0x8086, 0x8c3d, 0xffff, 0, "Intel Lynx Point KT Controller", 0x10 }, { 0x8086, 0x8cbd, 0xffff, 0, "Intel Wildcat Point KT Controller", 0x10 }, { 0x8086, 0x9c3d, 0xffff, 0, "Intel Lynx Point-LP HECI KT", 0x10 }, { 0x9710, 0x9820, 0x1000, 1, "NetMos NM9820 Serial Port", 0x10 }, { 0x9710, 0x9835, 0x1000, 1, "NetMos NM9835 Serial Port", 0x10 }, { 0x9710, 0x9865, 0xa000, 0x1000, "NetMos NM9865 Serial Port", 0x10 }, { 0x9710, 0x9900, 0xa000, 0x1000, "MosChip MCS9900 PCIe to Peripheral Controller", 0x10 }, { 0x9710, 0x9901, 0xa000, 0x1000, "MosChip MCS9901 PCIe to Peripheral Controller", 0x10 }, { 0x9710, 0x9904, 0xa000, 0x1000, "MosChip MCS9904 PCIe to Peripheral Controller", 0x10 }, { 0x9710, 0x9922, 0xa000, 0x1000, "MosChip MCS9922 PCIe to Peripheral Controller", 0x10 }, { 0xdeaf, 0x9051, 0xffff, 0, "Middle Digital PC Weasel Serial Port", 0x10 }, { 0xffff, 0, 0xffff, 0, NULL, 0, 0} }; const static struct pci_id * uart_pci_match(device_t dev, const struct pci_id *id) { uint16_t device, subdev, subven, vendor; vendor = pci_get_vendor(dev); device = pci_get_device(dev); while (id->vendor != 0xffff && (id->vendor != vendor || id->device != device)) id++; if (id->vendor == 0xffff) return (NULL); if (id->subven == 0xffff) return (id); subven = pci_get_subvendor(dev); subdev = pci_get_subdevice(dev); while (id->vendor == vendor && id->device == device && (id->subven != subven || id->subdev != subdev)) id++; return ((id->vendor == vendor && id->device == device) ? id : NULL); } static int uart_pci_probe(device_t dev) { struct uart_softc *sc; const struct pci_id *id; int result; sc = device_get_softc(dev); id = uart_pci_match(dev, pci_ns8250_ids); if (id != NULL) { sc->sc_class = &uart_ns8250_class; goto match; } /* Add checks for non-ns8250 IDs here. */ return (ENXIO); match: - result = uart_bus_probe(dev, id->regshft, 0, id->rclk, id->rid, 0); + result = uart_bus_probe(dev, id->regshft, 0, id->rclk, id->rid, 0, 0); /* Bail out on error. */ if (result > 0) return (result); /* Set/override the device description. */ if (id->desc) device_set_desc(dev, id->desc); return (result); } static int uart_pci_attach(device_t dev) { struct uart_softc *sc; int count; sc = device_get_softc(dev); /* * Use MSI in preference to legacy IRQ if available. * Whilst some PCIe UARTs support >1 MSI vector, use only the first. */ if (pci_msi_count(dev) > 0) { count = 1; if (pci_alloc_msi(dev, &count) == 0) { sc->sc_irid = 1; device_printf(dev, "Using %d MSI message\n", count); } } return (uart_bus_attach(dev)); } static int uart_pci_detach(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); if (sc->sc_irid != 0) pci_release_msi(dev); return (uart_bus_detach(dev)); } DRIVER_MODULE(uart, pci, uart_pci_driver, uart_devclass, NULL, NULL); Index: head/sys/dev/uart/uart_bus_puc.c =================================================================== --- head/sys/dev/uart/uart_bus_puc.c (revision 338073) +++ head/sys/dev/uart/uart_bus_puc.c (revision 338074) @@ -1,89 +1,89 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Marcel Moolenaar. All rights reserved. * Copyright (c) 2002 JF Hay. All rights reserved. * Copyright (c) 2001 M. Warner Losh. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static int uart_puc_probe(device_t dev); static device_method_t uart_puc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_puc_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), /* Serdev interface */ DEVMETHOD(serdev_ihand, uart_bus_ihand), DEVMETHOD(serdev_ipend, uart_bus_ipend), { 0, 0 } }; static driver_t uart_puc_driver = { uart_driver_name, uart_puc_methods, sizeof(struct uart_softc), }; static int uart_puc_probe(device_t dev) { device_t parent; struct uart_softc *sc; uintptr_t rclk, type; parent = device_get_parent(dev); sc = device_get_softc(dev); if (BUS_READ_IVAR(parent, dev, PUC_IVAR_TYPE, &type)) return (ENXIO); if (type != PUC_TYPE_SERIAL) return (ENXIO); sc->sc_class = &uart_ns8250_class; if (BUS_READ_IVAR(parent, dev, PUC_IVAR_CLOCK, &rclk)) rclk = 0; - return (uart_bus_probe(dev, 0, 0, rclk, 0, 0)); + return (uart_bus_probe(dev, 0, 0, rclk, 0, 0, 0)); } DRIVER_MODULE(uart, puc, uart_puc_driver, uart_devclass, 0, 0); Index: head/sys/dev/uart/uart_bus_scc.c =================================================================== --- head/sys/dev/uart/uart_bus_scc.c (revision 338073) +++ head/sys/dev/uart/uart_bus_scc.c (revision 338074) @@ -1,120 +1,120 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2006 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static int uart_scc_attach(device_t dev); static int uart_scc_probe(device_t dev); static device_method_t uart_scc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_scc_probe), DEVMETHOD(device_attach, uart_scc_attach), DEVMETHOD(device_detach, uart_bus_detach), /* Serdev interface */ DEVMETHOD(serdev_ihand, uart_bus_ihand), DEVMETHOD(serdev_sysdev, uart_bus_sysdev), { 0, 0 } }; static driver_t uart_scc_driver = { uart_driver_name, uart_scc_methods, sizeof(struct uart_softc), }; static int uart_scc_attach(device_t dev) { device_t parent; struct uart_softc *sc; uintptr_t mtx; parent = device_get_parent(dev); sc = device_get_softc(dev); if (BUS_READ_IVAR(parent, dev, SCC_IVAR_HWMTX, &mtx)) return (ENXIO); sc->sc_hwmtx = (struct mtx *)(void *)mtx; return (uart_bus_attach(dev)); } static int uart_scc_probe(device_t dev) { device_t parent; struct uart_softc *sc; uintptr_t ch, cl, md, rs; parent = device_get_parent(dev); sc = device_get_softc(dev); if (BUS_READ_IVAR(parent, dev, SCC_IVAR_MODE, &md) || BUS_READ_IVAR(parent, dev, SCC_IVAR_CLASS, &cl)) return (ENXIO); if (md != SCC_MODE_ASYNC) return (ENXIO); switch (cl) { case SCC_CLASS_QUICC: sc->sc_class = &uart_quicc_class; break; case SCC_CLASS_SAB82532: sc->sc_class = &uart_sab82532_class; break; case SCC_CLASS_Z8530: sc->sc_class = &uart_z8530_class; break; default: return (ENXIO); } if (BUS_READ_IVAR(parent, dev, SCC_IVAR_CHANNEL, &ch) || BUS_READ_IVAR(parent, dev, SCC_IVAR_CLOCK, &cl) || BUS_READ_IVAR(parent, dev, SCC_IVAR_REGSHFT, &rs)) return (ENXIO); - return (uart_bus_probe(dev, rs, 0, cl, 0, ch)); + return (uart_bus_probe(dev, rs, 0, cl, 0, ch, 0)); } DRIVER_MODULE(uart, scc, uart_scc_driver, uart_devclass, 0, 0); Index: head/sys/dev/uart/uart_core.c =================================================================== --- head/sys/dev/uart/uart_core.c (revision 338073) +++ head/sys/dev/uart/uart_core.c (revision 338074) @@ -1,823 +1,824 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" devclass_t uart_devclass; const char uart_driver_name[] = "uart"; SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs = SLIST_HEAD_INITIALIZER(uart_sysdevs); static MALLOC_DEFINE(M_UART, "UART", "UART driver"); #ifndef UART_POLL_FREQ #define UART_POLL_FREQ 50 #endif static int uart_poll_freq = UART_POLL_FREQ; SYSCTL_INT(_debug, OID_AUTO, uart_poll_freq, CTLFLAG_RDTUN, &uart_poll_freq, 0, "UART poll frequency"); static int uart_force_poll; SYSCTL_INT(_debug, OID_AUTO, uart_force_poll, CTLFLAG_RDTUN, &uart_force_poll, 0, "Force UART polling"); static inline int uart_pps_mode_valid(int pps_mode) { int opt; switch(pps_mode & UART_PPS_SIGNAL_MASK) { case UART_PPS_DISABLED: case UART_PPS_CTS: case UART_PPS_DCD: break; default: return (false); } opt = pps_mode & UART_PPS_OPTION_MASK; if ((opt & ~(UART_PPS_INVERT_PULSE | UART_PPS_NARROW_PULSE)) != 0) return (false); return (true); } static void uart_pps_print_mode(struct uart_softc *sc) { device_printf(sc->sc_dev, "PPS capture mode: "); switch(sc->sc_pps_mode & UART_PPS_SIGNAL_MASK) { case UART_PPS_DISABLED: printf("disabled"); break; case UART_PPS_CTS: printf("CTS"); break; case UART_PPS_DCD: printf("DCD"); break; default: printf("invalid"); break; } if (sc->sc_pps_mode & UART_PPS_INVERT_PULSE) printf("-Inverted"); if (sc->sc_pps_mode & UART_PPS_NARROW_PULSE) printf("-NarrowPulse"); printf("\n"); } static int uart_pps_mode_sysctl(SYSCTL_HANDLER_ARGS) { struct uart_softc *sc; int err, tmp; sc = arg1; tmp = sc->sc_pps_mode; err = sysctl_handle_int(oidp, &tmp, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (!uart_pps_mode_valid(tmp)) return (EINVAL); sc->sc_pps_mode = tmp; return(0); } static void uart_pps_process(struct uart_softc *sc, int ser_sig) { sbintime_t now; int is_assert, pps_sig; /* Which signal is configured as PPS? Early out if none. */ switch(sc->sc_pps_mode & UART_PPS_SIGNAL_MASK) { case UART_PPS_CTS: pps_sig = SER_CTS; break; case UART_PPS_DCD: pps_sig = SER_DCD; break; default: return; } /* Early out if there is no change in the signal configured as PPS. */ if ((ser_sig & SER_DELTA(pps_sig)) == 0) return; /* * In narrow-pulse mode we need to synthesize both capture and clear * events from a single "delta occurred" indication from the uart * hardware because the pulse width is too narrow to reliably detect * both edges. However, when the pulse width is close to our interrupt * processing latency we might intermittantly catch both edges. To * guard against generating spurious events when that happens, we use a * separate timer to ensure at least half a second elapses before we * generate another event. */ pps_capture(&sc->sc_pps); if (sc->sc_pps_mode & UART_PPS_NARROW_PULSE) { now = getsbinuptime(); if (now > sc->sc_pps_captime + 500 * SBT_1MS) { sc->sc_pps_captime = now; pps_event(&sc->sc_pps, PPS_CAPTUREASSERT); pps_event(&sc->sc_pps, PPS_CAPTURECLEAR); } } else { is_assert = ser_sig & pps_sig; if (sc->sc_pps_mode & UART_PPS_INVERT_PULSE) is_assert = !is_assert; pps_event(&sc->sc_pps, is_assert ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } } static void uart_pps_init(struct uart_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; ctx = device_get_sysctl_ctx(sc->sc_dev); tree = device_get_sysctl_tree(sc->sc_dev); /* * The historical default for pps capture mode is either DCD or CTS, * depending on the UART_PPS_ON_CTS kernel option. Start with that, * then try to fetch the tunable that overrides the mode for all uart * devices, then try to fetch the sysctl-tunable that overrides the mode * for one specific device. */ #ifdef UART_PPS_ON_CTS sc->sc_pps_mode = UART_PPS_CTS; #else sc->sc_pps_mode = UART_PPS_DCD; #endif TUNABLE_INT_FETCH("hw.uart.pps_mode", &sc->sc_pps_mode); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pps_mode", CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, uart_pps_mode_sysctl, "I", "pulse mode: 0/1/2=disabled/CTS/DCD; " "add 0x10 to invert, 0x20 for narrow pulse"); if (!uart_pps_mode_valid(sc->sc_pps_mode)) { device_printf(sc->sc_dev, "Invalid pps_mode 0x%02x configured; disabling PPS capture\n", sc->sc_pps_mode); sc->sc_pps_mode = UART_PPS_DISABLED; } else if (bootverbose) { uart_pps_print_mode(sc); } sc->sc_pps.ppscap = PPS_CAPTUREBOTH; sc->sc_pps.driver_mtx = uart_tty_getlock(sc); sc->sc_pps.driver_abi = PPS_ABI_VERSION; pps_init_abi(&sc->sc_pps); } void uart_add_sysdev(struct uart_devinfo *di) { SLIST_INSERT_HEAD(&uart_sysdevs, di, next); } const char * uart_getname(struct uart_class *uc) { return ((uc != NULL) ? uc->name : NULL); } struct uart_ops * uart_getops(struct uart_class *uc) { return ((uc != NULL) ? uc->uc_ops : NULL); } int uart_getrange(struct uart_class *uc) { return ((uc != NULL) ? uc->uc_range : 0); } u_int uart_getregshift(struct uart_class *uc) { return ((uc != NULL) ? uc->uc_rshift : 0); } u_int uart_getregiowidth(struct uart_class *uc) { return ((uc != NULL) ? uc->uc_riowidth : 0); } /* * Schedule a soft interrupt. We do this on the 0 to !0 transition * of the TTY pending interrupt status. */ void uart_sched_softih(struct uart_softc *sc, uint32_t ipend) { uint32_t new, old; do { old = sc->sc_ttypend; new = old | ipend; } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); if ((old & SER_INT_MASK) == 0) swi_sched(sc->sc_softih, 0); } /* * A break condition has been detected. We treat the break condition as * a special case that should not happen during normal operation. When * the break condition is to be passed to higher levels in the form of * a NUL character, we really want the break to be in the right place in * the input stream. The overhead to achieve that is not in relation to * the exceptional nature of the break condition, so we permit ourselves * to be sloppy. */ static __inline int uart_intr_break(void *arg) { struct uart_softc *sc = arg; #if defined(KDB) if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { if (kdb_break()) return (0); } #endif if (sc->sc_opened) uart_sched_softih(sc, SER_INT_BREAK); return (0); } /* * Handle a receiver overrun situation. We lost at least 1 byte in the * input stream and it's our job to contain the situation. We grab as * much of the data we can, but otherwise flush the receiver FIFO to * create some breathing room. The net effect is that we avoid the * overrun condition to happen for the next X characters, where X is * related to the FIFO size at the cost of losing data right away. * So, instead of having multiple overrun interrupts in close proximity * to each other and possibly pessimizing UART interrupt latency for * other UARTs in a multiport configuration, we create a longer segment * of missing characters by freeing up the FIFO. * Each overrun condition is marked in the input buffer by a token. The * token represents the loss of at least one, but possible more bytes in * the input stream. */ static __inline int uart_intr_overrun(void *arg) { struct uart_softc *sc = arg; if (sc->sc_opened) { UART_RECEIVE(sc); if (uart_rx_put(sc, UART_STAT_OVERRUN)) sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; uart_sched_softih(sc, SER_INT_RXREADY); } UART_FLUSH(sc, UART_FLUSH_RECEIVER); return (0); } /* * Received data ready. */ static __inline int uart_intr_rxready(void *arg) { struct uart_softc *sc = arg; int rxp; rxp = sc->sc_rxput; UART_RECEIVE(sc); #if defined(KDB) if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { while (rxp != sc->sc_rxput) { kdb_alt_break(sc->sc_rxbuf[rxp++], &sc->sc_altbrk); if (rxp == sc->sc_rxbufsz) rxp = 0; } } #endif if (sc->sc_opened) uart_sched_softih(sc, SER_INT_RXREADY); else sc->sc_rxput = sc->sc_rxget; /* Ignore received data. */ return (1); } /* * Line or modem status change (OOB signalling). * We pass the signals to the software interrupt handler for further * processing. Note that we merge the delta bits, but set the state * bits. This is to avoid losing state transitions due to having more * than 1 hardware interrupt between software interrupts. */ static __inline int uart_intr_sigchg(void *arg) { struct uart_softc *sc = arg; int new, old, sig; sig = UART_GETSIG(sc); /* * Time pulse counting support, invoked whenever the PPS parameters are * currently set to capture either edge of the signal. */ if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) { uart_pps_process(sc, sig); } /* * Keep track of signal changes, even when the device is not * opened. This allows us to inform upper layers about a * possible loss of DCD and thus the existence of a (possibly) * different connection when we have DCD back, during the time * that the device was closed. */ do { old = sc->sc_ttypend; new = old & ~SER_MASK_STATE; new |= sig & SER_INT_SIGMASK; } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); if (sc->sc_opened) uart_sched_softih(sc, SER_INT_SIGCHG); return (1); } /* * The transmitter can accept more data. */ static __inline int uart_intr_txidle(void *arg) { struct uart_softc *sc = arg; if (sc->sc_txbusy) { sc->sc_txbusy = 0; uart_sched_softih(sc, SER_INT_TXIDLE); } return (0); } static int uart_intr(void *arg) { struct uart_softc *sc = arg; int cnt, ipend, testintr; if (sc->sc_leaving) return (FILTER_STRAY); cnt = 0; testintr = sc->sc_testintr; while ((!testintr || cnt < 20) && (ipend = UART_IPEND(sc)) != 0) { cnt++; if (ipend & SER_INT_OVERRUN) uart_intr_overrun(sc); if (ipend & SER_INT_BREAK) uart_intr_break(sc); if (ipend & SER_INT_RXREADY) uart_intr_rxready(sc); if (ipend & SER_INT_SIGCHG) uart_intr_sigchg(sc); if (ipend & SER_INT_TXIDLE) uart_intr_txidle(sc); } if (sc->sc_polled) { callout_reset(&sc->sc_timer, hz / uart_poll_freq, (timeout_t *)uart_intr, sc); } return ((cnt == 0) ? FILTER_STRAY : ((testintr && cnt == 20) ? FILTER_SCHEDULE_THREAD : FILTER_HANDLED)); } serdev_intr_t * uart_bus_ihand(device_t dev, int ipend) { switch (ipend) { case SER_INT_BREAK: return (uart_intr_break); case SER_INT_OVERRUN: return (uart_intr_overrun); case SER_INT_RXREADY: return (uart_intr_rxready); case SER_INT_SIGCHG: return (uart_intr_sigchg); case SER_INT_TXIDLE: return (uart_intr_txidle); } return (NULL); } int uart_bus_ipend(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); return (UART_IPEND(sc)); } int uart_bus_sysdev(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); return ((sc->sc_sysdev != NULL) ? 1 : 0); } int -uart_bus_probe(device_t dev, int regshft, int regiowidth, int rclk, int rid, int chan) +uart_bus_probe(device_t dev, int regshft, int regiowidth, int rclk, int rid, int chan, int quirks) { struct uart_softc *sc; struct uart_devinfo *sysdev; int error; sc = device_get_softc(dev); /* * All uart_class references are weak. Check that the needed * class has been compiled-in. Fail if not. */ if (sc->sc_class == NULL) return (ENXIO); /* * Initialize the instance. Note that the instance (=softc) does * not necessarily match the hardware specific softc. We can't do * anything about it now, because we may not attach to the device. * Hardware drivers cannot use any of the class specific fields * while probing. */ kobj_init((kobj_t)sc, (kobj_class_t)sc->sc_class); sc->sc_dev = dev; if (device_get_desc(dev) == NULL) device_set_desc(dev, uart_getname(sc->sc_class)); /* * Allocate the register resource. We assume that all UARTs have * a single register window in either I/O port space or memory * mapped I/O space. Any UART that needs multiple windows will * consequently not be supported by this driver as-is. We try I/O * port space first because that's the common case. */ sc->sc_rrid = rid; sc->sc_rtype = SYS_RES_IOPORT; sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid, RF_ACTIVE); if (sc->sc_rres == NULL) { sc->sc_rrid = rid; sc->sc_rtype = SYS_RES_MEMORY; sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid, RF_ACTIVE); if (sc->sc_rres == NULL) return (ENXIO); } /* * Fill in the bus access structure and compare this device with * a possible console device and/or a debug port. We set the flags * in the softc so that the hardware dependent probe can adjust * accordingly. In general, you don't want to permanently disrupt * console I/O. */ sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); sc->sc_bas.chan = chan; sc->sc_bas.regshft = regshft; sc->sc_bas.regiowidth = regiowidth; sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk; + sc->sc_bas.busy_detect = !!(quirks & UART_F_BUSY_DETECT); SLIST_FOREACH(sysdev, &uart_sysdevs, next) { if (chan == sysdev->bas.chan && uart_cpu_eqres(&sc->sc_bas, &sysdev->bas)) { /* XXX check if ops matches class. */ sc->sc_sysdev = sysdev; sysdev->bas.rclk = sc->sc_bas.rclk; } } error = UART_PROBE(sc); bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); return ((error) ? error : BUS_PROBE_DEFAULT); } int uart_bus_attach(device_t dev) { struct uart_softc *sc, *sc0; const char *sep; int error, filt; /* * The sc_class field defines the type of UART we're going to work * with and thus the size of the softc. Replace the generic softc * with one that matches the UART now that we're certain we handle * the device. */ sc0 = device_get_softc(dev); if (sc0->sc_class->size > device_get_driver(dev)->size) { sc = malloc(sc0->sc_class->size, M_UART, M_WAITOK|M_ZERO); bcopy(sc0, sc, sizeof(*sc)); device_set_softc(dev, sc); } else sc = sc0; /* * Now that we know the softc for this device, connect the back * pointer from the sysdev for this device, if any */ if (sc->sc_sysdev != NULL) sc->sc_sysdev->sc = sc; /* * Protect ourselves against interrupts while we're not completely * finished attaching and initializing. We don't expect interrupts * until after UART_ATTACH(), though. */ sc->sc_leaving = 1; mtx_init(&sc->sc_hwmtx_s, "uart_hwmtx", NULL, MTX_SPIN); if (sc->sc_hwmtx == NULL) sc->sc_hwmtx = &sc->sc_hwmtx_s; /* * Re-allocate. We expect that the softc contains the information * collected by uart_bus_probe() intact. */ sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid, RF_ACTIVE); if (sc->sc_rres == NULL) { mtx_destroy(&sc->sc_hwmtx_s); return (ENXIO); } sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); /* * Ensure there is room for at least three full FIFOs of data in the * receive buffer (handles the case of low-level drivers with huge * FIFOs), and also ensure that there is no less than the historical * size of 384 bytes (handles the typical small-FIFO case). */ sc->sc_rxbufsz = MAX(384, sc->sc_rxfifosz * 3); sc->sc_rxbuf = malloc(sc->sc_rxbufsz * sizeof(*sc->sc_rxbuf), M_UART, M_WAITOK); sc->sc_txbuf = malloc(sc->sc_txfifosz * sizeof(*sc->sc_txbuf), M_UART, M_WAITOK); error = UART_ATTACH(sc); if (error) goto fail; if (sc->sc_hwiflow || sc->sc_hwoflow) { sep = ""; device_print_prettyname(dev); if (sc->sc_hwiflow) { printf("%sRTS iflow", sep); sep = ", "; } if (sc->sc_hwoflow) { printf("%sCTS oflow", sep); sep = ", "; } printf("\n"); } if (sc->sc_sysdev != NULL) { if (sc->sc_sysdev->baudrate == 0) { if (UART_IOCTL(sc, UART_IOCTL_BAUD, (intptr_t)&sc->sc_sysdev->baudrate) != 0) sc->sc_sysdev->baudrate = -1; } switch (sc->sc_sysdev->type) { case UART_DEV_CONSOLE: device_printf(dev, "console"); break; case UART_DEV_DBGPORT: device_printf(dev, "debug port"); break; case UART_DEV_KEYBOARD: device_printf(dev, "keyboard"); break; default: device_printf(dev, "unknown system device"); break; } printf(" (%d,%c,%d,%d)\n", sc->sc_sysdev->baudrate, "noems"[sc->sc_sysdev->parity], sc->sc_sysdev->databits, sc->sc_sysdev->stopbits); } sc->sc_leaving = 0; sc->sc_testintr = 1; filt = uart_intr(sc); sc->sc_testintr = 0; /* * Don't use interrupts if we couldn't clear any pending interrupt * conditions. We may have broken H/W and polling is probably the * safest thing to do. */ if (filt != FILTER_SCHEDULE_THREAD && !uart_force_poll) { sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); } if (sc->sc_ires != NULL) { error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY, uart_intr, NULL, sc, &sc->sc_icookie); sc->sc_fastintr = (error == 0) ? 1 : 0; if (!sc->sc_fastintr) error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, NULL, (driver_intr_t *)uart_intr, sc, &sc->sc_icookie); if (error) { device_printf(dev, "could not activate interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); sc->sc_ires = NULL; } } if (sc->sc_ires == NULL) { /* No interrupt resource. Force polled mode. */ sc->sc_polled = 1; callout_init(&sc->sc_timer, 1); callout_reset(&sc->sc_timer, hz / uart_poll_freq, (timeout_t *)uart_intr, sc); } if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { sep = ""; device_print_prettyname(dev); if (sc->sc_fastintr) { printf("%sfast interrupt", sep); sep = ", "; } if (sc->sc_polled) { printf("%spolled mode (%dHz)", sep, uart_poll_freq); sep = ", "; } printf("\n"); } if (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL) { if ((error = sc->sc_sysdev->attach(sc)) != 0) goto fail; } else { if ((error = uart_tty_attach(sc)) != 0) goto fail; uart_pps_init(sc); } if (sc->sc_sysdev != NULL) sc->sc_sysdev->hwmtx = sc->sc_hwmtx; return (0); fail: free(sc->sc_txbuf, M_UART); free(sc->sc_rxbuf, M_UART); if (sc->sc_ires != NULL) { bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); } bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); mtx_destroy(&sc->sc_hwmtx_s); return (error); } int uart_bus_detach(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); sc->sc_leaving = 1; if (sc->sc_sysdev != NULL) sc->sc_sysdev->hwmtx = NULL; UART_DETACH(sc); if (sc->sc_sysdev != NULL && sc->sc_sysdev->detach != NULL) (*sc->sc_sysdev->detach)(sc); else uart_tty_detach(sc); free(sc->sc_txbuf, M_UART); free(sc->sc_rxbuf, M_UART); if (sc->sc_ires != NULL) { bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); } bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); mtx_destroy(&sc->sc_hwmtx_s); if (sc->sc_class->size > device_get_driver(dev)->size) { device_set_softc(dev, NULL); free(sc, M_UART); } return (0); } int uart_bus_resume(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); return (UART_ATTACH(sc)); } void uart_grab(struct uart_devinfo *di) { if (di->sc) UART_GRAB(di->sc); } void uart_ungrab(struct uart_devinfo *di) { if (di->sc) UART_UNGRAB(di->sc); } Index: head/sys/dev/uart/uart_cpu_acpi.h =================================================================== --- head/sys/dev/uart/uart_cpu_acpi.h (revision 338073) +++ head/sys/dev/uart/uart_cpu_acpi.h (revision 338074) @@ -1,63 +1,69 @@ /*- * Copyright (c) 2015 Michal Meloun * Copyright (c) 2016 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_UART_CPU_ACPI_H_ #define _DEV_UART_CPU_ACPI_H_ #include struct uart_class; struct acpi_uart_compat_data { - const char *hid; - struct uart_class *clas; - uint16_t port_subtype; + const char *cd_hid; + struct uart_class *cd_class; + + uint16_t cd_port_subtype; + int cd_regshft; + int cd_regiowidth; + int cd_rclk; + int cd_quirks; + const char *cd_desc; }; /* * If your UART driver implements only uart_class and uses uart_cpu_acpi.c * for device instantiation, then use UART_ACPI_CLASS_AND_DEVICE for its * declaration */ SET_DECLARE(uart_acpi_class_and_device_set, struct acpi_uart_compat_data); #define UART_ACPI_CLASS_AND_DEVICE(data) \ DATA_SET(uart_acpi_class_and_device_set, data) /* * If your UART driver implements uart_class and custom device layer, * then use UART_ACPI_CLASS for its declaration */ SET_DECLARE(uart_acpi_class_set, struct acpi_uart_compat_data); #define UART_ACPI_CLASS(data) \ DATA_SET(uart_acpi_class_set, data) #endif /* _DEV_UART_CPU_ACPI_H_ */ Index: head/sys/dev/uart/uart_cpu_arm64.c =================================================================== --- head/sys/dev/uart/uart_cpu_arm64.c (revision 338073) +++ head/sys/dev/uart/uart_cpu_arm64.c (revision 338074) @@ -1,211 +1,211 @@ /*- * Copyright (c) 2016 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_acpi.h" #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #ifdef DEV_ACPI #include #include #include #include #endif #ifdef FDT #include #include #include #include #endif /* * UART console routines. */ bus_space_tag_t uart_bus_space_io; bus_space_tag_t uart_bus_space_mem; int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) { if (pmap_kextract(b1->bsh) == 0) return (0); if (pmap_kextract(b2->bsh) == 0) return (0); return ((pmap_kextract(b1->bsh) == pmap_kextract(b2->bsh)) ? 1 : 0); } #ifdef DEV_ACPI static struct acpi_uart_compat_data * uart_cpu_acpi_scan(uint8_t interface_type) { struct acpi_uart_compat_data **cd, *curcd; int i; SET_FOREACH(cd, uart_acpi_class_and_device_set) { curcd = *cd; - for (i = 0; curcd[i].hid != NULL; i++) { - if (curcd[i].port_subtype == interface_type) + for (i = 0; curcd[i].cd_hid != NULL; i++) { + if (curcd[i].cd_port_subtype == interface_type) return (&curcd[i]); } } SET_FOREACH(cd, uart_acpi_class_set) { curcd = *cd; - for (i = 0; curcd[i].hid != NULL; i++) { - if (curcd[i].port_subtype == interface_type) + for (i = 0; curcd[i].cd_hid != NULL; i++) { + if (curcd[i].cd_port_subtype == interface_type) return (&curcd[i]); } } return (NULL); } static int uart_cpu_acpi_probe(struct uart_class **classp, bus_space_tag_t *bst, bus_space_handle_t *bsh, int *baud, u_int *rclk, u_int *shiftp, u_int *iowidthp) { struct acpi_uart_compat_data *cd; ACPI_TABLE_SPCR *spcr; vm_paddr_t spcr_physaddr; int err; err = ENXIO; spcr_physaddr = acpi_find_table(ACPI_SIG_SPCR); if (spcr_physaddr == 0) return (ENXIO); spcr = acpi_map_table(spcr_physaddr, ACPI_SIG_SPCR); cd = uart_cpu_acpi_scan(spcr->InterfaceType); if (cd == NULL) goto out; switch(spcr->BaudRate) { case 3: *baud = 9600; break; case 4: *baud = 19200; break; case 6: *baud = 57600; break; case 7: *baud = 115200; break; default: goto out; } err = acpi_map_addr(&spcr->SerialPort, bst, bsh, PAGE_SIZE); if (err != 0) goto out; - *classp = cd->clas; + *classp = cd->cd_class; *rclk = 0; *shiftp = 2; *iowidthp = spcr->SerialPort.BitWidth / 8; out: acpi_unmap_table(spcr); return (err); } #endif int uart_cpu_getdev(int devtype, struct uart_devinfo *di) { struct uart_class *class; bus_space_handle_t bsh; bus_space_tag_t bst; u_int rclk, shift, iowidth; int br, err; /* Allow overriding the FDT using the environment. */ class = &uart_ns8250_class; err = uart_getenv(devtype, di, class); if (err == 0) return (0); if (devtype != UART_DEV_CONSOLE) return (ENXIO); err = ENXIO; #ifdef DEV_ACPI err = uart_cpu_acpi_probe(&class, &bst, &bsh, &br, &rclk, &shift, &iowidth); #endif #ifdef FDT if (err != 0) { err = uart_cpu_fdt_probe(&class, &bst, &bsh, &br, &rclk, &shift, &iowidth); } #endif if (err != 0) return (err); /* * Finalize configuration. */ di->bas.chan = 0; di->bas.regshft = shift; di->bas.regiowidth = iowidth; di->baudrate = br; di->bas.rclk = rclk; di->ops = uart_getops(class); di->databits = 8; di->stopbits = 1; di->parity = UART_PARITY_NONE; di->bas.bst = bst; di->bas.bsh = bsh; uart_bus_space_mem = di->bas.bst; uart_bus_space_io = NULL; return (0); } Index: head/sys/dev/uart/uart_dev_ns8250.c =================================================================== --- head/sys/dev/uart/uart_dev_ns8250.c (revision 338073) +++ head/sys/dev/uart/uart_dev_ns8250.c (revision 338074) @@ -1,1060 +1,1084 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "opt_acpi.h" #include "opt_platform.h" #include "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include #ifdef FDT #include #endif #include #include #include +#ifdef DEV_ACPI +#include +#endif #include #include "uart_if.h" #define DEFAULT_RCLK 1843200 /* * Set the default baudrate tolerance to 3.0%. * * Some embedded boards have odd reference clocks (eg 25MHz) * and we need to handle higher variances in the target baud rate. */ #ifndef UART_DEV_TOLERANCE_PCT #define UART_DEV_TOLERANCE_PCT 30 #endif /* UART_DEV_TOLERANCE_PCT */ static int broken_txfifo = 0; SYSCTL_INT(_hw, OID_AUTO, broken_txfifo, CTLFLAG_RWTUN, &broken_txfifo, 0, "UART FIFO has QEMU emulation bug"); /* * Clear pending interrupts. THRE is cleared by reading IIR. Data * that may have been received gets lost here. */ static void ns8250_clrint(struct uart_bas *bas) { uint8_t iir, lsr; iir = uart_getreg(bas, REG_IIR); while ((iir & IIR_NOPEND) == 0) { iir &= IIR_IMASK; if (iir == IIR_RLS) { lsr = uart_getreg(bas, REG_LSR); if (lsr & (LSR_BI|LSR_FE|LSR_PE)) (void)uart_getreg(bas, REG_DATA); } else if (iir == IIR_RXRDY || iir == IIR_RXTOUT) (void)uart_getreg(bas, REG_DATA); else if (iir == IIR_MLSC) (void)uart_getreg(bas, REG_MSR); uart_barrier(bas); iir = uart_getreg(bas, REG_IIR); } } static int ns8250_delay(struct uart_bas *bas) { int divisor; u_char lcr; lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); /* 1/10th the time to transmit 1 character (estimate). */ if (divisor <= 134) return (16000000 * divisor / bas->rclk); return (16000 * divisor / (bas->rclk / 1000)); } static int ns8250_divisor(int rclk, int baudrate) { int actual_baud, divisor; int error; if (baudrate == 0) return (0); divisor = (rclk / (baudrate << 3) + 1) >> 1; if (divisor == 0 || divisor >= 65536) return (0); actual_baud = rclk / (divisor << 4); /* 10 times error in percent: */ error = ((actual_baud - baudrate) * 2000 / baudrate + 1) >> 1; /* enforce maximum error tolerance: */ if (error < -UART_DEV_TOLERANCE_PCT || error > UART_DEV_TOLERANCE_PCT) return (0); return (divisor); } static int ns8250_drain(struct uart_bas *bas, int what) { int delay, limit; delay = ns8250_delay(bas); if (what & UART_DRAIN_TRANSMITTER) { /* * Pick an arbitrary high limit to avoid getting stuck in * an infinite loop when the hardware is broken. Make the * limit high enough to handle large FIFOs. */ limit = 10*1024; while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) DELAY(delay); if (limit == 0) { /* printf("ns8250: transmitter appears stuck... "); */ return (EIO); } } if (what & UART_DRAIN_RECEIVER) { /* * Pick an arbitrary high limit to avoid getting stuck in * an infinite loop when the hardware is broken. Make the * limit high enough to handle large FIFOs and integrated * UARTs. The HP rx2600 for example has 3 UARTs on the * management board that tend to get a lot of data send * to it when the UART is first activated. */ limit=10*4096; while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit) { (void)uart_getreg(bas, REG_DATA); uart_barrier(bas); DELAY(delay << 2); } if (limit == 0) { /* printf("ns8250: receiver appears broken... "); */ return (EIO); } } return (0); } /* * We can only flush UARTs with FIFOs. UARTs without FIFOs should be * drained. WARNING: this function clobbers the FIFO setting! */ static void ns8250_flush(struct uart_bas *bas, int what) { uint8_t fcr; fcr = FCR_ENABLE; #ifdef CPU_XBURST fcr |= FCR_UART_ON; #endif if (what & UART_FLUSH_TRANSMITTER) fcr |= FCR_XMT_RST; if (what & UART_FLUSH_RECEIVER) fcr |= FCR_RCV_RST; uart_setreg(bas, REG_FCR, fcr); uart_barrier(bas); } static int ns8250_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int divisor; uint8_t lcr; lcr = 0; if (databits >= 8) lcr |= LCR_8BITS; else if (databits == 7) lcr |= LCR_7BITS; else if (databits == 6) lcr |= LCR_6BITS; else lcr |= LCR_5BITS; if (stopbits > 1) lcr |= LCR_STOPB; lcr |= parity << 3; /* Set baudrate. */ if (baudrate > 0) { divisor = ns8250_divisor(bas->rclk, baudrate); if (divisor == 0) return (EINVAL); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); uart_setreg(bas, REG_DLL, divisor & 0xff); uart_setreg(bas, REG_DLH, (divisor >> 8) & 0xff); uart_barrier(bas); } /* Set LCR and clear DLAB. */ uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); return (0); } /* * Low-level UART interface. */ static int ns8250_probe(struct uart_bas *bas); static void ns8250_init(struct uart_bas *bas, int, int, int, int); static void ns8250_term(struct uart_bas *bas); static void ns8250_putc(struct uart_bas *bas, int); static int ns8250_rxready(struct uart_bas *bas); static int ns8250_getc(struct uart_bas *bas, struct mtx *); struct uart_ops uart_ns8250_ops = { .probe = ns8250_probe, .init = ns8250_init, .term = ns8250_term, .putc = ns8250_putc, .rxready = ns8250_rxready, .getc = ns8250_getc, }; static int ns8250_probe(struct uart_bas *bas) { u_char val; #ifdef CPU_XBURST uart_setreg(bas, REG_FCR, FCR_UART_ON); #endif /* Check known 0 bits that don't depend on DLAB. */ val = uart_getreg(bas, REG_IIR); if (val & 0x30) return (ENXIO); /* * Bit 6 of the MCR (= 0x40) appears to be 1 for the Sun1699 * chip, but otherwise doesn't seem to have a function. In * other words, uart(4) works regardless. Ignore that bit so * the probe succeeds. */ val = uart_getreg(bas, REG_MCR); if (val & 0xa0) return (ENXIO); return (0); } static void ns8250_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { u_char ier, val; if (bas->rclk == 0) bas->rclk = DEFAULT_RCLK; ns8250_param(bas, baudrate, databits, stopbits, parity); /* Disable all interrupt sources. */ /* * We use 0xe0 instead of 0xf0 as the mask because the XScale PXA * UARTs split the receive time-out interrupt bit out separately as * 0x10. This gets handled by ier_mask and ier_rxbits below. */ ier = uart_getreg(bas, REG_IER) & 0xe0; uart_setreg(bas, REG_IER, ier); uart_barrier(bas); /* Disable the FIFO (if present). */ val = 0; #ifdef CPU_XBURST val |= FCR_UART_ON; #endif uart_setreg(bas, REG_FCR, val); uart_barrier(bas); /* Set RTS & DTR. */ uart_setreg(bas, REG_MCR, MCR_IE | MCR_RTS | MCR_DTR); uart_barrier(bas); ns8250_clrint(bas); } static void ns8250_term(struct uart_bas *bas) { /* Clear RTS & DTR. */ uart_setreg(bas, REG_MCR, MCR_IE); uart_barrier(bas); } static void ns8250_putc(struct uart_bas *bas, int c) { int limit; limit = 250000; while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0 && --limit) DELAY(4); uart_setreg(bas, REG_DATA, c); uart_barrier(bas); } static int ns8250_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) != 0 ? 1 : 0); } static int ns8250_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) == 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } c = uart_getreg(bas, REG_DATA); uart_unlock(hwmtx); return (c); } static kobj_method_t ns8250_methods[] = { KOBJMETHOD(uart_attach, ns8250_bus_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_probe, ns8250_bus_probe), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD(uart_grab, ns8250_bus_grab), KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), { 0, 0 } }; struct uart_class uart_ns8250_class = { "ns8250", ns8250_methods, sizeof(struct ns8250_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, .uc_rclk = DEFAULT_RCLK, .uc_rshift = 0 }; + +/* + * XXX -- refactor out ACPI and FDT ifdefs + */ +#ifdef DEV_ACPI +static struct acpi_uart_compat_data acpi_compat_data[] = { + {"AMD0020", &uart_ns8250_class, 0, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, + {"AMDI0020", &uart_ns8250_class, 0, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, + {"PNP0500", &uart_ns8250_class, 0, 0, 0, 0, 0, "Standard PC COM port"}, + {"PNP0501", &uart_ns8250_class, 0, 0, 0, 0, 0, "16550A-compatible COM port"}, + {"PNP0502", &uart_ns8250_class, 0, 0, 0, 0, 0, "Multiport serial device (non-intelligent 16550)"}, + {"PNP0510", &uart_ns8250_class, 0, 0, 0, 0, 0, "Generic IRDA-compatible device"}, + {"PNP0511", &uart_ns8250_class, 0, 0, 0, 0, 0, "Generic IRDA-compatible device"}, + {"WACF004", &uart_ns8250_class, 0, 0, 0, 0, 0, "Wacom Tablet PC Screen"}, + {"WACF00E", &uart_ns8250_class, 0, 0, 0, 0, 0, "Wacom Tablet PC Screen 00e"}, + {"FUJ02E5", &uart_ns8250_class, 0, 0, 0, 0, 0, "Wacom Tablet at FuS Lifebook T"}, + {NULL, NULL, 0, 0 , 0, 0, 0, NULL}, +}; +UART_ACPI_CLASS_AND_DEVICE(acpi_compat_data); +#endif #ifdef FDT static struct ofw_compat_data compat_data[] = { {"ns16550", (uintptr_t)&uart_ns8250_class}, {"ns16550a", (uintptr_t)&uart_ns8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); #endif /* Use token-pasting to form SER_ and MSR_ named constants. */ #define SER(sig) SER_##sig #define SERD(sig) SER_D##sig #define MSR(sig) MSR_##sig #define MSRD(sig) MSR_D##sig /* * Detect signal changes using software delta detection. The previous state of * the signals is in 'var' the new hardware state is in 'msr', and 'sig' is the * short name (DCD, CTS, etc) of the signal bit being processed; 'var' gets the * new state of both the signal and the delta bits. */ #define SIGCHGSW(var, msr, sig) \ if ((msr) & MSR(sig)) { \ if ((var & SER(sig)) == 0) \ var |= SERD(sig) | SER(sig); \ } else { \ if ((var & SER(sig)) != 0) \ var = SERD(sig) | (var & ~SER(sig)); \ } /* * Detect signal changes using the hardware msr delta bits. This is currently * used only when PPS timing information is being captured using the "narrow * pulse" option. With a narrow PPS pulse the signal may not still be asserted * by time the interrupt handler is invoked. The hardware will latch the fact * that it changed in the delta bits. */ #define SIGCHGHW(var, msr, sig) \ if ((msr) & MSRD(sig)) { \ if (((msr) & MSR(sig)) != 0) \ var |= SERD(sig) | SER(sig); \ else \ var = SERD(sig) | (var & ~SER(sig)); \ } int ns8250_bus_attach(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas; unsigned int ivar; #ifdef FDT phandle_t node; pcell_t cell; #endif #ifdef FDT /* Check whether uart has a broken txfifo. */ node = ofw_bus_get_node(sc->sc_dev); if ((OF_getencprop(node, "broken-txfifo", &cell, sizeof(cell))) > 0) broken_txfifo = cell ? 1 : 0; #endif bas = &sc->sc_bas; ns8250->busy_detect = bas->busy_detect; ns8250->mcr = uart_getreg(bas, REG_MCR); ns8250->fcr = FCR_ENABLE; #ifdef CPU_XBURST ns8250->fcr |= FCR_UART_ON; #endif if (!resource_int_value("uart", device_get_unit(sc->sc_dev), "flags", &ivar)) { if (UART_FLAGS_FCR_RX_LOW(ivar)) ns8250->fcr |= FCR_RX_LOW; else if (UART_FLAGS_FCR_RX_MEDL(ivar)) ns8250->fcr |= FCR_RX_MEDL; else if (UART_FLAGS_FCR_RX_HIGH(ivar)) ns8250->fcr |= FCR_RX_HIGH; else ns8250->fcr |= FCR_RX_MEDH; } else ns8250->fcr |= FCR_RX_MEDH; /* Get IER mask */ ivar = 0xf0; resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_mask", &ivar); ns8250->ier_mask = (uint8_t)(ivar & 0xff); /* Get IER RX interrupt bits */ ivar = IER_EMSC | IER_ERLS | IER_ERXRDY; resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_rxbits", &ivar); ns8250->ier_rxbits = (uint8_t)(ivar & 0xff); uart_setreg(bas, REG_FCR, ns8250->fcr); uart_barrier(bas); ns8250_bus_flush(sc, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); if (ns8250->mcr & MCR_DTR) sc->sc_hwsig |= SER_DTR; if (ns8250->mcr & MCR_RTS) sc->sc_hwsig |= SER_RTS; ns8250_bus_getsig(sc); ns8250_clrint(bas); ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; ns8250->ier |= ns8250->ier_rxbits; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); /* * Timing of the H/W access was changed with r253161 of uart_core.c * It has been observed that an ITE IT8513E would signal a break * condition with pretty much every character it received, unless * it had enough time to settle between ns8250_bus_attach() and * ns8250_bus_ipend() -- which it accidentally had before r253161. * It's not understood why the UART chip behaves this way and it * could very well be that the DELAY make the H/W work in the same * accidental manner as before. More analysis is warranted, but * at least now we fixed a known regression. */ DELAY(200); return (0); } int ns8250_bus_detach(struct uart_softc *sc) { struct ns8250_softc *ns8250; struct uart_bas *bas; u_char ier; ns8250 = (struct ns8250_softc *)sc; bas = &sc->sc_bas; ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; uart_setreg(bas, REG_IER, ier); uart_barrier(bas); ns8250_clrint(bas); return (0); } int ns8250_bus_flush(struct uart_softc *sc, int what) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); if (sc->sc_rxfifosz > 1) { ns8250_flush(bas, what); uart_setreg(bas, REG_FCR, ns8250->fcr); uart_barrier(bas); error = 0; } else error = ns8250_drain(bas, what); uart_unlock(sc->sc_hwmtx); return (error); } int ns8250_bus_getsig(struct uart_softc *sc) { uint32_t old, sig; uint8_t msr; /* * The delta bits are reputed to be broken on some hardware, so use * software delta detection by default. Use the hardware delta bits * when capturing PPS pulses which are too narrow for software detection * to see the edges. Hardware delta for RI doesn't work like the * others, so always use software for it. Other threads may be changing * other (non-MSR) bits in sc_hwsig, so loop until it can successfully * update without other changes happening. Note that the SIGCHGxx() * macros carefully preserve the delta bits when we have to loop several * times and a signal transitions between iterations. */ do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); msr = uart_getreg(&sc->sc_bas, REG_MSR); uart_unlock(sc->sc_hwmtx); if (sc->sc_pps_mode & UART_PPS_NARROW_PULSE) { SIGCHGHW(sig, msr, DSR); SIGCHGHW(sig, msr, CTS); SIGCHGHW(sig, msr, DCD); } else { SIGCHGSW(sig, msr, DSR); SIGCHGSW(sig, msr, CTS); SIGCHGSW(sig, msr, DCD); } SIGCHGSW(sig, msr, RI); } while (!atomic_cmpset_32(&sc->sc_hwsig, old, sig & ~SER_MASK_DELTA)); return (sig); } int ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; uint8_t efr, lcr; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: lcr = uart_getreg(bas, REG_LCR); if (data) lcr |= LCR_SBREAK; else lcr &= ~LCR_SBREAK; uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_IFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_RTS; else efr &= ~EFR_RTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_OFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_CTS; else efr &= ~EFR_CTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_BAUD: lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); baudrate = (divisor > 0) ? bas->rclk / divisor / 16 : 0; if (baudrate > 0) *(int*)data = baudrate; else error = ENXIO; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } int ns8250_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; struct ns8250_softc *ns8250; int ipend; uint8_t iir, lsr; ns8250 = (struct ns8250_softc *)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); iir = uart_getreg(bas, REG_IIR); if (ns8250->busy_detect && (iir & IIR_BUSY) == IIR_BUSY) { (void)uart_getreg(bas, DW_REG_USR); uart_unlock(sc->sc_hwmtx); return (0); } if (iir & IIR_NOPEND) { uart_unlock(sc->sc_hwmtx); return (0); } ipend = 0; if (iir & IIR_RXRDY) { lsr = uart_getreg(bas, REG_LSR); if (lsr & LSR_OE) ipend |= SER_INT_OVERRUN; if (lsr & LSR_BI) ipend |= SER_INT_BREAK; if (lsr & LSR_RXRDY) ipend |= SER_INT_RXREADY; } else { if (iir & IIR_TXRDY) { ipend |= SER_INT_TXIDLE; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); } else ipend |= SER_INT_SIGCHG; } if (ipend == 0) ns8250_clrint(bas); uart_unlock(sc->sc_hwmtx); return (ipend); } int ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct ns8250_softc *ns8250; struct uart_bas *bas; int error, limit; ns8250 = (struct ns8250_softc*)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* * When using DW UART with BUSY detection it is necessary to wait * until all serial transfers are finished before manipulating the * line control. LCR will not be affected when UART is busy. */ if (ns8250->busy_detect != 0) { /* * Pick an arbitrary high limit to avoid getting stuck in * an infinite loop in case when the hardware is broken. */ limit = 10 * 1024; while (((uart_getreg(bas, DW_REG_USR) & USR_BUSY) != 0) && --limit) DELAY(4); if (limit <= 0) { /* UART appears to be stuck */ uart_unlock(sc->sc_hwmtx); return (EIO); } } error = ns8250_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } int ns8250_bus_probe(struct uart_softc *sc) { struct ns8250_softc *ns8250; struct uart_bas *bas; int count, delay, error, limit; uint8_t lsr, mcr, ier; uint8_t val; ns8250 = (struct ns8250_softc *)sc; bas = &sc->sc_bas; error = ns8250_probe(bas); if (error) return (error); mcr = MCR_IE; if (sc->sc_sysdev == NULL) { /* By using ns8250_init() we also set DTR and RTS. */ ns8250_init(bas, 115200, 8, 1, UART_PARITY_NONE); } else mcr |= MCR_DTR | MCR_RTS; error = ns8250_drain(bas, UART_DRAIN_TRANSMITTER); if (error) return (error); /* * Set loopback mode. This avoids having garbage on the wire and * also allows us send and receive data. We set DTR and RTS to * avoid the possibility that automatic flow-control prevents * any data from being sent. */ uart_setreg(bas, REG_MCR, MCR_LOOPBACK | MCR_IE | MCR_DTR | MCR_RTS); uart_barrier(bas); /* * Enable FIFOs. And check that the UART has them. If not, we're * done. Since this is the first time we enable the FIFOs, we reset * them. */ val = FCR_ENABLE; #ifdef CPU_XBURST val |= FCR_UART_ON; #endif uart_setreg(bas, REG_FCR, val); uart_barrier(bas); if (!(uart_getreg(bas, REG_IIR) & IIR_FIFO_MASK)) { /* * NS16450 or INS8250. We don't bother to differentiate * between them. They're too old to be interesting. */ uart_setreg(bas, REG_MCR, mcr); uart_barrier(bas); sc->sc_rxfifosz = sc->sc_txfifosz = 1; device_set_desc(sc->sc_dev, "8250 or 16450 or compatible"); return (0); } val = FCR_ENABLE | FCR_XMT_RST | FCR_RCV_RST; #ifdef CPU_XBURST val |= FCR_UART_ON; #endif uart_setreg(bas, REG_FCR, val); uart_barrier(bas); count = 0; delay = ns8250_delay(bas); /* We have FIFOs. Drain the transmitter and receiver. */ error = ns8250_drain(bas, UART_DRAIN_RECEIVER|UART_DRAIN_TRANSMITTER); if (error) { uart_setreg(bas, REG_MCR, mcr); val = 0; #ifdef CPU_XBURST val |= FCR_UART_ON; #endif uart_setreg(bas, REG_FCR, val); uart_barrier(bas); goto describe; } /* * We should have a sufficiently clean "pipe" to determine the * size of the FIFOs. We send as much characters as is reasonable * and wait for the overflow bit in the LSR register to be * asserted, counting the characters as we send them. Based on * that count we know the FIFO size. */ do { uart_setreg(bas, REG_DATA, 0); uart_barrier(bas); count++; limit = 30; lsr = 0; /* * LSR bits are cleared upon read, so we must accumulate * them to be able to test LSR_OE below. */ while (((lsr |= uart_getreg(bas, REG_LSR)) & LSR_TEMT) == 0 && --limit) DELAY(delay); if (limit == 0) { ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; uart_setreg(bas, REG_IER, ier); uart_setreg(bas, REG_MCR, mcr); val = 0; #ifdef CPU_XBURST val |= FCR_UART_ON; #endif uart_setreg(bas, REG_FCR, val); uart_barrier(bas); count = 0; goto describe; } } while ((lsr & LSR_OE) == 0 && count < 260); count--; uart_setreg(bas, REG_MCR, mcr); /* Reset FIFOs. */ ns8250_flush(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); describe: if (count >= 14 && count <= 16) { sc->sc_rxfifosz = 16; device_set_desc(sc->sc_dev, "16550 or compatible"); } else if (count >= 28 && count <= 32) { sc->sc_rxfifosz = 32; device_set_desc(sc->sc_dev, "16650 or compatible"); } else if (count >= 56 && count <= 64) { sc->sc_rxfifosz = 64; device_set_desc(sc->sc_dev, "16750 or compatible"); } else if (count >= 112 && count <= 128) { sc->sc_rxfifosz = 128; device_set_desc(sc->sc_dev, "16950 or compatible"); } else if (count >= 224 && count <= 256) { sc->sc_rxfifosz = 256; device_set_desc(sc->sc_dev, "16x50 with 256 byte FIFO"); } else { sc->sc_rxfifosz = 16; device_set_desc(sc->sc_dev, "Non-standard ns8250 class UART with FIFOs"); } /* * Force the Tx FIFO size to 16 bytes for now. We don't program the * Tx trigger. Also, we assume that all data has been sent when the * interrupt happens. */ sc->sc_txfifosz = 16; #if 0 /* * XXX there are some issues related to hardware flow control and * it's likely that uart(4) is the cause. This basically needs more * investigation, but we avoid using for hardware flow control * until then. */ /* 16650s or higher have automatic flow control. */ if (sc->sc_rxfifosz > 16) { sc->sc_hwiflow = 1; sc->sc_hwoflow = 1; } #endif return (0); } int ns8250_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc; uint8_t lsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); lsr = uart_getreg(bas, REG_LSR); while (lsr & LSR_RXRDY) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = uart_getreg(bas, REG_DATA); if (lsr & LSR_FE) xc |= UART_STAT_FRAMERR; if (lsr & LSR_PE) xc |= UART_STAT_PARERR; uart_rx_put(sc, xc); lsr = uart_getreg(bas, REG_LSR); } /* Discard everything left in the Rx FIFO. */ while (lsr & LSR_RXRDY) { (void)uart_getreg(bas, REG_DATA); uart_barrier(bas); lsr = uart_getreg(bas, REG_LSR); } uart_unlock(sc->sc_hwmtx); return (0); } int ns8250_bus_setsig(struct uart_softc *sc, int sig) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { new = (new & ~SER_DTR) | (sig & (SER_DTR | SER_DDTR)); } if (sig & SER_DRTS) { new = (new & ~SER_RTS) | (sig & (SER_RTS | SER_DRTS)); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); ns8250->mcr &= ~(MCR_DTR|MCR_RTS); if (new & SER_DTR) ns8250->mcr |= MCR_DTR; if (new & SER_RTS) ns8250->mcr |= MCR_RTS; uart_setreg(bas, REG_MCR, ns8250->mcr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } int ns8250_bus_transmit(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas; int i; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); if (sc->sc_txdatasz > 1) { if ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0) ns8250_drain(bas, UART_DRAIN_TRANSMITTER); } else { while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0) DELAY(4); } for (i = 0; i < sc->sc_txdatasz; i++) { uart_setreg(bas, REG_DATA, sc->sc_txbuf[i]); uart_barrier(bas); } uart_setreg(bas, REG_IER, ns8250->ier | IER_ETXRDY); uart_barrier(bas); if (broken_txfifo) ns8250_drain(bas, UART_DRAIN_TRANSMITTER); else sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); if (broken_txfifo) uart_sched_softih(sc, SER_INT_TXIDLE); return (0); } void ns8250_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; u_char ier; /* * turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. * All pending interrupt signals are reset when IER is set to 0. */ uart_lock(sc->sc_hwmtx); ier = uart_getreg(bas, REG_IER); uart_setreg(bas, REG_IER, ier & ns8250->ier_mask); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } void ns8250_bus_ungrab(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_pl011.c =================================================================== --- head/sys/dev/uart/uart_dev_pl011.c (revision 338073) +++ head/sys/dev/uart/uart_dev_pl011.c (revision 338074) @@ -1,613 +1,613 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_acpi.h" #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #ifdef FDT #include #include #endif #include #include "uart_if.h" #ifdef DEV_ACPI #include #include #include #include #endif #include #ifdef __aarch64__ #define IS_FDT (arm64_bus_method == ARM64_BUS_FDT) #elif defined(FDT) #define IS_FDT 1 #else #error Unsupported configuration #endif /* PL011 UART registers and masks*/ #define UART_DR 0x00 /* Data register */ #define DR_FE (1 << 8) /* Framing error */ #define DR_PE (1 << 9) /* Parity error */ #define DR_BE (1 << 10) /* Break error */ #define DR_OE (1 << 11) /* Overrun error */ #define UART_FR 0x06 /* Flag register */ #define FR_RXFE (1 << 4) /* Receive FIFO/reg empty */ #define FR_TXFF (1 << 5) /* Transmit FIFO/reg full */ #define FR_RXFF (1 << 6) /* Receive FIFO/reg full */ #define FR_TXFE (1 << 7) /* Transmit FIFO/reg empty */ #define UART_IBRD 0x09 /* Integer baud rate register */ #define IBRD_BDIVINT 0xffff /* Significant part of int. divisor value */ #define UART_FBRD 0x0a /* Fractional baud rate register */ #define FBRD_BDIVFRAC 0x3f /* Significant part of frac. divisor value */ #define UART_LCR_H 0x0b /* Line control register */ #define LCR_H_WLEN8 (0x3 << 5) #define LCR_H_WLEN7 (0x2 << 5) #define LCR_H_WLEN6 (0x1 << 5) #define LCR_H_FEN (1 << 4) /* FIFO mode enable */ #define LCR_H_STP2 (1 << 3) /* 2 stop frames at the end */ #define LCR_H_EPS (1 << 2) /* Even parity select */ #define LCR_H_PEN (1 << 1) /* Parity enable */ #define UART_CR 0x0c /* Control register */ #define CR_RXE (1 << 9) /* Receive enable */ #define CR_TXE (1 << 8) /* Transmit enable */ #define CR_UARTEN (1 << 0) /* UART enable */ #define UART_IFLS 0x0d /* FIFO level select register */ #define IFLS_RX_SHIFT 3 /* RX level in bits [5:3] */ #define IFLS_TX_SHIFT 0 /* TX level in bits [2:0] */ #define IFLS_MASK 0x07 /* RX/TX level is 3 bits */ #define IFLS_LVL_1_8th 0 /* Interrupt at 1/8 full */ #define IFLS_LVL_2_8th 1 /* Interrupt at 1/4 full */ #define IFLS_LVL_4_8th 2 /* Interrupt at 1/2 full */ #define IFLS_LVL_6_8th 3 /* Interrupt at 3/4 full */ #define IFLS_LVL_7_8th 4 /* Interrupt at 7/8 full */ #define UART_IMSC 0x0e /* Interrupt mask set/clear register */ #define IMSC_MASK_ALL 0x7ff /* Mask all interrupts */ #define UART_RIS 0x0f /* Raw interrupt status register */ #define UART_RXREADY (1 << 4) /* RX buffer full */ #define UART_TXEMPTY (1 << 5) /* TX buffer empty */ #define RIS_RTIM (1 << 6) /* Receive timeout */ #define RIS_FE (1 << 7) /* Framing error interrupt status */ #define RIS_PE (1 << 8) /* Parity error interrupt status */ #define RIS_BE (1 << 9) /* Break error interrupt status */ #define RIS_OE (1 << 10) /* Overrun interrupt status */ #define UART_MIS 0x10 /* Masked interrupt status register */ #define UART_ICR 0x11 /* Interrupt clear register */ #define UART_PIDREG_0 0x3f8 /* Peripheral ID register 0 */ #define UART_PIDREG_1 0x3f9 /* Peripheral ID register 1 */ #define UART_PIDREG_2 0x3fa /* Peripheral ID register 2 */ #define UART_PIDREG_3 0x3fb /* Peripheral ID register 3 */ /* * The hardware FIFOs are 16 bytes each on rev 2 and earlier hardware, 32 bytes * on rev 3 and later. We configure them to interrupt when 3/4 full/empty. For * RX we set the size to the full hardware capacity so that the uart core * allocates enough buffer space to hold a complete fifo full of incoming data. * For TX, we need to limit the size to the capacity we know will be available * when the interrupt occurs; uart_core will feed exactly that many bytes to * uart_pl011_bus_transmit() which must consume them all. */ #define FIFO_RX_SIZE_R2 16 #define FIFO_TX_SIZE_R2 12 #define FIFO_RX_SIZE_R3 32 #define FIFO_TX_SIZE_R3 24 #define FIFO_IFLS_BITS ((IFLS_LVL_6_8th << IFLS_RX_SHIFT) | (IFLS_LVL_2_8th)) /* * FIXME: actual register size is SoC-dependent, we need to handle it */ #define __uart_getreg(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) #define __uart_setreg(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value) /* * Low-level UART interface. */ static int uart_pl011_probe(struct uart_bas *bas); static void uart_pl011_init(struct uart_bas *bas, int, int, int, int); static void uart_pl011_term(struct uart_bas *bas); static void uart_pl011_putc(struct uart_bas *bas, int); static int uart_pl011_rxready(struct uart_bas *bas); static int uart_pl011_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_pl011_ops = { .probe = uart_pl011_probe, .init = uart_pl011_init, .term = uart_pl011_term, .putc = uart_pl011_putc, .rxready = uart_pl011_rxready, .getc = uart_pl011_getc, }; static int uart_pl011_probe(struct uart_bas *bas) { return (0); } static void uart_pl011_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t ctrl, line; uint32_t baud; /* * Zero all settings to make sure * UART is disabled and not configured */ ctrl = line = 0x0; __uart_setreg(bas, UART_CR, ctrl); /* As we know UART is disabled we may setup the line */ switch (databits) { case 7: line |= LCR_H_WLEN7; break; case 6: line |= LCR_H_WLEN6; break; case 8: default: line |= LCR_H_WLEN8; break; } if (stopbits == 2) line |= LCR_H_STP2; else line &= ~LCR_H_STP2; if (parity) line |= LCR_H_PEN; else line &= ~LCR_H_PEN; line |= LCR_H_FEN; /* Configure the rest */ ctrl |= (CR_RXE | CR_TXE | CR_UARTEN); if (bas->rclk != 0 && baudrate != 0) { baud = bas->rclk * 4 / baudrate; __uart_setreg(bas, UART_IBRD, ((uint32_t)(baud >> 6)) & IBRD_BDIVINT); __uart_setreg(bas, UART_FBRD, (uint32_t)(baud & 0x3F) & FBRD_BDIVFRAC); } /* Add config. to line before reenabling UART */ __uart_setreg(bas, UART_LCR_H, (__uart_getreg(bas, UART_LCR_H) & ~0xff) | line); /* Set rx and tx fifo levels. */ __uart_setreg(bas, UART_IFLS, FIFO_IFLS_BITS); __uart_setreg(bas, UART_CR, ctrl); } static void uart_pl011_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { /* Mask all interrupts */ __uart_setreg(bas, UART_IMSC, __uart_getreg(bas, UART_IMSC) & ~IMSC_MASK_ALL); uart_pl011_param(bas, baudrate, databits, stopbits, parity); } static void uart_pl011_term(struct uart_bas *bas) { } static void uart_pl011_putc(struct uart_bas *bas, int c) { /* Wait when TX FIFO full. Push character otherwise. */ while (__uart_getreg(bas, UART_FR) & FR_TXFF) ; __uart_setreg(bas, UART_DR, c & 0xff); } static int uart_pl011_rxready(struct uart_bas *bas) { return !(__uart_getreg(bas, UART_FR) & FR_RXFE); } static int uart_pl011_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; while (!uart_pl011_rxready(bas)) ; c = __uart_getreg(bas, UART_DR) & 0xff; return (c); } /* * High-level UART interface. */ struct uart_pl011_softc { struct uart_softc base; uint16_t imsc; /* Interrupt mask */ }; static int uart_pl011_bus_attach(struct uart_softc *); static int uart_pl011_bus_detach(struct uart_softc *); static int uart_pl011_bus_flush(struct uart_softc *, int); static int uart_pl011_bus_getsig(struct uart_softc *); static int uart_pl011_bus_ioctl(struct uart_softc *, int, intptr_t); static int uart_pl011_bus_ipend(struct uart_softc *); static int uart_pl011_bus_param(struct uart_softc *, int, int, int, int); static int uart_pl011_bus_probe(struct uart_softc *); static int uart_pl011_bus_receive(struct uart_softc *); static int uart_pl011_bus_setsig(struct uart_softc *, int); static int uart_pl011_bus_transmit(struct uart_softc *); static void uart_pl011_bus_grab(struct uart_softc *); static void uart_pl011_bus_ungrab(struct uart_softc *); static kobj_method_t uart_pl011_methods[] = { KOBJMETHOD(uart_attach, uart_pl011_bus_attach), KOBJMETHOD(uart_detach, uart_pl011_bus_detach), KOBJMETHOD(uart_flush, uart_pl011_bus_flush), KOBJMETHOD(uart_getsig, uart_pl011_bus_getsig), KOBJMETHOD(uart_ioctl, uart_pl011_bus_ioctl), KOBJMETHOD(uart_ipend, uart_pl011_bus_ipend), KOBJMETHOD(uart_param, uart_pl011_bus_param), KOBJMETHOD(uart_probe, uart_pl011_bus_probe), KOBJMETHOD(uart_receive, uart_pl011_bus_receive), KOBJMETHOD(uart_setsig, uart_pl011_bus_setsig), KOBJMETHOD(uart_transmit, uart_pl011_bus_transmit), KOBJMETHOD(uart_grab, uart_pl011_bus_grab), KOBJMETHOD(uart_ungrab, uart_pl011_bus_ungrab), { 0, 0 } }; static struct uart_class uart_pl011_class = { "uart_pl011", uart_pl011_methods, sizeof(struct uart_pl011_softc), .uc_ops = &uart_pl011_ops, .uc_range = 0x48, .uc_rclk = 0, .uc_rshift = 2 }; #ifdef FDT static struct ofw_compat_data fdt_compat_data[] = { {"arm,pl011", (uintptr_t)&uart_pl011_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(fdt_compat_data); #endif #ifdef DEV_ACPI static struct acpi_uart_compat_data acpi_compat_data[] = { - {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_PL011}, - {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_GENERIC}, - {NULL, NULL, 0}, + {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_PL011, 2, 0, 0, 0, "uart plo11"}, + {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_GENERIC, 2, 0, 0, 0, "uart plo11"}, + {NULL, NULL, 0, 0, 0, 0, 0, NULL}, }; UART_ACPI_CLASS_AND_DEVICE(acpi_compat_data); #endif static int uart_pl011_bus_attach(struct uart_softc *sc) { struct uart_pl011_softc *psc; struct uart_bas *bas; psc = (struct uart_pl011_softc *)sc; bas = &sc->sc_bas; /* Enable interrupts */ psc->imsc = (UART_RXREADY | RIS_RTIM | UART_TXEMPTY); __uart_setreg(bas, UART_IMSC, psc->imsc); /* Clear interrupts */ __uart_setreg(bas, UART_ICR, IMSC_MASK_ALL); return (0); } static int uart_pl011_bus_detach(struct uart_softc *sc) { return (0); } static int uart_pl011_bus_flush(struct uart_softc *sc, int what) { return (0); } static int uart_pl011_bus_getsig(struct uart_softc *sc) { return (0); } static int uart_pl011_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { int error; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: break; case UART_IOCTL_BAUD: *(int*)data = 115200; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int uart_pl011_bus_ipend(struct uart_softc *sc) { struct uart_pl011_softc *psc; struct uart_bas *bas; uint32_t ints; int ipend; psc = (struct uart_pl011_softc *)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); ints = __uart_getreg(bas, UART_MIS); ipend = 0; if (ints & (UART_RXREADY | RIS_RTIM)) ipend |= SER_INT_RXREADY; if (ints & RIS_BE) ipend |= SER_INT_BREAK; if (ints & RIS_OE) ipend |= SER_INT_OVERRUN; if (ints & UART_TXEMPTY) { if (sc->sc_txbusy) ipend |= SER_INT_TXIDLE; /* Disable TX interrupt */ __uart_setreg(bas, UART_IMSC, psc->imsc & ~UART_TXEMPTY); } uart_unlock(sc->sc_hwmtx); return (ipend); } static int uart_pl011_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { uart_lock(sc->sc_hwmtx); uart_pl011_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (0); } #ifdef FDT static int uart_pl011_bus_hwrev_fdt(struct uart_softc *sc) { pcell_t node; uint32_t periphid; /* * The FIFO sizes vary depending on hardware; rev 2 and below have 16 * byte FIFOs, rev 3 and up are 32 byte. The hardware rev is in the * primecell periphid register, but we get a bit of drama, as always, * with the bcm2835 (rpi), which claims to be rev 3, but has 16 byte * FIFOs. We check for both the old freebsd-historic and the proper * bindings-defined compatible strings for bcm2835, and also check the * workaround the linux drivers use for rpi3, which is to override the * primecell periphid register value with a property. */ if (ofw_bus_is_compatible(sc->sc_dev, "brcm,bcm2835-pl011") || ofw_bus_is_compatible(sc->sc_dev, "broadcom,bcm2835-uart")) { return (2); } else { node = ofw_bus_get_node(sc->sc_dev); if (OF_getencprop(node, "arm,primecell-periphid", &periphid, sizeof(periphid)) > 0) { return ((periphid >> 20) & 0x0f); } } return (-1); } #endif static int uart_pl011_bus_probe(struct uart_softc *sc) { int hwrev; hwrev = -1; #ifdef FDT if (IS_FDT) hwrev = uart_pl011_bus_hwrev_fdt(sc); #endif if (hwrev < 0) hwrev = __uart_getreg(&sc->sc_bas, UART_PIDREG_2) >> 4; if (hwrev <= 2) { sc->sc_rxfifosz = FIFO_RX_SIZE_R2; sc->sc_txfifosz = FIFO_TX_SIZE_R2; } else { sc->sc_rxfifosz = FIFO_RX_SIZE_R3; sc->sc_txfifosz = FIFO_TX_SIZE_R3; } device_set_desc(sc->sc_dev, "PrimeCell UART (PL011)"); return (0); } static int uart_pl011_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; uint32_t ints, xc; int rx; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); for (;;) { ints = __uart_getreg(bas, UART_FR); if (ints & FR_RXFE) break; if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = __uart_getreg(bas, UART_DR); rx = xc & 0xff; if (xc & DR_FE) rx |= UART_STAT_FRAMERR; if (xc & DR_PE) rx |= UART_STAT_PARERR; uart_rx_put(sc, rx); } uart_unlock(sc->sc_hwmtx); return (0); } static int uart_pl011_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int uart_pl011_bus_transmit(struct uart_softc *sc) { struct uart_pl011_softc *psc; struct uart_bas *bas; int i; psc = (struct uart_pl011_softc *)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); for (i = 0; i < sc->sc_txdatasz; i++) { __uart_setreg(bas, UART_DR, sc->sc_txbuf[i]); uart_barrier(bas); } /* Mark busy and enable TX interrupt */ sc->sc_txbusy = 1; __uart_setreg(bas, UART_IMSC, psc->imsc); uart_unlock(sc->sc_hwmtx); return (0); } static void uart_pl011_bus_grab(struct uart_softc *sc) { struct uart_pl011_softc *psc; struct uart_bas *bas; psc = (struct uart_pl011_softc *)sc; bas = &sc->sc_bas; /* Disable interrupts on switch to polling */ uart_lock(sc->sc_hwmtx); __uart_setreg(bas, UART_IMSC, psc->imsc & ~IMSC_MASK_ALL); uart_unlock(sc->sc_hwmtx); } static void uart_pl011_bus_ungrab(struct uart_softc *sc) { struct uart_pl011_softc *psc; struct uart_bas *bas; psc = (struct uart_pl011_softc *)sc; bas = &sc->sc_bas; /* Switch to using interrupts while not grabbed */ uart_lock(sc->sc_hwmtx); __uart_setreg(bas, UART_IMSC, psc->imsc); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_snps.c =================================================================== --- head/sys/dev/uart/uart_dev_snps.c (revision 338073) +++ head/sys/dev/uart/uart_dev_snps.c (revision 338074) @@ -1,321 +1,308 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EXT_RESOURCES #include #include #endif #include "uart_if.h" struct snps_softc { struct ns8250_softc ns8250; #ifdef EXT_RESOURCES clk_t baudclk; clk_t apb_pclk; hwreset_t reset; #endif }; /* * To use early printf on 64 bits Allwinner SoC, add to kernel config * options SOCDEV_PA=0x0 * options SOCDEV_VA=0x40000000 * options EARLY_PRINTF * * To use early printf on 32 bits Allwinner SoC, add to kernel config * options SOCDEV_PA=0x01C00000 * options SOCDEV_VA=0x10000000 * options EARLY_PRINTF * * remove the if 0 */ #if 0 #ifdef EARLY_PRINTF static void uart_snps_early_putc(int c) { volatile uint32_t *stat; volatile uint32_t *tx; #ifdef ALLWINNER_64 stat = (uint32_t *) (SOCDEV_VA + 0x1C2807C); tx = (uint32_t *) (SOCDEV_VA + 0x1C28000); #endif #ifdef ALLWINNER_32 stat = (uint32_t *) (SOCDEV_VA + 0x2807C); tx = (uint32_t *) (SOCDEV_VA + 0x28000); #endif while ((*stat & (1 << 2)) == 0) continue; *tx = c; } early_putc_t *early_putc = uart_snps_early_putc; #endif /* EARLY_PRINTF */ #endif -static int -snps_uart_attach(struct uart_softc *uart_sc) -{ - struct snps_softc *sc; - - sc = (struct snps_softc *)uart_sc; - - /* UART requires to read USR reg when IIR_BUSY */ - uart_sc->sc_bas.busy_detect = 1; - - return (ns8250_bus_attach(uart_sc)); -} - static kobj_method_t snps_methods[] = { KOBJMETHOD(uart_probe, ns8250_bus_probe), - KOBJMETHOD(uart_attach, snps_uart_attach), + KOBJMETHOD(uart_attach, ns8250_bus_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD(uart_grab, ns8250_bus_grab), KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), KOBJMETHOD_END }; struct uart_class uart_snps_class = { "snps", snps_methods, sizeof(struct snps_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, .uc_rclk = 0, }; static struct ofw_compat_data compat_data[] = { { "snps,dw-apb-uart", (uintptr_t)&uart_snps_class }, { NULL, (uintptr_t)NULL } }; UART_FDT_CLASS(compat_data); #ifdef EXT_RESOURCES static int snps_get_clocks(device_t dev, clk_t *baudclk, clk_t *apb_pclk) { *baudclk = NULL; *apb_pclk = NULL; /* Baud clock is either named "baudclk", or there is a single * unnamed clock. */ if (clk_get_by_ofw_name(dev, 0, "baudclk", baudclk) != 0 && clk_get_by_ofw_index(dev, 0, 0, baudclk) != 0) return (ENOENT); /* APB peripheral clock is optional */ (void)clk_get_by_ofw_name(dev, 0, "apb_pclk", apb_pclk); return (0); } #endif static int snps_probe(device_t dev) { struct snps_softc *sc; struct uart_class *uart_class; phandle_t node; uint32_t shift, iowidth, clock; uint64_t freq; int error; #ifdef EXT_RESOURCES clk_t baudclk, apb_pclk; hwreset_t reset; #endif if (!ofw_bus_status_okay(dev)) return (ENXIO); uart_class = (struct uart_class *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (uart_class == NULL) return (ENXIO); freq = 0; sc = device_get_softc(dev); sc->ns8250.base.sc_class = uart_class; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "reg-shift", &shift, sizeof(shift)) <= 0) shift = 0; if (OF_getencprop(node, "reg-io-width", &iowidth, sizeof(iowidth)) <= 0) iowidth = 1; if (OF_getencprop(node, "clock-frequency", &clock, sizeof(clock)) <= 0) clock = 0; #ifdef EXT_RESOURCES if (hwreset_get_by_ofw_idx(dev, 0, 0, &reset) == 0) { error = hwreset_deassert(reset); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); return (error); } } if (snps_get_clocks(dev, &baudclk, &apb_pclk) == 0) { error = clk_enable(baudclk); if (error != 0) { device_printf(dev, "cannot enable baud clock\n"); return (error); } if (apb_pclk != NULL) { error = clk_enable(apb_pclk); if (error != 0) { device_printf(dev, "cannot enable peripheral clock\n"); return (error); } } if (clock == 0) { error = clk_get_freq(baudclk, &freq); if (error != 0) { device_printf(dev, "cannot get frequency\n"); return (error); } clock = (uint32_t)freq; } } #endif if (bootverbose && clock == 0) device_printf(dev, "could not determine frequency\n"); - error = uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0); + error = uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0, UART_F_BUSY_DETECT); if (error != 0) return (error); #ifdef EXT_RESOURCES /* XXX uart_bus_probe has changed the softc, so refresh it */ sc = device_get_softc(dev); /* Store clock and reset handles for detach */ sc->baudclk = baudclk; sc->apb_pclk = apb_pclk; sc->reset = reset; #endif return (0); } static int snps_detach(device_t dev) { #ifdef EXT_RESOURCES struct snps_softc *sc; clk_t baudclk, apb_pclk; hwreset_t reset; #endif int error; #ifdef EXT_RESOURCES sc = device_get_softc(dev); baudclk = sc->baudclk; apb_pclk = sc->apb_pclk; reset = sc->reset; #endif error = uart_bus_detach(dev); if (error != 0) return (error); #ifdef EXT_RESOURCES if (reset != NULL) { error = hwreset_assert(reset); if (error != 0) { device_printf(dev, "cannot assert reset\n"); return (error); } hwreset_release(reset); } if (apb_pclk != NULL) { error = clk_release(apb_pclk); if (error != 0) { device_printf(dev, "cannot release peripheral clock\n"); return (error); } } if (baudclk != NULL) { error = clk_release(baudclk); if (error != 0) { device_printf(dev, "cannot release baud clock\n"); return (error); } } #endif return (0); } static device_method_t snps_bus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, snps_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, snps_detach), DEVMETHOD_END }; static driver_t snps_uart_driver = { uart_driver_name, snps_bus_methods, sizeof(struct snps_softc) }; DRIVER_MODULE(uart_snps, simplebus, snps_uart_driver, uart_devclass, 0, 0); Index: head/sys/mips/atheros/ar531x/uart_bus_ar5315.c =================================================================== --- head/sys/mips/atheros/ar531x/uart_bus_ar5315.c (revision 338073) +++ head/sys/mips/atheros/ar531x/uart_bus_ar5315.c (revision 338074) @@ -1,89 +1,89 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ #include "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" static int uart_ar5315_probe(device_t dev); extern struct uart_class uart_ar5315_uart_class; static device_method_t uart_ar5315_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_ar5315_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), DEVMETHOD_END }; static driver_t uart_ar5315_driver = { uart_driver_name, uart_ar5315_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_ar5315_probe(device_t dev) { struct uart_softc *sc; uint64_t freq; freq = ar531x_ahb_freq(); sc = device_get_softc(dev); sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); sc->sc_class = &uart_ns8250_class; bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); sc->sc_sysdev->bas.regshft = 2; sc->sc_sysdev->bas.bst = mips_bus_space_generic; sc->sc_sysdev->bas.bsh = ar531x_uart_addr() + 3; sc->sc_bas.regshft = 2; sc->sc_bas.bst = mips_bus_space_generic; sc->sc_bas.bsh = ar531x_uart_addr() + 3; - return (uart_bus_probe(dev, 2, 0, freq, 0, 0)); + return (uart_bus_probe(dev, 2, 0, freq, 0, 0, 0)); } DRIVER_MODULE(uart, apb, uart_ar5315_driver, uart_devclass, 0, 0); Index: head/sys/mips/atheros/uart_bus_ar71xx.c =================================================================== --- head/sys/mips/atheros/uart_bus_ar71xx.c (revision 338073) +++ head/sys/mips/atheros/uart_bus_ar71xx.c (revision 338074) @@ -1,108 +1,108 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ #include "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" static int uart_ar71xx_probe(device_t dev); extern struct uart_class uart_ar71xx_uart_class; static device_method_t uart_ar71xx_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_ar71xx_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_ar71xx_driver = { uart_driver_name, uart_ar71xx_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_ar71xx_probe(device_t dev) { struct uart_softc *sc; uint64_t freq; freq = ar71xx_uart_freq(); sc = device_get_softc(dev); sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); sc->sc_class = &uart_ns8250_class; bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); sc->sc_sysdev->bas.regshft = 2; sc->sc_sysdev->bas.bst = mips_bus_space_generic; sc->sc_sysdev->bas.bsh = MIPS_PHYS_TO_KSEG1(AR71XX_UART_ADDR) + 3; sc->sc_bas.regshft = 2; sc->sc_bas.bst = mips_bus_space_generic; sc->sc_bas.bsh = MIPS_PHYS_TO_KSEG1(AR71XX_UART_ADDR) + 3; - return (uart_bus_probe(dev, 2, 0, freq, 0, 0)); + return (uart_bus_probe(dev, 2, 0, freq, 0, 0, 0)); } #ifdef EARLY_PRINTF static void ar71xx_early_putc(int c) { int i; for (i = 0; i < 1000; i++) { if (ATH_READ_REG(AR71XX_UART_ADDR + AR71XX_UART_LSR) & AR71XX_UART_LSR_THRE) break; } ATH_WRITE_REG(AR71XX_UART_ADDR + AR71XX_UART_THR, (c & 0xff)); } early_putc_t *early_putc = ar71xx_early_putc; #endif DRIVER_MODULE(uart, apb, uart_ar71xx_driver, uart_devclass, 0, 0); Index: head/sys/mips/atheros/uart_bus_ar933x.c =================================================================== --- head/sys/mips/atheros/uart_bus_ar933x.c (revision 338073) +++ head/sys/mips/atheros/uart_bus_ar933x.c (revision 338074) @@ -1,117 +1,117 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012, Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ #include "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EARLY_PRINTF #include #endif #include "uart_if.h" static int uart_ar933x_probe(device_t dev); extern struct uart_class uart_ar933x_uart_class; static device_method_t uart_ar933x_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_ar933x_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_ar933x_driver = { uart_driver_name, uart_ar933x_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_ar933x_probe(device_t dev) { struct uart_softc *sc; uint64_t freq; freq = ar71xx_uart_freq(); sc = device_get_softc(dev); sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); sc->sc_class = &uart_ar933x_class; bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); sc->sc_sysdev->bas.regshft = 0; sc->sc_sysdev->bas.bst = mips_bus_space_generic; sc->sc_sysdev->bas.bsh = MIPS_PHYS_TO_KSEG1(AR71XX_UART_ADDR); sc->sc_bas.regshft = 0; sc->sc_bas.bst = mips_bus_space_generic; sc->sc_bas.bsh = MIPS_PHYS_TO_KSEG1(AR71XX_UART_ADDR); - return (uart_bus_probe(dev, 2, 0, freq, 0, 0)); + return (uart_bus_probe(dev, 2, 0, freq, 0, 0, 0)); } /* * Assume the UART is setup by the bootloader and just echo that. */ #if defined(EARLY_PRINTF) static void ar933x_early_putc(int c) { int i = 1000; /* Wait until FIFO is clear */ while ((i > 0) && (ATH_READ_REG(AR71XX_UART_ADDR + AR933X_UART_CS_REG) & AR933X_UART_CS_TX_BUSY)) i--; /* Write it out */ ATH_WRITE_REG(AR71XX_UART_ADDR + AR933X_UART_DATA_REG, (c & 0xff)| AR933X_UART_DATA_TX_CSR); } early_putc_t *early_putc = ar933x_early_putc; #endif /* EARLY_PRINTF */ DRIVER_MODULE(uart, apb, uart_ar933x_driver, uart_devclass, 0, 0); Index: head/sys/mips/broadcom/uart_bus_chipc.c =================================================================== --- head/sys/mips/broadcom/uart_bus_chipc.c (revision 338073) +++ head/sys/mips/broadcom/uart_bus_chipc.c (revision 338074) @@ -1,81 +1,81 @@ /*- * 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. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_uart.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #include "bhnd_chipc_if.h" #include "bcm_machdep.h" static int uart_chipc_probe(device_t dev) { struct uart_softc *sc; u_int rclk; sc = device_get_softc(dev); sc->sc_class = &uart_ns8250_class; rclk = bcm_get_uart_rclk(bcm_get_platform()); - return (uart_bus_probe(dev, 0, 0, rclk, 0, 0)); + return (uart_bus_probe(dev, 0, 0, rclk, 0, 0, 0)); } static device_method_t uart_chipc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_chipc_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_chipc_driver = { uart_driver_name, uart_chipc_methods, sizeof(struct uart_softc), }; DRIVER_MODULE(uart, bhnd_chipc, uart_chipc_driver, uart_devclass, 0, 0); Index: head/sys/mips/cavium/uart_bus_octeonusart.c =================================================================== --- head/sys/mips/cavium/uart_bus_octeonusart.c (revision 338073) +++ head/sys/mips/cavium/uart_bus_octeonusart.c (revision 338074) @@ -1,113 +1,113 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Wojciech A. Koszek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * $Id$ */ /* * Skeleton of this file was based on respective code for ARM * code written by Olivier Houchard. */ /* * XXXMIPS: This file is hacked from arm/... . XXXMIPS here means this file is * experimental and was written for MIPS32 port. */ #include "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" extern struct uart_class uart_oct16550_class; static int uart_octeon_probe(device_t dev); static device_method_t uart_octeon_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_octeon_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), {0, 0} }; static driver_t uart_octeon_driver = { uart_driver_name, uart_octeon_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_octeon_probe(device_t dev) { struct uart_softc *sc; int unit; unit = device_get_unit(dev); sc = device_get_softc(dev); sc->sc_class = &uart_oct16550_class; /* * We inherit the settings from the systme console. Note, the bst * bad bus_space_map are bogus here, but obio doesn't yet support * them, it seems. */ sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); sc->sc_bas.bst = uart_bus_space_mem; /* * XXX * RBR isn't really a great base address. */ if (bus_space_map(sc->sc_bas.bst, CVMX_MIO_UARTX_RBR(0), uart_getrange(sc->sc_class), 0, &sc->sc_bas.bsh) != 0) return (ENXIO); - return (uart_bus_probe(dev, sc->sc_bas.regshft, 0, 0, 0, unit)); + return (uart_bus_probe(dev, sc->sc_bas.regshft, 0, 0, 0, unit, 0)); } DRIVER_MODULE(uart, obio, uart_octeon_driver, uart_devclass, 0, 0); Index: head/sys/mips/ingenic/jz4780_uart.c =================================================================== --- head/sys/mips/ingenic/jz4780_uart.c (revision 338073) +++ head/sys/mips/ingenic/jz4780_uart.c (revision 338074) @@ -1,220 +1,220 @@ /*- * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * High-level UART interface. */ struct jz4780_uart_softc { struct ns8250_softc ns8250_base; clk_t clk_mod; clk_t clk_baud; }; static int jz4780_bus_attach(struct uart_softc *sc) { struct ns8250_softc *ns8250; struct uart_bas *bas; int rv; ns8250 = (struct ns8250_softc *)sc; bas = &sc->sc_bas; rv = ns8250_bus_attach(sc); if (rv != 0) return (0); /* Configure uart to use extra IER_RXTMOUT bit */ ns8250->ier_rxbits = IER_RXTMOUT | IER_EMSC | IER_ERLS | IER_ERXRDY; ns8250->ier_mask = ~(ns8250->ier_rxbits); ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; ns8250->ier |= ns8250->ier_rxbits; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); return (0); } static kobj_method_t jz4780_uart_methods[] = { KOBJMETHOD(uart_probe, ns8250_bus_probe), KOBJMETHOD(uart_attach, jz4780_bus_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD(uart_grab, ns8250_bus_grab), KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), KOBJMETHOD_END }; static struct uart_class jz4780_uart_class = { "jz4780_uart_class", jz4780_uart_methods, sizeof(struct jz4780_uart_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, .uc_rclk = 0, }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"ingenic,jz4780-uart", (uintptr_t)&jz4780_uart_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS(compat_data); /* * UART Driver interface. */ static int jz4780_uart_get_shift(device_t dev) { phandle_t node; pcell_t shift; node = ofw_bus_get_node(dev); if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) shift = 2; return ((int)shift); } static int jz4780_uart_probe(device_t dev) { struct jz4780_uart_softc *sc; uint64_t freq; int shift; int rv; const struct ofw_compat_data *cd; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); cd = ofw_bus_search_compatible(dev, compat_data); if (cd->ocd_data == 0) return (ENXIO); /* Figure out clock setup */ rv = clk_get_by_ofw_name(dev, 0, "module", &sc->clk_mod); if (rv != 0) { device_printf(dev, "Cannot get UART clock: %d\n", rv); return (ENXIO); } rv = clk_enable(sc->clk_mod); if (rv != 0) { device_printf(dev, "Cannot enable UART clock: %d\n", rv); return (ENXIO); } rv = clk_get_by_ofw_name(dev, 0, "baud", &sc->clk_baud); if (rv != 0) { device_printf(dev, "Cannot get UART clock: %d\n", rv); return (ENXIO); } rv = clk_enable(sc->clk_baud); if (rv != 0) { device_printf(dev, "Cannot enable UART clock: %d\n", rv); return (ENXIO); } rv = clk_get_freq(sc->clk_baud, &freq); if (rv != 0) { device_printf(dev, "Cannot determine UART clock frequency: %d\n", rv); return (ENXIO); } if (bootverbose) device_printf(dev, "got UART clock: %lld\n", freq); sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; shift = jz4780_uart_get_shift(dev); - return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0)); + return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0)); } static int jz4780_uart_detach(device_t dev) { struct jz4780_uart_softc *sc; int rv; rv = uart_bus_detach(dev); if (rv != 0) return (rv); sc = device_get_softc(dev); if (sc->clk_mod != NULL) { clk_release(sc->clk_mod); } if (sc->clk_baud != NULL) { clk_release(sc->clk_baud); } return (0); } static device_method_t jz4780_uart_bus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, jz4780_uart_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, jz4780_uart_detach), { 0, 0 } }; static driver_t jz4780_uart_driver = { uart_driver_name, jz4780_uart_bus_methods, sizeof(struct jz4780_uart_softc), }; DRIVER_MODULE(jz4780_uart, simplebus, jz4780_uart_driver, uart_devclass, 0, 0); Index: head/sys/mips/malta/uart_bus_maltausart.c =================================================================== --- head/sys/mips/malta/uart_bus_maltausart.c (revision 338073) +++ head/sys/mips/malta/uart_bus_maltausart.c (revision 338074) @@ -1,93 +1,93 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Wojciech A. Koszek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * $Id$ */ /* * Skeleton of this file was based on respective code for ARM * code written by Olivier Houchard. */ #include "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" static int uart_malta_probe(device_t dev); extern struct uart_class malta_uart_class; static device_method_t uart_malta_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_malta_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_malta_driver = { uart_driver_name, uart_malta_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_malta_probe(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); sc->sc_class = &uart_ns8250_class; bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); sc->sc_sysdev->bas.bst = mips_bus_space_generic; sc->sc_sysdev->bas.bsh = MIPS_PHYS_TO_KSEG1(MALTA_UART0ADR); sc->sc_bas.bst = mips_bus_space_generic; sc->sc_bas.bsh = MIPS_PHYS_TO_KSEG1(MALTA_UART0ADR); - return(uart_bus_probe(dev, 0, 0, 0, 0, 0)); + return(uart_bus_probe(dev, 0, 0, 0, 0, 0, 0)); } DRIVER_MODULE(uart, obio, uart_malta_driver, uart_devclass, 0, 0); Index: head/sys/modules/uart/Makefile =================================================================== --- head/sys/modules/uart/Makefile (revision 338073) +++ head/sys/modules/uart/Makefile (revision 338074) @@ -1,42 +1,47 @@ # $FreeBSD$ .PATH: ${SRCTOP}/sys/dev/uart +.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ + ${MACHINE_CPUARCH} == "i386" +uart_bus_acpi=uart_bus_acpi.c +.endif + .if ${MACHINE_CPUARCH} == "sparc64" uart_bus_ebus= uart_bus_ebus.c .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" || \ ${MACHINE_CPUARCH} == "powerpc" || ${MACHINE_CPUARCH} == "riscv" || \ ${MACHINE_CPUARCH} == "sparc64" ofw_bus_if= ofw_bus_if.h .endif .if ${MACHINE} == "i386" || ${MACHINE} == "amd64" _uart_cpu=uart_cpu_x86.c .else _uart_cpu=uart_cpu_${MACHINE}.c .endif .if exists(${.CURDIR:H:H}/dev/uart/${_uart_cpu}) uart_cpu_machine= ${_uart_cpu} .endif .if ${MACHINE} == "arm64" uart_dev_mvebu=uart_dev_mvebu.c uart_dev_mu=uart_dev_mu.c .endif KMOD= uart -SRCS= uart_bus_acpi.c ${uart_bus_ebus} uart_bus_isa.c uart_bus_pccard.c \ +SRCS= ${uart_bus_acpi} ${uart_bus_ebus} uart_bus_isa.c uart_bus_pccard.c \ uart_bus_pci.c uart_bus_puc.c uart_bus_scc.c \ uart_core.c ${uart_cpu_machine} uart_dbg.c \ ${uart_dev_mvebu} uart_dev_ns8250.c ${uart_dev_mu} \ uart_dev_quicc.c uart_dev_sab82532.c uart_dev_z8530.c \ uart_if.c uart_if.h uart_subr.c uart_tty.c SRCS+= acpi_if.h bus_if.h card_if.h device_if.h isa_if.h ${ofw_bus_if} \ pci_if.h \ power_if.h pccarddevs.h serdev_if.h -SRCS+= opt_platform.h opt_uart.h +SRCS+= opt_acpi.h opt_platform.h opt_uart.h .include Index: head/sys/powerpc/psim/uart_iobus.c =================================================================== --- head/sys/powerpc/psim/uart_iobus.c (revision 338073) +++ head/sys/powerpc/psim/uart_iobus.c (revision 338074) @@ -1,89 +1,89 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * PSIM local bus 16550 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int uart_iobus_probe(device_t dev); static device_method_t uart_iobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_iobus_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_iobus_driver = { uart_driver_name, uart_iobus_methods, sizeof(struct uart_softc), }; static int uart_iobus_probe(device_t dev) { struct uart_softc *sc; char *type; type = iobus_get_name(dev); if (strncmp(type, "com", 3) != 0) return (ENXIO); sc = device_get_softc(dev); sc->sc_class = &uart_ns8250_class; device_set_desc(dev, "PSIM serial port"); - return (uart_bus_probe(dev, 0, 0, 0, 0, 0)); + return (uart_bus_probe(dev, 0, 0, 0, 0, 0, 0)); } DRIVER_MODULE(uart, iobus, uart_iobus_driver, uart_devclass, 0, 0); Index: head/sys/sparc64/pci/sbbc.c =================================================================== --- head/sys/sparc64/pci/sbbc.c (revision 338073) +++ head/sys/sparc64/pci/sbbc.c (revision 338074) @@ -1,1113 +1,1113 @@ /* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */ /*- * SPDX-License-Identifier: (ISC AND BSD-2-Clause-FreeBSD) * * Copyright (c) 2008 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /*- * Copyright (c) 2010 Marius Strobl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "uart_if.h" #define SBBC_PCI_BAR PCIR_BAR(0) #define SBBC_PCI_VENDOR 0x108e #define SBBC_PCI_PRODUCT 0xc416 #define SBBC_REGS_OFFSET 0x800000 #define SBBC_REGS_SIZE 0x6230 #define SBBC_EPLD_OFFSET 0x8e0000 #define SBBC_EPLD_SIZE 0x20 #define SBBC_SRAM_OFFSET 0x900000 #define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */ #define SBBC_PCI_INT_STATUS 0x2320 #define SBBC_PCI_INT_ENABLE 0x2330 #define SBBC_PCI_ENABLE_INT_A 0x11 #define SBBC_EPLD_INTERRUPT 0x13 #define SBBC_EPLD_INTERRUPT_ON 0x01 #define SBBC_SRAM_CONS_IN 0x00000001 #define SBBC_SRAM_CONS_OUT 0x00000002 #define SBBC_SRAM_CONS_BRK 0x00000004 #define SBBC_SRAM_CONS_SPACE_IN 0x00000008 #define SBBC_SRAM_CONS_SPACE_OUT 0x00000010 #define SBBC_TAG_KEY_SIZE 8 #define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */ #define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */ #define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */ #define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */ #define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */ #define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */ #define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x) struct sbbc_sram_tag { char tag_key[SBBC_TAG_KEY_SIZE]; uint32_t tag_size; uint32_t tag_offset; } __packed; #define SBBC_TOC_MAGIC "TOCSRAM" #define SBBC_TOC_MAGIC_SIZE 8 #define SBBC_TOC_TAGS_MAX 32 #define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x) struct sbbc_sram_toc { char toc_magic[SBBC_TOC_MAGIC_SIZE]; uint8_t toc_reserved; uint8_t toc_type; uint16_t toc_version; uint32_t toc_ntags; struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX]; } __packed; #define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */ #define SBBC_TOD_VERSION 1 #define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x) struct sbbc_sram_tod { uint32_t tod_magic; uint32_t tod_version; uint64_t tod_time; uint64_t tod_skew; uint32_t tod_reserved; uint32_t tod_heartbeat; uint32_t tod_timeout; } __packed; #define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */ #define SBBC_CONS_VERSION 1 #define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x) struct sbbc_sram_cons { uint32_t cons_magic; uint32_t cons_version; uint32_t cons_size; uint32_t cons_in_begin; uint32_t cons_in_end; uint32_t cons_in_rdptr; uint32_t cons_in_wrptr; uint32_t cons_out_begin; uint32_t cons_out_end; uint32_t cons_out_rdptr; uint32_t cons_out_wrptr; } __packed; struct sbbc_softc { struct resource *sc_res; }; #define SBBC_READ_N(wdth, offs) \ bus_space_read_ ## wdth((bst), (bsh), (offs)) #define SBBC_WRITE_N(wdth, offs, val) \ bus_space_write_ ## wdth((bst), (bsh), (offs), (val)) #define SBBC_READ_1(offs) \ SBBC_READ_N(1, (offs)) #define SBBC_READ_2(offs) \ bswap16(SBBC_READ_N(2, (offs))) #define SBBC_READ_4(offs) \ bswap32(SBBC_READ_N(4, (offs))) #define SBBC_READ_8(offs) \ bswap64(SBBC_READ_N(8, (offs))) #define SBBC_WRITE_1(offs, val) \ SBBC_WRITE_N(1, (offs), (val)) #define SBBC_WRITE_2(offs, val) \ SBBC_WRITE_N(2, (offs), bswap16(val)) #define SBBC_WRITE_4(offs, val) \ SBBC_WRITE_N(4, (offs), bswap32(val)) #define SBBC_WRITE_8(offs, val) \ SBBC_WRITE_N(8, (offs), bswap64(val)) #define SBBC_REGS_READ_1(offs) \ SBBC_READ_1((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_READ_2(offs) \ SBBC_READ_2((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_READ_4(offs) \ SBBC_READ_4((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_READ_8(offs) \ SBBC_READ_8((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_WRITE_1(offs, val) \ SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_REGS_WRITE_2(offs, val) \ SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_REGS_WRITE_4(offs, val) \ SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_REGS_WRITE_8(offs, val) \ SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_EPLD_READ_1(offs) \ SBBC_READ_1((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_READ_2(offs) \ SBBC_READ_2((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_READ_4(offs) \ SBBC_READ_4((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_READ_8(offs) \ SBBC_READ_8((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_WRITE_1(offs, val) \ SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_EPLD_WRITE_2(offs, val) \ SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_EPLD_WRITE_4(offs, val) \ SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_EPLD_WRITE_8(offs, val) \ SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_SRAM_READ_1(offs) \ SBBC_READ_1((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_READ_2(offs) \ SBBC_READ_2((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_READ_4(offs) \ SBBC_READ_4((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_READ_8(offs) \ SBBC_READ_8((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_WRITE_1(offs, val) \ SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val)) #define SBBC_SRAM_WRITE_2(offs, val) \ SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val)) #define SBBC_SRAM_WRITE_4(offs, val) \ SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val)) #define SBBC_SRAM_WRITE_8(offs, val) \ SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val)) #define SUNW_SETCONSINPUT "SUNW,set-console-input" #define SUNW_SETCONSINPUT_CLNT "CON_CLNT" #define SUNW_SETCONSINPUT_OBP "CON_OBP" static u_int sbbc_console; static uint32_t sbbc_scsolie; static uint32_t sbbc_scsolir; static uint32_t sbbc_solcons; static uint32_t sbbc_solscie; static uint32_t sbbc_solscir; static uint32_t sbbc_toddata; /* * internal helpers */ static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh); static inline void sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh); static const char *sbbc_serengeti_set_console_input(char *new); /* * SBBC PCI interface */ static bus_activate_resource_t sbbc_bus_activate_resource; static bus_adjust_resource_t sbbc_bus_adjust_resource; static bus_deactivate_resource_t sbbc_bus_deactivate_resource; static bus_alloc_resource_t sbbc_bus_alloc_resource; static bus_release_resource_t sbbc_bus_release_resource; static bus_get_resource_list_t sbbc_bus_get_resource_list; static bus_setup_intr_t sbbc_bus_setup_intr; static bus_teardown_intr_t sbbc_bus_teardown_intr; static device_attach_t sbbc_pci_attach; static device_probe_t sbbc_pci_probe; static clock_gettime_t sbbc_tod_gettime; static clock_settime_t sbbc_tod_settime; static device_method_t sbbc_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbbc_pci_probe), DEVMETHOD(device_attach, sbbc_pci_attach), DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource), DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource), DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource), DEVMETHOD(bus_adjust_resource, sbbc_bus_adjust_resource), DEVMETHOD(bus_release_resource, sbbc_bus_release_resource), DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr), DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list), /* clock interface */ DEVMETHOD(clock_gettime, sbbc_tod_gettime), DEVMETHOD(clock_settime, sbbc_tod_settime), DEVMETHOD_END }; static devclass_t sbbc_devclass; DEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc)); DRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, NULL, NULL); static int sbbc_pci_probe(device_t dev) { if (pci_get_vendor(dev) == SBBC_PCI_VENDOR && pci_get_device(dev) == SBBC_PCI_PRODUCT) { device_set_desc(dev, "Sun BootBus controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int sbbc_pci_attach(device_t dev) { struct sbbc_softc *sc; struct timespec ts; device_t child; bus_space_tag_t bst; bus_space_handle_t bsh; phandle_t node; int error, rid; uint32_t val; /* Nothing to do if we're not the chosen one. */ if ((node = OF_finddevice("/chosen")) == -1) { device_printf(dev, "failed to find /chosen\n"); return (ENXIO); } if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) { device_printf(dev, "failed to get iosram\n"); return (ENXIO); } if (node != ofw_bus_get_node(dev)) return (0); sc = device_get_softc(dev); rid = SBBC_PCI_BAR; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_res == NULL) { device_printf(dev, "failed to allocate resources\n"); return (ENXIO); } bst = rman_get_bustag(sc->sc_res); bsh = rman_get_bushandle(sc->sc_res); if (sbbc_console != 0) { /* Once again the interrupt pin isn't set. */ if (pci_get_intpin(dev) == 0) pci_set_intpin(dev, 1); child = device_add_child(dev, NULL, -1); if (child == NULL) device_printf(dev, "failed to add UART device\n"); error = bus_generic_attach(dev); if (error != 0) device_printf(dev, "failed to attach UART device\n"); } else { error = sbbc_parse_toc(bst, bsh); if (error != 0) { device_printf(dev, "failed to parse TOC\n"); if (sbbc_console != 0) { bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_res); return (error); } } } if (sbbc_toddata != 0) { if ((val = SBBC_SRAM_READ_4(sbbc_toddata + SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC) device_printf(dev, "invalid TOD magic %#x\n", val); else if ((val = SBBC_SRAM_READ_4(sbbc_toddata + SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION) device_printf(dev, "invalid TOD version %#x\n", val); else { clock_register(dev, 1000000); /* 1 sec. resolution */ if (bootverbose) { sbbc_tod_gettime(dev, &ts); device_printf(dev, "current time: %ld.%09ld\n", (long)ts.tv_sec, ts.tv_nsec); } } } return (0); } /* * Note that the bus methods don't pass-through the uart(4) requests but act * as if they would come from sbbc(4) in order to avoid complications with * pci(4) (actually, uart(4) isn't a real child but rather a function of * sbbc(4) anyway). */ static struct resource * sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct sbbc_softc *sc; sc = device_get_softc(dev); switch (type) { case SYS_RES_IRQ: return (bus_generic_alloc_resource(dev, dev, type, rid, start, end, count, flags)); case SYS_RES_MEMORY: return (sc->sc_res); default: return (NULL); } } static int sbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (type == SYS_RES_MEMORY) return (0); return (bus_generic_activate_resource(bus, child, type, rid, res)); } static int sbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (type == SYS_RES_MEMORY) return (0); return (bus_generic_deactivate_resource(bus, child, type, rid, res)); } static int sbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused, int type __unused, struct resource *res __unused, rman_res_t start __unused, rman_res_t end __unused) { return (ENXIO); } static int sbbc_bus_release_resource(device_t dev, device_t child __unused, int type, int rid, struct resource *res) { if (type == SYS_RES_IRQ) return (bus_generic_release_resource(dev, dev, type, rid, res)); return (0); } static struct resource_list * sbbc_bus_get_resource_list(device_t dev, device_t child __unused) { return (bus_generic_get_resource_list(dev, dev)); } static int sbbc_bus_setup_intr(device_t dev, device_t child __unused, struct resource *res, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg, cookiep)); } static int sbbc_bus_teardown_intr(device_t dev, device_t child __unused, struct resource *res, void *cookie) { return (bus_generic_teardown_intr(dev, dev, res, cookie)); } /* * internal helpers */ static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh) { char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)]; bus_size_t tag; phandle_t node; uint32_t off, sram_toc; u_int i, tags; if ((node = OF_finddevice("/chosen")) == -1) return (ENXIO); /* SRAM TOC offset defaults to 0. */ if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0) sram_toc = 0; bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc + SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE); buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0'; if (strcmp(buf, SBBC_TOC_MAGIC) != 0) return (ENXIO); tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags)); for (i = 0; i < tags; i++) { tag = sram_toc + SBBC_TOC_OFF(toc_tag) + i * sizeof(struct sbbc_sram_tag); bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag + SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE); buf[SBBC_TAG_KEY_SIZE - 1] = '\0'; off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset)); if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0) sbbc_scsolie = off; else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0) sbbc_scsolir = off; else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0) sbbc_solcons = off; else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0) sbbc_solscie = off; else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0) sbbc_solscir = off; else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0) sbbc_toddata = off; } return (0); } static const char * sbbc_serengeti_set_console_input(char *new) { struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t new; cell_t old; } args = { (cell_t)SUNW_SETCONSINPUT, 1, 1, }; args.new = (cell_t)new; if (ofw_entry(&args) == -1) return (NULL); return ((const char *)args.old); } static inline void sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh) { SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON); bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); } /* * TOD interface */ static int sbbc_tod_gettime(device_t dev, struct timespec *ts) { struct sbbc_softc *sc; bus_space_tag_t bst; bus_space_handle_t bsh; sc = device_get_softc(dev); bst = rman_get_bustag(sc->sc_res); bsh = rman_get_bushandle(sc->sc_res); ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) + SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew)); ts->tv_nsec = 0; return (0); } static int sbbc_tod_settime(device_t dev, struct timespec *ts) { struct sbbc_softc *sc; bus_space_tag_t bst; bus_space_handle_t bsh; sc = device_get_softc(dev); bst = rman_get_bustag(sc->sc_res); bsh = rman_get_bushandle(sc->sc_res); SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec - SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time))); return (0); } /* * UART bus front-end */ static device_probe_t sbbc_uart_sbbc_probe; static device_method_t sbbc_uart_sbbc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbbc_uart_sbbc_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), DEVMETHOD_END }; DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods, sizeof(struct uart_softc)); DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL); static int sbbc_uart_sbbc_probe(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); sc->sc_class = &uart_sbbc_class; device_set_desc(dev, "Serengeti console"); - return (uart_bus_probe(dev, 0, 0, 0, SBBC_PCI_BAR, 0)); + return (uart_bus_probe(dev, 0, 0, 0, SBBC_PCI_BAR, 0, 0)); } /* * Low-level UART interface */ static int sbbc_uart_probe(struct uart_bas *bas); static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity); static void sbbc_uart_term(struct uart_bas *bas); static void sbbc_uart_putc(struct uart_bas *bas, int c); static int sbbc_uart_rxready(struct uart_bas *bas); static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx); static struct uart_ops sbbc_uart_ops = { .probe = sbbc_uart_probe, .init = sbbc_uart_init, .term = sbbc_uart_term, .putc = sbbc_uart_putc, .rxready = sbbc_uart_rxready, .getc = sbbc_uart_getc, }; static int sbbc_uart_probe(struct uart_bas *bas) { bus_space_tag_t bst; bus_space_handle_t bsh; int error; sbbc_console = 1; bst = bas->bst; bsh = bas->bsh; error = sbbc_parse_toc(bst, bsh); if (error != 0) return (error); if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 || sbbc_solscie == 0 || sbbc_solscir == 0) return (ENXIO); if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) != SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION) return (ENXIO); return (0); } static void sbbc_uart_init(struct uart_bas *bas, int baudrate __unused, int databits __unused, int stopbits __unused, int parity __unused) { bus_space_tag_t bst; bus_space_handle_t bsh; bst = bas->bst; bsh = bas->bsh; /* Enable output to and space in from the SC interrupts. */ SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) | SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN); uart_barrier(bas); /* Take over the console input. */ sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT); } static void sbbc_uart_term(struct uart_bas *bas __unused) { /* Give back the console input. */ sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); } static void sbbc_uart_putc(struct uart_bas *bas, int c) { bus_space_tag_t bst; bus_space_handle_t bsh; uint32_t wrptr; bst = bas->bst; bsh = bas->bsh; wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)); SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c); uart_barrier(bas); if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end))) wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_begin)); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), wrptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_OUT); uart_barrier(bas); sbbc_send_intr(bst, bsh); } static int sbbc_uart_rxready(struct uart_bas *bas) { bus_space_tag_t bst; bus_space_handle_t bsh; bst = bas->bst; bsh = bas->bsh; if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))) return (0); return (1); } static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { bus_space_tag_t bst; bus_space_handle_t bsh; int c; uint32_t rdptr; bst = bas->bst; bsh = bas->bsh; uart_lock(hwmtx); while (sbbc_uart_rxready(bas) == 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); uart_barrier(bas); if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end))) rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_begin)); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), rdptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_SPACE_IN); uart_barrier(bas); sbbc_send_intr(bst, bsh); uart_unlock(hwmtx); return (c); } /* * High-level UART interface */ static int sbbc_uart_bus_attach(struct uart_softc *sc); static int sbbc_uart_bus_detach(struct uart_softc *sc); static int sbbc_uart_bus_flush(struct uart_softc *sc, int what); static int sbbc_uart_bus_getsig(struct uart_softc *sc); static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data); static int sbbc_uart_bus_ipend(struct uart_softc *sc); static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity); static int sbbc_uart_bus_probe(struct uart_softc *sc); static int sbbc_uart_bus_receive(struct uart_softc *sc); static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig); static int sbbc_uart_bus_transmit(struct uart_softc *sc); static kobj_method_t sbbc_uart_methods[] = { KOBJMETHOD(uart_attach, sbbc_uart_bus_attach), KOBJMETHOD(uart_detach, sbbc_uart_bus_detach), KOBJMETHOD(uart_flush, sbbc_uart_bus_flush), KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig), KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl), KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend), KOBJMETHOD(uart_param, sbbc_uart_bus_param), KOBJMETHOD(uart_probe, sbbc_uart_bus_probe), KOBJMETHOD(uart_receive, sbbc_uart_bus_receive), KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig), KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit), DEVMETHOD_END }; struct uart_class uart_sbbc_class = { "sbbc", sbbc_uart_methods, sizeof(struct uart_softc), .uc_ops = &sbbc_uart_ops, .uc_range = 1, .uc_rclk = 0x5bbc, /* arbitrary */ .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if ((c) != 0) { \ i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \ } else { \ i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \ } static int sbbc_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; uint32_t wrptr; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); /* * Let the current output drain before enabling interrupts. Not * doing so tends to cause lost output when turning them on. */ wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)); while (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) != wrptr); cpu_spinwait(); /* Clear and acknowledge possibly outstanding interrupts. */ SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); uart_barrier(bas); SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, SBBC_SRAM_READ_4(sbbc_scsolir)); uart_barrier(bas); /* Enable PCI interrupts. */ SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A); uart_barrier(bas); /* Enable input from and output to SC as well as break interrupts. */ SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) | SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK | SBBC_SRAM_CONS_SPACE_OUT); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int sbbc_uart_bus_detach(struct uart_softc *sc) { /* Give back the console input. */ sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); return (0); } static int sbbc_uart_bus_flush(struct uart_softc *sc, int what) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; if ((what & UART_FLUSH_TRANSMITTER) != 0) return (ENODEV); if ((what & UART_FLUSH_RECEIVER) != 0) { SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))); uart_barrier(bas); } return (0); } static int sbbc_uart_bus_getsig(struct uart_softc *sc) { uint32_t dummy, new, old, sig; do { old = sc->sc_hwsig; sig = old; dummy = 0; SIGCHG(dummy, sig, SER_CTS, SER_DCTS); SIGCHG(dummy, sig, SER_DCD, SER_DDCD); SIGCHG(dummy, sig, SER_DSR, SER_DDSR); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { int error; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BAUD: *(int*)data = 9600; /* arbitrary */ break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int sbbc_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; int ipend; uint32_t reason, status; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS); if (status == 0) { uart_unlock(sc->sc_hwmtx); return (0); } /* * Unfortunately, we can't use compare and swap for non-cachable * memory. */ reason = SBBC_SRAM_READ_4(sbbc_scsolir); SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); uart_barrier(bas); /* Acknowledge the interrupt. */ SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); ipend = 0; if ((reason & SBBC_SRAM_CONS_IN) != 0) ipend |= SER_INT_RXREADY; if ((reason & SBBC_SRAM_CONS_BRK) != 0) ipend |= SER_INT_BREAK; if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 && SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr))) ipend |= SER_INT_TXIDLE; return (ipend); } static int sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused, int databits __unused, int stopbits __unused, int parity __unused) { return (0); } static int sbbc_uart_bus_probe(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; if (sbbc_console != 0) { bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_begin)) - 1; sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_begin)) - 1; return (0); } return (ENXIO); } static int sbbc_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; int c; uint32_t end, rdptr, wrptr; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)); rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)); while (rdptr != wrptr) { if (uart_rx_full(sc) != 0) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); uart_rx_put(sc, c); if (++rdptr == end) rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_begin)); } uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), rdptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_SPACE_IN); uart_barrier(bas); sbbc_send_intr(bst, bsh); uart_unlock(sc->sc_hwmtx); return (0); } static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if ((sig & SER_DDTR) != 0) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if ((sig & SER_DRTS) != 0) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (0); } static int sbbc_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; int i; uint32_t end, wrptr; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)); wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)); for (i = 0; i < sc->sc_txdatasz; i++) { SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]); if (++wrptr == end) wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_begin)); } uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), wrptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_OUT); uart_barrier(bas); sbbc_send_intr(bst, bsh); sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); }