diff --git a/sys/dev/acpica/acpi_video.c b/sys/dev/acpica/acpi_video.c index dc4a4385f3b5..917c6548f3a2 100644 --- a/sys/dev/acpica/acpi_video.c +++ b/sys/dev/acpica/acpi_video.c @@ -1,1255 +1,1255 @@ /*- * Copyright (c) 2002-2003 Taku YAMAMOTO * 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. * * $Id: acpi_vid.c,v 1.4 2003/10/13 10:07:36 taku Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_evdev.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef EVDEV_SUPPORT #include #include #endif /* ACPI video extension driver. */ struct acpi_video_output { ACPI_HANDLE handle; UINT32 adr; STAILQ_ENTRY(acpi_video_output) vo_next; struct { int num; STAILQ_ENTRY(acpi_video_output) next; } vo_unit; int vo_hasbqc; /* Query method is present. */ int vo_level; /* Cached level when !vo_hasbqc. */ int vo_brightness; int vo_fullpower; int vo_economy; int vo_numlevels; int *vo_levels; struct sysctl_ctx_list vo_sysctl_ctx; struct sysctl_oid *vo_sysctl_tree; #ifdef EVDEV_SUPPORT struct evdev_dev *evdev; #endif }; STAILQ_HEAD(acpi_video_output_queue, acpi_video_output); struct acpi_video_softc { device_t device; ACPI_HANDLE handle; struct acpi_video_output_queue vid_outputs; eventhandler_tag vid_pwr_evh; #ifdef EVDEV_SUPPORT struct evdev_dev *evdev; #endif }; /* interfaces */ static int acpi_video_modevent(struct module*, int, void *); static void acpi_video_identify(driver_t *driver, device_t parent); static int acpi_video_probe(device_t); static int acpi_video_attach(device_t); static int acpi_video_detach(device_t); static int acpi_video_resume(device_t); static int acpi_video_shutdown(device_t); static void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); static void acpi_video_power_profile(void *); static void acpi_video_bind_outputs(struct acpi_video_softc *); static struct acpi_video_output *acpi_video_vo_init(UINT32); static void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); static void acpi_video_vo_destroy(struct acpi_video_output *); static int acpi_video_vo_check_level(struct acpi_video_output *, int); static void acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *); static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); /* operations */ static void vid_set_switch_policy(ACPI_HANDLE, UINT32); static int vid_enum_outputs(ACPI_HANDLE, void(*)(ACPI_HANDLE, UINT32, void *), void *); static int vo_get_brightness_levels(ACPI_HANDLE, int **); static int vo_get_brightness(struct acpi_video_output *); static void vo_set_brightness(struct acpi_video_output *, int); static UINT32 vo_get_device_status(ACPI_HANDLE); static UINT32 vo_get_graphics_state(ACPI_HANDLE); static void vo_set_device_state(ACPI_HANDLE, UINT32); /* events */ #define VID_NOTIFY_SWITCHED 0x80 #define VID_NOTIFY_REPROBE 0x81 #define VID_NOTIFY_CYCLE_OUT 0x82 #define VID_NOTIFY_NEXT_OUT 0x83 #define VID_NOTIFY_PREV_OUT 0x84 #define VID_NOTIFY_CYCLE_BRN 0x85 #define VID_NOTIFY_INC_BRN 0x86 #define VID_NOTIFY_DEC_BRN 0x87 #define VID_NOTIFY_ZERO_BRN 0x88 #define VID_NOTIFY_DISP_OFF 0x89 /* _DOS (Enable/Disable Output Switching) argument bits */ #define DOS_SWITCH_MASK 3 #define DOS_SWITCH_BY_OSPM 0 #define DOS_SWITCH_BY_BIOS 1 #define DOS_SWITCH_LOCKED 2 #define DOS_BRIGHTNESS_BY_OSPM (1 << 2) /* _DOD and subdev's _ADR */ #define DOD_DEVID_MASK 0x0f00 #define DOD_DEVID_MASK_FULL 0xffff #define DOD_DEVID_MASK_DISPIDX 0x000f #define DOD_DEVID_MASK_DISPPORT 0x00f0 #define DOD_DEVID_MONITOR 0x0100 #define DOD_DEVID_LCD 0x0110 #define DOD_DEVID_TV 0x0200 #define DOD_DEVID_EXT 0x0300 #define DOD_DEVID_INTDFP 0x0400 #define DOD_BIOS (1 << 16) #define DOD_NONVGA (1 << 17) #define DOD_HEAD_ID_SHIFT 18 #define DOD_HEAD_ID_BITS 3 #define DOD_HEAD_ID_MASK \ (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) #define DOD_DEVID_SCHEME_STD (1U << 31) /* _BCL related constants */ #define BCL_FULLPOWER 0 #define BCL_ECONOMY 1 /* _DCS (Device Currrent Status) value bits and masks. */ #define DCS_EXISTS (1 << 0) #define DCS_ACTIVE (1 << 1) #define DCS_READY (1 << 2) #define DCS_FUNCTIONAL (1 << 3) #define DCS_ATTACHED (1 << 4) /* _DSS (Device Set Status) argument bits and masks. */ #define DSS_INACTIVE 0 #define DSS_ACTIVE (1 << 0) #define DSS_SETNEXT (1 << 30) #define DSS_COMMIT (1U << 31) static device_method_t acpi_video_methods[] = { DEVMETHOD(device_identify, acpi_video_identify), DEVMETHOD(device_probe, acpi_video_probe), DEVMETHOD(device_attach, acpi_video_attach), DEVMETHOD(device_detach, acpi_video_detach), DEVMETHOD(device_resume, acpi_video_resume), DEVMETHOD(device_shutdown, acpi_video_shutdown), { 0, 0 } }; static driver_t acpi_video_driver = { "acpi_video", acpi_video_methods, sizeof(struct acpi_video_softc), }; static devclass_t acpi_video_devclass; DRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass, acpi_video_modevent, NULL); MODULE_DEPEND(acpi_video, acpi, 1, 1, 1); #ifdef EVDEV_SUPPORT MODULE_DEPEND(acpi_video, evdev, 1, 1, 1); #endif static struct sysctl_ctx_list acpi_video_sysctl_ctx; static struct sysctl_oid *acpi_video_sysctl_tree; static struct acpi_video_output_queue crt_units, tv_units, ext_units, lcd_units, other_units; /* * The 'video' lock protects the hierarchy of video output devices * (the video "bus"). The 'video_output' lock protects per-output * data is equivalent to a softc lock for each video output. */ ACPI_SERIAL_DECL(video, "ACPI video"); ACPI_SERIAL_DECL(video_output, "ACPI video output"); static MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); #ifdef EVDEV_SUPPORT static const struct { UINT32 notify; uint16_t key; } acpi_video_evdev_map[] = { { VID_NOTIFY_SWITCHED, KEY_SWITCHVIDEOMODE }, { VID_NOTIFY_REPROBE, KEY_SWITCHVIDEOMODE }, { VID_NOTIFY_CYCLE_OUT, KEY_SWITCHVIDEOMODE }, { VID_NOTIFY_NEXT_OUT, KEY_VIDEO_NEXT }, { VID_NOTIFY_PREV_OUT, KEY_VIDEO_PREV }, { VID_NOTIFY_CYCLE_BRN, KEY_BRIGHTNESS_CYCLE }, { VID_NOTIFY_INC_BRN, KEY_BRIGHTNESSUP }, { VID_NOTIFY_DEC_BRN, KEY_BRIGHTNESSDOWN }, { VID_NOTIFY_ZERO_BRN, KEY_BRIGHTNESS_ZERO }, { VID_NOTIFY_DISP_OFF, KEY_DISPLAY_OFF }, }; static void acpi_video_push_evdev_event(struct evdev_dev *evdev, UINT32 notify) { int i; uint16_t key; - /* Do not allow to execute 2 instances this routine concurently */ + /* Do not allow to execute 2 instances this routine concurrently */ ACPI_SERIAL_ASSERT(video_output); for (i = 0; i < nitems(acpi_video_evdev_map); i++) { if (acpi_video_evdev_map[i].notify == notify) { key = acpi_video_evdev_map[i].key; evdev_push_key(evdev, key, 1); evdev_sync(evdev); evdev_push_key(evdev, key, 0); evdev_sync(evdev); break; } } } #endif static int acpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) { int err; err = 0; switch (evt) { case MOD_LOAD: sysctl_ctx_init(&acpi_video_sysctl_ctx); STAILQ_INIT(&crt_units); STAILQ_INIT(&tv_units); STAILQ_INIT(&ext_units); STAILQ_INIT(&lcd_units); STAILQ_INIT(&other_units); break; case MOD_UNLOAD: sysctl_ctx_free(&acpi_video_sysctl_ctx); acpi_video_sysctl_tree = NULL; break; default: err = EINVAL; } return (err); } static void acpi_video_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "acpi_video", -1) == NULL) device_add_child(parent, "acpi_video", -1); } static int acpi_video_probe(device_t dev) { ACPI_HANDLE devh, h; ACPI_OBJECT_TYPE t_dos; devh = acpi_get_handle(dev); if (acpi_disabled("video") || ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || ACPI_FAILURE(AcpiGetType(h, &t_dos)) || t_dos != ACPI_TYPE_METHOD) return (ENXIO); device_set_desc(dev, "ACPI video extension"); return (0); } static int acpi_video_attach(device_t dev) { struct acpi_softc *acpi_sc; struct acpi_video_softc *sc; #ifdef EVDEV_SUPPORT int i; #endif sc = device_get_softc(dev); acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); if (acpi_sc == NULL) return (ENXIO); #ifdef EVDEV_SUPPORT sc->evdev = evdev_alloc(); evdev_set_name(sc->evdev, device_get_desc(dev)); evdev_set_phys(sc->evdev, device_get_nameunit(dev)); evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1); evdev_support_event(sc->evdev, EV_SYN); evdev_support_event(sc->evdev, EV_KEY); for (i = 0; i < nitems(acpi_video_evdev_map); i++) evdev_support_key(sc->evdev, acpi_video_evdev_map[i].key); if (evdev_register(sc->evdev) != 0) return (ENXIO); #endif ACPI_SERIAL_BEGIN(video); if (acpi_video_sysctl_tree == NULL) { acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "video", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "video extension control"); } ACPI_SERIAL_END(video); sc->device = dev; sc->handle = acpi_get_handle(dev); STAILQ_INIT(&sc->vid_outputs); AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_video_notify_handler, sc); sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, acpi_video_power_profile, sc, 0); ACPI_SERIAL_BEGIN(video); acpi_video_bind_outputs(sc); ACPI_SERIAL_END(video); /* * Notify the BIOS that we want to switch both active outputs and * brightness levels. */ vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM | DOS_BRIGHTNESS_BY_OSPM); acpi_video_power_profile(sc); return (0); } static int acpi_video_detach(device_t dev) { struct acpi_video_softc *sc; struct acpi_video_output *vo, *vn; sc = device_get_softc(dev); vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_video_notify_handler); ACPI_SERIAL_BEGIN(video); STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { acpi_video_vo_destroy(vo); } ACPI_SERIAL_END(video); #ifdef EVDEV_SUPPORT evdev_free(sc->evdev); #endif return (0); } static int acpi_video_resume(device_t dev) { struct acpi_video_softc *sc; struct acpi_video_output *vo, *vn; int level; sc = device_get_softc(dev); /* Restore brightness level */ ACPI_SERIAL_BEGIN(video); ACPI_SERIAL_BEGIN(video_output); STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { if ((vo->adr & DOD_DEVID_MASK_FULL) != DOD_DEVID_LCD && (vo->adr & DOD_DEVID_MASK) != DOD_DEVID_INTDFP) continue; if ((vo_get_device_status(vo->handle) & DCS_ACTIVE) == 0) continue; level = vo_get_brightness(vo); if (level != -1) vo_set_brightness(vo, level); } ACPI_SERIAL_END(video_output); ACPI_SERIAL_END(video); return (0); } static int acpi_video_shutdown(device_t dev) { struct acpi_video_softc *sc; sc = device_get_softc(dev); vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); return (0); } static void acpi_video_invoke_event_handler(void *context) { EVENTHANDLER_INVOKE(acpi_video_event, (int)(intptr_t)context); } static void acpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) { struct acpi_video_softc *sc; struct acpi_video_output *vo, *vo_tmp; ACPI_HANDLE lasthand; UINT32 dcs, dss, dss_p; sc = (struct acpi_video_softc *)context; switch (notify) { case VID_NOTIFY_SWITCHED: dss_p = 0; lasthand = NULL; ACPI_SERIAL_BEGIN(video); ACPI_SERIAL_BEGIN(video_output); STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { dss = vo_get_graphics_state(vo->handle); dcs = vo_get_device_status(vo->handle); if (!(dcs & DCS_READY)) dss = DSS_INACTIVE; if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { if (lasthand != NULL) vo_set_device_state(lasthand, dss_p); dss_p = dss; lasthand = vo->handle; } } if (lasthand != NULL) vo_set_device_state(lasthand, dss_p|DSS_COMMIT); ACPI_SERIAL_END(video_output); ACPI_SERIAL_END(video); break; case VID_NOTIFY_REPROBE: ACPI_SERIAL_BEGIN(video); STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) vo->handle = NULL; acpi_video_bind_outputs(sc); STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) { if (vo->handle == NULL) { STAILQ_REMOVE(&sc->vid_outputs, vo, acpi_video_output, vo_next); acpi_video_vo_destroy(vo); } } ACPI_SERIAL_END(video); break; /* Next events should not appear if DOS_SWITCH_BY_OSPM policy is set */ case VID_NOTIFY_CYCLE_OUT: case VID_NOTIFY_NEXT_OUT: case VID_NOTIFY_PREV_OUT: default: device_printf(sc->device, "unknown notify event 0x%x\n", notify); } AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_video_invoke_event_handler, (void *)(uintptr_t)notify); #ifdef EVDEV_SUPPORT ACPI_SERIAL_BEGIN(video_output); acpi_video_push_evdev_event(sc->evdev, notify); ACPI_SERIAL_END(video_output); #endif } static void acpi_video_power_profile(void *context) { int state; struct acpi_video_softc *sc; struct acpi_video_output *vo; sc = context; state = power_profile_get_state(); if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY) return; ACPI_SERIAL_BEGIN(video); ACPI_SERIAL_BEGIN(video_output); STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { if (vo->vo_levels != NULL && vo->vo_brightness == -1) vo_set_brightness(vo, state == POWER_PROFILE_ECONOMY ? vo->vo_economy : vo->vo_fullpower); } ACPI_SERIAL_END(video_output); ACPI_SERIAL_END(video); } static void acpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) { struct acpi_video_softc *sc; struct acpi_video_output *vo; ACPI_SERIAL_ASSERT(video); sc = context; STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { if (vo->adr == adr) { acpi_video_vo_bind(vo, handle); return; } } vo = acpi_video_vo_init(adr); if (vo != NULL) { #ifdef EVDEV_SUPPORT vo->evdev = sc->evdev; #endif acpi_video_vo_bind(vo, handle); STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); } } static void acpi_video_bind_outputs(struct acpi_video_softc *sc) { ACPI_SERIAL_ASSERT(video); vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); } static struct acpi_video_output * acpi_video_vo_init(UINT32 adr) { struct acpi_video_output *vn, *vo, *vp; int n, x; char name[8], env[32]; const char *type, *desc; struct acpi_video_output_queue *voqh; ACPI_SERIAL_ASSERT(video); switch (adr & DOD_DEVID_MASK) { case DOD_DEVID_MONITOR: if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) { /* DOD_DEVID_LCD is a common, backward compatible ID */ desc = "Internal/Integrated Digital Flat Panel"; type = "lcd"; voqh = &lcd_units; } else { desc = "VGA CRT or VESA Compatible Analog Monitor"; type = "crt"; voqh = &crt_units; } break; case DOD_DEVID_TV: desc = "TV/HDTV or Analog-Video Monitor"; type = "tv"; voqh = &tv_units; break; case DOD_DEVID_EXT: desc = "External Digital Monitor"; type = "ext"; voqh = &ext_units; break; case DOD_DEVID_INTDFP: desc = "Internal/Integrated Digital Flat Panel"; type = "lcd"; voqh = &lcd_units; break; default: desc = "unknown output"; type = "out"; voqh = &other_units; } n = 0; vp = NULL; STAILQ_FOREACH(vn, voqh, vo_unit.next) { if (vn->vo_unit.num != n) break; vp = vn; n++; } snprintf(name, sizeof(name), "%s%d", type, n); vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); if (vo != NULL) { vo->handle = NULL; vo->adr = adr; vo->vo_unit.num = n; vo->vo_hasbqc = -1; vo->vo_level = -1; vo->vo_brightness = -1; vo->vo_fullpower = -1; /* TODO: override with tunables */ vo->vo_economy = -1; vo->vo_numlevels = 0; vo->vo_levels = NULL; snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); if (getenv_int(env, &x)) vo->vo_fullpower = x; snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); if (getenv_int(env, &x)) vo->vo_economy = x; sysctl_ctx_init(&vo->vo_sysctl_ctx); if (vp != NULL) STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); else STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); if (acpi_video_sysctl_tree != NULL) vo->vo_sysctl_tree = SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(acpi_video_sysctl_tree), OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, desc); if (vo->vo_sysctl_tree != NULL) { SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, vo, 0, acpi_video_vo_active_sysctl, "I", "current activity of this device"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "brightness", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, vo, 0, acpi_video_vo_bright_sysctl, "I", "current brightness level"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "fullpower", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, vo, POWER_PROFILE_PERFORMANCE, acpi_video_vo_presets_sysctl, "I", "preset level for full power mode"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "economy", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, vo, POWER_PROFILE_ECONOMY, acpi_video_vo_presets_sysctl, "I", "preset level for economy mode"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "levels", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, vo, 0, acpi_video_vo_levels_sysctl, "I", "supported brightness levels"); } else printf("%s: sysctl node creation failed\n", type); } else printf("%s: softc allocation failed\n", type); if (bootverbose) { printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL); printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX); printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4); if (adr & DOD_BIOS) printf(", detectable by BIOS"); if (adr & DOD_NONVGA) printf(" (Non-VGA output device whose power " "is related to the VGA device)"); printf(", head #%d\n", (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); } return (vo); } static void acpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) { ACPI_SERIAL_BEGIN(video_output); if (vo->vo_levels != NULL) { AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, acpi_video_vo_notify_handler); AcpiOsFree(vo->vo_levels); vo->vo_levels = NULL; } vo->handle = handle; vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); if (vo->vo_numlevels >= 2) { if (vo->vo_fullpower == -1 || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) { /* XXX - can't deal with rebinding... */ vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; } if (vo->vo_economy == -1 || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) { /* XXX - see above. */ vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; } AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, acpi_video_vo_notify_handler, vo); } ACPI_SERIAL_END(video_output); } static void acpi_video_vo_destroy(struct acpi_video_output *vo) { struct acpi_video_output_queue *voqh; ACPI_SERIAL_ASSERT(video); if (vo->vo_sysctl_tree != NULL) { vo->vo_sysctl_tree = NULL; sysctl_ctx_free(&vo->vo_sysctl_ctx); } if (vo->vo_levels != NULL) { AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, acpi_video_vo_notify_handler); AcpiOsFree(vo->vo_levels); } switch (vo->adr & DOD_DEVID_MASK) { case DOD_DEVID_MONITOR: if ((vo->adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) voqh = &lcd_units; else voqh = &crt_units; break; case DOD_DEVID_TV: voqh = &tv_units; break; case DOD_DEVID_EXT: voqh = &ext_units; break; case DOD_DEVID_INTDFP: voqh = &lcd_units; break; default: voqh = &other_units; } STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); free(vo, M_ACPIVIDEO); } static int acpi_video_vo_check_level(struct acpi_video_output *vo, int level) { int i; ACPI_SERIAL_ASSERT(video_output); if (vo->vo_levels == NULL) return (ENODEV); for (i = 0; i < vo->vo_numlevels; i++) if (vo->vo_levels[i] == level) return (0); return (EINVAL); } static void acpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) { struct acpi_video_output *vo; int i, j, level, new_level; vo = context; ACPI_SERIAL_BEGIN(video_output); if (vo->handle != handle) goto out; switch (notify) { case VID_NOTIFY_CYCLE_BRN: if (vo->vo_numlevels <= 3) goto out; /* FALLTHROUGH */ case VID_NOTIFY_INC_BRN: case VID_NOTIFY_DEC_BRN: case VID_NOTIFY_ZERO_BRN: case VID_NOTIFY_DISP_OFF: if (vo->vo_levels == NULL) goto out; level = vo_get_brightness(vo); if (level < 0) goto out; break; default: printf("unknown notify event 0x%x from %s\n", notify, acpi_name(handle)); goto out; } new_level = level; switch (notify) { case VID_NOTIFY_CYCLE_BRN: for (i = 2; i < vo->vo_numlevels; i++) if (vo->vo_levels[i] == level) { new_level = vo->vo_numlevels > i + 1 ? vo->vo_levels[i + 1] : vo->vo_levels[2]; break; } break; case VID_NOTIFY_INC_BRN: case VID_NOTIFY_DEC_BRN: for (i = 0; i < vo->vo_numlevels; i++) { j = vo->vo_levels[i]; if (notify == VID_NOTIFY_INC_BRN) { if (j > level && (j < new_level || level == new_level)) new_level = j; } else { if (j < level && (j > new_level || level == new_level)) new_level = j; } } break; case VID_NOTIFY_ZERO_BRN: for (i = 0; i < vo->vo_numlevels; i++) if (vo->vo_levels[i] == 0) { new_level = 0; break; } break; case VID_NOTIFY_DISP_OFF: acpi_pwr_switch_consumer(handle, ACPI_STATE_D3); break; } if (new_level != level) { vo_set_brightness(vo, new_level); vo->vo_brightness = new_level; } #ifdef EVDEV_SUPPORT acpi_video_push_evdev_event(vo->evdev, notify); #endif out: ACPI_SERIAL_END(video_output); AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_video_invoke_event_handler, (void *)(uintptr_t)notify); } /* ARGSUSED */ static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int state, err; vo = (struct acpi_video_output *)arg1; if (vo->handle == NULL) return (ENXIO); ACPI_SERIAL_BEGIN(video_output); state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; err = sysctl_handle_int(oidp, &state, 0, req); if (err != 0 || req->newptr == NULL) goto out; vo_set_device_state(vo->handle, DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); out: ACPI_SERIAL_END(video_output); return (err); } /* ARGSUSED */ static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int level, preset, err; vo = (struct acpi_video_output *)arg1; ACPI_SERIAL_BEGIN(video_output); if (vo->handle == NULL) { err = ENXIO; goto out; } if (vo->vo_levels == NULL) { err = ENODEV; goto out; } preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? vo->vo_economy : vo->vo_fullpower; level = vo->vo_brightness; if (level == -1) level = preset; err = sysctl_handle_int(oidp, &level, 0, req); if (err != 0 || req->newptr == NULL) goto out; if (level < -1 || level > 100) { err = EINVAL; goto out; } if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) goto out; vo->vo_brightness = level; vo_set_brightness(vo, (level == -1) ? preset : level); out: ACPI_SERIAL_END(video_output); return (err); } static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int i, level, *preset, err; vo = (struct acpi_video_output *)arg1; ACPI_SERIAL_BEGIN(video_output); if (vo->handle == NULL) { err = ENXIO; goto out; } if (vo->vo_levels == NULL) { err = ENODEV; goto out; } preset = (arg2 == POWER_PROFILE_ECONOMY) ? &vo->vo_economy : &vo->vo_fullpower; level = *preset; err = sysctl_handle_int(oidp, &level, 0, req); if (err != 0 || req->newptr == NULL) goto out; if (level < -1 || level > 100) { err = EINVAL; goto out; } if (level == -1) { i = (arg2 == POWER_PROFILE_ECONOMY) ? BCL_ECONOMY : BCL_FULLPOWER; level = vo->vo_levels[i]; } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) goto out; if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) vo_set_brightness(vo, level); *preset = level; out: ACPI_SERIAL_END(video_output); return (err); } /* ARGSUSED */ static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int err; vo = (struct acpi_video_output *)arg1; ACPI_SERIAL_BEGIN(video_output); if (vo->vo_levels == NULL) { err = ENODEV; goto out; } if (req->newptr != NULL) { err = EPERM; goto out; } err = sysctl_handle_opaque(oidp, vo->vo_levels, vo->vo_numlevels * sizeof(*vo->vo_levels), req); out: ACPI_SERIAL_END(video_output); return (err); } static void vid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) { ACPI_STATUS status; status = acpi_SetInteger(handle, "_DOS", policy); if (ACPI_FAILURE(status)) printf("can't evaluate %s._DOS - %s\n", acpi_name(handle), AcpiFormatException(status)); } struct enum_callback_arg { void (*callback)(ACPI_HANDLE, UINT32, void *); void *context; ACPI_OBJECT *dod_pkg; int count; }; static ACPI_STATUS vid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, void *context, void **retp __unused) { ACPI_STATUS status; UINT32 adr, val; struct enum_callback_arg *argset; size_t i; ACPI_SERIAL_ASSERT(video); argset = context; status = acpi_GetInteger(handle, "_ADR", &adr); if (ACPI_FAILURE(status)) return (AE_OK); for (i = 0; i < argset->dod_pkg->Package.Count; i++) { if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && (val & DOD_DEVID_MASK_FULL) == (adr & DOD_DEVID_MASK_FULL)) { argset->callback(handle, val, argset->context); argset->count++; } } return (AE_OK); } static int vid_enum_outputs(ACPI_HANDLE handle, void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) { ACPI_STATUS status; ACPI_BUFFER dod_buf; ACPI_OBJECT *res; struct enum_callback_arg argset; ACPI_SERIAL_ASSERT(video); dod_buf.Length = ACPI_ALLOCATE_BUFFER; dod_buf.Pointer = NULL; status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) printf("can't evaluate %s._DOD - %s\n", acpi_name(handle), AcpiFormatException(status)); argset.count = -1; goto out; } res = (ACPI_OBJECT *)dod_buf.Pointer; if (!ACPI_PKG_VALID(res, 1)) { printf("evaluation of %s._DOD makes no sense\n", acpi_name(handle)); argset.count = -1; goto out; } if (callback == NULL) { argset.count = res->Package.Count; goto out; } argset.callback = callback; argset.context = context; argset.dod_pkg = res; argset.count = 0; status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, vid_enum_outputs_subr, NULL, &argset, NULL); if (ACPI_FAILURE(status)) printf("failed walking down %s - %s\n", acpi_name(handle), AcpiFormatException(status)); out: if (dod_buf.Pointer != NULL) AcpiOsFree(dod_buf.Pointer); return (argset.count); } static int vo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) { ACPI_STATUS status; ACPI_BUFFER bcl_buf; ACPI_OBJECT *res; int num, i, n, *levels; bcl_buf.Length = ACPI_ALLOCATE_BUFFER; bcl_buf.Pointer = NULL; status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) printf("can't evaluate %s._BCL - %s\n", acpi_name(handle), AcpiFormatException(status)); goto out; } res = (ACPI_OBJECT *)bcl_buf.Pointer; if (!ACPI_PKG_VALID(res, 2)) { printf("evaluation of %s._BCL makes no sense\n", acpi_name(handle)); goto out; } num = res->Package.Count; if (num < 2 || levelp == NULL) goto out; levels = AcpiOsAllocate(num * sizeof(*levels)); if (levels == NULL) goto out; for (i = 0, n = 0; i < num; i++) if (acpi_PkgInt32(res, i, &levels[n]) == 0) n++; if (n < 2) { AcpiOsFree(levels); goto out; } *levelp = levels; return (n); out: if (bcl_buf.Pointer != NULL) AcpiOsFree(bcl_buf.Pointer); return (0); } static int vo_get_bqc(struct acpi_video_output *vo, UINT32 *level) { ACPI_STATUS status; switch (vo->vo_hasbqc) { case 1: case -1: status = acpi_GetInteger(vo->handle, "_BQC", level); if (vo->vo_hasbqc == 1) break; vo->vo_hasbqc = status != AE_NOT_FOUND; if (vo->vo_hasbqc == 1) break; /* FALLTHROUGH */ default: KASSERT(vo->vo_hasbqc == 0, ("bad vo_hasbqc state %d", vo->vo_hasbqc)); *level = vo->vo_level; status = AE_OK; } return (status); } static int vo_get_brightness(struct acpi_video_output *vo) { UINT32 level; ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); status = vo_get_bqc(vo, &level); if (ACPI_FAILURE(status)) { printf("can't evaluate %s._BQC - %s\n", acpi_name(vo->handle), AcpiFormatException(status)); return (-1); } if (level > 100) return (-1); return (level); } static void vo_set_brightness(struct acpi_video_output *vo, int level) { char notify_buf[16]; ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); status = acpi_SetInteger(vo->handle, "_BCM", level); if (ACPI_FAILURE(status)) { printf("can't evaluate %s._BCM - %s\n", acpi_name(vo->handle), AcpiFormatException(status)); } else { vo->vo_level = level; } snprintf(notify_buf, sizeof(notify_buf), "notify=%d", level); devctl_notify("ACPI", "Video", "brightness", notify_buf); } static UINT32 vo_get_device_status(ACPI_HANDLE handle) { UINT32 dcs; ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); dcs = 0; status = acpi_GetInteger(handle, "_DCS", &dcs); if (ACPI_FAILURE(status)) { /* * If the method is missing, assume that the device is always * operational. */ if (status != AE_NOT_FOUND) { printf("can't evaluate %s._DCS - %s\n", acpi_name(handle), AcpiFormatException(status)); } else { dcs = 0xff; } } return (dcs); } static UINT32 vo_get_graphics_state(ACPI_HANDLE handle) { UINT32 dgs; ACPI_STATUS status; dgs = 0; status = acpi_GetInteger(handle, "_DGS", &dgs); if (ACPI_FAILURE(status)) { /* * If the method is missing, assume that the device is always * operational. */ if (status != AE_NOT_FOUND) { printf("can't evaluate %s._DGS - %s\n", acpi_name(handle), AcpiFormatException(status)); } else { dgs = 0xff; } } return (dgs); } static void vo_set_device_state(ACPI_HANDLE handle, UINT32 state) { ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); status = acpi_SetInteger(handle, "_DSS", state); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) printf("can't evaluate %s._DSS - %s\n", acpi_name(handle), AcpiFormatException(status)); } diff --git a/sys/dev/aic7xxx/aic79xx.h b/sys/dev/aic7xxx/aic79xx.h index 38b152ef4051..f610f00af869 100644 --- a/sys/dev/aic7xxx/aic79xx.h +++ b/sys/dev/aic7xxx/aic79xx.h @@ -1,1593 +1,1593 @@ /*- * Core definitions and data structures shareable across OS platforms. * * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1994-2002 Justin T. Gibbs. * Copyright (c) 2000-2002 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#107 $ * * $FreeBSD$ */ #ifndef _AIC79XX_H_ #define _AIC79XX_H_ /* Register Definitions */ #include "aic79xx_reg.h" /************************* Forward Declarations *******************************/ struct ahd_platform_data; struct scb_platform_data; /****************************** Useful Macros *********************************/ #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array)) #define ALL_CHANNELS '\0' #define ALL_TARGETS_MASK 0xFFFF #define INITIATOR_WILDCARD (~0) #define SCB_LIST_NULL 0xFF00 #define SCB_LIST_NULL_LE (aic_htole16(SCB_LIST_NULL)) #define QOUTFIFO_ENTRY_VALID 0x80 #define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL) #define SCSIID_TARGET(ahd, scsiid) \ (((scsiid) & TID) >> TID_SHIFT) #define SCSIID_OUR_ID(scsiid) \ ((scsiid) & OID) #define SCSIID_CHANNEL(ahd, scsiid) ('A') #define SCB_IS_SCSIBUS_B(ahd, scb) (0) #define SCB_GET_OUR_ID(scb) \ SCSIID_OUR_ID((scb)->hscb->scsiid) #define SCB_GET_TARGET(ahd, scb) \ SCSIID_TARGET((ahd), (scb)->hscb->scsiid) #define SCB_GET_CHANNEL(ahd, scb) \ SCSIID_CHANNEL(ahd, (scb)->hscb->scsiid) #define SCB_GET_LUN(scb) \ ((scb)->hscb->lun) #define SCB_GET_TARGET_OFFSET(ahd, scb) \ SCB_GET_TARGET(ahd, scb) #define SCB_GET_TARGET_MASK(ahd, scb) \ (0x01 << (SCB_GET_TARGET_OFFSET(ahd, scb))) #ifdef AHD_DEBUG #define SCB_IS_SILENT(scb) \ ((ahd_debug & AHD_SHOW_MASKED_ERRORS) == 0 \ && (((scb)->flags & SCB_SILENT) != 0)) #else #define SCB_IS_SILENT(scb) \ (((scb)->flags & SCB_SILENT) != 0) #endif /* * TCLs have the following format: TTTTLLLLLLLL */ #define TCL_TARGET_OFFSET(tcl) \ ((((tcl) >> 4) & TID) >> 4) #define TCL_LUN(tcl) \ (tcl & (AHD_NUM_LUNS - 1)) #define BUILD_TCL(scsiid, lun) \ ((lun) | (((scsiid) & TID) << 4)) #define BUILD_TCL_RAW(target, channel, lun) \ ((lun) | ((target) << 8)) #define SCB_GET_TAG(scb) \ aic_le16toh(scb->hscb->tag) #ifndef AHD_TARGET_MODE #undef AHD_TMODE_ENABLE #define AHD_TMODE_ENABLE 0 #endif #define AHD_BUILD_COL_IDX(target, lun) \ (((lun) << 4) | target) #define AHD_GET_SCB_COL_IDX(ahd, scb) \ ((SCB_GET_LUN(scb) << 4) | SCB_GET_TARGET(ahd, scb)) #define AHD_SET_SCB_COL_IDX(scb, col_idx) \ do { \ (scb)->hscb->scsiid = ((col_idx) << TID_SHIFT) & TID; \ (scb)->hscb->lun = ((col_idx) >> 4) & (AHD_NUM_LUNS_NONPKT-1); \ } while (0) #define AHD_COPY_SCB_COL_IDX(dst, src) \ do { \ dst->hscb->scsiid = src->hscb->scsiid; \ dst->hscb->lun = src->hscb->lun; \ } while (0) #define AHD_NEVER_COL_IDX 0xFFFF /**************************** Driver Constants ********************************/ /* * The maximum number of supported targets. */ #define AHD_NUM_TARGETS 16 /* * The maximum number of supported luns. * The identify message only supports 64 luns in non-packetized transfers. * You can have 2^64 luns when information unit transfers are enabled, * but until we see a need to support that many, we support 256. */ #define AHD_NUM_LUNS_NONPKT 64 #define AHD_NUM_LUNS 256 /* * The maximum transfer per S/G segment. */ #define AHD_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */ /* * The maximum amount of SCB storage in hardware on a controller. * This value represents an upper bound. Due to software design, * we may not be able to use this number. */ #define AHD_SCB_MAX 512 /* * The maximum number of concurrent transactions supported per driver instance. * Sequencer Control Blocks (SCBs) store per-transaction information. */ #define AHD_MAX_QUEUE AHD_SCB_MAX /* * Define the size of our QIN and QOUT FIFOs. They must be a power of 2 * in size and accommodate as many transactions as can be queued concurrently. */ #define AHD_QIN_SIZE AHD_MAX_QUEUE #define AHD_QOUT_SIZE AHD_MAX_QUEUE #define AHD_QIN_WRAP(x) ((x) & (AHD_QIN_SIZE-1)) /* * The maximum amount of SCB storage we allocate in host memory. */ #define AHD_SCB_MAX_ALLOC AHD_MAX_QUEUE /* * Ring Buffer of incoming target commands. * We allocate 256 to simplify the logic in the sequencer * by using the natural wrap point of an 8bit counter. */ #define AHD_TMODE_CMDS 256 /* Reset line assertion time in us */ #define AHD_BUSRESET_DELAY 25 /******************* Chip Characteristics/Operating Settings *****************/ extern uint32_t ahd_attach_to_HostRAID_controllers; /* * Chip Type * The chip order is from least sophisticated to most sophisticated. */ typedef enum { AHD_NONE = 0x0000, AHD_CHIPID_MASK = 0x00FF, AHD_AIC7901 = 0x0001, AHD_AIC7902 = 0x0002, AHD_AIC7901A = 0x0003, AHD_PCI = 0x0100, /* Bus type PCI */ AHD_PCIX = 0x0200, /* Bus type PCIX */ AHD_BUS_MASK = 0x0F00 } ahd_chip; /* * Features available in each chip type. */ typedef enum { AHD_FENONE = 0x00000, AHD_WIDE = 0x00001,/* Wide Channel */ AHD_MULTI_FUNC = 0x00100,/* Multi-Function/Channel Device */ AHD_TARGETMODE = 0x01000,/* Has tested target mode support */ AHD_MULTIROLE = 0x02000,/* Space for two roles at a time */ AHD_RTI = 0x04000,/* Retained Training Support */ AHD_NEW_IOCELL_OPTS = 0x08000,/* More Signal knobs in the IOCELL */ AHD_NEW_DFCNTRL_OPTS = 0x10000,/* SCSIENWRDIS bit */ AHD_FAST_CDB_DELIVERY = 0x20000,/* CDB acks released to Output Sync */ AHD_REMOVABLE = 0x00000,/* Hot-Swap supported - None so far*/ AHD_AIC7901_FE = AHD_FENONE, AHD_AIC7901A_FE = AHD_FENONE, AHD_AIC7902_FE = AHD_MULTI_FUNC } ahd_feature; /* * Bugs in the silicon that we work around in software. */ typedef enum { AHD_BUGNONE = 0x0000, /* * Rev A hardware fails to update LAST/CURR/NEXTSCB * correctly in certain packetized selection cases. */ AHD_SENT_SCB_UPDATE_BUG = 0x0001, /* The wrong SCB is accessed to check the abort pending bit. */ AHD_ABORT_LQI_BUG = 0x0002, /* Packetized bitbucket crosses packet boundaries. */ AHD_PKT_BITBUCKET_BUG = 0x0004, /* The selection timer runs twice as long as its setting. */ AHD_LONG_SETIMO_BUG = 0x0008, /* The Non-LQ CRC error status is delayed until phase change. */ AHD_NLQICRC_DELAYED_BUG = 0x0010, /* The chip must be reset for all outgoing bus resets. */ AHD_SCSIRST_BUG = 0x0020, /* Some PCIX fields must be saved and restored across chip reset. */ AHD_PCIX_CHIPRST_BUG = 0x0040, /* MMAPIO is not functional in PCI-X mode. */ AHD_PCIX_MMAPIO_BUG = 0x0080, /* Reads to SCBRAM fail to reset the discard timer. */ AHD_PCIX_SCBRAM_RD_BUG = 0x0100, /* Bug workarounds that can be disabled on non-PCIX busses. */ AHD_PCIX_BUG_MASK = AHD_PCIX_CHIPRST_BUG | AHD_PCIX_MMAPIO_BUG | AHD_PCIX_SCBRAM_RD_BUG, /* * LQOSTOP0 status set even for forced selections with ATN * to perform non-packetized message delivery. */ AHD_LQO_ATNO_BUG = 0x0200, /* FIFO auto-flush does not always trigger. */ AHD_AUTOFLUSH_BUG = 0x0400, /* The CLRLQO registers are not self-clearing. */ AHD_CLRLQO_AUTOCLR_BUG = 0x0800, /* The PACKETIZED status bit refers to the previous connection. */ AHD_PKTIZED_STATUS_BUG = 0x1000, /* "Short Luns" are not placed into outgoing LQ packets correctly. */ AHD_PKT_LUN_BUG = 0x2000, /* * Only the FIFO allocated to the non-packetized connection may * be in use during a non-packetzied connection. */ AHD_NONPACKFIFO_BUG = 0x4000, /* - * Writing to a DFF SCBPTR register may fail if concurent with + * Writing to a DFF SCBPTR register may fail if concurrent with * a hardware write to the other DFF SCBPTR register. This is * not currently a concern in our sequencer since all chips with * this bug have the AHD_NONPACKFIFO_BUG and all writes of concern * occur in non-packetized connections. */ AHD_MDFF_WSCBPTR_BUG = 0x8000, /* SGHADDR updates are slow. */ AHD_REG_SLOW_SETTLE_BUG = 0x10000, /* * Changing the MODE_PTR coincident with an interrupt that * switches to a different mode will cause the interrupt to * be in the mode written outside of interrupt context. */ AHD_SET_MODE_BUG = 0x20000, /* Non-packetized busfree revision does not work. */ AHD_BUSFREEREV_BUG = 0x40000, /* * Paced transfers are indicated with a non-standard PPR * option bit in the neg table, 160MHz is indicated by * sync factor 0x7, and the offset if off by a factor of 2. */ AHD_PACED_NEGTABLE_BUG = 0x80000, /* LQOOVERRUN false positives. */ AHD_LQOOVERRUN_BUG = 0x100000, /* * Controller write to INTSTAT will lose to a host * write to CLRINT. */ AHD_INTCOLLISION_BUG = 0x200000, /* * The GEM318 violates the SCSI spec by not waiting * the mandated bus settle delay between phase changes * in some situations. Some aic79xx chip revs. are more * strict in this regard and will treat REQ assertions * that fall within the bus settle delay window as * glitches. This flag tells the firmware to tolerate * early REQ assertions. */ AHD_EARLY_REQ_BUG = 0x400000, /* * The LED does not stay on long enough in packetized modes. */ AHD_FAINT_LED_BUG = 0x800000 } ahd_bug; /* * Configuration specific settings. * The driver determines these settings by probing the * chip/controller's configuration. */ typedef enum { AHD_FNONE = 0x00000, AHD_BOOT_CHANNEL = 0x00001,/* We were set as the boot channel. */ AHD_USEDEFAULTS = 0x00004,/* * For cards without an seeprom * or a BIOS to initialize the chip's * SRAM, we use the default target * settings. */ AHD_SEQUENCER_DEBUG = 0x00008, AHD_RESET_BUS_A = 0x00010, AHD_EXTENDED_TRANS_A = 0x00020, AHD_TERM_ENB_A = 0x00040, AHD_SPCHK_ENB_A = 0x00080, AHD_STPWLEVEL_A = 0x00100, AHD_INITIATORROLE = 0x00200,/* * Allow initiator operations on * this controller. */ AHD_TARGETROLE = 0x00400,/* * Allow target operations on this * controller. */ AHD_RESOURCE_SHORTAGE = 0x00800, AHD_TQINFIFO_BLOCKED = 0x01000,/* Blocked waiting for ATIOs */ AHD_INT50_SPEEDFLEX = 0x02000,/* * Internal 50pin connector * sits behind an aic3860 */ AHD_BIOS_ENABLED = 0x04000, AHD_ALL_INTERRUPTS = 0x08000, AHD_39BIT_ADDRESSING = 0x10000,/* Use 39 bit addressing scheme. */ AHD_64BIT_ADDRESSING = 0x20000,/* Use 64 bit addressing scheme. */ AHD_CURRENT_SENSING = 0x40000, AHD_SCB_CONFIG_USED = 0x80000,/* No SEEPROM but SCB had info. */ AHD_HP_BOARD = 0x100000, AHD_RESET_POLL_ACTIVE = 0x200000, AHD_UPDATE_PEND_CMDS = 0x400000, AHD_RUNNING_QOUTFIFO = 0x800000, AHD_HAD_FIRST_SEL = 0x1000000, AHD_SHUTDOWN_RECOVERY = 0x2000000, /* Terminate recovery thread. */ AHD_HOSTRAID_BOARD = 0x4000000 } ahd_flag; /************************* Hardware SCB Definition ***************************/ /* * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB * consists of a "hardware SCB" mirroring the fields available on the card * and additional information the kernel stores for each transaction. * * To minimize space utilization, a portion of the hardware scb stores * different data during different portions of a SCSI transaction. * As initialized by the host driver for the initiator role, this area * contains the SCSI cdb (or a pointer to the cdb) to be executed. After * the cdb has been presented to the target, this area serves to store * residual transfer information and the SCSI status byte. * For the target role, the contents of this area do not change, but * still serve a different purpose than for the initiator role. See * struct target_data for details. */ /* * Status information embedded in the shared poriton of * an SCB after passing the cdb to the target. The kernel * driver will only read this data for transactions that * complete abnormally. */ struct initiator_status { uint32_t residual_datacnt; /* Residual in the current S/G seg */ uint32_t residual_sgptr; /* The next S/G for this transfer */ uint8_t scsi_status; /* Standard SCSI status byte */ }; struct target_status { uint32_t residual_datacnt; /* Residual in the current S/G seg */ uint32_t residual_sgptr; /* The next S/G for this transfer */ uint8_t scsi_status; /* SCSI status to give to initiator */ uint8_t target_phases; /* Bitmap of phases to execute */ uint8_t data_phase; /* Data-In or Data-Out */ uint8_t initiator_tag; /* Initiator's transaction tag */ }; /* * Initiator mode SCB shared data area. * If the embedded CDB is 12 bytes or less, we embed * the sense buffer address in the SCB. This allows * us to retrieve sense information without interrupting * the host in packetized mode. */ typedef uint32_t sense_addr_t; #define MAX_CDB_LEN 16 #define MAX_CDB_LEN_WITH_SENSE_ADDR (MAX_CDB_LEN - sizeof(sense_addr_t)) union initiator_data { struct { uint64_t cdbptr; uint8_t cdblen; } cdb_from_host; uint8_t cdb[MAX_CDB_LEN]; struct { uint8_t cdb[MAX_CDB_LEN_WITH_SENSE_ADDR]; sense_addr_t sense_addr; } cdb_plus_saddr; }; /* * Target mode version of the shared data SCB segment. */ struct target_data { uint32_t spare[2]; uint8_t scsi_status; /* SCSI status to give to initiator */ uint8_t target_phases; /* Bitmap of phases to execute */ uint8_t data_phase; /* Data-In or Data-Out */ uint8_t initiator_tag; /* Initiator's transaction tag */ }; struct hardware_scb { /*0*/ union { union initiator_data idata; struct target_data tdata; struct initiator_status istatus; struct target_status tstatus; } shared_data; /* * A word about residuals. * The scb is presented to the sequencer with the dataptr and datacnt * fields initialized to the contents of the first S/G element to * transfer. The sgptr field is initialized to the bus address for * the S/G element that follows the first in the in core S/G array * or'ed with the SG_FULL_RESID flag. Sgptr may point to an invalid * S/G entry for this transfer (single S/G element transfer with the * first elements address and length preloaded in the dataptr/datacnt * fields). If no transfer is to occur, sgptr is set to SG_LIST_NULL. * The SG_FULL_RESID flag ensures that the residual will be correctly * noted even if no data transfers occur. Once the data phase is entered, * the residual sgptr and datacnt are loaded from the sgptr and the * datacnt fields. After each S/G element's dataptr and length are * loaded into the hardware, the residual sgptr is advanced. After * each S/G element is expired, its datacnt field is checked to see * if the LAST_SEG flag is set. If so, SG_LIST_NULL is set in the * residual sg ptr and the transfer is considered complete. If the * sequencer determines that there is a residual in the transfer, or * there is non-zero status, it will set the SG_STATUS_VALID flag in * sgptr and dma the scb back into host memory. To sumarize: * * Sequencer: * o A residual has occurred if SG_FULL_RESID is set in sgptr, * or residual_sgptr does not have SG_LIST_NULL set. * * o We are transferring the last segment if residual_datacnt has * the SG_LAST_SEG flag set. * * Host: * o A residual can only have occurred if a completed scb has the * SG_STATUS_VALID flag set. Inspection of the SCSI status field, * the residual_datacnt, and the residual_sgptr field will tell * for sure. * * o residual_sgptr and sgptr refer to the "next" sg entry * and so may point beyond the last valid sg entry for the * transfer. */ #define SG_PTR_MASK 0xFFFFFFF8 /*16*/ uint16_t tag; /* Reused by Sequencer. */ /*18*/ uint8_t control; /* See SCB_CONTROL in aic79xx.reg for details */ /*19*/ uint8_t scsiid; /* * Selection out Id * Our Id (bits 0-3) Their ID (bits 4-7) */ /*20*/ uint8_t lun; /*21*/ uint8_t task_attribute; /*22*/ uint8_t cdb_len; /*23*/ uint8_t task_management; /*24*/ uint64_t dataptr; /*32*/ uint32_t datacnt; /* Byte 3 is spare. */ /*36*/ uint32_t sgptr; /*40*/ uint32_t hscb_busaddr; /*44*/ uint32_t next_hscb_busaddr; /********** Long lun field only downloaded for full 8 byte lun support ********/ /*48*/ uint8_t pkt_long_lun[8]; /******* Fields below are not Downloaded (Sequencer may use for scratch) ******/ /*56*/ uint8_t spare[8]; }; /************************ Kernel SCB Definitions ******************************/ /* * Some fields of the SCB are OS dependent. Here we collect the * definitions for elements that all OS platforms need to include * in there SCB definition. */ /* * Definition of a scatter/gather element as transferred to the controller. * The aic7xxx chips only support a 24bit length. We use the top byte of * the length to store additional address bits and a flag to indicate * that a given segment terminates the transfer. This gives us an * addressable range of 512GB on machines with 64bit PCI or with chips * that can support dual address cycles on 32bit PCI busses. */ struct ahd_dma_seg { uint32_t addr; uint32_t len; #define AHD_DMA_LAST_SEG 0x80000000 #define AHD_SG_HIGH_ADDR_MASK 0x7F000000 #define AHD_SG_LEN_MASK 0x00FFFFFF }; struct ahd_dma64_seg { uint64_t addr; uint32_t len; uint32_t pad; }; struct map_node { bus_dmamap_t dmamap; bus_addr_t busaddr; uint8_t *vaddr; SLIST_ENTRY(map_node) links; }; /* * The current state of this SCB. */ typedef enum { SCB_FLAG_NONE = 0x00000, SCB_TRANSMISSION_ERROR = 0x00001,/* * We detected a parity or CRC * error that has effected the * payload of the command. This * flag is checked when normal * status is returned to catch * the case of a target not * responding to our attempt * to report the error. */ SCB_OTHERTCL_TIMEOUT = 0x00002,/* * Another device was active * during the first timeout for * this SCB so we gave ourselves * an additional timeout period * in case it was hogging the * bus. */ SCB_DEVICE_RESET = 0x00004, SCB_SENSE = 0x00008, SCB_CDB32_PTR = 0x00010, SCB_RECOVERY_SCB = 0x00020, SCB_AUTO_NEGOTIATE = 0x00040,/* Negotiate to achieve goal. */ SCB_NEGOTIATE = 0x00080,/* Negotiation forced for command. */ SCB_ABORT = 0x00100, SCB_ACTIVE = 0x00200, SCB_TARGET_IMMEDIATE = 0x00400, SCB_PACKETIZED = 0x00800, SCB_EXPECT_PPR_BUSFREE = 0x01000, SCB_PKT_SENSE = 0x02000, SCB_CMDPHASE_ABORT = 0x04000, SCB_ON_COL_LIST = 0x08000, SCB_SILENT = 0x10000,/* * Be quiet about transmission type * errors. They are expected and we * don't want to upset the user. This * flag is typically used during DV. */ SCB_TIMEDOUT = 0x20000/* * SCB has timed out and is on the * timedout list. */ } scb_flag; struct scb { struct hardware_scb *hscb; union { SLIST_ENTRY(scb) sle; LIST_ENTRY(scb) le; TAILQ_ENTRY(scb) tqe; } links; union { SLIST_ENTRY(scb) sle; LIST_ENTRY(scb) le; TAILQ_ENTRY(scb) tqe; } links2; #define pending_links links2.le #define collision_links links2.le LIST_ENTRY(scb) timedout_links; struct scb *col_scb; aic_io_ctx_t io_ctx; struct ahd_softc *ahd_softc; scb_flag flags; #ifndef __linux__ bus_dmamap_t dmamap; #endif struct scb_platform_data *platform_data; struct map_node *hscb_map; struct map_node *sg_map; struct map_node *sense_map; void *sg_list; uint8_t *sense_data; bus_addr_t sg_list_busaddr; bus_addr_t sense_busaddr; u_int sg_count;/* How full ahd_dma_seg is */ #define AHD_MAX_LQ_CRC_ERRORS 5 u_int crc_retry_count; aic_timer_t io_timer; }; TAILQ_HEAD(scb_tailq, scb); LIST_HEAD(scb_list, scb); struct scb_data { /* * TAILQ of lists of free SCBs grouped by device * collision domains. */ struct scb_tailq free_scbs; /* * Per-device lists of SCBs whose tag ID would collide * with an already active tag on the device. */ struct scb_list free_scb_lists[AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT]; /* * SCBs that will not collide with any active device. */ struct scb_list any_dev_free_scb_list; /* * Mapping from tag to SCB. */ struct scb *scbindex[AHD_SCB_MAX]; u_int recovery_scbs; /* Transactions currently in recovery */ /* * "Bus" addresses of our data structures. */ bus_dma_tag_t hscb_dmat; /* dmat for our hardware SCB array */ bus_dma_tag_t sg_dmat; /* dmat for our sg segments */ bus_dma_tag_t sense_dmat; /* dmat for our sense buffers */ SLIST_HEAD(, map_node) hscb_maps; SLIST_HEAD(, map_node) sg_maps; SLIST_HEAD(, map_node) sense_maps; int scbs_left; /* unallocated scbs in head map_node */ int sgs_left; /* unallocated sgs in head map_node */ int sense_left; /* unallocated sense in head map_node */ uint16_t numscbs; uint16_t maxhscbs; /* Number of SCBs on the card */ uint8_t init_level; /* * How far we've initialized * this structure. */ }; /************************ Target Mode Definitions *****************************/ /* * Connection desciptor for select-in requests in target mode. */ struct target_cmd { uint8_t scsiid; /* Our ID and the initiator's ID */ uint8_t identify; /* Identify message */ uint8_t bytes[22]; /* * Bytes contains any additional message * bytes terminated by 0xFF. The remainder * is the cdb to execute. */ uint8_t cmd_valid; /* * When a command is complete, the firmware * will set cmd_valid to all bits set. * After the host has seen the command, * the bits are cleared. This allows us * to just peek at host memory to determine * if more work is complete. cmd_valid is on * an 8 byte boundary to simplify setting * it on aic7880 hardware which only has * limited direct access to the DMA FIFO. */ uint8_t pad[7]; }; /* * Number of events we can buffer up if we run out * of immediate notify ccbs. */ #define AHD_TMODE_EVENT_BUFFER_SIZE 8 struct ahd_tmode_event { uint8_t initiator_id; uint8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */ #define EVENT_TYPE_BUS_RESET 0xFF uint8_t event_arg; }; /* * Per enabled lun target mode state. * As this state is directly influenced by the host OS'es target mode * environment, we let the OS module define it. Forward declare the * structure here so we can store arrays of them, etc. in OS neutral * data structures. */ #ifdef AHD_TARGET_MODE struct ahd_tmode_lstate { struct cam_path *path; struct ccb_hdr_slist accept_tios; struct ccb_hdr_slist immed_notifies; struct ahd_tmode_event event_buffer[AHD_TMODE_EVENT_BUFFER_SIZE]; uint8_t event_r_idx; uint8_t event_w_idx; }; #else struct ahd_tmode_lstate; #endif /******************** Transfer Negotiation Datastructures *********************/ #define AHD_TRANS_CUR 0x01 /* Modify current neogtiation status */ #define AHD_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */ #define AHD_TRANS_GOAL 0x04 /* Modify negotiation goal */ #define AHD_TRANS_USER 0x08 /* Modify user negotiation settings */ #define AHD_PERIOD_10MHz 0x19 #define AHD_WIDTH_UNKNOWN 0xFF #define AHD_PERIOD_UNKNOWN 0xFF #define AHD_OFFSET_UNKNOWN 0xFF #define AHD_PPR_OPTS_UNKNOWN 0xFF /* * Transfer Negotiation Information. */ struct ahd_transinfo { uint8_t protocol_version; /* SCSI Revision level */ uint8_t transport_version; /* SPI Revision level */ uint8_t width; /* Bus width */ uint8_t period; /* Sync rate factor */ uint8_t offset; /* Sync offset */ uint8_t ppr_options; /* Parallel Protocol Request options */ }; /* * Per-initiator current, goal and user transfer negotiation information. */ struct ahd_initiator_tinfo { struct ahd_transinfo curr; struct ahd_transinfo goal; struct ahd_transinfo user; }; /* * Per enabled target ID state. * Pointers to lun target state as well as sync/wide negotiation information * for each initiator<->target mapping. For the initiator role we pretend * that we are the target and the targets are the initiators since the * negotiation is the same regardless of role. */ struct ahd_tmode_tstate { struct ahd_tmode_lstate* enabled_luns[AHD_NUM_LUNS]; struct ahd_initiator_tinfo transinfo[AHD_NUM_TARGETS]; /* * Per initiator state bitmasks. */ uint16_t auto_negotiate;/* Auto Negotiation Required */ uint16_t discenable; /* Disconnection allowed */ uint16_t tagenable; /* Tagged Queuing allowed */ }; /* * Points of interest along the negotiated transfer scale. */ #define AHD_SYNCRATE_160 0x8 #define AHD_SYNCRATE_PACED 0x8 #define AHD_SYNCRATE_DT 0x9 #define AHD_SYNCRATE_ULTRA2 0xa #define AHD_SYNCRATE_ULTRA 0xc #define AHD_SYNCRATE_FAST 0x19 #define AHD_SYNCRATE_MIN_DT AHD_SYNCRATE_FAST #define AHD_SYNCRATE_SYNC 0x32 #define AHD_SYNCRATE_MIN 0x60 #define AHD_SYNCRATE_ASYNC 0xFF #define AHD_SYNCRATE_MAX AHD_SYNCRATE_160 /* Safe and valid period for async negotiations. */ #define AHD_ASYNC_XFER_PERIOD 0x44 /* * In RevA, the synctable uses a 120MHz rate for the period * factor 8 and 160MHz for the period factor 7. The 120MHz * rate never made it into the official SCSI spec, so we must * compensate when setting the negotiation table for Rev A * parts. */ #define AHD_SYNCRATE_REVA_120 0x8 #define AHD_SYNCRATE_REVA_160 0x7 /***************************** Lookup Tables **********************************/ /* * Phase -> name and message out response * to parity errors in each phase table. */ struct ahd_phase_table_entry { uint8_t phase; uint8_t mesg_out; /* Message response to parity errors */ char *phasemsg; }; /************************** Serial EEPROM Format ******************************/ struct seeprom_config { /* * Per SCSI ID Configuration Flags */ uint16_t device_flags[16]; /* words 0-15 */ #define CFXFER 0x003F /* synchronous transfer rate */ #define CFXFER_ASYNC 0x3F #define CFQAS 0x0040 /* Negotiate QAS */ #define CFPACKETIZED 0x0080 /* Negotiate Packetized Transfers */ #define CFSTART 0x0100 /* send start unit SCSI command */ #define CFINCBIOS 0x0200 /* include in BIOS scan */ #define CFDISC 0x0400 /* enable disconnection */ #define CFMULTILUNDEV 0x0800 /* Probe multiple luns in BIOS scan */ #define CFWIDEB 0x1000 /* wide bus device */ #define CFHOSTMANAGED 0x8000 /* Managed by a RAID controller */ /* * BIOS Control Bits */ uint16_t bios_control; /* word 16 */ #define CFSUPREM 0x0001 /* support all removeable drives */ #define CFSUPREMB 0x0002 /* support removeable boot drives */ #define CFBIOSSTATE 0x000C /* BIOS Action State */ #define CFBS_DISABLED 0x00 #define CFBS_ENABLED 0x04 #define CFBS_DISABLED_SCAN 0x08 #define CFENABLEDV 0x0010 /* Perform Domain Validation */ #define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */ #define CFSPARITY 0x0040 /* SCSI parity */ #define CFEXTEND 0x0080 /* extended translation enabled */ #define CFBOOTCD 0x0100 /* Support Bootable CD-ROM */ #define CFMSG_LEVEL 0x0600 /* BIOS Message Level */ #define CFMSG_VERBOSE 0x0000 #define CFMSG_SILENT 0x0200 #define CFMSG_DIAG 0x0400 #define CFRESETB 0x0800 /* reset SCSI bus at boot */ /* UNUSED 0xf000 */ /* * Host Adapter Control Bits */ uint16_t adapter_control; /* word 17 */ #define CFAUTOTERM 0x0001 /* Perform Auto termination */ #define CFSTERM 0x0002 /* SCSI low byte termination */ #define CFWSTERM 0x0004 /* SCSI high byte termination */ #define CFSEAUTOTERM 0x0008 /* Ultra2 Perform secondary Auto Term*/ #define CFSELOWTERM 0x0010 /* Ultra2 secondary low term */ #define CFSEHIGHTERM 0x0020 /* Ultra2 secondary high term */ #define CFSTPWLEVEL 0x0040 /* Termination level control */ #define CFBIOSAUTOTERM 0x0080 /* Perform Auto termination */ #define CFTERM_MENU 0x0100 /* BIOS displays termination menu */ #define CFCLUSTERENB 0x8000 /* Cluster Enable */ /* * Bus Release Time, Host Adapter ID */ uint16_t brtime_id; /* word 18 */ #define CFSCSIID 0x000f /* host adapter SCSI ID */ /* UNUSED 0x00f0 */ #define CFBRTIME 0xff00 /* bus release time/PCI Latency Time */ /* * Maximum targets */ uint16_t max_targets; /* word 19 */ #define CFMAXTARG 0x00ff /* maximum targets */ #define CFBOOTLUN 0x0f00 /* Lun to boot from */ #define CFBOOTID 0xf000 /* Target to boot from */ uint16_t res_1[10]; /* words 20-29 */ uint16_t signature; /* BIOS Signature */ #define CFSIGNATURE 0x400 uint16_t checksum; /* word 31 */ }; /* * Vital Product Data used during POST and by the BIOS. */ struct vpd_config { uint8_t bios_flags; #define VPDMASTERBIOS 0x0001 #define VPDBOOTHOST 0x0002 uint8_t reserved_1[21]; uint8_t resource_type; uint8_t resource_len[2]; uint8_t resource_data[8]; uint8_t vpd_tag; uint16_t vpd_len; uint8_t vpd_keyword[2]; uint8_t length; uint8_t revision; uint8_t device_flags; uint8_t termnation_menus[2]; uint8_t fifo_threshold; uint8_t end_tag; uint8_t vpd_checksum; uint16_t default_target_flags; uint16_t default_bios_flags; uint16_t default_ctrl_flags; uint8_t default_irq; uint8_t pci_lattime; uint8_t max_target; uint8_t boot_lun; uint16_t signature; uint8_t reserved_2; uint8_t checksum; uint8_t reserved_3[4]; }; /****************************** Flexport Logic ********************************/ #define FLXADDR_TERMCTL 0x0 #define FLX_TERMCTL_ENSECHIGH 0x8 #define FLX_TERMCTL_ENSECLOW 0x4 #define FLX_TERMCTL_ENPRIHIGH 0x2 #define FLX_TERMCTL_ENPRILOW 0x1 #define FLXADDR_ROMSTAT_CURSENSECTL 0x1 #define FLX_ROMSTAT_SEECFG 0xF0 #define FLX_ROMSTAT_EECFG 0x0F #define FLX_ROMSTAT_SEE_93C66 0x00 #define FLX_ROMSTAT_SEE_NONE 0xF0 #define FLX_ROMSTAT_EE_512x8 0x0 #define FLX_ROMSTAT_EE_1MBx8 0x1 #define FLX_ROMSTAT_EE_2MBx8 0x2 #define FLX_ROMSTAT_EE_4MBx8 0x3 #define FLX_ROMSTAT_EE_16MBx8 0x4 #define CURSENSE_ENB 0x1 #define FLXADDR_FLEXSTAT 0x2 #define FLX_FSTAT_BUSY 0x1 #define FLXADDR_CURRENT_STAT 0x4 #define FLX_CSTAT_SEC_HIGH 0xC0 #define FLX_CSTAT_SEC_LOW 0x30 #define FLX_CSTAT_PRI_HIGH 0x0C #define FLX_CSTAT_PRI_LOW 0x03 #define FLX_CSTAT_MASK 0x03 #define FLX_CSTAT_SHIFT 2 #define FLX_CSTAT_OKAY 0x0 #define FLX_CSTAT_OVER 0x1 #define FLX_CSTAT_UNDER 0x2 #define FLX_CSTAT_INVALID 0x3 int ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf, u_int start_addr, u_int count, int bstream); int ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf, u_int start_addr, u_int count); int ahd_wait_seeprom(struct ahd_softc *ahd); int ahd_verify_vpd_cksum(struct vpd_config *vpd); int ahd_verify_cksum(struct seeprom_config *sc); int ahd_acquire_seeprom(struct ahd_softc *ahd); void ahd_release_seeprom(struct ahd_softc *ahd); /**************************** Message Buffer *********************************/ typedef enum { MSG_FLAG_NONE = 0x00, MSG_FLAG_EXPECT_PPR_BUSFREE = 0x01, MSG_FLAG_IU_REQ_CHANGED = 0x02, MSG_FLAG_EXPECT_IDE_BUSFREE = 0x04, MSG_FLAG_EXPECT_QASREJ_BUSFREE = 0x08, MSG_FLAG_PACKETIZED = 0x10 } ahd_msg_flags; typedef enum { MSG_TYPE_NONE = 0x00, MSG_TYPE_INITIATOR_MSGOUT = 0x01, MSG_TYPE_INITIATOR_MSGIN = 0x02, MSG_TYPE_TARGET_MSGOUT = 0x03, MSG_TYPE_TARGET_MSGIN = 0x04 } ahd_msg_type; typedef enum { MSGLOOP_IN_PROG, MSGLOOP_MSGCOMPLETE, MSGLOOP_TERMINATED } msg_loop_stat; /*********************** Software Configuration Structure *********************/ struct ahd_suspend_channel_state { uint8_t scsiseq; uint8_t sxfrctl0; uint8_t sxfrctl1; uint8_t simode0; uint8_t simode1; uint8_t seltimer; uint8_t seqctl; }; struct ahd_suspend_state { struct ahd_suspend_channel_state channel[2]; uint8_t optionmode; uint8_t dscommand0; uint8_t dspcistatus; /* hsmailbox */ uint8_t crccontrol1; uint8_t scbbaddr; /* Host and sequencer SCB counts */ uint8_t dff_thrsh; uint8_t *scratch_ram; uint8_t *btt; }; typedef void (*ahd_bus_intr_t)(struct ahd_softc *); typedef enum { AHD_MODE_DFF0, AHD_MODE_DFF1, AHD_MODE_CCHAN, AHD_MODE_SCSI, AHD_MODE_CFG, AHD_MODE_UNKNOWN } ahd_mode; #define AHD_MK_MSK(x) (0x01 << (x)) #define AHD_MODE_DFF0_MSK AHD_MK_MSK(AHD_MODE_DFF0) #define AHD_MODE_DFF1_MSK AHD_MK_MSK(AHD_MODE_DFF1) #define AHD_MODE_CCHAN_MSK AHD_MK_MSK(AHD_MODE_CCHAN) #define AHD_MODE_SCSI_MSK AHD_MK_MSK(AHD_MODE_SCSI) #define AHD_MODE_CFG_MSK AHD_MK_MSK(AHD_MODE_CFG) #define AHD_MODE_UNKNOWN_MSK AHD_MK_MSK(AHD_MODE_UNKNOWN) #define AHD_MODE_ANY_MSK (~0) typedef enum { AHD_SYSCTL_ROOT, AHD_SYSCTL_SUMMARY, AHD_SYSCTL_DEBUG, AHD_SYSCTL_NUMBER } ahd_sysctl_types_t; typedef enum { AHD_ERRORS_CORRECTABLE, AHD_ERRORS_UNCORRECTABLE, AHD_ERRORS_FATAL, AHD_ERRORS_NUMBER } ahd_sysctl_errors_t; #define AHD_CORRECTABLE_ERROR(sc) \ (((sc)->summerr[AHD_ERRORS_CORRECTABLE])++) #define AHD_UNCORRECTABLE_ERROR(sc) \ (((sc)->summerr[AHD_ERRORS_UNCORRECTABLE])++) #define AHD_FATAL_ERROR(sc) \ (((sc)->summerr[AHD_ERRORS_FATAL])++) typedef uint8_t ahd_mode_state; typedef void ahd_callback_t (void *); struct ahd_completion { uint16_t tag; uint8_t sg_status; uint8_t valid_tag; }; #define AIC_SCB_DATA(softc) (&(softc)->scb_data) struct ahd_softc { bus_space_tag_t tags[2]; bus_space_handle_t bshs[2]; #ifndef __linux__ bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */ #endif struct scb_data scb_data; struct hardware_scb *next_queued_hscb; struct map_node *next_queued_hscb_map; /* * SCBs that have been sent to the controller */ LIST_HEAD(, scb) pending_scbs; /* * SCBs whose timeout routine has been called. */ LIST_HEAD(, scb) timedout_scbs; /* * Current register window mode information. */ ahd_mode dst_mode; ahd_mode src_mode; /* * Saved register window mode information * used for restore on next unpause. */ ahd_mode saved_dst_mode; ahd_mode saved_src_mode; /* * Platform specific data. */ struct ahd_platform_data *platform_data; /* * Platform specific device information. */ aic_dev_softc_t dev_softc; /* * Bus specific device information. */ ahd_bus_intr_t bus_intr; /* * Target mode related state kept on a per enabled lun basis. * Targets that are not enabled will have null entries. * As an initiator, we keep one target entry for our initiator * ID to store our sync/wide transfer settings. */ struct ahd_tmode_tstate *enabled_targets[AHD_NUM_TARGETS]; /* * The black hole device responsible for handling requests for * disabled luns on enabled targets. */ struct ahd_tmode_lstate *black_hole; /* * Device instance currently on the bus awaiting a continue TIO * for a command that was not given the disconnect priveledge. */ struct ahd_tmode_lstate *pending_device; /* * Timer handles for timer driven callbacks. */ aic_timer_t reset_timer; aic_timer_t stat_timer; /* * Statistics. */ #define AHD_STAT_UPDATE_MS 250 #define AHD_STAT_BUCKETS 4 u_int cmdcmplt_bucket; uint32_t cmdcmplt_counts[AHD_STAT_BUCKETS]; uint32_t cmdcmplt_total; /* * Errors statistics and printouts. */ struct sysctl_ctx_list sysctl_ctx[AHD_SYSCTL_NUMBER]; struct sysctl_oid *sysctl_tree[AHD_SYSCTL_NUMBER]; u_int summerr[AHD_ERRORS_NUMBER]; /* * Card characteristics */ ahd_chip chip; ahd_feature features; ahd_bug bugs; ahd_flag flags; struct seeprom_config *seep_config; /* Command Queues */ struct ahd_completion *qoutfifo; uint16_t qoutfifonext; uint16_t qoutfifonext_valid_tag; uint16_t qinfifonext; uint16_t qinfifo[AHD_SCB_MAX]; /* * Our qfreeze count. The sequencer compares * this value with its own counter to determine * whether to allow selections to occur. */ uint16_t qfreeze_cnt; /* Values to store in the SEQCTL register for pause and unpause */ uint8_t unpause; uint8_t pause; /* Critical Section Data */ struct cs *critical_sections; u_int num_critical_sections; /* Buffer for handling packetized bitbucket. */ uint8_t *overrun_buf; /* Links for chaining softcs */ TAILQ_ENTRY(ahd_softc) links; /* Channel Names ('A', 'B', etc.) */ char channel; /* Initiator Bus ID */ uint8_t our_id; /* * Target incoming command FIFO. */ struct target_cmd *targetcmds; uint8_t tqinfifonext; /* * Cached verson of the hs_mailbox so we can avoid * pausing the sequencer during mailbox updates. */ uint8_t hs_mailbox; /* * Incoming and outgoing message handling. */ uint8_t send_msg_perror; ahd_msg_flags msg_flags; ahd_msg_type msg_type; uint8_t msgout_buf[12];/* Message we are sending */ uint8_t msgin_buf[12];/* Message we are receiving */ u_int msgout_len; /* Length of message to send */ u_int msgout_index; /* Current index in msgout */ u_int msgin_index; /* Current index in msgin */ /* * Mapping information for data structures shared * between the sequencer and kernel. */ bus_dma_tag_t parent_dmat; bus_dma_tag_t shared_data_dmat; struct map_node shared_data_map; /* Information saved through suspend/resume cycles */ struct ahd_suspend_state suspend_state; /* Number of enabled target mode device on this card */ u_int enabled_luns; /* Initialization level of this data structure */ u_int init_level; /* PCI cacheline size. */ u_int pci_cachesize; /* PCI-X capability offset. */ int pcix_ptr; /* IO Cell Parameters */ uint8_t iocell_opts[AHD_NUM_PER_DEV_ANNEXCOLS]; u_int stack_size; uint16_t *saved_stack; /* Per-Unit descriptive information */ const char *description; const char *bus_description; char *name; int unit; /* Selection Timer settings */ int seltime; /* * Interrupt coalescing settings. */ #define AHD_INT_COALESCING_TIMER_DEFAULT 250 /*us*/ #define AHD_INT_COALESCING_MAXCMDS_DEFAULT 10 #define AHD_INT_COALESCING_MAXCMDS_MAX 127 #define AHD_INT_COALESCING_MINCMDS_DEFAULT 5 #define AHD_INT_COALESCING_MINCMDS_MAX 127 #define AHD_INT_COALESCING_THRESHOLD_DEFAULT 2000 #define AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT 1000 u_int int_coalescing_timer; u_int int_coalescing_maxcmds; u_int int_coalescing_mincmds; u_int int_coalescing_threshold; u_int int_coalescing_stop_threshold; uint16_t user_discenable;/* Disconnection allowed */ uint16_t user_tagenable;/* Tagged Queuing allowed */ }; TAILQ_HEAD(ahd_softc_tailq, ahd_softc); extern struct ahd_softc_tailq ahd_tailq; /*************************** IO Cell Configuration ****************************/ #define AHD_PRECOMP_SLEW_INDEX \ (AHD_ANNEXCOL_PRECOMP_SLEW - AHD_ANNEXCOL_PER_DEV0) #define AHD_AMPLITUDE_INDEX \ (AHD_ANNEXCOL_AMPLITUDE - AHD_ANNEXCOL_PER_DEV0) #define AHD_SET_SLEWRATE(ahd, new_slew) \ do { \ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_SLEWRATE_MASK; \ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |= \ (((new_slew) << AHD_SLEWRATE_SHIFT) & AHD_SLEWRATE_MASK); \ } while (0) #define AHD_SET_PRECOMP(ahd, new_pcomp) \ do { \ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK; \ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |= \ (((new_pcomp) << AHD_PRECOMP_SHIFT) & AHD_PRECOMP_MASK); \ } while (0) #define AHD_SET_AMPLITUDE(ahd, new_amp) \ do { \ (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] &= ~AHD_AMPLITUDE_MASK; \ (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] |= \ (((new_amp) << AHD_AMPLITUDE_SHIFT) & AHD_AMPLITUDE_MASK); \ } while (0) /************************ Active Device Information ***************************/ typedef enum { ROLE_UNKNOWN, ROLE_INITIATOR, ROLE_TARGET } role_t; struct ahd_devinfo { int our_scsiid; int target_offset; uint16_t target_mask; u_int target; u_int lun; char channel; role_t role; /* * Only guaranteed to be correct if not * in the busfree state. */ }; /****************************** PCI Structures ********************************/ #define AHD_PCI_IOADDR0 PCIR_BAR(0) /* I/O BAR*/ #define AHD_PCI_MEMADDR PCIR_BAR(1) /* Memory BAR */ #define AHD_PCI_IOADDR1 PCIR_BAR(3) /* Second I/O BAR */ typedef int (ahd_device_setup_t)(struct ahd_softc *); struct ahd_pci_identity { uint64_t full_id; uint64_t id_mask; char *name; ahd_device_setup_t *setup; }; extern struct ahd_pci_identity ahd_pci_ident_table []; extern const u_int ahd_num_pci_devs; /*************************** Function Declarations ****************************/ /******************************************************************************/ void ahd_reset_cmds_pending(struct ahd_softc *ahd); u_int ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl); void ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int busyid); static __inline void ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl); static __inline void ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl) { ahd_busy_tcl(ahd, tcl, SCB_LIST_NULL); } /***************************** PCI Front End *********************************/ struct ahd_pci_identity *ahd_find_pci_device(aic_dev_softc_t); int ahd_pci_config(struct ahd_softc *, struct ahd_pci_identity *); int ahd_pci_test_register_access(struct ahd_softc *); /************************** SCB and SCB queue management **********************/ int ahd_probe_scbs(struct ahd_softc *); void ahd_qinfifo_requeue_tail(struct ahd_softc *ahd, struct scb *scb); int ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, int target, char channel, int lun, u_int tag, role_t role); /****************************** Initialization ********************************/ struct ahd_softc *ahd_alloc(void *platform_arg, char *name); int ahd_softc_init(struct ahd_softc *); void ahd_controller_info(struct ahd_softc *ahd, char *buf); int ahd_init(struct ahd_softc *ahd); int ahd_default_config(struct ahd_softc *ahd); int ahd_parse_vpddata(struct ahd_softc *ahd, struct vpd_config *vpd); int ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc); void ahd_intr_enable(struct ahd_softc *ahd, int enable); void ahd_update_coalescing_values(struct ahd_softc *ahd, u_int timer, u_int maxcmds, u_int mincmds); void ahd_enable_coalescing(struct ahd_softc *ahd, int enable); void ahd_pause_and_flushwork(struct ahd_softc *ahd); int ahd_suspend(struct ahd_softc *ahd); int ahd_resume(struct ahd_softc *ahd); void ahd_softc_insert(struct ahd_softc *); void ahd_set_unit(struct ahd_softc *, int); void ahd_set_name(struct ahd_softc *, char *); struct scb *ahd_get_scb(struct ahd_softc *ahd, u_int col_idx); void ahd_free_scb(struct ahd_softc *ahd, struct scb *scb); int ahd_alloc_scbs(struct ahd_softc *ahd); void ahd_free(struct ahd_softc *ahd); int ahd_reset(struct ahd_softc *ahd, int reinit); void ahd_shutdown(void *arg); int ahd_write_flexport(struct ahd_softc *ahd, u_int addr, u_int value); int ahd_read_flexport(struct ahd_softc *ahd, u_int addr, uint8_t *value); int ahd_wait_flexport(struct ahd_softc *ahd); /*************************** Interrupt Services *******************************/ void ahd_pci_intr(struct ahd_softc *ahd); void ahd_clear_intstat(struct ahd_softc *ahd); void ahd_flush_qoutfifo(struct ahd_softc *ahd); void ahd_run_qoutfifo(struct ahd_softc *ahd); #ifdef AHD_TARGET_MODE void ahd_run_tqinfifo(struct ahd_softc *ahd, int paused); #endif void ahd_handle_hwerrint(struct ahd_softc *ahd); void ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat); void ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat); void ahd_clear_critical_section(struct ahd_softc *ahd); /***************************** Error Recovery *********************************/ typedef enum { SEARCH_COMPLETE, SEARCH_COUNT, SEARCH_REMOVE, SEARCH_PRINT } ahd_search_action; void ahd_done_with_status(struct ahd_softc *ahd, struct scb *scb, uint32_t status); int ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, role_t role, uint32_t status, ahd_search_action action); int ahd_search_disc_list(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, int stop_on_first, int remove, int save_state); void ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb); int ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset); int ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, role_t role, uint32_t status); void ahd_restart(struct ahd_softc *ahd); void ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo); void ahd_handle_scb_status(struct ahd_softc *ahd, struct scb *scb); void ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb); void ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb); void ahd_timeout(struct scb *scb); void ahd_recover_commands(struct ahd_softc *ahd); /*************************** Utility Functions ********************************/ struct ahd_phase_table_entry* ahd_lookup_phase_entry(int phase); void ahd_compile_devinfo(struct ahd_devinfo *devinfo, u_int our_id, u_int target, u_int lun, char channel, role_t role); /************************** Transfer Negotiation ******************************/ void ahd_find_syncrate(struct ahd_softc *ahd, u_int *period, u_int *ppr_options, u_int maxsync); void ahd_validate_offset(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo, u_int period, u_int *offset, int wide, role_t role); void ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo, u_int *bus_width, role_t role); /* * Negotiation types. These are used to qualify if we should renegotiate * even if our goal and current transport parameters are identical. */ typedef enum { AHD_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */ AHD_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */ AHD_NEG_ALWAYS /* Renegotiat even if goal is async. */ } ahd_neg_type; int ahd_update_neg_request(struct ahd_softc*, struct ahd_devinfo*, struct ahd_tmode_tstate*, struct ahd_initiator_tinfo*, ahd_neg_type); void ahd_set_width(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, u_int width, u_int type, int paused); void ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, u_int period, u_int offset, u_int ppr_options, u_int type, int paused); typedef enum { AHD_QUEUE_NONE, AHD_QUEUE_BASIC, AHD_QUEUE_TAGGED } ahd_queue_alg; void ahd_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, ahd_queue_alg alg); /**************************** Target Mode *************************************/ #ifdef AHD_TARGET_MODE void ahd_send_lstate_events(struct ahd_softc *, struct ahd_tmode_lstate *); void ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb); cam_status ahd_find_tmode_devs(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb, struct ahd_tmode_tstate **tstate, struct ahd_tmode_lstate **lstate, int notfound_failure); #ifndef AHD_TMODE_ENABLE #define AHD_TMODE_ENABLE 0 #endif #endif /******************************* Debug ***************************************/ #ifdef AHD_DEBUG extern uint32_t ahd_debug; #define AHD_SHOW_MISC 0x00001 #define AHD_SHOW_SENSE 0x00002 #define AHD_SHOW_RECOVERY 0x00004 #define AHD_DUMP_SEEPROM 0x00008 #define AHD_SHOW_TERMCTL 0x00010 #define AHD_SHOW_MEMORY 0x00020 #define AHD_SHOW_MESSAGES 0x00040 #define AHD_SHOW_MODEPTR 0x00080 #define AHD_SHOW_SELTO 0x00100 #define AHD_SHOW_FIFOS 0x00200 #define AHD_SHOW_QFULL 0x00400 #define AHD_SHOW_DV 0x00800 #define AHD_SHOW_MASKED_ERRORS 0x01000 #define AHD_SHOW_QUEUE 0x02000 #define AHD_SHOW_TQIN 0x04000 #define AHD_SHOW_SG 0x08000 #define AHD_SHOW_INT_COALESCING 0x10000 #define AHD_DEBUG_SEQUENCER 0x20000 #endif void ahd_print_scb(struct scb *scb); void ahd_print_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo); void ahd_dump_sglist(struct scb *scb); void ahd_dump_all_cards_state(void); void ahd_dump_card_state(struct ahd_softc *ahd); int ahd_print_register(ahd_reg_parse_entry_t *table, u_int num_entries, const char *name, u_int address, u_int value, u_int *cur_column, u_int wrap_point); void ahd_dump_scbs(struct ahd_softc *ahd); #endif /* _AIC79XX_H_ */ diff --git a/sys/dev/gpio/gpiopps.c b/sys/dev/gpio/gpiopps.c index 8a6f1a6a3f6b..4700acf19bcd 100644 --- a/sys/dev/gpio/gpiopps.c +++ b/sys/dev/gpio/gpiopps.c @@ -1,295 +1,295 @@ /*- * Copyright (c) 2016 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 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 "opt_platform.h" #ifdef FDT #include static struct ofw_compat_data compat_data[] = { {"pps-gpio", 1}, {NULL, 0} }; SIMPLEBUS_PNP_INFO(compat_data); #endif /* FDT */ static devclass_t pps_devclass; struct pps_softc { device_t dev; gpio_pin_t gpin; void *ihandler; struct resource *ires; int irid; struct cdev *pps_cdev; struct pps_state pps_state; struct mtx pps_mtx; bool falling_edge; }; #define PPS_CDEV_NAME "gpiopps" static int gpiopps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct pps_softc *sc = dev->si_drv1; /* We can't be unloaded while open, so mark ourselves BUSY. */ mtx_lock(&sc->pps_mtx); if (device_get_state(sc->dev) < DS_BUSY) { device_busy(sc->dev); } mtx_unlock(&sc->pps_mtx); return 0; } static int gpiopps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct pps_softc *sc = dev->si_drv1; /* * Un-busy on last close. We rely on the vfs counting stuff to only call * this routine on last-close, so we don't need any open-count logic. */ mtx_lock(&sc->pps_mtx); device_unbusy(sc->dev); mtx_unlock(&sc->pps_mtx); return 0; } static int gpiopps_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct pps_softc *sc = dev->si_drv1; int err; /* Let the kernel do the heavy lifting for ioctl. */ mtx_lock(&sc->pps_mtx); err = pps_ioctl(cmd, data, &sc->pps_state); mtx_unlock(&sc->pps_mtx); return err; } static struct cdevsw pps_cdevsw = { .d_version = D_VERSION, .d_open = gpiopps_open, .d_close = gpiopps_close, .d_ioctl = gpiopps_ioctl, .d_name = PPS_CDEV_NAME, }; static int gpiopps_ifltr(void *arg) { struct pps_softc *sc = arg; /* * There is no locking here by design... The kernel cleverly captures * the current time into an area of the pps_state structure which is * written only by the pps_capture() routine and read only by the * pps_event() routine. We don't need lock-based management of access * to the capture area because we have time-based access management: we - * can't be reading and writing concurently because we can't be running + * can't be reading and writing concurrently because we can't be running * both the threaded and filter handlers concurrently (because a new * hardware interrupt can't happen until the threaded handler for the * current interrupt exits, after which the system does the EOI that * enables a new hardware interrupt). */ pps_capture(&sc->pps_state); return (FILTER_SCHEDULE_THREAD); } static void gpiopps_ithrd(void *arg) { struct pps_softc *sc = arg; /* * Go create a pps event from the data captured in the filter handler. * * Note that we DO need locking here, unlike the case with the filter * handler. The pps_event() routine updates the non-capture part of the * pps_state structure, and the ioctl() code could be accessing that * data right now in a non-interrupt context, so we need an interlock. */ mtx_lock(&sc->pps_mtx); pps_event(&sc->pps_state, PPS_CAPTUREASSERT); mtx_unlock(&sc->pps_mtx); } static int gpiopps_detach(device_t dev) { struct pps_softc *sc = device_get_softc(dev); if (sc->pps_cdev != NULL) destroy_dev(sc->pps_cdev); if (sc->ihandler != NULL) bus_teardown_intr(dev, sc->ires, sc->ihandler); if (sc->ires != NULL) bus_release_resource(dev, SYS_RES_IRQ, sc->irid, sc->ires); if (sc->gpin != NULL) gpiobus_release_pin(GPIO_GET_BUS(sc->gpin->dev), sc->gpin->pin); return (0); } #ifdef FDT static int gpiopps_fdt_attach(device_t dev) { struct pps_softc *sc; struct make_dev_args devargs; phandle_t node; uint32_t edge, pincaps; int err; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->pps_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Initialize the pps_state struct. */ sc->pps_state.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; sc->pps_state.driver_abi = PPS_ABI_VERSION; sc->pps_state.driver_mtx = &sc->pps_mtx; pps_init_abi(&sc->pps_state); /* Check which edge we're configured to capture (default is rising). */ if (ofw_bus_has_prop(dev, "assert-falling-edge")) edge = GPIO_INTR_EDGE_FALLING; else edge = GPIO_INTR_EDGE_RISING; /* * Look up the configured gpio pin and ensure it can be configured for * the interrupt mode we need. */ node = ofw_bus_get_node(dev); if ((err = gpio_pin_get_by_ofw_idx(dev, node, 0, &sc->gpin)) != 0) { device_printf(dev, "Cannot obtain gpio pin\n"); return (err); } device_printf(dev, "PPS input on %s pin %u\n", device_get_nameunit(sc->gpin->dev), sc->gpin->pin); if ((err = gpio_pin_getcaps(sc->gpin, &pincaps)) != 0) { device_printf(dev, "Cannot query capabilities of gpio pin\n"); gpiopps_detach(dev); return (err); } if ((pincaps & edge) == 0) { device_printf(dev, "Pin cannot be configured for the requested signal edge\n"); gpiopps_detach(dev); return (ENOTSUP); } /* * Transform our 'gpios' property into an interrupt resource and set up * the interrupt. */ if ((sc->ires = gpio_alloc_intr_resource(dev, &sc->irid, RF_ACTIVE, sc->gpin, edge)) == NULL) { device_printf(dev, "Cannot allocate an IRQ for the GPIO\n"); gpiopps_detach(dev); return (err); } err = bus_setup_intr(dev, sc->ires, INTR_TYPE_CLK | INTR_MPSAFE, gpiopps_ifltr, gpiopps_ithrd, sc, &sc->ihandler); if (err != 0) { device_printf(dev, "Unable to setup pps irq handler\n"); gpiopps_detach(dev); return (err); } /* Create the RFC 2783 pps-api cdev. */ make_dev_args_init(&devargs); devargs.mda_devsw = &pps_cdevsw; devargs.mda_uid = UID_ROOT; devargs.mda_gid = GID_WHEEL; devargs.mda_mode = 0660; devargs.mda_si_drv1 = sc; err = make_dev_s(&devargs, &sc->pps_cdev, PPS_CDEV_NAME "%d", device_get_unit(dev)); if (err != 0) { device_printf(dev, "Unable to create pps cdev\n"); gpiopps_detach(dev); return (err); } return (0); } static int gpiopps_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "GPIO PPS"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static device_method_t pps_fdt_methods[] = { DEVMETHOD(device_probe, gpiopps_fdt_probe), DEVMETHOD(device_attach, gpiopps_fdt_attach), DEVMETHOD(device_detach, gpiopps_detach), DEVMETHOD_END }; static driver_t pps_fdt_driver = { "gpiopps", pps_fdt_methods, sizeof(struct pps_softc), }; DRIVER_MODULE(gpiopps, simplebus, pps_fdt_driver, pps_devclass, 0, 0); #endif /* FDT */