Index: projects/ppc64/contrib/expat =================================================================== --- projects/ppc64/contrib/expat (revision 204271) +++ projects/ppc64/contrib/expat (revision 204272) Property changes on: projects/ppc64/contrib/expat ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/expat:r204217-204271 Index: projects/ppc64/contrib/groff =================================================================== --- projects/ppc64/contrib/groff (revision 204271) +++ projects/ppc64/contrib/groff (revision 204272) Property changes on: projects/ppc64/contrib/groff ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/groff:r204217-204271 Index: projects/ppc64/contrib/one-true-awk =================================================================== --- projects/ppc64/contrib/one-true-awk (revision 204271) +++ projects/ppc64/contrib/one-true-awk (revision 204272) Property changes on: projects/ppc64/contrib/one-true-awk ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/one-true-awk:r204217-204271 Index: projects/ppc64/gnu/usr.bin/diff/Makefile =================================================================== --- projects/ppc64/gnu/usr.bin/diff/Makefile (revision 204271) +++ projects/ppc64/gnu/usr.bin/diff/Makefile (revision 204272) @@ -1,36 +1,36 @@ # $FreeBSD$ DIFFSRC=${.CURDIR}/../../../contrib/diff/src .PATH: ${DIFFSRC} \ ${.CURDIR}/../../../contrib/diff/lib \ ${.CURDIR}/../../../contrib/diff/man PROG= diff SRCS= analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \ normal.c side.c util.c \ xmalloc.c strtoumax.c cmpbuf.c exitfail.c error.c quotesys.c \ strftime.c c-stack.c basename.c exclude.c hard-locale.c \ file-type.c posixver.c prepargs.c version-etc.c # Important for ctype macros! CFLAGS+=-funsigned-char CFLAGS+=-DHAVE_CONFIG_H CFLAGS+=-DPR_PROGRAM=\"/usr/bin/pr\" CFLAGS+=-I${DESTDIR}/usr/include/gnu CFLAGS+=-I${.CURDIR}/../../../contrib/diff CFLAGS+=-I${.CURDIR}/../../../contrib/diff/src CFLAGS+=-I${.CURDIR}/../../../contrib/diff/lib SUBDIR+=doc DPADD= ${LIBGNUREGEX} LDADD= -lgnuregex .for f in diff.c context.c ${f}: ${DIFFSRC}/${f} ${.CURDIR}/${f}.diff - patch -s -b .orig -o ${.TARGET} < ${.CURDIR}/${f}.diff ${DIFFSRC}/${f} + patch -s -o ${.TARGET} < ${.CURDIR}/${f}.diff ${DIFFSRC}/${f} CLEANFILES+= ${f} .endfor .include Index: projects/ppc64/gnu/usr.bin/diff3/Makefile =================================================================== --- projects/ppc64/gnu/usr.bin/diff3/Makefile (revision 204271) +++ projects/ppc64/gnu/usr.bin/diff3/Makefile (revision 204272) @@ -1,27 +1,27 @@ # $FreeBSD$ DIFFSRC=${.CURDIR}/../../../contrib/diff/src .PATH: ${DIFFSRC} \ ${.CURDIR}/../../../contrib/diff/lib \ ${.CURDIR}/../../../contrib/diff/man PROG= diff3 SRCS= diff3.c version-etc.c \ xmalloc.c error.c c-stack.c exitfail.c cmpbuf.c # Important for ctype macros! CFLAGS+=-funsigned-char CFLAGS+=-I${.CURDIR}/../../../contrib/diff/ CFLAGS+=-I${.CURDIR}/../../../contrib/diff/src CFLAGS+=-I${.CURDIR}/../../../contrib/diff/lib CFLAGS+=-DHAVE_CONFIG_H CFLAGS+=-DDEFAULT_DIFF_PROGRAM=\"/usr/bin/diff\" .for f in diff3.c ${f}: ${DIFFSRC}/${f} ${.CURDIR}/${f}.diff - patch -s -b .orig -o ${.TARGET} < ${.CURDIR}/${f}.diff ${DIFFSRC}/${f} + patch -s -o ${.TARGET} < ${.CURDIR}/${f}.diff ${DIFFSRC}/${f} CLEANFILES+= ${f} .endfor .include Index: projects/ppc64/gnu/usr.bin/sdiff/Makefile =================================================================== --- projects/ppc64/gnu/usr.bin/sdiff/Makefile (revision 204271) +++ projects/ppc64/gnu/usr.bin/sdiff/Makefile (revision 204272) @@ -1,28 +1,28 @@ # $FreeBSD$ DIFFSRC=${.CURDIR}/../../../contrib/diff/src .PATH: ${DIFFSRC} \ ${.CURDIR}/../../../contrib/diff/lib \ ${.CURDIR}/../../../contrib/diff/man PROG= sdiff SRCS= sdiff.c version-etc.c \ error.c xmalloc.c c-stack.c basename.c strtoumax.c \ exitfail.c # Important for ctype macros! CFLAGS+=-funsigned-char CFLAGS+=-I${.CURDIR}/../../../contrib/diff CFLAGS+=-I${.CURDIR}/../../../contrib/diff/src CFLAGS+=-I${.CURDIR}/../../../contrib/diff/lib CFLAGS+=-DHAVE_CONFIG_H CFLAGS+=-DDEFAULT_DIFF_PROGRAM=\"/usr/bin/diff\" .for f in sdiff.c ${f}: ${DIFFSRC}/${f} ${.CURDIR}/${f}.diff - patch -s -b .orig -o ${.TARGET} < ${.CURDIR}/${f}.diff ${DIFFSRC}/${f} + patch -s -o ${.TARGET} < ${.CURDIR}/${f}.diff ${DIFFSRC}/${f} CLEANFILES+= ${f} .endfor .include Index: projects/ppc64/lib/libz =================================================================== --- projects/ppc64/lib/libz (revision 204271) +++ projects/ppc64/lib/libz (revision 204272) Property changes on: projects/ppc64/lib/libz ___________________________________________________________________ Added: svn:mergeinfo ## -0,0 +0,3 ## Merged /vendor/libz/dist:r17652-204238 Merged /vendor/resolver/dist/lib/libz:r1540-186085 Merged /head/lib/libz:r195651-204271 Index: projects/ppc64/share/man/man4/man4.powerpc/Makefile =================================================================== --- projects/ppc64/share/man/man4/man4.powerpc/Makefile (revision 204271) +++ projects/ppc64/share/man/man4/man4.powerpc/Makefile (revision 204272) @@ -1,16 +1,17 @@ # $FreeBSD$ MAN= adb.4 \ akbd.4 \ ams.4 \ bm.4 \ cuda.4 \ pmu.4 \ powermac_nvram.4 \ + smu.4 \ snd_ai2s.4 \ snd_davbus.4 \ tsec.4 MANSUBDIR=/powerpc .include Index: projects/ppc64/share/man/man4/man4.powerpc/smu.4 =================================================================== --- projects/ppc64/share/man/man4/man4.powerpc/smu.4 (nonexistent) +++ projects/ppc64/share/man/man4/man4.powerpc/smu.4 (revision 204272) @@ -0,0 +1,125 @@ +.\"- +.\" Copyright (c) 2010 Nathan Whitehorn +.\" 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$ +.\" +.Dd February 22, 2010 +.Dt SMU 4 +.Os +.Sh NAME +.Nm smu +.Nd Apple System Management Unit Driver +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device smu" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for the System Management Unit (SMU) found in many +Apple G5 systems. +This includes most Power Macintosh G5 and all iMac G5 systems. +.Pp +The Apple SMU controller provides software power management and thermal +control functionality, and is responsible for managing system cooling +devices. +.Sh HARDWARE +Chips supported by the +.Nm +driver include: +.Pp +.Bl -bullet -compact +.It +Apple System Management Unit +.El +.Sh THERMAL MANAGEMENT +The +.Nm +driver provides basic automatic thermal management. Without a userspace +daemon providing more advanced control, the driver will attempt to maintain +system temperatures in a conservative range through coarse-grained control of +system cooling devices (see below). Automatic kernel-level thermal control +will take over if more than 3 seconds elapses between userspace cooling +setting adjustments. +.Sh SYSCTL VARIABLES +The +.Nm +driver provides power management services and thermal readout through a +sysctl interface. +The following sysctls can be used to control the +power management behavior and to examine current system power and +thermal conditions. +.Bl -tag -width indent +.It Va dev.smu.%d.server_mode +Restart after power failure behavior (1 causes system to reboot after power +cut, 0 causes system to remain off). +.It Va dev.smu.%d.target_temp +Target system temperature, in degrees Celsius. The +.Nm +driver will attempt to adjust fans to maintain the temperature of the +warmest component in the system at or below this level. +.It Va dev.smu.%d.critical_temp +System critical temperature, in degrees Celsius. If any component in +the system exceeds this temperature, the machine will be shut down within +500 ms. +.It Va dev.smu.%d.fans.%s.minrpm +Minimum allowed speed for this fan. +.It Va dev.smu.%d.fans.%s.maxrpm +Maximum allowed speed for this fan. +.It Va dev.smu.%d.fans.%s.rpm +Current speed for this fan. The fan speed can be adjusted by changing this +sysctl. If more than 3 seconds elapses between fan speed adjustments, the +kernel will resume automatic control of the fan. +.It Va dev.smu.%d.sensors.%s +Current reading from this sensor. Four sensor types are supported. Temperature +sensors are in units of degrees Celsius, current sensors in milliamps, voltage +sensors in millivolts, and power sensors in milliwatts. +.El +.Sh LED INTERFACE +The +.Nm +driver provides an +.Xr led 4 +annunciator interface at +.Pa /dev/led/sleepled . +.Sh SEE ALSO +.Xr acpi 4 , +.Xr pmu 4 , +.Xr led 4 +.Sh HISTORY +The +.Nm +device driver appeared in +.Fx 8.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Nathan Whitehorn +.Aq nwhitehorn@FreeBSD.org . Property changes on: projects/ppc64/share/man/man4/man4.powerpc/smu.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/ppc64/sys/cam/ata/ata_xpt.c =================================================================== --- projects/ppc64/sys/cam/ata/ata_xpt.c (revision 204271) +++ projects/ppc64/sys/cam/ata/ata_xpt.c (revision 204272) @@ -1,1643 +1,1724 @@ /*- * Copyright (c) 2009 Alexander Motin * 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, immediately at the beginning of the file. * 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 #ifdef PC98 #include /* geometry translation */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for xpt_print below */ #include "opt_cam.h" struct ata_quirk_entry { struct scsi_inquiry_pattern inq_pat; u_int8_t quirks; #define CAM_QUIRK_MAXTAGS 0x01 u_int maxtags; }; static periph_init_t probe_periph_init; static struct periph_driver probe_driver = { probe_periph_init, "aprobe", TAILQ_HEAD_INITIALIZER(probe_driver.units), /* generation */ 0, CAM_PERIPH_DRV_EARLY }; PERIPHDRIVER_DECLARE(aprobe, probe_driver); typedef enum { PROBE_RESET, PROBE_IDENTIFY, PROBE_SPINUP, PROBE_SETMODE, PROBE_SET_MULTI, PROBE_INQUIRY, PROBE_FULL_INQUIRY, PROBE_PM_PID, PROBE_PM_PRV, PROBE_INVALID } probe_action; static char *probe_action_text[] = { "PROBE_RESET", "PROBE_IDENTIFY", "PROBE_SPINUP", "PROBE_SETMODE", "PROBE_SET_MULTI", "PROBE_INQUIRY", "PROBE_FULL_INQUIRY", "PROBE_PM_PID", "PROBE_PM_PRV", "PROBE_INVALID" }; #define PROBE_SET_ACTION(softc, newaction) \ do { \ char **text; \ text = probe_action_text; \ CAM_DEBUG((softc)->periph->path, CAM_DEBUG_INFO, \ ("Probe %s to %s\n", text[(softc)->action], \ text[(newaction)])); \ (softc)->action = (newaction); \ } while(0) typedef enum { PROBE_NO_ANNOUNCE = 0x04 } probe_flags; typedef struct { TAILQ_HEAD(, ccb_hdr) request_ccbs; struct ata_params ident_data; probe_action action; probe_flags flags; uint32_t pm_pid; uint32_t pm_prv; int restart; int spinup; struct cam_periph *periph; } probe_softc; static struct ata_quirk_entry ata_quirk_table[] = { { /* Default tagged queuing parameters for all devices */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, /*vendor*/"*", /*product*/"*", /*revision*/"*" }, /*quirks*/0, /*maxtags*/0 }, }; static const int ata_quirk_table_size = sizeof(ata_quirk_table) / sizeof(*ata_quirk_table); static cam_status proberegister(struct cam_periph *periph, void *arg); static void probeschedule(struct cam_periph *probe_periph); static void probestart(struct cam_periph *periph, union ccb *start_ccb); //static void proberequestdefaultnegotiation(struct cam_periph *periph); //static int proberequestbackoff(struct cam_periph *periph, // struct cam_ed *device); static void probedone(struct cam_periph *periph, union ccb *done_ccb); static void probecleanup(struct cam_periph *periph); static void ata_find_quirk(struct cam_ed *device); static void ata_scan_bus(struct cam_periph *periph, union ccb *ccb); static void ata_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *ccb); static void xptscandone(struct cam_periph *periph, union ccb *done_ccb); static struct cam_ed * ata_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); static void ata_device_transport(struct cam_path *path); static void ata_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update); static void ata_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); static void ata_action(union ccb *start_ccb); +static void ata_announce_periph(struct cam_periph *periph); static struct xpt_xport ata_xport = { .alloc_device = ata_alloc_device, .action = ata_action, .async = ata_dev_async, + .announce = ata_announce_periph, }; struct xpt_xport * ata_get_xport(void) { return (&ata_xport); } static void probe_periph_init() { } static cam_status proberegister(struct cam_periph *periph, void *arg) { union ccb *request_ccb; /* CCB representing the probe request */ cam_status status; probe_softc *softc; request_ccb = (union ccb *)arg; if (periph == NULL) { printf("proberegister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (request_ccb == NULL) { printf("proberegister: no probe CCB, " "can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (probe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_ZERO | M_NOWAIT); if (softc == NULL) { printf("proberegister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } TAILQ_INIT(&softc->request_ccbs); TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); softc->flags = 0; periph->softc = softc; softc->periph = periph; softc->action = PROBE_INVALID; status = cam_periph_acquire(periph); if (status != CAM_REQ_CMP) { return (status); } /* * Ensure nobody slip in until probe finish. */ cam_freeze_devq_arg(periph->path, RELSIM_RELEASE_RUNLEVEL, CAM_RL_XPT + 1); probeschedule(periph); return(CAM_REQ_CMP); } static void probeschedule(struct cam_periph *periph) { union ccb *ccb; probe_softc *softc; softc = (probe_softc *)periph->softc; ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) || periph->path->device->protocol == PROTO_SATAPM) PROBE_SET_ACTION(softc, PROBE_RESET); else PROBE_SET_ACTION(softc, PROBE_IDENTIFY); if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE) softc->flags |= PROBE_NO_ANNOUNCE; else softc->flags &= ~PROBE_NO_ANNOUNCE; xpt_schedule(periph, CAM_PRIORITY_XPT); } static void probestart(struct cam_periph *periph, union ccb *start_ccb) { struct ccb_trans_settings cts; struct ccb_ataio *ataio; struct ccb_scsiio *csio; probe_softc *softc; struct cam_path *path; struct ata_params *ident_buf; CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n")); softc = (probe_softc *)periph->softc; path = start_ccb->ccb_h.path; ataio = &start_ccb->ataio; csio = &start_ccb->csio; ident_buf = &periph->path->device->ident_data; if (softc->restart) { softc->restart = 0; if ((path->device->flags & CAM_DEV_UNCONFIGURED) || path->device->protocol == PROTO_SATAPM) softc->action = PROBE_RESET; else softc->action = PROBE_IDENTIFY; } switch (softc->action) { case PROBE_RESET: cam_fill_ataio(ataio, 0, probedone, /*flags*/CAM_DIR_NONE, 0, /*data_ptr*/NULL, /*dxfer_len*/0, 15 * 1000); ata_reset_cmd(ataio); break; case PROBE_IDENTIFY: cam_fill_ataio(ataio, 1, probedone, /*flags*/CAM_DIR_IN, 0, /*data_ptr*/(u_int8_t *)&softc->ident_data, /*dxfer_len*/sizeof(softc->ident_data), 30 * 1000); if (periph->path->device->protocol == PROTO_ATA) ata_28bit_cmd(ataio, ATA_ATA_IDENTIFY, 0, 0, 0); else ata_28bit_cmd(ataio, ATA_ATAPI_IDENTIFY, 0, 0, 0); break; case PROBE_SPINUP: if (bootverbose) xpt_print(path, "Spinning up device\n"); cam_fill_ataio(ataio, 1, probedone, /*flags*/CAM_DIR_NONE | CAM_HIGH_POWER, 0, /*data_ptr*/NULL, /*dxfer_len*/0, 30 * 1000); ata_28bit_cmd(ataio, ATA_SETFEATURES, ATA_SF_PUIS_SPINUP, 0, 0); break; case PROBE_SETMODE: { int mode, wantmode; mode = 0; /* Fetch user modes from SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_USER_SETTINGS; xpt_action((union ccb *)&cts); if (path->device->transport == XPORT_ATA) { if (cts.xport_specific.ata.valid & CTS_ATA_VALID_MODE) mode = cts.xport_specific.ata.mode; } else { if (cts.xport_specific.sata.valid & CTS_SATA_VALID_MODE) mode = cts.xport_specific.sata.mode; } negotiate: /* Honor device capabilities. */ wantmode = mode = ata_max_mode(ident_buf, mode); /* Report modes to SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; if (path->device->transport == XPORT_ATA) { cts.xport_specific.ata.mode = mode; cts.xport_specific.ata.valid = CTS_ATA_VALID_MODE; } else { cts.xport_specific.sata.mode = mode; cts.xport_specific.sata.valid = CTS_SATA_VALID_MODE; } xpt_action((union ccb *)&cts); /* Fetch current modes from SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if (path->device->transport == XPORT_ATA) { if (cts.xport_specific.ata.valid & CTS_ATA_VALID_MODE) mode = cts.xport_specific.ata.mode; } else { if (cts.xport_specific.ata.valid & CTS_SATA_VALID_MODE) mode = cts.xport_specific.sata.mode; } /* If SIM disagree - renegotiate. */ if (mode != wantmode) goto negotiate; cam_fill_ataio(ataio, 1, probedone, /*flags*/CAM_DIR_NONE, 0, /*data_ptr*/NULL, /*dxfer_len*/0, 30 * 1000); ata_28bit_cmd(ataio, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode); break; } case PROBE_SET_MULTI: { u_int sectors, bytecount; bytecount = 8192; /* SATA maximum */ /* Fetch user bytecount from SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_USER_SETTINGS; xpt_action((union ccb *)&cts); if (path->device->transport == XPORT_ATA) { if (cts.xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT) bytecount = cts.xport_specific.ata.bytecount; } else { if (cts.xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) bytecount = cts.xport_specific.sata.bytecount; } /* Honor device capabilities. */ sectors = max(1, min(ident_buf->sectors_intr & 0xff, bytecount / ata_logical_sector_size(ident_buf))); /* Report bytecount to SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; if (path->device->transport == XPORT_ATA) { cts.xport_specific.ata.bytecount = sectors * ata_logical_sector_size(ident_buf); cts.xport_specific.ata.valid = CTS_ATA_VALID_BYTECOUNT; } else { cts.xport_specific.sata.bytecount = sectors * ata_logical_sector_size(ident_buf); cts.xport_specific.sata.valid = CTS_SATA_VALID_BYTECOUNT; } xpt_action((union ccb *)&cts); /* Fetch current bytecount from SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if (path->device->transport == XPORT_ATA) { if (cts.xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT) bytecount = cts.xport_specific.ata.bytecount; } else { if (cts.xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) bytecount = cts.xport_specific.sata.bytecount; } sectors = bytecount / ata_logical_sector_size(ident_buf); cam_fill_ataio(ataio, 1, probedone, CAM_DIR_NONE, 0, NULL, 0, 30*1000); ata_28bit_cmd(ataio, ATA_SET_MULTI, 0, 0, sectors); break; } case PROBE_INQUIRY: { u_int bytecount; bytecount = 8192; /* SATA maximum */ /* Fetch user bytecount from SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_USER_SETTINGS; xpt_action((union ccb *)&cts); if (path->device->transport == XPORT_ATA) { if (cts.xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT) bytecount = cts.xport_specific.ata.bytecount; } else { if (cts.xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) bytecount = cts.xport_specific.sata.bytecount; } /* Honor device capabilities. */ bytecount &= ~1; bytecount = max(2, min(65534, bytecount)); if (ident_buf->satacapabilities != 0x0000 && ident_buf->satacapabilities != 0xffff) { bytecount = min(8192, bytecount); } /* Report bytecount to SIM. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; if (path->device->transport == XPORT_ATA) { cts.xport_specific.ata.bytecount = bytecount; cts.xport_specific.ata.valid = CTS_ATA_VALID_BYTECOUNT; } else { cts.xport_specific.sata.bytecount = bytecount; cts.xport_specific.sata.valid = CTS_SATA_VALID_BYTECOUNT; } xpt_action((union ccb *)&cts); /* FALLTHROUGH */ } case PROBE_FULL_INQUIRY: { u_int inquiry_len; struct scsi_inquiry_data *inq_buf = &periph->path->device->inq_data; if (softc->action == PROBE_INQUIRY) inquiry_len = SHORT_INQUIRY_LENGTH; else inquiry_len = SID_ADDITIONAL_LENGTH(inq_buf); /* * Some parallel SCSI devices fail to send an * ignore wide residue message when dealing with * odd length inquiry requests. Round up to be * safe. */ inquiry_len = roundup2(inquiry_len, 2); scsi_inquiry(csio, /*retries*/1, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)inq_buf, inquiry_len, /*evpd*/FALSE, /*page_code*/0, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } case PROBE_PM_PID: cam_fill_ataio(ataio, 1, probedone, /*flags*/CAM_DIR_NONE, 0, /*data_ptr*/NULL, /*dxfer_len*/0, 10 * 1000); ata_pm_read_cmd(ataio, 0, 15); break; case PROBE_PM_PRV: cam_fill_ataio(ataio, 1, probedone, /*flags*/CAM_DIR_NONE, 0, /*data_ptr*/NULL, /*dxfer_len*/0, 10 * 1000); ata_pm_read_cmd(ataio, 1, 15); break; case PROBE_INVALID: CAM_DEBUG(path, CAM_DEBUG_INFO, ("probestart: invalid action state\n")); default: break; } xpt_action(start_ccb); } #if 0 static void proberequestdefaultnegotiation(struct cam_periph *periph) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_USER_SETTINGS; xpt_action((union ccb *)&cts); if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { return; } cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); } /* * Backoff Negotiation Code- only pertinent for SPI devices. */ static int proberequestbackoff(struct cam_periph *periph, struct cam_ed *device) { struct ccb_trans_settings cts; struct ccb_trans_settings_spi *spi; memset(&cts, 0, sizeof (cts)); xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (bootverbose) { xpt_print(periph->path, "failed to get current device settings\n"); } return (0); } if (cts.transport != XPORT_SPI) { if (bootverbose) { xpt_print(periph->path, "not SPI transport\n"); } return (0); } spi = &cts.xport_specific.spi; /* * We cannot renegotiate sync rate if we don't have one. */ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) { if (bootverbose) { xpt_print(periph->path, "no sync rate known\n"); } return (0); } /* * We'll assert that we don't have to touch PPR options- the * SIM will see what we do with period and offset and adjust * the PPR options as appropriate. */ /* * A sync rate with unknown or zero offset is nonsensical. * A sync period of zero means Async. */ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0 || spi->sync_offset == 0 || spi->sync_period == 0) { if (bootverbose) { xpt_print(periph->path, "no sync rate available\n"); } return (0); } if (device->flags & CAM_DEV_DV_HIT_BOTTOM) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("hit async: giving up on DV\n")); return (0); } /* * Jump sync_period up by one, but stop at 5MHz and fall back to Async. * We don't try to remember 'last' settings to see if the SIM actually * gets into the speed we want to set. We check on the SIM telling * us that a requested speed is bad, but otherwise don't try and * check the speed due to the asynchronous and handshake nature * of speed setting. */ spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET; for (;;) { spi->sync_period++; if (spi->sync_period >= 0xf) { spi->sync_period = 0; spi->sync_offset = 0; CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("setting to async for DV\n")); /* * Once we hit async, we don't want to try * any more settings. */ device->flags |= CAM_DEV_DV_HIT_BOTTOM; } else if (bootverbose) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("DV: period 0x%x\n", spi->sync_period)); printf("setting period to 0x%x\n", spi->sync_period); } cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if ((cts.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { break; } CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("DV: failed to set period 0x%x\n", spi->sync_period)); if (spi->sync_period == 0) { return (0); } } return (1); } #endif static void probedone(struct cam_periph *periph, union ccb *done_ccb) { struct ccb_trans_settings cts; struct ata_params *ident_buf; probe_softc *softc; struct cam_path *path; u_int32_t priority; int found = 1; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); softc = (probe_softc *)periph->softc; path = done_ccb->ccb_h.path; priority = done_ccb->ccb_h.pinfo.priority; ident_buf = &path->device->ident_data; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { device_fail: if ((!softc->restart) && cam_periph_error(done_ccb, 0, 0, NULL) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* Old PIO2 devices may not support mode setting. */ if (softc->action == PROBE_SETMODE && ata_max_pmode(ident_buf) <= ATA_PIO2 && (ident_buf->capabilities1 & ATA_SUPPORT_IORDY) == 0) goto noerror; /* * If we get to this point, we got an error status back * from the inquiry and the error status doesn't require * automatically retrying the command. Therefore, the * inquiry failed. If we had inquiry information before * for this device, but this latest inquiry command failed, * the device has probably gone away. If this device isn't * already marked unconfigured, notify the peripheral * drivers that this device is no more. */ if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) xpt_async(AC_LOST_DEVICE, path, NULL); found = 0; goto done; } noerror: if (softc->restart) goto done; switch (softc->action) { case PROBE_RESET: { int sign = (done_ccb->ataio.res.lba_high << 8) + done_ccb->ataio.res.lba_mid; if (bootverbose) xpt_print(path, "SIGNATURE: %04x\n", sign); if (sign == 0x0000 && done_ccb->ccb_h.target_id != 15) { path->device->protocol = PROTO_ATA; PROBE_SET_ACTION(softc, PROBE_IDENTIFY); } else if (sign == 0x9669 && done_ccb->ccb_h.target_id == 15) { /* Report SIM that PM is present. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.xport_specific.sata.pm_present = 1; cts.xport_specific.sata.valid = CTS_SATA_VALID_PM; xpt_action((union ccb *)&cts); path->device->protocol = PROTO_SATAPM; PROBE_SET_ACTION(softc, PROBE_PM_PID); } else if (sign == 0xeb14 && done_ccb->ccb_h.target_id != 15) { path->device->protocol = PROTO_SCSI; PROBE_SET_ACTION(softc, PROBE_IDENTIFY); } else { if (done_ccb->ccb_h.target_id != 15) { xpt_print(path, "Unexpected signature 0x%04x\n", sign); } goto device_fail; } xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } case PROBE_IDENTIFY: { int16_t *ptr; ident_buf = &softc->ident_data; for (ptr = (int16_t *)ident_buf; ptr < (int16_t *)ident_buf + sizeof(struct ata_params)/2; ptr++) { *ptr = le16toh(*ptr); } if (strncmp(ident_buf->model, "FX", 2) && strncmp(ident_buf->model, "NEC", 3) && strncmp(ident_buf->model, "Pioneer", 7) && strncmp(ident_buf->model, "SHARP", 5)) { ata_bswap(ident_buf->model, sizeof(ident_buf->model)); ata_bswap(ident_buf->revision, sizeof(ident_buf->revision)); ata_bswap(ident_buf->serial, sizeof(ident_buf->serial)); } ata_btrim(ident_buf->model, sizeof(ident_buf->model)); ata_bpack(ident_buf->model, ident_buf->model, sizeof(ident_buf->model)); ata_btrim(ident_buf->revision, sizeof(ident_buf->revision)); ata_bpack(ident_buf->revision, ident_buf->revision, sizeof(ident_buf->revision)); ata_btrim(ident_buf->serial, sizeof(ident_buf->serial)); ata_bpack(ident_buf->serial, ident_buf->serial, sizeof(ident_buf->serial)); /* Device may need spin-up before IDENTIFY become valid. */ if ((ident_buf->config & ATA_RESP_INCOMPLETE) || ((ident_buf->support.command2 & ATA_SUPPORT_STANDBY) && (ident_buf->enabled.command2 & ATA_SUPPORT_STANDBY) && (ident_buf->support.command2 & ATA_SUPPORT_SPINUP) && softc->spinup == 0)) { PROBE_SET_ACTION(softc, PROBE_SPINUP); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } ident_buf = &path->device->ident_data; if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) { /* Check that it is the same device. */ if (bcmp(softc->ident_data.model, ident_buf->model, sizeof(ident_buf->model)) || bcmp(softc->ident_data.revision, ident_buf->revision, sizeof(ident_buf->revision)) || bcmp(softc->ident_data.serial, ident_buf->serial, sizeof(ident_buf->serial))) { /* Device changed. */ xpt_async(AC_LOST_DEVICE, path, NULL); } else bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params)); } else { bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params)); /* Clean up from previous instance of this device */ if (path->device->serial_num != NULL) { free(path->device->serial_num, M_CAMXPT); path->device->serial_num = NULL; path->device->serial_num_len = 0; } path->device->serial_num = (u_int8_t *)malloc((sizeof(ident_buf->serial) + 1), M_CAMXPT, M_NOWAIT); if (path->device->serial_num != NULL) { bcopy(ident_buf->serial, path->device->serial_num, sizeof(ident_buf->serial)); path->device->serial_num[sizeof(ident_buf->serial)] = '\0'; path->device->serial_num_len = strlen(path->device->serial_num); } path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID; } if (ident_buf->satacapabilities & ATA_SUPPORT_NCQ) { path->device->mintags = path->device->maxtags = ATA_QUEUE_LEN(ident_buf->queue) + 1; } ata_find_quirk(path->device); if (path->device->mintags != 0 && path->bus->sim->max_tagged_dev_openings != 0) { /* Report SIM which tags are allowed. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.xport_specific.sata.tags = path->device->maxtags; cts.xport_specific.sata.valid = CTS_SATA_VALID_TAGS; xpt_action((union ccb *)&cts); /* Reconfigure queues for tagged queueing. */ xpt_start_tags(path); } ata_device_transport(path); PROBE_SET_ACTION(softc, PROBE_SETMODE); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } case PROBE_SPINUP: if (bootverbose) xpt_print(path, "Spin-up done\n"); softc->spinup = 1; PROBE_SET_ACTION(softc, PROBE_IDENTIFY); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; case PROBE_SETMODE: if (path->device->protocol == PROTO_ATA) { PROBE_SET_ACTION(softc, PROBE_SET_MULTI); } else { PROBE_SET_ACTION(softc, PROBE_INQUIRY); } xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; case PROBE_SET_MULTI: if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } break; case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: { struct scsi_inquiry_data *inq_buf; u_int8_t periph_qual, len; path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID; inq_buf = &path->device->inq_data; periph_qual = SID_QUAL(inq_buf); if (periph_qual != SID_QUAL_LU_CONNECTED) break; /* * We conservatively request only * SHORT_INQUIRY_LEN bytes of inquiry * information during our first try * at sending an INQUIRY. If the device * has more information to give, * perform a second request specifying * the amount of information the device * is willing to give. */ len = inq_buf->additional_length + offsetof(struct scsi_inquiry_data, additional_length) + 1; if (softc->action == PROBE_INQUIRY && len > SHORT_INQUIRY_LENGTH) { PROBE_SET_ACTION(softc, PROBE_FULL_INQUIRY); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } ata_device_transport(path); if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } break; } case PROBE_PM_PID: if ((path->device->flags & CAM_DEV_IDENTIFY_DATA_VALID) == 0) bzero(ident_buf, sizeof(*ident_buf)); softc->pm_pid = (done_ccb->ataio.res.lba_high << 24) + (done_ccb->ataio.res.lba_mid << 16) + (done_ccb->ataio.res.lba_low << 8) + done_ccb->ataio.res.sector_count; ((uint32_t *)ident_buf)[0] = softc->pm_pid; snprintf(ident_buf->model, sizeof(ident_buf->model), "Port Multiplier %08x", softc->pm_pid); PROBE_SET_ACTION(softc, PROBE_PM_PRV); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; case PROBE_PM_PRV: softc->pm_prv = (done_ccb->ataio.res.lba_high << 24) + (done_ccb->ataio.res.lba_mid << 16) + (done_ccb->ataio.res.lba_low << 8) + done_ccb->ataio.res.sector_count; ((uint32_t *)ident_buf)[1] = softc->pm_prv; snprintf(ident_buf->revision, sizeof(ident_buf->revision), "%04x", softc->pm_prv); path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID; if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } else { done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_SCSI_AEN, done_ccb->ccb_h.path, done_ccb); } break; case PROBE_INVALID: CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_INFO, ("probedone: invalid action state\n")); default: break; } done: if (softc->restart) { softc->restart = 0; xpt_release_ccb(done_ccb); probeschedule(periph); return; } xpt_release_ccb(done_ccb); while ((done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs))) { TAILQ_REMOVE(&softc->request_ccbs, &done_ccb->ccb_h, periph_links.tqe); done_ccb->ccb_h.status = found ? CAM_REQ_CMP : CAM_REQ_CMP_ERR; xpt_done(done_ccb); } cam_release_devq(periph->path, RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_XPT + 1, FALSE); cam_periph_invalidate(periph); cam_periph_release_locked(periph); } static void probecleanup(struct cam_periph *periph) { free(periph->softc, M_CAMXPT); } static void ata_find_quirk(struct cam_ed *device) { struct ata_quirk_entry *quirk; caddr_t match; match = cam_quirkmatch((caddr_t)&device->ident_data, (caddr_t)ata_quirk_table, ata_quirk_table_size, sizeof(*ata_quirk_table), ata_identify_match); if (match == NULL) panic("xpt_find_quirk: device didn't match wildcard entry!!"); quirk = (struct ata_quirk_entry *)match; device->quirk = quirk; if (quirk->quirks & CAM_QUIRK_MAXTAGS) device->mintags = device->maxtags = quirk->maxtags; } typedef struct { union ccb *request_ccb; struct ccb_pathinq *cpi; int counter; } ata_scan_bus_info; /* * To start a scan, request_ccb is an XPT_SCAN_BUS ccb. * As the scan progresses, xpt_scan_bus is used as the * callback on completion function. */ static void ata_scan_bus(struct cam_periph *periph, union ccb *request_ccb) { struct cam_path *path; ata_scan_bus_info *scan_info; union ccb *work_ccb, *reset_ccb; cam_status status; CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_scan_bus\n")); switch (request_ccb->ccb_h.func_code) { case XPT_SCAN_BUS: /* Find out the characteristics of the bus */ work_ccb = xpt_alloc_ccb_nowait(); if (work_ccb == NULL) { request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); return; } xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_PATH_INQ; xpt_action(work_ccb); if (work_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = work_ccb->ccb_h.status; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } /* We may need to reset bus first, if we haven't done it yet. */ if ((work_ccb->cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) && !(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) && !timevalisset(&request_ccb->ccb_h.path->bus->last_reset)) { reset_ccb = xpt_alloc_ccb_nowait(); xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path, CAM_PRIORITY_NONE); reset_ccb->ccb_h.func_code = XPT_RESET_BUS; xpt_action(reset_ccb); if (reset_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = reset_ccb->ccb_h.status; xpt_free_ccb(reset_ccb); xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } xpt_free_ccb(reset_ccb); } /* Save some state for use while we probe for devices */ scan_info = (ata_scan_bus_info *) malloc(sizeof(ata_scan_bus_info), M_CAMXPT, M_NOWAIT); if (scan_info == NULL) { request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); return; } scan_info->request_ccb = request_ccb; scan_info->cpi = &work_ccb->cpi; /* If PM supported, probe it first. */ if (scan_info->cpi->hba_inquiry & PI_SATAPM) scan_info->counter = scan_info->cpi->max_target; else scan_info->counter = 0; work_ccb = xpt_alloc_ccb_nowait(); if (work_ccb == NULL) { free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); break; } goto scan_next; case XPT_SCAN_LUN: work_ccb = request_ccb; /* Reuse the same CCB to query if a device was really found */ scan_info = (ata_scan_bus_info *)work_ccb->ccb_h.ppriv_ptr0; /* Free the current request path- we're done with it. */ xpt_free_path(work_ccb->ccb_h.path); /* If there is PMP... */ if ((scan_info->cpi->hba_inquiry & PI_SATAPM) && (scan_info->counter == scan_info->cpi->max_target)) { if (work_ccb->ccb_h.status == CAM_REQ_CMP) { /* everything else willbe probed by it */ goto done; } else { struct ccb_trans_settings cts; /* Report SIM that PM is absent. */ bzero(&cts, sizeof(cts)); xpt_setup_ccb(&cts.ccb_h, scan_info->request_ccb->ccb_h.path, 1); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.xport_specific.sata.pm_present = 0; cts.xport_specific.sata.valid = CTS_SATA_VALID_PM; xpt_action((union ccb *)&cts); } } if (scan_info->counter == ((scan_info->cpi->hba_inquiry & PI_SATAPM) ? 0 : scan_info->cpi->max_target)) { done: xpt_free_ccb(work_ccb); xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); break; } /* Take next device. Wrap from max (PMP) to 0. */ scan_info->counter = (scan_info->counter + 1 ) % (scan_info->cpi->max_target + 1); scan_next: status = xpt_create_path(&path, xpt_periph, scan_info->request_ccb->ccb_h.path_id, scan_info->counter, 0); if (status != CAM_REQ_CMP) { printf("xpt_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); xpt_free_ccb(work_ccb); xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = status; xpt_done(request_ccb); break; } xpt_setup_ccb(&work_ccb->ccb_h, path, scan_info->request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_SCAN_LUN; work_ccb->ccb_h.cbfcnp = ata_scan_bus; work_ccb->ccb_h.ppriv_ptr0 = scan_info; work_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; xpt_action(work_ccb); break; default: break; } } static void ata_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *request_ccb) { struct ccb_pathinq cpi; cam_status status; struct cam_path *new_path; struct cam_periph *old_periph; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_scan_lun\n")); xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status != CAM_REQ_CMP) { if (request_ccb != NULL) { request_ccb->ccb_h.status = cpi.ccb_h.status; xpt_done(request_ccb); } return; } if (request_ccb == NULL) { request_ccb = malloc(sizeof(union ccb), M_CAMXPT, M_NOWAIT); if (request_ccb == NULL) { xpt_print(path, "xpt_scan_lun: can't allocate CCB, " "can't continue\n"); return; } new_path = malloc(sizeof(*new_path), M_CAMXPT, M_NOWAIT); if (new_path == NULL) { xpt_print(path, "xpt_scan_lun: can't allocate path, " "can't continue\n"); free(request_ccb, M_CAMXPT); return; } status = xpt_compile_path(new_path, xpt_periph, path->bus->path_id, path->target->target_id, path->device->lun_id); if (status != CAM_REQ_CMP) { xpt_print(path, "xpt_scan_lun: can't compile path, " "can't continue\n"); free(request_ccb, M_CAMXPT); free(new_path, M_CAMXPT); return; } xpt_setup_ccb(&request_ccb->ccb_h, new_path, CAM_PRIORITY_XPT); request_ccb->ccb_h.cbfcnp = xptscandone; request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->crcn.flags = flags; } if ((old_periph = cam_periph_find(path, "aprobe")) != NULL) { probe_softc *softc; softc = (probe_softc *)old_periph->softc; TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); softc->restart = 1; } else { status = cam_periph_alloc(proberegister, NULL, probecleanup, probestart, "aprobe", CAM_PERIPH_BIO, request_ccb->ccb_h.path, NULL, 0, request_ccb); if (status != CAM_REQ_CMP) { xpt_print(path, "xpt_scan_lun: cam_alloc_periph " "returned an error, can't continue probe\n"); request_ccb->ccb_h.status = status; xpt_done(request_ccb); } } } static void xptscandone(struct cam_periph *periph, union ccb *done_ccb) { xpt_release_path(done_ccb->ccb_h.path); free(done_ccb->ccb_h.path, M_CAMXPT); free(done_ccb, M_CAMXPT); } static struct cam_ed * ata_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct cam_path path; struct ata_quirk_entry *quirk; struct cam_ed *device; struct cam_ed *cur_device; device = xpt_alloc_device(bus, target, lun_id); if (device == NULL) return (NULL); /* * Take the default quirk entry until we have inquiry * data and can determine a better quirk to use. */ quirk = &ata_quirk_table[ata_quirk_table_size - 1]; device->quirk = (void *)quirk; device->mintags = 0; device->maxtags = 0; bzero(&device->inq_data, sizeof(device->inq_data)); device->inq_flags = 0; device->queue_flags = 0; device->serial_num = NULL; device->serial_num_len = 0; /* * XXX should be limited by number of CCBs this bus can * do. */ bus->sim->max_ccbs += device->ccbq.devq_openings; /* Insertion sort into our target's device list */ cur_device = TAILQ_FIRST(&target->ed_entries); while (cur_device != NULL && cur_device->lun_id < lun_id) cur_device = TAILQ_NEXT(cur_device, links); if (cur_device != NULL) { TAILQ_INSERT_BEFORE(cur_device, device, links); } else { TAILQ_INSERT_TAIL(&target->ed_entries, device, links); } target->generation++; if (lun_id != CAM_LUN_WILDCARD) { xpt_compile_path(&path, NULL, bus->path_id, target->target_id, lun_id); ata_device_transport(&path); xpt_release_path(&path); } return (device); } static void ata_device_transport(struct cam_path *path) { struct ccb_pathinq cpi; struct ccb_trans_settings cts; struct scsi_inquiry_data *inq_buf = NULL; struct ata_params *ident_buf = NULL; /* Get transport information from the SIM */ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); path->device->transport = cpi.transport; if ((path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0) inq_buf = &path->device->inq_data; if ((path->device->flags & CAM_DEV_IDENTIFY_DATA_VALID) != 0) ident_buf = &path->device->ident_data; if (path->device->protocol == PROTO_ATA) { path->device->protocol_version = ident_buf ? ata_version(ident_buf->version_major) : cpi.protocol_version; } else if (path->device->protocol == PROTO_SCSI) { path->device->protocol_version = inq_buf ? SID_ANSI_REV(inq_buf) : cpi.protocol_version; } path->device->transport_version = ident_buf ? ata_version(ident_buf->version_major) : cpi.transport_version; /* Tell the controller what we think */ xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.transport = path->device->transport; cts.transport_version = path->device->transport_version; cts.protocol = path->device->protocol; cts.protocol_version = path->device->protocol_version; cts.proto_specific.valid = 0; if (ident_buf) { if (path->device->transport == XPORT_ATA) { cts.xport_specific.ata.atapi = ((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_16) ? 16 : ((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) ? 12 : 0; cts.xport_specific.ata.valid = CTS_ATA_VALID_ATAPI; } else { cts.xport_specific.sata.atapi = ((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_16) ? 16 : ((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) ? 12 : 0; cts.xport_specific.sata.valid = CTS_SATA_VALID_ATAPI; } } else cts.xport_specific.valid = 0; xpt_action((union ccb *)&cts); } static void ata_action(union ccb *start_ccb) { switch (start_ccb->ccb_h.func_code) { case XPT_SET_TRAN_SETTINGS: { ata_set_transfer_settings(&start_ccb->cts, start_ccb->ccb_h.path->device, /*async_update*/FALSE); break; } case XPT_SCAN_BUS: ata_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); break; case XPT_SCAN_LUN: ata_scan_lun(start_ccb->ccb_h.path->periph, start_ccb->ccb_h.path, start_ccb->crcn.flags, start_ccb); break; case XPT_GET_TRAN_SETTINGS: { struct cam_sim *sim; sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_SCSI_IO: { struct cam_ed *device; u_int maxlen = 0; device = start_ccb->ccb_h.path->device; if (device->protocol == PROTO_SCSI && (device->flags & CAM_DEV_IDENTIFY_DATA_VALID)) { uint16_t p = device->ident_data.config & ATA_PROTO_MASK; maxlen = (p == ATA_PROTO_ATAPI_16) ? 16 : (p == ATA_PROTO_ATAPI_12) ? 12 : 0; } if (start_ccb->csio.cdb_len > maxlen) { start_ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(start_ccb); break; } /* FALLTHROUGH */ } default: xpt_action_default(start_ccb); break; } } static void ata_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update) { struct ccb_pathinq cpi; struct ccb_trans_settings cur_cts; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_scsi *cur_scsi; struct cam_sim *sim; struct scsi_inquiry_data *inq_data; if (device == NULL) { cts->ccb_h.status = CAM_PATH_INVALID; xpt_done((union ccb *)cts); return; } if (cts->protocol == PROTO_UNKNOWN || cts->protocol == PROTO_UNSPECIFIED) { cts->protocol = device->protocol; cts->protocol_version = device->protocol_version; } if (cts->protocol_version == PROTO_VERSION_UNKNOWN || cts->protocol_version == PROTO_VERSION_UNSPECIFIED) cts->protocol_version = device->protocol_version; if (cts->protocol != device->protocol) { xpt_print(cts->ccb_h.path, "Uninitialized Protocol %x:%x?\n", cts->protocol, device->protocol); cts->protocol = device->protocol; } if (cts->protocol_version > device->protocol_version) { if (bootverbose) { xpt_print(cts->ccb_h.path, "Down reving Protocol " "Version from %d to %d?\n", cts->protocol_version, device->protocol_version); } cts->protocol_version = device->protocol_version; } if (cts->transport == XPORT_UNKNOWN || cts->transport == XPORT_UNSPECIFIED) { cts->transport = device->transport; cts->transport_version = device->transport_version; } if (cts->transport_version == XPORT_VERSION_UNKNOWN || cts->transport_version == XPORT_VERSION_UNSPECIFIED) cts->transport_version = device->transport_version; if (cts->transport != device->transport) { xpt_print(cts->ccb_h.path, "Uninitialized Transport %x:%x?\n", cts->transport, device->transport); cts->transport = device->transport; } if (cts->transport_version > device->transport_version) { if (bootverbose) { xpt_print(cts->ccb_h.path, "Down reving Transport " "Version from %d to %d?\n", cts->transport_version, device->transport_version); } cts->transport_version = device->transport_version; } sim = cts->ccb_h.path->bus->sim; /* * Nothing more of interest to do unless * this is a device connected via the * SCSI protocol. */ if (cts->protocol != PROTO_SCSI) { if (async_update == FALSE) (*(sim->sim_action))(sim, (union ccb *)cts); return; } inq_data = &device->inq_data; scsi = &cts->proto_specific.scsi; xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* SCSI specific sanity checking */ if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0 || (INQ_DATA_TQ_ENABLED(inq_data)) == 0 || (device->queue_flags & SCP_QUEUE_DQUE) != 0 || (device->mintags == 0)) { /* * Can't tag on hardware that doesn't support tags, * doesn't have it enabled, or has broken tag support. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } if (async_update == FALSE) { /* * Perform sanity checking against what the * controller and device can do. */ xpt_setup_ccb(&cur_cts.ccb_h, cts->ccb_h.path, CAM_PRIORITY_NONE); cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cur_cts.type = cts->type; xpt_action((union ccb *)&cur_cts); if ((cur_cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { return; } cur_scsi = &cur_cts.proto_specific.scsi; if ((scsi->valid & CTS_SCSI_VALID_TQ) == 0) { scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->flags |= cur_scsi->flags & CTS_SCSI_FLAGS_TAG_ENB; } if ((cur_scsi->valid & CTS_SCSI_VALID_TQ) == 0) scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (scsi->valid & CTS_SCSI_VALID_TQ) != 0) { int device_tagenb; /* * If we are transitioning from tags to no-tags or * vice-versa, we need to carefully freeze and restart * the queue so that we don't overlap tagged and non-tagged * commands. We also temporarily stop tags if there is * a change in transfer negotiation settings to allow * "tag-less" negotiation. */ if ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (device->inq_flags & SID_CmdQue) != 0) device_tagenb = TRUE; else device_tagenb = FALSE; if (((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && device_tagenb == FALSE) || ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) == 0 && device_tagenb == TRUE)) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) { /* * Delay change to use tags until after a * few commands have gone to this device so * the controller has time to perform transfer * negotiations without tagged messages getting * in the way. */ device->tag_delay_count = CAM_TAG_DELAY_COUNT; device->flags |= CAM_DEV_TAG_AFTER_COUNT; } else { xpt_stop_tags(cts->ccb_h.path); } } } if (async_update == FALSE) (*(sim->sim_action))(sim, (union ccb *)cts); } /* * Handle any per-device event notifications that require action by the XPT. */ static void ata_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg) { cam_status status; struct cam_path newpath; /* * We only need to handle events for real devices. */ if (target->target_id == CAM_TARGET_WILDCARD || device->lun_id == CAM_LUN_WILDCARD) return; /* * We need our own path with wildcards expanded to * handle certain types of events. */ if ((async_code == AC_SENT_BDR) || (async_code == AC_BUS_RESET) || (async_code == AC_INQ_CHANGED)) status = xpt_compile_path(&newpath, NULL, bus->path_id, target->target_id, device->lun_id); else status = CAM_REQ_CMP_ERR; if (status == CAM_REQ_CMP) { if (async_code == AC_INQ_CHANGED) { /* * We've sent a start unit command, or * something similar to a device that * may have caused its inquiry data to * change. So we re-scan the device to * refresh the inquiry data for it. */ ata_scan_lun(newpath.periph, &newpath, CAM_EXPECT_INQ_CHANGE, NULL); } else { /* We need to reinitialize device after reset. */ ata_scan_lun(newpath.periph, &newpath, 0, NULL); } xpt_release_path(&newpath); } else if (async_code == AC_LOST_DEVICE && (device->flags & CAM_DEV_UNCONFIGURED) == 0) { device->flags |= CAM_DEV_UNCONFIGURED; xpt_release_device(device); } else if (async_code == AC_TRANSFER_NEG) { struct ccb_trans_settings *settings; settings = (struct ccb_trans_settings *)async_arg; ata_set_transfer_settings(settings, device, /*async_update*/TRUE); } +} + +static void +ata_announce_periph(struct cam_periph *periph) +{ + struct ccb_pathinq cpi; + struct ccb_trans_settings cts; + struct cam_path *path = periph->path; + u_int speed; + u_int mb; + + mtx_assert(periph->sim->mtx, MA_OWNED); + + xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL); + cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; + cts.type = CTS_TYPE_CURRENT_SETTINGS; + xpt_action((union ccb*)&cts); + if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + return; + /* Ask the SIM for its base transfer speed */ + xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL); + cpi.ccb_h.func_code = XPT_PATH_INQ; + xpt_action((union ccb *)&cpi); + /* Report connection speed */ + speed = cpi.base_transfer_speed; + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_ATA) { + struct ccb_trans_settings_ata *ata = + &cts.xport_specific.ata; + + if (ata->valid & CTS_ATA_VALID_MODE) + speed = ata_mode2speed(ata->mode); + } + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SATA) { + struct ccb_trans_settings_sata *sata = + &cts.xport_specific.sata; + + if (sata->valid & CTS_SATA_VALID_REVISION) + speed = ata_revision2speed(sata->revision); + } + mb = speed / 1000; + if (mb > 0) + printf("%s%d: %d.%03dMB/s transfers", + periph->periph_name, periph->unit_number, + mb, speed % 1000); + else + printf("%s%d: %dKB/s transfers", periph->periph_name, + periph->unit_number, speed); + /* Report additional information about connection */ + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_ATA) { + struct ccb_trans_settings_ata *ata = + &cts.xport_specific.ata; + + printf(" ("); + if (ata->valid & CTS_ATA_VALID_MODE) + printf("%s, ", ata_mode2string(ata->mode)); + if ((ata->valid & CTS_ATA_VALID_ATAPI) && ata->atapi != 0) + printf("ATAPI %dbytes, ", ata->atapi); + if (ata->valid & CTS_ATA_VALID_BYTECOUNT) + printf("PIO %dbytes", ata->bytecount); + printf(")"); + } + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SATA) { + struct ccb_trans_settings_sata *sata = + &cts.xport_specific.sata; + + printf(" ("); + if (sata->valid & CTS_SATA_VALID_REVISION) + printf("SATA %d.x, ", sata->revision); + else + printf("SATA, "); + if (sata->valid & CTS_SATA_VALID_MODE) + printf("%s, ", ata_mode2string(sata->mode)); + if ((sata->valid & CTS_ATA_VALID_ATAPI) && sata->atapi != 0) + printf("ATAPI %dbytes, ", sata->atapi); + if (sata->valid & CTS_SATA_VALID_BYTECOUNT) + printf("PIO %dbytes", sata->bytecount); + printf(")"); + } + printf("\n"); } Index: projects/ppc64/sys/cam/cam_xpt.c =================================================================== --- projects/ppc64/sys/cam/cam_xpt.c (revision 204271) +++ projects/ppc64/sys/cam/cam_xpt.c (revision 204272) @@ -1,5062 +1,4922 @@ /*- * Implementation of the Common Access Method Transport (XPT) layer. * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * 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, immediately at the beginning of the file. * 2. 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 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 #ifdef PC98 #include /* geometry translation */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for xpt_print below */ #include "opt_cam.h" /* * This is the maximum number of high powered commands (e.g. start unit) * that can be outstanding at a particular time. */ #ifndef CAM_MAX_HIGHPOWER #define CAM_MAX_HIGHPOWER 4 #endif /* Datastructures internal to the xpt layer */ MALLOC_DEFINE(M_CAMXPT, "CAM XPT", "CAM XPT buffers"); /* Object for defering XPT actions to a taskqueue */ struct xpt_task { struct task task; void *data1; uintptr_t data2; }; typedef enum { XPT_FLAG_OPEN = 0x01 } xpt_flags; struct xpt_softc { xpt_flags flags; u_int32_t xpt_generation; /* number of high powered commands that can go through right now */ STAILQ_HEAD(highpowerlist, ccb_hdr) highpowerq; int num_highpower; /* queue for handling async rescan requests. */ TAILQ_HEAD(, ccb_hdr) ccb_scanq; int buses_to_config; int buses_config_done; /* Registered busses */ TAILQ_HEAD(,cam_eb) xpt_busses; u_int bus_generation; struct intr_config_hook *xpt_config_hook; int boot_delay; struct callout boot_callout; struct mtx xpt_topo_lock; struct mtx xpt_lock; }; typedef enum { DM_RET_COPY = 0x01, DM_RET_FLAG_MASK = 0x0f, DM_RET_NONE = 0x00, DM_RET_STOP = 0x10, DM_RET_DESCEND = 0x20, DM_RET_ERROR = 0x30, DM_RET_ACTION_MASK = 0xf0 } dev_match_ret; typedef enum { XPT_DEPTH_BUS, XPT_DEPTH_TARGET, XPT_DEPTH_DEVICE, XPT_DEPTH_PERIPH } xpt_traverse_depth; struct xpt_traverse_config { xpt_traverse_depth depth; void *tr_func; void *tr_arg; }; typedef int xpt_busfunc_t (struct cam_eb *bus, void *arg); typedef int xpt_targetfunc_t (struct cam_et *target, void *arg); typedef int xpt_devicefunc_t (struct cam_ed *device, void *arg); typedef int xpt_periphfunc_t (struct cam_periph *periph, void *arg); typedef int xpt_pdrvfunc_t (struct periph_driver **pdrv, void *arg); /* Transport layer configuration information */ static struct xpt_softc xsoftc; TUNABLE_INT("kern.cam.boot_delay", &xsoftc.boot_delay); SYSCTL_INT(_kern_cam, OID_AUTO, boot_delay, CTLFLAG_RDTUN, &xsoftc.boot_delay, 0, "Bus registration wait time"); static int xpt_power_down = 0; TUNABLE_INT("kern.cam.power_down", &xpt_power_down); SYSCTL_INT(_kern_cam, OID_AUTO, power_down, CTLFLAG_RW, &xpt_power_down, 0, "Power down devices on shutdown"); /* Queues for our software interrupt handler */ typedef TAILQ_HEAD(cam_isrq, ccb_hdr) cam_isrq_t; typedef TAILQ_HEAD(cam_simq, cam_sim) cam_simq_t; static cam_simq_t cam_simq; static struct mtx cam_simq_lock; /* Pointers to software interrupt handlers */ static void *cambio_ih; struct cam_periph *xpt_periph; static periph_init_t xpt_periph_init; static struct periph_driver xpt_driver = { xpt_periph_init, "xpt", TAILQ_HEAD_INITIALIZER(xpt_driver.units), /* generation */ 0, CAM_PERIPH_DRV_EARLY }; PERIPHDRIVER_DECLARE(xpt, xpt_driver); static d_open_t xptopen; static d_close_t xptclose; static d_ioctl_t xptioctl; static struct cdevsw xpt_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = xptopen, .d_close = xptclose, .d_ioctl = xptioctl, .d_name = "xpt", }; /* Storage for debugging datastructures */ #ifdef CAMDEBUG struct cam_path *cam_dpath; u_int32_t cam_dflags; u_int32_t cam_debug_delay; #endif /* Our boot-time initialization hook */ static int cam_module_event_handler(module_t, int /*modeventtype_t*/, void *); static moduledata_t cam_moduledata = { "cam", cam_module_event_handler, NULL }; static int xpt_init(void *); DECLARE_MODULE(cam, cam_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(cam, 1); static void xpt_async_bcast(struct async_list *async_head, u_int32_t async_code, struct cam_path *path, void *async_arg); static path_id_t xptnextfreepathid(void); static path_id_t xptpathid(const char *sim_name, int sim_unit, int sim_bus); static union ccb *xpt_get_ccb(struct cam_ed *device); static void xpt_run_dev_allocq(struct cam_eb *bus); static void xpt_run_dev_sendq(struct cam_eb *bus); static timeout_t xpt_release_devq_timeout; static void xpt_release_simq_timeout(void *arg) __unused; static void xpt_release_bus(struct cam_eb *bus); static void xpt_release_devq_device(struct cam_ed *dev, cam_rl rl, u_int count, int run_queue); static struct cam_et* xpt_alloc_target(struct cam_eb *bus, target_id_t target_id); static void xpt_release_target(struct cam_et *target); static struct cam_eb* xpt_find_bus(path_id_t path_id); static struct cam_et* xpt_find_target(struct cam_eb *bus, target_id_t target_id); static struct cam_ed* xpt_find_device(struct cam_et *target, lun_id_t lun_id); static void xpt_config(void *arg); static xpt_devicefunc_t xptpassannouncefunc; static void xpt_shutdown(void *arg, int howto); static void xptaction(struct cam_sim *sim, union ccb *work_ccb); static void xptpoll(struct cam_sim *sim); static void camisr(void *); static void camisr_runqueue(void *); static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_eb *bus); static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_ed *device); static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_periph *periph); static xpt_busfunc_t xptedtbusfunc; static xpt_targetfunc_t xptedttargetfunc; static xpt_devicefunc_t xptedtdevicefunc; static xpt_periphfunc_t xptedtperiphfunc; static xpt_pdrvfunc_t xptplistpdrvfunc; static xpt_periphfunc_t xptplistperiphfunc; static int xptedtmatch(struct ccb_dev_match *cdm); static int xptperiphlistmatch(struct ccb_dev_match *cdm); static int xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg); static int xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, xpt_targetfunc_t *tr_func, void *arg); static int xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, xpt_devicefunc_t *tr_func, void *arg); static int xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg); static int xptpdrvtraverse(struct periph_driver **start_pdrv, xpt_pdrvfunc_t *tr_func, void *arg); static int xptpdperiphtraverse(struct periph_driver **pdrv, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg); static xpt_busfunc_t xptdefbusfunc; static xpt_targetfunc_t xptdeftargetfunc; static xpt_devicefunc_t xptdefdevicefunc; static xpt_periphfunc_t xptdefperiphfunc; static void xpt_finishconfig_task(void *context, int pending); static int xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg); static int xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg); static void xpt_dev_async_default(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); static struct cam_ed * xpt_alloc_device_default(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); static xpt_devicefunc_t xptsetasyncfunc; static xpt_busfunc_t xptsetasyncbusfunc; static cam_status xptregister(struct cam_periph *periph, void *arg); static __inline int periph_is_queued(struct cam_periph *periph); static __inline int device_is_alloc_queued(struct cam_ed *device); static __inline int device_is_send_queued(struct cam_ed *device); static __inline int xpt_schedule_dev_allocq(struct cam_eb *bus, struct cam_ed *dev) { int retval; if ((dev->drvq.entries > 0) && (dev->ccbq.devq_openings > 0) && (cam_ccbq_frozen(&dev->ccbq, CAM_PRIORITY_TO_RL( CAMQ_GET_PRIO(&dev->drvq))) == 0)) { /* * The priority of a device waiting for CCB resources * is that of the the highest priority peripheral driver * enqueued. */ retval = xpt_schedule_dev(&bus->sim->devq->alloc_queue, &dev->alloc_ccb_entry.pinfo, CAMQ_GET_PRIO(&dev->drvq)); } else { retval = 0; } return (retval); } static __inline int xpt_schedule_dev_sendq(struct cam_eb *bus, struct cam_ed *dev) { int retval; if ((dev->ccbq.queue.entries > 0) && (dev->ccbq.dev_openings > 0) && (cam_ccbq_frozen_top(&dev->ccbq) == 0)) { /* * The priority of a device waiting for controller * resources is that of the the highest priority CCB * enqueued. */ retval = xpt_schedule_dev(&bus->sim->devq->send_queue, &dev->send_ccb_entry.pinfo, CAMQ_GET_PRIO(&dev->ccbq.queue)); } else { retval = 0; } return (retval); } static __inline int periph_is_queued(struct cam_periph *periph) { return (periph->pinfo.index != CAM_UNQUEUED_INDEX); } static __inline int device_is_alloc_queued(struct cam_ed *device) { return (device->alloc_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX); } static __inline int device_is_send_queued(struct cam_ed *device) { return (device->send_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX); } static void xpt_periph_init() { make_dev(&xpt_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "xpt0"); } static void xptdone(struct cam_periph *periph, union ccb *done_ccb) { /* Caller will release the CCB */ wakeup(&done_ccb->ccb_h.cbfcnp); } static int xptopen(struct cdev *dev, int flags, int fmt, struct thread *td) { /* * Only allow read-write access. */ if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) return(EPERM); /* * We don't allow nonblocking access. */ if ((flags & O_NONBLOCK) != 0) { printf("%s: can't do nonblocking access\n", devtoname(dev)); return(ENODEV); } /* Mark ourselves open */ mtx_lock(&xsoftc.xpt_lock); xsoftc.flags |= XPT_FLAG_OPEN; mtx_unlock(&xsoftc.xpt_lock); return(0); } static int xptclose(struct cdev *dev, int flag, int fmt, struct thread *td) { /* Mark ourselves closed */ mtx_lock(&xsoftc.xpt_lock); xsoftc.flags &= ~XPT_FLAG_OPEN; mtx_unlock(&xsoftc.xpt_lock); return(0); } /* * Don't automatically grab the xpt softc lock here even though this is going * through the xpt device. The xpt device is really just a back door for * accessing other devices and SIMs, so the right thing to do is to grab * the appropriate SIM lock once the bus/SIM is located. */ static int xptioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { int error; error = 0; switch(cmd) { /* * For the transport layer CAMIOCOMMAND ioctl, we really only want * to accept CCB types that don't quite make sense to send through a * passthrough driver. XPT_PATH_INQ is an exception to this, as stated * in the CAM spec. */ case CAMIOCOMMAND: { union ccb *ccb; union ccb *inccb; struct cam_eb *bus; inccb = (union ccb *)addr; bus = xpt_find_bus(inccb->ccb_h.path_id); if (bus == NULL) { error = EINVAL; break; } switch(inccb->ccb_h.func_code) { case XPT_SCAN_BUS: case XPT_RESET_BUS: if ((inccb->ccb_h.target_id != CAM_TARGET_WILDCARD) || (inccb->ccb_h.target_lun != CAM_LUN_WILDCARD)) { error = EINVAL; break; } /* FALLTHROUGH */ case XPT_PATH_INQ: case XPT_ENG_INQ: case XPT_SCAN_LUN: ccb = xpt_alloc_ccb(); CAM_SIM_LOCK(bus->sim); /* Ensure passed in target/lun supported on this bus. */ if ((inccb->ccb_h.target_id != CAM_TARGET_WILDCARD) || (inccb->ccb_h.target_lun != CAM_LUN_WILDCARD)) { if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, inccb->ccb_h.path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { error = EINVAL; CAM_SIM_UNLOCK(bus->sim); xpt_free_ccb(ccb); break; } xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, inccb->ccb_h.pinfo.priority); ccb->ccb_h.func_code = XPT_PATH_INQ; xpt_action(ccb); xpt_free_path(ccb->ccb_h.path); if ((inccb->ccb_h.target_id != CAM_TARGET_WILDCARD && inccb->ccb_h.target_id > ccb->cpi.max_target) || (inccb->ccb_h.target_lun != CAM_LUN_WILDCARD && inccb->ccb_h.target_lun > ccb->cpi.max_lun)) { error = EINVAL; CAM_SIM_UNLOCK(bus->sim); xpt_free_ccb(ccb); break; } } /* * Create a path using the bus, target, and lun the * user passed in. */ if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, inccb->ccb_h.path_id, inccb->ccb_h.target_id, inccb->ccb_h.target_lun) != CAM_REQ_CMP){ error = EINVAL; CAM_SIM_UNLOCK(bus->sim); xpt_free_ccb(ccb); break; } /* Ensure all of our fields are correct */ xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, inccb->ccb_h.pinfo.priority); xpt_merge_ccb(ccb, inccb); ccb->ccb_h.cbfcnp = xptdone; cam_periph_runccb(ccb, NULL, 0, 0, NULL); bcopy(ccb, inccb, sizeof(union ccb)); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); CAM_SIM_UNLOCK(bus->sim); break; case XPT_DEBUG: { union ccb ccb; /* * This is an immediate CCB, so it's okay to * allocate it on the stack. */ CAM_SIM_LOCK(bus->sim); /* * Create a path using the bus, target, and lun the * user passed in. */ if (xpt_create_path(&ccb.ccb_h.path, xpt_periph, inccb->ccb_h.path_id, inccb->ccb_h.target_id, inccb->ccb_h.target_lun) != CAM_REQ_CMP){ error = EINVAL; CAM_SIM_UNLOCK(bus->sim); break; } /* Ensure all of our fields are correct */ xpt_setup_ccb(&ccb.ccb_h, ccb.ccb_h.path, inccb->ccb_h.pinfo.priority); xpt_merge_ccb(&ccb, inccb); ccb.ccb_h.cbfcnp = xptdone; xpt_action(&ccb); CAM_SIM_UNLOCK(bus->sim); bcopy(&ccb, inccb, sizeof(union ccb)); xpt_free_path(ccb.ccb_h.path); break; } case XPT_DEV_MATCH: { struct cam_periph_map_info mapinfo; struct cam_path *old_path; /* * We can't deal with physical addresses for this * type of transaction. */ if (inccb->ccb_h.flags & CAM_DATA_PHYS) { error = EINVAL; break; } /* * Save this in case the caller had it set to * something in particular. */ old_path = inccb->ccb_h.path; /* * We really don't need a path for the matching * code. The path is needed because of the * debugging statements in xpt_action(). They * assume that the CCB has a valid path. */ inccb->ccb_h.path = xpt_periph->path; bzero(&mapinfo, sizeof(mapinfo)); /* * Map the pattern and match buffers into kernel * virtual address space. */ error = cam_periph_mapmem(inccb, &mapinfo); if (error) { inccb->ccb_h.path = old_path; break; } /* * This is an immediate CCB, we can send it on directly. */ xpt_action(inccb); /* * Map the buffers back into user space. */ cam_periph_unmapmem(inccb, &mapinfo); inccb->ccb_h.path = old_path; error = 0; break; } default: error = ENOTSUP; break; } xpt_release_bus(bus); break; } /* * This is the getpassthru ioctl. It takes a XPT_GDEVLIST ccb as input, * with the periphal driver name and unit name filled in. The other * fields don't really matter as input. The passthrough driver name * ("pass"), and unit number are passed back in the ccb. The current * device generation number, and the index into the device peripheral * driver list, and the status are also passed back. Note that * since we do everything in one pass, unlike the XPT_GDEVLIST ccb, * we never return a status of CAM_GDEVLIST_LIST_CHANGED. It is * (or rather should be) impossible for the device peripheral driver * list to change since we look at the whole thing in one pass, and * we do it with lock protection. * */ case CAMGETPASSTHRU: { union ccb *ccb; struct cam_periph *periph; struct periph_driver **p_drv; char *name; u_int unit; u_int cur_generation; int base_periph_found; int splbreaknum; ccb = (union ccb *)addr; unit = ccb->cgdl.unit_number; name = ccb->cgdl.periph_name; /* * Every 100 devices, we want to drop our lock protection to * give the software interrupt handler a chance to run. * Most systems won't run into this check, but this should * avoid starvation in the software interrupt handler in * large systems. */ splbreaknum = 100; ccb = (union ccb *)addr; base_periph_found = 0; /* * Sanity check -- make sure we don't get a null peripheral * driver name. */ if (*ccb->cgdl.periph_name == '\0') { error = EINVAL; break; } /* Keep the list from changing while we traverse it */ mtx_lock(&xsoftc.xpt_topo_lock); ptstartover: cur_generation = xsoftc.xpt_generation; /* first find our driver in the list of drivers */ for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) if (strcmp((*p_drv)->driver_name, name) == 0) break; if (*p_drv == NULL) { mtx_unlock(&xsoftc.xpt_topo_lock); ccb->ccb_h.status = CAM_REQ_CMP_ERR; ccb->cgdl.status = CAM_GDEVLIST_ERROR; *ccb->cgdl.periph_name = '\0'; ccb->cgdl.unit_number = 0; error = ENOENT; break; } /* * Run through every peripheral instance of this driver * and check to see whether it matches the unit passed * in by the user. If it does, get out of the loops and * find the passthrough driver associated with that * peripheral driver. */ for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL; periph = TAILQ_NEXT(periph, unit_links)) { if (periph->unit_number == unit) { break; } else if (--splbreaknum == 0) { mtx_unlock(&xsoftc.xpt_topo_lock); mtx_lock(&xsoftc.xpt_topo_lock); splbreaknum = 100; if (cur_generation != xsoftc.xpt_generation) goto ptstartover; } } /* * If we found the peripheral driver that the user passed * in, go through all of the peripheral drivers for that * particular device and look for a passthrough driver. */ if (periph != NULL) { struct cam_ed *device; int i; base_periph_found = 1; device = periph->path->device; for (i = 0, periph = SLIST_FIRST(&device->periphs); periph != NULL; periph = SLIST_NEXT(periph, periph_links), i++) { /* * Check to see whether we have a * passthrough device or not. */ if (strcmp(periph->periph_name, "pass") == 0) { /* * Fill in the getdevlist fields. */ strcpy(ccb->cgdl.periph_name, periph->periph_name); ccb->cgdl.unit_number = periph->unit_number; if (SLIST_NEXT(periph, periph_links)) ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; else ccb->cgdl.status = CAM_GDEVLIST_LAST_DEVICE; ccb->cgdl.generation = device->generation; ccb->cgdl.index = i; /* * Fill in some CCB header fields * that the user may want. */ ccb->ccb_h.path_id = periph->path->bus->path_id; ccb->ccb_h.target_id = periph->path->target->target_id; ccb->ccb_h.target_lun = periph->path->device->lun_id; ccb->ccb_h.status = CAM_REQ_CMP; break; } } } /* * If the periph is null here, one of two things has * happened. The first possibility is that we couldn't * find the unit number of the particular peripheral driver * that the user is asking about. e.g. the user asks for * the passthrough driver for "da11". We find the list of * "da" peripherals all right, but there is no unit 11. * The other possibility is that we went through the list * of peripheral drivers attached to the device structure, * but didn't find one with the name "pass". Either way, * we return ENOENT, since we couldn't find something. */ if (periph == NULL) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; ccb->cgdl.status = CAM_GDEVLIST_ERROR; *ccb->cgdl.periph_name = '\0'; ccb->cgdl.unit_number = 0; error = ENOENT; /* * It is unfortunate that this is even necessary, * but there are many, many clueless users out there. * If this is true, the user is looking for the * passthrough driver, but doesn't have one in his * kernel. */ if (base_periph_found == 1) { printf("xptioctl: pass driver is not in the " "kernel\n"); printf("xptioctl: put \"device pass\" in " "your kernel config file\n"); } } mtx_unlock(&xsoftc.xpt_topo_lock); break; } default: error = ENOTTY; break; } return(error); } static int cam_module_event_handler(module_t mod, int what, void *arg) { int error; switch (what) { case MOD_LOAD: if ((error = xpt_init(NULL)) != 0) return (error); break; case MOD_UNLOAD: return EBUSY; default: return EOPNOTSUPP; } return 0; } static void xpt_rescan_done(struct cam_periph *periph, union ccb *done_ccb) { if (done_ccb->ccb_h.ppriv_ptr1 == NULL) { xpt_free_path(done_ccb->ccb_h.path); xpt_free_ccb(done_ccb); } else { done_ccb->ccb_h.cbfcnp = done_ccb->ccb_h.ppriv_ptr1; (*done_ccb->ccb_h.cbfcnp)(periph, done_ccb); } xpt_release_boot(); } /* thread to handle bus rescans */ static void xpt_scanner_thread(void *dummy) { union ccb *ccb; struct cam_sim *sim; xpt_lock_buses(); for (;;) { if (TAILQ_EMPTY(&xsoftc.ccb_scanq)) msleep(&xsoftc.ccb_scanq, &xsoftc.xpt_topo_lock, PRIBIO, "ccb_scanq", 0); if ((ccb = (union ccb *)TAILQ_FIRST(&xsoftc.ccb_scanq)) != NULL) { TAILQ_REMOVE(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe); xpt_unlock_buses(); sim = ccb->ccb_h.path->bus->sim; CAM_SIM_LOCK(sim); xpt_action(ccb); CAM_SIM_UNLOCK(sim); xpt_lock_buses(); } } } void xpt_rescan(union ccb *ccb) { struct ccb_hdr *hdr; /* Prepare request */ - if(ccb->ccb_h.path->target->target_id == CAM_TARGET_WILDCARD) + if (ccb->ccb_h.path->target->target_id == CAM_TARGET_WILDCARD || + ccb->ccb_h.path->device->lun_id == CAM_LUN_WILDCARD) ccb->ccb_h.func_code = XPT_SCAN_BUS; else ccb->ccb_h.func_code = XPT_SCAN_LUN; ccb->ccb_h.ppriv_ptr1 = ccb->ccb_h.cbfcnp; ccb->ccb_h.cbfcnp = xpt_rescan_done; xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, CAM_PRIORITY_XPT); /* Don't make duplicate entries for the same paths. */ xpt_lock_buses(); if (ccb->ccb_h.ppriv_ptr1 == NULL) { TAILQ_FOREACH(hdr, &xsoftc.ccb_scanq, sim_links.tqe) { if (xpt_path_comp(hdr->path, ccb->ccb_h.path) == 0) { wakeup(&xsoftc.ccb_scanq); xpt_unlock_buses(); xpt_print(ccb->ccb_h.path, "rescan already queued\n"); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); return; } } } TAILQ_INSERT_TAIL(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe); xsoftc.buses_to_config++; wakeup(&xsoftc.ccb_scanq); xpt_unlock_buses(); } /* Functions accessed by the peripheral drivers */ static int xpt_init(void *dummy) { struct cam_sim *xpt_sim; struct cam_path *path; struct cam_devq *devq; cam_status status; TAILQ_INIT(&xsoftc.xpt_busses); TAILQ_INIT(&cam_simq); TAILQ_INIT(&xsoftc.ccb_scanq); STAILQ_INIT(&xsoftc.highpowerq); xsoftc.num_highpower = CAM_MAX_HIGHPOWER; mtx_init(&cam_simq_lock, "CAM SIMQ lock", NULL, MTX_DEF); mtx_init(&xsoftc.xpt_lock, "XPT lock", NULL, MTX_DEF); mtx_init(&xsoftc.xpt_topo_lock, "XPT topology lock", NULL, MTX_DEF); /* * The xpt layer is, itself, the equivelent of a SIM. * Allow 16 ccbs in the ccb pool for it. This should * give decent parallelism when we probe busses and * perform other XPT functions. */ devq = cam_simq_alloc(16); xpt_sim = cam_sim_alloc(xptaction, xptpoll, "xpt", /*softc*/NULL, /*unit*/0, /*mtx*/&xsoftc.xpt_lock, /*max_dev_transactions*/0, /*max_tagged_dev_transactions*/0, devq); if (xpt_sim == NULL) return (ENOMEM); mtx_lock(&xsoftc.xpt_lock); if ((status = xpt_bus_register(xpt_sim, NULL, 0)) != CAM_SUCCESS) { mtx_unlock(&xsoftc.xpt_lock); printf("xpt_init: xpt_bus_register failed with status %#x," " failing attach\n", status); return (EINVAL); } /* * Looking at the XPT from the SIM layer, the XPT is * the equivelent of a peripheral driver. Allocate * a peripheral driver entry for us. */ if ((status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)) != CAM_REQ_CMP) { mtx_unlock(&xsoftc.xpt_lock); printf("xpt_init: xpt_create_path failed with status %#x," " failing attach\n", status); return (EINVAL); } cam_periph_alloc(xptregister, NULL, NULL, NULL, "xpt", CAM_PERIPH_BIO, path, NULL, 0, xpt_sim); xpt_free_path(path); mtx_unlock(&xsoftc.xpt_lock); /* Install our software interrupt handlers */ swi_add(NULL, "cambio", camisr, NULL, SWI_CAMBIO, INTR_MPSAFE, &cambio_ih); /* * Register a callback for when interrupts are enabled. */ xsoftc.xpt_config_hook = (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook), M_CAMXPT, M_NOWAIT | M_ZERO); if (xsoftc.xpt_config_hook == NULL) { printf("xpt_init: Cannot malloc config hook " "- failing attach\n"); return (ENOMEM); } xsoftc.xpt_config_hook->ich_func = xpt_config; if (config_intrhook_establish(xsoftc.xpt_config_hook) != 0) { free (xsoftc.xpt_config_hook, M_CAMXPT); printf("xpt_init: config_intrhook_establish failed " "- failing attach\n"); } return (0); } static cam_status xptregister(struct cam_periph *periph, void *arg) { struct cam_sim *xpt_sim; if (periph == NULL) { printf("xptregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } xpt_sim = (struct cam_sim *)arg; xpt_sim->softc = periph; xpt_periph = periph; periph->softc = NULL; return(CAM_REQ_CMP); } int32_t xpt_add_periph(struct cam_periph *periph) { struct cam_ed *device; int32_t status; struct periph_list *periph_head; mtx_assert(periph->sim->mtx, MA_OWNED); device = periph->path->device; periph_head = &device->periphs; status = CAM_REQ_CMP; if (device != NULL) { /* * Make room for this peripheral * so it will fit in the queue * when it's scheduled to run */ status = camq_resize(&device->drvq, device->drvq.array_size + 1); device->generation++; SLIST_INSERT_HEAD(periph_head, periph, periph_links); } mtx_lock(&xsoftc.xpt_topo_lock); xsoftc.xpt_generation++; mtx_unlock(&xsoftc.xpt_topo_lock); return (status); } void xpt_remove_periph(struct cam_periph *periph) { struct cam_ed *device; mtx_assert(periph->sim->mtx, MA_OWNED); device = periph->path->device; if (device != NULL) { struct periph_list *periph_head; periph_head = &device->periphs; /* Release the slot for this peripheral */ camq_resize(&device->drvq, device->drvq.array_size - 1); device->generation++; SLIST_REMOVE(periph_head, periph, cam_periph, periph_links); } mtx_lock(&xsoftc.xpt_topo_lock); xsoftc.xpt_generation++; mtx_unlock(&xsoftc.xpt_topo_lock); } void xpt_announce_periph(struct cam_periph *periph, char *announce_string) { - struct ccb_pathinq cpi; - struct ccb_trans_settings cts; - struct cam_path *path; - u_int speed; - u_int freq; - u_int mb; + struct cam_path *path = periph->path; mtx_assert(periph->sim->mtx, MA_OWNED); - path = periph->path; - /* - * To ensure that this is printed in one piece, - * mask out CAM interrupts. - */ printf("%s%d at %s%d bus %d scbus%d target %d lun %d\n", periph->periph_name, periph->unit_number, path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id, path->bus->path_id, path->target->target_id, path->device->lun_id); printf("%s%d: ", periph->periph_name, periph->unit_number); if (path->device->protocol == PROTO_SCSI) - scsi_print_inquiry(&path->device->inq_data); + scsi_print_inquiry(&path->device->inq_data); else if (path->device->protocol == PROTO_ATA || path->device->protocol == PROTO_SATAPM) ata_print_ident(&path->device->ident_data); else - printf("Unknown protocol device\n"); + printf("Unknown protocol device\n"); if (bootverbose && path->device->serial_num_len > 0) { /* Don't wrap the screen - print only the first 60 chars */ printf("%s%d: Serial Number %.60s\n", periph->periph_name, periph->unit_number, path->device->serial_num); } - xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL); - cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; - cts.type = CTS_TYPE_CURRENT_SETTINGS; - xpt_action((union ccb*)&cts); - if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - return; - } - - /* Ask the SIM for its base transfer speed */ - xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL); - cpi.ccb_h.func_code = XPT_PATH_INQ; - xpt_action((union ccb *)&cpi); - - speed = cpi.base_transfer_speed; - freq = 0; - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { - struct ccb_trans_settings_spi *spi = - &cts.xport_specific.spi; - - if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0 - && spi->sync_offset != 0) { - freq = scsi_calc_syncsrate(spi->sync_period); - speed = freq; - } - if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) - speed *= (0x01 << spi->bus_width); - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { - struct ccb_trans_settings_fc *fc = - &cts.xport_specific.fc; - - if (fc->valid & CTS_FC_VALID_SPEED) - speed = fc->bitrate; - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SAS) { - struct ccb_trans_settings_sas *sas = - &cts.xport_specific.sas; - - if (sas->valid & CTS_SAS_VALID_SPEED) - speed = sas->bitrate; - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_ATA) { - struct ccb_trans_settings_ata *ata = - &cts.xport_specific.ata; - - if (ata->valid & CTS_ATA_VALID_MODE) - speed = ata_mode2speed(ata->mode); - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SATA) { - struct ccb_trans_settings_sata *sata = - &cts.xport_specific.sata; - - if (sata->valid & CTS_SATA_VALID_REVISION) - speed = ata_revision2speed(sata->revision); - } - - mb = speed / 1000; - if (mb > 0) - printf("%s%d: %d.%03dMB/s transfers", - periph->periph_name, periph->unit_number, - mb, speed % 1000); - else - printf("%s%d: %dKB/s transfers", periph->periph_name, - periph->unit_number, speed); - /* Report additional information about SPI connections */ - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { - struct ccb_trans_settings_spi *spi; - - spi = &cts.xport_specific.spi; - if (freq != 0) { - printf(" (%d.%03dMHz%s, offset %d", freq / 1000, - freq % 1000, - (spi->ppr_options & MSG_EXT_PPR_DT_REQ) != 0 - ? " DT" : "", - spi->sync_offset); - } - if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0 - && spi->bus_width > 0) { - if (freq != 0) { - printf(", "); - } else { - printf(" ("); - } - printf("%dbit)", 8 * (0x01 << spi->bus_width)); - } else if (freq != 0) { - printf(")"); - } - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { - struct ccb_trans_settings_fc *fc; - - fc = &cts.xport_specific.fc; - if (fc->valid & CTS_FC_VALID_WWNN) - printf(" WWNN 0x%llx", (long long) fc->wwnn); - if (fc->valid & CTS_FC_VALID_WWPN) - printf(" WWPN 0x%llx", (long long) fc->wwpn); - if (fc->valid & CTS_FC_VALID_PORT) - printf(" PortID 0x%x", fc->port); - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_ATA) { - struct ccb_trans_settings_ata *ata = - &cts.xport_specific.ata; - - printf(" ("); - if (ata->valid & CTS_ATA_VALID_MODE) - printf("%s, ", ata_mode2string(ata->mode)); - if ((ata->valid & CTS_ATA_VALID_ATAPI) && ata->atapi != 0) - printf("ATAPI %dbytes, ", ata->atapi); - if (ata->valid & CTS_ATA_VALID_BYTECOUNT) - printf("PIO %dbytes", ata->bytecount); - printf(")"); - } - if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SATA) { - struct ccb_trans_settings_sata *sata = - &cts.xport_specific.sata; - - printf(" ("); - if (sata->valid & CTS_SATA_VALID_REVISION) - printf("SATA %d.x, ", sata->revision); - else - printf("SATA, "); - if (sata->valid & CTS_SATA_VALID_MODE) - printf("%s, ", ata_mode2string(sata->mode)); - if ((sata->valid & CTS_ATA_VALID_ATAPI) && sata->atapi != 0) - printf("ATAPI %dbytes, ", sata->atapi); - if (sata->valid & CTS_SATA_VALID_BYTECOUNT) - printf("PIO %dbytes", sata->bytecount); - printf(")"); - } + /* Announce transport details. */ + (*(path->bus->xport->announce))(periph); + /* Announce command queueing. */ if (path->device->inq_flags & SID_CmdQue || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) { printf("\n%s%d: Command Queueing enabled", periph->periph_name, periph->unit_number); } - printf("\n"); - - /* - * We only want to print the caller's announce string if they've - * passed one in.. - */ + /* Announce caller's details if they've passed in. */ if (announce_string != NULL) printf("%s%d: %s\n", periph->periph_name, periph->unit_number, announce_string); } static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_eb *bus) { dev_match_ret retval; int i; retval = DM_RET_NONE; /* * If we aren't given something to match against, that's an error. */ if (bus == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this bus matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_DESCEND | DM_RET_COPY); for (i = 0; i < num_patterns; i++) { struct bus_match_pattern *cur_pattern; /* * If the pattern in question isn't for a bus node, we * aren't interested. However, we do indicate to the * calling routine that we should continue descending the * tree, since the user wants to match against lower-level * EDT elements. */ if (patterns[i].type != DEV_MATCH_BUS) { if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_DESCEND; continue; } cur_pattern = &patterns[i].pattern.bus_pattern; /* * If they want to match any bus node, we give them any * device node. */ if (cur_pattern->flags == BUS_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * If we've already decided on an action, go ahead * and return. */ if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == BUS_MATCH_NONE) continue; if (((cur_pattern->flags & BUS_MATCH_PATH) != 0) && (cur_pattern->path_id != bus->path_id)) continue; if (((cur_pattern->flags & BUS_MATCH_BUS_ID) != 0) && (cur_pattern->bus_id != bus->sim->bus_id)) continue; if (((cur_pattern->flags & BUS_MATCH_UNIT) != 0) && (cur_pattern->unit_number != bus->sim->unit_number)) continue; if (((cur_pattern->flags & BUS_MATCH_NAME) != 0) && (strncmp(cur_pattern->dev_name, bus->sim->sim_name, DEV_IDLEN) != 0)) continue; /* * If we get to this point, the user definitely wants * information on this bus. So tell the caller to copy the * data out. */ retval |= DM_RET_COPY; /* * If the return action has been set to descend, then we * know that we've already seen a non-bus matching * expression, therefore we need to further descend the tree. * This won't change by continuing around the loop, so we * go ahead and return. If we haven't seen a non-bus * matching expression, we keep going around the loop until * we exhaust the matching expressions. We'll set the stop * flag once we fall out of the loop. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) return(retval); } /* * If the return action hasn't been set to descend yet, that means * we haven't seen anything other than bus matching patterns. So * tell the caller to stop descending the tree -- the user doesn't * want to match against lower level tree elements. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_STOP; return(retval); } static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_ed *device) { dev_match_ret retval; int i; retval = DM_RET_NONE; /* * If we aren't given something to match against, that's an error. */ if (device == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this device matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_DESCEND | DM_RET_COPY); for (i = 0; i < num_patterns; i++) { struct device_match_pattern *cur_pattern; /* * If the pattern in question isn't for a device node, we * aren't interested. */ if (patterns[i].type != DEV_MATCH_DEVICE) { if ((patterns[i].type == DEV_MATCH_PERIPH) && ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE)) retval |= DM_RET_DESCEND; continue; } cur_pattern = &patterns[i].pattern.device_pattern; /* * If they want to match any device node, we give them any * device node. */ if (cur_pattern->flags == DEV_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * If we've already decided on an action, go ahead * and return. */ if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == DEV_MATCH_NONE) continue; if (((cur_pattern->flags & DEV_MATCH_PATH) != 0) && (cur_pattern->path_id != device->target->bus->path_id)) continue; if (((cur_pattern->flags & DEV_MATCH_TARGET) != 0) && (cur_pattern->target_id != device->target->target_id)) continue; if (((cur_pattern->flags & DEV_MATCH_LUN) != 0) && (cur_pattern->target_lun != device->lun_id)) continue; if (((cur_pattern->flags & DEV_MATCH_INQUIRY) != 0) && (cam_quirkmatch((caddr_t)&device->inq_data, (caddr_t)&cur_pattern->inq_pat, 1, sizeof(cur_pattern->inq_pat), scsi_static_inquiry_match) == NULL)) continue; /* * If we get to this point, the user definitely wants * information on this device. So tell the caller to copy * the data out. */ retval |= DM_RET_COPY; /* * If the return action has been set to descend, then we * know that we've already seen a peripheral matching * expression, therefore we need to further descend the tree. * This won't change by continuing around the loop, so we * go ahead and return. If we haven't seen a peripheral * matching expression, we keep going around the loop until * we exhaust the matching expressions. We'll set the stop * flag once we fall out of the loop. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) return(retval); } /* * If the return action hasn't been set to descend yet, that means * we haven't seen any peripheral matching patterns. So tell the * caller to stop descending the tree -- the user doesn't want to * match against lower level tree elements. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_STOP; return(retval); } /* * Match a single peripheral against any number of match patterns. */ static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_periph *periph) { dev_match_ret retval; int i; /* * If we aren't given something to match against, that's an error. */ if (periph == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this peripheral matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_STOP | DM_RET_COPY); /* * There aren't any nodes below a peripheral node, so there's no * reason to descend the tree any further. */ retval = DM_RET_STOP; for (i = 0; i < num_patterns; i++) { struct periph_match_pattern *cur_pattern; /* * If the pattern in question isn't for a peripheral, we * aren't interested. */ if (patterns[i].type != DEV_MATCH_PERIPH) continue; cur_pattern = &patterns[i].pattern.periph_pattern; /* * If they want to match on anything, then we will do so. */ if (cur_pattern->flags == PERIPH_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * We've already set the return action to stop, * since there are no nodes below peripherals in * the tree. */ return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == PERIPH_MATCH_NONE) continue; if (((cur_pattern->flags & PERIPH_MATCH_PATH) != 0) && (cur_pattern->path_id != periph->path->bus->path_id)) continue; /* * For the target and lun id's, we have to make sure the * target and lun pointers aren't NULL. The xpt peripheral * has a wildcard target and device. */ if (((cur_pattern->flags & PERIPH_MATCH_TARGET) != 0) && ((periph->path->target == NULL) ||(cur_pattern->target_id != periph->path->target->target_id))) continue; if (((cur_pattern->flags & PERIPH_MATCH_LUN) != 0) && ((periph->path->device == NULL) || (cur_pattern->target_lun != periph->path->device->lun_id))) continue; if (((cur_pattern->flags & PERIPH_MATCH_UNIT) != 0) && (cur_pattern->unit_number != periph->unit_number)) continue; if (((cur_pattern->flags & PERIPH_MATCH_NAME) != 0) && (strncmp(cur_pattern->periph_name, periph->periph_name, DEV_IDLEN) != 0)) continue; /* * If we get to this point, the user definitely wants * information on this peripheral. So tell the caller to * copy the data out. */ retval |= DM_RET_COPY; /* * The return action has already been set to stop, since * peripherals don't have any nodes below them in the EDT. */ return(retval); } /* * If we get to this point, the peripheral that was passed in * doesn't match any of the patterns. */ return(retval); } static int xptedtbusfunc(struct cam_eb *bus, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; /* * If our position is for something deeper in the tree, that means * that we've already seen this node. So, we keep going down. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target != NULL)) retval = DM_RET_DESCEND; else retval = xptbusmatch(cdm->patterns, cdm->num_patterns, bus); /* * If we got an error, bail out of the search. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this bus out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS; cdm->pos.cookie.bus = bus; cdm->pos.generations[CAM_BUS_GENERATION]= xsoftc.bus_generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_BUS; cdm->matches[j].result.bus_result.path_id = bus->path_id; cdm->matches[j].result.bus_result.bus_id = bus->sim->bus_id; cdm->matches[j].result.bus_result.unit_number = bus->sim->unit_number; strncpy(cdm->matches[j].result.bus_result.dev_name, bus->sim->sim_name, DEV_IDLEN); } /* * If the user is only interested in busses, there's no * reason to descend to the next level in the tree. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) return(1); /* * If there is a target generation recorded, check it to * make sure the target list hasn't changed. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (bus == cdm->pos.cookie.bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.generations[CAM_TARGET_GENERATION] != 0) && (cdm->pos.generations[CAM_TARGET_GENERATION] != bus->generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target != NULL)) return(xpttargettraverse(bus, (struct cam_et *)cdm->pos.cookie.target, xptedttargetfunc, arg)); else return(xpttargettraverse(bus, NULL, xptedttargetfunc, arg)); } static int xptedttargetfunc(struct cam_et *target, void *arg) { struct ccb_dev_match *cdm; cdm = (struct ccb_dev_match *)arg; /* * If there is a device list generation recorded, check it to * make sure the device list hasn't changed. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == target->bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.generations[CAM_DEV_GENERATION] != 0) && (cdm->pos.generations[CAM_DEV_GENERATION] != target->generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == target->bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device != NULL)) return(xptdevicetraverse(target, (struct cam_ed *)cdm->pos.cookie.device, xptedtdevicefunc, arg)); else return(xptdevicetraverse(target, NULL, xptedtdevicefunc, arg)); } static int xptedtdevicefunc(struct cam_ed *device, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; /* * If our position is for something deeper in the tree, that means * that we've already seen this node. So, we keep going down. */ if ((cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device == device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) retval = DM_RET_DESCEND; else retval = xptdevicematch(cdm->patterns, cdm->num_patterns, device); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this device out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE; cdm->pos.cookie.bus = device->target->bus; cdm->pos.generations[CAM_BUS_GENERATION]= xsoftc.bus_generation; cdm->pos.cookie.target = device->target; cdm->pos.generations[CAM_TARGET_GENERATION] = device->target->bus->generation; cdm->pos.cookie.device = device; cdm->pos.generations[CAM_DEV_GENERATION] = device->target->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_DEVICE; cdm->matches[j].result.device_result.path_id = device->target->bus->path_id; cdm->matches[j].result.device_result.target_id = device->target->target_id; cdm->matches[j].result.device_result.target_lun = device->lun_id; cdm->matches[j].result.device_result.protocol = device->protocol; bcopy(&device->inq_data, &cdm->matches[j].result.device_result.inq_data, sizeof(struct scsi_inquiry_data)); bcopy(&device->ident_data, &cdm->matches[j].result.device_result.ident_data, sizeof(struct ata_params)); /* Let the user know whether this device is unconfigured */ if (device->flags & CAM_DEV_UNCONFIGURED) cdm->matches[j].result.device_result.flags = DEV_RESULT_UNCONFIGURED; else cdm->matches[j].result.device_result.flags = DEV_RESULT_NOFLAG; } /* * If the user isn't interested in peripherals, don't descend * the tree any further. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) return(1); /* * If there is a peripheral list generation recorded, make sure * it hasn't changed. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (device->target->bus == cdm->pos.cookie.bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (device->target == cdm->pos.cookie.target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (device == cdm->pos.cookie.device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != 0) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != device->generation)){ cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == device->target->bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == device->target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device == device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) return(xptperiphtraverse(device, (struct cam_periph *)cdm->pos.cookie.periph, xptedtperiphfunc, arg)); else return(xptperiphtraverse(device, NULL, xptedtperiphfunc, arg)); } static int xptedtperiphfunc(struct cam_periph *periph, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this peripheral out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE | CAM_DEV_POS_PERIPH; cdm->pos.cookie.bus = periph->path->bus; cdm->pos.generations[CAM_BUS_GENERATION]= xsoftc.bus_generation; cdm->pos.cookie.target = periph->path->target; cdm->pos.generations[CAM_TARGET_GENERATION] = periph->path->bus->generation; cdm->pos.cookie.device = periph->path->device; cdm->pos.generations[CAM_DEV_GENERATION] = periph->path->target->generation; cdm->pos.cookie.periph = periph; cdm->pos.generations[CAM_PERIPH_GENERATION] = periph->path->device->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_PERIPH; cdm->matches[j].result.periph_result.path_id = periph->path->bus->path_id; cdm->matches[j].result.periph_result.target_id = periph->path->target->target_id; cdm->matches[j].result.periph_result.target_lun = periph->path->device->lun_id; cdm->matches[j].result.periph_result.unit_number = periph->unit_number; strncpy(cdm->matches[j].result.periph_result.periph_name, periph->periph_name, DEV_IDLEN); } return(1); } static int xptedtmatch(struct ccb_dev_match *cdm) { int ret; cdm->num_matches = 0; /* * Check the bus list generation. If it has changed, the user * needs to reset everything and start over. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.generations[CAM_BUS_GENERATION] != 0) && (cdm->pos.generations[CAM_BUS_GENERATION] != xsoftc.bus_generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus != NULL)) ret = xptbustraverse((struct cam_eb *)cdm->pos.cookie.bus, xptedtbusfunc, cdm); else ret = xptbustraverse(NULL, xptedtbusfunc, cdm); /* * If we get back 0, that means that we had to stop before fully * traversing the EDT. It also means that one of the subroutines * has set the status field to the proper value. If we get back 1, * we've fully traversed the EDT and copied out any matching entries. */ if (ret == 1) cdm->status = CAM_DEV_MATCH_LAST; return(ret); } static int xptplistpdrvfunc(struct periph_driver **pdrv, void *arg) { struct ccb_dev_match *cdm; cdm = (struct ccb_dev_match *)arg; if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv == pdrv) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != 0) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != (*pdrv)->generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv == pdrv) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) return(xptpdperiphtraverse(pdrv, (struct cam_periph *)cdm->pos.cookie.periph, xptplistperiphfunc, arg)); else return(xptpdperiphtraverse(pdrv, NULL,xptplistperiphfunc, arg)); } static int xptplistperiphfunc(struct cam_periph *periph, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this peripheral out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { struct periph_driver **pdrv; pdrv = NULL; bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_PDRV | CAM_DEV_POS_PDPTR | CAM_DEV_POS_PERIPH; /* * This may look a bit non-sensical, but it is * actually quite logical. There are very few * peripheral drivers, and bloating every peripheral * structure with a pointer back to its parent * peripheral driver linker set entry would cost * more in the long run than doing this quick lookup. */ for (pdrv = periph_drivers; *pdrv != NULL; pdrv++) { if (strcmp((*pdrv)->driver_name, periph->periph_name) == 0) break; } if (*pdrv == NULL) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } cdm->pos.cookie.pdrv = pdrv; /* * The periph generation slot does double duty, as * does the periph pointer slot. They are used for * both edt and pdrv lookups and positioning. */ cdm->pos.cookie.periph = periph; cdm->pos.generations[CAM_PERIPH_GENERATION] = (*pdrv)->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_PERIPH; cdm->matches[j].result.periph_result.path_id = periph->path->bus->path_id; /* * The transport layer peripheral doesn't have a target or * lun. */ if (periph->path->target) cdm->matches[j].result.periph_result.target_id = periph->path->target->target_id; else cdm->matches[j].result.periph_result.target_id = -1; if (periph->path->device) cdm->matches[j].result.periph_result.target_lun = periph->path->device->lun_id; else cdm->matches[j].result.periph_result.target_lun = -1; cdm->matches[j].result.periph_result.unit_number = periph->unit_number; strncpy(cdm->matches[j].result.periph_result.periph_name, periph->periph_name, DEV_IDLEN); } return(1); } static int xptperiphlistmatch(struct ccb_dev_match *cdm) { int ret; cdm->num_matches = 0; /* * At this point in the edt traversal function, we check the bus * list generation to make sure that no busses have been added or * removed since the user last sent a XPT_DEV_MATCH ccb through. * For the peripheral driver list traversal function, however, we * don't have to worry about new peripheral driver types coming or * going; they're in a linker set, and therefore can't change * without a recompile. */ if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv != NULL)) ret = xptpdrvtraverse( (struct periph_driver **)cdm->pos.cookie.pdrv, xptplistpdrvfunc, cdm); else ret = xptpdrvtraverse(NULL, xptplistpdrvfunc, cdm); /* * If we get back 0, that means that we had to stop before fully * traversing the peripheral driver tree. It also means that one of * the subroutines has set the status field to the proper value. If * we get back 1, we've fully traversed the EDT and copied out any * matching entries. */ if (ret == 1) cdm->status = CAM_DEV_MATCH_LAST; return(ret); } static int xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg) { struct cam_eb *bus, *next_bus; int retval; retval = 1; mtx_lock(&xsoftc.xpt_topo_lock); for (bus = (start_bus ? start_bus : TAILQ_FIRST(&xsoftc.xpt_busses)); bus != NULL; bus = next_bus) { next_bus = TAILQ_NEXT(bus, links); mtx_unlock(&xsoftc.xpt_topo_lock); CAM_SIM_LOCK(bus->sim); retval = tr_func(bus, arg); CAM_SIM_UNLOCK(bus->sim); if (retval == 0) return(retval); mtx_lock(&xsoftc.xpt_topo_lock); } mtx_unlock(&xsoftc.xpt_topo_lock); return(retval); } int xpt_sim_opened(struct cam_sim *sim) { struct cam_eb *bus; struct cam_et *target; struct cam_ed *device; struct cam_periph *periph; KASSERT(sim->refcount >= 1, ("sim->refcount >= 1")); mtx_assert(sim->mtx, MA_OWNED); mtx_lock(&xsoftc.xpt_topo_lock); TAILQ_FOREACH(bus, &xsoftc.xpt_busses, links) { if (bus->sim != sim) continue; TAILQ_FOREACH(target, &bus->et_entries, links) { TAILQ_FOREACH(device, &target->ed_entries, links) { SLIST_FOREACH(periph, &device->periphs, periph_links) { if (periph->refcount > 0) { mtx_unlock(&xsoftc.xpt_topo_lock); return (1); } } } } } mtx_unlock(&xsoftc.xpt_topo_lock); return (0); } static int xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, xpt_targetfunc_t *tr_func, void *arg) { struct cam_et *target, *next_target; int retval; retval = 1; for (target = (start_target ? start_target : TAILQ_FIRST(&bus->et_entries)); target != NULL; target = next_target) { next_target = TAILQ_NEXT(target, links); retval = tr_func(target, arg); if (retval == 0) return(retval); } return(retval); } static int xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, xpt_devicefunc_t *tr_func, void *arg) { struct cam_ed *device, *next_device; int retval; retval = 1; for (device = (start_device ? start_device : TAILQ_FIRST(&target->ed_entries)); device != NULL; device = next_device) { next_device = TAILQ_NEXT(device, links); retval = tr_func(device, arg); if (retval == 0) return(retval); } return(retval); } static int xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg) { struct cam_periph *periph, *next_periph; int retval; retval = 1; for (periph = (start_periph ? start_periph : SLIST_FIRST(&device->periphs)); periph != NULL; periph = next_periph) { next_periph = SLIST_NEXT(periph, periph_links); retval = tr_func(periph, arg); if (retval == 0) return(retval); } return(retval); } static int xptpdrvtraverse(struct periph_driver **start_pdrv, xpt_pdrvfunc_t *tr_func, void *arg) { struct periph_driver **pdrv; int retval; retval = 1; /* * We don't traverse the peripheral driver list like we do the * other lists, because it is a linker set, and therefore cannot be * changed during runtime. If the peripheral driver list is ever * re-done to be something other than a linker set (i.e. it can * change while the system is running), the list traversal should * be modified to work like the other traversal functions. */ for (pdrv = (start_pdrv ? start_pdrv : periph_drivers); *pdrv != NULL; pdrv++) { retval = tr_func(pdrv, arg); if (retval == 0) return(retval); } return(retval); } static int xptpdperiphtraverse(struct periph_driver **pdrv, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg) { struct cam_periph *periph, *next_periph; int retval; retval = 1; for (periph = (start_periph ? start_periph : TAILQ_FIRST(&(*pdrv)->units)); periph != NULL; periph = next_periph) { next_periph = TAILQ_NEXT(periph, unit_links); retval = tr_func(periph, arg); if (retval == 0) return(retval); } return(retval); } static int xptdefbusfunc(struct cam_eb *bus, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_BUS) { xpt_busfunc_t *tr_func; tr_func = (xpt_busfunc_t *)tr_config->tr_func; return(tr_func(bus, tr_config->tr_arg)); } else return(xpttargettraverse(bus, NULL, xptdeftargetfunc, arg)); } static int xptdeftargetfunc(struct cam_et *target, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_TARGET) { xpt_targetfunc_t *tr_func; tr_func = (xpt_targetfunc_t *)tr_config->tr_func; return(tr_func(target, tr_config->tr_arg)); } else return(xptdevicetraverse(target, NULL, xptdefdevicefunc, arg)); } static int xptdefdevicefunc(struct cam_ed *device, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_DEVICE) { xpt_devicefunc_t *tr_func; tr_func = (xpt_devicefunc_t *)tr_config->tr_func; return(tr_func(device, tr_config->tr_arg)); } else return(xptperiphtraverse(device, NULL, xptdefperiphfunc, arg)); } static int xptdefperiphfunc(struct cam_periph *periph, void *arg) { struct xpt_traverse_config *tr_config; xpt_periphfunc_t *tr_func; tr_config = (struct xpt_traverse_config *)arg; tr_func = (xpt_periphfunc_t *)tr_config->tr_func; /* * Unlike the other default functions, we don't check for depth * here. The peripheral driver level is the last level in the EDT, * so if we're here, we should execute the function in question. */ return(tr_func(periph, tr_config->tr_arg)); } /* * Execute the given function for every bus in the EDT. */ static int xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_BUS; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } /* * Execute the given function for every device in the EDT. */ static int xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_DEVICE; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } static int xptsetasyncfunc(struct cam_ed *device, void *arg) { struct cam_path path; struct ccb_getdev cgd; struct ccb_setasync *csa = (struct ccb_setasync *)arg; /* * Don't report unconfigured devices (Wildcard devs, * devices only for target mode, device instances * that have been invalidated but are waiting for * their last reference count to be released). */ if ((device->flags & CAM_DEV_UNCONFIGURED) != 0) return (1); xpt_compile_path(&path, NULL, device->target->bus->path_id, device->target->target_id, device->lun_id); xpt_setup_ccb(&cgd.ccb_h, &path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); csa->callback(csa->callback_arg, AC_FOUND_DEVICE, &path, &cgd); xpt_release_path(&path); return(1); } static int xptsetasyncbusfunc(struct cam_eb *bus, void *arg) { struct cam_path path; struct ccb_pathinq cpi; struct ccb_setasync *csa = (struct ccb_setasync *)arg; xpt_compile_path(&path, /*periph*/NULL, bus->sim->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); xpt_setup_ccb(&cpi.ccb_h, &path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); csa->callback(csa->callback_arg, AC_PATH_REGISTERED, &path, &cpi); xpt_release_path(&path); return(1); } void xpt_action(union ccb *start_ccb) { CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_action\n")); start_ccb->ccb_h.status = CAM_REQ_INPROG; /* Compatibility for RL-unaware code. */ if (CAM_PRIORITY_TO_RL(start_ccb->ccb_h.pinfo.priority) == 0) start_ccb->ccb_h.pinfo.priority += CAM_PRIORITY_NORMAL - 1; (*(start_ccb->ccb_h.path->bus->xport->action))(start_ccb); } void xpt_action_default(union ccb *start_ccb) { CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_action_default\n")); switch (start_ccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct cam_ed *device; #ifdef CAMDEBUG char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; struct cam_path *path; path = start_ccb->ccb_h.path; #endif /* * For the sake of compatibility with SCSI-1 * devices that may not understand the identify * message, we include lun information in the * second byte of all commands. SCSI-1 specifies * that luns are a 3 bit value and reserves only 3 * bits for lun information in the CDB. Later * revisions of the SCSI spec allow for more than 8 * luns, but have deprecated lun information in the * CDB. So, if the lun won't fit, we must omit. * * Also be aware that during initial probing for devices, * the inquiry information is unknown but initialized to 0. * This means that this code will be exercised while probing * devices with an ANSI revision greater than 2. */ device = start_ccb->ccb_h.path->device; if (device->protocol_version <= SCSI_REV_2 && start_ccb->ccb_h.target_lun < 8 && (start_ccb->ccb_h.flags & CAM_CDB_POINTER) == 0) { start_ccb->csio.cdb_io.cdb_bytes[1] |= start_ccb->ccb_h.target_lun << 5; } start_ccb->csio.scsi_status = SCSI_STATUS_OK; CAM_DEBUG(path, CAM_DEBUG_CDB,("%s. CDB: %s\n", scsi_op_desc(start_ccb->csio.cdb_io.cdb_bytes[0], &path->device->inq_data), scsi_cdb_string(start_ccb->csio.cdb_io.cdb_bytes, cdb_str, sizeof(cdb_str)))); } /* FALLTHROUGH */ case XPT_TARGET_IO: case XPT_CONT_TARGET_IO: start_ccb->csio.sense_resid = 0; start_ccb->csio.resid = 0; /* FALLTHROUGH */ case XPT_ATA_IO: if (start_ccb->ccb_h.func_code == XPT_ATA_IO) { start_ccb->ataio.resid = 0; } case XPT_RESET_DEV: case XPT_ENG_EXEC: { struct cam_path *path = start_ccb->ccb_h.path; int frozen; frozen = cam_ccbq_insert_ccb(&path->device->ccbq, start_ccb); path->device->sim->devq->alloc_openings += frozen; if (frozen > 0) xpt_run_dev_allocq(path->bus); if (xpt_schedule_dev_sendq(path->bus, path->device)) xpt_run_dev_sendq(path->bus); break; } case XPT_CALC_GEOMETRY: { struct cam_sim *sim; /* Filter out garbage */ if (start_ccb->ccg.block_size == 0 || start_ccb->ccg.volume_size == 0) { start_ccb->ccg.cylinders = 0; start_ccb->ccg.heads = 0; start_ccb->ccg.secs_per_track = 0; start_ccb->ccb_h.status = CAM_REQ_CMP; break; } #ifdef PC98 /* * In a PC-98 system, geometry translation depens on * the "real" device geometry obtained from mode page 4. * SCSI geometry translation is performed in the * initialization routine of the SCSI BIOS and the result * stored in host memory. If the translation is available * in host memory, use it. If not, rely on the default * translation the device driver performs. */ if (scsi_da_bios_params(&start_ccb->ccg) != 0) { start_ccb->ccb_h.status = CAM_REQ_CMP; break; } #endif sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_ABORT: { union ccb* abort_ccb; abort_ccb = start_ccb->cab.abort_ccb; if (XPT_FC_IS_DEV_QUEUED(abort_ccb)) { if (abort_ccb->ccb_h.pinfo.index >= 0) { struct cam_ccbq *ccbq; struct cam_ed *device; device = abort_ccb->ccb_h.path->device; ccbq = &device->ccbq; device->sim->devq->alloc_openings -= cam_ccbq_remove_ccb(ccbq, abort_ccb); abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN; xpt_freeze_devq(abort_ccb->ccb_h.path, 1); xpt_done(abort_ccb); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } if (abort_ccb->ccb_h.pinfo.index == CAM_UNQUEUED_INDEX && (abort_ccb->ccb_h.status & CAM_SIM_QUEUED) == 0) { /* * We've caught this ccb en route to * the SIM. Flag it for abort and the * SIM will do so just before starting * real work on the CCB. */ abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN; xpt_freeze_devq(abort_ccb->ccb_h.path, 1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } } if (XPT_FC_IS_QUEUED(abort_ccb) && (abort_ccb->ccb_h.pinfo.index == CAM_DONEQ_INDEX)) { /* * It's already completed but waiting * for our SWI to get to it. */ start_ccb->ccb_h.status = CAM_UA_ABORT; break; } /* * If we weren't able to take care of the abort request * in the XPT, pass the request down to the SIM for processing. */ } /* FALLTHROUGH */ case XPT_ACCEPT_TARGET_IO: case XPT_EN_LUN: case XPT_IMMED_NOTIFY: case XPT_NOTIFY_ACK: case XPT_RESET_BUS: case XPT_IMMEDIATE_NOTIFY: case XPT_NOTIFY_ACKNOWLEDGE: case XPT_GET_SIM_KNOB: case XPT_SET_SIM_KNOB: { struct cam_sim *sim; sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_PATH_INQ: { struct cam_sim *sim; sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_PATH_STATS: start_ccb->cpis.last_reset = start_ccb->ccb_h.path->bus->last_reset; start_ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_GDEV_TYPE: { struct cam_ed *dev; dev = start_ccb->ccb_h.path->device; if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) { start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { struct ccb_getdev *cgd; struct cam_eb *bus; struct cam_et *tar; cgd = &start_ccb->cgd; bus = cgd->ccb_h.path->bus; tar = cgd->ccb_h.path->target; cgd->protocol = dev->protocol; cgd->inq_data = dev->inq_data; cgd->ident_data = dev->ident_data; cgd->inq_flags = dev->inq_flags; cgd->ccb_h.status = CAM_REQ_CMP; cgd->serial_num_len = dev->serial_num_len; if ((dev->serial_num_len > 0) && (dev->serial_num != NULL)) bcopy(dev->serial_num, cgd->serial_num, dev->serial_num_len); } break; } case XPT_GDEV_STATS: { struct cam_ed *dev; dev = start_ccb->ccb_h.path->device; if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) { start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { struct ccb_getdevstats *cgds; struct cam_eb *bus; struct cam_et *tar; cgds = &start_ccb->cgds; bus = cgds->ccb_h.path->bus; tar = cgds->ccb_h.path->target; cgds->dev_openings = dev->ccbq.dev_openings; cgds->dev_active = dev->ccbq.dev_active; cgds->devq_openings = dev->ccbq.devq_openings; cgds->devq_queued = dev->ccbq.queue.entries; cgds->held = dev->ccbq.held; cgds->last_reset = tar->last_reset; cgds->maxtags = dev->maxtags; cgds->mintags = dev->mintags; if (timevalcmp(&tar->last_reset, &bus->last_reset, <)) cgds->last_reset = bus->last_reset; cgds->ccb_h.status = CAM_REQ_CMP; } break; } case XPT_GDEVLIST: { struct cam_periph *nperiph; struct periph_list *periph_head; struct ccb_getdevlist *cgdl; u_int i; struct cam_ed *device; int found; found = 0; /* * Don't want anyone mucking with our data. */ device = start_ccb->ccb_h.path->device; periph_head = &device->periphs; cgdl = &start_ccb->cgdl; /* * Check and see if the list has changed since the user * last requested a list member. If so, tell them that the * list has changed, and therefore they need to start over * from the beginning. */ if ((cgdl->index != 0) && (cgdl->generation != device->generation)) { cgdl->status = CAM_GDEVLIST_LIST_CHANGED; break; } /* * Traverse the list of peripherals and attempt to find * the requested peripheral. */ for (nperiph = SLIST_FIRST(periph_head), i = 0; (nperiph != NULL) && (i <= cgdl->index); nperiph = SLIST_NEXT(nperiph, periph_links), i++) { if (i == cgdl->index) { strncpy(cgdl->periph_name, nperiph->periph_name, DEV_IDLEN); cgdl->unit_number = nperiph->unit_number; found = 1; } } if (found == 0) { cgdl->status = CAM_GDEVLIST_ERROR; break; } if (nperiph == NULL) cgdl->status = CAM_GDEVLIST_LAST_DEVICE; else cgdl->status = CAM_GDEVLIST_MORE_DEVS; cgdl->index++; cgdl->generation = device->generation; cgdl->ccb_h.status = CAM_REQ_CMP; break; } case XPT_DEV_MATCH: { dev_pos_type position_type; struct ccb_dev_match *cdm; cdm = &start_ccb->cdm; /* * There are two ways of getting at information in the EDT. * The first way is via the primary EDT tree. It starts * with a list of busses, then a list of targets on a bus, * then devices/luns on a target, and then peripherals on a * device/lun. The "other" way is by the peripheral driver * lists. The peripheral driver lists are organized by * peripheral driver. (obviously) So it makes sense to * use the peripheral driver list if the user is looking * for something like "da1", or all "da" devices. If the * user is looking for something on a particular bus/target * or lun, it's generally better to go through the EDT tree. */ if (cdm->pos.position_type != CAM_DEV_POS_NONE) position_type = cdm->pos.position_type; else { u_int i; position_type = CAM_DEV_POS_NONE; for (i = 0; i < cdm->num_patterns; i++) { if ((cdm->patterns[i].type == DEV_MATCH_BUS) ||(cdm->patterns[i].type == DEV_MATCH_DEVICE)){ position_type = CAM_DEV_POS_EDT; break; } } if (cdm->num_patterns == 0) position_type = CAM_DEV_POS_EDT; else if (position_type == CAM_DEV_POS_NONE) position_type = CAM_DEV_POS_PDRV; } switch(position_type & CAM_DEV_POS_TYPEMASK) { case CAM_DEV_POS_EDT: xptedtmatch(cdm); break; case CAM_DEV_POS_PDRV: xptperiphlistmatch(cdm); break; default: cdm->status = CAM_DEV_MATCH_ERROR; break; } if (cdm->status == CAM_DEV_MATCH_ERROR) start_ccb->ccb_h.status = CAM_REQ_CMP_ERR; else start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_SASYNC_CB: { struct ccb_setasync *csa; struct async_node *cur_entry; struct async_list *async_head; u_int32_t added; csa = &start_ccb->csa; added = csa->event_enable; async_head = &csa->ccb_h.path->device->asyncs; /* * If there is already an entry for us, simply * update it. */ cur_entry = SLIST_FIRST(async_head); while (cur_entry != NULL) { if ((cur_entry->callback_arg == csa->callback_arg) && (cur_entry->callback == csa->callback)) break; cur_entry = SLIST_NEXT(cur_entry, links); } if (cur_entry != NULL) { /* * If the request has no flags set, * remove the entry. */ added &= ~cur_entry->event_enable; if (csa->event_enable == 0) { SLIST_REMOVE(async_head, cur_entry, async_node, links); xpt_release_device(csa->ccb_h.path->device); free(cur_entry, M_CAMXPT); } else { cur_entry->event_enable = csa->event_enable; } csa->event_enable = added; } else { cur_entry = malloc(sizeof(*cur_entry), M_CAMXPT, M_NOWAIT); if (cur_entry == NULL) { csa->ccb_h.status = CAM_RESRC_UNAVAIL; break; } cur_entry->event_enable = csa->event_enable; cur_entry->callback_arg = csa->callback_arg; cur_entry->callback = csa->callback; SLIST_INSERT_HEAD(async_head, cur_entry, links); xpt_acquire_device(csa->ccb_h.path->device); } start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_REL_SIMQ: { struct ccb_relsim *crs; struct cam_ed *dev; crs = &start_ccb->crs; dev = crs->ccb_h.path->device; if (dev == NULL) { crs->ccb_h.status = CAM_DEV_NOT_THERE; break; } if ((crs->release_flags & RELSIM_ADJUST_OPENINGS) != 0) { if (INQ_DATA_TQ_ENABLED(&dev->inq_data)) { /* Don't ever go below one opening */ if (crs->openings > 0) { xpt_dev_ccbq_resize(crs->ccb_h.path, crs->openings); if (bootverbose) { xpt_print(crs->ccb_h.path, "tagged openings now %d\n", crs->openings); } } } } if ((crs->release_flags & RELSIM_RELEASE_AFTER_TIMEOUT) != 0) { if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { /* * Just extend the old timeout and decrement * the freeze count so that a single timeout * is sufficient for releasing the queue. */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; callout_stop(&dev->callout); } else { start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } callout_reset(&dev->callout, (crs->release_timeout * hz) / 1000, xpt_release_devq_timeout, dev); dev->flags |= CAM_DEV_REL_TIMEOUT_PENDING; } if ((crs->release_flags & RELSIM_RELEASE_AFTER_CMDCMPLT) != 0) { if ((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0) { /* * Decrement the freeze count so that a single * completion is still sufficient to unfreeze * the queue. */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; } else { dev->flags |= CAM_DEV_REL_ON_COMPLETE; start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } } if ((crs->release_flags & RELSIM_RELEASE_AFTER_QEMPTY) != 0) { if ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 || (dev->ccbq.dev_active == 0)) { start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; } else { dev->flags |= CAM_DEV_REL_ON_QUEUE_EMPTY; start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } } if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) == 0) { xpt_release_devq_rl(crs->ccb_h.path, /*runlevel*/ (crs->release_flags & RELSIM_RELEASE_RUNLEVEL) ? crs->release_timeout : 0, /*count*/1, /*run_queue*/TRUE); } start_ccb->crs.qfrozen_cnt = dev->ccbq.queue.qfrozen_cnt[0]; start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_DEBUG: { #ifdef CAMDEBUG #ifdef CAM_DEBUG_DELAY cam_debug_delay = CAM_DEBUG_DELAY; #endif cam_dflags = start_ccb->cdbg.flags; if (cam_dpath != NULL) { xpt_free_path(cam_dpath); cam_dpath = NULL; } if (cam_dflags != CAM_DEBUG_NONE) { if (xpt_create_path(&cam_dpath, xpt_periph, start_ccb->ccb_h.path_id, start_ccb->ccb_h.target_id, start_ccb->ccb_h.target_lun) != CAM_REQ_CMP) { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; cam_dflags = CAM_DEBUG_NONE; } else { start_ccb->ccb_h.status = CAM_REQ_CMP; xpt_print(cam_dpath, "debugging flags now %x\n", cam_dflags); } } else { cam_dpath = NULL; start_ccb->ccb_h.status = CAM_REQ_CMP; } #else /* !CAMDEBUG */ start_ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; #endif /* CAMDEBUG */ break; } case XPT_FREEZE_QUEUE: { struct ccb_relsim *crs = &start_ccb->crs; xpt_freeze_devq_rl(crs->ccb_h.path, /*runlevel*/ (crs->release_flags & RELSIM_RELEASE_RUNLEVEL) ? crs->release_timeout : 0, /*count*/1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_NOOP: if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0) xpt_freeze_devq(start_ccb->ccb_h.path, 1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; default: case XPT_SDEV_TYPE: case XPT_TERM_IO: case XPT_ENG_INQ: /* XXX Implement */ start_ccb->ccb_h.status = CAM_PROVIDE_FAIL; break; } } void xpt_polled_action(union ccb *start_ccb) { u_int32_t timeout; struct cam_sim *sim; struct cam_devq *devq; struct cam_ed *dev; timeout = start_ccb->ccb_h.timeout; sim = start_ccb->ccb_h.path->bus->sim; devq = sim->devq; dev = start_ccb->ccb_h.path->device; mtx_assert(sim->mtx, MA_OWNED); /* * Steal an opening so that no other queued requests * can get it before us while we simulate interrupts. */ dev->ccbq.devq_openings--; dev->ccbq.dev_openings--; while(((devq != NULL && devq->send_openings <= 0) || dev->ccbq.dev_openings < 0) && (--timeout > 0)) { DELAY(1000); (*(sim->sim_poll))(sim); camisr_runqueue(&sim->sim_doneq); } dev->ccbq.devq_openings++; dev->ccbq.dev_openings++; if (timeout != 0) { xpt_action(start_ccb); while(--timeout > 0) { (*(sim->sim_poll))(sim); camisr_runqueue(&sim->sim_doneq); if ((start_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) break; DELAY(1000); } if (timeout == 0) { /* * XXX Is it worth adding a sim_timeout entry * point so we can attempt recovery? If * this is only used for dumps, I don't think * it is. */ start_ccb->ccb_h.status = CAM_CMD_TIMEOUT; } } else { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; } } /* * Schedule a peripheral driver to receive a ccb when it's * target device has space for more transactions. */ void xpt_schedule(struct cam_periph *perph, u_int32_t new_priority) { struct cam_ed *device; int runq = 0; mtx_assert(perph->sim->mtx, MA_OWNED); CAM_DEBUG(perph->path, CAM_DEBUG_TRACE, ("xpt_schedule\n")); device = perph->path->device; if (periph_is_queued(perph)) { /* Simply reorder based on new priority */ CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, (" change priority to %d\n", new_priority)); if (new_priority < perph->pinfo.priority) { camq_change_priority(&device->drvq, perph->pinfo.index, new_priority); runq = xpt_schedule_dev_allocq(perph->path->bus, device); } } else { /* New entry on the queue */ CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, (" added periph to queue\n")); perph->pinfo.priority = new_priority; perph->pinfo.generation = ++device->drvq.generation; camq_insert(&device->drvq, &perph->pinfo); runq = xpt_schedule_dev_allocq(perph->path->bus, device); } if (runq != 0) { CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, (" calling xpt_run_devq\n")); xpt_run_dev_allocq(perph->path->bus); } } /* * Schedule a device to run on a given queue. * If the device was inserted as a new entry on the queue, * return 1 meaning the device queue should be run. If we * were already queued, implying someone else has already * started the queue, return 0 so the caller doesn't attempt * to run the queue. */ int xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo, u_int32_t new_priority) { int retval; u_int32_t old_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_schedule_dev\n")); old_priority = pinfo->priority; /* * Are we already queued? */ if (pinfo->index != CAM_UNQUEUED_INDEX) { /* Simply reorder based on new priority */ if (new_priority < old_priority) { camq_change_priority(queue, pinfo->index, new_priority); CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("changed priority to %d\n", new_priority)); retval = 1; } else retval = 0; } else { /* New entry on the queue */ if (new_priority < old_priority) pinfo->priority = new_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("Inserting onto queue\n")); pinfo->generation = ++queue->generation; camq_insert(queue, pinfo); retval = 1; } return (retval); } static void xpt_run_dev_allocq(struct cam_eb *bus) { struct cam_devq *devq; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_dev_allocq\n")); devq = bus->sim->devq; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, (" qfrozen_cnt == 0x%x, entries == %d, " "openings == %d, active == %d\n", devq->alloc_queue.qfrozen_cnt[0], devq->alloc_queue.entries, devq->alloc_openings, devq->alloc_active)); devq->alloc_queue.qfrozen_cnt[0]++; while ((devq->alloc_queue.entries > 0) && (devq->alloc_openings > 0) && (devq->alloc_queue.qfrozen_cnt[0] <= 1)) { struct cam_ed_qinfo *qinfo; struct cam_ed *device; union ccb *work_ccb; struct cam_periph *drv; struct camq *drvq; qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->alloc_queue, CAMQ_HEAD); device = qinfo->device; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("running device %p\n", device)); drvq = &device->drvq; #ifdef CAMDEBUG if (drvq->entries <= 0) { panic("xpt_run_dev_allocq: " "Device on queue without any work to do"); } #endif if ((work_ccb = xpt_get_ccb(device)) != NULL) { devq->alloc_openings--; devq->alloc_active++; drv = (struct cam_periph*)camq_remove(drvq, CAMQ_HEAD); xpt_setup_ccb(&work_ccb->ccb_h, drv->path, drv->pinfo.priority); CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("calling periph start\n")); drv->periph_start(drv, work_ccb); } else { /* * Malloc failure in alloc_ccb */ /* * XXX add us to a list to be run from free_ccb * if we don't have any ccbs active on this * device queue otherwise we may never get run * again. */ break; } /* We may have more work. Attempt to reschedule. */ xpt_schedule_dev_allocq(bus, device); } devq->alloc_queue.qfrozen_cnt[0]--; } static void xpt_run_dev_sendq(struct cam_eb *bus) { struct cam_devq *devq; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_dev_sendq\n")); devq = bus->sim->devq; devq->send_queue.qfrozen_cnt[0]++; while ((devq->send_queue.entries > 0) && (devq->send_openings > 0) && (devq->send_queue.qfrozen_cnt[0] <= 1)) { struct cam_ed_qinfo *qinfo; struct cam_ed *device; union ccb *work_ccb; struct cam_sim *sim; qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->send_queue, CAMQ_HEAD); device = qinfo->device; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("running device %p\n", device)); work_ccb = cam_ccbq_peek_ccb(&device->ccbq, CAMQ_HEAD); if (work_ccb == NULL) { printf("device on run queue with no ccbs???\n"); continue; } if ((work_ccb->ccb_h.flags & CAM_HIGH_POWER) != 0) { mtx_lock(&xsoftc.xpt_lock); if (xsoftc.num_highpower <= 0) { /* * We got a high power command, but we * don't have any available slots. Freeze * the device queue until we have a slot * available. */ xpt_freeze_devq(work_ccb->ccb_h.path, 1); STAILQ_INSERT_TAIL(&xsoftc.highpowerq, &work_ccb->ccb_h, xpt_links.stqe); mtx_unlock(&xsoftc.xpt_lock); continue; } else { /* * Consume a high power slot while * this ccb runs. */ xsoftc.num_highpower--; } mtx_unlock(&xsoftc.xpt_lock); } cam_ccbq_remove_ccb(&device->ccbq, work_ccb); cam_ccbq_send_ccb(&device->ccbq, work_ccb); devq->send_openings--; devq->send_active++; xpt_schedule_dev_sendq(bus, device); if (work_ccb && (work_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0){ /* * The client wants to freeze the queue * after this CCB is sent. */ xpt_freeze_devq(work_ccb->ccb_h.path, 1); } /* In Target mode, the peripheral driver knows best... */ if (work_ccb->ccb_h.func_code == XPT_SCSI_IO) { if ((device->inq_flags & SID_CmdQue) != 0 && work_ccb->csio.tag_action != CAM_TAG_ACTION_NONE) work_ccb->ccb_h.flags |= CAM_TAG_ACTION_VALID; else /* * Clear this in case of a retried CCB that * failed due to a rejected tag. */ work_ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; } /* * Device queues can be shared among multiple sim instances * that reside on different busses. Use the SIM in the queue * CCB's path, rather than the one in the bus that was passed * into this function. */ sim = work_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, work_ccb); } devq->send_queue.qfrozen_cnt[0]--; } /* * This function merges stuff from the slave ccb into the master ccb, while * keeping important fields in the master ccb constant. */ void xpt_merge_ccb(union ccb *master_ccb, union ccb *slave_ccb) { /* * Pull fields that are valid for peripheral drivers to set * into the master CCB along with the CCB "payload". */ master_ccb->ccb_h.retry_count = slave_ccb->ccb_h.retry_count; master_ccb->ccb_h.func_code = slave_ccb->ccb_h.func_code; master_ccb->ccb_h.timeout = slave_ccb->ccb_h.timeout; master_ccb->ccb_h.flags = slave_ccb->ccb_h.flags; bcopy(&(&slave_ccb->ccb_h)[1], &(&master_ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); } void xpt_setup_ccb(struct ccb_hdr *ccb_h, struct cam_path *path, u_int32_t priority) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_setup_ccb\n")); ccb_h->pinfo.priority = priority; ccb_h->path = path; ccb_h->path_id = path->bus->path_id; if (path->target) ccb_h->target_id = path->target->target_id; else ccb_h->target_id = CAM_TARGET_WILDCARD; if (path->device) { ccb_h->target_lun = path->device->lun_id; ccb_h->pinfo.generation = ++path->device->ccbq.queue.generation; } else { ccb_h->target_lun = CAM_TARGET_WILDCARD; } ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; ccb_h->flags = 0; } /* Path manipulation functions */ cam_status xpt_create_path(struct cam_path **new_path_ptr, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_path *path; cam_status status; path = (struct cam_path *)malloc(sizeof(*path), M_CAMXPT, M_NOWAIT); if (path == NULL) { status = CAM_RESRC_UNAVAIL; return(status); } status = xpt_compile_path(path, perph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) { free(path, M_CAMXPT); path = NULL; } *new_path_ptr = path; return (status); } cam_status xpt_create_path_unlocked(struct cam_path **new_path_ptr, struct cam_periph *periph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_path *path; struct cam_eb *bus = NULL; cam_status status; int need_unlock = 0; path = (struct cam_path *)malloc(sizeof(*path), M_CAMXPT, M_WAITOK); if (path_id != CAM_BUS_WILDCARD) { bus = xpt_find_bus(path_id); if (bus != NULL) { need_unlock = 1; CAM_SIM_LOCK(bus->sim); } } status = xpt_compile_path(path, periph, path_id, target_id, lun_id); if (need_unlock) CAM_SIM_UNLOCK(bus->sim); if (status != CAM_REQ_CMP) { free(path, M_CAMXPT); path = NULL; } *new_path_ptr = path; return (status); } cam_status xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_eb *bus; struct cam_et *target; struct cam_ed *device; cam_status status; status = CAM_REQ_CMP; /* Completed without error */ target = NULL; /* Wildcarded */ device = NULL; /* Wildcarded */ /* * We will potentially modify the EDT, so block interrupts * that may attempt to create cam paths. */ bus = xpt_find_bus(path_id); if (bus == NULL) { status = CAM_PATH_INVALID; } else { target = xpt_find_target(bus, target_id); if (target == NULL) { /* Create one */ struct cam_et *new_target; new_target = xpt_alloc_target(bus, target_id); if (new_target == NULL) { status = CAM_RESRC_UNAVAIL; } else { target = new_target; } } if (target != NULL) { device = xpt_find_device(target, lun_id); if (device == NULL) { /* Create one */ struct cam_ed *new_device; new_device = (*(bus->xport->alloc_device))(bus, target, lun_id); if (new_device == NULL) { status = CAM_RESRC_UNAVAIL; } else { device = new_device; } } } } /* * Only touch the user's data if we are successful. */ if (status == CAM_REQ_CMP) { new_path->periph = perph; new_path->bus = bus; new_path->target = target; new_path->device = device; CAM_DEBUG(new_path, CAM_DEBUG_TRACE, ("xpt_compile_path\n")); } else { if (device != NULL) xpt_release_device(device); if (target != NULL) xpt_release_target(target); if (bus != NULL) xpt_release_bus(bus); } return (status); } void xpt_release_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_release_path\n")); if (path->device != NULL) { xpt_release_device(path->device); path->device = NULL; } if (path->target != NULL) { xpt_release_target(path->target); path->target = NULL; } if (path->bus != NULL) { xpt_release_bus(path->bus); path->bus = NULL; } } void xpt_free_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_free_path\n")); xpt_release_path(path); free(path, M_CAMXPT); } /* * Return -1 for failure, 0 for exact match, 1 for match with wildcards * in path1, 2 for match with wildcards in path2. */ int xpt_path_comp(struct cam_path *path1, struct cam_path *path2) { int retval = 0; if (path1->bus != path2->bus) { if (path1->bus->path_id == CAM_BUS_WILDCARD) retval = 1; else if (path2->bus->path_id == CAM_BUS_WILDCARD) retval = 2; else return (-1); } if (path1->target != path2->target) { if (path1->target->target_id == CAM_TARGET_WILDCARD) { if (retval == 0) retval = 1; } else if (path2->target->target_id == CAM_TARGET_WILDCARD) retval = 2; else return (-1); } if (path1->device != path2->device) { if (path1->device->lun_id == CAM_LUN_WILDCARD) { if (retval == 0) retval = 1; } else if (path2->device->lun_id == CAM_LUN_WILDCARD) retval = 2; else return (-1); } return (retval); } void xpt_print_path(struct cam_path *path) { if (path == NULL) printf("(nopath): "); else { if (path->periph != NULL) printf("(%s%d:", path->periph->periph_name, path->periph->unit_number); else printf("(noperiph:"); if (path->bus != NULL) printf("%s%d:%d:", path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id); else printf("nobus:"); if (path->target != NULL) printf("%d:", path->target->target_id); else printf("X:"); if (path->device != NULL) printf("%d): ", path->device->lun_id); else printf("X): "); } } void xpt_print(struct cam_path *path, const char *fmt, ...) { va_list ap; xpt_print_path(path); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } int xpt_path_string(struct cam_path *path, char *str, size_t str_len) { struct sbuf sb; #ifdef INVARIANTS if (path != NULL && path->bus != NULL) mtx_assert(path->bus->sim->mtx, MA_OWNED); #endif sbuf_new(&sb, str, str_len, 0); if (path == NULL) sbuf_printf(&sb, "(nopath): "); else { if (path->periph != NULL) sbuf_printf(&sb, "(%s%d:", path->periph->periph_name, path->periph->unit_number); else sbuf_printf(&sb, "(noperiph:"); if (path->bus != NULL) sbuf_printf(&sb, "%s%d:%d:", path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id); else sbuf_printf(&sb, "nobus:"); if (path->target != NULL) sbuf_printf(&sb, "%d:", path->target->target_id); else sbuf_printf(&sb, "X:"); if (path->device != NULL) sbuf_printf(&sb, "%d): ", path->device->lun_id); else sbuf_printf(&sb, "X): "); } sbuf_finish(&sb); return(sbuf_len(&sb)); } path_id_t xpt_path_path_id(struct cam_path *path) { mtx_assert(path->bus->sim->mtx, MA_OWNED); return(path->bus->path_id); } target_id_t xpt_path_target_id(struct cam_path *path) { mtx_assert(path->bus->sim->mtx, MA_OWNED); if (path->target != NULL) return (path->target->target_id); else return (CAM_TARGET_WILDCARD); } lun_id_t xpt_path_lun_id(struct cam_path *path) { mtx_assert(path->bus->sim->mtx, MA_OWNED); if (path->device != NULL) return (path->device->lun_id); else return (CAM_LUN_WILDCARD); } struct cam_sim * xpt_path_sim(struct cam_path *path) { return (path->bus->sim); } struct cam_periph* xpt_path_periph(struct cam_path *path) { mtx_assert(path->bus->sim->mtx, MA_OWNED); return (path->periph); } /* * Release a CAM control block for the caller. Remit the cost of the structure * to the device referenced by the path. If the this device had no 'credits' * and peripheral drivers have registered async callbacks for this notification * call them now. */ void xpt_release_ccb(union ccb *free_ccb) { struct cam_path *path; struct cam_ed *device; struct cam_eb *bus; struct cam_sim *sim; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_release_ccb\n")); path = free_ccb->ccb_h.path; device = path->device; bus = path->bus; sim = bus->sim; mtx_assert(sim->mtx, MA_OWNED); cam_ccbq_release_opening(&device->ccbq); if (device->flags & CAM_DEV_RESIZE_QUEUE_NEEDED) { device->flags &= ~CAM_DEV_RESIZE_QUEUE_NEEDED; cam_ccbq_resize(&device->ccbq, device->ccbq.dev_openings + device->ccbq.dev_active); } if (sim->ccb_count > sim->max_ccbs) { xpt_free_ccb(free_ccb); sim->ccb_count--; } else { SLIST_INSERT_HEAD(&sim->ccb_freeq, &free_ccb->ccb_h, xpt_links.sle); } if (sim->devq == NULL) { return; } sim->devq->alloc_openings++; sim->devq->alloc_active--; if (device_is_alloc_queued(device) == 0) xpt_schedule_dev_allocq(bus, device); xpt_run_dev_allocq(bus); } /* Functions accessed by SIM drivers */ static struct xpt_xport xport_default = { .alloc_device = xpt_alloc_device_default, .action = xpt_action_default, .async = xpt_dev_async_default, }; /* * A sim structure, listing the SIM entry points and instance * identification info is passed to xpt_bus_register to hook the SIM * into the CAM framework. xpt_bus_register creates a cam_eb entry * for this new bus and places it in the array of busses and assigns * it a path_id. The path_id may be influenced by "hard wiring" * information specified by the user. Once interrupt services are * available, the bus will be probed. */ int32_t xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus) { struct cam_eb *new_bus; struct cam_eb *old_bus; struct ccb_pathinq cpi; struct cam_path *path; cam_status status; mtx_assert(sim->mtx, MA_OWNED); sim->bus_id = bus; new_bus = (struct cam_eb *)malloc(sizeof(*new_bus), M_CAMXPT, M_NOWAIT); if (new_bus == NULL) { /* Couldn't satisfy request */ return (CAM_RESRC_UNAVAIL); } path = (struct cam_path *)malloc(sizeof(*path), M_CAMXPT, M_NOWAIT); if (path == NULL) { free(new_bus, M_CAMXPT); return (CAM_RESRC_UNAVAIL); } if (strcmp(sim->sim_name, "xpt") != 0) { sim->path_id = xptpathid(sim->sim_name, sim->unit_number, sim->bus_id); } TAILQ_INIT(&new_bus->et_entries); new_bus->path_id = sim->path_id; cam_sim_hold(sim); new_bus->sim = sim; timevalclear(&new_bus->last_reset); new_bus->flags = 0; new_bus->refcount = 1; /* Held until a bus_deregister event */ new_bus->generation = 0; mtx_lock(&xsoftc.xpt_topo_lock); old_bus = TAILQ_FIRST(&xsoftc.xpt_busses); while (old_bus != NULL && old_bus->path_id < new_bus->path_id) old_bus = TAILQ_NEXT(old_bus, links); if (old_bus != NULL) TAILQ_INSERT_BEFORE(old_bus, new_bus, links); else TAILQ_INSERT_TAIL(&xsoftc.xpt_busses, new_bus, links); xsoftc.bus_generation++; mtx_unlock(&xsoftc.xpt_topo_lock); /* * Set a default transport so that a PATH_INQ can be issued to * the SIM. This will then allow for probing and attaching of * a more appropriate transport. */ new_bus->xport = &xport_default; status = xpt_compile_path(path, /*periph*/NULL, sim->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) printf("xpt_compile_path returned %d\n", status); xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status == CAM_REQ_CMP) { switch (cpi.transport) { case XPORT_SPI: case XPORT_SAS: case XPORT_FC: case XPORT_USB: case XPORT_ISCSI: case XPORT_PPB: new_bus->xport = scsi_get_xport(); break; case XPORT_ATA: case XPORT_SATA: new_bus->xport = ata_get_xport(); break; default: new_bus->xport = &xport_default; break; } } /* Notify interested parties */ if (sim->path_id != CAM_XPT_PATH_ID) { union ccb *scan_ccb; xpt_async(AC_PATH_REGISTERED, path, &cpi); /* Initiate bus rescan. */ scan_ccb = xpt_alloc_ccb_nowait(); scan_ccb->ccb_h.path = path; scan_ccb->ccb_h.func_code = XPT_SCAN_BUS; scan_ccb->crcn.flags = 0; xpt_rescan(scan_ccb); } else xpt_free_path(path); return (CAM_SUCCESS); } int32_t xpt_bus_deregister(path_id_t pathid) { struct cam_path bus_path; cam_status status; status = xpt_compile_path(&bus_path, NULL, pathid, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) return (status); xpt_async(AC_LOST_DEVICE, &bus_path, NULL); xpt_async(AC_PATH_DEREGISTERED, &bus_path, NULL); /* Release the reference count held while registered. */ xpt_release_bus(bus_path.bus); xpt_release_path(&bus_path); return (CAM_REQ_CMP); } static path_id_t xptnextfreepathid(void) { struct cam_eb *bus; path_id_t pathid; const char *strval; pathid = 0; mtx_lock(&xsoftc.xpt_topo_lock); bus = TAILQ_FIRST(&xsoftc.xpt_busses); retry: /* Find an unoccupied pathid */ while (bus != NULL && bus->path_id <= pathid) { if (bus->path_id == pathid) pathid++; bus = TAILQ_NEXT(bus, links); } mtx_unlock(&xsoftc.xpt_topo_lock); /* * Ensure that this pathid is not reserved for * a bus that may be registered in the future. */ if (resource_string_value("scbus", pathid, "at", &strval) == 0) { ++pathid; /* Start the search over */ mtx_lock(&xsoftc.xpt_topo_lock); goto retry; } return (pathid); } static path_id_t xptpathid(const char *sim_name, int sim_unit, int sim_bus) { path_id_t pathid; int i, dunit, val; char buf[32]; const char *dname; pathid = CAM_XPT_PATH_ID; snprintf(buf, sizeof(buf), "%s%d", sim_name, sim_unit); i = 0; while ((resource_find_match(&i, &dname, &dunit, "at", buf)) == 0) { if (strcmp(dname, "scbus")) { /* Avoid a bit of foot shooting. */ continue; } if (dunit < 0) /* unwired?! */ continue; if (resource_int_value("scbus", dunit, "bus", &val) == 0) { if (sim_bus == val) { pathid = dunit; break; } } else if (sim_bus == 0) { /* Unspecified matches bus 0 */ pathid = dunit; break; } else { printf("Ambiguous scbus configuration for %s%d " "bus %d, cannot wire down. The kernel " "config entry for scbus%d should " "specify a controller bus.\n" "Scbus will be assigned dynamically.\n", sim_name, sim_unit, sim_bus, dunit); break; } } if (pathid == CAM_XPT_PATH_ID) pathid = xptnextfreepathid(); return (pathid); } void xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) { struct cam_eb *bus; struct cam_et *target, *next_target; struct cam_ed *device, *next_device; mtx_assert(path->bus->sim->mtx, MA_OWNED); CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_async\n")); /* * Most async events come from a CAM interrupt context. In * a few cases, the error recovery code at the peripheral layer, * which may run from our SWI or a process context, may signal * deferred events with a call to xpt_async. */ bus = path->bus; if (async_code == AC_BUS_RESET) { /* Update our notion of when the last reset occurred */ microtime(&bus->last_reset); } for (target = TAILQ_FIRST(&bus->et_entries); target != NULL; target = next_target) { next_target = TAILQ_NEXT(target, links); if (path->target != target && path->target->target_id != CAM_TARGET_WILDCARD && target->target_id != CAM_TARGET_WILDCARD) continue; if (async_code == AC_SENT_BDR) { /* Update our notion of when the last reset occurred */ microtime(&path->target->last_reset); } for (device = TAILQ_FIRST(&target->ed_entries); device != NULL; device = next_device) { next_device = TAILQ_NEXT(device, links); if (path->device != device && path->device->lun_id != CAM_LUN_WILDCARD && device->lun_id != CAM_LUN_WILDCARD) continue; /* * The async callback could free the device. * If it is a broadcast async, it doesn't hold * device reference, so take our own reference. */ xpt_acquire_device(device); (*(bus->xport->async))(async_code, bus, target, device, async_arg); xpt_async_bcast(&device->asyncs, async_code, path, async_arg); xpt_release_device(device); } } /* * If this wasn't a fully wildcarded async, tell all * clients that want all async events. */ if (bus != xpt_periph->path->bus) xpt_async_bcast(&xpt_periph->path->device->asyncs, async_code, path, async_arg); } static void xpt_async_bcast(struct async_list *async_head, u_int32_t async_code, struct cam_path *path, void *async_arg) { struct async_node *cur_entry; cur_entry = SLIST_FIRST(async_head); while (cur_entry != NULL) { struct async_node *next_entry; /* * Grab the next list entry before we call the current * entry's callback. This is because the callback function * can delete its async callback entry. */ next_entry = SLIST_NEXT(cur_entry, links); if ((cur_entry->event_enable & async_code) != 0) cur_entry->callback(cur_entry->callback_arg, async_code, path, async_arg); cur_entry = next_entry; } } static void xpt_dev_async_default(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg) { printf("xpt_dev_async called\n"); } u_int32_t xpt_freeze_devq_rl(struct cam_path *path, cam_rl rl, u_int count) { struct cam_ed *dev = path->device; mtx_assert(path->bus->sim->mtx, MA_OWNED); dev->sim->devq->alloc_openings += cam_ccbq_freeze(&dev->ccbq, rl, count); /* Remove frozen device from allocq. */ if (device_is_alloc_queued(dev) && cam_ccbq_frozen(&dev->ccbq, CAM_PRIORITY_TO_RL( CAMQ_GET_PRIO(&dev->drvq)))) { camq_remove(&dev->sim->devq->alloc_queue, dev->alloc_ccb_entry.pinfo.index); } /* Remove frozen device from sendq. */ if (device_is_send_queued(dev) && cam_ccbq_frozen_top(&dev->ccbq)) { camq_remove(&dev->sim->devq->send_queue, dev->send_ccb_entry.pinfo.index); } return (dev->ccbq.queue.qfrozen_cnt[rl]); } u_int32_t xpt_freeze_devq(struct cam_path *path, u_int count) { return (xpt_freeze_devq_rl(path, 0, count)); } u_int32_t xpt_freeze_simq(struct cam_sim *sim, u_int count) { mtx_assert(sim->mtx, MA_OWNED); sim->devq->send_queue.qfrozen_cnt[0] += count; return (sim->devq->send_queue.qfrozen_cnt[0]); } static void xpt_release_devq_timeout(void *arg) { struct cam_ed *device; device = (struct cam_ed *)arg; xpt_release_devq_device(device, /*rl*/0, /*count*/1, /*run_queue*/TRUE); } void xpt_release_devq(struct cam_path *path, u_int count, int run_queue) { mtx_assert(path->bus->sim->mtx, MA_OWNED); xpt_release_devq_device(path->device, /*rl*/0, count, run_queue); } void xpt_release_devq_rl(struct cam_path *path, cam_rl rl, u_int count, int run_queue) { mtx_assert(path->bus->sim->mtx, MA_OWNED); xpt_release_devq_device(path->device, rl, count, run_queue); } static void xpt_release_devq_device(struct cam_ed *dev, cam_rl rl, u_int count, int run_queue) { if (count > dev->ccbq.queue.qfrozen_cnt[rl]) { #ifdef INVARIANTS printf("xpt_release_devq(%d): requested %u > present %u\n", rl, count, dev->ccbq.queue.qfrozen_cnt[rl]); #endif count = dev->ccbq.queue.qfrozen_cnt[rl]; } dev->sim->devq->alloc_openings -= cam_ccbq_release(&dev->ccbq, rl, count); if (cam_ccbq_frozen(&dev->ccbq, CAM_PRIORITY_TO_RL( CAMQ_GET_PRIO(&dev->drvq))) == 0) { if (xpt_schedule_dev_allocq(dev->target->bus, dev)) xpt_run_dev_allocq(dev->target->bus); } if (cam_ccbq_frozen_top(&dev->ccbq) == 0) { /* * No longer need to wait for a successful * command completion. */ dev->flags &= ~CAM_DEV_REL_ON_COMPLETE; /* * Remove any timeouts that might be scheduled * to release this queue. */ if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { callout_stop(&dev->callout); dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING; } if (run_queue == 0) return; /* * Now that we are unfrozen schedule the * device so any pending transactions are * run. */ if (xpt_schedule_dev_sendq(dev->target->bus, dev)) xpt_run_dev_sendq(dev->target->bus); } } void xpt_release_simq(struct cam_sim *sim, int run_queue) { struct camq *sendq; mtx_assert(sim->mtx, MA_OWNED); sendq = &(sim->devq->send_queue); if (sendq->qfrozen_cnt[0] <= 0) { #ifdef INVARIANTS printf("xpt_release_simq: requested 1 > present %u\n", sendq->qfrozen_cnt[0]); #endif } else sendq->qfrozen_cnt[0]--; if (sendq->qfrozen_cnt[0] == 0) { /* * If there is a timeout scheduled to release this * sim queue, remove it. The queue frozen count is * already at 0. */ if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) != 0){ callout_stop(&sim->callout); sim->flags &= ~CAM_SIM_REL_TIMEOUT_PENDING; } if (run_queue) { struct cam_eb *bus; /* * Now that we are unfrozen run the send queue. */ bus = xpt_find_bus(sim->path_id); xpt_run_dev_sendq(bus); xpt_release_bus(bus); } } } /* * XXX Appears to be unused. */ static void xpt_release_simq_timeout(void *arg) { struct cam_sim *sim; sim = (struct cam_sim *)arg; xpt_release_simq(sim, /* run_queue */ TRUE); } void xpt_done(union ccb *done_ccb) { struct cam_sim *sim; int first; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_done\n")); if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) != 0) { /* * Queue up the request for handling by our SWI handler * any of the "non-immediate" type of ccbs. */ sim = done_ccb->ccb_h.path->bus->sim; TAILQ_INSERT_TAIL(&sim->sim_doneq, &done_ccb->ccb_h, sim_links.tqe); done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX; if ((sim->flags & CAM_SIM_ON_DONEQ) == 0) { mtx_lock(&cam_simq_lock); first = TAILQ_EMPTY(&cam_simq); TAILQ_INSERT_TAIL(&cam_simq, sim, links); mtx_unlock(&cam_simq_lock); sim->flags |= CAM_SIM_ON_DONEQ; if (first) swi_sched(cambio_ih, 0); } } } union ccb * xpt_alloc_ccb() { union ccb *new_ccb; new_ccb = malloc(sizeof(*new_ccb), M_CAMXPT, M_ZERO|M_WAITOK); return (new_ccb); } union ccb * xpt_alloc_ccb_nowait() { union ccb *new_ccb; new_ccb = malloc(sizeof(*new_ccb), M_CAMXPT, M_ZERO|M_NOWAIT); return (new_ccb); } void xpt_free_ccb(union ccb *free_ccb) { free(free_ccb, M_CAMXPT); } /* Private XPT functions */ /* * Get a CAM control block for the caller. Charge the structure to the device * referenced by the path. If the this device has no 'credits' then the * device already has the maximum number of outstanding operations under way * and we return NULL. If we don't have sufficient resources to allocate more * ccbs, we also return NULL. */ static union ccb * xpt_get_ccb(struct cam_ed *device) { union ccb *new_ccb; struct cam_sim *sim; sim = device->sim; if ((new_ccb = (union ccb *)SLIST_FIRST(&sim->ccb_freeq)) == NULL) { new_ccb = xpt_alloc_ccb_nowait(); if (new_ccb == NULL) { return (NULL); } if ((sim->flags & CAM_SIM_MPSAFE) == 0) callout_handle_init(&new_ccb->ccb_h.timeout_ch); SLIST_INSERT_HEAD(&sim->ccb_freeq, &new_ccb->ccb_h, xpt_links.sle); sim->ccb_count++; } cam_ccbq_take_opening(&device->ccbq); SLIST_REMOVE_HEAD(&sim->ccb_freeq, xpt_links.sle); return (new_ccb); } static void xpt_release_bus(struct cam_eb *bus) { if ((--bus->refcount == 0) && (TAILQ_FIRST(&bus->et_entries) == NULL)) { mtx_lock(&xsoftc.xpt_topo_lock); TAILQ_REMOVE(&xsoftc.xpt_busses, bus, links); xsoftc.bus_generation++; mtx_unlock(&xsoftc.xpt_topo_lock); cam_sim_release(bus->sim); free(bus, M_CAMXPT); } } static struct cam_et * xpt_alloc_target(struct cam_eb *bus, target_id_t target_id) { struct cam_et *target; target = (struct cam_et *)malloc(sizeof(*target), M_CAMXPT, M_NOWAIT); if (target != NULL) { struct cam_et *cur_target; TAILQ_INIT(&target->ed_entries); target->bus = bus; target->target_id = target_id; target->refcount = 1; target->generation = 0; timevalclear(&target->last_reset); /* * Hold a reference to our parent bus so it * will not go away before we do. */ bus->refcount++; /* Insertion sort into our bus's target list */ cur_target = TAILQ_FIRST(&bus->et_entries); while (cur_target != NULL && cur_target->target_id < target_id) cur_target = TAILQ_NEXT(cur_target, links); if (cur_target != NULL) { TAILQ_INSERT_BEFORE(cur_target, target, links); } else { TAILQ_INSERT_TAIL(&bus->et_entries, target, links); } bus->generation++; } return (target); } static void xpt_release_target(struct cam_et *target) { if ((--target->refcount == 0) && (TAILQ_FIRST(&target->ed_entries) == NULL)) { TAILQ_REMOVE(&target->bus->et_entries, target, links); target->bus->generation++; xpt_release_bus(target->bus); free(target, M_CAMXPT); } } static struct cam_ed * xpt_alloc_device_default(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct cam_ed *device, *cur_device; device = xpt_alloc_device(bus, target, lun_id); if (device == NULL) return (NULL); device->mintags = 1; device->maxtags = 1; bus->sim->max_ccbs += device->ccbq.devq_openings; cur_device = TAILQ_FIRST(&target->ed_entries); while (cur_device != NULL && cur_device->lun_id < lun_id) cur_device = TAILQ_NEXT(cur_device, links); if (cur_device != NULL) { TAILQ_INSERT_BEFORE(cur_device, device, links); } else { TAILQ_INSERT_TAIL(&target->ed_entries, device, links); } target->generation++; return (device); } struct cam_ed * xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct cam_ed *device; struct cam_devq *devq; cam_status status; /* Make space for us in the device queue on our bus */ devq = bus->sim->devq; status = cam_devq_resize(devq, devq->alloc_queue.array_size + 1); if (status != CAM_REQ_CMP) { device = NULL; } else { device = (struct cam_ed *)malloc(sizeof(*device), M_CAMXPT, M_NOWAIT); } if (device != NULL) { cam_init_pinfo(&device->alloc_ccb_entry.pinfo); device->alloc_ccb_entry.device = device; cam_init_pinfo(&device->send_ccb_entry.pinfo); device->send_ccb_entry.device = device; device->target = target; device->lun_id = lun_id; device->sim = bus->sim; /* Initialize our queues */ if (camq_init(&device->drvq, 0) != 0) { free(device, M_CAMXPT); return (NULL); } if (cam_ccbq_init(&device->ccbq, bus->sim->max_dev_openings) != 0) { camq_fini(&device->drvq); free(device, M_CAMXPT); return (NULL); } SLIST_INIT(&device->asyncs); SLIST_INIT(&device->periphs); device->generation = 0; device->owner = NULL; device->flags = CAM_DEV_UNCONFIGURED; device->tag_delay_count = 0; device->tag_saved_openings = 0; device->refcount = 1; callout_init_mtx(&device->callout, bus->sim->mtx, 0); /* * Hold a reference to our parent target so it * will not go away before we do. */ target->refcount++; } return (device); } void xpt_acquire_device(struct cam_ed *device) { device->refcount++; } void xpt_release_device(struct cam_ed *device) { if (--device->refcount == 0) { struct cam_devq *devq; if (device->alloc_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX || device->send_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX) panic("Removing device while still queued for ccbs"); if ((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) callout_stop(&device->callout); TAILQ_REMOVE(&device->target->ed_entries, device,links); device->target->generation++; device->target->bus->sim->max_ccbs -= device->ccbq.devq_openings; /* Release our slot in the devq */ devq = device->target->bus->sim->devq; cam_devq_resize(devq, devq->alloc_queue.array_size - 1); camq_fini(&device->drvq); cam_ccbq_fini(&device->ccbq); xpt_release_target(device->target); free(device, M_CAMXPT); } } u_int32_t xpt_dev_ccbq_resize(struct cam_path *path, int newopenings) { int diff; int result; struct cam_ed *dev; dev = path->device; diff = newopenings - (dev->ccbq.dev_active + dev->ccbq.dev_openings); result = cam_ccbq_resize(&dev->ccbq, newopenings); if (result == CAM_REQ_CMP && (diff < 0)) { dev->flags |= CAM_DEV_RESIZE_QUEUE_NEEDED; } if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (dev->inq_flags & SID_CmdQue) != 0) dev->tag_saved_openings = newopenings; /* Adjust the global limit */ dev->sim->max_ccbs += diff; return (result); } static struct cam_eb * xpt_find_bus(path_id_t path_id) { struct cam_eb *bus; mtx_lock(&xsoftc.xpt_topo_lock); for (bus = TAILQ_FIRST(&xsoftc.xpt_busses); bus != NULL; bus = TAILQ_NEXT(bus, links)) { if (bus->path_id == path_id) { bus->refcount++; break; } } mtx_unlock(&xsoftc.xpt_topo_lock); return (bus); } static struct cam_et * xpt_find_target(struct cam_eb *bus, target_id_t target_id) { struct cam_et *target; for (target = TAILQ_FIRST(&bus->et_entries); target != NULL; target = TAILQ_NEXT(target, links)) { if (target->target_id == target_id) { target->refcount++; break; } } return (target); } static struct cam_ed * xpt_find_device(struct cam_et *target, lun_id_t lun_id) { struct cam_ed *device; for (device = TAILQ_FIRST(&target->ed_entries); device != NULL; device = TAILQ_NEXT(device, links)) { if (device->lun_id == lun_id) { device->refcount++; break; } } return (device); } void xpt_start_tags(struct cam_path *path) { struct ccb_relsim crs; struct cam_ed *device; struct cam_sim *sim; int newopenings; device = path->device; sim = path->bus->sim; device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; xpt_freeze_devq(path, /*count*/1); device->inq_flags |= SID_CmdQue; if (device->tag_saved_openings != 0) newopenings = device->tag_saved_openings; else newopenings = min(device->maxtags, sim->max_tagged_dev_openings); xpt_dev_ccbq_resize(path, newopenings); xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } void xpt_stop_tags(struct cam_path *path) { struct ccb_relsim crs; struct cam_ed *device; struct cam_sim *sim; device = path->device; sim = path->bus->sim; device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; device->tag_delay_count = 0; xpt_freeze_devq(path, /*count*/1); device->inq_flags &= ~SID_CmdQue; xpt_dev_ccbq_resize(path, sim->max_dev_openings); xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } static void xpt_boot_delay(void *arg) { xpt_release_boot(); } static void xpt_config(void *arg) { /* * Now that interrupts are enabled, go find our devices */ #ifdef CAMDEBUG /* Setup debugging flags and path */ #ifdef CAM_DEBUG_FLAGS cam_dflags = CAM_DEBUG_FLAGS; #else /* !CAM_DEBUG_FLAGS */ cam_dflags = CAM_DEBUG_NONE; #endif /* CAM_DEBUG_FLAGS */ #ifdef CAM_DEBUG_BUS if (cam_dflags != CAM_DEBUG_NONE) { /* * Locking is specifically omitted here. No SIMs have * registered yet, so xpt_create_path will only be searching * empty lists of targets and devices. */ if (xpt_create_path(&cam_dpath, xpt_periph, CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN) != CAM_REQ_CMP) { printf("xpt_config: xpt_create_path() failed for debug" " target %d:%d:%d, debugging disabled\n", CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN); cam_dflags = CAM_DEBUG_NONE; } } else cam_dpath = NULL; #else /* !CAM_DEBUG_BUS */ cam_dpath = NULL; #endif /* CAM_DEBUG_BUS */ #endif /* CAMDEBUG */ /* Register our shutdown event handler */ if ((EVENTHANDLER_REGISTER(shutdown_final, xpt_shutdown, NULL, SHUTDOWN_PRI_FIRST)) == NULL) { printf("xpt_config: failed to register shutdown event.\n"); } periphdriver_init(1); xpt_hold_boot(); callout_init(&xsoftc.boot_callout, 1); callout_reset(&xsoftc.boot_callout, hz * xsoftc.boot_delay / 1000, xpt_boot_delay, NULL); /* Fire up rescan thread. */ if (kproc_create(xpt_scanner_thread, NULL, NULL, 0, 0, "xpt_thrd")) { printf("xpt_config: failed to create rescan thread.\n"); } } void xpt_hold_boot(void) { xpt_lock_buses(); xsoftc.buses_to_config++; xpt_unlock_buses(); } void xpt_release_boot(void) { xpt_lock_buses(); xsoftc.buses_to_config--; if (xsoftc.buses_to_config == 0 && xsoftc.buses_config_done == 0) { struct xpt_task *task; xsoftc.buses_config_done = 1; xpt_unlock_buses(); /* Call manually because we don't have any busses */ task = malloc(sizeof(struct xpt_task), M_CAMXPT, M_NOWAIT); if (task != NULL) { TASK_INIT(&task->task, 0, xpt_finishconfig_task, task); taskqueue_enqueue(taskqueue_thread, &task->task); } } else xpt_unlock_buses(); } /* * If the given device only has one peripheral attached to it, and if that * peripheral is the passthrough driver, announce it. This insures that the * user sees some sort of announcement for every peripheral in their system. */ static int xptpassannouncefunc(struct cam_ed *device, void *arg) { struct cam_periph *periph; int i; for (periph = SLIST_FIRST(&device->periphs), i = 0; periph != NULL; periph = SLIST_NEXT(periph, periph_links), i++); periph = SLIST_FIRST(&device->periphs); if ((i == 1) && (strncmp(periph->periph_name, "pass", 4) == 0)) xpt_announce_periph(periph, NULL); return(1); } static void xpt_finishconfig_task(void *context, int pending) { periphdriver_init(2); /* * Check for devices with no "standard" peripheral driver * attached. For any devices like that, announce the * passthrough driver so the user will see something. */ xpt_for_all_devices(xptpassannouncefunc, NULL); /* Release our hook so that the boot can continue. */ config_intrhook_disestablish(xsoftc.xpt_config_hook); free(xsoftc.xpt_config_hook, M_CAMXPT); xsoftc.xpt_config_hook = NULL; free(context, M_CAMXPT); } /* * Power down all devices when we are going to power down the system. */ static void xpt_shutdown_dev_done(struct cam_periph *periph, union ccb *done_ccb) { /* No-op. We're polling. */ return; } static int xpt_shutdown_dev(struct cam_ed *device, void *arg) { union ccb ccb; struct cam_path path; if (device->flags & CAM_DEV_UNCONFIGURED) return (1); if (device->protocol == PROTO_ATA) { /* Only power down device if it supports power management. */ if ((device->ident_data.support.command1 & ATA_SUPPORT_POWERMGT) == 0) return (1); } else if (device->protocol != PROTO_SCSI) return (1); xpt_compile_path(&path, NULL, device->target->bus->path_id, device->target->target_id, device->lun_id); xpt_setup_ccb(&ccb.ccb_h, &path, CAM_PRIORITY_NORMAL); if (device->protocol == PROTO_ATA) { cam_fill_ataio(&ccb.ataio, 1, xpt_shutdown_dev_done, CAM_DIR_NONE, 0, NULL, 0, 30*1000); ata_28bit_cmd(&ccb.ataio, ATA_SLEEP, 0, 0, 0); } else { scsi_start_stop(&ccb.csio, /*retries*/1, xpt_shutdown_dev_done, MSG_SIMPLE_Q_TAG, /*start*/FALSE, /*load/eject*/FALSE, /*immediate*/TRUE, SSD_FULL_SIZE, /*timeout*/50*1000); } xpt_polled_action(&ccb); if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) xpt_print(&path, "Device power down failed\n"); if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); xpt_release_path(&path); return (1); } static void xpt_shutdown(void * arg, int howto) { if (!xpt_power_down) return; if ((howto & RB_POWEROFF) == 0) return; xpt_for_all_devices(xpt_shutdown_dev, NULL); } cam_status xpt_register_async(int event, ac_callback_t *cbfunc, void *cbarg, struct cam_path *path) { struct ccb_setasync csa; cam_status status; int xptpath = 0; if (path == NULL) { mtx_lock(&xsoftc.xpt_lock); status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) { mtx_unlock(&xsoftc.xpt_lock); return (status); } xptpath = 1; } xpt_setup_ccb(&csa.ccb_h, path, CAM_PRIORITY_NORMAL); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = event; csa.callback = cbfunc; csa.callback_arg = cbarg; xpt_action((union ccb *)&csa); status = csa.ccb_h.status; if (xptpath) { xpt_free_path(path); mtx_unlock(&xsoftc.xpt_lock); if ((status == CAM_REQ_CMP) && (csa.event_enable & AC_FOUND_DEVICE)) { /* * Get this peripheral up to date with all * the currently existing devices. */ xpt_for_all_devices(xptsetasyncfunc, &csa); } if ((status == CAM_REQ_CMP) && (csa.event_enable & AC_PATH_REGISTERED)) { /* * Get this peripheral up to date with all * the currently existing busses. */ xpt_for_all_busses(xptsetasyncbusfunc, &csa); } } return (status); } static void xptaction(struct cam_sim *sim, union ccb *work_ccb) { CAM_DEBUG(work_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xptaction\n")); switch (work_ccb->ccb_h.func_code) { /* Common cases first */ case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi; cpi = &work_ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "", HBA_IDLEN); strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); cpi->unit_number = sim->unit_number; cpi->bus_id = sim->bus_id; cpi->base_transfer_speed = 0; cpi->protocol = PROTO_UNSPECIFIED; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->transport = XPORT_UNSPECIFIED; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(work_ccb); break; } default: work_ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(work_ccb); break; } } /* * The xpt as a "controller" has no interrupt sources, so polling * is a no-op. */ static void xptpoll(struct cam_sim *sim) { } void xpt_lock_buses(void) { mtx_lock(&xsoftc.xpt_topo_lock); } void xpt_unlock_buses(void) { mtx_unlock(&xsoftc.xpt_topo_lock); } static void camisr(void *dummy) { cam_simq_t queue; struct cam_sim *sim; mtx_lock(&cam_simq_lock); TAILQ_INIT(&queue); while (!TAILQ_EMPTY(&cam_simq)) { TAILQ_CONCAT(&queue, &cam_simq, links); mtx_unlock(&cam_simq_lock); while ((sim = TAILQ_FIRST(&queue)) != NULL) { TAILQ_REMOVE(&queue, sim, links); CAM_SIM_LOCK(sim); sim->flags &= ~CAM_SIM_ON_DONEQ; camisr_runqueue(&sim->sim_doneq); CAM_SIM_UNLOCK(sim); } mtx_lock(&cam_simq_lock); } mtx_unlock(&cam_simq_lock); } static void camisr_runqueue(void *V_queue) { cam_isrq_t *queue = V_queue; struct ccb_hdr *ccb_h; while ((ccb_h = TAILQ_FIRST(queue)) != NULL) { int runq; TAILQ_REMOVE(queue, ccb_h, sim_links.tqe); ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; CAM_DEBUG(ccb_h->path, CAM_DEBUG_TRACE, ("camisr\n")); runq = FALSE; if (ccb_h->flags & CAM_HIGH_POWER) { struct highpowerlist *hphead; union ccb *send_ccb; mtx_lock(&xsoftc.xpt_lock); hphead = &xsoftc.highpowerq; send_ccb = (union ccb *)STAILQ_FIRST(hphead); /* * Increment the count since this command is done. */ xsoftc.num_highpower++; /* * Any high powered commands queued up? */ if (send_ccb != NULL) { STAILQ_REMOVE_HEAD(hphead, xpt_links.stqe); mtx_unlock(&xsoftc.xpt_lock); xpt_release_devq(send_ccb->ccb_h.path, /*count*/1, /*runqueue*/TRUE); } else mtx_unlock(&xsoftc.xpt_lock); } if ((ccb_h->func_code & XPT_FC_USER_CCB) == 0) { struct cam_ed *dev; dev = ccb_h->path->device; cam_ccbq_ccb_done(&dev->ccbq, (union ccb *)ccb_h); ccb_h->path->bus->sim->devq->send_active--; ccb_h->path->bus->sim->devq->send_openings++; runq = TRUE; if (((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0 && (ccb_h->status&CAM_STATUS_MASK) != CAM_REQUEUE_REQ) || ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 && (dev->ccbq.dev_active == 0))) { xpt_release_devq(ccb_h->path, /*count*/1, /*run_queue*/FALSE); } if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 && (--dev->tag_delay_count == 0)) xpt_start_tags(ccb_h->path); } if (ccb_h->status & CAM_RELEASE_SIMQ) { xpt_release_simq(ccb_h->path->bus->sim, /*run_queue*/TRUE); ccb_h->status &= ~CAM_RELEASE_SIMQ; runq = FALSE; } if ((ccb_h->flags & CAM_DEV_QFRZDIS) && (ccb_h->status & CAM_DEV_QFRZN)) { xpt_release_devq(ccb_h->path, /*count*/1, /*run_queue*/TRUE); ccb_h->status &= ~CAM_DEV_QFRZN; } else if (runq) { xpt_run_dev_sendq(ccb_h->path->bus); } /* Call the peripheral driver's callback */ (*ccb_h->cbfcnp)(ccb_h->path->periph, (union ccb *)ccb_h); } } Index: projects/ppc64/sys/cam/cam_xpt_internal.h =================================================================== --- projects/ppc64/sys/cam/cam_xpt_internal.h (revision 204271) +++ projects/ppc64/sys/cam/cam_xpt_internal.h (revision 204272) @@ -1,183 +1,182 @@ /*- * Copyright 2009 Scott Long * 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, immediately at the beginning of the file. * 2. 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 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 _CAM_CAM_XPT_INTERNAL_H #define _CAM_CAM_XPT_INTERNAL_H 1 /* Forward Declarations */ struct cam_eb; struct cam_et; struct cam_ed; typedef struct cam_ed * (*xpt_alloc_device_func)(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); typedef void (*xpt_release_device_func)(struct cam_ed *device); typedef void (*xpt_action_func)(union ccb *start_ccb); typedef void (*xpt_dev_async_func)(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); -typedef void (*xpt_announce_periph_func)(struct cam_periph *periph, - char *announce_string); +typedef void (*xpt_announce_periph_func)(struct cam_periph *periph); struct xpt_xport { xpt_alloc_device_func alloc_device; xpt_release_device_func reldev; xpt_action_func action; xpt_dev_async_func async; xpt_announce_periph_func announce; }; /* * Structure for queueing a device in a run queue. * There is one run queue for allocating new ccbs, * and another for sending ccbs to the controller. */ struct cam_ed_qinfo { cam_pinfo pinfo; struct cam_ed *device; }; /* * The CAM EDT (Existing Device Table) contains the device information for * all devices for all busses in the system. The table contains a * cam_ed structure for each device on the bus. */ struct cam_ed { TAILQ_ENTRY(cam_ed) links; struct cam_ed_qinfo alloc_ccb_entry; struct cam_ed_qinfo send_ccb_entry; struct cam_et *target; struct cam_sim *sim; lun_id_t lun_id; struct camq drvq; /* * Queue of type drivers wanting to do * work on this device. */ struct cam_ccbq ccbq; /* Queue of pending ccbs */ struct async_list asyncs; /* Async callback info for this B/T/L */ struct periph_list periphs; /* All attached devices */ u_int generation; /* Generation number */ struct cam_periph *owner; /* Peripheral driver's ownership tag */ void *quirk; /* Oddities about this device */ u_int maxtags; u_int mintags; cam_proto protocol; u_int protocol_version; cam_xport transport; u_int transport_version; struct scsi_inquiry_data inq_data; struct ata_params ident_data; u_int8_t inq_flags; /* * Current settings for inquiry flags. * This allows us to override settings * like disconnection and tagged * queuing for a device. */ u_int8_t queue_flags; /* Queue flags from the control page */ u_int8_t serial_num_len; u_int8_t *serial_num; u_int32_t flags; #define CAM_DEV_UNCONFIGURED 0x01 #define CAM_DEV_REL_TIMEOUT_PENDING 0x02 #define CAM_DEV_REL_ON_COMPLETE 0x04 #define CAM_DEV_REL_ON_QUEUE_EMPTY 0x08 #define CAM_DEV_RESIZE_QUEUE_NEEDED 0x10 #define CAM_DEV_TAG_AFTER_COUNT 0x20 #define CAM_DEV_INQUIRY_DATA_VALID 0x40 #define CAM_DEV_IN_DV 0x80 #define CAM_DEV_DV_HIT_BOTTOM 0x100 #define CAM_DEV_IDENTIFY_DATA_VALID 0x200 u_int32_t tag_delay_count; #define CAM_TAG_DELAY_COUNT 5 u_int32_t tag_saved_openings; u_int32_t refcount; struct callout callout; }; /* * Each target is represented by an ET (Existing Target). These * entries are created when a target is successfully probed with an * identify, and removed when a device fails to respond after a number * of retries, or a bus rescan finds the device missing. */ struct cam_et { TAILQ_HEAD(, cam_ed) ed_entries; TAILQ_ENTRY(cam_et) links; struct cam_eb *bus; target_id_t target_id; u_int32_t refcount; u_int generation; struct timeval last_reset; }; /* * Each bus is represented by an EB (Existing Bus). These entries * are created by calls to xpt_bus_register and deleted by calls to * xpt_bus_deregister. */ struct cam_eb { TAILQ_HEAD(, cam_et) et_entries; TAILQ_ENTRY(cam_eb) links; path_id_t path_id; struct cam_sim *sim; struct timeval last_reset; u_int32_t flags; #define CAM_EB_RUNQ_SCHEDULED 0x01 u_int32_t refcount; u_int generation; device_t parent_dev; struct xpt_xport *xport; }; struct cam_path { struct cam_periph *periph; struct cam_eb *bus; struct cam_et *target; struct cam_ed *device; }; struct xpt_xport * scsi_get_xport(void); struct xpt_xport * ata_get_xport(void); struct cam_ed * xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); void xpt_acquire_device(struct cam_ed *device); void xpt_release_device(struct cam_ed *device); int xpt_schedule_dev(struct camq *queue, cam_pinfo *dev_pinfo, u_int32_t new_priority); u_int32_t xpt_dev_ccbq_resize(struct cam_path *path, int newopenings); void xpt_start_tags(struct cam_path *path); void xpt_stop_tags(struct cam_path *path); MALLOC_DECLARE(M_CAMXPT); #endif Index: projects/ppc64/sys/cam/scsi/scsi_xpt.c =================================================================== --- projects/ppc64/sys/cam/scsi/scsi_xpt.c (revision 204271) +++ projects/ppc64/sys/cam/scsi/scsi_xpt.c (revision 204272) @@ -1,2416 +1,2515 @@ /*- * Implementation of the SCSI Transport * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * 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, immediately at the beginning of the file. * 2. 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 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 #ifdef PC98 #include /* geometry translation */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for xpt_print below */ #include "opt_cam.h" struct scsi_quirk_entry { struct scsi_inquiry_pattern inq_pat; u_int8_t quirks; #define CAM_QUIRK_NOLUNS 0x01 #define CAM_QUIRK_NOSERIAL 0x02 #define CAM_QUIRK_HILUNS 0x04 #define CAM_QUIRK_NOHILUNS 0x08 u_int mintags; u_int maxtags; }; #define SCSI_QUIRK(dev) ((struct scsi_quirk_entry *)((dev)->quirk)) static int cam_srch_hi = 0; TUNABLE_INT("kern.cam.cam_srch_hi", &cam_srch_hi); static int sysctl_cam_search_luns(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_kern_cam, OID_AUTO, cam_srch_hi, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_cam_search_luns, "I", "allow search above LUN 7 for SCSI3 and greater devices"); #define CAM_SCSI2_MAXLUN 8 /* * If we're not quirked to search <= the first 8 luns * and we are either quirked to search above lun 8, * or we're > SCSI-2 and we've enabled hilun searching, * or we're > SCSI-2 and the last lun was a success, * we can look for luns above lun 8. */ #define CAN_SRCH_HI_SPARSE(dv) \ (((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_NOHILUNS) == 0) \ && ((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_HILUNS) \ || (SID_ANSI_REV(&dv->inq_data) > SCSI_REV_2 && cam_srch_hi))) #define CAN_SRCH_HI_DENSE(dv) \ (((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_NOHILUNS) == 0) \ && ((SCSI_QUIRK(dv)->quirks & CAM_QUIRK_HILUNS) \ || (SID_ANSI_REV(&dv->inq_data) > SCSI_REV_2))) static periph_init_t probe_periph_init; static struct periph_driver probe_driver = { probe_periph_init, "probe", TAILQ_HEAD_INITIALIZER(probe_driver.units), /* generation */ 0, CAM_PERIPH_DRV_EARLY }; PERIPHDRIVER_DECLARE(probe, probe_driver); typedef enum { PROBE_TUR, PROBE_INQUIRY, /* this counts as DV0 for Basic Domain Validation */ PROBE_FULL_INQUIRY, PROBE_MODE_SENSE, PROBE_SERIAL_NUM_0, PROBE_SERIAL_NUM_1, PROBE_TUR_FOR_NEGOTIATION, PROBE_INQUIRY_BASIC_DV1, PROBE_INQUIRY_BASIC_DV2, PROBE_DV_EXIT, PROBE_INVALID } probe_action; static char *probe_action_text[] = { "PROBE_TUR", "PROBE_INQUIRY", "PROBE_FULL_INQUIRY", "PROBE_MODE_SENSE", "PROBE_SERIAL_NUM_0", "PROBE_SERIAL_NUM_1", "PROBE_TUR_FOR_NEGOTIATION", "PROBE_INQUIRY_BASIC_DV1", "PROBE_INQUIRY_BASIC_DV2", "PROBE_DV_EXIT", "PROBE_INVALID" }; #define PROBE_SET_ACTION(softc, newaction) \ do { \ char **text; \ text = probe_action_text; \ CAM_DEBUG((softc)->periph->path, CAM_DEBUG_INFO, \ ("Probe %s to %s\n", text[(softc)->action], \ text[(newaction)])); \ (softc)->action = (newaction); \ } while(0) typedef enum { PROBE_INQUIRY_CKSUM = 0x01, PROBE_SERIAL_CKSUM = 0x02, PROBE_NO_ANNOUNCE = 0x04 } probe_flags; typedef struct { TAILQ_HEAD(, ccb_hdr) request_ccbs; probe_action action; union ccb saved_ccb; probe_flags flags; MD5_CTX context; u_int8_t digest[16]; struct cam_periph *periph; } probe_softc; static const char quantum[] = "QUANTUM"; static const char sony[] = "SONY"; static const char west_digital[] = "WDIGTL"; static const char samsung[] = "SAMSUNG"; static const char seagate[] = "SEAGATE"; static const char microp[] = "MICROP"; static struct scsi_quirk_entry scsi_quirk_table[] = { { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP39100*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP34550*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP32275*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, microp, "4421-07*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, "HP", "C372*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, microp, "3391*", "x43h" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Unfortunately, the Quantum Atlas III has the same * problem as the Atlas II drives above. * Reported by: "Johan Granlund" * * For future reference, the drive with the problem was: * QUANTUM QM39100TD-SW N1B0 * * It's possible that Quantum will fix the problem in later * firmware revisions. If that happens, the quirk entry * will need to be made specific to the firmware revisions * with the problem. * */ /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM39100*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* * 18 Gig Atlas III, same problem as the 9G version. * Reported by: Andre Albsmeier * * * For future reference, the drive with the problem was: * QUANTUM QM318000TD-S N491 */ /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM318000*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* * Broken tagged queuing drive * Reported by: Bret Ford * and: Martin Renters */ { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST410800*", "71*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, /* * The Seagate Medalist Pro drives have very poor write * performance with anything more than 2 tags. * * Reported by: Paul van der Zwan * Drive: * * Reported by: Jeremy Lea * Drive: * * No one has actually reported that the 9G version * (ST39140*) of the Medalist Pro has the same problem, but * we're assuming that it does because the 4G and 6.5G * versions of the drive are broken. */ { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST34520*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST36530*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST39140*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { /* * Slow when tagged queueing is enabled. Write performance * steadily drops off with more and more concurrent * transactions. Best sequential write performance with * tagged queueing turned off and write caching turned on. * * PR: kern/10398 * Submitted by: Hideaki Okada * Drive: DCAS-34330 w/ "S65A" firmware. * * The drive with the problem had the "S65A" firmware * revision, and has also been reported (by Stephen J. * Roznowski ) for a drive with the "S61A" * firmware revision. * * Although no one has reported problems with the 2 gig * version of the DCAS drive, the assumption is that it * has the same problems as the 4 gig version. Therefore * this quirk entries disables tagged queueing for all * DCAS drives. */ { T_DIRECT, SIP_MEDIA_FIXED, "IBM", "DCAS*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_REMOVABLE, "iomega", "jaz*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CFP2107*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* This does not support other than LUN 0 */ { T_DIRECT, SIP_MEDIA_FIXED, "VMware*", "*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/2, /*maxtags*/255 }, { /* * Broken tagged queuing drive. * Submitted by: * NAKAJI Hiroyuki * in PR kern/9535 */ { T_DIRECT, SIP_MEDIA_FIXED, samsung, "WN34324U*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Slow when tagged queueing is enabled. (1.5MB/sec versus * 8MB/sec.) * Submitted by: Andrew Gallatin * Best performance with these drives is achieved with * tagged queueing turned off, and write caching turned on. */ { T_DIRECT, SIP_MEDIA_FIXED, west_digital, "WDE*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Slow when tagged queueing is enabled. (1.5MB/sec versus * 8MB/sec.) * Submitted by: Andrew Gallatin * Best performance with these drives is achieved with * tagged queueing turned off, and write caching turned on. */ { T_DIRECT, SIP_MEDIA_FIXED, west_digital, "ENTERPRISE", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Doesn't handle queue full condition correctly, * so we need to limit maxtags to what the device * can handle instead of determining this automatically. */ { T_DIRECT, SIP_MEDIA_FIXED, samsung, "WN321010S*", "*" }, /*quirks*/0, /*mintags*/2, /*maxtags*/32 }, { /* Really only one LUN */ { T_ENCLOSURE, SIP_MEDIA_FIXED, "SUN", "SENA", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* I can't believe we need a quirk for DPT volumes. */ { T_ANY, SIP_MEDIA_FIXED|SIP_MEDIA_REMOVABLE, "DPT", "*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/255 }, { /* * Many Sony CDROM drives don't like multi-LUN probing. */ { T_CDROM, SIP_MEDIA_REMOVABLE, sony, "CD-ROM CDU*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * This drive doesn't like multiple LUN probing. * Submitted by: Parag Patel */ { T_WORM, SIP_MEDIA_REMOVABLE, sony, "CD-R CDU9*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { { T_WORM, SIP_MEDIA_REMOVABLE, "YAMAHA", "CDR100*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * The 8200 doesn't like multi-lun probing, and probably * don't like serial number requests either. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE", "EXB-8200*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * Let's try the same as above, but for a drive that says * it's an IPL-6860 but is actually an EXB 8200. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE", "IPL-6860*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * These Hitachi drives don't like multi-lun probing. * The PR submitter has a DK319H, but says that the Linux * kernel has a similar work-around for the DK312 and DK314, * so all DK31* drives are quirked here. * PR: misc/18793 * Submitted by: Paul Haddad */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "DK31*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/2, /*maxtags*/255 }, { /* * The Hitachi CJ series with J8A8 firmware apparantly has * problems with tagged commands. * PR: 23536 * Reported by: amagai@nue.org */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "DK32CJ*", "J8A8" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * These are the large storage arrays. * Submitted by: William Carrel */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "OPEN*", "*" }, CAM_QUIRK_HILUNS, 2, 1024 }, { /* * This old revision of the TDC3600 is also SCSI-1, and * hangs upon serial number probing. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG", " TDC 3600", "U07:" }, CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0 }, { /* * Would repond to all LUNs if asked for. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "CALIPER", "CP150", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * Would repond to all LUNs if asked for. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY", "96X2*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Submitted by: Matthew Dodd */ { T_PROCESSOR, SIP_MEDIA_FIXED, "Cabletrn", "EA41*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Submitted by: Matthew Dodd */ { T_PROCESSOR, SIP_MEDIA_FIXED, "CABLETRN", "EA41*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* TeraSolutions special settings for TRC-22 RAID */ { T_DIRECT, SIP_MEDIA_FIXED, "TERASOLU", "TRC-22", "*" }, /*quirks*/0, /*mintags*/55, /*maxtags*/255 }, { /* Veritas Storage Appliance */ { T_DIRECT, SIP_MEDIA_FIXED, "VERITAS", "*", "*" }, CAM_QUIRK_HILUNS, /*mintags*/2, /*maxtags*/1024 }, { /* * Would respond to all LUNs. Device type and removable * flag are jumper-selectable. */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, "MaxOptix", "Tahiti 1", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* EasyRAID E5A aka. areca ARC-6010 */ { T_DIRECT, SIP_MEDIA_FIXED, "easyRAID", "*", "*" }, CAM_QUIRK_NOHILUNS, /*mintags*/2, /*maxtags*/255 }, { { T_ENCLOSURE, SIP_MEDIA_FIXED, "DP", "BACKPLANE", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Default tagged queuing parameters for all devices */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, /*vendor*/"*", /*product*/"*", /*revision*/"*" }, /*quirks*/0, /*mintags*/2, /*maxtags*/255 }, }; static const int scsi_quirk_table_size = sizeof(scsi_quirk_table) / sizeof(*scsi_quirk_table); static cam_status proberegister(struct cam_periph *periph, void *arg); static void probeschedule(struct cam_periph *probe_periph); static void probestart(struct cam_periph *periph, union ccb *start_ccb); static void proberequestdefaultnegotiation(struct cam_periph *periph); static int proberequestbackoff(struct cam_periph *periph, struct cam_ed *device); static void probedone(struct cam_periph *periph, union ccb *done_ccb); static void probecleanup(struct cam_periph *periph); static void scsi_find_quirk(struct cam_ed *device); static void scsi_scan_bus(struct cam_periph *periph, union ccb *ccb); static void scsi_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *ccb); static void xptscandone(struct cam_periph *periph, union ccb *done_ccb); static struct cam_ed * scsi_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); static void scsi_devise_transport(struct cam_path *path); static void scsi_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update); static void scsi_toggle_tags(struct cam_path *path); static void scsi_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); static void scsi_action(union ccb *start_ccb); +static void scsi_announce_periph(struct cam_periph *periph); static struct xpt_xport scsi_xport = { .alloc_device = scsi_alloc_device, .action = scsi_action, .async = scsi_dev_async, + .announce = scsi_announce_periph, }; struct xpt_xport * scsi_get_xport(void) { return (&scsi_xport); } static void probe_periph_init() { } static cam_status proberegister(struct cam_periph *periph, void *arg) { union ccb *request_ccb; /* CCB representing the probe request */ cam_status status; probe_softc *softc; request_ccb = (union ccb *)arg; if (periph == NULL) { printf("proberegister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (request_ccb == NULL) { printf("proberegister: no probe CCB, " "can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (probe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_NOWAIT); if (softc == NULL) { printf("proberegister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } TAILQ_INIT(&softc->request_ccbs); TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); softc->flags = 0; periph->softc = softc; softc->periph = periph; softc->action = PROBE_INVALID; status = cam_periph_acquire(periph); if (status != CAM_REQ_CMP) { return (status); } /* * Ensure we've waited at least a bus settle * delay before attempting to probe the device. * For HBAs that don't do bus resets, this won't make a difference. */ cam_periph_freeze_after_event(periph, &periph->path->bus->last_reset, scsi_delay); /* * Ensure nobody slip in until probe finish. */ cam_freeze_devq_arg(periph->path, RELSIM_RELEASE_RUNLEVEL, CAM_RL_XPT + 1); probeschedule(periph); return(CAM_REQ_CMP); } static void probeschedule(struct cam_periph *periph) { struct ccb_pathinq cpi; union ccb *ccb; probe_softc *softc; softc = (probe_softc *)periph->softc; ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* * If a device has gone away and another device, or the same one, * is back in the same place, it should have a unit attention * condition pending. It will not report the unit attention in * response to an inquiry, which may leave invalid transfer * negotiations in effect. The TUR will reveal the unit attention * condition. Only send the TUR for lun 0, since some devices * will get confused by commands other than inquiry to non-existent * luns. If you think a device has gone away start your scan from * lun 0. This will insure that any bogus transfer settings are * invalidated. * * If we haven't seen the device before and the controller supports * some kind of transfer negotiation, negotiate with the first * sent command if no bus reset was performed at startup. This * ensures that the device is not confused by transfer negotiation * settings left over by loader or BIOS action. */ if (((ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) == 0) && (ccb->ccb_h.target_lun == 0)) { PROBE_SET_ACTION(softc, PROBE_TUR); } else if ((cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) != 0 && (cpi.hba_misc & PIM_NOBUSRESET) != 0) { proberequestdefaultnegotiation(periph); PROBE_SET_ACTION(softc, PROBE_INQUIRY); } else { PROBE_SET_ACTION(softc, PROBE_INQUIRY); } if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE) softc->flags |= PROBE_NO_ANNOUNCE; else softc->flags &= ~PROBE_NO_ANNOUNCE; xpt_schedule(periph, CAM_PRIORITY_XPT); } static void probestart(struct cam_periph *periph, union ccb *start_ccb) { /* Probe the device that our peripheral driver points to */ struct ccb_scsiio *csio; probe_softc *softc; CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n")); softc = (probe_softc *)periph->softc; csio = &start_ccb->csio; switch (softc->action) { case PROBE_TUR: case PROBE_TUR_FOR_NEGOTIATION: case PROBE_DV_EXIT: { scsi_test_unit_ready(csio, /*retries*/10, probedone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/60000); break; } case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: case PROBE_INQUIRY_BASIC_DV1: case PROBE_INQUIRY_BASIC_DV2: { u_int inquiry_len; struct scsi_inquiry_data *inq_buf; inq_buf = &periph->path->device->inq_data; /* * If the device is currently configured, we calculate an * MD5 checksum of the inquiry data, and if the serial number * length is greater than 0, add the serial number data * into the checksum as well. Once the inquiry and the * serial number check finish, we attempt to figure out * whether we still have the same device. */ if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) { MD5Init(&softc->context); MD5Update(&softc->context, (unsigned char *)inq_buf, sizeof(struct scsi_inquiry_data)); softc->flags |= PROBE_INQUIRY_CKSUM; if (periph->path->device->serial_num_len > 0) { MD5Update(&softc->context, periph->path->device->serial_num, periph->path->device->serial_num_len); softc->flags |= PROBE_SERIAL_CKSUM; } MD5Final(softc->digest, &softc->context); } if (softc->action == PROBE_INQUIRY) inquiry_len = SHORT_INQUIRY_LENGTH; else inquiry_len = SID_ADDITIONAL_LENGTH(inq_buf); /* * Some parallel SCSI devices fail to send an * ignore wide residue message when dealing with * odd length inquiry requests. Round up to be * safe. */ inquiry_len = roundup2(inquiry_len, 2); if (softc->action == PROBE_INQUIRY_BASIC_DV1 || softc->action == PROBE_INQUIRY_BASIC_DV2) { inq_buf = malloc(inquiry_len, M_CAMXPT, M_NOWAIT); } if (inq_buf == NULL) { xpt_print(periph->path, "malloc failure- skipping Basic" "Domain Validation\n"); PROBE_SET_ACTION(softc, PROBE_DV_EXIT); scsi_test_unit_ready(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/60000); break; } scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)inq_buf, inquiry_len, /*evpd*/FALSE, /*page_code*/0, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } case PROBE_MODE_SENSE: { void *mode_buf; int mode_buf_len; mode_buf_len = sizeof(struct scsi_mode_header_6) + sizeof(struct scsi_mode_blk_desc) + sizeof(struct scsi_control_page); mode_buf = malloc(mode_buf_len, M_CAMXPT, M_NOWAIT); if (mode_buf != NULL) { scsi_mode_sense(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SMS_CONTROL_MODE_PAGE, mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60000); break; } xpt_print(periph->path, "Unable to mode sense control page - " "malloc failure\n"); PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); } /* FALLTHROUGH */ case PROBE_SERIAL_NUM_0: { struct scsi_vpd_supported_page_list *vpd_list = NULL; struct cam_ed *device; device = periph->path->device; if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOSERIAL) == 0) { vpd_list = malloc(sizeof(*vpd_list), M_CAMXPT, M_NOWAIT | M_ZERO); } if (vpd_list != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)vpd_list, sizeof(*vpd_list), /*evpd*/TRUE, SVPD_SUPPORTED_PAGE_LIST, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } /* * We'll have to do without, let our probedone * routine finish up for us. */ start_ccb->csio.data_ptr = NULL; probedone(periph, start_ccb); return; } case PROBE_SERIAL_NUM_1: { struct scsi_vpd_unit_serial_number *serial_buf; struct cam_ed* device; serial_buf = NULL; device = periph->path->device; if (device->serial_num != NULL) { free(device->serial_num, M_CAMXPT); device->serial_num = NULL; device->serial_num_len = 0; } serial_buf = (struct scsi_vpd_unit_serial_number *) malloc(sizeof(*serial_buf), M_CAMXPT, M_NOWAIT|M_ZERO); if (serial_buf != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)serial_buf, sizeof(*serial_buf), /*evpd*/TRUE, SVPD_UNIT_SERIAL_NUMBER, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } /* * We'll have to do without, let our probedone * routine finish up for us. */ start_ccb->csio.data_ptr = NULL; probedone(periph, start_ccb); return; } case PROBE_INVALID: CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO, ("probestart: invalid action state\n")); default: break; } xpt_action(start_ccb); } static void proberequestdefaultnegotiation(struct cam_periph *periph) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_USER_SETTINGS; xpt_action((union ccb *)&cts); if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { return; } cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); } /* * Backoff Negotiation Code- only pertinent for SPI devices. */ static int proberequestbackoff(struct cam_periph *periph, struct cam_ed *device) { struct ccb_trans_settings cts; struct ccb_trans_settings_spi *spi; memset(&cts, 0, sizeof (cts)); xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (bootverbose) { xpt_print(periph->path, "failed to get current device settings\n"); } return (0); } if (cts.transport != XPORT_SPI) { if (bootverbose) { xpt_print(periph->path, "not SPI transport\n"); } return (0); } spi = &cts.xport_specific.spi; /* * We cannot renegotiate sync rate if we don't have one. */ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) { if (bootverbose) { xpt_print(periph->path, "no sync rate known\n"); } return (0); } /* * We'll assert that we don't have to touch PPR options- the * SIM will see what we do with period and offset and adjust * the PPR options as appropriate. */ /* * A sync rate with unknown or zero offset is nonsensical. * A sync period of zero means Async. */ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0 || spi->sync_offset == 0 || spi->sync_period == 0) { if (bootverbose) { xpt_print(periph->path, "no sync rate available\n"); } return (0); } if (device->flags & CAM_DEV_DV_HIT_BOTTOM) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("hit async: giving up on DV\n")); return (0); } /* * Jump sync_period up by one, but stop at 5MHz and fall back to Async. * We don't try to remember 'last' settings to see if the SIM actually * gets into the speed we want to set. We check on the SIM telling * us that a requested speed is bad, but otherwise don't try and * check the speed due to the asynchronous and handshake nature * of speed setting. */ spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET; for (;;) { spi->sync_period++; if (spi->sync_period >= 0xf) { spi->sync_period = 0; spi->sync_offset = 0; CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("setting to async for DV\n")); /* * Once we hit async, we don't want to try * any more settings. */ device->flags |= CAM_DEV_DV_HIT_BOTTOM; } else if (bootverbose) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("DV: period 0x%x\n", spi->sync_period)); printf("setting period to 0x%x\n", spi->sync_period); } cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb *)&cts); if ((cts.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { break; } CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("DV: failed to set period 0x%x\n", spi->sync_period)); if (spi->sync_period == 0) { return (0); } } return (1); } static void probedone(struct cam_periph *periph, union ccb *done_ccb) { probe_softc *softc; struct cam_path *path; u_int32_t priority; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); softc = (probe_softc *)periph->softc; path = done_ccb->ccb_h.path; priority = done_ccb->ccb_h.pinfo.priority; switch (softc->action) { case PROBE_TUR: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (cam_periph_error(done_ccb, 0, SF_NO_PRINT, NULL) == ERESTART) return; else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } PROBE_SET_ACTION(softc, PROBE_INQUIRY); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { struct scsi_inquiry_data *inq_buf; u_int8_t periph_qual; path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID; inq_buf = &path->device->inq_data; periph_qual = SID_QUAL(inq_buf); switch(periph_qual) { case SID_QUAL_LU_CONNECTED: { u_int8_t len; /* * We conservatively request only * SHORT_INQUIRY_LEN bytes of inquiry * information during our first try * at sending an INQUIRY. If the device * has more information to give, * perform a second request specifying * the amount of information the device * is willing to give. */ len = inq_buf->additional_length + offsetof(struct scsi_inquiry_data, additional_length) + 1; if (softc->action == PROBE_INQUIRY && len > SHORT_INQUIRY_LENGTH) { PROBE_SET_ACTION(softc, PROBE_FULL_INQUIRY); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } scsi_find_quirk(path->device); scsi_devise_transport(path); if (INQ_DATA_TQ_ENABLED(inq_buf)) PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); else PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); } xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } default: break; } } else if (cam_periph_error(done_ccb, 0, done_ccb->ccb_h.target_lun > 0 ? SF_RETRY_UA|SF_QUIET_IR : SF_RETRY_UA, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * If we get to this point, we got an error status back * from the inquiry and the error status doesn't require * automatically retrying the command. Therefore, the * inquiry failed. If we had inquiry information before * for this device, but this latest inquiry command failed, * the device has probably gone away. If this device isn't * already marked unconfigured, notify the peripheral * drivers that this device is no more. */ if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) /* Send the async notification. */ xpt_async(AC_LOST_DEVICE, path, NULL); xpt_release_ccb(done_ccb); break; } case PROBE_MODE_SENSE: { struct ccb_scsiio *csio; struct scsi_mode_header_6 *mode_hdr; csio = &done_ccb->csio; mode_hdr = (struct scsi_mode_header_6 *)csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { struct scsi_control_page *page; u_int8_t *offset; offset = ((u_int8_t *)&mode_hdr[1]) + mode_hdr->blk_desc_len; page = (struct scsi_control_page *)offset; path->device->queue_flags = page->queue_flags; } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } xpt_release_ccb(done_ccb); free(mode_hdr, M_CAMXPT); PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); xpt_schedule(periph, priority); return; } case PROBE_SERIAL_NUM_0: { struct ccb_scsiio *csio; struct scsi_vpd_supported_page_list *page_list; int length, serialnum_supported, i; serialnum_supported = 0; csio = &done_ccb->csio; page_list = (struct scsi_vpd_supported_page_list *)csio->data_ptr; if (page_list == NULL) { /* * Don't process the command as it was never sent */ } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (page_list->length > 0)) { length = min(page_list->length, SVPD_SUPPORTED_PAGES_SIZE); for (i = 0; i < length; i++) { if (page_list->list[i] == SVPD_UNIT_SERIAL_NUMBER) { serialnum_supported = 1; break; } } } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } if (page_list != NULL) free(page_list, M_CAMXPT); if (serialnum_supported) { xpt_release_ccb(done_ccb); PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_1); xpt_schedule(periph, priority); return; } csio->data_ptr = NULL; /* FALLTHROUGH */ } case PROBE_SERIAL_NUM_1: { struct ccb_scsiio *csio; struct scsi_vpd_unit_serial_number *serial_buf; u_int32_t priority; int changed; int have_serialnum; changed = 1; have_serialnum = 0; csio = &done_ccb->csio; priority = done_ccb->ccb_h.pinfo.priority; serial_buf = (struct scsi_vpd_unit_serial_number *)csio->data_ptr; /* Clean up from previous instance of this device */ if (path->device->serial_num != NULL) { free(path->device->serial_num, M_CAMXPT); path->device->serial_num = NULL; path->device->serial_num_len = 0; } if (serial_buf == NULL) { /* * Don't process the command as it was never sent */ } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (serial_buf->length > 0)) { have_serialnum = 1; path->device->serial_num = (u_int8_t *)malloc((serial_buf->length + 1), M_CAMXPT, M_NOWAIT); if (path->device->serial_num != NULL) { bcopy(serial_buf->serial_num, path->device->serial_num, serial_buf->length); path->device->serial_num_len = serial_buf->length; path->device->serial_num[serial_buf->length] = '\0'; } } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * Let's see if we have seen this device before. */ if ((softc->flags & PROBE_INQUIRY_CKSUM) != 0) { MD5_CTX context; u_int8_t digest[16]; MD5Init(&context); MD5Update(&context, (unsigned char *)&path->device->inq_data, sizeof(struct scsi_inquiry_data)); if (have_serialnum) MD5Update(&context, serial_buf->serial_num, serial_buf->length); MD5Final(digest, &context); if (bcmp(softc->digest, digest, 16) == 0) changed = 0; /* * XXX Do we need to do a TUR in order to ensure * that the device really hasn't changed??? */ if ((changed != 0) && ((softc->flags & PROBE_NO_ANNOUNCE) == 0)) xpt_async(AC_LOST_DEVICE, path, NULL); } if (serial_buf != NULL) free(serial_buf, M_CAMXPT); if (changed != 0) { /* * Now that we have all the necessary * information to safely perform transfer * negotiations... Controllers don't perform * any negotiation or tagged queuing until * after the first XPT_SET_TRAN_SETTINGS ccb is * received. So, on a new device, just retrieve * the user settings, and set them as the current * settings to set the device up. */ proberequestdefaultnegotiation(periph); xpt_release_ccb(done_ccb); /* * Perform a TUR to allow the controller to * perform any necessary transfer negotiation. */ PROBE_SET_ACTION(softc, PROBE_TUR_FOR_NEGOTIATION); xpt_schedule(periph, priority); return; } xpt_release_ccb(done_ccb); break; } case PROBE_TUR_FOR_NEGOTIATION: if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { DELAY(500000); if (cam_periph_error(done_ccb, 0, SF_RETRY_UA, NULL) == ERESTART) return; } /* FALLTHROUGH */ case PROBE_DV_EXIT: if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * Do Domain Validation for lun 0 on devices that claim * to support Synchronous Transfer modes. */ if (softc->action == PROBE_TUR_FOR_NEGOTIATION && done_ccb->ccb_h.target_lun == 0 && (path->device->inq_data.flags & SID_Sync) != 0 && (path->device->flags & CAM_DEV_IN_DV) == 0) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Begin Domain Validation\n")); path->device->flags |= CAM_DEV_IN_DV; xpt_release_ccb(done_ccb); PROBE_SET_ACTION(softc, PROBE_INQUIRY_BASIC_DV1); xpt_schedule(periph, priority); return; } if (softc->action == PROBE_DV_EXIT) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Leave Domain Validation\n")); } if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); } path->device->flags &= ~(CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } xpt_release_ccb(done_ccb); break; case PROBE_INQUIRY_BASIC_DV1: case PROBE_INQUIRY_BASIC_DV2: { struct scsi_inquiry_data *nbuf; struct ccb_scsiio *csio; if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } csio = &done_ccb->csio; nbuf = (struct scsi_inquiry_data *)csio->data_ptr; if (bcmp(nbuf, &path->device->inq_data, SHORT_INQUIRY_LENGTH)) { xpt_print(path, "inquiry data fails comparison at DV%d step\n", softc->action == PROBE_INQUIRY_BASIC_DV1 ? 1 : 2); if (proberequestbackoff(periph, path->device)) { path->device->flags &= ~CAM_DEV_IN_DV; PROBE_SET_ACTION(softc, PROBE_TUR_FOR_NEGOTIATION); } else { /* give up */ PROBE_SET_ACTION(softc, PROBE_DV_EXIT); } free(nbuf, M_CAMXPT); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } free(nbuf, M_CAMXPT); if (softc->action == PROBE_INQUIRY_BASIC_DV1) { PROBE_SET_ACTION(softc, PROBE_INQUIRY_BASIC_DV2); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } if (softc->action == PROBE_INQUIRY_BASIC_DV2) { CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Leave Domain Validation Successfully\n")); } if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_acquire_device(path->device); } path->device->flags &= ~(CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); } xpt_release_ccb(done_ccb); break; } case PROBE_INVALID: CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_INFO, ("probedone: invalid action state\n")); default: break; } done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); TAILQ_REMOVE(&softc->request_ccbs, &done_ccb->ccb_h, periph_links.tqe); done_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(done_ccb); if (TAILQ_FIRST(&softc->request_ccbs) == NULL) { cam_release_devq(periph->path, RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_XPT + 1, FALSE); cam_periph_invalidate(periph); cam_periph_release_locked(periph); } else { probeschedule(periph); } } static void probecleanup(struct cam_periph *periph) { free(periph->softc, M_CAMXPT); } static void scsi_find_quirk(struct cam_ed *device) { struct scsi_quirk_entry *quirk; caddr_t match; match = cam_quirkmatch((caddr_t)&device->inq_data, (caddr_t)scsi_quirk_table, sizeof(scsi_quirk_table) / sizeof(*scsi_quirk_table), sizeof(*scsi_quirk_table), scsi_inquiry_match); if (match == NULL) panic("xpt_find_quirk: device didn't match wildcard entry!!"); quirk = (struct scsi_quirk_entry *)match; device->quirk = quirk; device->mintags = quirk->mintags; device->maxtags = quirk->maxtags; } static int sysctl_cam_search_luns(SYSCTL_HANDLER_ARGS) { int error, bool; bool = cam_srch_hi; error = sysctl_handle_int(oidp, &bool, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (bool == 0 || bool == 1) { cam_srch_hi = bool; return (0); } else { return (EINVAL); } } typedef struct { union ccb *request_ccb; struct ccb_pathinq *cpi; int counter; } scsi_scan_bus_info; /* * To start a scan, request_ccb is an XPT_SCAN_BUS ccb. * As the scan progresses, scsi_scan_bus is used as the * callback on completion function. */ static void scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb) { CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("scsi_scan_bus\n")); switch (request_ccb->ccb_h.func_code) { case XPT_SCAN_BUS: { scsi_scan_bus_info *scan_info; union ccb *work_ccb, *reset_ccb; struct cam_path *path; u_int i; u_int max_target; u_int initiator_id; /* Find out the characteristics of the bus */ work_ccb = xpt_alloc_ccb_nowait(); if (work_ccb == NULL) { request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); return; } xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_PATH_INQ; xpt_action(work_ccb); if (work_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = work_ccb->ccb_h.status; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } if ((work_ccb->cpi.hba_misc & PIM_NOINITIATOR) != 0) { /* * Can't scan the bus on an adapter that * cannot perform the initiator role. */ request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } /* We may need to reset bus first, if we haven't done it yet. */ if ((work_ccb->cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) && !(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) && !timevalisset(&request_ccb->ccb_h.path->bus->last_reset)) { reset_ccb = xpt_alloc_ccb_nowait(); xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path, CAM_PRIORITY_NONE); reset_ccb->ccb_h.func_code = XPT_RESET_BUS; xpt_action(reset_ccb); if (reset_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = reset_ccb->ccb_h.status; xpt_free_ccb(reset_ccb); xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } xpt_free_ccb(reset_ccb); } /* Save some state for use while we probe for devices */ scan_info = (scsi_scan_bus_info *) malloc(sizeof(scsi_scan_bus_info), M_CAMXPT, M_NOWAIT); if (scan_info == NULL) { request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); return; } scan_info->request_ccb = request_ccb; scan_info->cpi = &work_ccb->cpi; /* Cache on our stack so we can work asynchronously */ max_target = scan_info->cpi->max_target; initiator_id = scan_info->cpi->initiator_id; /* * We can scan all targets in parallel, or do it sequentially. */ if (scan_info->cpi->hba_misc & PIM_SEQSCAN) { max_target = 0; scan_info->counter = 0; } else { scan_info->counter = scan_info->cpi->max_target + 1; if (scan_info->cpi->initiator_id < scan_info->counter) { scan_info->counter--; } } for (i = 0; i <= max_target; i++) { cam_status status; if (i == initiator_id) continue; status = xpt_create_path(&path, xpt_periph, request_ccb->ccb_h.path_id, i, 0); if (status != CAM_REQ_CMP) { printf("scsi_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = status; xpt_free_ccb(work_ccb); xpt_done(request_ccb); break; } work_ccb = xpt_alloc_ccb_nowait(); if (work_ccb == NULL) { xpt_free_ccb((union ccb *)scan_info->cpi); free(scan_info, M_CAMXPT); xpt_free_path(path); request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(request_ccb); break; } xpt_setup_ccb(&work_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_SCAN_LUN; work_ccb->ccb_h.cbfcnp = scsi_scan_bus; work_ccb->ccb_h.ppriv_ptr0 = scan_info; work_ccb->crcn.flags = request_ccb->crcn.flags; xpt_action(work_ccb); } break; } case XPT_SCAN_LUN: { cam_status status; struct cam_path *path; scsi_scan_bus_info *scan_info; path_id_t path_id; target_id_t target_id; lun_id_t lun_id; /* Reuse the same CCB to query if a device was really found */ scan_info = (scsi_scan_bus_info *)request_ccb->ccb_h.ppriv_ptr0; xpt_setup_ccb(&request_ccb->ccb_h, request_ccb->ccb_h.path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_GDEV_TYPE; path_id = request_ccb->ccb_h.path_id; target_id = request_ccb->ccb_h.target_id; lun_id = request_ccb->ccb_h.target_lun; xpt_action(request_ccb); if (request_ccb->ccb_h.status != CAM_REQ_CMP) { struct cam_ed *device; struct cam_et *target; int phl; /* * If we already probed lun 0 successfully, or * we have additional configured luns on this * target that might have "gone away", go onto * the next lun. */ target = request_ccb->ccb_h.path->target; /* * We may touch devices that we don't * hold references too, so ensure they * don't disappear out from under us. * The target above is referenced by the * path in the request ccb. */ phl = 0; device = TAILQ_FIRST(&target->ed_entries); if (device != NULL) { phl = CAN_SRCH_HI_SPARSE(device); if (device->lun_id == 0) device = TAILQ_NEXT(device, links); } if ((lun_id != 0) || (device != NULL)) { if (lun_id < (CAM_SCSI2_MAXLUN-1) || phl) lun_id++; } } else { struct cam_ed *device; device = request_ccb->ccb_h.path->device; if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOLUNS) == 0) { /* Try the next lun */ if (lun_id < (CAM_SCSI2_MAXLUN-1) || CAN_SRCH_HI_DENSE(device)) lun_id++; } } /* * Free the current request path- we're done with it. */ xpt_free_path(request_ccb->ccb_h.path); /* * Check to see if we scan any further luns. */ if (lun_id == request_ccb->ccb_h.target_lun || lun_id > scan_info->cpi->max_lun) { int done; hop_again: done = 0; if (scan_info->cpi->hba_misc & PIM_SEQSCAN) { scan_info->counter++; if (scan_info->counter == scan_info->cpi->initiator_id) { scan_info->counter++; } if (scan_info->counter >= scan_info->cpi->max_target+1) { done = 1; } } else { scan_info->counter--; if (scan_info->counter == 0) { done = 1; } } if (done) { xpt_free_ccb(request_ccb); xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); break; } if ((scan_info->cpi->hba_misc & PIM_SEQSCAN) == 0) { xpt_free_ccb(request_ccb); break; } status = xpt_create_path(&path, xpt_periph, scan_info->request_ccb->ccb_h.path_id, scan_info->counter, 0); if (status != CAM_REQ_CMP) { printf("scsi_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); xpt_free_ccb(request_ccb); xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_CAMXPT); request_ccb->ccb_h.status = status; xpt_done(request_ccb); break; } xpt_setup_ccb(&request_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->ccb_h.cbfcnp = scsi_scan_bus; request_ccb->ccb_h.ppriv_ptr0 = scan_info; request_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; } else { status = xpt_create_path(&path, xpt_periph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) { printf("scsi_scan_bus: xpt_create_path failed " "with status %#x, halting LUN scan\n", status); goto hop_again; } xpt_setup_ccb(&request_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->ccb_h.cbfcnp = scsi_scan_bus; request_ccb->ccb_h.ppriv_ptr0 = scan_info; request_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; } xpt_action(request_ccb); break; } default: break; } } static void scsi_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *request_ccb) { struct ccb_pathinq cpi; cam_status status; struct cam_path *new_path; struct cam_periph *old_periph; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("scsi_scan_lun\n")); xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status != CAM_REQ_CMP) { if (request_ccb != NULL) { request_ccb->ccb_h.status = cpi.ccb_h.status; xpt_done(request_ccb); } return; } if ((cpi.hba_misc & PIM_NOINITIATOR) != 0) { /* * Can't scan the bus on an adapter that * cannot perform the initiator role. */ if (request_ccb != NULL) { request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); } return; } if (request_ccb == NULL) { request_ccb = malloc(sizeof(union ccb), M_CAMXPT, M_NOWAIT); if (request_ccb == NULL) { xpt_print(path, "scsi_scan_lun: can't allocate CCB, " "can't continue\n"); return; } new_path = malloc(sizeof(*new_path), M_CAMXPT, M_NOWAIT); if (new_path == NULL) { xpt_print(path, "scsi_scan_lun: can't allocate path, " "can't continue\n"); free(request_ccb, M_CAMXPT); return; } status = xpt_compile_path(new_path, xpt_periph, path->bus->path_id, path->target->target_id, path->device->lun_id); if (status != CAM_REQ_CMP) { xpt_print(path, "scsi_scan_lun: can't compile path, " "can't continue\n"); free(request_ccb, M_CAMXPT); free(new_path, M_CAMXPT); return; } xpt_setup_ccb(&request_ccb->ccb_h, new_path, CAM_PRIORITY_XPT); request_ccb->ccb_h.cbfcnp = xptscandone; request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->crcn.flags = flags; } if ((old_periph = cam_periph_find(path, "probe")) != NULL) { probe_softc *softc; softc = (probe_softc *)old_periph->softc; TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); } else { status = cam_periph_alloc(proberegister, NULL, probecleanup, probestart, "probe", CAM_PERIPH_BIO, request_ccb->ccb_h.path, NULL, 0, request_ccb); if (status != CAM_REQ_CMP) { xpt_print(path, "scsi_scan_lun: cam_alloc_periph " "returned an error, can't continue probe\n"); request_ccb->ccb_h.status = status; xpt_done(request_ccb); } } } static void xptscandone(struct cam_periph *periph, union ccb *done_ccb) { xpt_release_path(done_ccb->ccb_h.path); free(done_ccb->ccb_h.path, M_CAMXPT); free(done_ccb, M_CAMXPT); } static struct cam_ed * scsi_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct cam_path path; struct scsi_quirk_entry *quirk; struct cam_ed *device; struct cam_ed *cur_device; device = xpt_alloc_device(bus, target, lun_id); if (device == NULL) return (NULL); /* * Take the default quirk entry until we have inquiry * data and can determine a better quirk to use. */ quirk = &scsi_quirk_table[scsi_quirk_table_size - 1]; device->quirk = (void *)quirk; device->mintags = quirk->mintags; device->maxtags = quirk->maxtags; bzero(&device->inq_data, sizeof(device->inq_data)); device->inq_flags = 0; device->queue_flags = 0; device->serial_num = NULL; device->serial_num_len = 0; /* * XXX should be limited by number of CCBs this bus can * do. */ bus->sim->max_ccbs += device->ccbq.devq_openings; /* Insertion sort into our target's device list */ cur_device = TAILQ_FIRST(&target->ed_entries); while (cur_device != NULL && cur_device->lun_id < lun_id) cur_device = TAILQ_NEXT(cur_device, links); if (cur_device != NULL) { TAILQ_INSERT_BEFORE(cur_device, device, links); } else { TAILQ_INSERT_TAIL(&target->ed_entries, device, links); } target->generation++; if (lun_id != CAM_LUN_WILDCARD) { xpt_compile_path(&path, NULL, bus->path_id, target->target_id, lun_id); scsi_devise_transport(&path); xpt_release_path(&path); } return (device); } static void scsi_devise_transport(struct cam_path *path) { struct ccb_pathinq cpi; struct ccb_trans_settings cts; struct scsi_inquiry_data *inq_buf; /* Get transport information from the SIM */ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); inq_buf = NULL; if ((path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0) inq_buf = &path->device->inq_data; path->device->protocol = PROTO_SCSI; path->device->protocol_version = inq_buf != NULL ? SID_ANSI_REV(inq_buf) : cpi.protocol_version; path->device->transport = cpi.transport; path->device->transport_version = cpi.transport_version; /* * Any device not using SPI3 features should * be considered SPI2 or lower. */ if (inq_buf != NULL) { if (path->device->transport == XPORT_SPI && (inq_buf->spi3data & SID_SPI_MASK) == 0 && path->device->transport_version > 2) path->device->transport_version = 2; } else { struct cam_ed* otherdev; for (otherdev = TAILQ_FIRST(&path->target->ed_entries); otherdev != NULL; otherdev = TAILQ_NEXT(otherdev, links)) { if (otherdev != path->device) break; } if (otherdev != NULL) { /* * Initially assume the same versioning as * prior luns for this target. */ path->device->protocol_version = otherdev->protocol_version; path->device->transport_version = otherdev->transport_version; } else { /* Until we know better, opt for safty */ path->device->protocol_version = 2; if (path->device->transport == XPORT_SPI) path->device->transport_version = 2; else path->device->transport_version = 0; } } /* * XXX * For a device compliant with SPC-2 we should be able * to determine the transport version supported by * scrutinizing the version descriptors in the * inquiry buffer. */ /* Tell the controller what we think */ xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.transport = path->device->transport; cts.transport_version = path->device->transport_version; cts.protocol = path->device->protocol; cts.protocol_version = path->device->protocol_version; cts.proto_specific.valid = 0; cts.xport_specific.valid = 0; xpt_action((union ccb *)&cts); } static void scsi_action(union ccb *start_ccb) { switch (start_ccb->ccb_h.func_code) { case XPT_SET_TRAN_SETTINGS: { scsi_set_transfer_settings(&start_ccb->cts, start_ccb->ccb_h.path->device, /*async_update*/FALSE); break; } case XPT_SCAN_BUS: scsi_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); break; case XPT_SCAN_LUN: scsi_scan_lun(start_ccb->ccb_h.path->periph, start_ccb->ccb_h.path, start_ccb->crcn.flags, start_ccb); break; case XPT_GET_TRAN_SETTINGS: { struct cam_sim *sim; sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } default: xpt_action_default(start_ccb); break; } } static void scsi_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update) { struct ccb_pathinq cpi; struct ccb_trans_settings cur_cts; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_scsi *cur_scsi; struct cam_sim *sim; struct scsi_inquiry_data *inq_data; if (device == NULL) { cts->ccb_h.status = CAM_PATH_INVALID; xpt_done((union ccb *)cts); return; } if (cts->protocol == PROTO_UNKNOWN || cts->protocol == PROTO_UNSPECIFIED) { cts->protocol = device->protocol; cts->protocol_version = device->protocol_version; } if (cts->protocol_version == PROTO_VERSION_UNKNOWN || cts->protocol_version == PROTO_VERSION_UNSPECIFIED) cts->protocol_version = device->protocol_version; if (cts->protocol != device->protocol) { xpt_print(cts->ccb_h.path, "Uninitialized Protocol %x:%x?\n", cts->protocol, device->protocol); cts->protocol = device->protocol; } if (cts->protocol_version > device->protocol_version) { if (bootverbose) { xpt_print(cts->ccb_h.path, "Down reving Protocol " "Version from %d to %d?\n", cts->protocol_version, device->protocol_version); } cts->protocol_version = device->protocol_version; } if (cts->transport == XPORT_UNKNOWN || cts->transport == XPORT_UNSPECIFIED) { cts->transport = device->transport; cts->transport_version = device->transport_version; } if (cts->transport_version == XPORT_VERSION_UNKNOWN || cts->transport_version == XPORT_VERSION_UNSPECIFIED) cts->transport_version = device->transport_version; if (cts->transport != device->transport) { xpt_print(cts->ccb_h.path, "Uninitialized Transport %x:%x?\n", cts->transport, device->transport); cts->transport = device->transport; } if (cts->transport_version > device->transport_version) { if (bootverbose) { xpt_print(cts->ccb_h.path, "Down reving Transport " "Version from %d to %d?\n", cts->transport_version, device->transport_version); } cts->transport_version = device->transport_version; } sim = cts->ccb_h.path->bus->sim; /* * Nothing more of interest to do unless * this is a device connected via the * SCSI protocol. */ if (cts->protocol != PROTO_SCSI) { if (async_update == FALSE) (*(sim->sim_action))(sim, (union ccb *)cts); return; } inq_data = &device->inq_data; scsi = &cts->proto_specific.scsi; xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, CAM_PRIORITY_NONE); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* SCSI specific sanity checking */ if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0 || (INQ_DATA_TQ_ENABLED(inq_data)) == 0 || (device->queue_flags & SCP_QUEUE_DQUE) != 0 || (device->mintags == 0)) { /* * Can't tag on hardware that doesn't support tags, * doesn't have it enabled, or has broken tag support. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } if (async_update == FALSE) { /* * Perform sanity checking against what the * controller and device can do. */ xpt_setup_ccb(&cur_cts.ccb_h, cts->ccb_h.path, CAM_PRIORITY_NONE); cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cur_cts.type = cts->type; xpt_action((union ccb *)&cur_cts); if ((cur_cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { return; } cur_scsi = &cur_cts.proto_specific.scsi; if ((scsi->valid & CTS_SCSI_VALID_TQ) == 0) { scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->flags |= cur_scsi->flags & CTS_SCSI_FLAGS_TAG_ENB; } if ((cur_scsi->valid & CTS_SCSI_VALID_TQ) == 0) scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } /* SPI specific sanity checking */ if (cts->transport == XPORT_SPI && async_update == FALSE) { u_int spi3caps; struct ccb_trans_settings_spi *spi; struct ccb_trans_settings_spi *cur_spi; spi = &cts->xport_specific.spi; cur_spi = &cur_cts.xport_specific.spi; /* Fill in any gaps in what the user gave us */ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) spi->sync_period = cur_spi->sync_period; if ((cur_spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) spi->sync_period = 0; if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) spi->sync_offset = cur_spi->sync_offset; if ((cur_spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) spi->sync_offset = 0; if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) spi->ppr_options = cur_spi->ppr_options; if ((cur_spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) spi->ppr_options = 0; if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0) spi->bus_width = cur_spi->bus_width; if ((cur_spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0) spi->bus_width = 0; if ((spi->valid & CTS_SPI_VALID_DISC) == 0) { spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; spi->flags |= cur_spi->flags & CTS_SPI_FLAGS_DISC_ENB; } if ((cur_spi->valid & CTS_SPI_VALID_DISC) == 0) spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && (inq_data->flags & SID_Sync) == 0 && cts->type == CTS_TYPE_CURRENT_SETTINGS) || ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0)) { /* Force async */ spi->sync_period = 0; spi->sync_offset = 0; } switch (spi->bus_width) { case MSG_EXT_WDTR_BUS_32_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus32) != 0 || cts->type == CTS_TYPE_USER_SETTINGS) && (cpi.hba_inquiry & PI_WIDE_32) != 0) break; /* Fall Through to 16-bit */ case MSG_EXT_WDTR_BUS_16_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus16) != 0 || cts->type == CTS_TYPE_USER_SETTINGS) && (cpi.hba_inquiry & PI_WIDE_16) != 0) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; break; } /* Fall Through to 8-bit */ default: /* New bus width?? */ case MSG_EXT_WDTR_BUS_8_BIT: /* All targets can do this */ spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } spi3caps = cpi.xport_specific.spi.ppr_options; if ((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && cts->type == CTS_TYPE_CURRENT_SETTINGS) spi3caps &= inq_data->spi3data; if ((spi3caps & SID_SPI_CLOCK_DT) == 0) spi->ppr_options &= ~MSG_EXT_PPR_DT_REQ; if ((spi3caps & SID_SPI_IUS) == 0) spi->ppr_options &= ~MSG_EXT_PPR_IU_REQ; if ((spi3caps & SID_SPI_QAS) == 0) spi->ppr_options &= ~MSG_EXT_PPR_QAS_REQ; /* No SPI Transfer settings are allowed unless we are wide */ if (spi->bus_width == 0) spi->ppr_options = 0; if ((spi->valid & CTS_SPI_VALID_DISC) && ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) == 0)) { /* * Can't tag queue without disconnection. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->valid |= CTS_SCSI_VALID_TQ; } /* * If we are currently performing tagged transactions to * this device and want to change its negotiation parameters, * go non-tagged for a bit to give the controller a chance to * negotiate unhampered by tag messages. */ if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (device->inq_flags & SID_CmdQue) != 0 && (scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && (spi->flags & (CTS_SPI_VALID_SYNC_RATE| CTS_SPI_VALID_SYNC_OFFSET| CTS_SPI_VALID_BUS_WIDTH)) != 0) scsi_toggle_tags(cts->ccb_h.path); } if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (scsi->valid & CTS_SCSI_VALID_TQ) != 0) { int device_tagenb; /* * If we are transitioning from tags to no-tags or * vice-versa, we need to carefully freeze and restart * the queue so that we don't overlap tagged and non-tagged * commands. We also temporarily stop tags if there is * a change in transfer negotiation settings to allow * "tag-less" negotiation. */ if ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (device->inq_flags & SID_CmdQue) != 0) device_tagenb = TRUE; else device_tagenb = FALSE; if (((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && device_tagenb == FALSE) || ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) == 0 && device_tagenb == TRUE)) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) { /* * Delay change to use tags until after a * few commands have gone to this device so * the controller has time to perform transfer * negotiations without tagged messages getting * in the way. */ device->tag_delay_count = CAM_TAG_DELAY_COUNT; device->flags |= CAM_DEV_TAG_AFTER_COUNT; } else { xpt_stop_tags(cts->ccb_h.path); } } } if (async_update == FALSE) (*(sim->sim_action))(sim, (union ccb *)cts); } static void scsi_toggle_tags(struct cam_path *path) { struct cam_ed *dev; /* * Give controllers a chance to renegotiate * before starting tag operations. We * "toggle" tagged queuing off then on * which causes the tag enable command delay * counter to come into effect. */ dev = path->device; if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || ((dev->inq_flags & SID_CmdQue) != 0 && (dev->inq_flags & (SID_Sync|SID_WBus16|SID_WBus32)) != 0)) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE); cts.protocol = PROTO_SCSI; cts.protocol_version = PROTO_VERSION_UNSPECIFIED; cts.transport = XPORT_UNSPECIFIED; cts.transport_version = XPORT_VERSION_UNSPECIFIED; cts.proto_specific.scsi.flags = 0; cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ; scsi_set_transfer_settings(&cts, path->device, /*async_update*/TRUE); cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB; scsi_set_transfer_settings(&cts, path->device, /*async_update*/TRUE); } } /* * Handle any per-device event notifications that require action by the XPT. */ static void scsi_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg) { cam_status status; struct cam_path newpath; /* * We only need to handle events for real devices. */ if (target->target_id == CAM_TARGET_WILDCARD || device->lun_id == CAM_LUN_WILDCARD) return; /* * We need our own path with wildcards expanded to * handle certain types of events. */ if ((async_code == AC_SENT_BDR) || (async_code == AC_BUS_RESET) || (async_code == AC_INQ_CHANGED)) status = xpt_compile_path(&newpath, NULL, bus->path_id, target->target_id, device->lun_id); else status = CAM_REQ_CMP_ERR; if (status == CAM_REQ_CMP) { /* * Allow transfer negotiation to occur in a * tag free environment and after settle delay. */ if (async_code == AC_SENT_BDR || async_code == AC_BUS_RESET) { cam_freeze_devq(&newpath); cam_release_devq(&newpath, RELSIM_RELEASE_AFTER_TIMEOUT, /*reduction*/0, /*timeout*/scsi_delay, /*getcount_only*/0); scsi_toggle_tags(&newpath); } if (async_code == AC_INQ_CHANGED) { /* * We've sent a start unit command, or * something similar to a device that * may have caused its inquiry data to * change. So we re-scan the device to * refresh the inquiry data for it. */ scsi_scan_lun(newpath.periph, &newpath, CAM_EXPECT_INQ_CHANGE, NULL); } xpt_release_path(&newpath); } else if (async_code == AC_LOST_DEVICE && (device->flags & CAM_DEV_UNCONFIGURED) == 0) { device->flags |= CAM_DEV_UNCONFIGURED; xpt_release_device(device); } else if (async_code == AC_TRANSFER_NEG) { struct ccb_trans_settings *settings; settings = (struct ccb_trans_settings *)async_arg; scsi_set_transfer_settings(settings, device, /*async_update*/TRUE); } +} + +static void +scsi_announce_periph(struct cam_periph *periph) +{ + struct ccb_pathinq cpi; + struct ccb_trans_settings cts; + struct cam_path *path = periph->path; + u_int speed; + u_int freq; + u_int mb; + + mtx_assert(periph->sim->mtx, MA_OWNED); + + xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL); + cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; + cts.type = CTS_TYPE_CURRENT_SETTINGS; + xpt_action((union ccb*)&cts); + if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + return; + /* Ask the SIM for its base transfer speed */ + xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL); + cpi.ccb_h.func_code = XPT_PATH_INQ; + xpt_action((union ccb *)&cpi); + /* Report connection speed */ + speed = cpi.base_transfer_speed; + freq = 0; + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { + struct ccb_trans_settings_spi *spi = + &cts.xport_specific.spi; + + if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0 + && spi->sync_offset != 0) { + freq = scsi_calc_syncsrate(spi->sync_period); + speed = freq; + } + if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) + speed *= (0x01 << spi->bus_width); + } + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { + struct ccb_trans_settings_fc *fc = + &cts.xport_specific.fc; + + if (fc->valid & CTS_FC_VALID_SPEED) + speed = fc->bitrate; + } + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SAS) { + struct ccb_trans_settings_sas *sas = + &cts.xport_specific.sas; + + if (sas->valid & CTS_SAS_VALID_SPEED) + speed = sas->bitrate; + } + mb = speed / 1000; + if (mb > 0) + printf("%s%d: %d.%03dMB/s transfers", + periph->periph_name, periph->unit_number, + mb, speed % 1000); + else + printf("%s%d: %dKB/s transfers", periph->periph_name, + periph->unit_number, speed); + /* Report additional information about SPI connections */ + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { + struct ccb_trans_settings_spi *spi; + + spi = &cts.xport_specific.spi; + if (freq != 0) { + printf(" (%d.%03dMHz%s, offset %d", freq / 1000, + freq % 1000, + (spi->ppr_options & MSG_EXT_PPR_DT_REQ) != 0 + ? " DT" : "", + spi->sync_offset); + } + if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0 + && spi->bus_width > 0) { + if (freq != 0) { + printf(", "); + } else { + printf(" ("); + } + printf("%dbit)", 8 * (0x01 << spi->bus_width)); + } else if (freq != 0) { + printf(")"); + } + } + if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { + struct ccb_trans_settings_fc *fc; + + fc = &cts.xport_specific.fc; + if (fc->valid & CTS_FC_VALID_WWNN) + printf(" WWNN 0x%llx", (long long) fc->wwnn); + if (fc->valid & CTS_FC_VALID_WWPN) + printf(" WWPN 0x%llx", (long long) fc->wwpn); + if (fc->valid & CTS_FC_VALID_PORT) + printf(" PortID 0x%x", fc->port); + } + printf("\n"); } Index: projects/ppc64/sys/dev/aac/aac.c =================================================================== --- projects/ppc64/sys/dev/aac/aac.c (revision 204271) +++ projects/ppc64/sys/dev/aac/aac.c (revision 204272) @@ -1,3640 +1,3639 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2001 Scott Long * Copyright (c) 2000 BSDi * Copyright (c) 2001 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. * 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$"); /* * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters. */ #define AAC_DRIVERNAME "aac" #include "opt_aac.h" /* #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void aac_startup(void *arg); static void aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f); static void aac_get_bus_info(struct aac_softc *sc); static void aac_daemon(void *arg); /* Command Processing */ static void aac_timeout(struct aac_softc *sc); static void aac_complete(void *context, int pending); static int aac_bio_command(struct aac_softc *sc, struct aac_command **cmp); static void aac_bio_complete(struct aac_command *cm); static int aac_wait_command(struct aac_command *cm); static void aac_command_thread(struct aac_softc *sc); /* Command Buffer Management */ static void aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error); static void aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int aac_alloc_commands(struct aac_softc *sc); static void aac_free_commands(struct aac_softc *sc); static void aac_unmap_command(struct aac_command *cm); /* Hardware Interface */ static int aac_alloc(struct aac_softc *sc); static void aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int aac_check_firmware(struct aac_softc *sc); static int aac_init(struct aac_softc *sc); static int aac_sync_command(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3, u_int32_t *sp); static int aac_setup_intr(struct aac_softc *sc); static int aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm); static int aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, struct aac_fib **fib_addr); static int aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib); /* StrongARM interface */ static int aac_sa_get_fwstatus(struct aac_softc *sc); static void aac_sa_qnotify(struct aac_softc *sc, int qbit); static int aac_sa_get_istatus(struct aac_softc *sc); static void aac_sa_clear_istatus(struct aac_softc *sc, int mask); static void aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3); static int aac_sa_get_mailbox(struct aac_softc *sc, int mb); static void aac_sa_set_interrupts(struct aac_softc *sc, int enable); struct aac_interface aac_sa_interface = { aac_sa_get_fwstatus, aac_sa_qnotify, aac_sa_get_istatus, aac_sa_clear_istatus, aac_sa_set_mailbox, aac_sa_get_mailbox, aac_sa_set_interrupts, NULL, NULL, NULL }; /* i960Rx interface */ static int aac_rx_get_fwstatus(struct aac_softc *sc); static void aac_rx_qnotify(struct aac_softc *sc, int qbit); static int aac_rx_get_istatus(struct aac_softc *sc); static void aac_rx_clear_istatus(struct aac_softc *sc, int mask); static void aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3); static int aac_rx_get_mailbox(struct aac_softc *sc, int mb); static void aac_rx_set_interrupts(struct aac_softc *sc, int enable); static int aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm); static int aac_rx_get_outb_queue(struct aac_softc *sc); static void aac_rx_set_outb_queue(struct aac_softc *sc, int index); struct aac_interface aac_rx_interface = { aac_rx_get_fwstatus, aac_rx_qnotify, aac_rx_get_istatus, aac_rx_clear_istatus, aac_rx_set_mailbox, aac_rx_get_mailbox, aac_rx_set_interrupts, aac_rx_send_command, aac_rx_get_outb_queue, aac_rx_set_outb_queue }; /* Rocket/MIPS interface */ static int aac_rkt_get_fwstatus(struct aac_softc *sc); static void aac_rkt_qnotify(struct aac_softc *sc, int qbit); static int aac_rkt_get_istatus(struct aac_softc *sc); static void aac_rkt_clear_istatus(struct aac_softc *sc, int mask); static void aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3); static int aac_rkt_get_mailbox(struct aac_softc *sc, int mb); static void aac_rkt_set_interrupts(struct aac_softc *sc, int enable); static int aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm); static int aac_rkt_get_outb_queue(struct aac_softc *sc); static void aac_rkt_set_outb_queue(struct aac_softc *sc, int index); struct aac_interface aac_rkt_interface = { aac_rkt_get_fwstatus, aac_rkt_qnotify, aac_rkt_get_istatus, aac_rkt_clear_istatus, aac_rkt_set_mailbox, aac_rkt_get_mailbox, aac_rkt_set_interrupts, aac_rkt_send_command, aac_rkt_get_outb_queue, aac_rkt_set_outb_queue }; /* Debugging and Diagnostics */ static void aac_describe_controller(struct aac_softc *sc); static char *aac_describe_code(struct aac_code_lookup *table, u_int32_t code); /* Management Interface */ static d_open_t aac_open; static d_close_t aac_close; static d_ioctl_t aac_ioctl; static d_poll_t aac_poll; static int aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib); static int aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg); static void aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib); static int aac_rev_check(struct aac_softc *sc, caddr_t udata); static int aac_open_aif(struct aac_softc *sc, caddr_t arg); static int aac_close_aif(struct aac_softc *sc, caddr_t arg); static int aac_getnext_aif(struct aac_softc *sc, caddr_t arg); static int aac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr); static int aac_query_disk(struct aac_softc *sc, caddr_t uptr); static int aac_get_pci_info(struct aac_softc *sc, caddr_t uptr); static int aac_supported_features(struct aac_softc *sc, caddr_t uptr); static void aac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg); static struct aac_mntinforesp * aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid); static struct cdevsw aac_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = aac_open, .d_close = aac_close, .d_ioctl = aac_ioctl, .d_poll = aac_poll, .d_name = "aac", }; MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver"); /* sysctl node */ SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters"); /* * Device Interface */ /* * Initialize the controller and softc */ int aac_attach(struct aac_softc *sc) { int error, unit; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* * Initialize per-controller queues. */ aac_initq_free(sc); aac_initq_ready(sc); aac_initq_busy(sc); aac_initq_bio(sc); /* * Initialize command-completion task. */ TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc); /* mark controller as suspended until we get ourselves organised */ sc->aac_state |= AAC_STATE_SUSPEND; /* * Check that the firmware on the card is supported. */ if ((error = aac_check_firmware(sc)) != 0) return(error); /* * Initialize locks */ mtx_init(&sc->aac_aifq_lock, "AAC AIF lock", NULL, MTX_DEF); mtx_init(&sc->aac_io_lock, "AAC I/O lock", NULL, MTX_DEF); mtx_init(&sc->aac_container_lock, "AAC container lock", NULL, MTX_DEF); TAILQ_INIT(&sc->aac_container_tqh); TAILQ_INIT(&sc->aac_ev_cmfree); /* Initialize the clock daemon callout. */ callout_init_mtx(&sc->aac_daemontime, &sc->aac_io_lock, 0); /* * Initialize the adapter. */ if ((error = aac_alloc(sc)) != 0) return(error); if ((error = aac_init(sc)) != 0) return(error); /* * Allocate and connect our interrupt. */ if ((error = aac_setup_intr(sc)) != 0) return(error); /* * Print a little information about the controller. */ aac_describe_controller(sc); /* * Register to probe our containers later. */ sc->aac_ich.ich_func = aac_startup; sc->aac_ich.ich_arg = sc; if (config_intrhook_establish(&sc->aac_ich) != 0) { device_printf(sc->aac_dev, "can't establish configuration hook\n"); return(ENXIO); } /* * Make the control device. */ unit = device_get_unit(sc->aac_dev); sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "aac%d", unit); (void)make_dev_alias(sc->aac_dev_t, "afa%d", unit); (void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit); sc->aac_dev_t->si_drv1 = sc; /* Create the AIF thread */ if (kproc_create((void(*)(void *))aac_command_thread, sc, &sc->aifthread, 0, 0, "aac%daif", unit)) panic("Could not create AIF thread"); /* Register the shutdown method to only be called post-dump */ if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown, sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL) device_printf(sc->aac_dev, "shutdown event registration failed\n"); /* Register with CAM for the non-DASD devices */ if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) { TAILQ_INIT(&sc->aac_sim_tqh); aac_get_bus_info(sc); } mtx_lock(&sc->aac_io_lock); callout_reset(&sc->aac_daemontime, 60 * hz, aac_daemon, sc); mtx_unlock(&sc->aac_io_lock); return(0); } static void aac_daemon(void *arg) { struct timeval tv; struct aac_softc *sc; struct aac_fib *fib; sc = arg; mtx_assert(&sc->aac_io_lock, MA_OWNED); if (callout_pending(&sc->aac_daemontime) || callout_active(&sc->aac_daemontime) == 0) return; getmicrotime(&tv); aac_alloc_sync_fib(sc, &fib); *(uint32_t *)fib->data = tv.tv_sec; aac_sync_fib(sc, SendHostTime, 0, fib, sizeof(uint32_t)); aac_release_sync_fib(sc); callout_schedule(&sc->aac_daemontime, 30 * 60 * hz); } void aac_add_event(struct aac_softc *sc, struct aac_event *event) { switch (event->ev_type & AAC_EVENT_MASK) { case AAC_EVENT_CMFREE: TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links); break; default: device_printf(sc->aac_dev, "aac_add event: unknown event %d\n", event->ev_type); break; } return; } /* * Request information of container #cid */ static struct aac_mntinforesp * aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid) { struct aac_mntinfo *mi; mi = (struct aac_mntinfo *)&fib->data[0]; /* use 64-bit LBA if enabled */ mi->Command = (sc->flags & AAC_FLAGS_LBA_64BIT) ? VM_NameServe64 : VM_NameServe; mi->MntType = FT_FILESYS; mi->MntCount = cid; if (aac_sync_fib(sc, ContainerCommand, 0, fib, sizeof(struct aac_mntinfo))) { printf("Error probing container %d\n", cid); return (NULL); } return ((struct aac_mntinforesp *)&fib->data[0]); } /* * Probe for containers, create disks. */ static void aac_startup(void *arg) { struct aac_softc *sc; struct aac_fib *fib; struct aac_mntinforesp *mir; int count = 0, i = 0; sc = (struct aac_softc *)arg; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* disconnect ourselves from the intrhook chain */ config_intrhook_disestablish(&sc->aac_ich); mtx_lock(&sc->aac_io_lock); aac_alloc_sync_fib(sc, &fib); /* loop over possible containers */ do { if ((mir = aac_get_container_info(sc, fib, i)) == NULL) continue; if (i == 0) count = mir->MntRespCount; aac_add_container(sc, mir, 0); i++; } while ((i < count) && (i < AAC_MAX_CONTAINERS)); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); /* poke the bus to actually attach the child devices */ if (bus_generic_attach(sc->aac_dev)) device_printf(sc->aac_dev, "bus_generic_attach failed\n"); /* mark the controller up */ sc->aac_state &= ~AAC_STATE_SUSPEND; /* enable interrupts now */ AAC_UNMASK_INTERRUPTS(sc); } /* * Create a device to represent a new container */ static void aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f) { struct aac_container *co; device_t child; /* * Check container volume type for validity. Note that many of * the possible types may never show up. */ if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) { co = (struct aac_container *)malloc(sizeof *co, M_AACBUF, M_NOWAIT | M_ZERO); if (co == NULL) panic("Out of memory?!"); fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "id %x name '%.16s' size %u type %d", mir->MntTable[0].ObjectId, mir->MntTable[0].FileSystemName, mir->MntTable[0].Capacity, mir->MntTable[0].VolType); if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL) device_printf(sc->aac_dev, "device_add_child failed\n"); else device_set_ivars(child, co); device_set_desc(child, aac_describe_code(aac_container_types, mir->MntTable[0].VolType)); co->co_disk = child; co->co_found = f; bcopy(&mir->MntTable[0], &co->co_mntobj, sizeof(struct aac_mntobj)); mtx_lock(&sc->aac_container_lock); TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link); mtx_unlock(&sc->aac_container_lock); } } /* * Allocate resources associated with (sc) */ static int aac_alloc(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* * Create DMA tag for mapping buffers into controller-addressable space. */ if (bus_dma_tag_create(sc->aac_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ (sc->flags & AAC_FLAGS_SG_64BIT) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, /* maxsize */ sc->aac_sg_tablesize, /* nsegments */ MAXBSIZE, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, /* lockfunc */ &sc->aac_io_lock, /* lockfuncarg */ &sc->aac_buffer_dmat)) { device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n"); return (ENOMEM); } /* * Create DMA tag for mapping FIBs into controller-addressable space.. */ if (bus_dma_tag_create(sc->aac_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ (sc->flags & AAC_FLAGS_4GB_WINDOW) ? BUS_SPACE_MAXADDR_32BIT : 0x7fffffff, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sc->aac_max_fibs_alloc * sc->aac_max_fib_size, /* maxsize */ 1, /* nsegments */ sc->aac_max_fibs_alloc * sc->aac_max_fib_size, /* maxsize */ 0, /* flags */ NULL, NULL, /* No locking needed */ &sc->aac_fib_dmat)) { device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n"); return (ENOMEM); } /* * Create DMA tag for the common structure and allocate it. */ if (bus_dma_tag_create(sc->aac_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ (sc->flags & AAC_FLAGS_4GB_WINDOW) ? BUS_SPACE_MAXADDR_32BIT : 0x7fffffff, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 8192 + sizeof(struct aac_common), /* maxsize */ 1, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* No locking needed */ &sc->aac_common_dmat)) { device_printf(sc->aac_dev, "can't allocate common structure DMA tag\n"); return (ENOMEM); } if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common, BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) { device_printf(sc->aac_dev, "can't allocate common structure\n"); return (ENOMEM); } /* * Work around a bug in the 2120 and 2200 that cannot DMA commands * below address 8192 in physical memory. * XXX If the padding is not needed, can it be put to use instead * of ignored? */ (void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap, sc->aac_common, 8192 + sizeof(*sc->aac_common), aac_common_map, sc, 0); if (sc->aac_common_busaddr < 8192) { sc->aac_common = (struct aac_common *) ((uint8_t *)sc->aac_common + 8192); sc->aac_common_busaddr += 8192; } bzero(sc->aac_common, sizeof(*sc->aac_common)); /* Allocate some FIBs and associated command structs */ TAILQ_INIT(&sc->aac_fibmap_tqh); sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command), M_AACBUF, M_WAITOK|M_ZERO); while (sc->total_fibs < sc->aac_max_fibs) { if (aac_alloc_commands(sc) != 0) break; } if (sc->total_fibs == 0) return (ENOMEM); return (0); } /* * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ void aac_free(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* remove the control device */ if (sc->aac_dev_t != NULL) destroy_dev(sc->aac_dev_t); /* throw away any FIB buffers, discard the FIB DMA tag */ aac_free_commands(sc); if (sc->aac_fib_dmat) bus_dma_tag_destroy(sc->aac_fib_dmat); free(sc->aac_commands, M_AACBUF); /* destroy the common area */ if (sc->aac_common) { bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap); bus_dmamem_free(sc->aac_common_dmat, sc->aac_common, sc->aac_common_dmamap); } if (sc->aac_common_dmat) bus_dma_tag_destroy(sc->aac_common_dmat); /* disconnect the interrupt handler */ if (sc->aac_intr) bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr); if (sc->aac_irq != NULL) bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid, sc->aac_irq); /* destroy data-transfer DMA tag */ if (sc->aac_buffer_dmat) bus_dma_tag_destroy(sc->aac_buffer_dmat); /* destroy the parent DMA tag */ if (sc->aac_parent_dmat) bus_dma_tag_destroy(sc->aac_parent_dmat); /* release the register window mapping */ if (sc->aac_regs_res0 != NULL) bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, sc->aac_regs_rid0, sc->aac_regs_res0); if (sc->aac_hwif == AAC_HWIF_NARK && sc->aac_regs_res1 != NULL) bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, sc->aac_regs_rid1, sc->aac_regs_res1); } /* * Disconnect from the controller completely, in preparation for unload. */ int aac_detach(device_t dev) { struct aac_softc *sc; struct aac_container *co; struct aac_sim *sim; int error; sc = device_get_softc(dev); fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if (sc->aac_state & AAC_STATE_OPEN) return(EBUSY); callout_drain(&sc->aac_daemontime); /* Remove the child containers */ while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) { error = device_delete_child(dev, co->co_disk); if (error) return (error); TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link); free(co, M_AACBUF); } /* Remove the CAM SIMs */ while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) { TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link); error = device_delete_child(dev, sim->sim_dev); if (error) return (error); free(sim, M_AACBUF); } if (sc->aifflags & AAC_AIFFLAGS_RUNNING) { sc->aifflags |= AAC_AIFFLAGS_EXIT; wakeup(sc->aifthread); tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz); } if (sc->aifflags & AAC_AIFFLAGS_RUNNING) panic("Cannot shutdown AIF thread"); if ((error = aac_shutdown(dev))) return(error); EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh); aac_free(sc); mtx_destroy(&sc->aac_aifq_lock); mtx_destroy(&sc->aac_io_lock); mtx_destroy(&sc->aac_container_lock); return(0); } /* * Bring the controller down to a dormant state and detach all child devices. * * This function is called before detach or system shutdown. * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ int aac_shutdown(device_t dev) { struct aac_softc *sc; struct aac_fib *fib; struct aac_close_command *cc; sc = device_get_softc(dev); fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); sc->aac_state |= AAC_STATE_SUSPEND; /* * Send a Container shutdown followed by a HostShutdown FIB to the * controller to convince it that we don't want to talk to it anymore. * We've been closed and all I/O completed already */ device_printf(sc->aac_dev, "shutting down controller..."); mtx_lock(&sc->aac_io_lock); aac_alloc_sync_fib(sc, &fib); cc = (struct aac_close_command *)&fib->data[0]; bzero(cc, sizeof(struct aac_close_command)); cc->Command = VM_CloseAll; cc->ContainerId = 0xffffffff; if (aac_sync_fib(sc, ContainerCommand, 0, fib, sizeof(struct aac_close_command))) printf("FAILED.\n"); else printf("done\n"); #if 0 else { fib->data[0] = 0; /* * XXX Issuing this command to the controller makes it shut down * but also keeps it from coming back up without a reset of the * PCI bus. This is not desirable if you are just unloading the * driver module with the intent to reload it later. */ if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN, fib, 1)) { printf("FAILED.\n"); } else { printf("done.\n"); } } #endif AAC_MASK_INTERRUPTS(sc); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); return(0); } /* * Bring the controller to a quiescent state, ready for system suspend. */ int aac_suspend(device_t dev) { struct aac_softc *sc; sc = device_get_softc(dev); fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); sc->aac_state |= AAC_STATE_SUSPEND; AAC_MASK_INTERRUPTS(sc); return(0); } /* * Bring the controller back to a state ready for operation. */ int aac_resume(device_t dev) { struct aac_softc *sc; sc = device_get_softc(dev); fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); sc->aac_state &= ~AAC_STATE_SUSPEND; AAC_UNMASK_INTERRUPTS(sc); return(0); } /* * Interrupt handler for NEW_COMM interface. */ void aac_new_intr(void *arg) { struct aac_softc *sc; u_int32_t index, fast; struct aac_command *cm; struct aac_fib *fib; int i; sc = (struct aac_softc *)arg; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_lock(&sc->aac_io_lock); while (1) { index = AAC_GET_OUTB_QUEUE(sc); if (index == 0xffffffff) index = AAC_GET_OUTB_QUEUE(sc); if (index == 0xffffffff) break; if (index & 2) { if (index == 0xfffffffe) { /* XXX This means that the controller wants * more work. Ignore it for now. */ continue; } /* AIF */ fib = (struct aac_fib *)malloc(sizeof *fib, M_AACBUF, M_NOWAIT | M_ZERO); if (fib == NULL) { /* If we're really this short on memory, * hopefully breaking out of the handler will * allow something to get freed. This * actually sucks a whole lot. */ break; } index &= ~2; for (i = 0; i < sizeof(struct aac_fib)/4; ++i) ((u_int32_t *)fib)[i] = AAC_MEM1_GETREG4(sc, index + i*4); aac_handle_aif(sc, fib); free(fib, M_AACBUF); /* * AIF memory is owned by the adapter, so let it * know that we are done with it. */ AAC_SET_OUTB_QUEUE(sc, index); AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY); } else { fast = index & 1; cm = sc->aac_commands + (index >> 2); fib = cm->cm_fib; if (fast) { fib->Header.XferState |= AAC_FIBSTATE_DONEADAP; *((u_int32_t *)(fib->data)) = AAC_ERROR_NORMAL; } aac_remove_busy(cm); aac_unmap_command(cm); cm->cm_flags |= AAC_CMD_COMPLETED; /* is there a completion handler? */ if (cm->cm_complete != NULL) { cm->cm_complete(cm); } else { /* assume that someone is sleeping on this * command */ wakeup(cm); } sc->flags &= ~AAC_QUEUE_FRZN; } } /* see if we can start some more I/O */ if ((sc->flags & AAC_QUEUE_FRZN) == 0) aac_startio(sc); mtx_unlock(&sc->aac_io_lock); } /* * Interrupt filter for !NEW_COMM interface. */ int aac_filter(void *arg) { struct aac_softc *sc; u_int16_t reason; sc = (struct aac_softc *)arg; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* * Read the status register directly. This is faster than taking the * driver lock and reading the queues directly. It also saves having * to turn parts of the driver lock into a spin mutex, which would be * ugly. */ reason = AAC_GET_ISTATUS(sc); AAC_CLEAR_ISTATUS(sc, reason); /* handle completion processing */ if (reason & AAC_DB_RESPONSE_READY) taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete); /* controller wants to talk to us */ if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) { /* * XXX Make sure that we don't get fooled by strange messages * that start with a NULL. */ if ((reason & AAC_DB_PRINTF) && (sc->aac_common->ac_printf[0] == 0)) sc->aac_common->ac_printf[0] = 32; /* * This might miss doing the actual wakeup. However, the * msleep that this is waking up has a timeout, so it will * wake up eventually. AIFs and printfs are low enough * priority that they can handle hanging out for a few seconds * if needed. */ wakeup(sc->aifthread); } return (FILTER_HANDLED); } /* * Command Processing */ /* * Start as much queued I/O as possible on the controller */ void aac_startio(struct aac_softc *sc) { struct aac_command *cm; int error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); for (;;) { /* * This flag might be set if the card is out of resources. * Checking it here prevents an infinite loop of deferrals. */ if (sc->flags & AAC_QUEUE_FRZN) break; /* * Try to get a command that's been put off for lack of * resources */ cm = aac_dequeue_ready(sc); /* * Try to build a command off the bio queue (ignore error * return) */ if (cm == NULL) aac_bio_command(sc, &cm); /* nothing to do? */ if (cm == NULL) break; /* don't map more than once */ if (cm->cm_flags & AAC_CMD_MAPPED) panic("aac: command %p already mapped", cm); /* * Set up the command to go to the controller. If there are no * data buffers associated with the command then it can bypass * busdma. */ if (cm->cm_datalen != 0) { error = bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap, cm->cm_data, cm->cm_datalen, aac_map_command_sg, cm, 0); if (error == EINPROGRESS) { fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "freezing queue\n"); sc->flags |= AAC_QUEUE_FRZN; error = 0; } else if (error != 0) panic("aac_startio: unexpected error %d from " "busdma", error); } else aac_map_command_sg(cm, NULL, 0, 0); } } /* * Handle notification of one or more FIBs coming from the controller. */ static void aac_command_thread(struct aac_softc *sc) { struct aac_fib *fib; u_int32_t fib_size; int size, retval; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_lock(&sc->aac_io_lock); sc->aifflags = AAC_AIFFLAGS_RUNNING; while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) { retval = 0; if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0) retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO, "aifthd", AAC_PERIODIC_INTERVAL * hz); /* * First see if any FIBs need to be allocated. This needs * to be called without the driver lock because contigmalloc * will grab Giant, and would result in an LOR. */ if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) { mtx_unlock(&sc->aac_io_lock); aac_alloc_commands(sc); mtx_lock(&sc->aac_io_lock); sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS; aac_startio(sc); } /* * While we're here, check to see if any commands are stuck. * This is pretty low-priority, so it's ok if it doesn't * always fire. */ if (retval == EWOULDBLOCK) aac_timeout(sc); /* Check the hardware printf message buffer */ if (sc->aac_common->ac_printf[0] != 0) aac_print_printf(sc); /* Also check to see if the adapter has a command for us. */ if (sc->flags & AAC_FLAGS_NEW_COMM) continue; for (;;) { if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE, &fib_size, &fib)) break; AAC_PRINT_FIB(sc, fib); switch (fib->Header.Command) { case AifRequest: aac_handle_aif(sc, fib); break; default: device_printf(sc->aac_dev, "unknown command " "from controller\n"); break; } if ((fib->Header.XferState == 0) || (fib->Header.StructType != AAC_FIBTYPE_TFIB)) { break; } /* Return the AIF to the controller. */ if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) { fib->Header.XferState |= AAC_FIBSTATE_DONEHOST; *(AAC_FSAStatus*)fib->data = ST_OK; /* XXX Compute the Size field? */ size = fib->Header.Size; if (size > sizeof(struct aac_fib)) { size = sizeof(struct aac_fib); fib->Header.Size = size; } /* * Since we did not generate this command, it * cannot go through the normal * enqueue->startio chain. */ aac_enqueue_response(sc, AAC_ADAP_NORM_RESP_QUEUE, fib); } } } sc->aifflags &= ~AAC_AIFFLAGS_RUNNING; mtx_unlock(&sc->aac_io_lock); wakeup(sc->aac_dev); kproc_exit(0); } /* * Process completed commands. */ static void aac_complete(void *context, int pending) { struct aac_softc *sc; struct aac_command *cm; struct aac_fib *fib; u_int32_t fib_size; sc = (struct aac_softc *)context; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_lock(&sc->aac_io_lock); /* pull completed commands off the queue */ for (;;) { /* look for completed FIBs on our queue */ if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size, &fib)) break; /* nothing to do */ /* get the command, unmap and hand off for processing */ cm = sc->aac_commands + fib->Header.SenderData; if (cm == NULL) { AAC_PRINT_FIB(sc, fib); break; } aac_remove_busy(cm); aac_unmap_command(cm); cm->cm_flags |= AAC_CMD_COMPLETED; /* is there a completion handler? */ if (cm->cm_complete != NULL) { cm->cm_complete(cm); } else { /* assume that someone is sleeping on this command */ wakeup(cm); } } /* see if we can start some more I/O */ sc->flags &= ~AAC_QUEUE_FRZN; aac_startio(sc); mtx_unlock(&sc->aac_io_lock); } /* * Handle a bio submitted from a disk device. */ void aac_submit_bio(struct bio *bp) { struct aac_disk *ad; struct aac_softc *sc; ad = (struct aac_disk *)bp->bio_disk->d_drv1; sc = ad->ad_controller; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* queue the BIO and try to get some work done */ aac_enqueue_bio(sc, bp); aac_startio(sc); } /* * Get a bio and build a command to go with it. */ static int aac_bio_command(struct aac_softc *sc, struct aac_command **cmp) { struct aac_command *cm; struct aac_fib *fib; struct aac_disk *ad; struct bio *bp; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* get the resources we will need */ cm = NULL; bp = NULL; if (aac_alloc_command(sc, &cm)) /* get a command */ goto fail; if ((bp = aac_dequeue_bio(sc)) == NULL) goto fail; /* fill out the command */ cm->cm_data = (void *)bp->bio_data; cm->cm_datalen = bp->bio_bcount; cm->cm_complete = aac_bio_complete; cm->cm_private = bp; cm->cm_timestamp = time_uptime; - cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE; /* build the FIB */ fib = cm->cm_fib; fib->Header.Size = sizeof(struct aac_fib_header); fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY | AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM | AAC_FIBSTATE_ASYNC | AAC_FIBSTATE_FAST_RESPONSE; /* build the read/write request */ ad = (struct aac_disk *)bp->bio_disk->d_drv1; if (sc->flags & AAC_FLAGS_RAW_IO) { struct aac_raw_io *raw; raw = (struct aac_raw_io *)&fib->data[0]; fib->Header.Command = RawIo; raw->BlockNumber = (u_int64_t)bp->bio_pblkno; raw->ByteCount = bp->bio_bcount; raw->ContainerId = ad->ad_container->co_mntobj.ObjectId; raw->BpTotal = 0; raw->BpComplete = 0; fib->Header.Size += sizeof(struct aac_raw_io); cm->cm_sgtable = (struct aac_sg_table *)&raw->SgMapRaw; if (bp->bio_cmd == BIO_READ) { raw->Flags = 1; cm->cm_flags |= AAC_CMD_DATAIN; } else { raw->Flags = 0; cm->cm_flags |= AAC_CMD_DATAOUT; } } else if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) { fib->Header.Command = ContainerCommand; if (bp->bio_cmd == BIO_READ) { struct aac_blockread *br; br = (struct aac_blockread *)&fib->data[0]; br->Command = VM_CtBlockRead; br->ContainerId = ad->ad_container->co_mntobj.ObjectId; br->BlockNumber = bp->bio_pblkno; br->ByteCount = bp->bio_bcount; fib->Header.Size += sizeof(struct aac_blockread); cm->cm_sgtable = &br->SgMap; cm->cm_flags |= AAC_CMD_DATAIN; } else { struct aac_blockwrite *bw; bw = (struct aac_blockwrite *)&fib->data[0]; bw->Command = VM_CtBlockWrite; bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; bw->BlockNumber = bp->bio_pblkno; bw->ByteCount = bp->bio_bcount; bw->Stable = CUNSTABLE; fib->Header.Size += sizeof(struct aac_blockwrite); cm->cm_flags |= AAC_CMD_DATAOUT; cm->cm_sgtable = &bw->SgMap; } } else { fib->Header.Command = ContainerCommand64; if (bp->bio_cmd == BIO_READ) { struct aac_blockread64 *br; br = (struct aac_blockread64 *)&fib->data[0]; br->Command = VM_CtHostRead64; br->ContainerId = ad->ad_container->co_mntobj.ObjectId; br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE; br->BlockNumber = bp->bio_pblkno; br->Pad = 0; br->Flags = 0; fib->Header.Size += sizeof(struct aac_blockread64); cm->cm_flags |= AAC_CMD_DATAIN; cm->cm_sgtable = (struct aac_sg_table *)&br->SgMap64; } else { struct aac_blockwrite64 *bw; bw = (struct aac_blockwrite64 *)&fib->data[0]; bw->Command = VM_CtHostWrite64; bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE; bw->BlockNumber = bp->bio_pblkno; bw->Pad = 0; bw->Flags = 0; fib->Header.Size += sizeof(struct aac_blockwrite64); cm->cm_flags |= AAC_CMD_DATAOUT; cm->cm_sgtable = (struct aac_sg_table *)&bw->SgMap64; } } *cmp = cm; return(0); fail: if (bp != NULL) aac_enqueue_bio(sc, bp); if (cm != NULL) aac_release_command(cm); return(ENOMEM); } /* * Handle a bio-instigated command that has been completed. */ static void aac_bio_complete(struct aac_command *cm) { struct aac_blockread_response *brr; struct aac_blockwrite_response *bwr; struct bio *bp; AAC_FSAStatus status; /* fetch relevant status and then release the command */ bp = (struct bio *)cm->cm_private; if (bp->bio_cmd == BIO_READ) { brr = (struct aac_blockread_response *)&cm->cm_fib->data[0]; status = brr->Status; } else { bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0]; status = bwr->Status; } aac_release_command(cm); /* fix up the bio based on status */ if (status == ST_OK) { bp->bio_resid = 0; } else { bp->bio_error = EIO; bp->bio_flags |= BIO_ERROR; /* pass an error string out to the disk layer */ bp->bio_driver1 = aac_describe_code(aac_command_status_table, status); } aac_biodone(bp); } /* * Submit a command to the controller, return when it completes. * XXX This is very dangerous! If the card has gone out to lunch, we could * be stuck here forever. At the same time, signals are not caught * because there is a risk that a signal could wakeup the sleep before * the card has a chance to complete the command. Since there is no way * to cancel a command that is in progress, we can't protect against the * card completing a command late and spamming the command and data * memory. So, we are held hostage until the command completes. */ static int aac_wait_command(struct aac_command *cm) { struct aac_softc *sc; int error; sc = cm->cm_sc; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* Put the command on the ready queue and get things going */ - cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE; aac_enqueue_ready(cm); aac_startio(sc); error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0); return(error); } /* *Command Buffer Management */ /* * Allocate a command. */ int aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp) { struct aac_command *cm; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if ((cm = aac_dequeue_free(sc)) == NULL) { if (sc->total_fibs < sc->aac_max_fibs) { sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS; wakeup(sc->aifthread); } return (EBUSY); } *cmp = cm; return(0); } /* * Release a command back to the freelist. */ void aac_release_command(struct aac_command *cm) { struct aac_event *event; struct aac_softc *sc; sc = cm->cm_sc; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* (re)initialize the command/FIB */ cm->cm_sgtable = NULL; cm->cm_flags = 0; cm->cm_complete = NULL; cm->cm_private = NULL; + cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE; cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY; cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB; cm->cm_fib->Header.Flags = 0; cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size; /* * These are duplicated in aac_start to cover the case where an * intermediate stage may have destroyed them. They're left * initialized here for debugging purposes only. */ cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys; cm->cm_fib->Header.SenderData = 0; aac_enqueue_free(cm); /* * Dequeue all events so that there's no risk of events getting * stranded. */ while ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) { TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links); event->ev_callback(sc, event, event->ev_arg); } } /* * Map helper for command/FIB allocation. */ static void aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { uint64_t *fibphys; fibphys = (uint64_t *)arg; *fibphys = segs[0].ds_addr; } /* * Allocate and initialize commands/FIBs for this adapter. */ static int aac_alloc_commands(struct aac_softc *sc) { struct aac_command *cm; struct aac_fibmap *fm; uint64_t fibphys; int i, error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs) return (ENOMEM); fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO); if (fm == NULL) return (ENOMEM); /* allocate the FIBs in DMAable memory and load them */ if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs, BUS_DMA_NOWAIT, &fm->aac_fibmap)) { device_printf(sc->aac_dev, "Not enough contiguous memory available.\n"); free(fm, M_AACBUF); return (ENOMEM); } /* Ignore errors since this doesn't bounce */ (void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs, sc->aac_max_fibs_alloc * sc->aac_max_fib_size, aac_map_command_helper, &fibphys, 0); /* initialize constant fields in the command structure */ bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * sc->aac_max_fib_size); for (i = 0; i < sc->aac_max_fibs_alloc; i++) { cm = sc->aac_commands + sc->total_fibs; fm->aac_commands = cm; cm->cm_sc = sc; cm->cm_fib = (struct aac_fib *) ((u_int8_t *)fm->aac_fibs + i*sc->aac_max_fib_size); cm->cm_fibphys = fibphys + i*sc->aac_max_fib_size; cm->cm_index = sc->total_fibs; if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0, &cm->cm_datamap)) != 0) break; mtx_lock(&sc->aac_io_lock); aac_release_command(cm); sc->total_fibs++; mtx_unlock(&sc->aac_io_lock); } if (i > 0) { mtx_lock(&sc->aac_io_lock); TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link); fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs); mtx_unlock(&sc->aac_io_lock); return (0); } bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap); bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap); free(fm, M_AACBUF); return (ENOMEM); } /* * Free FIBs owned by this adapter. */ static void aac_free_commands(struct aac_softc *sc) { struct aac_fibmap *fm; struct aac_command *cm; int i; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) { TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link); /* * We check against total_fibs to handle partially * allocated blocks. */ for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) { cm = fm->aac_commands + i; bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap); } bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap); bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap); free(fm, M_AACBUF); } } /* * Command-mapping helper function - populate this command's s/g table. */ static void aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct aac_softc *sc; struct aac_command *cm; struct aac_fib *fib; int i; cm = (struct aac_command *)arg; sc = cm->cm_sc; fib = cm->cm_fib; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* copy into the FIB */ if (cm->cm_sgtable != NULL) { if (fib->Header.Command == RawIo) { struct aac_sg_tableraw *sg; sg = (struct aac_sg_tableraw *)cm->cm_sgtable; sg->SgCount = nseg; for (i = 0; i < nseg; i++) { sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr; sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len; sg->SgEntryRaw[i].Next = 0; sg->SgEntryRaw[i].Prev = 0; sg->SgEntryRaw[i].Flags = 0; } /* update the FIB size for the s/g count */ fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw); } else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) { struct aac_sg_table *sg; sg = cm->cm_sgtable; sg->SgCount = nseg; for (i = 0; i < nseg; i++) { sg->SgEntry[i].SgAddress = segs[i].ds_addr; sg->SgEntry[i].SgByteCount = segs[i].ds_len; } /* update the FIB size for the s/g count */ fib->Header.Size += nseg*sizeof(struct aac_sg_entry); } else { struct aac_sg_table64 *sg; sg = (struct aac_sg_table64 *)cm->cm_sgtable; sg->SgCount = nseg; for (i = 0; i < nseg; i++) { sg->SgEntry64[i].SgAddress = segs[i].ds_addr; sg->SgEntry64[i].SgByteCount = segs[i].ds_len; } /* update the FIB size for the s/g count */ fib->Header.Size += nseg*sizeof(struct aac_sg_entry64); } } /* Fix up the address values in the FIB. Use the command array index * instead of a pointer since these fields are only 32 bits. Shift * the SenderFibAddress over to make room for the fast response bit * and for the AIF bit */ cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2); cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys; /* save a pointer to the command for speedy reverse-lookup */ cm->cm_fib->Header.SenderData = cm->cm_index; if (cm->cm_flags & AAC_CMD_DATAIN) bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_PREREAD); if (cm->cm_flags & AAC_CMD_DATAOUT) bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_PREWRITE); cm->cm_flags |= AAC_CMD_MAPPED; if (sc->flags & AAC_FLAGS_NEW_COMM) { int count = 10000000L; while (AAC_SEND_COMMAND(sc, cm) != 0) { if (--count == 0) { aac_unmap_command(cm); sc->flags |= AAC_QUEUE_FRZN; aac_requeue_ready(cm); } DELAY(5); /* wait 5 usec. */ } } else { /* Put the FIB on the outbound queue */ if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) { aac_unmap_command(cm); sc->flags |= AAC_QUEUE_FRZN; aac_requeue_ready(cm); } } return; } /* * Unmap a command from controller-visible space. */ static void aac_unmap_command(struct aac_command *cm) { struct aac_softc *sc; sc = cm->cm_sc; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if (!(cm->cm_flags & AAC_CMD_MAPPED)) return; if (cm->cm_datalen != 0) { if (cm->cm_flags & AAC_CMD_DATAIN) bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_POSTREAD); if (cm->cm_flags & AAC_CMD_DATAOUT) bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap); } cm->cm_flags &= ~AAC_CMD_MAPPED; } /* * Hardware Interface */ /* * Initialize the adapter. */ static void aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct aac_softc *sc; sc = (struct aac_softc *)arg; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); sc->aac_common_busaddr = segs[0].ds_addr; } static int aac_check_firmware(struct aac_softc *sc) { u_int32_t code, major, minor, options = 0, atu_size = 0; int status; time_t then; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* * Wait for the adapter to come ready. */ then = time_uptime; do { code = AAC_GET_FWSTATUS(sc); if (code & AAC_SELF_TEST_FAILED) { device_printf(sc->aac_dev, "FATAL: selftest failed\n"); return(ENXIO); } if (code & AAC_KERNEL_PANIC) { device_printf(sc->aac_dev, "FATAL: controller kernel panic"); return(ENXIO); } if (time_uptime > (then + AAC_BOOT_TIMEOUT)) { device_printf(sc->aac_dev, "FATAL: controller not coming ready, " "status %x\n", code); return(ENXIO); } } while (!(code & AAC_UP_AND_RUNNING)); /* * Retrieve the firmware version numbers. Dell PERC2/QC cards with * firmware version 1.x are not compatible with this driver. */ if (sc->flags & AAC_FLAGS_PERC2QC) { if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0, NULL)) { device_printf(sc->aac_dev, "Error reading firmware version\n"); return (EIO); } /* These numbers are stored as ASCII! */ major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30; minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30; if (major == 1) { device_printf(sc->aac_dev, "Firmware version %d.%d is not supported.\n", major, minor); return (EINVAL); } } /* * Retrieve the capabilities/supported options word so we know what * work-arounds to enable. Some firmware revs don't support this * command. */ if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status)) { if (status != AAC_SRB_STS_INVALID_REQUEST) { device_printf(sc->aac_dev, "RequestAdapterInfo failed\n"); return (EIO); } } else { options = AAC_GET_MAILBOX(sc, 1); atu_size = AAC_GET_MAILBOX(sc, 2); sc->supported_options = options; if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 && (sc->flags & AAC_FLAGS_NO4GB) == 0) sc->flags |= AAC_FLAGS_4GB_WINDOW; if (options & AAC_SUPPORTED_NONDASD) sc->flags |= AAC_FLAGS_ENABLE_CAM; if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0 && (sizeof(bus_addr_t) > 4)) { device_printf(sc->aac_dev, "Enabling 64-bit address support\n"); sc->flags |= AAC_FLAGS_SG_64BIT; } if ((options & AAC_SUPPORTED_NEW_COMM) && sc->aac_if.aif_send_command) sc->flags |= AAC_FLAGS_NEW_COMM; if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE) sc->flags |= AAC_FLAGS_ARRAY_64BIT; } /* Check for broken hardware that does a lower number of commands */ sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512); /* Remap mem. resource, if required */ if ((sc->flags & AAC_FLAGS_NEW_COMM) && atu_size > rman_get_size(sc->aac_regs_res1)) { bus_release_resource( sc->aac_dev, SYS_RES_MEMORY, sc->aac_regs_rid1, sc->aac_regs_res1); sc->aac_regs_res1 = bus_alloc_resource( sc->aac_dev, SYS_RES_MEMORY, &sc->aac_regs_rid1, 0ul, ~0ul, atu_size, RF_ACTIVE); if (sc->aac_regs_res1 == NULL) { sc->aac_regs_res1 = bus_alloc_resource_any( sc->aac_dev, SYS_RES_MEMORY, &sc->aac_regs_rid1, RF_ACTIVE); if (sc->aac_regs_res1 == NULL) { device_printf(sc->aac_dev, "couldn't allocate register window\n"); return (ENXIO); } sc->flags &= ~AAC_FLAGS_NEW_COMM; } sc->aac_btag1 = rman_get_bustag(sc->aac_regs_res1); sc->aac_bhandle1 = rman_get_bushandle(sc->aac_regs_res1); if (sc->aac_hwif == AAC_HWIF_NARK) { sc->aac_regs_res0 = sc->aac_regs_res1; sc->aac_regs_rid0 = sc->aac_regs_rid1; sc->aac_btag0 = sc->aac_btag1; sc->aac_bhandle0 = sc->aac_bhandle1; } } /* Read preferred settings */ sc->aac_max_fib_size = sizeof(struct aac_fib); sc->aac_max_sectors = 128; /* 64KB */ if (sc->flags & AAC_FLAGS_SG_64BIT) sc->aac_sg_tablesize = (AAC_FIB_DATASIZE - sizeof(struct aac_blockwrite64)) / sizeof(struct aac_sg_entry64); else sc->aac_sg_tablesize = (AAC_FIB_DATASIZE - sizeof(struct aac_blockwrite)) / sizeof(struct aac_sg_entry); if (!aac_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) { options = AAC_GET_MAILBOX(sc, 1); sc->aac_max_fib_size = (options & 0xFFFF); sc->aac_max_sectors = (options >> 16) << 1; options = AAC_GET_MAILBOX(sc, 2); sc->aac_sg_tablesize = (options >> 16); options = AAC_GET_MAILBOX(sc, 3); sc->aac_max_fibs = (options & 0xFFFF); } if (sc->aac_max_fib_size > PAGE_SIZE) sc->aac_max_fib_size = PAGE_SIZE; sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size; if (sc->aac_max_fib_size > sizeof(struct aac_fib)) { sc->flags |= AAC_FLAGS_RAW_IO; device_printf(sc->aac_dev, "Enable Raw I/O\n"); } if ((sc->flags & AAC_FLAGS_RAW_IO) && (sc->flags & AAC_FLAGS_ARRAY_64BIT)) { sc->flags |= AAC_FLAGS_LBA_64BIT; device_printf(sc->aac_dev, "Enable 64-bit array\n"); } return (0); } static int aac_init(struct aac_softc *sc) { struct aac_adapter_init *ip; u_int32_t qoffset; int error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* * Fill in the init structure. This tells the adapter about the * physical location of various important shared data structures. */ ip = &sc->aac_common->ac_init; ip->InitStructRevision = AAC_INIT_STRUCT_REVISION; if (sc->aac_max_fib_size > sizeof(struct aac_fib)) { ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4; sc->flags |= AAC_FLAGS_RAW_IO; } ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION; ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr + offsetof(struct aac_common, ac_fibs); ip->AdapterFibsVirtualAddress = 0; ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib); ip->AdapterFibAlign = sizeof(struct aac_fib); ip->PrintfBufferAddress = sc->aac_common_busaddr + offsetof(struct aac_common, ac_printf); ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE; /* * The adapter assumes that pages are 4K in size, except on some * broken firmware versions that do the page->byte conversion twice, * therefore 'assuming' that this value is in 16MB units (2^24). * Round up since the granularity is so high. */ ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE; if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) { ip->HostPhysMemPages = (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE; } ip->HostElapsedSeconds = time_uptime; /* reset later if invalid */ ip->InitFlags = 0; if (sc->flags & AAC_FLAGS_NEW_COMM) { ip->InitFlags = INITFLAGS_NEW_COMM_SUPPORTED; device_printf(sc->aac_dev, "New comm. interface enabled\n"); } ip->MaxIoCommands = sc->aac_max_fibs; ip->MaxIoSize = sc->aac_max_sectors << 9; ip->MaxFibSize = sc->aac_max_fib_size; /* * Initialize FIB queues. Note that it appears that the layout of the * indexes and the segmentation of the entries may be mandated by the * adapter, which is only told about the base of the queue index fields. * * The initial values of the indices are assumed to inform the adapter * of the sizes of the respective queues, and theoretically it could * work out the entire layout of the queue structures from this. We * take the easy route and just lay this area out like everyone else * does. * * The Linux driver uses a much more complex scheme whereby several * header records are kept for each queue. We use a couple of generic * list manipulation functions which 'know' the size of each list by * virtue of a table. */ qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN; qoffset &= ~(AAC_QUEUE_ALIGN - 1); sc->aac_queues = (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset); ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset; sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = AAC_HOST_NORM_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = AAC_HOST_NORM_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = AAC_HOST_HIGH_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = AAC_HOST_HIGH_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = AAC_ADAP_NORM_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = AAC_ADAP_NORM_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = AAC_ADAP_HIGH_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = AAC_ADAP_HIGH_CMD_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]= AAC_HOST_NORM_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]= AAC_HOST_NORM_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]= AAC_HOST_HIGH_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]= AAC_HOST_HIGH_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]= AAC_ADAP_NORM_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]= AAC_ADAP_NORM_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]= AAC_ADAP_HIGH_RESP_ENTRIES; sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]= AAC_ADAP_HIGH_RESP_ENTRIES; sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] = &sc->aac_queues->qt_HostNormCmdQueue[0]; sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] = &sc->aac_queues->qt_HostHighCmdQueue[0]; sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] = &sc->aac_queues->qt_AdapNormCmdQueue[0]; sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] = &sc->aac_queues->qt_AdapHighCmdQueue[0]; sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] = &sc->aac_queues->qt_HostNormRespQueue[0]; sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] = &sc->aac_queues->qt_HostHighRespQueue[0]; sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] = &sc->aac_queues->qt_AdapNormRespQueue[0]; sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] = &sc->aac_queues->qt_AdapHighRespQueue[0]; /* * Do controller-type-specific initialisation */ switch (sc->aac_hwif) { case AAC_HWIF_I960RX: AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, ~0); break; case AAC_HWIF_RKT: AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, ~0); break; default: break; } /* * Give the init structure to the controller. */ if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT, sc->aac_common_busaddr + offsetof(struct aac_common, ac_init), 0, 0, 0, NULL)) { device_printf(sc->aac_dev, "error establishing init structure\n"); error = EIO; goto out; } error = 0; out: return(error); } static int aac_setup_intr(struct aac_softc *sc) { sc->aac_irq_rid = 0; if ((sc->aac_irq = bus_alloc_resource_any(sc->aac_dev, SYS_RES_IRQ, &sc->aac_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(sc->aac_dev, "can't allocate interrupt\n"); return (EINVAL); } if (sc->flags & AAC_FLAGS_NEW_COMM) { if (bus_setup_intr(sc->aac_dev, sc->aac_irq, INTR_MPSAFE|INTR_TYPE_BIO, NULL, aac_new_intr, sc, &sc->aac_intr)) { device_printf(sc->aac_dev, "can't set up interrupt\n"); return (EINVAL); } } else { if (bus_setup_intr(sc->aac_dev, sc->aac_irq, INTR_TYPE_BIO, aac_filter, NULL, sc, &sc->aac_intr)) { device_printf(sc->aac_dev, "can't set up interrupt filter\n"); return (EINVAL); } } return (0); } /* * Send a synchronous command to the controller and wait for a result. * Indicate if the controller completed the command with an error status. */ static int aac_sync_command(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3, u_int32_t *sp) { time_t then; u_int32_t status; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* populate the mailbox */ AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3); /* ensure the sync command doorbell flag is cleared */ AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND); /* then set it to signal the adapter */ AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND); /* spin waiting for the command to complete */ then = time_uptime; do { if (time_uptime > (then + AAC_IMMEDIATE_TIMEOUT)) { fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out"); return(EIO); } } while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND)); /* clear the completion flag */ AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND); /* get the command status */ status = AAC_GET_MAILBOX(sc, 0); if (sp != NULL) *sp = status; if (status != AAC_SRB_STS_SUCCESS) return (-1); return(0); } int aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate, struct aac_fib *fib, u_int16_t datasize) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_assert(&sc->aac_io_lock, MA_OWNED); if (datasize > AAC_FIB_DATASIZE) return(EINVAL); /* * Set up the sync FIB */ fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY; fib->Header.XferState |= xferstate; fib->Header.Command = command; fib->Header.StructType = AAC_FIBTYPE_TFIB; fib->Header.Size = sizeof(struct aac_fib_header) + datasize; fib->Header.SenderSize = sizeof(struct aac_fib); fib->Header.SenderFibAddress = 0; /* Not needed */ fib->Header.ReceiverFibAddress = sc->aac_common_busaddr + offsetof(struct aac_common, ac_sync_fib); /* * Give the FIB to the controller, wait for a response. */ if (aac_sync_command(sc, AAC_MONKER_SYNCFIB, fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) { fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error"); return(EIO); } return (0); } /* * Adapter-space FIB queue manipulation * * Note that the queue implementation here is a little funky; neither the PI or * CI will ever be zero. This behaviour is a controller feature. */ static struct { int size; int notify; } aac_qinfo[] = { {AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL}, {AAC_HOST_HIGH_CMD_ENTRIES, 0}, {AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY}, {AAC_ADAP_HIGH_CMD_ENTRIES, 0}, {AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL}, {AAC_HOST_HIGH_RESP_ENTRIES, 0}, {AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY}, {AAC_ADAP_HIGH_RESP_ENTRIES, 0} }; /* * Atomically insert an entry into the nominated queue, returns 0 on success or * EBUSY if the queue is full. * * Note: it would be more efficient to defer notifying the controller in * the case where we may be inserting several entries in rapid succession, * but implementing this usefully may be difficult (it would involve a * separate queue/notify interface). */ static int aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm) { u_int32_t pi, ci; int error; u_int32_t fib_size; u_int32_t fib_addr; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); fib_size = cm->cm_fib->Header.Size; fib_addr = cm->cm_fib->Header.ReceiverFibAddress; /* get the producer/consumer indices */ pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; /* wrap the queue? */ if (pi >= aac_qinfo[queue].size) pi = 0; /* check for queue full */ if ((pi + 1) == ci) { error = EBUSY; goto out; } /* * To avoid a race with its completion interrupt, place this command on * the busy queue prior to advertising it to the controller. */ aac_enqueue_busy(cm); /* populate queue entry */ (sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size; (sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr; /* update producer index */ sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1; /* notify the adapter if we know how */ if (aac_qinfo[queue].notify != 0) AAC_QNOTIFY(sc, aac_qinfo[queue].notify); error = 0; out: return(error); } /* * Atomically remove one entry from the nominated queue, returns 0 on * success or ENOENT if the queue is empty. */ static int aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, struct aac_fib **fib_addr) { u_int32_t pi, ci; u_int32_t fib_index; int error; int notify; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* get the producer/consumer indices */ pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; /* check for queue empty */ if (ci == pi) { error = ENOENT; goto out; } /* wrap the pi so the following test works */ if (pi >= aac_qinfo[queue].size) pi = 0; notify = 0; if (ci == pi + 1) notify++; /* wrap the queue? */ if (ci >= aac_qinfo[queue].size) ci = 0; /* fetch the entry */ *fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size; switch (queue) { case AAC_HOST_NORM_CMD_QUEUE: case AAC_HOST_HIGH_CMD_QUEUE: /* * The aq_fib_addr is only 32 bits wide so it can't be counted * on to hold an address. For AIF's, the adapter assumes * that it's giving us an address into the array of AIF fibs. * Therefore, we have to convert it to an index. */ fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr / sizeof(struct aac_fib); *fib_addr = &sc->aac_common->ac_fibs[fib_index]; break; case AAC_HOST_NORM_RESP_QUEUE: case AAC_HOST_HIGH_RESP_QUEUE: { struct aac_command *cm; /* * As above, an index is used instead of an actual address. * Gotta shift the index to account for the fast response * bit. No other correction is needed since this value was * originally provided by the driver via the SenderFibAddress * field. */ fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr; cm = sc->aac_commands + (fib_index >> 2); *fib_addr = cm->cm_fib; /* * Is this a fast response? If it is, update the fib fields in * local memory since the whole fib isn't DMA'd back up. */ if (fib_index & 0x01) { (*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP; *((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL; } break; } default: panic("Invalid queue in aac_dequeue_fib()"); break; } /* update consumer index */ sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1; /* if we have made the queue un-full, notify the adapter */ if (notify && (aac_qinfo[queue].notify != 0)) AAC_QNOTIFY(sc, aac_qinfo[queue].notify); error = 0; out: return(error); } /* * Put our response to an Adapter Initialed Fib on the response queue */ static int aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib) { u_int32_t pi, ci; int error; u_int32_t fib_size; u_int32_t fib_addr; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* Tell the adapter where the FIB is */ fib_size = fib->Header.Size; fib_addr = fib->Header.SenderFibAddress; fib->Header.ReceiverFibAddress = fib_addr; /* get the producer/consumer indices */ pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; /* wrap the queue? */ if (pi >= aac_qinfo[queue].size) pi = 0; /* check for queue full */ if ((pi + 1) == ci) { error = EBUSY; goto out; } /* populate queue entry */ (sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size; (sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr; /* update producer index */ sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1; /* notify the adapter if we know how */ if (aac_qinfo[queue].notify != 0) AAC_QNOTIFY(sc, aac_qinfo[queue].notify); error = 0; out: return(error); } /* * Check for commands that have been outstanding for a suspiciously long time, * and complain about them. */ static void aac_timeout(struct aac_softc *sc) { struct aac_command *cm; time_t deadline; int timedout, code; /* * Traverse the busy command list, bitch about late commands once * only. */ timedout = 0; deadline = time_uptime - AAC_CMD_TIMEOUT; TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) { if ((cm->cm_timestamp < deadline) /* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) { cm->cm_flags |= AAC_CMD_TIMEDOUT; device_printf(sc->aac_dev, "COMMAND %p (TYPE %d) TIMEOUT AFTER %d SECONDS\n", cm, cm->cm_fib->Header.Command, (int)(time_uptime-cm->cm_timestamp)); AAC_PRINT_FIB(sc, cm->cm_fib); timedout++; } } if (timedout) { code = AAC_GET_FWSTATUS(sc); if (code != AAC_UP_AND_RUNNING) { device_printf(sc->aac_dev, "WARNING! Controller is no " "longer running! code= 0x%x\n", code); } } return; } /* * Interface Function Vectors */ /* * Read the current firmware status word. */ static int aac_sa_get_fwstatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, AAC_SA_FWSTATUS)); } static int aac_rx_get_fwstatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ? AAC_RX_OMR0 : AAC_RX_FWSTATUS)); } static int aac_rkt_get_fwstatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ? AAC_RKT_OMR0 : AAC_RKT_FWSTATUS)); } /* * Notify the controller of a change in a given queue */ static void aac_sa_qnotify(struct aac_softc *sc, int qbit) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit); } static void aac_rx_qnotify(struct aac_softc *sc, int qbit) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG4(sc, AAC_RX_IDBR, qbit); } static void aac_rkt_qnotify(struct aac_softc *sc, int qbit) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG4(sc, AAC_RKT_IDBR, qbit); } /* * Get the interrupt reason bits */ static int aac_sa_get_istatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG2(sc, AAC_SA_DOORBELL0)); } static int aac_rx_get_istatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, AAC_RX_ODBR)); } static int aac_rkt_get_istatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, AAC_RKT_ODBR)); } /* * Clear some interrupt reason bits */ static void aac_sa_clear_istatus(struct aac_softc *sc, int mask) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask); } static void aac_rx_clear_istatus(struct aac_softc *sc, int mask) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, mask); } static void aac_rkt_clear_istatus(struct aac_softc *sc, int mask) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, mask); } /* * Populate the mailbox and set the command word */ static void aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX, command); AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0); AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1); AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2); AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3); } static void aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX, command); AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0); AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1); AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2); AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3); } static void aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX, command); AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 4, arg0); AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 8, arg1); AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 12, arg2); AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 16, arg3); } /* * Fetch the immediate command status word */ static int aac_sa_get_mailbox(struct aac_softc *sc, int mb) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM1_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4))); } static int aac_rx_get_mailbox(struct aac_softc *sc, int mb) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM1_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4))); } static int aac_rkt_get_mailbox(struct aac_softc *sc, int mb) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM1_GETREG4(sc, AAC_RKT_MAILBOX + (mb * 4))); } /* * Set/clear interrupt masks */ static void aac_sa_set_interrupts(struct aac_softc *sc, int enable) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis"); if (enable) { AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS); } else { AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_SET, ~0); } } static void aac_rx_set_interrupts(struct aac_softc *sc, int enable) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis"); if (enable) { if (sc->flags & AAC_FLAGS_NEW_COMM) AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INT_NEW_COMM); else AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS); } else { AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~0); } } static void aac_rkt_set_interrupts(struct aac_softc *sc, int enable) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis"); if (enable) { if (sc->flags & AAC_FLAGS_NEW_COMM) AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INT_NEW_COMM); else AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INTERRUPTS); } else { AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~0); } } /* * New comm. interface: Send command functions */ static int aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm) { u_int32_t index, device; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)"); index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE); if (index == 0xffffffffL) index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE); if (index == 0xffffffffL) return index; aac_enqueue_busy(cm); device = index; AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL)); device += 4; AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32)); device += 4; AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size); AAC_MEM0_SETREG4(sc, AAC_RX_IQUE, index); return 0; } static int aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm) { u_int32_t index, device; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)"); index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE); if (index == 0xffffffffL) index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE); if (index == 0xffffffffL) return index; aac_enqueue_busy(cm); device = index; AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL)); device += 4; AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32)); device += 4; AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size); AAC_MEM0_SETREG4(sc, AAC_RKT_IQUE, index); return 0; } /* * New comm. interface: get, set outbound queue index */ static int aac_rx_get_outb_queue(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, AAC_RX_OQUE)); } static int aac_rkt_get_outb_queue(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return(AAC_MEM0_GETREG4(sc, AAC_RKT_OQUE)); } static void aac_rx_set_outb_queue(struct aac_softc *sc, int index) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG4(sc, AAC_RX_OQUE, index); } static void aac_rkt_set_outb_queue(struct aac_softc *sc, int index) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); AAC_MEM0_SETREG4(sc, AAC_RKT_OQUE, index); } /* * Debugging and Diagnostics */ /* * Print some information about the controller. */ static void aac_describe_controller(struct aac_softc *sc) { struct aac_fib *fib; struct aac_adapter_info *info; char *adapter_type = "Adaptec RAID controller"; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_lock(&sc->aac_io_lock); aac_alloc_sync_fib(sc, &fib); fib->data[0] = 0; if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) { device_printf(sc->aac_dev, "RequestAdapterInfo failed\n"); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); return; } /* save the kernel revision structure for later use */ info = (struct aac_adapter_info *)&fib->data[0]; sc->aac_revision = info->KernelRevision; if (bootverbose) { device_printf(sc->aac_dev, "%s %dMHz, %dMB memory " "(%dMB cache, %dMB execution), %s\n", aac_describe_code(aac_cpu_variant, info->CpuVariant), info->ClockSpeed, info->TotalMem / (1024 * 1024), info->BufferMem / (1024 * 1024), info->ExecutionMem / (1024 * 1024), aac_describe_code(aac_battery_platform, info->batteryPlatform)); device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n", info->KernelRevision.external.comp.major, info->KernelRevision.external.comp.minor, info->KernelRevision.external.comp.dash, info->KernelRevision.buildNumber, (u_int32_t)(info->SerialNumber & 0xffffff)); device_printf(sc->aac_dev, "Supported Options=%b\n", sc->supported_options, "\20" "\1SNAPSHOT" "\2CLUSTERS" "\3WCACHE" "\4DATA64" "\5HOSTTIME" "\6RAID50" "\7WINDOW4GB" "\10SCSIUPGD" "\11SOFTERR" "\12NORECOND" "\13SGMAP64" "\14ALARM" "\15NONDASD" "\16SCSIMGT" "\17RAIDSCSI" "\21ADPTINFO" "\22NEWCOMM" "\23ARRAY64BIT" "\24HEATSENSOR"); } if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) { fib->data[0] = 0; if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1)) device_printf(sc->aac_dev, "RequestSupplementAdapterInfo failed\n"); else adapter_type = ((struct aac_supplement_adapter_info *) &fib->data[0])->AdapterTypeText; } device_printf(sc->aac_dev, "%s, aac driver %d.%d.%d-%d\n", adapter_type, AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION, AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); } /* * Look up a text description of a numeric error code and return a pointer to * same. */ static char * aac_describe_code(struct aac_code_lookup *table, u_int32_t code) { int i; for (i = 0; table[i].string != NULL; i++) if (table[i].code == code) return(table[i].string); return(table[i + 1].string); } /* * Management Interface */ static int aac_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct aac_softc *sc; sc = dev->si_drv1; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); sc->aac_open_cnt++; sc->aac_state |= AAC_STATE_OPEN; return 0; } static int aac_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct aac_softc *sc; sc = dev->si_drv1; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); sc->aac_open_cnt--; /* Mark this unit as no longer open */ if (sc->aac_open_cnt == 0) sc->aac_state &= ~AAC_STATE_OPEN; return 0; } static int aac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { union aac_statrequest *as; struct aac_softc *sc; int error = 0; as = (union aac_statrequest *)arg; sc = dev->si_drv1; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); switch (cmd) { case AACIO_STATS: switch (as->as_item) { case AACQ_FREE: case AACQ_BIO: case AACQ_READY: case AACQ_BUSY: bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat, sizeof(struct aac_qstat)); break; default: error = ENOENT; break; } break; case FSACTL_SENDFIB: case FSACTL_SEND_LARGE_FIB: arg = *(caddr_t*)arg; case FSACTL_LNX_SENDFIB: case FSACTL_LNX_SEND_LARGE_FIB: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB"); error = aac_ioctl_sendfib(sc, arg); break; case FSACTL_SEND_RAW_SRB: arg = *(caddr_t*)arg; case FSACTL_LNX_SEND_RAW_SRB: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB"); error = aac_ioctl_send_raw_srb(sc, arg); break; case FSACTL_AIF_THREAD: case FSACTL_LNX_AIF_THREAD: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD"); error = EINVAL; break; case FSACTL_OPEN_GET_ADAPTER_FIB: arg = *(caddr_t*)arg; case FSACTL_LNX_OPEN_GET_ADAPTER_FIB: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB"); error = aac_open_aif(sc, arg); break; case FSACTL_GET_NEXT_ADAPTER_FIB: arg = *(caddr_t*)arg; case FSACTL_LNX_GET_NEXT_ADAPTER_FIB: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB"); error = aac_getnext_aif(sc, arg); break; case FSACTL_CLOSE_GET_ADAPTER_FIB: arg = *(caddr_t*)arg; case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB"); error = aac_close_aif(sc, arg); break; case FSACTL_MINIPORT_REV_CHECK: arg = *(caddr_t*)arg; case FSACTL_LNX_MINIPORT_REV_CHECK: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK"); error = aac_rev_check(sc, arg); break; case FSACTL_QUERY_DISK: arg = *(caddr_t*)arg; case FSACTL_LNX_QUERY_DISK: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK"); error = aac_query_disk(sc, arg); break; case FSACTL_DELETE_DISK: case FSACTL_LNX_DELETE_DISK: /* * We don't trust the underland to tell us when to delete a * container, rather we rely on an AIF coming from the * controller */ error = 0; break; case FSACTL_GET_PCI_INFO: arg = *(caddr_t*)arg; case FSACTL_LNX_GET_PCI_INFO: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO"); error = aac_get_pci_info(sc, arg); break; case FSACTL_GET_FEATURES: arg = *(caddr_t*)arg; case FSACTL_LNX_GET_FEATURES: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_FEATURES"); error = aac_supported_features(sc, arg); break; default: fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd); error = EINVAL; break; } return(error); } static int aac_poll(struct cdev *dev, int poll_events, struct thread *td) { struct aac_softc *sc; struct aac_fib_context *ctx; int revents; sc = dev->si_drv1; revents = 0; mtx_lock(&sc->aac_aifq_lock); if ((poll_events & (POLLRDNORM | POLLIN)) != 0) { for (ctx = sc->fibctx; ctx; ctx = ctx->next) { if (ctx->ctx_idx != sc->aifq_idx || ctx->ctx_wrap) { revents |= poll_events & (POLLIN | POLLRDNORM); break; } } } mtx_unlock(&sc->aac_aifq_lock); if (revents == 0) { if (poll_events & (POLLIN | POLLRDNORM)) selrecord(td, &sc->rcv_select); } return (revents); } static void aac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg) { switch (event->ev_type) { case AAC_EVENT_CMFREE: mtx_assert(&sc->aac_io_lock, MA_OWNED); if (aac_alloc_command(sc, (struct aac_command **)arg)) { aac_add_event(sc, event); return; } free(event, M_AACBUF); wakeup(arg); break; default: break; } } /* * Send a FIB supplied from userspace */ static int aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib) { struct aac_command *cm; int size, error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); cm = NULL; /* * Get a command */ mtx_lock(&sc->aac_io_lock); if (aac_alloc_command(sc, &cm)) { struct aac_event *event; event = malloc(sizeof(struct aac_event), M_AACBUF, M_NOWAIT | M_ZERO); if (event == NULL) { error = EBUSY; mtx_unlock(&sc->aac_io_lock); goto out; } event->ev_type = AAC_EVENT_CMFREE; event->ev_callback = aac_ioctl_event; event->ev_arg = &cm; aac_add_event(sc, event); msleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0); } mtx_unlock(&sc->aac_io_lock); /* * Fetch the FIB header, then re-copy to get data as well. */ if ((error = copyin(ufib, cm->cm_fib, sizeof(struct aac_fib_header))) != 0) goto out; size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header); if (size > sc->aac_max_fib_size) { device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n", size, sc->aac_max_fib_size); size = sc->aac_max_fib_size; } if ((error = copyin(ufib, cm->cm_fib, size)) != 0) goto out; cm->cm_fib->Header.Size = size; cm->cm_timestamp = time_uptime; /* * Pass the FIB to the controller, wait for it to complete. */ mtx_lock(&sc->aac_io_lock); error = aac_wait_command(cm); mtx_unlock(&sc->aac_io_lock); if (error != 0) { device_printf(sc->aac_dev, "aac_wait_command return %d\n", error); goto out; } /* * Copy the FIB and data back out to the caller. */ size = cm->cm_fib->Header.Size; if (size > sc->aac_max_fib_size) { device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n", size, sc->aac_max_fib_size); size = sc->aac_max_fib_size; } error = copyout(cm->cm_fib, ufib, size); out: if (cm != NULL) { mtx_lock(&sc->aac_io_lock); aac_release_command(cm); mtx_unlock(&sc->aac_io_lock); } return(error); } /* * Send a passthrough FIB supplied from userspace */ static int aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg) { return (EINVAL); } /* * Handle an AIF sent to us by the controller; queue it for later reference. * If the queue fills up, then drop the older entries. */ static void aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib) { struct aac_aif_command *aif; struct aac_container *co, *co_next; struct aac_fib_context *ctx; struct aac_mntinforesp *mir; int next, current, found; int count = 0, added = 0, i = 0; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); aif = (struct aac_aif_command*)&fib->data[0]; aac_print_aif(sc, aif); /* Is it an event that we should care about? */ switch (aif->command) { case AifCmdEventNotify: switch (aif->data.EN.type) { case AifEnAddContainer: case AifEnDeleteContainer: /* * A container was added or deleted, but the message * doesn't tell us anything else! Re-enumerate the * containers and sort things out. */ aac_alloc_sync_fib(sc, &fib); do { /* * Ask the controller for its containers one at * a time. * XXX What if the controller's list changes * midway through this enumaration? * XXX This should be done async. */ if ((mir = aac_get_container_info(sc, fib, i)) == NULL) continue; if (i == 0) count = mir->MntRespCount; /* * Check the container against our list. * co->co_found was already set to 0 in a * previous run. */ if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) { found = 0; TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) { if (co->co_mntobj.ObjectId == mir->MntTable[0].ObjectId) { co->co_found = 1; found = 1; break; } } /* * If the container matched, continue * in the list. */ if (found) { i++; continue; } /* * This is a new container. Do all the * appropriate things to set it up. */ aac_add_container(sc, mir, 1); added = 1; } i++; } while ((i < count) && (i < AAC_MAX_CONTAINERS)); aac_release_sync_fib(sc); /* * Go through our list of containers and see which ones * were not marked 'found'. Since the controller didn't * list them they must have been deleted. Do the * appropriate steps to destroy the device. Also reset * the co->co_found field. */ co = TAILQ_FIRST(&sc->aac_container_tqh); while (co != NULL) { if (co->co_found == 0) { mtx_unlock(&sc->aac_io_lock); mtx_lock(&Giant); device_delete_child(sc->aac_dev, co->co_disk); mtx_unlock(&Giant); mtx_lock(&sc->aac_io_lock); co_next = TAILQ_NEXT(co, co_link); mtx_lock(&sc->aac_container_lock); TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link); mtx_unlock(&sc->aac_container_lock); free(co, M_AACBUF); co = co_next; } else { co->co_found = 0; co = TAILQ_NEXT(co, co_link); } } /* Attach the newly created containers */ if (added) { mtx_unlock(&sc->aac_io_lock); mtx_lock(&Giant); bus_generic_attach(sc->aac_dev); mtx_unlock(&Giant); mtx_lock(&sc->aac_io_lock); } break; default: break; } default: break; } /* Copy the AIF data to the AIF queue for ioctl retrieval */ mtx_lock(&sc->aac_aifq_lock); current = sc->aifq_idx; next = (current + 1) % AAC_AIFQ_LENGTH; if (next == 0) sc->aifq_filled = 1; bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib)); /* modify AIF contexts */ if (sc->aifq_filled) { for (ctx = sc->fibctx; ctx; ctx = ctx->next) { if (next == ctx->ctx_idx) ctx->ctx_wrap = 1; else if (current == ctx->ctx_idx && ctx->ctx_wrap) ctx->ctx_idx = next; } } sc->aifq_idx = next; /* On the off chance that someone is sleeping for an aif... */ if (sc->aac_state & AAC_STATE_AIF_SLEEPER) wakeup(sc->aac_aifq); /* Wakeup any poll()ers */ selwakeuppri(&sc->rcv_select, PRIBIO); mtx_unlock(&sc->aac_aifq_lock); return; } /* * Return the Revision of the driver to userspace and check to see if the * userspace app is possibly compatible. This is extremely bogus since * our driver doesn't follow Adaptec's versioning system. Cheat by just * returning what the card reported. */ static int aac_rev_check(struct aac_softc *sc, caddr_t udata) { struct aac_rev_check rev_check; struct aac_rev_check_resp rev_check_resp; int error = 0; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* * Copyin the revision struct from userspace */ if ((error = copyin(udata, (caddr_t)&rev_check, sizeof(struct aac_rev_check))) != 0) { return error; } fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n", rev_check.callingRevision.buildNumber); /* * Doctor up the response struct. */ rev_check_resp.possiblyCompatible = 1; rev_check_resp.adapterSWRevision.external.comp.major = AAC_DRIVER_MAJOR_VERSION; rev_check_resp.adapterSWRevision.external.comp.minor = AAC_DRIVER_MINOR_VERSION; rev_check_resp.adapterSWRevision.external.comp.type = AAC_DRIVER_TYPE; rev_check_resp.adapterSWRevision.external.comp.dash = AAC_DRIVER_BUGFIX_LEVEL; rev_check_resp.adapterSWRevision.buildNumber = AAC_DRIVER_BUILD; return(copyout((caddr_t)&rev_check_resp, udata, sizeof(struct aac_rev_check_resp))); } /* * Pass the fib context to the caller */ static int aac_open_aif(struct aac_softc *sc, caddr_t arg) { struct aac_fib_context *fibctx, *ctx; int error = 0; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); fibctx = malloc(sizeof(struct aac_fib_context), M_AACBUF, M_NOWAIT|M_ZERO); if (fibctx == NULL) return (ENOMEM); mtx_lock(&sc->aac_aifq_lock); /* all elements are already 0, add to queue */ if (sc->fibctx == NULL) sc->fibctx = fibctx; else { for (ctx = sc->fibctx; ctx->next; ctx = ctx->next) ; ctx->next = fibctx; fibctx->prev = ctx; } /* evaluate unique value */ fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff); ctx = sc->fibctx; while (ctx != fibctx) { if (ctx->unique == fibctx->unique) { fibctx->unique++; ctx = sc->fibctx; } else { ctx = ctx->next; } } mtx_unlock(&sc->aac_aifq_lock); error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t)); if (error) aac_close_aif(sc, (caddr_t)ctx); return error; } /* * Close the caller's fib context */ static int aac_close_aif(struct aac_softc *sc, caddr_t arg) { struct aac_fib_context *ctx; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_lock(&sc->aac_aifq_lock); for (ctx = sc->fibctx; ctx; ctx = ctx->next) { if (ctx->unique == *(uint32_t *)&arg) { if (ctx == sc->fibctx) sc->fibctx = NULL; else { ctx->prev->next = ctx->next; if (ctx->next) ctx->next->prev = ctx->prev; } break; } } mtx_unlock(&sc->aac_aifq_lock); if (ctx) free(ctx, M_AACBUF); return 0; } /* * Pass the caller the next AIF in their queue */ static int aac_getnext_aif(struct aac_softc *sc, caddr_t arg) { struct get_adapter_fib_ioctl agf; struct aac_fib_context *ctx; int error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if ((error = copyin(arg, &agf, sizeof(agf))) == 0) { for (ctx = sc->fibctx; ctx; ctx = ctx->next) { if (agf.AdapterFibContext == ctx->unique) break; } if (!ctx) return (EFAULT); error = aac_return_aif(sc, ctx, agf.AifFib); if (error == EAGAIN && agf.Wait) { fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF"); sc->aac_state |= AAC_STATE_AIF_SLEEPER; while (error == EAGAIN) { error = tsleep(sc->aac_aifq, PRIBIO | PCATCH, "aacaif", 0); if (error == 0) error = aac_return_aif(sc, ctx, agf.AifFib); } sc->aac_state &= ~AAC_STATE_AIF_SLEEPER; } } return(error); } /* * Hand the next AIF off the top of the queue out to userspace. */ static int aac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr) { int current, error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); mtx_lock(&sc->aac_aifq_lock); current = ctx->ctx_idx; if (current == sc->aifq_idx && !ctx->ctx_wrap) { /* empty */ mtx_unlock(&sc->aac_aifq_lock); return (EAGAIN); } error = copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib)); if (error) device_printf(sc->aac_dev, "aac_return_aif: copyout returned %d\n", error); else { ctx->ctx_wrap = 0; ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH; } mtx_unlock(&sc->aac_aifq_lock); return(error); } static int aac_get_pci_info(struct aac_softc *sc, caddr_t uptr) { struct aac_pci_info { u_int32_t bus; u_int32_t slot; } pciinf; int error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); pciinf.bus = pci_get_bus(sc->aac_dev); pciinf.slot = pci_get_slot(sc->aac_dev); error = copyout((caddr_t)&pciinf, uptr, sizeof(struct aac_pci_info)); return (error); } static int aac_supported_features(struct aac_softc *sc, caddr_t uptr) { struct aac_features f; int error; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if ((error = copyin(uptr, &f, sizeof (f))) != 0) return (error); /* * When the management driver receives FSACTL_GET_FEATURES ioctl with * ALL zero in the featuresState, the driver will return the current * state of all the supported features, the data field will not be * valid. * When the management driver receives FSACTL_GET_FEATURES ioctl with * a specific bit set in the featuresState, the driver will return the * current state of this specific feature and whatever data that are * associated with the feature in the data field or perform whatever * action needed indicates in the data field. */ if (f.feat.fValue == 0) { f.feat.fBits.largeLBA = (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0; /* TODO: In the future, add other features state here as well */ } else { if (f.feat.fBits.largeLBA) f.feat.fBits.largeLBA = (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0; /* TODO: Add other features state and data in the future */ } error = copyout(&f, uptr, sizeof (f)); return (error); } /* * Give the userland some information about the container. The AAC arch * expects the driver to be a SCSI passthrough type driver, so it expects * the containers to have b:t:l numbers. Fake it. */ static int aac_query_disk(struct aac_softc *sc, caddr_t uptr) { struct aac_query_disk query_disk; struct aac_container *co; struct aac_disk *disk; int error, id; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); disk = NULL; error = copyin(uptr, (caddr_t)&query_disk, sizeof(struct aac_query_disk)); if (error) return (error); id = query_disk.ContainerNumber; if (id == -1) return (EINVAL); mtx_lock(&sc->aac_container_lock); TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) { if (co->co_mntobj.ObjectId == id) break; } if (co == NULL) { query_disk.Valid = 0; query_disk.Locked = 0; query_disk.Deleted = 1; /* XXX is this right? */ } else { disk = device_get_softc(co->co_disk); query_disk.Valid = 1; query_disk.Locked = (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0; query_disk.Deleted = 0; query_disk.Bus = device_get_unit(sc->aac_dev); query_disk.Target = disk->unit; query_disk.Lun = 0; query_disk.UnMapped = 0; sprintf(&query_disk.diskDeviceName[0], "%s%d", disk->ad_disk->d_name, disk->ad_disk->d_unit); } mtx_unlock(&sc->aac_container_lock); error = copyout((caddr_t)&query_disk, uptr, sizeof(struct aac_query_disk)); return (error); } static void aac_get_bus_info(struct aac_softc *sc) { struct aac_fib *fib; struct aac_ctcfg *c_cmd; struct aac_ctcfg_resp *c_resp; struct aac_vmioctl *vmi; struct aac_vmi_businf_resp *vmi_resp; struct aac_getbusinf businfo; struct aac_sim *caminf; device_t child; int i, found, error; mtx_lock(&sc->aac_io_lock); aac_alloc_sync_fib(sc, &fib); c_cmd = (struct aac_ctcfg *)&fib->data[0]; bzero(c_cmd, sizeof(struct aac_ctcfg)); c_cmd->Command = VM_ContainerConfig; c_cmd->cmd = CT_GET_SCSI_METHOD; c_cmd->param = 0; error = aac_sync_fib(sc, ContainerCommand, 0, fib, sizeof(struct aac_ctcfg)); if (error) { device_printf(sc->aac_dev, "Error %d sending " "VM_ContainerConfig command\n", error); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); return; } c_resp = (struct aac_ctcfg_resp *)&fib->data[0]; if (c_resp->Status != ST_OK) { device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n", c_resp->Status); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); return; } sc->scsi_method_id = c_resp->param; vmi = (struct aac_vmioctl *)&fib->data[0]; bzero(vmi, sizeof(struct aac_vmioctl)); vmi->Command = VM_Ioctl; vmi->ObjType = FT_DRIVE; vmi->MethId = sc->scsi_method_id; vmi->ObjId = 0; vmi->IoctlCmd = GetBusInfo; error = aac_sync_fib(sc, ContainerCommand, 0, fib, sizeof(struct aac_vmi_businf_resp)); if (error) { device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n", error); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); return; } vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0]; if (vmi_resp->Status != ST_OK) { device_printf(sc->aac_dev, "VM_Ioctl returned %d\n", vmi_resp->Status); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); return; } bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf)); aac_release_sync_fib(sc); mtx_unlock(&sc->aac_io_lock); found = 0; for (i = 0; i < businfo.BusCount; i++) { if (businfo.BusValid[i] != AAC_BUS_VALID) continue; caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim), M_AACBUF, M_NOWAIT | M_ZERO); if (caminf == NULL) { device_printf(sc->aac_dev, "No memory to add passthrough bus %d\n", i); break; }; child = device_add_child(sc->aac_dev, "aacp", -1); if (child == NULL) { device_printf(sc->aac_dev, "device_add_child failed for passthrough bus %d\n", i); free(caminf, M_AACBUF); break; } caminf->TargetsPerBus = businfo.TargetsPerBus; caminf->BusNumber = i; caminf->InitiatorBusId = businfo.InitiatorBusId[i]; caminf->aac_sc = sc; caminf->sim_dev = child; device_set_ivars(child, caminf); device_set_desc(child, "SCSI Passthrough Bus"); TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link); found = 1; } if (found) bus_generic_attach(sc->aac_dev); return; } Index: projects/ppc64/sys/dev/aac/aac_cam.c =================================================================== --- projects/ppc64/sys/dev/aac/aac_cam.c (revision 204271) +++ projects/ppc64/sys/dev/aac/aac_cam.c (revision 204272) @@ -1,615 +1,614 @@ /*- * Copyright (c) 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. * 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$"); /* * CAM front-end for communicating with non-DASD devices */ #include "opt_aac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct aac_cam { device_t dev; struct aac_sim *inf; struct cam_sim *sim; struct cam_path *path; }; static int aac_cam_probe(device_t dev); static int aac_cam_attach(device_t dev); static int aac_cam_detach(device_t dev); static void aac_cam_action(struct cam_sim *, union ccb *); static void aac_cam_poll(struct cam_sim *); static void aac_cam_complete(struct aac_command *); static u_int32_t aac_cam_reset_bus(struct cam_sim *, union ccb *); static u_int32_t aac_cam_abort_ccb(struct cam_sim *, union ccb *); static u_int32_t aac_cam_term_io(struct cam_sim *, union ccb *); static devclass_t aac_pass_devclass; static device_method_t aac_pass_methods[] = { DEVMETHOD(device_probe, aac_cam_probe), DEVMETHOD(device_attach, aac_cam_attach), DEVMETHOD(device_detach, aac_cam_detach), { 0, 0 } }; static driver_t aac_pass_driver = { "aacp", aac_pass_methods, sizeof(struct aac_cam) }; DRIVER_MODULE(aacp, aac, aac_pass_driver, aac_pass_devclass, 0, 0); MODULE_DEPEND(aacp, cam, 1, 1, 1); MALLOC_DEFINE(M_AACCAM, "aaccam", "AAC CAM info"); static void aac_cam_event(struct aac_softc *sc, struct aac_event *event, void *arg) { union ccb *ccb; struct aac_cam *camsc; switch (event->ev_type) { case AAC_EVENT_CMFREE: ccb = arg; camsc = ccb->ccb_h.sim_priv.entries[0].ptr; free(event, M_AACCAM); xpt_release_simq(camsc->sim, 1); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); break; default: device_printf(sc->aac_dev, "unknown event %d in aac_cam\n", event->ev_type); break; } return; } static int aac_cam_probe(device_t dev) { fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); return (0); } static int aac_cam_detach(device_t dev) { struct aac_softc *sc; struct aac_cam *camsc; fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); camsc = (struct aac_cam *)device_get_softc(dev); sc = camsc->inf->aac_sc; mtx_lock(&sc->aac_io_lock); xpt_async(AC_LOST_DEVICE, camsc->path, NULL); xpt_free_path(camsc->path); xpt_bus_deregister(cam_sim_path(camsc->sim)); cam_sim_free(camsc->sim, /*free_devq*/TRUE); mtx_unlock(&sc->aac_io_lock); return (0); } /* * Register the driver as a CAM SIM */ static int aac_cam_attach(device_t dev) { struct cam_devq *devq; struct cam_sim *sim; struct cam_path *path; struct aac_cam *camsc; struct aac_sim *inf; fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); camsc = (struct aac_cam *)device_get_softc(dev); inf = (struct aac_sim *)device_get_ivars(dev); camsc->inf = inf; devq = cam_simq_alloc(inf->TargetsPerBus); if (devq == NULL) return (EIO); sim = cam_sim_alloc(aac_cam_action, aac_cam_poll, "aacp", camsc, device_get_unit(dev), &inf->aac_sc->aac_io_lock, 1, 1, devq); if (sim == NULL) { cam_simq_free(devq); return (EIO); } /* Since every bus has it's own sim, every bus 'appears' as bus 0 */ mtx_lock(&inf->aac_sc->aac_io_lock); if (xpt_bus_register(sim, dev, 0) != CAM_SUCCESS) { cam_sim_free(sim, TRUE); mtx_unlock(&inf->aac_sc->aac_io_lock); return (EIO); } if (xpt_create_path(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); mtx_unlock(&inf->aac_sc->aac_io_lock); return (EIO); } mtx_unlock(&inf->aac_sc->aac_io_lock); camsc->sim = sim; camsc->path = path; return (0); } static void aac_cam_action(struct cam_sim *sim, union ccb *ccb) { struct aac_cam *camsc; struct aac_softc *sc; struct aac_srb *srb; struct aac_fib *fib; struct aac_command *cm; camsc = (struct aac_cam *)cam_sim_softc(sim); sc = camsc->inf->aac_sc; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); /* Synchronous ops, and ops that don't require communication with the * controller */ switch(ccb->ccb_h.func_code) { case XPT_SCSI_IO: case XPT_RESET_DEV: /* These are handled down below */ break; case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg = &ccb->ccg; size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb >= (2 * 1024)) { /* 2GB */ ccg->heads = 255; ccg->secs_per_track = 63; } else if (size_mb >= (1 * 1024)) { /* 1GB */ ccg->heads = 128; ccg->secs_per_track = 32; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_WIDE_16; cpi->target_sprt = 0; /* * Resetting via the passthrough or parallel bus scan * causes problems. */ cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN; cpi->hba_eng_cnt = 0; cpi->max_target = camsc->inf->TargetsPerBus; cpi->max_lun = 8; /* Per the controller spec */ cpi->initiator_id = camsc->inf->InitiatorBusId; cpi->bus_id = camsc->inf->BusNumber; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings_scsi *scsi = &ccb->cts.proto_specific.scsi; struct ccb_trans_settings_spi *spi = &ccb->cts.xport_specific.spi; ccb->cts.protocol = PROTO_SCSI; ccb->cts.protocol_version = SCSI_REV_2; ccb->cts.transport = XPORT_SPI; ccb->cts.transport_version = 2; if (ccb->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; spi->valid |= CTS_SPI_VALID_DISC; } else { scsi->valid = 0; } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); return; case XPT_RESET_BUS: if (!(sc->flags & AAC_FLAGS_CAM_NORESET)) { ccb->ccb_h.status = aac_cam_reset_bus(sim, ccb); } else { ccb->ccb_h.status = CAM_REQ_CMP; } xpt_done(ccb); return; case XPT_ABORT: ccb->ccb_h.status = aac_cam_abort_ccb(sim, ccb); xpt_done(ccb); return; case XPT_TERM_IO: ccb->ccb_h.status = aac_cam_term_io(sim, ccb); xpt_done(ccb); return; default: device_printf(sc->aac_dev, "Unsupported command 0x%x\n", ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(ccb); return; } /* Async ops that require communcation with the controller */ if (aac_alloc_command(sc, &cm)) { struct aac_event *event; xpt_freeze_simq(sim, 1); ccb->ccb_h.status = CAM_RESRC_UNAVAIL; ccb->ccb_h.sim_priv.entries[0].ptr = camsc; event = malloc(sizeof(struct aac_event), M_AACCAM, M_NOWAIT | M_ZERO); if (event == NULL) { device_printf(sc->aac_dev, "Warning, out of memory for event\n"); return; } event->ev_callback = aac_cam_event; event->ev_arg = ccb; event->ev_type = AAC_EVENT_CMFREE; aac_add_event(sc, event); return; } fib = cm->cm_fib; srb = (struct aac_srb *)&fib->data[0]; cm->cm_datalen = 0; switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: srb->flags = AAC_SRB_FLAGS_DATA_IN; cm->cm_flags |= AAC_CMD_DATAIN; break; case CAM_DIR_OUT: srb->flags = AAC_SRB_FLAGS_DATA_OUT; cm->cm_flags |= AAC_CMD_DATAOUT; break; case CAM_DIR_NONE: srb->flags = AAC_SRB_FLAGS_NO_DATA_XFER; break; default: srb->flags = AAC_SRB_FLAGS_UNSPECIFIED_DIRECTION; cm->cm_flags |= AAC_CMD_DATAIN | AAC_CMD_DATAOUT; break; } switch(ccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct ccb_scsiio *csio = &ccb->csio; srb->function = AAC_SRB_FUNC_EXECUTE_SCSI; /* * Copy the CDB into the SRB. It's only 6-16 bytes, * so a copy is not too expensive. */ srb->cdb_len = csio->cdb_len; if (ccb->ccb_h.flags & CAM_CDB_POINTER) bcopy(csio->cdb_io.cdb_ptr, (u_int8_t *)&srb->cdb[0], srb->cdb_len); else bcopy(csio->cdb_io.cdb_bytes, (u_int8_t *)&srb->cdb[0], srb->cdb_len); /* Set command */ fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) ? ScsiPortCommandU64 : ScsiPortCommand; /* Map the s/g list. XXX 32bit addresses only! */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { srb->data_len = csio->dxfer_len; if (ccb->ccb_h.flags & CAM_DATA_PHYS) { /* Send a 32bit command */ fib->Header.Command = ScsiPortCommand; srb->sg_map.SgCount = 1; srb->sg_map.SgEntry[0].SgAddress = (uint32_t)(uintptr_t)csio->data_ptr; srb->sg_map.SgEntry[0].SgByteCount = csio->dxfer_len; } else { /* * Arrange things so that the S/G * map will get set up automagically */ cm->cm_data = (void *)csio->data_ptr; cm->cm_datalen = csio->dxfer_len; cm->cm_sgtable = &srb->sg_map; } } else { /* XXX Need to handle multiple s/g elements */ panic("aac_cam: multiple s/g elements"); } } else { srb->sg_map.SgCount = 0; srb->sg_map.SgEntry[0].SgByteCount = 0; srb->data_len = 0; } break; } case XPT_RESET_DEV: if (!(sc->flags & AAC_FLAGS_CAM_NORESET)) { srb->function = AAC_SRB_FUNC_RESET_DEVICE; break; } else { ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } default: break; } srb->bus = camsc->inf->BusNumber; /* Bus number relative to the card */ srb->target = ccb->ccb_h.target_id; srb->lun = ccb->ccb_h.target_lun; srb->timeout = ccb->ccb_h.timeout; /* XXX */ srb->retry_limit = 0; cm->cm_complete = aac_cam_complete; cm->cm_private = ccb; cm->cm_timestamp = time_uptime; - cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE; fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM; fib->Header.Size = sizeof(struct aac_fib_header) + sizeof(struct aac_srb); aac_enqueue_ready(cm); aac_startio(cm->cm_sc); return; } static void aac_cam_poll(struct cam_sim *sim) { /* * Pinging the interrupt routine isn't very safe, nor is it * really necessary. Do nothing. */ } static void aac_cam_complete(struct aac_command *cm) { union ccb *ccb; struct aac_srb_response *srbr; struct aac_softc *sc; sc = cm->cm_sc; fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); ccb = cm->cm_private; srbr = (struct aac_srb_response *)&cm->cm_fib->data[0]; if (srbr->fib_status != 0) { device_printf(sc->aac_dev, "Passthru FIB failed!\n"); ccb->ccb_h.status = CAM_REQ_ABORTED; } else { /* * The SRB error codes just happen to match the CAM error * codes. How convienient! */ ccb->ccb_h.status = srbr->srb_status; /* Take care of SCSI_IO ops. */ if (ccb->ccb_h.func_code == XPT_SCSI_IO) { u_int8_t command, device; ccb->csio.scsi_status = srbr->scsi_status; /* Take care of autosense */ if (srbr->sense_len) { int sense_len, scsi_sense_len; scsi_sense_len = sizeof(struct scsi_sense_data); bzero(&ccb->csio.sense_data, scsi_sense_len); sense_len = (srbr->sense_len > scsi_sense_len) ? scsi_sense_len : srbr->sense_len; bcopy(&srbr->sense[0], &ccb->csio.sense_data, srbr->sense_len); ccb->csio.sense_len = sense_len; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; // scsi_sense_print(&ccb->csio); } /* If this is an inquiry command, fake things out */ if (ccb->ccb_h.flags & CAM_CDB_POINTER) command = ccb->csio.cdb_io.cdb_ptr[0]; else command = ccb->csio.cdb_io.cdb_bytes[0]; if (command == INQUIRY) { if (ccb->ccb_h.status == CAM_REQ_CMP) { device = ccb->csio.data_ptr[0] & 0x1f; /* * We want DASD and PROC devices to only be * visible through the pass device. */ if ((device == T_DIRECT) || (device == T_PROCESSOR) || (sc->flags & AAC_FLAGS_CAM_PASSONLY)) ccb->csio.data_ptr[0] = ((device & 0xe0) | T_NODEVICE); } else if (ccb->ccb_h.status == CAM_SEL_TIMEOUT && ccb->ccb_h.target_lun != 0) { /* fix for INQUIRYs on Lun>0 */ ccb->ccb_h.status = CAM_DEV_NOT_THERE; } } } } aac_release_command(cm); xpt_done(ccb); return; } static u_int32_t aac_cam_reset_bus(struct cam_sim *sim, union ccb *ccb) { struct aac_fib *fib; struct aac_softc *sc; struct aac_cam *camsc; struct aac_vmioctl *vmi; struct aac_resetbus *rbc; int e; camsc = (struct aac_cam *)cam_sim_softc(sim); sc = camsc->inf->aac_sc; if (sc == NULL) { printf("Null sc?\n"); return (CAM_REQ_ABORTED); } aac_alloc_sync_fib(sc, &fib); vmi = (struct aac_vmioctl *)&fib->data[0]; bzero(vmi, sizeof(struct aac_vmioctl)); vmi->Command = VM_Ioctl; vmi->ObjType = FT_DRIVE; vmi->MethId = sc->scsi_method_id; vmi->ObjId = 0; vmi->IoctlCmd = ResetBus; rbc = (struct aac_resetbus *)&vmi->IoctlBuf[0]; rbc->BusNumber = camsc->inf->BusNumber; e = aac_sync_fib(sc, ContainerCommand, 0, fib, sizeof(struct aac_vmioctl)); if (e) { device_printf(sc->aac_dev,"Error %d sending ResetBus command\n", e); aac_release_sync_fib(sc); return (CAM_REQ_ABORTED); } aac_release_sync_fib(sc); return (CAM_REQ_CMP); } static u_int32_t aac_cam_abort_ccb(struct cam_sim *sim, union ccb *ccb) { return (CAM_UA_ABORT); } static u_int32_t aac_cam_term_io(struct cam_sim *sim, union ccb *ccb) { return (CAM_UA_TERMIO); } Index: projects/ppc64/sys/dev/alc/if_alc.c =================================================================== --- projects/ppc64/sys/dev/alc/if_alc.c (revision 204271) +++ projects/ppc64/sys/dev/alc/if_alc.c (revision 204272) @@ -1,3522 +1,3492 @@ /*- * Copyright (c) 2009, Pyun YongHyeon * 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 unmodified, 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. */ /* Driver for Atheros AR8131/AR8132 PCIe Ethernet. */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #undef ALC_USE_CUSTOM_CSUM #ifdef ALC_USE_CUSTOM_CSUM #define ALC_CSUM_FEATURES (CSUM_TCP | CSUM_UDP) #else #define ALC_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) #endif -#ifndef IFCAP_VLAN_HWTSO -#define IFCAP_VLAN_HWTSO 0 -#endif MODULE_DEPEND(alc, pci, 1, 1, 1); MODULE_DEPEND(alc, ether, 1, 1, 1); MODULE_DEPEND(alc, miibus, 1, 1, 1); /* Tunables. */ static int msi_disable = 0; static int msix_disable = 0; TUNABLE_INT("hw.alc.msi_disable", &msi_disable); TUNABLE_INT("hw.alc.msix_disable", &msix_disable); /* * Devices supported by this driver. */ static struct alc_dev { uint16_t alc_vendorid; uint16_t alc_deviceid; const char *alc_name; } alc_devs[] = { { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8131, "Atheros AR8131 PCIe Gigabit Ethernet" }, { VENDORID_ATHEROS, DEVICEID_ATHEROS_AR8132, "Atheros AR8132 PCIe Fast Ethernet" } }; static void alc_aspm(struct alc_softc *); static int alc_attach(device_t); static int alc_check_boundary(struct alc_softc *); static int alc_detach(device_t); static void alc_disable_l0s_l1(struct alc_softc *); static int alc_dma_alloc(struct alc_softc *); static void alc_dma_free(struct alc_softc *); static void alc_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int alc_encap(struct alc_softc *, struct mbuf **); #ifndef __NO_STRICT_ALIGNMENT static struct mbuf * alc_fixup_rx(struct ifnet *, struct mbuf *); #endif static void alc_get_macaddr(struct alc_softc *); static void alc_init(void *); static void alc_init_cmb(struct alc_softc *); static void alc_init_locked(struct alc_softc *); static void alc_init_rr_ring(struct alc_softc *); static int alc_init_rx_ring(struct alc_softc *); static void alc_init_smb(struct alc_softc *); static void alc_init_tx_ring(struct alc_softc *); static void alc_int_task(void *, int); static int alc_intr(void *); static int alc_ioctl(struct ifnet *, u_long, caddr_t); static void alc_mac_config(struct alc_softc *); static int alc_miibus_readreg(device_t, int, int); static void alc_miibus_statchg(device_t); static int alc_miibus_writereg(device_t, int, int, int); static int alc_mediachange(struct ifnet *); static void alc_mediastatus(struct ifnet *, struct ifmediareq *); static int alc_newbuf(struct alc_softc *, struct alc_rxdesc *); static void alc_phy_down(struct alc_softc *); static void alc_phy_reset(struct alc_softc *); static int alc_probe(device_t); static void alc_reset(struct alc_softc *); static int alc_resume(device_t); static void alc_rxeof(struct alc_softc *, struct rx_rdesc *); static int alc_rxintr(struct alc_softc *, int); static void alc_rxfilter(struct alc_softc *); static void alc_rxvlan(struct alc_softc *); static void alc_setlinkspeed(struct alc_softc *); static void alc_setwol(struct alc_softc *); static int alc_shutdown(device_t); static void alc_start(struct ifnet *); static void alc_start_queue(struct alc_softc *); static void alc_stats_clear(struct alc_softc *); static void alc_stats_update(struct alc_softc *); static void alc_stop(struct alc_softc *); static void alc_stop_mac(struct alc_softc *); static void alc_stop_queue(struct alc_softc *); static int alc_suspend(device_t); static void alc_sysctl_node(struct alc_softc *); static void alc_tick(void *); static void alc_tx_task(void *, int); static void alc_txeof(struct alc_softc *); static void alc_watchdog(struct alc_softc *); static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); static int sysctl_hw_alc_proc_limit(SYSCTL_HANDLER_ARGS); static int sysctl_hw_alc_int_mod(SYSCTL_HANDLER_ARGS); static device_method_t alc_methods[] = { /* Device interface. */ DEVMETHOD(device_probe, alc_probe), DEVMETHOD(device_attach, alc_attach), DEVMETHOD(device_detach, alc_detach), DEVMETHOD(device_shutdown, alc_shutdown), DEVMETHOD(device_suspend, alc_suspend), DEVMETHOD(device_resume, alc_resume), /* MII interface. */ DEVMETHOD(miibus_readreg, alc_miibus_readreg), DEVMETHOD(miibus_writereg, alc_miibus_writereg), DEVMETHOD(miibus_statchg, alc_miibus_statchg), { NULL, NULL } }; static driver_t alc_driver = { "alc", alc_methods, sizeof(struct alc_softc) }; static devclass_t alc_devclass; DRIVER_MODULE(alc, pci, alc_driver, alc_devclass, 0, 0); DRIVER_MODULE(miibus, alc, miibus_driver, miibus_devclass, 0, 0); static struct resource_spec alc_res_spec_mem[] = { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec alc_irq_spec_legacy[] = { { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0, 0 } }; static struct resource_spec alc_irq_spec_msi[] = { { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec alc_irq_spec_msix[] = { { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0, 0 } }; static uint32_t alc_dma_burst[] = { 128, 256, 512, 1024, 2048, 4096, 0 }; static int alc_miibus_readreg(device_t dev, int phy, int reg) { struct alc_softc *sc; uint32_t v; int i; sc = device_get_softc(dev); if (phy != sc->alc_phyaddr) return (0); /* * For AR8132 fast ethernet controller, do not report 1000baseT * capability to mii(4). Even though AR8132 uses the same * model/revision number of F1 gigabit PHY, the PHY has no * ability to establish 1000baseT link. */ if ((sc->alc_flags & ALC_FLAG_FASTETHER) != 0 && reg == MII_EXTSR) return (0); CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_READ | MDIO_SUP_PREAMBLE | MDIO_CLK_25_4 | MDIO_REG_ADDR(reg)); for (i = ALC_PHY_TIMEOUT; i > 0; i--) { DELAY(5); v = CSR_READ_4(sc, ALC_MDIO); if ((v & (MDIO_OP_EXECUTE | MDIO_OP_BUSY)) == 0) break; } if (i == 0) { device_printf(sc->alc_dev, "phy read timeout : %d\n", reg); return (0); } return ((v & MDIO_DATA_MASK) >> MDIO_DATA_SHIFT); } static int alc_miibus_writereg(device_t dev, int phy, int reg, int val) { struct alc_softc *sc; uint32_t v; int i; sc = device_get_softc(dev); if (phy != sc->alc_phyaddr) return (0); CSR_WRITE_4(sc, ALC_MDIO, MDIO_OP_EXECUTE | MDIO_OP_WRITE | (val & MDIO_DATA_MASK) << MDIO_DATA_SHIFT | MDIO_SUP_PREAMBLE | MDIO_CLK_25_4 | MDIO_REG_ADDR(reg)); for (i = ALC_PHY_TIMEOUT; i > 0; i--) { DELAY(5); v = CSR_READ_4(sc, ALC_MDIO); if ((v & (MDIO_OP_EXECUTE | MDIO_OP_BUSY)) == 0) break; } if (i == 0) device_printf(sc->alc_dev, "phy write timeout : %d\n", reg); return (0); } static void alc_miibus_statchg(device_t dev) { struct alc_softc *sc; struct mii_data *mii; struct ifnet *ifp; uint32_t reg; sc = device_get_softc(dev); mii = device_get_softc(sc->alc_miibus); ifp = sc->alc_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; sc->alc_flags &= ~ALC_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->alc_flags |= ALC_FLAG_LINK; break; case IFM_1000_T: if ((sc->alc_flags & ALC_FLAG_FASTETHER) == 0) sc->alc_flags |= ALC_FLAG_LINK; break; default: break; } } alc_stop_queue(sc); /* Stop Rx/Tx MACs. */ alc_stop_mac(sc); /* Program MACs with resolved speed/duplex/flow-control. */ if ((sc->alc_flags & ALC_FLAG_LINK) != 0) { alc_start_queue(sc); alc_mac_config(sc); /* Re-enable Tx/Rx MACs. */ reg = CSR_READ_4(sc, ALC_MAC_CFG); reg |= MAC_CFG_TX_ENB | MAC_CFG_RX_ENB; CSR_WRITE_4(sc, ALC_MAC_CFG, reg); } alc_aspm(sc); } static void alc_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { struct alc_softc *sc; struct mii_data *mii; sc = ifp->if_softc; ALC_LOCK(sc); if ((ifp->if_flags & IFF_UP) == 0) { ALC_UNLOCK(sc); return; } mii = device_get_softc(sc->alc_miibus); mii_pollstat(mii); ALC_UNLOCK(sc); ifmr->ifm_status = mii->mii_media_status; ifmr->ifm_active = mii->mii_media_active; } static int alc_mediachange(struct ifnet *ifp) { struct alc_softc *sc; struct mii_data *mii; struct mii_softc *miisc; int error; sc = ifp->if_softc; ALC_LOCK(sc); mii = device_get_softc(sc->alc_miibus); if (mii->mii_instance != 0) { LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } error = mii_mediachg(mii); ALC_UNLOCK(sc); return (error); } static int alc_probe(device_t dev) { struct alc_dev *sp; int i; uint16_t vendor, devid; vendor = pci_get_vendor(dev); devid = pci_get_device(dev); sp = alc_devs; for (i = 0; i < sizeof(alc_devs) / sizeof(alc_devs[0]); i++) { if (vendor == sp->alc_vendorid && devid == sp->alc_deviceid) { device_set_desc(dev, sp->alc_name); return (BUS_PROBE_DEFAULT); } sp++; } return (ENXIO); } static void alc_get_macaddr(struct alc_softc *sc) { uint32_t ea[2], opt; int i; opt = CSR_READ_4(sc, ALC_OPT_CFG); if ((CSR_READ_4(sc, ALC_TWSI_DEBUG) & TWSI_DEBUG_DEV_EXIST) != 0) { /* * EEPROM found, let TWSI reload EEPROM configuration. * This will set ethernet address of controller. */ if ((opt & OPT_CFG_CLK_ENB) == 0) { opt |= OPT_CFG_CLK_ENB; CSR_WRITE_4(sc, ALC_OPT_CFG, opt); CSR_READ_4(sc, ALC_OPT_CFG); DELAY(1000); } CSR_WRITE_4(sc, ALC_TWSI_CFG, CSR_READ_4(sc, ALC_TWSI_CFG) | TWSI_CFG_SW_LD_START); for (i = 100; i > 0; i--) { DELAY(1000); if ((CSR_READ_4(sc, ALC_TWSI_CFG) & TWSI_CFG_SW_LD_START) == 0) break; } if (i == 0) device_printf(sc->alc_dev, "reloading EEPROM timeout!\n"); } else { if (bootverbose) device_printf(sc->alc_dev, "EEPROM not found!\n"); } if ((opt & OPT_CFG_CLK_ENB) != 0) { opt &= ~OPT_CFG_CLK_ENB; CSR_WRITE_4(sc, ALC_OPT_CFG, opt); CSR_READ_4(sc, ALC_OPT_CFG); DELAY(1000); } ea[0] = CSR_READ_4(sc, ALC_PAR0); ea[1] = CSR_READ_4(sc, ALC_PAR1); sc->alc_eaddr[0] = (ea[1] >> 8) & 0xFF; sc->alc_eaddr[1] = (ea[1] >> 0) & 0xFF; sc->alc_eaddr[2] = (ea[0] >> 24) & 0xFF; sc->alc_eaddr[3] = (ea[0] >> 16) & 0xFF; sc->alc_eaddr[4] = (ea[0] >> 8) & 0xFF; sc->alc_eaddr[5] = (ea[0] >> 0) & 0xFF; } static void alc_disable_l0s_l1(struct alc_softc *sc) { uint32_t pmcfg; /* Another magic from vendor. */ pmcfg = CSR_READ_4(sc, ALC_PM_CFG); pmcfg &= ~(PM_CFG_L1_ENTRY_TIMER_MASK | PM_CFG_CLK_SWH_L1 | PM_CFG_ASPM_L0S_ENB | PM_CFG_ASPM_L1_ENB | PM_CFG_MAC_ASPM_CHK | PM_CFG_SERDES_PD_EX_L1); pmcfg |= PM_CFG_SERDES_BUDS_RX_L1_ENB | PM_CFG_SERDES_PLL_L1_ENB | PM_CFG_SERDES_L1_ENB; CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); } static void alc_phy_reset(struct alc_softc *sc) { uint16_t data; /* Reset magic from Linux. */ CSR_WRITE_2(sc, ALC_GPHY_CFG, GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE | GPHY_CFG_SEL_ANA_RESET); CSR_READ_2(sc, ALC_GPHY_CFG); DELAY(10 * 1000); CSR_WRITE_2(sc, ALC_GPHY_CFG, GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE | GPHY_CFG_SEL_ANA_RESET); CSR_READ_2(sc, ALC_GPHY_CFG); DELAY(10 * 1000); /* Load DSP codes, vendor magic. */ data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE | ((1 << ANA_INTERVAL_SEL_TIMER_SHIFT) & ANA_INTERVAL_SEL_TIMER_MASK); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, MII_ANA_CFG18); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA, data); data = ((2 << ANA_SERDES_CDR_BW_SHIFT) & ANA_SERDES_CDR_BW_MASK) | ANA_SERDES_EN_DEEM | ANA_SERDES_SEL_HSP | ANA_SERDES_EN_PLL | ANA_SERDES_EN_LCKDT; alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, MII_ANA_CFG5); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA, data); data = ((44 << ANA_LONG_CABLE_TH_100_SHIFT) & ANA_LONG_CABLE_TH_100_MASK) | ((33 << ANA_SHORT_CABLE_TH_100_SHIFT) & ANA_SHORT_CABLE_TH_100_SHIFT) | ANA_BP_BAD_LINK_ACCUM | ANA_BP_SMALL_BW; alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, MII_ANA_CFG54); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA, data); data = ((11 << ANA_IECHO_ADJ_3_SHIFT) & ANA_IECHO_ADJ_3_MASK) | ((11 << ANA_IECHO_ADJ_2_SHIFT) & ANA_IECHO_ADJ_2_MASK) | ((8 << ANA_IECHO_ADJ_1_SHIFT) & ANA_IECHO_ADJ_1_MASK) | ((8 << ANA_IECHO_ADJ_0_SHIFT) & ANA_IECHO_ADJ_0_MASK); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, MII_ANA_CFG4); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA, data); data = ((7 & ANA_MANUL_SWICH_ON_SHIFT) & ANA_MANUL_SWICH_ON_MASK) | ANA_RESTART_CAL | ANA_MAN_ENABLE | ANA_SEL_HSP | ANA_EN_HB | ANA_OEN_125M; alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_ADDR, MII_ANA_CFG0); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, ALC_MII_DBG_DATA, data); DELAY(1000); } static void alc_phy_down(struct alc_softc *sc) { /* Force PHY down. */ CSR_WRITE_2(sc, ALC_GPHY_CFG, GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE | GPHY_CFG_SEL_ANA_RESET | GPHY_CFG_PHY_IDDQ | GPHY_CFG_PWDOWN_HW); DELAY(1000); } static void alc_aspm(struct alc_softc *sc) { uint32_t pmcfg; ALC_LOCK_ASSERT(sc); pmcfg = CSR_READ_4(sc, ALC_PM_CFG); pmcfg &= ~PM_CFG_SERDES_PD_EX_L1; pmcfg |= PM_CFG_SERDES_BUDS_RX_L1_ENB; pmcfg |= PM_CFG_SERDES_L1_ENB; pmcfg &= ~PM_CFG_L1_ENTRY_TIMER_MASK; pmcfg |= PM_CFG_MAC_ASPM_CHK; if ((sc->alc_flags & ALC_FLAG_LINK) != 0) { pmcfg |= PM_CFG_SERDES_PLL_L1_ENB; pmcfg &= ~PM_CFG_CLK_SWH_L1; pmcfg &= ~PM_CFG_ASPM_L1_ENB; pmcfg &= ~PM_CFG_ASPM_L0S_ENB; } else { pmcfg &= ~PM_CFG_SERDES_PLL_L1_ENB; pmcfg |= PM_CFG_CLK_SWH_L1; pmcfg &= ~PM_CFG_ASPM_L1_ENB; pmcfg &= ~PM_CFG_ASPM_L0S_ENB; } CSR_WRITE_4(sc, ALC_PM_CFG, pmcfg); } static int alc_attach(device_t dev) { struct alc_softc *sc; struct ifnet *ifp; char *aspm_state[] = { "L0s/L1", "L0s", "L1", "L0s/l1" }; uint16_t burst; int base, error, i, msic, msixc, pmc, state; uint32_t cap, ctl, val; error = 0; sc = device_get_softc(dev); sc->alc_dev = dev; mtx_init(&sc->alc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->alc_tick_ch, &sc->alc_mtx, 0); TASK_INIT(&sc->alc_int_task, 0, alc_int_task, sc); /* Map the device. */ pci_enable_busmaster(dev); sc->alc_res_spec = alc_res_spec_mem; sc->alc_irq_spec = alc_irq_spec_legacy; error = bus_alloc_resources(dev, sc->alc_res_spec, sc->alc_res); if (error != 0) { device_printf(dev, "cannot allocate memory resources.\n"); goto fail; } /* Set PHY address. */ sc->alc_phyaddr = ALC_PHY_ADDR; /* Initialize DMA parameters. */ sc->alc_dma_rd_burst = 0; sc->alc_dma_wr_burst = 0; sc->alc_rcb = DMA_CFG_RCB_64; if (pci_find_extcap(dev, PCIY_EXPRESS, &base) == 0) { sc->alc_flags |= ALC_FLAG_PCIE; burst = CSR_READ_2(sc, base + PCIR_EXPRESS_DEVICE_CTL); sc->alc_dma_rd_burst = (burst & PCIM_EXP_CTL_MAX_READ_REQUEST) >> 12; sc->alc_dma_wr_burst = (burst & PCIM_EXP_CTL_MAX_PAYLOAD) >> 5; if (bootverbose) { device_printf(dev, "Read request size : %u bytes.\n", alc_dma_burst[sc->alc_dma_rd_burst]); device_printf(dev, "TLP payload size : %u bytes.\n", alc_dma_burst[sc->alc_dma_wr_burst]); } /* Clear data link and flow-control protocol error. */ val = CSR_READ_4(sc, ALC_PEX_UNC_ERR_SEV); val &= ~(PEX_UNC_ERR_SEV_DLP | PEX_UNC_ERR_SEV_FCP); CSR_WRITE_4(sc, ALC_PEX_UNC_ERR_SEV, val); /* Disable ASPM L0S and L1. */ cap = CSR_READ_2(sc, base + PCIR_EXPRESS_LINK_CAP); if ((cap & PCIM_LINK_CAP_ASPM) != 0) { ctl = CSR_READ_2(sc, base + PCIR_EXPRESS_LINK_CTL); if ((ctl & 0x08) != 0) sc->alc_rcb = DMA_CFG_RCB_128; if (bootverbose) device_printf(dev, "RCB %u bytes\n", sc->alc_rcb == DMA_CFG_RCB_64 ? 64 : 128); state = ctl & 0x03; if (bootverbose) device_printf(sc->alc_dev, "ASPM %s %s\n", aspm_state[state], state == 0 ? "disabled" : "enabled"); if (state != 0) alc_disable_l0s_l1(sc); } } /* Reset PHY. */ alc_phy_reset(sc); /* Reset the ethernet controller. */ alc_reset(sc); /* * One odd thing is AR8132 uses the same PHY hardware(F1 * gigabit PHY) of AR8131. So atphy(4) of AR8132 reports * the PHY supports 1000Mbps but that's not true. The PHY * used in AR8132 can't establish gigabit link even if it * shows the same PHY model/revision number of AR8131. */ if (pci_get_device(dev) == DEVICEID_ATHEROS_AR8132) sc->alc_flags |= ALC_FLAG_FASTETHER | ALC_FLAG_JUMBO; else sc->alc_flags |= ALC_FLAG_JUMBO | ALC_FLAG_ASPM_MON; /* * It seems that AR8131/AR8132 has silicon bug for SMB. In * addition, Atheros said that enabling SMB wouldn't improve * performance. However I think it's bad to access lots of * registers to extract MAC statistics. */ sc->alc_flags |= ALC_FLAG_SMB_BUG; /* * Don't use Tx CMB. It is known to have silicon bug. */ sc->alc_flags |= ALC_FLAG_CMB_BUG; sc->alc_rev = pci_get_revid(dev); sc->alc_chip_rev = CSR_READ_4(sc, ALC_MASTER_CFG) >> MASTER_CHIP_REV_SHIFT; if (bootverbose) { device_printf(dev, "PCI device revision : 0x%04x\n", sc->alc_rev); device_printf(dev, "Chip id/revision : 0x%04x\n", sc->alc_chip_rev); } device_printf(dev, "%u Tx FIFO, %u Rx FIFO\n", CSR_READ_4(sc, ALC_SRAM_TX_FIFO_LEN) * 8, CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN) * 8); /* Allocate IRQ resources. */ msixc = pci_msix_count(dev); msic = pci_msi_count(dev); if (bootverbose) { device_printf(dev, "MSIX count : %d\n", msixc); device_printf(dev, "MSI count : %d\n", msic); } /* Prefer MSIX over MSI. */ if (msix_disable == 0 || msi_disable == 0) { if (msix_disable == 0 && msixc == ALC_MSIX_MESSAGES && pci_alloc_msix(dev, &msixc) == 0) { if (msic == ALC_MSIX_MESSAGES) { device_printf(dev, "Using %d MSIX message(s).\n", msixc); sc->alc_flags |= ALC_FLAG_MSIX; sc->alc_irq_spec = alc_irq_spec_msix; } else pci_release_msi(dev); } if (msi_disable == 0 && (sc->alc_flags & ALC_FLAG_MSIX) == 0 && msic == ALC_MSI_MESSAGES && pci_alloc_msi(dev, &msic) == 0) { if (msic == ALC_MSI_MESSAGES) { device_printf(dev, "Using %d MSI message(s).\n", msic); sc->alc_flags |= ALC_FLAG_MSI; sc->alc_irq_spec = alc_irq_spec_msi; } else pci_release_msi(dev); } } error = bus_alloc_resources(dev, sc->alc_irq_spec, sc->alc_irq); if (error != 0) { device_printf(dev, "cannot allocate IRQ resources.\n"); goto fail; } /* Create device sysctl node. */ alc_sysctl_node(sc); if ((error = alc_dma_alloc(sc) != 0)) goto fail; /* Load station address. */ alc_get_macaddr(sc); ifp = sc->alc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "cannot allocate ifnet structure.\n"); error = ENXIO; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = alc_ioctl; ifp->if_start = alc_start; ifp->if_init = alc_init; ifp->if_snd.ifq_drv_maxlen = ALC_TX_RING_CNT - 1; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); ifp->if_capabilities = IFCAP_TXCSUM | IFCAP_TSO4; ifp->if_hwassist = ALC_CSUM_FEATURES | CSUM_TSO; if (pci_find_extcap(dev, PCIY_PMG, &pmc) == 0) ifp->if_capabilities |= IFCAP_WOL_MAGIC | IFCAP_WOL_MCAST; ifp->if_capenable = ifp->if_capabilities; /* Set up MII bus. */ if ((error = mii_phy_probe(dev, &sc->alc_miibus, alc_mediachange, alc_mediastatus)) != 0) { device_printf(dev, "no PHY found!\n"); goto fail; } ether_ifattach(ifp, sc->alc_eaddr); /* VLAN capability setup. */ - ifp->if_capabilities |= IFCAP_VLAN_MTU; - ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO; ifp->if_capenable = ifp->if_capabilities; /* * XXX * It seems enabling Tx checksum offloading makes more trouble. * Sometimes the controller does not receive any frames when * Tx checksum offloading is enabled. I'm not sure whether this * is a bug in Tx checksum offloading logic or I got broken * sample boards. To safety, don't enable Tx checksum offloading * by default but give chance to users to toggle it if they know * their controllers work without problems. */ ifp->if_capenable &= ~IFCAP_TXCSUM; ifp->if_hwassist &= ~ALC_CSUM_FEATURES; /* Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); /* Create local taskq. */ TASK_INIT(&sc->alc_tx_task, 1, alc_tx_task, ifp); sc->alc_tq = taskqueue_create_fast("alc_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->alc_tq); if (sc->alc_tq == NULL) { device_printf(dev, "could not create taskqueue.\n"); ether_ifdetach(ifp); error = ENXIO; goto fail; } taskqueue_start_threads(&sc->alc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->alc_dev)); if ((sc->alc_flags & ALC_FLAG_MSIX) != 0) msic = ALC_MSIX_MESSAGES; else if ((sc->alc_flags & ALC_FLAG_MSI) != 0) msic = ALC_MSI_MESSAGES; else msic = 1; for (i = 0; i < msic; i++) { error = bus_setup_intr(dev, sc->alc_irq[i], INTR_TYPE_NET | INTR_MPSAFE, alc_intr, NULL, sc, &sc->alc_intrhand[i]); if (error != 0) break; } if (error != 0) { device_printf(dev, "could not set up interrupt handler.\n"); taskqueue_free(sc->alc_tq); sc->alc_tq = NULL; ether_ifdetach(ifp); goto fail; } fail: if (error != 0) alc_detach(dev); return (error); } static int alc_detach(device_t dev) { struct alc_softc *sc; struct ifnet *ifp; int i, msic; sc = device_get_softc(dev); ifp = sc->alc_ifp; if (device_is_attached(dev)) { ALC_LOCK(sc); sc->alc_flags |= ALC_FLAG_DETACH; alc_stop(sc); ALC_UNLOCK(sc); callout_drain(&sc->alc_tick_ch); taskqueue_drain(sc->alc_tq, &sc->alc_int_task); taskqueue_drain(sc->alc_tq, &sc->alc_tx_task); ether_ifdetach(ifp); } if (sc->alc_tq != NULL) { taskqueue_drain(sc->alc_tq, &sc->alc_int_task); taskqueue_free(sc->alc_tq); sc->alc_tq = NULL; } if (sc->alc_miibus != NULL) { device_delete_child(dev, sc->alc_miibus); sc->alc_miibus = NULL; } bus_generic_detach(dev); alc_dma_free(sc); if (ifp != NULL) { if_free(ifp); sc->alc_ifp = NULL; } if ((sc->alc_flags & ALC_FLAG_MSIX) != 0) msic = ALC_MSIX_MESSAGES; else if ((sc->alc_flags & ALC_FLAG_MSI) != 0) msic = ALC_MSI_MESSAGES; else msic = 1; for (i = 0; i < msic; i++) { if (sc->alc_intrhand[i] != NULL) { bus_teardown_intr(dev, sc->alc_irq[i], sc->alc_intrhand[i]); sc->alc_intrhand[i] = NULL; } } if (sc->alc_res[0] != NULL) alc_phy_down(sc); bus_release_resources(dev, sc->alc_irq_spec, sc->alc_irq); if ((sc->alc_flags & (ALC_FLAG_MSI | ALC_FLAG_MSIX)) != 0) pci_release_msi(dev); bus_release_resources(dev, sc->alc_res_spec, sc->alc_res); mtx_destroy(&sc->alc_mtx); return (0); } #define ALC_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) #define ALC_SYSCTL_STAT_ADD64(c, h, n, p, d) \ SYSCTL_ADD_QUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) static void alc_sysctl_node(struct alc_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child, *parent; struct sysctl_oid *tree; struct alc_hw_stats *stats; int error; stats = &sc->alc_stats; ctx = device_get_sysctl_ctx(sc->alc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->alc_dev)); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_rx_mod", CTLTYPE_INT | CTLFLAG_RW, &sc->alc_int_rx_mod, 0, sysctl_hw_alc_int_mod, "I", "alc Rx interrupt moderation"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_tx_mod", CTLTYPE_INT | CTLFLAG_RW, &sc->alc_int_tx_mod, 0, sysctl_hw_alc_int_mod, "I", "alc Tx interrupt moderation"); /* Pull in device tunables. */ sc->alc_int_rx_mod = ALC_IM_RX_TIMER_DEFAULT; error = resource_int_value(device_get_name(sc->alc_dev), device_get_unit(sc->alc_dev), "int_rx_mod", &sc->alc_int_rx_mod); if (error == 0) { if (sc->alc_int_rx_mod < ALC_IM_TIMER_MIN || sc->alc_int_rx_mod > ALC_IM_TIMER_MAX) { device_printf(sc->alc_dev, "int_rx_mod value out of " "range; using default: %d\n", ALC_IM_RX_TIMER_DEFAULT); sc->alc_int_rx_mod = ALC_IM_RX_TIMER_DEFAULT; } } sc->alc_int_tx_mod = ALC_IM_TX_TIMER_DEFAULT; error = resource_int_value(device_get_name(sc->alc_dev), device_get_unit(sc->alc_dev), "int_tx_mod", &sc->alc_int_tx_mod); if (error == 0) { if (sc->alc_int_tx_mod < ALC_IM_TIMER_MIN || sc->alc_int_tx_mod > ALC_IM_TIMER_MAX) { device_printf(sc->alc_dev, "int_tx_mod value out of " "range; using default: %d\n", ALC_IM_TX_TIMER_DEFAULT); sc->alc_int_tx_mod = ALC_IM_TX_TIMER_DEFAULT; } } SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "process_limit", CTLTYPE_INT | CTLFLAG_RW, &sc->alc_process_limit, 0, sysctl_hw_alc_proc_limit, "I", "max number of Rx events to process"); /* Pull in device tunables. */ sc->alc_process_limit = ALC_PROC_DEFAULT; error = resource_int_value(device_get_name(sc->alc_dev), device_get_unit(sc->alc_dev), "process_limit", &sc->alc_process_limit); if (error == 0) { if (sc->alc_process_limit < ALC_PROC_MIN || sc->alc_process_limit > ALC_PROC_MAX) { device_printf(sc->alc_dev, "process_limit value out of range; " "using default: %d\n", ALC_PROC_DEFAULT); sc->alc_process_limit = ALC_PROC_DEFAULT; } } tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, NULL, "ALC statistics"); parent = SYSCTL_CHILDREN(tree); /* Rx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, NULL, "Rx MAC statistics"); child = SYSCTL_CHILDREN(tree); ALC_SYSCTL_STAT_ADD32(ctx, child, "good_frames", &stats->rx_frames, "Good frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", &stats->rx_bcast_frames, "Good broadcast frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", &stats->rx_mcast_frames, "Good multicast frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", &stats->rx_pause_frames, "Pause control frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "control_frames", &stats->rx_control_frames, "Control frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "crc_errs", &stats->rx_crcerrs, "CRC errors"); ALC_SYSCTL_STAT_ADD32(ctx, child, "len_errs", &stats->rx_lenerrs, "Frames with length mismatched"); ALC_SYSCTL_STAT_ADD64(ctx, child, "good_octets", &stats->rx_bytes, "Good octets"); ALC_SYSCTL_STAT_ADD64(ctx, child, "good_bcast_octets", &stats->rx_bcast_bytes, "Good broadcast octets"); ALC_SYSCTL_STAT_ADD64(ctx, child, "good_mcast_octets", &stats->rx_mcast_bytes, "Good multicast octets"); ALC_SYSCTL_STAT_ADD32(ctx, child, "runts", &stats->rx_runts, "Too short frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "fragments", &stats->rx_fragments, "Fragmented frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_64", &stats->rx_pkts_64, "64 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_65_127", &stats->rx_pkts_65_127, "65 to 127 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_128_255", &stats->rx_pkts_128_255, "128 to 255 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_256_511", &stats->rx_pkts_256_511, "256 to 511 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_512_1023", &stats->rx_pkts_512_1023, "512 to 1023 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_1024_1518", &stats->rx_pkts_1024_1518, "1024 to 1518 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_1519_max", &stats->rx_pkts_1519_max, "1519 to max frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "trunc_errs", &stats->rx_pkts_truncated, "Truncated frames due to MTU size"); ALC_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows", &stats->rx_fifo_oflows, "FIFO overflows"); ALC_SYSCTL_STAT_ADD32(ctx, child, "rrs_errs", &stats->rx_rrs_errs, "Return status write-back errors"); ALC_SYSCTL_STAT_ADD32(ctx, child, "align_errs", &stats->rx_alignerrs, "Alignment errors"); ALC_SYSCTL_STAT_ADD32(ctx, child, "filtered", &stats->rx_pkts_filtered, "Frames dropped due to address filtering"); /* Tx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "Tx MAC statistics"); child = SYSCTL_CHILDREN(tree); ALC_SYSCTL_STAT_ADD32(ctx, child, "good_frames", &stats->tx_frames, "Good frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", &stats->tx_bcast_frames, "Good broadcast frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", &stats->tx_mcast_frames, "Good multicast frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", &stats->tx_pause_frames, "Pause control frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "control_frames", &stats->tx_control_frames, "Control frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "excess_defers", &stats->tx_excess_defer, "Frames with excessive derferrals"); ALC_SYSCTL_STAT_ADD32(ctx, child, "defers", &stats->tx_excess_defer, "Frames with derferrals"); ALC_SYSCTL_STAT_ADD64(ctx, child, "good_octets", &stats->tx_bytes, "Good octets"); ALC_SYSCTL_STAT_ADD64(ctx, child, "good_bcast_octets", &stats->tx_bcast_bytes, "Good broadcast octets"); ALC_SYSCTL_STAT_ADD64(ctx, child, "good_mcast_octets", &stats->tx_mcast_bytes, "Good multicast octets"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_64", &stats->tx_pkts_64, "64 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_65_127", &stats->tx_pkts_65_127, "65 to 127 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_128_255", &stats->tx_pkts_128_255, "128 to 255 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_256_511", &stats->tx_pkts_256_511, "256 to 511 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_512_1023", &stats->tx_pkts_512_1023, "512 to 1023 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_1024_1518", &stats->tx_pkts_1024_1518, "1024 to 1518 bytes frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "frames_1519_max", &stats->tx_pkts_1519_max, "1519 to max frames"); ALC_SYSCTL_STAT_ADD32(ctx, child, "single_colls", &stats->tx_single_colls, "Single collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "multi_colls", &stats->tx_multi_colls, "Multiple collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "late_colls", &stats->tx_late_colls, "Late collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "excess_colls", &stats->tx_excess_colls, "Excessive collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "abort", &stats->tx_abort, "Aborted frames due to Excessive collisions"); ALC_SYSCTL_STAT_ADD32(ctx, child, "underruns", &stats->tx_underrun, "FIFO underruns"); ALC_SYSCTL_STAT_ADD32(ctx, child, "desc_underruns", &stats->tx_desc_underrun, "Descriptor write-back errors"); ALC_SYSCTL_STAT_ADD32(ctx, child, "len_errs", &stats->tx_lenerrs, "Frames with length mismatched"); ALC_SYSCTL_STAT_ADD32(ctx, child, "trunc_errs", &stats->tx_pkts_truncated, "Truncated frames due to MTU size"); } #undef ALC_SYSCTL_STAT_ADD32 #undef ALC_SYSCTL_STAT_ADD64 struct alc_dmamap_arg { bus_addr_t alc_busaddr; }; static void alc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct alc_dmamap_arg *ctx; if (error != 0) return; KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); ctx = (struct alc_dmamap_arg *)arg; ctx->alc_busaddr = segs[0].ds_addr; } /* * Normal and high Tx descriptors shares single Tx high address. * Four Rx descriptor/return rings and CMB shares the same Rx * high address. */ static int alc_check_boundary(struct alc_softc *sc) { bus_addr_t cmb_end, rx_ring_end, rr_ring_end, tx_ring_end; rx_ring_end = sc->alc_rdata.alc_rx_ring_paddr + ALC_RX_RING_SZ; rr_ring_end = sc->alc_rdata.alc_rr_ring_paddr + ALC_RR_RING_SZ; cmb_end = sc->alc_rdata.alc_cmb_paddr + ALC_CMB_SZ; tx_ring_end = sc->alc_rdata.alc_tx_ring_paddr + ALC_TX_RING_SZ; /* 4GB boundary crossing is not allowed. */ if ((ALC_ADDR_HI(rx_ring_end) != ALC_ADDR_HI(sc->alc_rdata.alc_rx_ring_paddr)) || (ALC_ADDR_HI(rr_ring_end) != ALC_ADDR_HI(sc->alc_rdata.alc_rr_ring_paddr)) || (ALC_ADDR_HI(cmb_end) != ALC_ADDR_HI(sc->alc_rdata.alc_cmb_paddr)) || (ALC_ADDR_HI(tx_ring_end) != ALC_ADDR_HI(sc->alc_rdata.alc_tx_ring_paddr))) return (EFBIG); /* * Make sure Rx return descriptor/Rx descriptor/CMB use * the same high address. */ if ((ALC_ADDR_HI(rx_ring_end) != ALC_ADDR_HI(rr_ring_end)) || (ALC_ADDR_HI(rx_ring_end) != ALC_ADDR_HI(cmb_end))) return (EFBIG); return (0); } static int alc_dma_alloc(struct alc_softc *sc) { struct alc_txdesc *txd; struct alc_rxdesc *rxd; bus_addr_t lowaddr; struct alc_dmamap_arg ctx; int error, i; lowaddr = BUS_SPACE_MAXADDR; again: /* Create parent DMA tag. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->alc_dev), /* parent */ 1, 0, /* alignment, boundary */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 0, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_parent_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create parent DMA tag.\n"); goto fail; } /* Create DMA tag for Tx descriptor ring. */ error = bus_dma_tag_create( sc->alc_cdata.alc_parent_tag, /* parent */ ALC_TX_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ALC_TX_RING_SZ, /* maxsize */ 1, /* nsegments */ ALC_TX_RING_SZ, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_tx_ring_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create Tx ring DMA tag.\n"); goto fail; } /* Create DMA tag for Rx free descriptor ring. */ error = bus_dma_tag_create( sc->alc_cdata.alc_parent_tag, /* parent */ ALC_RX_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ALC_RX_RING_SZ, /* maxsize */ 1, /* nsegments */ ALC_RX_RING_SZ, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_rx_ring_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create Rx ring DMA tag.\n"); goto fail; } /* Create DMA tag for Rx return descriptor ring. */ error = bus_dma_tag_create( sc->alc_cdata.alc_parent_tag, /* parent */ ALC_RR_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ALC_RR_RING_SZ, /* maxsize */ 1, /* nsegments */ ALC_RR_RING_SZ, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_rr_ring_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create Rx return ring DMA tag.\n"); goto fail; } /* Create DMA tag for coalescing message block. */ error = bus_dma_tag_create( sc->alc_cdata.alc_parent_tag, /* parent */ ALC_CMB_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ALC_CMB_SZ, /* maxsize */ 1, /* nsegments */ ALC_CMB_SZ, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_cmb_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create CMB DMA tag.\n"); goto fail; } /* Create DMA tag for status message block. */ error = bus_dma_tag_create( sc->alc_cdata.alc_parent_tag, /* parent */ ALC_SMB_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ALC_SMB_SZ, /* maxsize */ 1, /* nsegments */ ALC_SMB_SZ, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_smb_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create SMB DMA tag.\n"); goto fail; } /* Allocate DMA'able memory and load the DMA map for Tx ring. */ error = bus_dmamem_alloc(sc->alc_cdata.alc_tx_ring_tag, (void **)&sc->alc_rdata.alc_tx_ring, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->alc_cdata.alc_tx_ring_map); if (error != 0) { device_printf(sc->alc_dev, "could not allocate DMA'able memory for Tx ring.\n"); goto fail; } ctx.alc_busaddr = 0; error = bus_dmamap_load(sc->alc_cdata.alc_tx_ring_tag, sc->alc_cdata.alc_tx_ring_map, sc->alc_rdata.alc_tx_ring, ALC_TX_RING_SZ, alc_dmamap_cb, &ctx, 0); if (error != 0 || ctx.alc_busaddr == 0) { device_printf(sc->alc_dev, "could not load DMA'able memory for Tx ring.\n"); goto fail; } sc->alc_rdata.alc_tx_ring_paddr = ctx.alc_busaddr; /* Allocate DMA'able memory and load the DMA map for Rx ring. */ error = bus_dmamem_alloc(sc->alc_cdata.alc_rx_ring_tag, (void **)&sc->alc_rdata.alc_rx_ring, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->alc_cdata.alc_rx_ring_map); if (error != 0) { device_printf(sc->alc_dev, "could not allocate DMA'able memory for Rx ring.\n"); goto fail; } ctx.alc_busaddr = 0; error = bus_dmamap_load(sc->alc_cdata.alc_rx_ring_tag, sc->alc_cdata.alc_rx_ring_map, sc->alc_rdata.alc_rx_ring, ALC_RX_RING_SZ, alc_dmamap_cb, &ctx, 0); if (error != 0 || ctx.alc_busaddr == 0) { device_printf(sc->alc_dev, "could not load DMA'able memory for Rx ring.\n"); goto fail; } sc->alc_rdata.alc_rx_ring_paddr = ctx.alc_busaddr; /* Allocate DMA'able memory and load the DMA map for Rx return ring. */ error = bus_dmamem_alloc(sc->alc_cdata.alc_rr_ring_tag, (void **)&sc->alc_rdata.alc_rr_ring, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->alc_cdata.alc_rr_ring_map); if (error != 0) { device_printf(sc->alc_dev, "could not allocate DMA'able memory for Rx return ring.\n"); goto fail; } ctx.alc_busaddr = 0; error = bus_dmamap_load(sc->alc_cdata.alc_rr_ring_tag, sc->alc_cdata.alc_rr_ring_map, sc->alc_rdata.alc_rr_ring, ALC_RR_RING_SZ, alc_dmamap_cb, &ctx, 0); if (error != 0 || ctx.alc_busaddr == 0) { device_printf(sc->alc_dev, "could not load DMA'able memory for Tx ring.\n"); goto fail; } sc->alc_rdata.alc_rr_ring_paddr = ctx.alc_busaddr; /* Allocate DMA'able memory and load the DMA map for CMB. */ error = bus_dmamem_alloc(sc->alc_cdata.alc_cmb_tag, (void **)&sc->alc_rdata.alc_cmb, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->alc_cdata.alc_cmb_map); if (error != 0) { device_printf(sc->alc_dev, "could not allocate DMA'able memory for CMB.\n"); goto fail; } ctx.alc_busaddr = 0; error = bus_dmamap_load(sc->alc_cdata.alc_cmb_tag, sc->alc_cdata.alc_cmb_map, sc->alc_rdata.alc_cmb, ALC_CMB_SZ, alc_dmamap_cb, &ctx, 0); if (error != 0 || ctx.alc_busaddr == 0) { device_printf(sc->alc_dev, "could not load DMA'able memory for CMB.\n"); goto fail; } sc->alc_rdata.alc_cmb_paddr = ctx.alc_busaddr; /* Allocate DMA'able memory and load the DMA map for SMB. */ error = bus_dmamem_alloc(sc->alc_cdata.alc_smb_tag, (void **)&sc->alc_rdata.alc_smb, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->alc_cdata.alc_smb_map); if (error != 0) { device_printf(sc->alc_dev, "could not allocate DMA'able memory for SMB.\n"); goto fail; } ctx.alc_busaddr = 0; error = bus_dmamap_load(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map, sc->alc_rdata.alc_smb, ALC_SMB_SZ, alc_dmamap_cb, &ctx, 0); if (error != 0 || ctx.alc_busaddr == 0) { device_printf(sc->alc_dev, "could not load DMA'able memory for CMB.\n"); goto fail; } sc->alc_rdata.alc_smb_paddr = ctx.alc_busaddr; /* Make sure we've not crossed 4GB boundary. */ if (lowaddr != BUS_SPACE_MAXADDR_32BIT && (error = alc_check_boundary(sc)) != 0) { device_printf(sc->alc_dev, "4GB boundary crossed, " "switching to 32bit DMA addressing mode.\n"); alc_dma_free(sc); /* * Limit max allowable DMA address space to 32bit * and try again. */ lowaddr = BUS_SPACE_MAXADDR_32BIT; goto again; } /* * Create Tx buffer parent tag. * AR8131/AR8132 allows 64bit DMA addressing of Tx/Rx buffers * so it needs separate parent DMA tag as parent DMA address * space could be restricted to be within 32bit address space * by 4GB boundary crossing. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->alc_dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 0, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_buffer_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create parent buffer DMA tag.\n"); goto fail; } /* Create DMA tag for Tx buffers. */ error = bus_dma_tag_create( sc->alc_cdata.alc_buffer_tag, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ALC_TSO_MAXSIZE, /* maxsize */ ALC_MAXTXSEGS, /* nsegments */ ALC_TSO_MAXSEGSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_tx_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create Tx DMA tag.\n"); goto fail; } /* Create DMA tag for Rx buffers. */ error = bus_dma_tag_create( sc->alc_cdata.alc_buffer_tag, /* parent */ ALC_RX_BUF_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, /* maxsize */ 1, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->alc_cdata.alc_rx_tag); if (error != 0) { device_printf(sc->alc_dev, "could not create Rx DMA tag.\n"); goto fail; } /* Create DMA maps for Tx buffers. */ for (i = 0; i < ALC_TX_RING_CNT; i++) { txd = &sc->alc_cdata.alc_txdesc[i]; txd->tx_m = NULL; txd->tx_dmamap = NULL; error = bus_dmamap_create(sc->alc_cdata.alc_tx_tag, 0, &txd->tx_dmamap); if (error != 0) { device_printf(sc->alc_dev, "could not create Tx dmamap.\n"); goto fail; } } /* Create DMA maps for Rx buffers. */ if ((error = bus_dmamap_create(sc->alc_cdata.alc_rx_tag, 0, &sc->alc_cdata.alc_rx_sparemap)) != 0) { device_printf(sc->alc_dev, "could not create spare Rx dmamap.\n"); goto fail; } for (i = 0; i < ALC_RX_RING_CNT; i++) { rxd = &sc->alc_cdata.alc_rxdesc[i]; rxd->rx_m = NULL; rxd->rx_dmamap = NULL; error = bus_dmamap_create(sc->alc_cdata.alc_rx_tag, 0, &rxd->rx_dmamap); if (error != 0) { device_printf(sc->alc_dev, "could not create Rx dmamap.\n"); goto fail; } } fail: return (error); } static void alc_dma_free(struct alc_softc *sc) { struct alc_txdesc *txd; struct alc_rxdesc *rxd; int i; /* Tx buffers. */ if (sc->alc_cdata.alc_tx_tag != NULL) { for (i = 0; i < ALC_TX_RING_CNT; i++) { txd = &sc->alc_cdata.alc_txdesc[i]; if (txd->tx_dmamap != NULL) { bus_dmamap_destroy(sc->alc_cdata.alc_tx_tag, txd->tx_dmamap); txd->tx_dmamap = NULL; } } bus_dma_tag_destroy(sc->alc_cdata.alc_tx_tag); sc->alc_cdata.alc_tx_tag = NULL; } /* Rx buffers */ if (sc->alc_cdata.alc_rx_tag != NULL) { for (i = 0; i < ALC_RX_RING_CNT; i++) { rxd = &sc->alc_cdata.alc_rxdesc[i]; if (rxd->rx_dmamap != NULL) { bus_dmamap_destroy(sc->alc_cdata.alc_rx_tag, rxd->rx_dmamap); rxd->rx_dmamap = NULL; } } if (sc->alc_cdata.alc_rx_sparemap != NULL) { bus_dmamap_destroy(sc->alc_cdata.alc_rx_tag, sc->alc_cdata.alc_rx_sparemap); sc->alc_cdata.alc_rx_sparemap = NULL; } bus_dma_tag_destroy(sc->alc_cdata.alc_rx_tag); sc->alc_cdata.alc_rx_tag = NULL; } /* Tx descriptor ring. */ if (sc->alc_cdata.alc_tx_ring_tag != NULL) { if (sc->alc_cdata.alc_tx_ring_map != NULL) bus_dmamap_unload(sc->alc_cdata.alc_tx_ring_tag, sc->alc_cdata.alc_tx_ring_map); if (sc->alc_cdata.alc_tx_ring_map != NULL && sc->alc_rdata.alc_tx_ring != NULL) bus_dmamem_free(sc->alc_cdata.alc_tx_ring_tag, sc->alc_rdata.alc_tx_ring, sc->alc_cdata.alc_tx_ring_map); sc->alc_rdata.alc_tx_ring = NULL; sc->alc_cdata.alc_tx_ring_map = NULL; bus_dma_tag_destroy(sc->alc_cdata.alc_tx_ring_tag); sc->alc_cdata.alc_tx_ring_tag = NULL; } /* Rx ring. */ if (sc->alc_cdata.alc_rx_ring_tag != NULL) { if (sc->alc_cdata.alc_rx_ring_map != NULL) bus_dmamap_unload(sc->alc_cdata.alc_rx_ring_tag, sc->alc_cdata.alc_rx_ring_map); if (sc->alc_cdata.alc_rx_ring_map != NULL && sc->alc_rdata.alc_rx_ring != NULL) bus_dmamem_free(sc->alc_cdata.alc_rx_ring_tag, sc->alc_rdata.alc_rx_ring, sc->alc_cdata.alc_rx_ring_map); sc->alc_rdata.alc_rx_ring = NULL; sc->alc_cdata.alc_rx_ring_map = NULL; bus_dma_tag_destroy(sc->alc_cdata.alc_rx_ring_tag); sc->alc_cdata.alc_rx_ring_tag = NULL; } /* Rx return ring. */ if (sc->alc_cdata.alc_rr_ring_tag != NULL) { if (sc->alc_cdata.alc_rr_ring_map != NULL) bus_dmamap_unload(sc->alc_cdata.alc_rr_ring_tag, sc->alc_cdata.alc_rr_ring_map); if (sc->alc_cdata.alc_rr_ring_map != NULL && sc->alc_rdata.alc_rr_ring != NULL) bus_dmamem_free(sc->alc_cdata.alc_rr_ring_tag, sc->alc_rdata.alc_rr_ring, sc->alc_cdata.alc_rr_ring_map); sc->alc_rdata.alc_rr_ring = NULL; sc->alc_cdata.alc_rr_ring_map = NULL; bus_dma_tag_destroy(sc->alc_cdata.alc_rr_ring_tag); sc->alc_cdata.alc_rr_ring_tag = NULL; } /* CMB block */ if (sc->alc_cdata.alc_cmb_tag != NULL) { if (sc->alc_cdata.alc_cmb_map != NULL) bus_dmamap_unload(sc->alc_cdata.alc_cmb_tag, sc->alc_cdata.alc_cmb_map); if (sc->alc_cdata.alc_cmb_map != NULL && sc->alc_rdata.alc_cmb != NULL) bus_dmamem_free(sc->alc_cdata.alc_cmb_tag, sc->alc_rdata.alc_cmb, sc->alc_cdata.alc_cmb_map); sc->alc_rdata.alc_cmb = NULL; sc->alc_cdata.alc_cmb_map = NULL; bus_dma_tag_destroy(sc->alc_cdata.alc_cmb_tag); sc->alc_cdata.alc_cmb_tag = NULL; } /* SMB block */ if (sc->alc_cdata.alc_smb_tag != NULL) { if (sc->alc_cdata.alc_smb_map != NULL) bus_dmamap_unload(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map); if (sc->alc_cdata.alc_smb_map != NULL && sc->alc_rdata.alc_smb != NULL) bus_dmamem_free(sc->alc_cdata.alc_smb_tag, sc->alc_rdata.alc_smb, sc->alc_cdata.alc_smb_map); sc->alc_rdata.alc_smb = NULL; sc->alc_cdata.alc_smb_map = NULL; bus_dma_tag_destroy(sc->alc_cdata.alc_smb_tag); sc->alc_cdata.alc_smb_tag = NULL; } if (sc->alc_cdata.alc_buffer_tag != NULL) { bus_dma_tag_destroy(sc->alc_cdata.alc_buffer_tag); sc->alc_cdata.alc_buffer_tag = NULL; } if (sc->alc_cdata.alc_parent_tag != NULL) { bus_dma_tag_destroy(sc->alc_cdata.alc_parent_tag); sc->alc_cdata.alc_parent_tag = NULL; } } static int alc_shutdown(device_t dev) { return (alc_suspend(dev)); } /* * Note, this driver resets the link speed to 10/100Mbps by * restarting auto-negotiation in suspend/shutdown phase but we * don't know whether that auto-negotiation would succeed or not * as driver has no control after powering off/suspend operation. * If the renegotiation fail WOL may not work. Running at 1Gbps * will draw more power than 375mA at 3.3V which is specified in * PCI specification and that would result in complete * shutdowning power to ethernet controller. * * TODO * Save current negotiated media speed/duplex/flow-control to * softc and restore the same link again after resuming. PHY * handling such as power down/resetting to 100Mbps may be better * handled in suspend method in phy driver. */ static void alc_setlinkspeed(struct alc_softc *sc) { struct mii_data *mii; int aneg, i; mii = device_get_softc(sc->alc_miibus); mii_pollstat(mii); aneg = 0; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch IFM_SUBTYPE(mii->mii_media_active) { case IFM_10_T: case IFM_100_TX: return; case IFM_1000_T: aneg++; break; default: break; } } alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, MII_100T2CR, 0); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA); alc_miibus_writereg(sc->alc_dev, sc->alc_phyaddr, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); DELAY(1000); if (aneg != 0) { /* * Poll link state until alc(4) get a 10/100Mbps link. */ for (i = 0; i < MII_ANEGTICKS_GIGE; i++) { mii_pollstat(mii); if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE( mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: alc_mac_config(sc); return; default: break; } } ALC_UNLOCK(sc); pause("alclnk", hz); ALC_LOCK(sc); } if (i == MII_ANEGTICKS_GIGE) device_printf(sc->alc_dev, "establishing a link failed, WOL may not work!"); } /* * No link, force MAC to have 100Mbps, full-duplex link. * This is the last resort and may/may not work. */ mii->mii_media_status = IFM_AVALID | IFM_ACTIVE; mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX; alc_mac_config(sc); } static void alc_setwol(struct alc_softc *sc) { struct ifnet *ifp; uint32_t cap, reg, pmcs; uint16_t pmstat; int base, pmc; ALC_LOCK_ASSERT(sc); if (pci_find_extcap(sc->alc_dev, PCIY_EXPRESS, &base) == 0) { cap = CSR_READ_2(sc, base + PCIR_EXPRESS_LINK_CAP); if ((cap & PCIM_LINK_CAP_ASPM) != 0) { cap = CSR_READ_2(sc, base + PCIR_EXPRESS_LINK_CTL); alc_disable_l0s_l1(sc); } } if (pci_find_extcap(sc->alc_dev, PCIY_PMG, &pmc) != 0) { /* Disable WOL. */ CSR_WRITE_4(sc, ALC_WOL_CFG, 0); reg = CSR_READ_4(sc, ALC_PCIE_PHYMISC); reg |= PCIE_PHYMISC_FORCE_RCV_DET; CSR_WRITE_4(sc, ALC_PCIE_PHYMISC, reg); /* Force PHY power down. */ alc_phy_down(sc); return; } ifp = sc->alc_ifp; if ((ifp->if_capenable & IFCAP_WOL) != 0) { if ((sc->alc_flags & ALC_FLAG_FASTETHER) == 0) alc_setlinkspeed(sc); reg = CSR_READ_4(sc, ALC_MASTER_CFG); reg &= ~MASTER_CLK_SEL_DIS; CSR_WRITE_4(sc, ALC_MASTER_CFG, reg); } pmcs = 0; if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) pmcs |= WOL_CFG_MAGIC | WOL_CFG_MAGIC_ENB; CSR_WRITE_4(sc, ALC_WOL_CFG, pmcs); reg = CSR_READ_4(sc, ALC_MAC_CFG); reg &= ~(MAC_CFG_DBG | MAC_CFG_PROMISC | MAC_CFG_ALLMULTI | MAC_CFG_BCAST); if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) reg |= MAC_CFG_ALLMULTI | MAC_CFG_BCAST; if ((ifp->if_capenable & IFCAP_WOL) != 0) reg |= MAC_CFG_RX_ENB; CSR_WRITE_4(sc, ALC_MAC_CFG, reg); reg = CSR_READ_4(sc, ALC_PCIE_PHYMISC); reg |= PCIE_PHYMISC_FORCE_RCV_DET; CSR_WRITE_4(sc, ALC_PCIE_PHYMISC, reg); if ((ifp->if_capenable & IFCAP_WOL) == 0) { /* WOL disabled, PHY power down. */ alc_phy_down(sc); } /* Request PME. */ pmstat = pci_read_config(sc->alc_dev, pmc + PCIR_POWER_STATUS, 2); pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); if ((ifp->if_capenable & IFCAP_WOL) != 0) pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; pci_write_config(sc->alc_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); } static int alc_suspend(device_t dev) { struct alc_softc *sc; sc = device_get_softc(dev); ALC_LOCK(sc); alc_stop(sc); alc_setwol(sc); ALC_UNLOCK(sc); return (0); } static int alc_resume(device_t dev) { struct alc_softc *sc; struct ifnet *ifp; int pmc; uint16_t pmstat; sc = device_get_softc(dev); ALC_LOCK(sc); if (pci_find_extcap(sc->alc_dev, PCIY_PMG, &pmc) == 0) { /* Disable PME and clear PME status. */ pmstat = pci_read_config(sc->alc_dev, pmc + PCIR_POWER_STATUS, 2); if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { pmstat &= ~PCIM_PSTAT_PMEENABLE; pci_write_config(sc->alc_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); } } /* Reset PHY. */ alc_phy_reset(sc); ifp = sc->alc_ifp; if ((ifp->if_flags & IFF_UP) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; alc_init_locked(sc); } ALC_UNLOCK(sc); return (0); } static int alc_encap(struct alc_softc *sc, struct mbuf **m_head) { struct alc_txdesc *txd, *txd_last; struct tx_desc *desc; struct mbuf *m; struct ip *ip; struct tcphdr *tcp; bus_dma_segment_t txsegs[ALC_MAXTXSEGS]; bus_dmamap_t map; - uint32_t cflags, hdrlen, ip_off, poff, vtag; + uint32_t cflags, hdrlen, poff, vtag; int error, idx, nsegs, prod; ALC_LOCK_ASSERT(sc); M_ASSERTPKTHDR((*m_head)); m = *m_head; ip = NULL; tcp = NULL; - ip_off = poff = 0; + poff = 0; if ((m->m_pkthdr.csum_flags & (ALC_CSUM_FEATURES | CSUM_TSO)) != 0) { /* * AR8131/AR8132 requires offset of TCP/UDP header in its * Tx descriptor to perform Tx checksum offloading. TSO * also requires TCP header offset and modification of * IP/TCP header. This kind of operation takes many CPU * cycles on FreeBSD so fast host CPU is required to get * smooth TSO performance. */ - struct ether_header *eh; if (M_WRITABLE(m) == 0) { /* Get a writable copy. */ m = m_dup(*m_head, M_DONTWAIT); /* Release original mbufs. */ m_freem(*m_head); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m; } - ip_off = sizeof(struct ether_header); - m = m_pullup(m, ip_off); + m = m_pullup(m, sizeof(struct ether_header) + sizeof(struct ip)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } - eh = mtod(m, struct ether_header *); - /* - * Check if hardware VLAN insertion is off. - * Additional check for LLC/SNAP frame? - */ - if (eh->ether_type == htons(ETHERTYPE_VLAN)) { - ip_off = sizeof(struct ether_vlan_header); - m = m_pullup(m, ip_off); - if (m == NULL) { - *m_head = NULL; - return (ENOBUFS); - } - } - m = m_pullup(m, ip_off + sizeof(struct ip)); - if (m == NULL) { - *m_head = NULL; - return (ENOBUFS); - } - ip = (struct ip *)(mtod(m, char *) + ip_off); - poff = ip_off + (ip->ip_hl << 2); + ip = (struct ip *)(mtod(m, char *) + sizeof(struct ether_header)); + poff = sizeof(struct ether_header) + (ip->ip_hl << 2); if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { m = m_pullup(m, poff + sizeof(struct tcphdr)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } tcp = (struct tcphdr *)(mtod(m, char *) + poff); m = m_pullup(m, poff + (tcp->th_off << 2)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } /* * Due to strict adherence of Microsoft NDIS * Large Send specification, hardware expects * a pseudo TCP checksum inserted by upper * stack. Unfortunately the pseudo TCP * checksum that NDIS refers to does not include * TCP payload length so driver should recompute * the pseudo checksum here. Hopefully this * wouldn't be much burden on modern CPUs. * * Reset IP checksum and recompute TCP pseudo * checksum as NDIS specification said. */ ip->ip_sum = 0; tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(IPPROTO_TCP)); } *m_head = m; } prod = sc->alc_cdata.alc_tx_prod; txd = &sc->alc_cdata.alc_txdesc[prod]; txd_last = txd; map = txd->tx_dmamap; error = bus_dmamap_load_mbuf_sg(sc->alc_cdata.alc_tx_tag, map, *m_head, txsegs, &nsegs, 0); if (error == EFBIG) { m = m_collapse(*m_head, M_DONTWAIT, ALC_MAXTXSEGS); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOMEM); } *m_head = m; error = bus_dmamap_load_mbuf_sg(sc->alc_cdata.alc_tx_tag, map, *m_head, txsegs, &nsegs, 0); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (error); } } else if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* Check descriptor overrun. */ if (sc->alc_cdata.alc_tx_cnt + nsegs >= ALC_TX_RING_CNT - 3) { bus_dmamap_unload(sc->alc_cdata.alc_tx_tag, map); return (ENOBUFS); } bus_dmamap_sync(sc->alc_cdata.alc_tx_tag, map, BUS_DMASYNC_PREWRITE); m = *m_head; cflags = TD_ETHERNET; vtag = 0; desc = NULL; idx = 0; /* Configure VLAN hardware tag insertion. */ if ((m->m_flags & M_VLANTAG) != 0) { vtag = htons(m->m_pkthdr.ether_vtag); vtag = (vtag << TD_VLAN_SHIFT) & TD_VLAN_MASK; cflags |= TD_INS_VLAN_TAG; } /* Configure Tx checksum offload. */ if ((m->m_pkthdr.csum_flags & ALC_CSUM_FEATURES) != 0) { #ifdef ALC_USE_CUSTOM_CSUM cflags |= TD_CUSTOM_CSUM; /* Set checksum start offset. */ cflags |= ((poff >> 1) << TD_PLOAD_OFFSET_SHIFT) & TD_PLOAD_OFFSET_MASK; /* Set checksum insertion position of TCP/UDP. */ cflags |= (((poff + m->m_pkthdr.csum_data) >> 1) << TD_CUSTOM_CSUM_OFFSET_SHIFT) & TD_CUSTOM_CSUM_OFFSET_MASK; #else if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) cflags |= TD_IPCSUM; if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0) cflags |= TD_TCPCSUM; if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0) cflags |= TD_UDPCSUM; /* Set TCP/UDP header offset. */ cflags |= (poff << TD_L4HDR_OFFSET_SHIFT) & TD_L4HDR_OFFSET_MASK; #endif } else if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { /* Request TSO and set MSS. */ cflags |= TD_TSO | TD_TSO_DESCV1; cflags |= ((uint32_t)m->m_pkthdr.tso_segsz << TD_MSS_SHIFT) & TD_MSS_MASK; /* Set TCP header offset. */ cflags |= (poff << TD_TCPHDR_OFFSET_SHIFT) & TD_TCPHDR_OFFSET_MASK; /* * AR8131/AR8132 requires the first buffer should * only hold IP/TCP header data. Payload should * be handled in other descriptors. */ hdrlen = poff + (tcp->th_off << 2); desc = &sc->alc_rdata.alc_tx_ring[prod]; desc->len = htole32(TX_BYTES(hdrlen | vtag)); desc->flags = htole32(cflags); desc->addr = htole64(txsegs[0].ds_addr); sc->alc_cdata.alc_tx_cnt++; ALC_DESC_INC(prod, ALC_TX_RING_CNT); if (m->m_len - hdrlen > 0) { /* Handle remaining payload of the first fragment. */ desc = &sc->alc_rdata.alc_tx_ring[prod]; desc->len = htole32(TX_BYTES((m->m_len - hdrlen) | vtag)); desc->flags = htole32(cflags); desc->addr = htole64(txsegs[0].ds_addr + hdrlen); sc->alc_cdata.alc_tx_cnt++; ALC_DESC_INC(prod, ALC_TX_RING_CNT); } /* Handle remaining fragments. */ idx = 1; } for (; idx < nsegs; idx++) { desc = &sc->alc_rdata.alc_tx_ring[prod]; desc->len = htole32(TX_BYTES(txsegs[idx].ds_len) | vtag); desc->flags = htole32(cflags); desc->addr = htole64(txsegs[idx].ds_addr); sc->alc_cdata.alc_tx_cnt++; ALC_DESC_INC(prod, ALC_TX_RING_CNT); } /* Update producer index. */ sc->alc_cdata.alc_tx_prod = prod; /* Finally set EOP on the last descriptor. */ prod = (prod + ALC_TX_RING_CNT - 1) % ALC_TX_RING_CNT; desc = &sc->alc_rdata.alc_tx_ring[prod]; desc->flags |= htole32(TD_EOP); /* Swap dmamap of the first and the last. */ txd = &sc->alc_cdata.alc_txdesc[prod]; map = txd_last->tx_dmamap; txd_last->tx_dmamap = txd->tx_dmamap; txd->tx_dmamap = map; txd->tx_m = m; return (0); } static void alc_tx_task(void *arg, int pending) { struct ifnet *ifp; ifp = (struct ifnet *)arg; alc_start(ifp); } static void alc_start(struct ifnet *ifp) { struct alc_softc *sc; struct mbuf *m_head; int enq; sc = ifp->if_softc; ALC_LOCK(sc); /* Reclaim transmitted frames. */ if (sc->alc_cdata.alc_tx_cnt >= ALC_TX_DESC_HIWAT) alc_txeof(sc); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->alc_flags & ALC_FLAG_LINK) == 0) { ALC_UNLOCK(sc); return; } for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * Pack the data into the transmit ring. If we * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ if (alc_encap(sc, &m_head)) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ ETHER_BPF_MTAP(ifp, m_head); } if (enq > 0) { /* Sync descriptors. */ bus_dmamap_sync(sc->alc_cdata.alc_tx_ring_tag, sc->alc_cdata.alc_tx_ring_map, BUS_DMASYNC_PREWRITE); /* Kick. Assume we're using normal Tx priority queue. */ CSR_WRITE_4(sc, ALC_MBOX_TD_PROD_IDX, (sc->alc_cdata.alc_tx_prod << MBOX_TD_PROD_LO_IDX_SHIFT) & MBOX_TD_PROD_LO_IDX_MASK); /* Set a timeout in case the chip goes out to lunch. */ sc->alc_watchdog_timer = ALC_TX_TIMEOUT; } ALC_UNLOCK(sc); } static void alc_watchdog(struct alc_softc *sc) { struct ifnet *ifp; ALC_LOCK_ASSERT(sc); if (sc->alc_watchdog_timer == 0 || --sc->alc_watchdog_timer) return; ifp = sc->alc_ifp; if ((sc->alc_flags & ALC_FLAG_LINK) == 0) { if_printf(sc->alc_ifp, "watchdog timeout (lost link)\n"); ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; alc_init_locked(sc); return; } if_printf(sc->alc_ifp, "watchdog timeout -- resetting\n"); ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; alc_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue(sc->alc_tq, &sc->alc_tx_task); } static int alc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct alc_softc *sc; struct ifreq *ifr; struct mii_data *mii; int error, mask; sc = ifp->if_softc; ifr = (struct ifreq *)data; error = 0; switch (cmd) { case SIOCSIFMTU: if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ALC_JUMBO_MTU || ((sc->alc_flags & ALC_FLAG_JUMBO) == 0 && ifr->ifr_mtu > ETHERMTU)) error = EINVAL; else if (ifp->if_mtu != ifr->ifr_mtu) { ALC_LOCK(sc); ifp->if_mtu = ifr->ifr_mtu; /* AR8131/AR8132 has 13 bits MSS field. */ if (ifp->if_mtu > ALC_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; + VLAN_CAPABILITIES(ifp); } ALC_UNLOCK(sc); } break; case SIOCSIFFLAGS: ALC_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && ((ifp->if_flags ^ sc->alc_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) alc_rxfilter(sc); else if ((sc->alc_flags & ALC_FLAG_DETACH) == 0) alc_init_locked(sc); } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) alc_stop(sc); sc->alc_if_flags = ifp->if_flags; ALC_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: ALC_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) alc_rxfilter(sc); ALC_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: mii = device_get_softc(sc->alc_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: ALC_LOCK(sc); mask = ifr->ifr_reqcap ^ ifp->if_capenable; if ((mask & IFCAP_TXCSUM) != 0 && (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { ifp->if_capenable ^= IFCAP_TXCSUM; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) ifp->if_hwassist |= ALC_CSUM_FEATURES; else ifp->if_hwassist &= ~ALC_CSUM_FEATURES; } if ((mask & IFCAP_TSO4) != 0 && (ifp->if_capabilities & IFCAP_TSO4) != 0) { ifp->if_capenable ^= IFCAP_TSO4; if ((ifp->if_capenable & IFCAP_TSO4) != 0) { /* AR8131/AR8132 has 13 bits MSS field. */ if (ifp->if_mtu > ALC_TSO_MTU) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; } else ifp->if_hwassist |= CSUM_TSO; } else ifp->if_hwassist &= ~CSUM_TSO; } if ((mask & IFCAP_WOL_MCAST) != 0 && (ifp->if_capabilities & IFCAP_WOL_MCAST) != 0) ifp->if_capenable ^= IFCAP_WOL_MCAST; if ((mask & IFCAP_WOL_MAGIC) != 0 && (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) ifp->if_capenable ^= IFCAP_WOL_MAGIC; if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; alc_rxvlan(sc); } if ((mask & IFCAP_VLAN_HWCSUM) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; if ((mask & IFCAP_VLAN_HWTSO) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWTSO; - /* - * VLAN hardware tagging is required to do checksum - * offload or TSO on VLAN interface. Checksum offload - * on VLAN interface also requires hardware checksum - * offload of parent interface. - */ - if ((ifp->if_capenable & IFCAP_TXCSUM) == 0) - ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM; if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) ifp->if_capenable &= ~(IFCAP_VLAN_HWTSO | IFCAP_VLAN_HWCSUM); ALC_UNLOCK(sc); VLAN_CAPABILITIES(ifp); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void alc_mac_config(struct alc_softc *sc) { struct mii_data *mii; uint32_t reg; ALC_LOCK_ASSERT(sc); mii = device_get_softc(sc->alc_miibus); reg = CSR_READ_4(sc, ALC_MAC_CFG); reg &= ~(MAC_CFG_FULL_DUPLEX | MAC_CFG_TX_FC | MAC_CFG_RX_FC | MAC_CFG_SPEED_MASK); /* Reprogram MAC with resolved speed/duplex. */ switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: reg |= MAC_CFG_SPEED_10_100; break; case IFM_1000_T: reg |= MAC_CFG_SPEED_1000; break; } if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { reg |= MAC_CFG_FULL_DUPLEX; #ifdef notyet if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) reg |= MAC_CFG_TX_FC; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) reg |= MAC_CFG_RX_FC; #endif } CSR_WRITE_4(sc, ALC_MAC_CFG, reg); } static void alc_stats_clear(struct alc_softc *sc) { struct smb sb, *smb; uint32_t *reg; int i; if ((sc->alc_flags & ALC_FLAG_SMB_BUG) == 0) { bus_dmamap_sync(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); smb = sc->alc_rdata.alc_smb; /* Update done, clear. */ smb->updated = 0; bus_dmamap_sync(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } else { for (reg = &sb.rx_frames, i = 0; reg <= &sb.rx_pkts_filtered; reg++) { CSR_READ_4(sc, ALC_RX_MIB_BASE + i); i += sizeof(uint32_t); } /* Read Tx statistics. */ for (reg = &sb.tx_frames, i = 0; reg <= &sb.tx_mcast_bytes; reg++) { CSR_READ_4(sc, ALC_TX_MIB_BASE + i); i += sizeof(uint32_t); } } } static void alc_stats_update(struct alc_softc *sc) { struct alc_hw_stats *stat; struct smb sb, *smb; struct ifnet *ifp; uint32_t *reg; int i; ALC_LOCK_ASSERT(sc); ifp = sc->alc_ifp; stat = &sc->alc_stats; if ((sc->alc_flags & ALC_FLAG_SMB_BUG) == 0) { bus_dmamap_sync(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); smb = sc->alc_rdata.alc_smb; if (smb->updated == 0) return; } else { smb = &sb; /* Read Rx statistics. */ for (reg = &sb.rx_frames, i = 0; reg <= &sb.rx_pkts_filtered; reg++) { *reg = CSR_READ_4(sc, ALC_RX_MIB_BASE + i); i += sizeof(uint32_t); } /* Read Tx statistics. */ for (reg = &sb.tx_frames, i = 0; reg <= &sb.tx_mcast_bytes; reg++) { *reg = CSR_READ_4(sc, ALC_TX_MIB_BASE + i); i += sizeof(uint32_t); } } /* Rx stats. */ stat->rx_frames += smb->rx_frames; stat->rx_bcast_frames += smb->rx_bcast_frames; stat->rx_mcast_frames += smb->rx_mcast_frames; stat->rx_pause_frames += smb->rx_pause_frames; stat->rx_control_frames += smb->rx_control_frames; stat->rx_crcerrs += smb->rx_crcerrs; stat->rx_lenerrs += smb->rx_lenerrs; stat->rx_bytes += smb->rx_bytes; stat->rx_runts += smb->rx_runts; stat->rx_fragments += smb->rx_fragments; stat->rx_pkts_64 += smb->rx_pkts_64; stat->rx_pkts_65_127 += smb->rx_pkts_65_127; stat->rx_pkts_128_255 += smb->rx_pkts_128_255; stat->rx_pkts_256_511 += smb->rx_pkts_256_511; stat->rx_pkts_512_1023 += smb->rx_pkts_512_1023; stat->rx_pkts_1024_1518 += smb->rx_pkts_1024_1518; stat->rx_pkts_1519_max += smb->rx_pkts_1519_max; stat->rx_pkts_truncated += smb->rx_pkts_truncated; stat->rx_fifo_oflows += smb->rx_fifo_oflows; stat->rx_rrs_errs += smb->rx_rrs_errs; stat->rx_alignerrs += smb->rx_alignerrs; stat->rx_bcast_bytes += smb->rx_bcast_bytes; stat->rx_mcast_bytes += smb->rx_mcast_bytes; stat->rx_pkts_filtered += smb->rx_pkts_filtered; /* Tx stats. */ stat->tx_frames += smb->tx_frames; stat->tx_bcast_frames += smb->tx_bcast_frames; stat->tx_mcast_frames += smb->tx_mcast_frames; stat->tx_pause_frames += smb->tx_pause_frames; stat->tx_excess_defer += smb->tx_excess_defer; stat->tx_control_frames += smb->tx_control_frames; stat->tx_deferred += smb->tx_deferred; stat->tx_bytes += smb->tx_bytes; stat->tx_pkts_64 += smb->tx_pkts_64; stat->tx_pkts_65_127 += smb->tx_pkts_65_127; stat->tx_pkts_128_255 += smb->tx_pkts_128_255; stat->tx_pkts_256_511 += smb->tx_pkts_256_511; stat->tx_pkts_512_1023 += smb->tx_pkts_512_1023; stat->tx_pkts_1024_1518 += smb->tx_pkts_1024_1518; stat->tx_pkts_1519_max += smb->tx_pkts_1519_max; stat->tx_single_colls += smb->tx_single_colls; stat->tx_multi_colls += smb->tx_multi_colls; stat->tx_late_colls += smb->tx_late_colls; stat->tx_excess_colls += smb->tx_excess_colls; stat->tx_abort += smb->tx_abort; stat->tx_underrun += smb->tx_underrun; stat->tx_desc_underrun += smb->tx_desc_underrun; stat->tx_lenerrs += smb->tx_lenerrs; stat->tx_pkts_truncated += smb->tx_pkts_truncated; stat->tx_bcast_bytes += smb->tx_bcast_bytes; stat->tx_mcast_bytes += smb->tx_mcast_bytes; /* Update counters in ifnet. */ ifp->if_opackets += smb->tx_frames; ifp->if_collisions += smb->tx_single_colls + smb->tx_multi_colls * 2 + smb->tx_late_colls + smb->tx_abort * HDPX_CFG_RETRY_DEFAULT; /* * XXX * tx_pkts_truncated counter looks suspicious. It constantly * increments with no sign of Tx errors. This may indicate * the counter name is not correct one so I've removed the * counter in output errors. */ ifp->if_oerrors += smb->tx_abort + smb->tx_late_colls + smb->tx_underrun; ifp->if_ipackets += smb->rx_frames; ifp->if_ierrors += smb->rx_crcerrs + smb->rx_lenerrs + smb->rx_runts + smb->rx_pkts_truncated + smb->rx_fifo_oflows + smb->rx_rrs_errs + smb->rx_alignerrs; if ((sc->alc_flags & ALC_FLAG_SMB_BUG) == 0) { /* Update done, clear. */ smb->updated = 0; bus_dmamap_sync(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } } static int alc_intr(void *arg) { struct alc_softc *sc; uint32_t status; sc = (struct alc_softc *)arg; status = CSR_READ_4(sc, ALC_INTR_STATUS); if ((status & ALC_INTRS) == 0) return (FILTER_STRAY); /* Disable interrupts. */ CSR_WRITE_4(sc, ALC_INTR_STATUS, INTR_DIS_INT); taskqueue_enqueue(sc->alc_tq, &sc->alc_int_task); return (FILTER_HANDLED); } static void alc_int_task(void *arg, int pending) { struct alc_softc *sc; struct ifnet *ifp; uint32_t status; int more; sc = (struct alc_softc *)arg; ifp = sc->alc_ifp; status = CSR_READ_4(sc, ALC_INTR_STATUS); more = atomic_readandclear_int(&sc->alc_morework); if (more != 0) status |= INTR_RX_PKT; if ((status & ALC_INTRS) == 0) goto done; /* Acknowledge interrupts but still disable interrupts. */ CSR_WRITE_4(sc, ALC_INTR_STATUS, status | INTR_DIS_INT); more = 0; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if ((status & INTR_RX_PKT) != 0) { more = alc_rxintr(sc, sc->alc_process_limit); if (more == EAGAIN) atomic_set_int(&sc->alc_morework, 1); else if (more == EIO) { ALC_LOCK(sc); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; alc_init_locked(sc); ALC_UNLOCK(sc); return; } } if ((status & (INTR_DMA_RD_TO_RST | INTR_DMA_WR_TO_RST | INTR_TXQ_TO_RST)) != 0) { if ((status & INTR_DMA_RD_TO_RST) != 0) device_printf(sc->alc_dev, "DMA read error! -- resetting\n"); if ((status & INTR_DMA_WR_TO_RST) != 0) device_printf(sc->alc_dev, "DMA write error! -- resetting\n"); if ((status & INTR_TXQ_TO_RST) != 0) device_printf(sc->alc_dev, "TxQ reset! -- resetting\n"); ALC_LOCK(sc); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; alc_init_locked(sc); ALC_UNLOCK(sc); return; } if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue(sc->alc_tq, &sc->alc_tx_task); } if (more == EAGAIN || (CSR_READ_4(sc, ALC_INTR_STATUS) & ALC_INTRS) != 0) { taskqueue_enqueue(sc->alc_tq, &sc->alc_int_task); return; } done: if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { /* Re-enable interrupts if we're running. */ CSR_WRITE_4(sc, ALC_INTR_STATUS, 0x7FFFFFFF); } } static void alc_txeof(struct alc_softc *sc) { struct ifnet *ifp; struct alc_txdesc *txd; uint32_t cons, prod; int prog; ALC_LOCK_ASSERT(sc); ifp = sc->alc_ifp; if (sc->alc_cdata.alc_tx_cnt == 0) return; bus_dmamap_sync(sc->alc_cdata.alc_tx_ring_tag, sc->alc_cdata.alc_tx_ring_map, BUS_DMASYNC_POSTWRITE); if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0) { bus_dmamap_sync(sc->alc_cdata.alc_cmb_tag, sc->alc_cdata.alc_cmb_map, BUS_DMASYNC_POSTREAD); prod = sc->alc_rdata.alc_cmb->cons; } else prod = CSR_READ_4(sc, ALC_MBOX_TD_CONS_IDX); /* Assume we're using normal Tx priority queue. */ prod = (prod & MBOX_TD_CONS_LO_IDX_MASK) >> MBOX_TD_CONS_LO_IDX_SHIFT; cons = sc->alc_cdata.alc_tx_cons; /* * Go through our Tx list and free mbufs for those * frames which have been transmitted. */ for (prog = 0; cons != prod; prog++, ALC_DESC_INC(cons, ALC_TX_RING_CNT)) { if (sc->alc_cdata.alc_tx_cnt <= 0) break; prog++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->alc_cdata.alc_tx_cnt--; txd = &sc->alc_cdata.alc_txdesc[cons]; if (txd->tx_m != NULL) { /* Reclaim transmitted mbufs. */ bus_dmamap_sync(sc->alc_cdata.alc_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->alc_cdata.alc_tx_tag, txd->tx_dmamap); m_freem(txd->tx_m); txd->tx_m = NULL; } } if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0) bus_dmamap_sync(sc->alc_cdata.alc_cmb_tag, sc->alc_cdata.alc_cmb_map, BUS_DMASYNC_PREREAD); sc->alc_cdata.alc_tx_cons = cons; /* * Unarm watchdog timer only when there is no pending * frames in Tx queue. */ if (sc->alc_cdata.alc_tx_cnt == 0) sc->alc_watchdog_timer = 0; } static int alc_newbuf(struct alc_softc *sc, struct alc_rxdesc *rxd) { struct mbuf *m; bus_dma_segment_t segs[1]; bus_dmamap_t map; int nsegs; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = RX_BUF_SIZE_MAX; #ifndef __NO_STRICT_ALIGNMENT m_adj(m, sizeof(uint64_t)); #endif if (bus_dmamap_load_mbuf_sg(sc->alc_cdata.alc_rx_tag, sc->alc_cdata.alc_rx_sparemap, m, segs, &nsegs, 0) != 0) { m_freem(m); return (ENOBUFS); } KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->alc_cdata.alc_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->alc_cdata.alc_rx_tag, rxd->rx_dmamap); } map = rxd->rx_dmamap; rxd->rx_dmamap = sc->alc_cdata.alc_rx_sparemap; sc->alc_cdata.alc_rx_sparemap = map; bus_dmamap_sync(sc->alc_cdata.alc_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_PREREAD); rxd->rx_m = m; rxd->rx_desc->addr = htole64(segs[0].ds_addr); return (0); } static int alc_rxintr(struct alc_softc *sc, int count) { struct ifnet *ifp; struct rx_rdesc *rrd; uint32_t nsegs, status; int rr_cons, prog; bus_dmamap_sync(sc->alc_cdata.alc_rr_ring_tag, sc->alc_cdata.alc_rr_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_sync(sc->alc_cdata.alc_rx_ring_tag, sc->alc_cdata.alc_rx_ring_map, BUS_DMASYNC_POSTWRITE); rr_cons = sc->alc_cdata.alc_rr_cons; ifp = sc->alc_ifp; for (prog = 0; (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0;) { if (count-- <= 0) break; rrd = &sc->alc_rdata.alc_rr_ring[rr_cons]; status = le32toh(rrd->status); if ((status & RRD_VALID) == 0) break; nsegs = RRD_RD_CNT(le32toh(rrd->rdinfo)); if (nsegs == 0) { /* This should not happen! */ device_printf(sc->alc_dev, "unexpected segment count -- resetting\n"); return (EIO); } alc_rxeof(sc, rrd); /* Clear Rx return status. */ rrd->status = 0; ALC_DESC_INC(rr_cons, ALC_RR_RING_CNT); sc->alc_cdata.alc_rx_cons += nsegs; sc->alc_cdata.alc_rx_cons %= ALC_RR_RING_CNT; prog += nsegs; } if (prog > 0) { /* Update the consumer index. */ sc->alc_cdata.alc_rr_cons = rr_cons; /* Sync Rx return descriptors. */ bus_dmamap_sync(sc->alc_cdata.alc_rr_ring_tag, sc->alc_cdata.alc_rr_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * Sync updated Rx descriptors such that controller see * modified buffer addresses. */ bus_dmamap_sync(sc->alc_cdata.alc_rx_ring_tag, sc->alc_cdata.alc_rx_ring_map, BUS_DMASYNC_PREWRITE); /* * Let controller know availability of new Rx buffers. * Since alc(4) use RXQ_CFG_RD_BURST_DEFAULT descriptors * it may be possible to update ALC_MBOX_RD0_PROD_IDX * only when Rx buffer pre-fetching is required. In * addition we already set ALC_RX_RD_FREE_THRESH to * RX_RD_FREE_THRESH_LO_DEFAULT descriptors. However * it still seems that pre-fetching needs more * experimentation. */ CSR_WRITE_4(sc, ALC_MBOX_RD0_PROD_IDX, sc->alc_cdata.alc_rx_cons); } return (count > 0 ? 0 : EAGAIN); } #ifndef __NO_STRICT_ALIGNMENT static struct mbuf * alc_fixup_rx(struct ifnet *ifp, struct mbuf *m) { struct mbuf *n; int i; uint16_t *src, *dst; src = mtod(m, uint16_t *); dst = src - 3; if (m->m_next == NULL) { for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) *dst++ = *src++; m->m_data -= 6; return (m); } /* * Append a new mbuf to received mbuf chain and copy ethernet * header from the mbuf chain. This can save lots of CPU * cycles for jumbo frame. */ MGETHDR(n, M_DONTWAIT, MT_DATA); if (n == NULL) { ifp->if_iqdrops++; m_freem(m); return (NULL); } bcopy(m->m_data, n->m_data, ETHER_HDR_LEN); m->m_data += ETHER_HDR_LEN; m->m_len -= ETHER_HDR_LEN; n->m_len = ETHER_HDR_LEN; M_MOVE_PKTHDR(n, m); n->m_next = m; return (n); } #endif /* Receive a frame. */ static void alc_rxeof(struct alc_softc *sc, struct rx_rdesc *rrd) { struct alc_rxdesc *rxd; struct ifnet *ifp; struct mbuf *mp, *m; uint32_t rdinfo, status, vtag; int count, nsegs, rx_cons; ifp = sc->alc_ifp; status = le32toh(rrd->status); rdinfo = le32toh(rrd->rdinfo); rx_cons = RRD_RD_IDX(rdinfo); nsegs = RRD_RD_CNT(rdinfo); sc->alc_cdata.alc_rxlen = RRD_BYTES(status); if ((status & (RRD_ERR_SUM | RRD_ERR_LENGTH)) != 0) { /* * We want to pass the following frames to upper * layer regardless of error status of Rx return * ring. * * o IP/TCP/UDP checksum is bad. * o frame length and protocol specific length * does not match. * * Force network stack compute checksum for * errored frames. */ status |= RRD_TCP_UDPCSUM_NOK | RRD_IPCSUM_NOK; if ((RRD_ERR_CRC | RRD_ERR_ALIGN | RRD_ERR_TRUNC | RRD_ERR_RUNT) != 0) return; } for (count = 0; count < nsegs; count++, ALC_DESC_INC(rx_cons, ALC_RX_RING_CNT)) { rxd = &sc->alc_cdata.alc_rxdesc[rx_cons]; mp = rxd->rx_m; /* Add a new receive buffer to the ring. */ if (alc_newbuf(sc, rxd) != 0) { ifp->if_iqdrops++; /* Reuse Rx buffers. */ if (sc->alc_cdata.alc_rxhead != NULL) m_freem(sc->alc_cdata.alc_rxhead); break; } /* * Assume we've received a full sized frame. * Actual size is fixed when we encounter the end of * multi-segmented frame. */ mp->m_len = sc->alc_buf_size; /* Chain received mbufs. */ if (sc->alc_cdata.alc_rxhead == NULL) { sc->alc_cdata.alc_rxhead = mp; sc->alc_cdata.alc_rxtail = mp; } else { mp->m_flags &= ~M_PKTHDR; sc->alc_cdata.alc_rxprev_tail = sc->alc_cdata.alc_rxtail; sc->alc_cdata.alc_rxtail->m_next = mp; sc->alc_cdata.alc_rxtail = mp; } if (count == nsegs - 1) { /* Last desc. for this frame. */ m = sc->alc_cdata.alc_rxhead; m->m_flags |= M_PKTHDR; /* * It seems that L1C/L2C controller has no way * to tell hardware to strip CRC bytes. */ m->m_pkthdr.len = sc->alc_cdata.alc_rxlen - ETHER_CRC_LEN; if (nsegs > 1) { /* Set last mbuf size. */ mp->m_len = sc->alc_cdata.alc_rxlen - (nsegs - 1) * sc->alc_buf_size; /* Remove the CRC bytes in chained mbufs. */ if (mp->m_len <= ETHER_CRC_LEN) { sc->alc_cdata.alc_rxtail = sc->alc_cdata.alc_rxprev_tail; sc->alc_cdata.alc_rxtail->m_len -= (ETHER_CRC_LEN - mp->m_len); sc->alc_cdata.alc_rxtail->m_next = NULL; m_freem(mp); } else { mp->m_len -= ETHER_CRC_LEN; } } else m->m_len = m->m_pkthdr.len; m->m_pkthdr.rcvif = ifp; /* * Due to hardware bugs, Rx checksum offloading * was intentionally disabled. */ if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && (status & RRD_VLAN_TAG) != 0) { vtag = RRD_VLAN(le32toh(rrd->vtag)); m->m_pkthdr.ether_vtag = ntohs(vtag); m->m_flags |= M_VLANTAG; } #ifndef __NO_STRICT_ALIGNMENT m = alc_fixup_rx(ifp, m); if (m != NULL) #endif { /* Pass it on. */ (*ifp->if_input)(ifp, m); } } } /* Reset mbuf chains. */ ALC_RXCHAIN_RESET(sc); } static void alc_tick(void *arg) { struct alc_softc *sc; struct mii_data *mii; sc = (struct alc_softc *)arg; ALC_LOCK_ASSERT(sc); mii = device_get_softc(sc->alc_miibus); mii_tick(mii); alc_stats_update(sc); /* * alc(4) does not rely on Tx completion interrupts to reclaim * transferred buffers. Instead Tx completion interrupts are * used to hint for scheduling Tx task. So it's necessary to * release transmitted buffers by kicking Tx completion * handler. This limits the maximum reclamation delay to a hz. */ alc_txeof(sc); alc_watchdog(sc); callout_reset(&sc->alc_tick_ch, hz, alc_tick, sc); } static void alc_reset(struct alc_softc *sc) { uint32_t reg; int i; CSR_WRITE_4(sc, ALC_MASTER_CFG, MASTER_RESET); for (i = ALC_RESET_TIMEOUT; i > 0; i--) { DELAY(10); if ((CSR_READ_4(sc, ALC_MASTER_CFG) & MASTER_RESET) == 0) break; } if (i == 0) device_printf(sc->alc_dev, "master reset timeout!\n"); for (i = ALC_RESET_TIMEOUT; i > 0; i--) { if ((reg = CSR_READ_4(sc, ALC_IDLE_STATUS)) == 0) break; DELAY(10); } if (i == 0) device_printf(sc->alc_dev, "reset timeout(0x%08x)!\n", reg); } static void alc_init(void *xsc) { struct alc_softc *sc; sc = (struct alc_softc *)xsc; ALC_LOCK(sc); alc_init_locked(sc); ALC_UNLOCK(sc); } static void alc_init_locked(struct alc_softc *sc) { struct ifnet *ifp; struct mii_data *mii; uint8_t eaddr[ETHER_ADDR_LEN]; bus_addr_t paddr; uint32_t reg, rxf_hi, rxf_lo; ALC_LOCK_ASSERT(sc); ifp = sc->alc_ifp; mii = device_get_softc(sc->alc_miibus); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* * Cancel any pending I/O. */ alc_stop(sc); /* * Reset the chip to a known state. */ alc_reset(sc); /* Initialize Rx descriptors. */ if (alc_init_rx_ring(sc) != 0) { device_printf(sc->alc_dev, "no memory for Rx buffers.\n"); alc_stop(sc); return; } alc_init_rr_ring(sc); alc_init_tx_ring(sc); alc_init_cmb(sc); alc_init_smb(sc); /* Reprogram the station address. */ bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); CSR_WRITE_4(sc, ALC_PAR0, eaddr[2] << 24 | eaddr[3] << 16 | eaddr[4] << 8 | eaddr[5]); CSR_WRITE_4(sc, ALC_PAR1, eaddr[0] << 8 | eaddr[1]); /* * Clear WOL status and disable all WOL feature as WOL * would interfere Rx operation under normal environments. */ CSR_READ_4(sc, ALC_WOL_CFG); CSR_WRITE_4(sc, ALC_WOL_CFG, 0); /* Set Tx descriptor base addresses. */ paddr = sc->alc_rdata.alc_tx_ring_paddr; CSR_WRITE_4(sc, ALC_TX_BASE_ADDR_HI, ALC_ADDR_HI(paddr)); CSR_WRITE_4(sc, ALC_TDL_HEAD_ADDR_LO, ALC_ADDR_LO(paddr)); /* We don't use high priority ring. */ CSR_WRITE_4(sc, ALC_TDH_HEAD_ADDR_LO, 0); /* Set Tx descriptor counter. */ CSR_WRITE_4(sc, ALC_TD_RING_CNT, (ALC_TX_RING_CNT << TD_RING_CNT_SHIFT) & TD_RING_CNT_MASK); /* Set Rx descriptor base addresses. */ paddr = sc->alc_rdata.alc_rx_ring_paddr; CSR_WRITE_4(sc, ALC_RX_BASE_ADDR_HI, ALC_ADDR_HI(paddr)); CSR_WRITE_4(sc, ALC_RD0_HEAD_ADDR_LO, ALC_ADDR_LO(paddr)); /* We use one Rx ring. */ CSR_WRITE_4(sc, ALC_RD1_HEAD_ADDR_LO, 0); CSR_WRITE_4(sc, ALC_RD2_HEAD_ADDR_LO, 0); CSR_WRITE_4(sc, ALC_RD3_HEAD_ADDR_LO, 0); /* Set Rx descriptor counter. */ CSR_WRITE_4(sc, ALC_RD_RING_CNT, (ALC_RX_RING_CNT << RD_RING_CNT_SHIFT) & RD_RING_CNT_MASK); /* * Let hardware split jumbo frames into alc_max_buf_sized chunks. * if it do not fit the buffer size. Rx return descriptor holds * a counter that indicates how many fragments were made by the * hardware. The buffer size should be multiple of 8 bytes. * Since hardware has limit on the size of buffer size, always * use the maximum value. * For strict-alignment architectures make sure to reduce buffer * size by 8 bytes to make room for alignment fixup. */ #ifndef __NO_STRICT_ALIGNMENT sc->alc_buf_size = RX_BUF_SIZE_MAX - sizeof(uint64_t); #else sc->alc_buf_size = RX_BUF_SIZE_MAX; #endif CSR_WRITE_4(sc, ALC_RX_BUF_SIZE, sc->alc_buf_size); paddr = sc->alc_rdata.alc_rr_ring_paddr; /* Set Rx return descriptor base addresses. */ CSR_WRITE_4(sc, ALC_RRD0_HEAD_ADDR_LO, ALC_ADDR_LO(paddr)); /* We use one Rx return ring. */ CSR_WRITE_4(sc, ALC_RRD1_HEAD_ADDR_LO, 0); CSR_WRITE_4(sc, ALC_RRD2_HEAD_ADDR_LO, 0); CSR_WRITE_4(sc, ALC_RRD3_HEAD_ADDR_LO, 0); /* Set Rx return descriptor counter. */ CSR_WRITE_4(sc, ALC_RRD_RING_CNT, (ALC_RR_RING_CNT << RRD_RING_CNT_SHIFT) & RRD_RING_CNT_MASK); paddr = sc->alc_rdata.alc_cmb_paddr; CSR_WRITE_4(sc, ALC_CMB_BASE_ADDR_LO, ALC_ADDR_LO(paddr)); paddr = sc->alc_rdata.alc_smb_paddr; CSR_WRITE_4(sc, ALC_SMB_BASE_ADDR_HI, ALC_ADDR_HI(paddr)); CSR_WRITE_4(sc, ALC_SMB_BASE_ADDR_LO, ALC_ADDR_LO(paddr)); /* Tell hardware that we're ready to load DMA blocks. */ CSR_WRITE_4(sc, ALC_DMA_BLOCK, DMA_BLOCK_LOAD); /* Configure interrupt moderation timer. */ reg = ALC_USECS(sc->alc_int_rx_mod) << IM_TIMER_RX_SHIFT; reg |= ALC_USECS(sc->alc_int_tx_mod) << IM_TIMER_TX_SHIFT; CSR_WRITE_4(sc, ALC_IM_TIMER, reg); reg = CSR_READ_4(sc, ALC_MASTER_CFG); reg &= ~(MASTER_CHIP_REV_MASK | MASTER_CHIP_ID_MASK); /* * We don't want to automatic interrupt clear as task queue * for the interrupt should know interrupt status. */ reg &= ~MASTER_INTR_RD_CLR; reg &= ~(MASTER_IM_RX_TIMER_ENB | MASTER_IM_TX_TIMER_ENB); if (ALC_USECS(sc->alc_int_rx_mod) != 0) reg |= MASTER_IM_RX_TIMER_ENB; if (ALC_USECS(sc->alc_int_tx_mod) != 0) reg |= MASTER_IM_TX_TIMER_ENB; CSR_WRITE_4(sc, ALC_MASTER_CFG, reg); /* * Disable interrupt re-trigger timer. We don't want automatic * re-triggering of un-ACKed interrupts. */ CSR_WRITE_4(sc, ALC_INTR_RETRIG_TIMER, ALC_USECS(0)); /* Configure CMB. */ CSR_WRITE_4(sc, ALC_CMB_TD_THRESH, 4); if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0) CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, ALC_USECS(5000)); else CSR_WRITE_4(sc, ALC_CMB_TX_TIMER, ALC_USECS(0)); /* * Hardware can be configured to issue SMB interrupt based * on programmed interval. Since there is a callout that is * invoked for every hz in driver we use that instead of * relying on periodic SMB interrupt. */ CSR_WRITE_4(sc, ALC_SMB_STAT_TIMER, ALC_USECS(0)); /* Clear MAC statistics. */ alc_stats_clear(sc); /* * Always use maximum frame size that controller can support. * Otherwise received frames that has larger frame length * than alc(4) MTU would be silently dropped in hardware. This * would make path-MTU discovery hard as sender wouldn't get * any responses from receiver. alc(4) supports * multi-fragmented frames on Rx path so it has no issue on * assembling fragmented frames. Using maximum frame size also * removes the need to reinitialize hardware when interface * MTU configuration was changed. * * Be conservative in what you do, be liberal in what you * accept from others - RFC 793. */ CSR_WRITE_4(sc, ALC_FRAME_SIZE, ALC_JUMBO_FRAMELEN); /* Disable header split(?) */ CSR_WRITE_4(sc, ALC_HDS_CFG, 0); /* Configure IPG/IFG parameters. */ CSR_WRITE_4(sc, ALC_IPG_IFG_CFG, ((IPG_IFG_IPGT_DEFAULT << IPG_IFG_IPGT_SHIFT) & IPG_IFG_IPGT_MASK) | ((IPG_IFG_MIFG_DEFAULT << IPG_IFG_MIFG_SHIFT) & IPG_IFG_MIFG_MASK) | ((IPG_IFG_IPG1_DEFAULT << IPG_IFG_IPG1_SHIFT) & IPG_IFG_IPG1_MASK) | ((IPG_IFG_IPG2_DEFAULT << IPG_IFG_IPG2_SHIFT) & IPG_IFG_IPG2_MASK)); /* Set parameters for half-duplex media. */ CSR_WRITE_4(sc, ALC_HDPX_CFG, ((HDPX_CFG_LCOL_DEFAULT << HDPX_CFG_LCOL_SHIFT) & HDPX_CFG_LCOL_MASK) | ((HDPX_CFG_RETRY_DEFAULT << HDPX_CFG_RETRY_SHIFT) & HDPX_CFG_RETRY_MASK) | HDPX_CFG_EXC_DEF_EN | ((HDPX_CFG_ABEBT_DEFAULT << HDPX_CFG_ABEBT_SHIFT) & HDPX_CFG_ABEBT_MASK) | ((HDPX_CFG_JAMIPG_DEFAULT << HDPX_CFG_JAMIPG_SHIFT) & HDPX_CFG_JAMIPG_MASK)); /* * Set TSO/checksum offload threshold. For frames that is * larger than this threshold, hardware wouldn't do * TSO/checksum offloading. */ CSR_WRITE_4(sc, ALC_TSO_OFFLOAD_THRESH, (ALC_JUMBO_FRAMELEN >> TSO_OFFLOAD_THRESH_UNIT_SHIFT) & TSO_OFFLOAD_THRESH_MASK); /* Configure TxQ. */ reg = (alc_dma_burst[sc->alc_dma_rd_burst] << TXQ_CFG_TX_FIFO_BURST_SHIFT) & TXQ_CFG_TX_FIFO_BURST_MASK; reg |= (TXQ_CFG_TD_BURST_DEFAULT << TXQ_CFG_TD_BURST_SHIFT) & TXQ_CFG_TD_BURST_MASK; CSR_WRITE_4(sc, ALC_TXQ_CFG, reg | TXQ_CFG_ENHANCED_MODE); /* Configure Rx free descriptor pre-fetching. */ CSR_WRITE_4(sc, ALC_RX_RD_FREE_THRESH, ((RX_RD_FREE_THRESH_HI_DEFAULT << RX_RD_FREE_THRESH_HI_SHIFT) & RX_RD_FREE_THRESH_HI_MASK) | ((RX_RD_FREE_THRESH_LO_DEFAULT << RX_RD_FREE_THRESH_LO_SHIFT) & RX_RD_FREE_THRESH_LO_MASK)); /* * Configure flow control parameters. * XON : 80% of Rx FIFO * XOFF : 30% of Rx FIFO */ reg = CSR_READ_4(sc, ALC_SRAM_RX_FIFO_LEN); rxf_hi = (reg * 8) / 10; rxf_lo = (reg * 3)/ 10; CSR_WRITE_4(sc, ALC_RX_FIFO_PAUSE_THRESH, ((rxf_lo << RX_FIFO_PAUSE_THRESH_LO_SHIFT) & RX_FIFO_PAUSE_THRESH_LO_MASK) | ((rxf_hi << RX_FIFO_PAUSE_THRESH_HI_SHIFT) & RX_FIFO_PAUSE_THRESH_HI_MASK)); /* Disable RSS until I understand L1C/L2C's RSS logic. */ CSR_WRITE_4(sc, ALC_RSS_IDT_TABLE0, 0); CSR_WRITE_4(sc, ALC_RSS_CPU, 0); /* Configure RxQ. */ reg = (RXQ_CFG_RD_BURST_DEFAULT << RXQ_CFG_RD_BURST_SHIFT) & RXQ_CFG_RD_BURST_MASK; reg |= RXQ_CFG_RSS_MODE_DIS; if ((sc->alc_flags & ALC_FLAG_ASPM_MON) != 0) reg |= RXQ_CFG_ASPM_THROUGHPUT_LIMIT_100M; CSR_WRITE_4(sc, ALC_RXQ_CFG, reg); /* Configure Rx DMAW request thresold. */ CSR_WRITE_4(sc, ALC_RD_DMA_CFG, ((RD_DMA_CFG_THRESH_DEFAULT << RD_DMA_CFG_THRESH_SHIFT) & RD_DMA_CFG_THRESH_MASK) | ((ALC_RD_DMA_CFG_USECS(0) << RD_DMA_CFG_TIMER_SHIFT) & RD_DMA_CFG_TIMER_MASK)); /* Configure DMA parameters. */ reg = DMA_CFG_OUT_ORDER | DMA_CFG_RD_REQ_PRI; reg |= sc->alc_rcb; if ((sc->alc_flags & ALC_FLAG_CMB_BUG) == 0) reg |= DMA_CFG_CMB_ENB; if ((sc->alc_flags & ALC_FLAG_SMB_BUG) == 0) reg |= DMA_CFG_SMB_ENB; else reg |= DMA_CFG_SMB_DIS; reg |= (sc->alc_dma_rd_burst & DMA_CFG_RD_BURST_MASK) << DMA_CFG_RD_BURST_SHIFT; reg |= (sc->alc_dma_wr_burst & DMA_CFG_WR_BURST_MASK) << DMA_CFG_WR_BURST_SHIFT; reg |= (DMA_CFG_RD_DELAY_CNT_DEFAULT << DMA_CFG_RD_DELAY_CNT_SHIFT) & DMA_CFG_RD_DELAY_CNT_MASK; reg |= (DMA_CFG_WR_DELAY_CNT_DEFAULT << DMA_CFG_WR_DELAY_CNT_SHIFT) & DMA_CFG_WR_DELAY_CNT_MASK; CSR_WRITE_4(sc, ALC_DMA_CFG, reg); /* * Configure Tx/Rx MACs. * - Auto-padding for short frames. * - Enable CRC generation. * Actual reconfiguration of MAC for resolved speed/duplex * is followed after detection of link establishment. * AR8131/AR8132 always does checksum computation regardless * of MAC_CFG_RXCSUM_ENB bit. Also the controller is known to * have bug in protocol field in Rx return structure so * these controllers can't handle fragmented frames. Disable * Rx checksum offloading until there is a newer controller * that has sane implementation. */ reg = MAC_CFG_TX_CRC_ENB | MAC_CFG_TX_AUTO_PAD | MAC_CFG_FULL_DUPLEX | ((MAC_CFG_PREAMBLE_DEFAULT << MAC_CFG_PREAMBLE_SHIFT) & MAC_CFG_PREAMBLE_MASK); if ((sc->alc_flags & ALC_FLAG_FASTETHER) != 0) reg |= MAC_CFG_SPEED_10_100; else reg |= MAC_CFG_SPEED_1000; CSR_WRITE_4(sc, ALC_MAC_CFG, reg); /* Set up the receive filter. */ alc_rxfilter(sc); alc_rxvlan(sc); /* Acknowledge all pending interrupts and clear it. */ CSR_WRITE_4(sc, ALC_INTR_MASK, ALC_INTRS); CSR_WRITE_4(sc, ALC_INTR_STATUS, 0xFFFFFFFF); CSR_WRITE_4(sc, ALC_INTR_STATUS, 0); sc->alc_flags &= ~ALC_FLAG_LINK; /* Switch to the current media. */ mii_mediachg(mii); callout_reset(&sc->alc_tick_ch, hz, alc_tick, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void alc_stop(struct alc_softc *sc) { struct ifnet *ifp; struct alc_txdesc *txd; struct alc_rxdesc *rxd; uint32_t reg; int i; ALC_LOCK_ASSERT(sc); /* * Mark the interface down and cancel the watchdog timer. */ ifp = sc->alc_ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->alc_flags &= ~ALC_FLAG_LINK; callout_stop(&sc->alc_tick_ch); sc->alc_watchdog_timer = 0; alc_stats_update(sc); /* Disable interrupts. */ CSR_WRITE_4(sc, ALC_INTR_MASK, 0); CSR_WRITE_4(sc, ALC_INTR_STATUS, 0xFFFFFFFF); alc_stop_queue(sc); /* Disable DMA. */ reg = CSR_READ_4(sc, ALC_DMA_CFG); reg &= ~(DMA_CFG_CMB_ENB | DMA_CFG_SMB_ENB); reg |= DMA_CFG_SMB_DIS; CSR_WRITE_4(sc, ALC_DMA_CFG, reg); DELAY(1000); /* Stop Rx/Tx MACs. */ alc_stop_mac(sc); /* Disable interrupts which might be touched in taskq handler. */ CSR_WRITE_4(sc, ALC_INTR_STATUS, 0xFFFFFFFF); /* Reclaim Rx buffers that have been processed. */ if (sc->alc_cdata.alc_rxhead != NULL) m_freem(sc->alc_cdata.alc_rxhead); ALC_RXCHAIN_RESET(sc); /* * Free Tx/Rx mbufs still in the queues. */ for (i = 0; i < ALC_RX_RING_CNT; i++) { rxd = &sc->alc_cdata.alc_rxdesc[i]; if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->alc_cdata.alc_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->alc_cdata.alc_rx_tag, rxd->rx_dmamap); m_freem(rxd->rx_m); rxd->rx_m = NULL; } } for (i = 0; i < ALC_TX_RING_CNT; i++) { txd = &sc->alc_cdata.alc_txdesc[i]; if (txd->tx_m != NULL) { bus_dmamap_sync(sc->alc_cdata.alc_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->alc_cdata.alc_tx_tag, txd->tx_dmamap); m_freem(txd->tx_m); txd->tx_m = NULL; } } } static void alc_stop_mac(struct alc_softc *sc) { uint32_t reg; int i; ALC_LOCK_ASSERT(sc); /* Disable Rx/Tx MAC. */ reg = CSR_READ_4(sc, ALC_MAC_CFG); if ((reg & (MAC_CFG_TX_ENB | MAC_CFG_RX_ENB)) != 0) { reg &= ~MAC_CFG_TX_ENB | MAC_CFG_RX_ENB; CSR_WRITE_4(sc, ALC_MAC_CFG, reg); } for (i = ALC_TIMEOUT; i > 0; i--) { reg = CSR_READ_4(sc, ALC_IDLE_STATUS); if (reg == 0) break; DELAY(10); } if (i == 0) device_printf(sc->alc_dev, "could not disable Rx/Tx MAC(0x%08x)!\n", reg); } static void alc_start_queue(struct alc_softc *sc) { uint32_t qcfg[] = { 0, RXQ_CFG_QUEUE0_ENB, RXQ_CFG_QUEUE0_ENB | RXQ_CFG_QUEUE1_ENB, RXQ_CFG_QUEUE0_ENB | RXQ_CFG_QUEUE1_ENB | RXQ_CFG_QUEUE2_ENB, RXQ_CFG_ENB }; uint32_t cfg; ALC_LOCK_ASSERT(sc); /* Enable RxQ. */ cfg = CSR_READ_4(sc, ALC_RXQ_CFG); cfg &= ~RXQ_CFG_ENB; cfg |= qcfg[1]; CSR_WRITE_4(sc, ALC_RXQ_CFG, cfg); /* Enable TxQ. */ cfg = CSR_READ_4(sc, ALC_TXQ_CFG); cfg |= TXQ_CFG_ENB; CSR_WRITE_4(sc, ALC_TXQ_CFG, cfg); } static void alc_stop_queue(struct alc_softc *sc) { uint32_t reg; int i; ALC_LOCK_ASSERT(sc); /* Disable RxQ. */ reg = CSR_READ_4(sc, ALC_RXQ_CFG); if ((reg & RXQ_CFG_ENB) != 0) { reg &= ~RXQ_CFG_ENB; CSR_WRITE_4(sc, ALC_RXQ_CFG, reg); } /* Disable TxQ. */ reg = CSR_READ_4(sc, ALC_TXQ_CFG); if ((reg & TXQ_CFG_ENB) == 0) { reg &= ~TXQ_CFG_ENB; CSR_WRITE_4(sc, ALC_TXQ_CFG, reg); } for (i = ALC_TIMEOUT; i > 0; i--) { reg = CSR_READ_4(sc, ALC_IDLE_STATUS); if ((reg & (IDLE_STATUS_RXQ | IDLE_STATUS_TXQ)) == 0) break; DELAY(10); } if (i == 0) device_printf(sc->alc_dev, "could not disable RxQ/TxQ (0x%08x)!\n", reg); } static void alc_init_tx_ring(struct alc_softc *sc) { struct alc_ring_data *rd; struct alc_txdesc *txd; int i; ALC_LOCK_ASSERT(sc); sc->alc_cdata.alc_tx_prod = 0; sc->alc_cdata.alc_tx_cons = 0; sc->alc_cdata.alc_tx_cnt = 0; rd = &sc->alc_rdata; bzero(rd->alc_tx_ring, ALC_TX_RING_SZ); for (i = 0; i < ALC_TX_RING_CNT; i++) { txd = &sc->alc_cdata.alc_txdesc[i]; txd->tx_m = NULL; } bus_dmamap_sync(sc->alc_cdata.alc_tx_ring_tag, sc->alc_cdata.alc_tx_ring_map, BUS_DMASYNC_PREWRITE); } static int alc_init_rx_ring(struct alc_softc *sc) { struct alc_ring_data *rd; struct alc_rxdesc *rxd; int i; ALC_LOCK_ASSERT(sc); sc->alc_cdata.alc_rx_cons = ALC_RX_RING_CNT - 1; sc->alc_morework = 0; rd = &sc->alc_rdata; bzero(rd->alc_rx_ring, ALC_RX_RING_SZ); for (i = 0; i < ALC_RX_RING_CNT; i++) { rxd = &sc->alc_cdata.alc_rxdesc[i]; rxd->rx_m = NULL; rxd->rx_desc = &rd->alc_rx_ring[i]; if (alc_newbuf(sc, rxd) != 0) return (ENOBUFS); } /* * Since controller does not update Rx descriptors, driver * does have to read Rx descriptors back so BUS_DMASYNC_PREWRITE * is enough to ensure coherence. */ bus_dmamap_sync(sc->alc_cdata.alc_rx_ring_tag, sc->alc_cdata.alc_rx_ring_map, BUS_DMASYNC_PREWRITE); /* Let controller know availability of new Rx buffers. */ CSR_WRITE_4(sc, ALC_MBOX_RD0_PROD_IDX, sc->alc_cdata.alc_rx_cons); return (0); } static void alc_init_rr_ring(struct alc_softc *sc) { struct alc_ring_data *rd; ALC_LOCK_ASSERT(sc); sc->alc_cdata.alc_rr_cons = 0; ALC_RXCHAIN_RESET(sc); rd = &sc->alc_rdata; bzero(rd->alc_rr_ring, ALC_RR_RING_SZ); bus_dmamap_sync(sc->alc_cdata.alc_rr_ring_tag, sc->alc_cdata.alc_rr_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static void alc_init_cmb(struct alc_softc *sc) { struct alc_ring_data *rd; ALC_LOCK_ASSERT(sc); rd = &sc->alc_rdata; bzero(rd->alc_cmb, ALC_CMB_SZ); bus_dmamap_sync(sc->alc_cdata.alc_cmb_tag, sc->alc_cdata.alc_cmb_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static void alc_init_smb(struct alc_softc *sc) { struct alc_ring_data *rd; ALC_LOCK_ASSERT(sc); rd = &sc->alc_rdata; bzero(rd->alc_smb, ALC_SMB_SZ); bus_dmamap_sync(sc->alc_cdata.alc_smb_tag, sc->alc_cdata.alc_smb_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static void alc_rxvlan(struct alc_softc *sc) { struct ifnet *ifp; uint32_t reg; ALC_LOCK_ASSERT(sc); ifp = sc->alc_ifp; reg = CSR_READ_4(sc, ALC_MAC_CFG); if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) reg |= MAC_CFG_VLAN_TAG_STRIP; else reg &= ~MAC_CFG_VLAN_TAG_STRIP; CSR_WRITE_4(sc, ALC_MAC_CFG, reg); } static void alc_rxfilter(struct alc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; uint32_t crc; uint32_t mchash[2]; uint32_t rxcfg; ALC_LOCK_ASSERT(sc); ifp = sc->alc_ifp; bzero(mchash, sizeof(mchash)); rxcfg = CSR_READ_4(sc, ALC_MAC_CFG); rxcfg &= ~(MAC_CFG_ALLMULTI | MAC_CFG_BCAST | MAC_CFG_PROMISC); if ((ifp->if_flags & IFF_BROADCAST) != 0) rxcfg |= MAC_CFG_BCAST; if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { if ((ifp->if_flags & IFF_PROMISC) != 0) rxcfg |= MAC_CFG_PROMISC; if ((ifp->if_flags & IFF_ALLMULTI) != 0) rxcfg |= MAC_CFG_ALLMULTI; mchash[0] = 0xFFFFFFFF; mchash[1] = 0xFFFFFFFF; goto chipit; } if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &sc->alc_ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; crc = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN); mchash[crc >> 31] |= 1 << ((crc >> 26) & 0x1f); } if_maddr_runlock(ifp); chipit: CSR_WRITE_4(sc, ALC_MAR0, mchash[0]); CSR_WRITE_4(sc, ALC_MAR1, mchash[1]); CSR_WRITE_4(sc, ALC_MAC_CFG, rxcfg); } static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) { int error, value; if (arg1 == NULL) return (EINVAL); value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if (error || req->newptr == NULL) return (error); if (value < low || value > high) return (EINVAL); *(int *)arg1 = value; return (0); } static int sysctl_hw_alc_proc_limit(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, ALC_PROC_MIN, ALC_PROC_MAX)); } static int sysctl_hw_alc_int_mod(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, ALC_IM_TIMER_MIN, ALC_IM_TIMER_MAX)); } Index: projects/ppc64/sys/dev/ata/ata-all.c =================================================================== --- projects/ppc64/sys/dev/ata/ata-all.c (revision 204271) +++ projects/ppc64/sys/dev/ata/ata-all.c (revision 204272) @@ -1,1734 +1,1734 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * 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, immediately at the beginning of the file. * 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 "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATA_CAM #include #include #include #include #include #endif #ifndef ATA_CAM /* device structure */ static d_ioctl_t ata_ioctl; static struct cdevsw ata_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, /* we need this as newbus isn't mpsafe */ .d_ioctl = ata_ioctl, .d_name = "ata", }; #endif /* prototypes */ #ifndef ATA_CAM static void ata_boot_attach(void); static device_t ata_add_child(device_t, struct ata_device *, int); #else static void ataaction(struct cam_sim *sim, union ccb *ccb); static void atapoll(struct cam_sim *sim); #endif static void ata_conn_event(void *, int); static void bswap(int8_t *, int); static void btrim(int8_t *, int); static void bpack(int8_t *, int8_t *, int); static void ata_interrupt_locked(void *data); /* global vars */ MALLOC_DEFINE(M_ATA, "ata_generic", "ATA driver generic layer"); int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data) = NULL; struct intr_config_hook *ata_delayed_attach = NULL; devclass_t ata_devclass; uma_zone_t ata_request_zone; uma_zone_t ata_composite_zone; int ata_wc = 1; int ata_setmax = 0; int ata_dma_check_80pin = 1; /* local vars */ static int ata_dma = 1; static int atapi_dma = 1; /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); TUNABLE_INT("hw.ata.ata_dma", &ata_dma); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RDTUN, &ata_dma, 0, "ATA disk DMA mode control"); TUNABLE_INT("hw.ata.ata_dma_check_80pin", &ata_dma_check_80pin); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma_check_80pin, CTLFLAG_RDTUN, &ata_dma_check_80pin, 1, "Check for 80pin cable before setting ATA DMA mode"); TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma); SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0, "ATAPI device DMA mode control"); TUNABLE_INT("hw.ata.wc", &ata_wc); SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RDTUN, &ata_wc, 0, "ATA disk write caching"); TUNABLE_INT("hw.ata.setmax", &ata_setmax); SYSCTL_INT(_hw_ata, OID_AUTO, setmax, CTLFLAG_RDTUN, &ata_setmax, 0, "ATA disk set max native address"); /* * newbus device interface related functions */ int ata_probe(device_t dev) { return 0; } int ata_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); int error, rid; #ifdef ATA_CAM struct cam_devq *devq; int i; #endif /* check that we have a virgin channel to attach */ if (ch->r_irq) return EEXIST; /* initialize the softc basics */ ch->dev = dev; ch->state = ATA_IDLE; bzero(&ch->state_mtx, sizeof(struct mtx)); mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); bzero(&ch->queue_mtx, sizeof(struct mtx)); mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); TAILQ_INIT(&ch->ata_queue); TASK_INIT(&ch->conntask, 0, ata_conn_event, dev); #ifdef ATA_CAM for (i = 0; i < 16; i++) { ch->user[i].mode = 0; if (ch->flags & ATA_SATA) ch->user[i].bytecount = 8192; else ch->user[i].bytecount = MAXPHYS; ch->curr[i] = ch->user[i]; } #endif /* reset the controller HW, the channel and device(s) */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("ataatch", 1); #ifndef ATA_CAM ATA_RESET(dev); #endif ATA_LOCKING(dev, ATA_LF_UNLOCK); /* allocate DMA resources if DMA HW present*/ if (ch->dma.alloc) ch->dma.alloc(dev); /* setup interrupt delivery */ rid = ATA_IRQ_RID; ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (!ch->r_irq) { device_printf(dev, "unable to allocate interrupt\n"); return ENXIO; } if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, ata_interrupt, ch, &ch->ih))) { device_printf(dev, "unable to setup interrupt\n"); return error; } #ifndef ATA_CAM /* probe and attach devices on this channel unless we are in early boot */ if (!ata_delayed_attach) ata_identify(dev); return (0); #else mtx_lock(&ch->state_mtx); /* Create the device queue for our SIM. */ devq = cam_simq_alloc(1); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(ataaction, atapoll, "ata", ch, device_get_unit(dev), &ch->state_mtx, 1, 0, devq); if (ch->sim == NULL) { device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err2; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } mtx_unlock(&ch->state_mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); mtx_unlock(&ch->state_mtx); return (error); #endif } int ata_detach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); #ifndef ATA_CAM device_t *children; int nchildren, i; #endif /* check that we have a valid channel to detach */ if (!ch->r_irq) return ENXIO; /* grap the channel lock so no new requests gets launched */ mtx_lock(&ch->state_mtx); ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); #ifndef ATA_CAM /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } #endif taskqueue_drain(taskqueue_thread, &ch->conntask); #ifdef ATA_CAM mtx_lock(&ch->state_mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->state_mtx); #endif /* release resources */ bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ch->r_irq = NULL; /* free DMA resources if DMA HW present*/ if (ch->dma.free) ch->dma.free(dev); mtx_destroy(&ch->state_mtx); mtx_destroy(&ch->queue_mtx); return 0; } static void ata_conn_event(void *context, int dummy) { device_t dev = (device_t)context; - struct ata_channel *ch = device_get_softc(dev); #ifdef ATA_CAM + struct ata_channel *ch = device_get_softc(dev); union ccb *ccb; -#endif mtx_lock(&ch->state_mtx); ata_reinit(dev); mtx_unlock(&ch->state_mtx); -#ifdef ATA_CAM if ((ccb = xpt_alloc_ccb()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); +#else + ata_reinit(dev); #endif } int ata_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; #ifndef ATA_CAM device_t *children; int nchildren, i; /* check that we have a valid channel to reinit */ if (!ch || !ch->r_irq) return ENXIO; if (bootverbose) device_printf(dev, "reiniting channel ..\n"); /* poll for locking the channel */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("atarini", 1); /* catch eventual request in ch->running */ mtx_lock(&ch->state_mtx); if (ch->state & ATA_STALL_QUEUE) { /* Recursive reinits and reinits during detach prohobited. */ mtx_unlock(&ch->state_mtx); return (ENXIO); } if ((request = ch->running)) callout_stop(&request->callout); ch->running = NULL; /* unconditionally grap the channel lock */ ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* reinit the children and delete any that fails */ if (!device_get_children(dev, &children, &nchildren)) { mtx_lock(&Giant); /* newbus suckage it needs Giant */ for (i = 0; i < nchildren; i++) { /* did any children go missing ? */ if (children[i] && device_is_attached(children[i]) && ATA_REINIT(children[i])) { /* * if we had a running request and its device matches * this child we need to inform the request that the * device is gone. */ if (request && request->dev == children[i]) { request->result = ENXIO; device_printf(request->dev, "FAILURE - device detached\n"); /* if not timeout finish request here */ if (!(request->flags & ATA_R_TIMEOUT)) ata_finish(request); request = NULL; } device_delete_child(dev, children[i]); } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* if we still have a good request put it on the queue again */ if (request && !(request->flags & ATA_R_TIMEOUT)) { device_printf(request->dev, "WARNING - %s requeued due to channel reset", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); request->flags |= ATA_R_REQUEUE; ata_queue_request(request); } /* we're done release the channel for new work */ mtx_lock(&ch->state_mtx); ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); /* Add new children. */ /* ata_identify(dev); */ if (bootverbose) device_printf(dev, "reinit done ..\n"); /* kick off requests on the queue */ ata_start(dev); #else xpt_freeze_simq(ch->sim, 1); if ((request = ch->running)) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; callout_stop(&request->callout); if (ch->dma.unload) ch->dma.unload(request); request->result = ERESTART; ata_cam_end_transaction(dev, request); } /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); xpt_release_simq(ch->sim, TRUE); #endif return(0); } int ata_suspend(device_t dev) { struct ata_channel *ch; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; #ifndef ATA_CAM /* wait for the channel to be IDLE or detached before suspending */ while (ch->r_irq) { mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE) { ch->state = ATA_ACTIVE; mtx_unlock(&ch->state_mtx); break; } mtx_unlock(&ch->state_mtx); tsleep(ch, PRIBIO, "atasusp", hz/10); } ATA_LOCKING(dev, ATA_LF_UNLOCK); #endif return(0); } int ata_resume(device_t dev) { int error; /* check for valid device */ if (!dev || !device_get_softc(dev)) return ENXIO; /* reinit the devices, we dont know what mode/state they are in */ error = ata_reinit(dev); #ifndef ATA_CAM /* kick off requests on the queue */ ata_start(dev); #endif return error; } void ata_interrupt(void *data) { #ifdef ATA_CAM struct ata_channel *ch = (struct ata_channel *)data; mtx_lock(&ch->state_mtx); #endif ata_interrupt_locked(data); #ifdef ATA_CAM mtx_unlock(&ch->state_mtx); #endif } static void ata_interrupt_locked(void *data) { struct ata_channel *ch = (struct ata_channel *)data; struct ata_request *request; #ifndef ATA_CAM mtx_lock(&ch->state_mtx); #endif do { /* ignore interrupt if its not for us */ if (ch->hw.status && !ch->hw.status(ch->dev)) break; /* do we have a running request */ if (!(request = ch->running)) break; ATA_DEBUG_RQ(request, "interrupt"); /* safetycheck for the right state */ if (ch->state == ATA_IDLE) { device_printf(request->dev, "interrupt on idle channel ignored\n"); break; } /* * we have the HW locks, so end the transaction for this request * if it finishes immediately otherwise wait for next interrupt */ if (ch->hw.end_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; #ifdef ATA_CAM ata_cam_end_transaction(ch->dev, request); #else mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); #endif return; } } while (0); #ifndef ATA_CAM mtx_unlock(&ch->state_mtx); #endif } void ata_print_cable(device_t dev, u_int8_t *who) { device_printf(dev, "DMA limited to UDMA33, %s found non-ATA66 cable\n", who); } int ata_check_80pin(device_t dev, int mode) { struct ata_device *atadev = device_get_softc(dev); if (!ata_dma_check_80pin) { if (bootverbose) device_printf(dev, "Skipping 80pin cable check\n"); return mode; } if (mode > ATA_UDMA2 && !(atadev->param.hwres & ATA_CABLE_ID)) { ata_print_cable(dev, "device"); mode = ATA_UDMA2; } return mode; } void ata_setmode(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); int error, mode, pmode; mode = atadev->mode; do { pmode = mode = ata_limit_mode(dev, mode, ATA_DMA_MAX); mode = ATA_SETMODE(device_get_parent(dev), atadev->unit, mode); if ((ch->flags & (ATA_CHECKS_CABLE | ATA_SATA)) == 0) mode = ata_check_80pin(dev, mode); } while (pmode != mode); /* Interate till successfull negotiation. */ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode); if (bootverbose) device_printf(dev, "%ssetting %s\n", (error) ? "FAILURE " : "", ata_mode2str(mode)); atadev->mode = mode; } /* * device related interfaces */ #ifndef ATA_CAM static int ata_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag, struct thread *td) { device_t device, *children; struct ata_ioc_devices *devices = (struct ata_ioc_devices *)data; int *value = (int *)data; int i, nchildren, error = ENOTTY; switch (cmd) { case IOCATAGMAXCHANNEL: /* In case we have channel 0..n this will return n+1. */ *value = devclass_get_maxunit(ata_devclass); error = 0; break; case IOCATAREINIT: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = ata_reinit(device); break; case IOCATAATTACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = DEVICE_ATTACH(device); break; case IOCATADETACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = DEVICE_DETACH(device); break; case IOCATADEVICES: if (devices->channel >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, devices->channel)) || !device_is_attached(device)) return ENXIO; bzero(devices->name[0], 32); bzero(&devices->params[0], sizeof(struct ata_params)); bzero(devices->name[1], 32); bzero(&devices->params[1], sizeof(struct ata_params)); if (!device_get_children(device, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && device_is_attached(children[i])) { struct ata_device *atadev = device_get_softc(children[i]); if (atadev->unit == ATA_MASTER) { /* XXX SOS PM */ strncpy(devices->name[0], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[0], sizeof(struct ata_params)); } if (atadev->unit == ATA_SLAVE) { /* XXX SOS PM */ strncpy(devices->name[1], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[1], sizeof(struct ata_params)); } } } free(children, M_TEMP); error = 0; } else error = ENODEV; break; default: if (ata_raid_ioctl_func) error = ata_raid_ioctl_func(cmd, data); } return error; } #endif int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data) { struct ata_device *atadev = device_get_softc(dev); struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data; struct ata_params *params = (struct ata_params *)data; int *mode = (int *)data; struct ata_request *request; caddr_t buf; int error; switch (cmd) { case IOCATAREQUEST: if (ioc_request->count > (ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS)) { return (EFBIG); } if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) { return ENOMEM; } if (!(request = ata_alloc_request())) { free(buf, M_ATA); return ENOMEM; } request->dev = atadev->dev; if (ioc_request->flags & ATA_CMD_WRITE) { error = copyin(ioc_request->data, buf, ioc_request->count); if (error) { free(buf, M_ATA); ata_free_request(request); return error; } } if (ioc_request->flags & ATA_CMD_ATAPI) { request->flags = ATA_R_ATAPI; bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16); } else { request->u.ata.command = ioc_request->u.ata.command; request->u.ata.feature = ioc_request->u.ata.feature; request->u.ata.lba = ioc_request->u.ata.lba; request->u.ata.count = ioc_request->u.ata.count; } request->timeout = ioc_request->timeout; request->data = buf; request->bytecount = ioc_request->count; request->transfersize = request->bytecount; if (ioc_request->flags & ATA_CMD_CONTROL) request->flags |= ATA_R_CONTROL; if (ioc_request->flags & ATA_CMD_READ) request->flags |= ATA_R_READ; if (ioc_request->flags & ATA_CMD_WRITE) request->flags |= ATA_R_WRITE; ata_queue_request(request); if (request->flags & ATA_R_ATAPI) { bcopy(&request->u.atapi.sense, &ioc_request->u.atapi.sense, sizeof(struct atapi_sense)); } else { ioc_request->u.ata.command = request->u.ata.command; ioc_request->u.ata.feature = request->u.ata.feature; ioc_request->u.ata.lba = request->u.ata.lba; ioc_request->u.ata.count = request->u.ata.count; } ioc_request->error = request->result; if (ioc_request->flags & ATA_CMD_READ) error = copyout(buf, ioc_request->data, ioc_request->count); else error = 0; free(buf, M_ATA); ata_free_request(request); return error; case IOCATAGPARM: ata_getparam(atadev, 0); bcopy(&atadev->param, params, sizeof(struct ata_params)); return 0; case IOCATASMODE: atadev->mode = *mode; ata_setmode(dev); return 0; case IOCATAGMODE: *mode = atadev->mode | (ATA_GETREV(device_get_parent(dev), atadev->unit) << 8); return 0; case IOCATASSPINDOWN: atadev->spindown = *mode; return 0; case IOCATAGSPINDOWN: *mode = atadev->spindown; return 0; default: return ENOTTY; } } #ifndef ATA_CAM static void ata_boot_attach(void) { struct ata_channel *ch; int ctlr; mtx_lock(&Giant); /* newbus suckage it needs Giant */ /* kick of probe and attach on all channels */ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) { if ((ch = devclass_get_softc(ata_devclass, ctlr))) { ata_identify(ch->dev); } } /* release the hook that got us here, we are only needed once during boot */ if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_TEMP); ata_delayed_attach = NULL; } mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } #endif /* * misc support functions */ #ifndef ATA_CAM static device_t ata_add_child(device_t parent, struct ata_device *atadev, int unit) { device_t child; if ((child = device_add_child(parent, NULL, unit))) { device_set_softc(child, atadev); device_quiet(child); atadev->dev = child; atadev->max_iosize = DEV_BSIZE; atadev->mode = ATA_PIO_MAX; } return child; } #endif int ata_getparam(struct ata_device *atadev, int init) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); struct ata_request *request; u_int8_t command = 0; int error = ENOMEM, retries = 2; if (ch->devices & (ATA_ATA_MASTER << atadev->unit)) command = ATA_ATA_IDENTIFY; if (ch->devices & (ATA_ATAPI_MASTER << atadev->unit)) command = ATA_ATAPI_IDENTIFY; if (!command) return ENXIO; while (retries-- > 0 && error) { if (!(request = ata_alloc_request())) break; request->dev = atadev->dev; request->timeout = 1; request->retries = 0; request->u.ata.command = command; request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT); if (!bootverbose) request->flags |= ATA_R_QUIET; request->data = (void *)&atadev->param; request->bytecount = sizeof(struct ata_params); request->donecount = 0; request->transfersize = DEV_BSIZE; ata_queue_request(request); error = request->result; ata_free_request(request); } if (!error && (isprint(atadev->param.model[0]) || isprint(atadev->param.model[1]))) { struct ata_params *atacap = &atadev->param; int16_t *ptr; for (ptr = (int16_t *)atacap; ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) { *ptr = le16toh(*ptr); } if (!(!strncmp(atacap->model, "FX", 2) || !strncmp(atacap->model, "NEC", 3) || !strncmp(atacap->model, "Pioneer", 7) || !strncmp(atacap->model, "SHARP", 5))) { bswap(atacap->model, sizeof(atacap->model)); bswap(atacap->revision, sizeof(atacap->revision)); bswap(atacap->serial, sizeof(atacap->serial)); } btrim(atacap->model, sizeof(atacap->model)); bpack(atacap->model, atacap->model, sizeof(atacap->model)); btrim(atacap->revision, sizeof(atacap->revision)); bpack(atacap->revision, atacap->revision, sizeof(atacap->revision)); btrim(atacap->serial, sizeof(atacap->serial)); bpack(atacap->serial, atacap->serial, sizeof(atacap->serial)); if (bootverbose) printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n", device_get_unit(ch->dev), ata_unit2str(atadev), ata_mode2str(ata_pmode(atacap)), ata_mode2str(ata_wmode(atacap)), ata_mode2str(ata_umode(atacap)), (atacap->hwres & ATA_CABLE_ID) ? "80":"40"); if (init) { char buffer[64]; sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision); device_set_desc_copy(atadev->dev, buffer); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config != ATA_CFA_MAGIC1) && (atadev->param.config != ATA_CFA_MAGIC2)) { if (atapi_dma && (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR && ata_umode(&atadev->param) >= ATA_UDMA2) atadev->mode = ATA_DMA_MAX; } else { if (ata_dma && (ata_umode(&atadev->param) > 0 || ata_wmode(&atadev->param) > 0)) atadev->mode = ATA_DMA_MAX; } } } else { if (!error) error = ENXIO; } return error; } #ifndef ATA_CAM int ata_identify(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_device *atadev; device_t *children; device_t child, master = NULL; int nchildren, i, n = ch->devices; if (bootverbose) device_printf(dev, "Identifying devices: %08x\n", ch->devices); mtx_lock(&Giant); /* Skip existing devices. */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && (atadev = device_get_softc(children[i]))) n &= ~((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << atadev->unit); } free(children, M_TEMP); } /* Create new devices. */ if (bootverbose) device_printf(dev, "New devices: %08x\n", n); if (n == 0) { mtx_unlock(&Giant); return (0); } for (i = 0; i < ATA_PM; ++i) { if (n & (((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << i))) { int unit = -1; if (!(atadev = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } atadev->unit = i; #ifdef ATA_STATIC_ID if (n & (ATA_ATA_MASTER << i)) unit = (device_get_unit(dev) << 1) + i; #endif if ((child = ata_add_child(dev, atadev, unit))) { /* * PATA slave should be identified first, to allow * device cable detection on master to work properly. */ if (i == 0 && (n & ATA_PORTMULTIPLIER) == 0 && (n & ((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << 1)) != 0) { master = child; continue; } if (ata_getparam(atadev, 1)) { device_delete_child(dev, child); free(atadev, M_ATA); } } else free(atadev, M_ATA); } } if (master) { atadev = device_get_softc(master); if (ata_getparam(atadev, 1)) { device_delete_child(dev, master); free(atadev, M_ATA); } } bus_generic_probe(dev); bus_generic_attach(dev); mtx_unlock(&Giant); return 0; } #endif void ata_default_registers(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* fill in the defaults from whats setup already */ ch->r_io[ATA_ERROR].res = ch->r_io[ATA_FEATURE].res; ch->r_io[ATA_ERROR].offset = ch->r_io[ATA_FEATURE].offset; ch->r_io[ATA_IREASON].res = ch->r_io[ATA_COUNT].res; ch->r_io[ATA_IREASON].offset = ch->r_io[ATA_COUNT].offset; ch->r_io[ATA_STATUS].res = ch->r_io[ATA_COMMAND].res; ch->r_io[ATA_STATUS].offset = ch->r_io[ATA_COMMAND].offset; ch->r_io[ATA_ALTSTAT].res = ch->r_io[ATA_CONTROL].res; ch->r_io[ATA_ALTSTAT].offset = ch->r_io[ATA_CONTROL].offset; } void ata_modify_if_48bit(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); request->flags &= ~ATA_R_48BIT; if (((request->u.ata.lba + request->u.ata.count) >= ATA_MAX_28BIT_LBA || request->u.ata.count > 256) && atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_READ: request->u.ata.command = ATA_READ48; break; case ATA_READ_MUL: request->u.ata.command = ATA_READ_MUL48; break; case ATA_READ_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA48; break; case ATA_READ_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA_QUEUED48; break; case ATA_WRITE: request->u.ata.command = ATA_WRITE48; break; case ATA_WRITE_MUL: request->u.ata.command = ATA_WRITE_MUL48; break; case ATA_WRITE_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA48; break; case ATA_WRITE_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA_QUEUED48; break; case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } request->flags |= ATA_R_48BIT; } else if (atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_READ_NATIVE_MAX_ADDRESS: request->u.ata.command = ATA_READ_NATIVE_MAX_ADDRESS48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } request->flags |= ATA_R_48BIT; } } void ata_udelay(int interval) { /* for now just use DELAY, the timer/sleep subsytems are not there yet */ if (1 || interval < (1000000/hz) || ata_delayed_attach) DELAY(interval); else pause("ataslp", interval/(1000000/hz)); } char * ata_unit2str(struct ata_device *atadev) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); static char str[8]; if (ch->devices & ATA_PORTMULTIPLIER) sprintf(str, "port%d", atadev->unit); else sprintf(str, "%s", atadev->unit == ATA_MASTER ? "master" : "slave"); return str; } const char * ata_mode2str(int mode) { switch (mode) { case -1: return "UNSUPPORTED"; case ATA_PIO0: return "PIO0"; case ATA_PIO1: return "PIO1"; case ATA_PIO2: return "PIO2"; case ATA_PIO3: return "PIO3"; case ATA_PIO4: return "PIO4"; case ATA_WDMA0: return "WDMA0"; case ATA_WDMA1: return "WDMA1"; case ATA_WDMA2: return "WDMA2"; case ATA_UDMA0: return "UDMA16"; case ATA_UDMA1: return "UDMA25"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA3: return "UDMA40"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_UDMA6: return "UDMA133"; case ATA_SA150: return "SATA150"; case ATA_SA300: return "SATA300"; default: if (mode & ATA_DMA_MASK) return "BIOSDMA"; else return "BIOSPIO"; } } const char * ata_satarev2str(int rev) { switch (rev) { case 0: return ""; case 1: return "SATA 1.5Gb/s"; case 2: return "SATA 3Gb/s"; case 3: return "SATA 6Gb/s"; case 0xff: return "SATA"; default: return "???"; } } int ata_atapi(device_t dev, int target) { struct ata_channel *ch = device_get_softc(dev); return (ch->devices & (ATA_ATAPI_MASTER << target)); } int ata_pmode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_64_70) { if (ap->apiomodes & 0x02) return ATA_PIO4; if (ap->apiomodes & 0x01) return ATA_PIO3; } if (ap->mwdmamodes & 0x04) return ATA_PIO4; if (ap->mwdmamodes & 0x02) return ATA_PIO3; if (ap->mwdmamodes & 0x01) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x200) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x100) return ATA_PIO1; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x000) return ATA_PIO0; return ATA_PIO0; } int ata_wmode(struct ata_params *ap) { if (ap->mwdmamodes & 0x04) return ATA_WDMA2; if (ap->mwdmamodes & 0x02) return ATA_WDMA1; if (ap->mwdmamodes & 0x01) return ATA_WDMA0; return -1; } int ata_umode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_88) { if (ap->udmamodes & 0x40) return ATA_UDMA6; if (ap->udmamodes & 0x20) return ATA_UDMA5; if (ap->udmamodes & 0x10) return ATA_UDMA4; if (ap->udmamodes & 0x08) return ATA_UDMA3; if (ap->udmamodes & 0x04) return ATA_UDMA2; if (ap->udmamodes & 0x02) return ATA_UDMA1; if (ap->udmamodes & 0x01) return ATA_UDMA0; } return -1; } int ata_limit_mode(device_t dev, int mode, int maxmode) { struct ata_device *atadev = device_get_softc(dev); if (maxmode && mode > maxmode) mode = maxmode; if (mode >= ATA_UDMA0 && ata_umode(&atadev->param) > 0) return min(mode, ata_umode(&atadev->param)); if (mode >= ATA_WDMA0 && ata_wmode(&atadev->param) > 0) return min(mode, ata_wmode(&atadev->param)); if (mode > ata_pmode(&atadev->param)) return min(mode, ata_pmode(&atadev->param)); return mode; } static void bswap(int8_t *buf, int len) { u_int16_t *ptr = (u_int16_t*)(buf + len); while (--ptr >= (u_int16_t*)buf) *ptr = ntohs(*ptr); } static void btrim(int8_t *buf, int len) { int8_t *ptr; for (ptr = buf; ptr < buf+len; ++ptr) if (!*ptr || *ptr == '_') *ptr = ' '; for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr) *ptr = 0; } static void bpack(int8_t *src, int8_t *dst, int len) { int i, j, blank; for (i = j = blank = 0 ; i < len; i++) { if (blank && src[i] == ' ') continue; if (blank && src[i] != ' ') { dst[j++] = src[i]; blank = 0; continue; } if (src[i] == ' ') { blank = 1; if (i == 0) continue; } dst[j++] = src[i]; } if (j < len) dst[j] = 0x00; } #ifdef ATA_CAM void ata_cam_begin_transaction(device_t dev, union ccb *ccb) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in start\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } bzero(request, sizeof(*request)); /* setup request */ request->dev = NULL; request->parent = dev; request->unit = ccb->ccb_h.target_id; if (ccb->ccb_h.func_code == XPT_ATA_IO) { request->data = ccb->ataio.data_ptr; request->bytecount = ccb->ataio.dxfer_len; request->u.ata.command = ccb->ataio.cmd.command; request->u.ata.feature = ((uint16_t)ccb->ataio.cmd.features_exp << 8) | (uint16_t)ccb->ataio.cmd.features; request->u.ata.count = ((uint16_t)ccb->ataio.cmd.sector_count_exp << 8) | (uint16_t)ccb->ataio.cmd.sector_count; if (ccb->ataio.cmd.flags & CAM_ATAIO_48BIT) { request->flags |= ATA_R_48BIT; request->u.ata.lba = ((uint64_t)ccb->ataio.cmd.lba_high_exp << 40) | ((uint64_t)ccb->ataio.cmd.lba_mid_exp << 32) | ((uint64_t)ccb->ataio.cmd.lba_low_exp << 24); } else { request->u.ata.lba = ((uint64_t)(ccb->ataio.cmd.device & 0x0f) << 24); } request->u.ata.lba |= ((uint64_t)ccb->ataio.cmd.lba_high << 16) | ((uint64_t)ccb->ataio.cmd.lba_mid << 8) | (uint64_t)ccb->ataio.cmd.lba_low; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ccb->ataio.cmd.flags & CAM_ATAIO_DMA) request->flags |= ATA_R_DMA; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) request->flags |= ATA_R_READ; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) request->flags |= ATA_R_WRITE; } else { request->data = ccb->csio.data_ptr; request->bytecount = ccb->csio.dxfer_len; bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, request->u.atapi.ccb, ccb->csio.cdb_len); request->flags |= ATA_R_ATAPI; if (ch->curr[ccb->ccb_h.target_id].atapi == 16) request->flags |= ATA_R_ATAPI16; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) request->flags |= ATA_R_DMA; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) request->flags |= ATA_R_READ; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) request->flags |= ATA_R_WRITE; } request->transfersize = min(request->bytecount, ch->curr[ccb->ccb_h.target_id].bytecount); request->retries = 0; request->timeout = (ccb->ccb_h.timeout + 999) / 1000; callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); request->ccb = ccb; ch->running = request; ch->state = ATA_ACTIVE; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; ch->state = ATA_IDLE; ata_cam_end_transaction(dev, request); return; } } void ata_cam_end_transaction(device_t dev, struct ata_request *request) { struct ata_channel *ch = device_get_softc(dev); union ccb *ccb = request->ccb; int fatalerr = 0; ccb->ccb_h.status &= ~CAM_STATUS_MASK; if (request->flags & ATA_R_TIMEOUT) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT | CAM_RELEASE_SIMQ; fatalerr = 1; } else if (request->status & ATA_S_ERROR) { if (ccb->ccb_h.func_code == XPT_ATA_IO) { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } else { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } } else if (request->result == ERESTART) ccb->ccb_h.status |= CAM_REQUEUE_REQ; else if (request->result != 0) ccb->ccb_h.status |= CAM_REQ_CMP_ERR; else ccb->ccb_h.status |= CAM_REQ_CMP; if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } if (ccb->ccb_h.func_code == XPT_ATA_IO && ((request->status & ATA_S_ERROR) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT))) { struct ata_res *res = &ccb->ataio.res; res->status = request->status; res->error = request->error; res->lba_low = request->u.ata.lba; res->lba_mid = request->u.ata.lba >> 8; res->lba_high = request->u.ata.lba >> 16; res->device = request->u.ata.lba >> 24; res->lba_low_exp = request->u.ata.lba >> 24; res->lba_mid_exp = request->u.ata.lba >> 32; res->lba_high_exp = request->u.ata.lba >> 40; res->sector_count = request->u.ata.count; res->sector_count_exp = request->u.ata.count >> 8; } ata_free_request(request); xpt_done(ccb); /* Do error recovery if needed. */ if (fatalerr) ata_reinit(dev); } static void ataaction(struct cam_sim *sim, union ccb *ccb) { device_t dev; struct ata_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ataaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct ata_channel *)cam_sim_softc(sim); dev = ch->dev; switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if ((ch->devices & ((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << ccb->ccb_h.target_id)) == 0) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; xpt_done(ccb); break; } if (ch->running) device_printf(dev, "already running!\n"); if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET)) { struct ata_res *res = &ccb->ataio.res; bzero(res, sizeof(*res)); if (ch->devices & (ATA_ATA_MASTER << ccb->ccb_h.target_id)) { res->lba_high = 0; res->lba_mid = 0; } else { res->lba_high = 0xeb; res->lba_mid = 0x14; } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } ata_cam_begin_transaction(dev, ccb); break; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ata_cam_device *d; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (ch->flags & ATA_SATA) { if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) { if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { d->mode = ATA_SETMODE(ch->dev, ccb->ccb_h.target_id, cts->xport_specific.sata.mode); } else d->mode = cts->xport_specific.sata.mode; } if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; } else { if (cts->xport_specific.ata.valid & CTS_ATA_VALID_MODE) { if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { d->mode = ATA_SETMODE(ch->dev, ccb->ccb_h.target_id, cts->xport_specific.ata.mode); } else d->mode = cts->xport_specific.ata.mode; } if (cts->xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT) d->bytecount = cts->xport_specific.ata.bytecount; if (cts->xport_specific.ata.valid & CTS_ATA_VALID_ATAPI) d->atapi = cts->xport_specific.ata.atapi; } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ata_cam_device *d; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; if (ch->flags & ATA_SATA) { cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.sata.valid = 0; cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { cts->xport_specific.sata.revision = ATA_GETREV(dev, ccb->ccb_h.target_id); if (cts->xport_specific.sata.revision != 0xff) { cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; } else { cts->transport = XPORT_ATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.ata.valid = 0; cts->xport_specific.ata.mode = d->mode; cts->xport_specific.ata.valid |= CTS_ATA_VALID_MODE; cts->xport_specific.ata.bytecount = d->bytecount; cts->xport_specific.ata.valid |= CTS_ATA_VALID_BYTECOUNT; cts->xport_specific.ata.atapi = d->atapi; cts->xport_specific.ata.valid |= CTS_ATA_VALID_ATAPI; } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } #if 0 case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; uint32_t size_mb; uint32_t secs_per_cylinder; ccg = &ccb->ccg; size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb >= 1024 && (aha->extended_trans != 0)) { if (size_mb >= 2048) { ccg->heads = 255; ccg->secs_per_track = 63; } else { ccg->heads = 128; ccg->secs_per_track = 32; } } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } #endif case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ ata_reinit(dev); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; if (ch->flags & ATA_NO_SLAVE) cpi->max_target = 0; else cpi->max_target = 1; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); if (ch->flags & ATA_SATA) cpi->base_transfer_speed = 150000; else cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "ATA", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); if (ch->flags & ATA_SATA) cpi->transport = XPORT_SATA; else cpi->transport = XPORT_ATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static void atapoll(struct cam_sim *sim) { struct ata_channel *ch = (struct ata_channel *)cam_sim_softc(sim); ata_interrupt_locked(ch); } #endif /* * module handeling */ static int ata_module_event_handler(module_t mod, int what, void *arg) { #ifndef ATA_CAM static struct cdev *atacdev; #endif switch (what) { case MOD_LOAD: #ifndef ATA_CAM /* register controlling device */ atacdev = make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); if (cold) { /* register boot attach to be run when interrupts are enabled */ if (!(ata_delayed_attach = (struct intr_config_hook *) malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT | M_ZERO))) { printf("ata: malloc of delayed attach hook failed\n"); return EIO; } ata_delayed_attach->ich_func = (void*)ata_boot_attach; if (config_intrhook_establish(ata_delayed_attach) != 0) { printf("ata: config_intrhook_establish failed\n"); free(ata_delayed_attach, M_TEMP); } } #endif return 0; case MOD_UNLOAD: #ifndef ATA_CAM /* deregister controlling device */ destroy_dev(atacdev); #endif return 0; default: return EOPNOTSUPP; } } static moduledata_t ata_moduledata = { "ata", ata_module_event_handler, NULL }; DECLARE_MODULE(ata, ata_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(ata, 1); #ifdef ATA_CAM MODULE_DEPEND(ata, cam, 1, 1, 1); #endif static void ata_init(void) { ata_request_zone = uma_zcreate("ata_request", sizeof(struct ata_request), NULL, NULL, NULL, NULL, 0, 0); ata_composite_zone = uma_zcreate("ata_composite", sizeof(struct ata_composite), NULL, NULL, NULL, NULL, 0, 0); } SYSINIT(ata_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL); static void ata_uninit(void) { uma_zdestroy(ata_composite_zone); uma_zdestroy(ata_request_zone); } SYSUNINIT(ata_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_uninit, NULL); Index: projects/ppc64/sys/dev/bge/if_bge.c =================================================================== --- projects/ppc64/sys/dev/bge/if_bge.c (revision 204271) +++ projects/ppc64/sys/dev/bge/if_bge.c (revision 204272) @@ -1,5219 +1,5206 @@ /*- * Copyright (c) 2001 Wind River Systems * Copyright (c) 1997, 1998, 1999, 2001 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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$"); /* * Broadcom BCM570x family gigabit ethernet driver for FreeBSD. * * The Broadcom BCM5700 is based on technology originally developed by * Alteon Networks as part of the Tigon I and Tigon II gigabit ethernet * MAC chips. The BCM5700, sometimes refered to as the Tigon III, has * two on-board MIPS R4000 CPUs and can have as much as 16MB of external * SSRAM. The BCM5700 supports TCP, UDP and IP checksum offload, jumbo * frames, highly configurable RX filtering, and 16 RX and TX queues * (which, along with RX filter rules, can be used for QOS applications). * Other features, such as TCP segmentation, may be available as part * of value-added firmware updates. Unlike the Tigon I and Tigon II, * firmware images can be stored in hardware and need not be compiled * into the driver. * * The BCM5700 supports the PCI v2.2 and PCI-X v1.0 standards, and will * function in a 32-bit/64-bit 33/66Mhz bus, or a 64-bit/133Mhz bus. * * The BCM5701 is a single-chip solution incorporating both the BCM5700 * MAC and a BCM5401 10/100/1000 PHY. Unlike the BCM5700, the BCM5701 * does not support external SSRAM. * * Broadcom also produces a variation of the BCM5700 under the "Altima" * brand name, which is functionally similar but lacks PCI-X support. * * Without external SSRAM, you can only have at most 4 TX rings, * and the use of the mini RX ring is disabled. This seems to imply * that these features are simply not available on the BCM5701. As a * result, this driver does not implement any support for the mini RX * ring. */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "miidevs.h" #include #ifdef __sparc64__ #include #include #include #include #endif #include #include #include #define BGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) #define ETHER_MIN_NOPAD (ETHER_MIN_LEN - ETHER_CRC_LEN) /* i.e., 60 */ MODULE_DEPEND(bge, pci, 1, 1, 1); MODULE_DEPEND(bge, ether, 1, 1, 1); MODULE_DEPEND(bge, miibus, 1, 1, 1); /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Various supported device vendors/types and their names. Note: the * spec seems to indicate that the hardware still has Alteon's vendor * ID burned into it, though it will always be overriden by the vendor * ID in the EEPROM. Just to be safe, we cover all possibilities. */ static const struct bge_type { uint16_t bge_vid; uint16_t bge_did; } bge_devs[] = { { ALTEON_VENDORID, ALTEON_DEVICEID_BCM5700 }, { ALTEON_VENDORID, ALTEON_DEVICEID_BCM5701 }, { ALTIMA_VENDORID, ALTIMA_DEVICE_AC1000 }, { ALTIMA_VENDORID, ALTIMA_DEVICE_AC1002 }, { ALTIMA_VENDORID, ALTIMA_DEVICE_AC9100 }, { APPLE_VENDORID, APPLE_DEVICE_BCM5701 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5700 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5701 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5702 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5702_ALT }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5702X }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5703 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5703_ALT }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5703X }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5704C }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5704S }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5704S_ALT }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5705 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5705F }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5705K }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5705M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5705M_ALT }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5714C }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5714S }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5715 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5715S }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5720 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5721 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5722 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5723 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5750 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5750M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5751 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5751F }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5751M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5752 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5752M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5753 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5753F }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5753M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5754 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5754M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5755 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5755M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5756 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5761 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5761E }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5761S }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5761SE }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5764 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5780 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5780S }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5781 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5782 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5784 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5785F }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5785G }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5786 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5787 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5787F }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5787M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5788 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5789 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5901 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5901A2 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5903M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5906 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM5906M }, { BCOM_VENDORID, BCOM_DEVICEID_BCM57760 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM57780 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM57788 }, { BCOM_VENDORID, BCOM_DEVICEID_BCM57790 }, { SK_VENDORID, SK_DEVICEID_ALTIMA }, { TC_VENDORID, TC_DEVICEID_3C996 }, { FJTSU_VENDORID, FJTSU_DEVICEID_PW008GE4 }, { FJTSU_VENDORID, FJTSU_DEVICEID_PW008GE5 }, { FJTSU_VENDORID, FJTSU_DEVICEID_PP250450 }, { 0, 0 } }; static const struct bge_vendor { uint16_t v_id; const char *v_name; } bge_vendors[] = { { ALTEON_VENDORID, "Alteon" }, { ALTIMA_VENDORID, "Altima" }, { APPLE_VENDORID, "Apple" }, { BCOM_VENDORID, "Broadcom" }, { SK_VENDORID, "SysKonnect" }, { TC_VENDORID, "3Com" }, { FJTSU_VENDORID, "Fujitsu" }, { 0, NULL } }; static const struct bge_revision { uint32_t br_chipid; const char *br_name; } bge_revisions[] = { { BGE_CHIPID_BCM5700_A0, "BCM5700 A0" }, { BGE_CHIPID_BCM5700_A1, "BCM5700 A1" }, { BGE_CHIPID_BCM5700_B0, "BCM5700 B0" }, { BGE_CHIPID_BCM5700_B1, "BCM5700 B1" }, { BGE_CHIPID_BCM5700_B2, "BCM5700 B2" }, { BGE_CHIPID_BCM5700_B3, "BCM5700 B3" }, { BGE_CHIPID_BCM5700_ALTIMA, "BCM5700 Altima" }, { BGE_CHIPID_BCM5700_C0, "BCM5700 C0" }, { BGE_CHIPID_BCM5701_A0, "BCM5701 A0" }, { BGE_CHIPID_BCM5701_B0, "BCM5701 B0" }, { BGE_CHIPID_BCM5701_B2, "BCM5701 B2" }, { BGE_CHIPID_BCM5701_B5, "BCM5701 B5" }, { BGE_CHIPID_BCM5703_A0, "BCM5703 A0" }, { BGE_CHIPID_BCM5703_A1, "BCM5703 A1" }, { BGE_CHIPID_BCM5703_A2, "BCM5703 A2" }, { BGE_CHIPID_BCM5703_A3, "BCM5703 A3" }, { BGE_CHIPID_BCM5703_B0, "BCM5703 B0" }, { BGE_CHIPID_BCM5704_A0, "BCM5704 A0" }, { BGE_CHIPID_BCM5704_A1, "BCM5704 A1" }, { BGE_CHIPID_BCM5704_A2, "BCM5704 A2" }, { BGE_CHIPID_BCM5704_A3, "BCM5704 A3" }, { BGE_CHIPID_BCM5704_B0, "BCM5704 B0" }, { BGE_CHIPID_BCM5705_A0, "BCM5705 A0" }, { BGE_CHIPID_BCM5705_A1, "BCM5705 A1" }, { BGE_CHIPID_BCM5705_A2, "BCM5705 A2" }, { BGE_CHIPID_BCM5705_A3, "BCM5705 A3" }, { BGE_CHIPID_BCM5750_A0, "BCM5750 A0" }, { BGE_CHIPID_BCM5750_A1, "BCM5750 A1" }, { BGE_CHIPID_BCM5750_A3, "BCM5750 A3" }, { BGE_CHIPID_BCM5750_B0, "BCM5750 B0" }, { BGE_CHIPID_BCM5750_B1, "BCM5750 B1" }, { BGE_CHIPID_BCM5750_C0, "BCM5750 C0" }, { BGE_CHIPID_BCM5750_C1, "BCM5750 C1" }, { BGE_CHIPID_BCM5750_C2, "BCM5750 C2" }, { BGE_CHIPID_BCM5714_A0, "BCM5714 A0" }, { BGE_CHIPID_BCM5752_A0, "BCM5752 A0" }, { BGE_CHIPID_BCM5752_A1, "BCM5752 A1" }, { BGE_CHIPID_BCM5752_A2, "BCM5752 A2" }, { BGE_CHIPID_BCM5714_B0, "BCM5714 B0" }, { BGE_CHIPID_BCM5714_B3, "BCM5714 B3" }, { BGE_CHIPID_BCM5715_A0, "BCM5715 A0" }, { BGE_CHIPID_BCM5715_A1, "BCM5715 A1" }, { BGE_CHIPID_BCM5715_A3, "BCM5715 A3" }, { BGE_CHIPID_BCM5755_A0, "BCM5755 A0" }, { BGE_CHIPID_BCM5755_A1, "BCM5755 A1" }, { BGE_CHIPID_BCM5755_A2, "BCM5755 A2" }, { BGE_CHIPID_BCM5722_A0, "BCM5722 A0" }, { BGE_CHIPID_BCM5761_A0, "BCM5761 A0" }, { BGE_CHIPID_BCM5761_A1, "BCM5761 A1" }, { BGE_CHIPID_BCM5784_A0, "BCM5784 A0" }, { BGE_CHIPID_BCM5784_A1, "BCM5784 A1" }, /* 5754 and 5787 share the same ASIC ID */ { BGE_CHIPID_BCM5787_A0, "BCM5754/5787 A0" }, { BGE_CHIPID_BCM5787_A1, "BCM5754/5787 A1" }, { BGE_CHIPID_BCM5787_A2, "BCM5754/5787 A2" }, { BGE_CHIPID_BCM5906_A1, "BCM5906 A1" }, { BGE_CHIPID_BCM5906_A2, "BCM5906 A2" }, { BGE_CHIPID_BCM57780_A0, "BCM57780 A0" }, { BGE_CHIPID_BCM57780_A1, "BCM57780 A1" }, { 0, NULL } }; /* * Some defaults for major revisions, so that newer steppings * that we don't know about have a shot at working. */ static const struct bge_revision bge_majorrevs[] = { { BGE_ASICREV_BCM5700, "unknown BCM5700" }, { BGE_ASICREV_BCM5701, "unknown BCM5701" }, { BGE_ASICREV_BCM5703, "unknown BCM5703" }, { BGE_ASICREV_BCM5704, "unknown BCM5704" }, { BGE_ASICREV_BCM5705, "unknown BCM5705" }, { BGE_ASICREV_BCM5750, "unknown BCM5750" }, { BGE_ASICREV_BCM5714_A0, "unknown BCM5714" }, { BGE_ASICREV_BCM5752, "unknown BCM5752" }, { BGE_ASICREV_BCM5780, "unknown BCM5780" }, { BGE_ASICREV_BCM5714, "unknown BCM5714" }, { BGE_ASICREV_BCM5755, "unknown BCM5755" }, { BGE_ASICREV_BCM5761, "unknown BCM5761" }, { BGE_ASICREV_BCM5784, "unknown BCM5784" }, { BGE_ASICREV_BCM5785, "unknown BCM5785" }, /* 5754 and 5787 share the same ASIC ID */ { BGE_ASICREV_BCM5787, "unknown BCM5754/5787" }, { BGE_ASICREV_BCM5906, "unknown BCM5906" }, { BGE_ASICREV_BCM57780, "unknown BCM57780" }, { 0, NULL } }; #define BGE_IS_JUMBO_CAPABLE(sc) ((sc)->bge_flags & BGE_FLAG_JUMBO) #define BGE_IS_5700_FAMILY(sc) ((sc)->bge_flags & BGE_FLAG_5700_FAMILY) #define BGE_IS_5705_PLUS(sc) ((sc)->bge_flags & BGE_FLAG_5705_PLUS) #define BGE_IS_5714_FAMILY(sc) ((sc)->bge_flags & BGE_FLAG_5714_FAMILY) #define BGE_IS_575X_PLUS(sc) ((sc)->bge_flags & BGE_FLAG_575X_PLUS) #define BGE_IS_5755_PLUS(sc) ((sc)->bge_flags & BGE_FLAG_5755_PLUS) const struct bge_revision * bge_lookup_rev(uint32_t); const struct bge_vendor * bge_lookup_vendor(uint16_t); typedef int (*bge_eaddr_fcn_t)(struct bge_softc *, uint8_t[]); static int bge_probe(device_t); static int bge_attach(device_t); static int bge_detach(device_t); static int bge_suspend(device_t); static int bge_resume(device_t); static void bge_release_resources(struct bge_softc *); static void bge_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int bge_dma_alloc(device_t); static void bge_dma_free(struct bge_softc *); static int bge_get_eaddr_fw(struct bge_softc *sc, uint8_t ether_addr[]); static int bge_get_eaddr_mem(struct bge_softc *, uint8_t[]); static int bge_get_eaddr_nvram(struct bge_softc *, uint8_t[]); static int bge_get_eaddr_eeprom(struct bge_softc *, uint8_t[]); static int bge_get_eaddr(struct bge_softc *, uint8_t[]); static void bge_txeof(struct bge_softc *, uint16_t); static int bge_rxeof(struct bge_softc *, uint16_t, int); static void bge_asf_driver_up (struct bge_softc *); static void bge_tick(void *); static void bge_stats_update(struct bge_softc *); static void bge_stats_update_regs(struct bge_softc *); static struct mbuf *bge_setup_tso(struct bge_softc *, struct mbuf *, uint16_t *); static int bge_encap(struct bge_softc *, struct mbuf **, uint32_t *); static void bge_intr(void *); static int bge_msi_intr(void *); static void bge_intr_task(void *, int); static void bge_start_locked(struct ifnet *); static void bge_start(struct ifnet *); static int bge_ioctl(struct ifnet *, u_long, caddr_t); static void bge_init_locked(struct bge_softc *); static void bge_init(void *); static void bge_stop(struct bge_softc *); static void bge_watchdog(struct bge_softc *); static int bge_shutdown(device_t); static int bge_ifmedia_upd_locked(struct ifnet *); static int bge_ifmedia_upd(struct ifnet *); static void bge_ifmedia_sts(struct ifnet *, struct ifmediareq *); static uint8_t bge_nvram_getbyte(struct bge_softc *, int, uint8_t *); static int bge_read_nvram(struct bge_softc *, caddr_t, int, int); static uint8_t bge_eeprom_getbyte(struct bge_softc *, int, uint8_t *); static int bge_read_eeprom(struct bge_softc *, caddr_t, int, int); static void bge_setpromisc(struct bge_softc *); static void bge_setmulti(struct bge_softc *); static void bge_setvlan(struct bge_softc *); static int bge_newbuf_std(struct bge_softc *, int); static int bge_newbuf_jumbo(struct bge_softc *, int); static int bge_init_rx_ring_std(struct bge_softc *); static void bge_free_rx_ring_std(struct bge_softc *); static int bge_init_rx_ring_jumbo(struct bge_softc *); static void bge_free_rx_ring_jumbo(struct bge_softc *); static void bge_free_tx_ring(struct bge_softc *); static int bge_init_tx_ring(struct bge_softc *); static int bge_chipinit(struct bge_softc *); static int bge_blockinit(struct bge_softc *); static int bge_has_eaddr(struct bge_softc *); static uint32_t bge_readmem_ind(struct bge_softc *, int); static void bge_writemem_ind(struct bge_softc *, int, int); static void bge_writembx(struct bge_softc *, int, int); #ifdef notdef static uint32_t bge_readreg_ind(struct bge_softc *, int); #endif static void bge_writemem_direct(struct bge_softc *, int, int); static void bge_writereg_ind(struct bge_softc *, int, int); static void bge_set_max_readrq(struct bge_softc *); static int bge_miibus_readreg(device_t, int, int); static int bge_miibus_writereg(device_t, int, int, int); static void bge_miibus_statchg(device_t); #ifdef DEVICE_POLLING static int bge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count); #endif #define BGE_RESET_START 1 #define BGE_RESET_STOP 2 static void bge_sig_post_reset(struct bge_softc *, int); static void bge_sig_legacy(struct bge_softc *, int); static void bge_sig_pre_reset(struct bge_softc *, int); static int bge_reset(struct bge_softc *); static void bge_link_upd(struct bge_softc *); /* * The BGE_REGISTER_DEBUG option is only for low-level debugging. It may * leak information to untrusted users. It is also known to cause alignment * traps on certain architectures. */ #ifdef BGE_REGISTER_DEBUG static int bge_sysctl_debug_info(SYSCTL_HANDLER_ARGS); static int bge_sysctl_reg_read(SYSCTL_HANDLER_ARGS); static int bge_sysctl_mem_read(SYSCTL_HANDLER_ARGS); #endif static void bge_add_sysctls(struct bge_softc *); static int bge_sysctl_stats(SYSCTL_HANDLER_ARGS); static device_method_t bge_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bge_probe), DEVMETHOD(device_attach, bge_attach), DEVMETHOD(device_detach, bge_detach), DEVMETHOD(device_shutdown, bge_shutdown), DEVMETHOD(device_suspend, bge_suspend), DEVMETHOD(device_resume, bge_resume), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, bge_miibus_readreg), DEVMETHOD(miibus_writereg, bge_miibus_writereg), DEVMETHOD(miibus_statchg, bge_miibus_statchg), { 0, 0 } }; static driver_t bge_driver = { "bge", bge_methods, sizeof(struct bge_softc) }; static devclass_t bge_devclass; DRIVER_MODULE(bge, pci, bge_driver, bge_devclass, 0, 0); DRIVER_MODULE(miibus, bge, miibus_driver, miibus_devclass, 0, 0); static int bge_allow_asf = 1; TUNABLE_INT("hw.bge.allow_asf", &bge_allow_asf); SYSCTL_NODE(_hw, OID_AUTO, bge, CTLFLAG_RD, 0, "BGE driver parameters"); SYSCTL_INT(_hw_bge, OID_AUTO, allow_asf, CTLFLAG_RD, &bge_allow_asf, 0, "Allow ASF mode if available"); #define SPARC64_BLADE_1500_MODEL "SUNW,Sun-Blade-1500" #define SPARC64_BLADE_1500_PATH_BGE "/pci@1f,700000/network@2" #define SPARC64_BLADE_2500_MODEL "SUNW,Sun-Blade-2500" #define SPARC64_BLADE_2500_PATH_BGE "/pci@1c,600000/network@3" #define SPARC64_OFW_SUBVENDOR "subsystem-vendor-id" static int bge_has_eaddr(struct bge_softc *sc) { #ifdef __sparc64__ char buf[sizeof(SPARC64_BLADE_1500_PATH_BGE)]; device_t dev; uint32_t subvendor; dev = sc->bge_dev; /* * The on-board BGEs found in sun4u machines aren't fitted with * an EEPROM which means that we have to obtain the MAC address * via OFW and that some tests will always fail. We distinguish * such BGEs by the subvendor ID, which also has to be obtained * from OFW instead of the PCI configuration space as the latter * indicates Broadcom as the subvendor of the netboot interface. * For early Blade 1500 and 2500 we even have to check the OFW * device path as the subvendor ID always defaults to Broadcom * there. */ if (OF_getprop(ofw_bus_get_node(dev), SPARC64_OFW_SUBVENDOR, &subvendor, sizeof(subvendor)) == sizeof(subvendor) && subvendor == SUN_VENDORID) return (0); memset(buf, 0, sizeof(buf)); if (OF_package_to_path(ofw_bus_get_node(dev), buf, sizeof(buf)) > 0) { if (strcmp(sparc64_model, SPARC64_BLADE_1500_MODEL) == 0 && strcmp(buf, SPARC64_BLADE_1500_PATH_BGE) == 0) return (0); if (strcmp(sparc64_model, SPARC64_BLADE_2500_MODEL) == 0 && strcmp(buf, SPARC64_BLADE_2500_PATH_BGE) == 0) return (0); } #endif return (1); } static uint32_t bge_readmem_ind(struct bge_softc *sc, int off) { device_t dev; uint32_t val; dev = sc->bge_dev; pci_write_config(dev, BGE_PCI_MEMWIN_BASEADDR, off, 4); val = pci_read_config(dev, BGE_PCI_MEMWIN_DATA, 4); pci_write_config(dev, BGE_PCI_MEMWIN_BASEADDR, 0, 4); return (val); } static void bge_writemem_ind(struct bge_softc *sc, int off, int val) { device_t dev; dev = sc->bge_dev; pci_write_config(dev, BGE_PCI_MEMWIN_BASEADDR, off, 4); pci_write_config(dev, BGE_PCI_MEMWIN_DATA, val, 4); pci_write_config(dev, BGE_PCI_MEMWIN_BASEADDR, 0, 4); } /* * PCI Express only */ static void bge_set_max_readrq(struct bge_softc *sc) { device_t dev; uint16_t val; dev = sc->bge_dev; val = pci_read_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_CTL, 2); if ((val & PCIM_EXP_CTL_MAX_READ_REQUEST) != BGE_PCIE_DEVCTL_MAX_READRQ_4096) { if (bootverbose) device_printf(dev, "adjust device control 0x%04x ", val); val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; val |= BGE_PCIE_DEVCTL_MAX_READRQ_4096; pci_write_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_CTL, val, 2); if (bootverbose) printf("-> 0x%04x\n", val); } } #ifdef notdef static uint32_t bge_readreg_ind(struct bge_softc *sc, int off) { device_t dev; dev = sc->bge_dev; pci_write_config(dev, BGE_PCI_REG_BASEADDR, off, 4); return (pci_read_config(dev, BGE_PCI_REG_DATA, 4)); } #endif static void bge_writereg_ind(struct bge_softc *sc, int off, int val) { device_t dev; dev = sc->bge_dev; pci_write_config(dev, BGE_PCI_REG_BASEADDR, off, 4); pci_write_config(dev, BGE_PCI_REG_DATA, val, 4); } static void bge_writemem_direct(struct bge_softc *sc, int off, int val) { CSR_WRITE_4(sc, off, val); } static void bge_writembx(struct bge_softc *sc, int off, int val) { if (sc->bge_asicrev == BGE_ASICREV_BCM5906) off += BGE_LPMBX_IRQ0_HI - BGE_MBX_IRQ0_HI; CSR_WRITE_4(sc, off, val); } /* * Map a single buffer address. */ static void bge_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct bge_dmamap_arg *ctx; if (error) return; ctx = arg; if (nseg > ctx->bge_maxsegs) { ctx->bge_maxsegs = 0; return; } ctx->bge_busaddr = segs->ds_addr; } static uint8_t bge_nvram_getbyte(struct bge_softc *sc, int addr, uint8_t *dest) { uint32_t access, byte = 0; int i; /* Lock. */ CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_SET1); for (i = 0; i < 8000; i++) { if (CSR_READ_4(sc, BGE_NVRAM_SWARB) & BGE_NVRAMSWARB_GNT1) break; DELAY(20); } if (i == 8000) return (1); /* Enable access. */ access = CSR_READ_4(sc, BGE_NVRAM_ACCESS); CSR_WRITE_4(sc, BGE_NVRAM_ACCESS, access | BGE_NVRAMACC_ENABLE); CSR_WRITE_4(sc, BGE_NVRAM_ADDR, addr & 0xfffffffc); CSR_WRITE_4(sc, BGE_NVRAM_CMD, BGE_NVRAM_READCMD); for (i = 0; i < BGE_TIMEOUT * 10; i++) { DELAY(10); if (CSR_READ_4(sc, BGE_NVRAM_CMD) & BGE_NVRAMCMD_DONE) { DELAY(10); break; } } if (i == BGE_TIMEOUT * 10) { if_printf(sc->bge_ifp, "nvram read timed out\n"); return (1); } /* Get result. */ byte = CSR_READ_4(sc, BGE_NVRAM_RDDATA); *dest = (bswap32(byte) >> ((addr % 4) * 8)) & 0xFF; /* Disable access. */ CSR_WRITE_4(sc, BGE_NVRAM_ACCESS, access); /* Unlock. */ CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_CLR1); CSR_READ_4(sc, BGE_NVRAM_SWARB); return (0); } /* * Read a sequence of bytes from NVRAM. */ static int bge_read_nvram(struct bge_softc *sc, caddr_t dest, int off, int cnt) { int err = 0, i; uint8_t byte = 0; if (sc->bge_asicrev != BGE_ASICREV_BCM5906) return (1); for (i = 0; i < cnt; i++) { err = bge_nvram_getbyte(sc, off + i, &byte); if (err) break; *(dest + i) = byte; } return (err ? 1 : 0); } /* * Read a byte of data stored in the EEPROM at address 'addr.' The * BCM570x supports both the traditional bitbang interface and an * auto access interface for reading the EEPROM. We use the auto * access method. */ static uint8_t bge_eeprom_getbyte(struct bge_softc *sc, int addr, uint8_t *dest) { int i; uint32_t byte = 0; /* * Enable use of auto EEPROM access so we can avoid * having to use the bitbang method. */ BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_AUTO_EEPROM); /* Reset the EEPROM, load the clock period. */ CSR_WRITE_4(sc, BGE_EE_ADDR, BGE_EEADDR_RESET | BGE_EEHALFCLK(BGE_HALFCLK_384SCL)); DELAY(20); /* Issue the read EEPROM command. */ CSR_WRITE_4(sc, BGE_EE_ADDR, BGE_EE_READCMD | addr); /* Wait for completion */ for(i = 0; i < BGE_TIMEOUT * 10; i++) { DELAY(10); if (CSR_READ_4(sc, BGE_EE_ADDR) & BGE_EEADDR_DONE) break; } if (i == BGE_TIMEOUT * 10) { device_printf(sc->bge_dev, "EEPROM read timed out\n"); return (1); } /* Get result. */ byte = CSR_READ_4(sc, BGE_EE_DATA); *dest = (byte >> ((addr % 4) * 8)) & 0xFF; return (0); } /* * Read a sequence of bytes from the EEPROM. */ static int bge_read_eeprom(struct bge_softc *sc, caddr_t dest, int off, int cnt) { int i, error = 0; uint8_t byte = 0; for (i = 0; i < cnt; i++) { error = bge_eeprom_getbyte(sc, off + i, &byte); if (error) break; *(dest + i) = byte; } return (error ? 1 : 0); } static int bge_miibus_readreg(device_t dev, int phy, int reg) { struct bge_softc *sc; uint32_t val, autopoll; int i; sc = device_get_softc(dev); /* * Broadcom's own driver always assumes the internal * PHY is at GMII address 1. On some chips, the PHY responds * to accesses at all addresses, which could cause us to * bogusly attach the PHY 32 times at probe type. Always * restricting the lookup to address 1 is simpler than * trying to figure out which chips revisions should be * special-cased. */ if (phy != 1) return (0); /* Reading with autopolling on may trigger PCI errors */ autopoll = CSR_READ_4(sc, BGE_MI_MODE); if (autopoll & BGE_MIMODE_AUTOPOLL) { BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); DELAY(40); } CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_READ | BGE_MICOMM_BUSY | BGE_MIPHY(phy) | BGE_MIREG(reg)); for (i = 0; i < BGE_TIMEOUT; i++) { DELAY(10); val = CSR_READ_4(sc, BGE_MI_COMM); if (!(val & BGE_MICOMM_BUSY)) break; } if (i == BGE_TIMEOUT) { device_printf(sc->bge_dev, "PHY read timed out (phy %d, reg %d, val 0x%08x)\n", phy, reg, val); val = 0; goto done; } DELAY(5); val = CSR_READ_4(sc, BGE_MI_COMM); done: if (autopoll & BGE_MIMODE_AUTOPOLL) { BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); DELAY(40); } if (val & BGE_MICOMM_READFAIL) return (0); return (val & 0xFFFF); } static int bge_miibus_writereg(device_t dev, int phy, int reg, int val) { struct bge_softc *sc; uint32_t autopoll; int i; sc = device_get_softc(dev); if (sc->bge_asicrev == BGE_ASICREV_BCM5906 && (reg == BRGPHY_MII_1000CTL || reg == BRGPHY_MII_AUXCTL)) return(0); /* Reading with autopolling on may trigger PCI errors */ autopoll = CSR_READ_4(sc, BGE_MI_MODE); if (autopoll & BGE_MIMODE_AUTOPOLL) { BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); DELAY(40); } CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE | BGE_MICOMM_BUSY | BGE_MIPHY(phy) | BGE_MIREG(reg) | val); for (i = 0; i < BGE_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, BGE_MI_COMM) & BGE_MICOMM_BUSY)) { DELAY(5); CSR_READ_4(sc, BGE_MI_COMM); /* dummy read */ break; } } if (i == BGE_TIMEOUT) { device_printf(sc->bge_dev, "PHY write timed out (phy %d, reg %d, val %d)\n", phy, reg, val); return (0); } if (autopoll & BGE_MIMODE_AUTOPOLL) { BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL); DELAY(40); } return (0); } static void bge_miibus_statchg(device_t dev) { struct bge_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->bge_miibus); BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_PORTMODE); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) BGE_SETBIT(sc, BGE_MAC_MODE, BGE_PORTMODE_GMII); else BGE_SETBIT(sc, BGE_MAC_MODE, BGE_PORTMODE_MII); if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX); else BGE_SETBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX); } /* * Intialize a standard receive ring descriptor. */ static int bge_newbuf_std(struct bge_softc *sc, int i) { struct mbuf *m; struct bge_rx_bd *r; bus_dma_segment_t segs[1]; bus_dmamap_t map; int error, nsegs; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; if ((sc->bge_flags & BGE_FLAG_RX_ALIGNBUG) == 0) m_adj(m, ETHER_ALIGN); error = bus_dmamap_load_mbuf_sg(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_sparemap, m, segs, &nsegs, 0); if (error != 0) { m_freem(m); return (error); } if (sc->bge_cdata.bge_rx_std_chain[i] != NULL) { bus_dmamap_sync(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_dmamap[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_dmamap[i]); } map = sc->bge_cdata.bge_rx_std_dmamap[i]; sc->bge_cdata.bge_rx_std_dmamap[i] = sc->bge_cdata.bge_rx_std_sparemap; sc->bge_cdata.bge_rx_std_sparemap = map; sc->bge_cdata.bge_rx_std_chain[i] = m; r = &sc->bge_ldata.bge_rx_std_ring[sc->bge_std]; r->bge_addr.bge_addr_lo = BGE_ADDR_LO(segs[0].ds_addr); r->bge_addr.bge_addr_hi = BGE_ADDR_HI(segs[0].ds_addr); r->bge_flags = BGE_RXBDFLAG_END; r->bge_len = segs[0].ds_len; r->bge_idx = i; bus_dmamap_sync(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_dmamap[i], BUS_DMASYNC_PREREAD); return (0); } /* * Initialize a jumbo receive ring descriptor. This allocates * a jumbo buffer from the pool managed internally by the driver. */ static int bge_newbuf_jumbo(struct bge_softc *sc, int i) { bus_dma_segment_t segs[BGE_NSEG_JUMBO]; bus_dmamap_t map; struct bge_extrx_bd *r; struct mbuf *m; int error, nsegs; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return (ENOBUFS); m_cljget(m, M_DONTWAIT, MJUM9BYTES); if (!(m->m_flags & M_EXT)) { m_freem(m); return (ENOBUFS); } m->m_len = m->m_pkthdr.len = MJUM9BYTES; if ((sc->bge_flags & BGE_FLAG_RX_ALIGNBUG) == 0) m_adj(m, ETHER_ALIGN); error = bus_dmamap_load_mbuf_sg(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_sparemap, m, segs, &nsegs, 0); if (error != 0) { m_freem(m); return (error); } if (sc->bge_cdata.bge_rx_jumbo_chain[i] == NULL) { bus_dmamap_sync(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_dmamap[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_dmamap[i]); } map = sc->bge_cdata.bge_rx_jumbo_dmamap[i]; sc->bge_cdata.bge_rx_jumbo_dmamap[i] = sc->bge_cdata.bge_rx_jumbo_sparemap; sc->bge_cdata.bge_rx_jumbo_sparemap = map; sc->bge_cdata.bge_rx_jumbo_chain[i] = m; /* * Fill in the extended RX buffer descriptor. */ r = &sc->bge_ldata.bge_rx_jumbo_ring[sc->bge_jumbo]; r->bge_flags = BGE_RXBDFLAG_JUMBO_RING | BGE_RXBDFLAG_END; r->bge_idx = i; r->bge_len3 = r->bge_len2 = r->bge_len1 = 0; switch (nsegs) { case 4: r->bge_addr3.bge_addr_lo = BGE_ADDR_LO(segs[3].ds_addr); r->bge_addr3.bge_addr_hi = BGE_ADDR_HI(segs[3].ds_addr); r->bge_len3 = segs[3].ds_len; case 3: r->bge_addr2.bge_addr_lo = BGE_ADDR_LO(segs[2].ds_addr); r->bge_addr2.bge_addr_hi = BGE_ADDR_HI(segs[2].ds_addr); r->bge_len2 = segs[2].ds_len; case 2: r->bge_addr1.bge_addr_lo = BGE_ADDR_LO(segs[1].ds_addr); r->bge_addr1.bge_addr_hi = BGE_ADDR_HI(segs[1].ds_addr); r->bge_len1 = segs[1].ds_len; case 1: r->bge_addr0.bge_addr_lo = BGE_ADDR_LO(segs[0].ds_addr); r->bge_addr0.bge_addr_hi = BGE_ADDR_HI(segs[0].ds_addr); r->bge_len0 = segs[0].ds_len; break; default: panic("%s: %d segments\n", __func__, nsegs); } bus_dmamap_sync(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_dmamap[i], BUS_DMASYNC_PREREAD); return (0); } /* * The standard receive ring has 512 entries in it. At 2K per mbuf cluster, * that's 1MB or memory, which is a lot. For now, we fill only the first * 256 ring entries and hope that our CPU is fast enough to keep up with * the NIC. */ static int bge_init_rx_ring_std(struct bge_softc *sc) { int error, i; bzero(sc->bge_ldata.bge_rx_std_ring, BGE_STD_RX_RING_SZ); sc->bge_std = 0; for (i = 0; i < BGE_SSLOTS; i++) { if ((error = bge_newbuf_std(sc, i)) != 0) return (error); BGE_INC(sc->bge_std, BGE_STD_RX_RING_CNT); }; bus_dmamap_sync(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map, BUS_DMASYNC_PREWRITE); sc->bge_std = i - 1; bge_writembx(sc, BGE_MBX_RX_STD_PROD_LO, sc->bge_std); return (0); } static void bge_free_rx_ring_std(struct bge_softc *sc) { int i; for (i = 0; i < BGE_STD_RX_RING_CNT; i++) { if (sc->bge_cdata.bge_rx_std_chain[i] != NULL) { bus_dmamap_sync(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_dmamap[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_dmamap[i]); m_freem(sc->bge_cdata.bge_rx_std_chain[i]); sc->bge_cdata.bge_rx_std_chain[i] = NULL; } bzero((char *)&sc->bge_ldata.bge_rx_std_ring[i], sizeof(struct bge_rx_bd)); } } static int bge_init_rx_ring_jumbo(struct bge_softc *sc) { struct bge_rcb *rcb; int error, i; bzero(sc->bge_ldata.bge_rx_jumbo_ring, BGE_JUMBO_RX_RING_SZ); sc->bge_jumbo = 0; for (i = 0; i < BGE_JUMBO_RX_RING_CNT; i++) { if ((error = bge_newbuf_jumbo(sc, i)) != 0) return (error); BGE_INC(sc->bge_jumbo, BGE_JUMBO_RX_RING_CNT); }; bus_dmamap_sync(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_cdata.bge_rx_jumbo_ring_map, BUS_DMASYNC_PREWRITE); sc->bge_jumbo = i - 1; rcb = &sc->bge_ldata.bge_info.bge_jumbo_rx_rcb; rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(0, BGE_RCB_FLAG_USE_EXT_RX_BD); CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_MAXLEN_FLAGS, rcb->bge_maxlen_flags); bge_writembx(sc, BGE_MBX_RX_JUMBO_PROD_LO, sc->bge_jumbo); return (0); } static void bge_free_rx_ring_jumbo(struct bge_softc *sc) { int i; for (i = 0; i < BGE_JUMBO_RX_RING_CNT; i++) { if (sc->bge_cdata.bge_rx_jumbo_chain[i] != NULL) { bus_dmamap_sync(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_dmamap[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_dmamap[i]); m_freem(sc->bge_cdata.bge_rx_jumbo_chain[i]); sc->bge_cdata.bge_rx_jumbo_chain[i] = NULL; } bzero((char *)&sc->bge_ldata.bge_rx_jumbo_ring[i], sizeof(struct bge_extrx_bd)); } } static void bge_free_tx_ring(struct bge_softc *sc) { int i; if (sc->bge_ldata.bge_tx_ring == NULL) return; for (i = 0; i < BGE_TX_RING_CNT; i++) { if (sc->bge_cdata.bge_tx_chain[i] != NULL) { bus_dmamap_sync(sc->bge_cdata.bge_tx_mtag, sc->bge_cdata.bge_tx_dmamap[i], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->bge_cdata.bge_tx_mtag, sc->bge_cdata.bge_tx_dmamap[i]); m_freem(sc->bge_cdata.bge_tx_chain[i]); sc->bge_cdata.bge_tx_chain[i] = NULL; } bzero((char *)&sc->bge_ldata.bge_tx_ring[i], sizeof(struct bge_tx_bd)); } } static int bge_init_tx_ring(struct bge_softc *sc) { sc->bge_txcnt = 0; sc->bge_tx_saved_considx = 0; bzero(sc->bge_ldata.bge_tx_ring, BGE_TX_RING_SZ); bus_dmamap_sync(sc->bge_cdata.bge_tx_ring_tag, sc->bge_cdata.bge_tx_ring_map, BUS_DMASYNC_PREWRITE); /* Initialize transmit producer index for host-memory send ring. */ sc->bge_tx_prodidx = 0; bge_writembx(sc, BGE_MBX_TX_HOST_PROD0_LO, sc->bge_tx_prodidx); /* 5700 b2 errata */ if (sc->bge_chiprev == BGE_CHIPREV_5700_BX) bge_writembx(sc, BGE_MBX_TX_HOST_PROD0_LO, sc->bge_tx_prodidx); /* NIC-memory send ring not used; initialize to zero. */ bge_writembx(sc, BGE_MBX_TX_NIC_PROD0_LO, 0); /* 5700 b2 errata */ if (sc->bge_chiprev == BGE_CHIPREV_5700_BX) bge_writembx(sc, BGE_MBX_TX_NIC_PROD0_LO, 0); return (0); } static void bge_setpromisc(struct bge_softc *sc) { struct ifnet *ifp; BGE_LOCK_ASSERT(sc); ifp = sc->bge_ifp; /* Enable or disable promiscuous mode as needed. */ if (ifp->if_flags & IFF_PROMISC) BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_RX_PROMISC); else BGE_CLRBIT(sc, BGE_RX_MODE, BGE_RXMODE_RX_PROMISC); } static void bge_setmulti(struct bge_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; uint32_t hashes[4] = { 0, 0, 0, 0 }; int h, i; BGE_LOCK_ASSERT(sc); ifp = sc->bge_ifp; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { for (i = 0; i < 4; i++) CSR_WRITE_4(sc, BGE_MAR0 + (i * 4), 0xFFFFFFFF); return; } /* First, zot all the existing filters. */ for (i = 0; i < 4; i++) CSR_WRITE_4(sc, BGE_MAR0 + (i * 4), 0); /* Now program new ones. */ if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_le(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) & 0x7F; hashes[(h & 0x60) >> 5] |= 1 << (h & 0x1F); } if_maddr_runlock(ifp); for (i = 0; i < 4; i++) CSR_WRITE_4(sc, BGE_MAR0 + (i * 4), hashes[i]); } static void bge_setvlan(struct bge_softc *sc) { struct ifnet *ifp; BGE_LOCK_ASSERT(sc); ifp = sc->bge_ifp; /* Enable or disable VLAN tag stripping as needed. */ if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) BGE_CLRBIT(sc, BGE_RX_MODE, BGE_RXMODE_RX_KEEP_VLAN_DIAG); else BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_RX_KEEP_VLAN_DIAG); } static void bge_sig_pre_reset(sc, type) struct bge_softc *sc; int type; { /* * Some chips don't like this so only do this if ASF is enabled */ if (sc->bge_asf_mode) bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM, BGE_MAGIC_NUMBER); if (sc->bge_asf_mode & ASF_NEW_HANDSHAKE) { switch (type) { case BGE_RESET_START: bge_writemem_ind(sc, BGE_SDI_STATUS, 0x1); /* START */ break; case BGE_RESET_STOP: bge_writemem_ind(sc, BGE_SDI_STATUS, 0x2); /* UNLOAD */ break; } } } static void bge_sig_post_reset(sc, type) struct bge_softc *sc; int type; { if (sc->bge_asf_mode & ASF_NEW_HANDSHAKE) { switch (type) { case BGE_RESET_START: bge_writemem_ind(sc, BGE_SDI_STATUS, 0x80000001); /* START DONE */ break; case BGE_RESET_STOP: bge_writemem_ind(sc, BGE_SDI_STATUS, 0x80000002); break; } } } static void bge_sig_legacy(sc, type) struct bge_softc *sc; int type; { if (sc->bge_asf_mode) { switch (type) { case BGE_RESET_START: bge_writemem_ind(sc, BGE_SDI_STATUS, 0x1); /* START */ break; case BGE_RESET_STOP: bge_writemem_ind(sc, BGE_SDI_STATUS, 0x2); /* UNLOAD */ break; } } } void bge_stop_fw(struct bge_softc *); void bge_stop_fw(sc) struct bge_softc *sc; { int i; if (sc->bge_asf_mode) { bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM_FW, BGE_FW_PAUSE); CSR_WRITE_4(sc, BGE_CPU_EVENT, CSR_READ_4(sc, BGE_CPU_EVENT) | (1 << 14)); for (i = 0; i < 100; i++ ) { if (!(CSR_READ_4(sc, BGE_CPU_EVENT) & (1 << 14))) break; DELAY(10); } } } /* * Do endian, PCI and DMA initialization. */ static int bge_chipinit(struct bge_softc *sc) { uint32_t dma_rw_ctl; int i; /* Set endianness before we access any non-PCI registers. */ pci_write_config(sc->bge_dev, BGE_PCI_MISC_CTL, BGE_INIT, 4); /* Clear the MAC control register */ CSR_WRITE_4(sc, BGE_MAC_MODE, 0); /* * Clear the MAC statistics block in the NIC's * internal memory. */ for (i = BGE_STATS_BLOCK; i < BGE_STATS_BLOCK_END + 1; i += sizeof(uint32_t)) BGE_MEMWIN_WRITE(sc, i, 0); for (i = BGE_STATUS_BLOCK; i < BGE_STATUS_BLOCK_END + 1; i += sizeof(uint32_t)) BGE_MEMWIN_WRITE(sc, i, 0); /* * Set up the PCI DMA control register. */ dma_rw_ctl = BGE_PCIDMARWCTL_RD_CMD_SHIFT(6) | BGE_PCIDMARWCTL_WR_CMD_SHIFT(7); if (sc->bge_flags & BGE_FLAG_PCIE) { /* Read watermark not used, 128 bytes for write. */ dma_rw_ctl |= BGE_PCIDMARWCTL_WR_WAT_SHIFT(3); } else if (sc->bge_flags & BGE_FLAG_PCIX) { if (BGE_IS_5714_FAMILY(sc)) { /* 256 bytes for read and write. */ dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(2) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(2); dma_rw_ctl |= (sc->bge_asicrev == BGE_ASICREV_BCM5780) ? BGE_PCIDMARWCTL_ONEDMA_ATONCE_GLOBAL : BGE_PCIDMARWCTL_ONEDMA_ATONCE_LOCAL; } else if (sc->bge_asicrev == BGE_ASICREV_BCM5704) { /* 1536 bytes for read, 384 bytes for write. */ dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(7) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(3); } else { /* 384 bytes for read and write. */ dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(3) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(3) | 0x0F; } if (sc->bge_asicrev == BGE_ASICREV_BCM5703 || sc->bge_asicrev == BGE_ASICREV_BCM5704) { uint32_t tmp; /* Set ONE_DMA_AT_ONCE for hardware workaround. */ tmp = CSR_READ_4(sc, BGE_PCI_CLKCTL) & 0x1F; if (tmp == 6 || tmp == 7) dma_rw_ctl |= BGE_PCIDMARWCTL_ONEDMA_ATONCE_GLOBAL; /* Set PCI-X DMA write workaround. */ dma_rw_ctl |= BGE_PCIDMARWCTL_ASRT_ALL_BE; } } else { /* Conventional PCI bus: 256 bytes for read and write. */ dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(7) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(7); if (sc->bge_asicrev != BGE_ASICREV_BCM5705 && sc->bge_asicrev != BGE_ASICREV_BCM5750) dma_rw_ctl |= 0x0F; } if (sc->bge_asicrev == BGE_ASICREV_BCM5700 || sc->bge_asicrev == BGE_ASICREV_BCM5701) dma_rw_ctl |= BGE_PCIDMARWCTL_USE_MRM | BGE_PCIDMARWCTL_ASRT_ALL_BE; if (sc->bge_asicrev == BGE_ASICREV_BCM5703 || sc->bge_asicrev == BGE_ASICREV_BCM5704) dma_rw_ctl &= ~BGE_PCIDMARWCTL_MINDMA; pci_write_config(sc->bge_dev, BGE_PCI_DMA_RW_CTL, dma_rw_ctl, 4); /* * Set up general mode register. */ CSR_WRITE_4(sc, BGE_MODE_CTL, BGE_DMA_SWAP_OPTIONS | BGE_MODECTL_MAC_ATTN_INTR | BGE_MODECTL_HOST_SEND_BDS | BGE_MODECTL_TX_NO_PHDR_CSUM); /* * BCM5701 B5 have a bug causing data corruption when using * 64-bit DMA reads, which can be terminated early and then * completed later as 32-bit accesses, in combination with * certain bridges. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5701 && sc->bge_chipid == BGE_CHIPID_BCM5701_B5) BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_FORCE_PCI32); /* * Tell the firmware the driver is running */ if (sc->bge_asf_mode & ASF_STACKUP) BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); /* * Disable memory write invalidate. Apparently it is not supported * properly by these devices. Also ensure that INTx isn't disabled, * as these chips need it even when using MSI. */ PCI_CLRBIT(sc->bge_dev, BGE_PCI_CMD, PCIM_CMD_INTxDIS | PCIM_CMD_MWIEN, 4); /* Set the timer prescaler (always 66Mhz) */ CSR_WRITE_4(sc, BGE_MISC_CFG, BGE_32BITTIME_66MHZ); /* XXX: The Linux tg3 driver does this at the start of brgphy_reset. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5906) { DELAY(40); /* XXX */ /* Put PHY into ready state */ BGE_CLRBIT(sc, BGE_MISC_CFG, BGE_MISCCFG_EPHY_IDDQ); CSR_READ_4(sc, BGE_MISC_CFG); /* Flush */ DELAY(40); } return (0); } static int bge_blockinit(struct bge_softc *sc) { struct bge_rcb *rcb; bus_size_t vrcb; bge_hostaddr taddr; uint32_t val; int i; /* * Initialize the memory window pointer register so that * we can access the first 32K of internal NIC RAM. This will * allow us to set up the TX send ring RCBs and the RX return * ring RCBs, plus other things which live in NIC memory. */ CSR_WRITE_4(sc, BGE_PCI_MEMWIN_BASEADDR, 0); /* Note: the BCM5704 has a smaller mbuf space than other chips. */ if (!(BGE_IS_5705_PLUS(sc))) { /* Configure mbuf memory pool */ CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_BASEADDR, BGE_BUFFPOOL_1); if (sc->bge_asicrev == BGE_ASICREV_BCM5704) CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_LEN, 0x10000); else CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_LEN, 0x18000); /* Configure DMA resource pool */ CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_BASEADDR, BGE_DMA_DESCRIPTORS); CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_LEN, 0x2000); } /* Configure mbuf pool watermarks */ if (!BGE_IS_5705_PLUS(sc)) { CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_READDMA_LOWAT, 0x50); CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_MACRX_LOWAT, 0x20); CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_HIWAT, 0x60); } else if (sc->bge_asicrev == BGE_ASICREV_BCM5906) { CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_READDMA_LOWAT, 0x0); CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_MACRX_LOWAT, 0x04); CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_HIWAT, 0x10); } else { CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_READDMA_LOWAT, 0x0); CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_MACRX_LOWAT, 0x10); CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_HIWAT, 0x60); } /* Configure DMA resource watermarks */ CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_LOWAT, 5); CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_HIWAT, 10); /* Enable buffer manager */ if (!(BGE_IS_5705_PLUS(sc))) { CSR_WRITE_4(sc, BGE_BMAN_MODE, BGE_BMANMODE_ENABLE | BGE_BMANMODE_LOMBUF_ATTN); /* Poll for buffer manager start indication */ for (i = 0; i < BGE_TIMEOUT; i++) { DELAY(10); if (CSR_READ_4(sc, BGE_BMAN_MODE) & BGE_BMANMODE_ENABLE) break; } if (i == BGE_TIMEOUT) { device_printf(sc->bge_dev, "buffer manager failed to start\n"); return (ENXIO); } } /* Enable flow-through queues */ CSR_WRITE_4(sc, BGE_FTQ_RESET, 0xFFFFFFFF); CSR_WRITE_4(sc, BGE_FTQ_RESET, 0); /* Wait until queue initialization is complete */ for (i = 0; i < BGE_TIMEOUT; i++) { DELAY(10); if (CSR_READ_4(sc, BGE_FTQ_RESET) == 0) break; } if (i == BGE_TIMEOUT) { device_printf(sc->bge_dev, "flow-through queue init failed\n"); return (ENXIO); } /* Initialize the standard RX ring control block */ rcb = &sc->bge_ldata.bge_info.bge_std_rx_rcb; rcb->bge_hostaddr.bge_addr_lo = BGE_ADDR_LO(sc->bge_ldata.bge_rx_std_ring_paddr); rcb->bge_hostaddr.bge_addr_hi = BGE_ADDR_HI(sc->bge_ldata.bge_rx_std_ring_paddr); bus_dmamap_sync(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map, BUS_DMASYNC_PREREAD); if (BGE_IS_5705_PLUS(sc)) rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(512, 0); else rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(BGE_MAX_FRAMELEN, 0); rcb->bge_nicaddr = BGE_STD_RX_RINGS; CSR_WRITE_4(sc, BGE_RX_STD_RCB_HADDR_HI, rcb->bge_hostaddr.bge_addr_hi); CSR_WRITE_4(sc, BGE_RX_STD_RCB_HADDR_LO, rcb->bge_hostaddr.bge_addr_lo); CSR_WRITE_4(sc, BGE_RX_STD_RCB_MAXLEN_FLAGS, rcb->bge_maxlen_flags); CSR_WRITE_4(sc, BGE_RX_STD_RCB_NICADDR, rcb->bge_nicaddr); /* * Initialize the jumbo RX ring control block * We set the 'ring disabled' bit in the flags * field until we're actually ready to start * using this ring (i.e. once we set the MTU * high enough to require it). */ if (BGE_IS_JUMBO_CAPABLE(sc)) { rcb = &sc->bge_ldata.bge_info.bge_jumbo_rx_rcb; rcb->bge_hostaddr.bge_addr_lo = BGE_ADDR_LO(sc->bge_ldata.bge_rx_jumbo_ring_paddr); rcb->bge_hostaddr.bge_addr_hi = BGE_ADDR_HI(sc->bge_ldata.bge_rx_jumbo_ring_paddr); bus_dmamap_sync(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_cdata.bge_rx_jumbo_ring_map, BUS_DMASYNC_PREREAD); rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(0, BGE_RCB_FLAG_USE_EXT_RX_BD | BGE_RCB_FLAG_RING_DISABLED); rcb->bge_nicaddr = BGE_JUMBO_RX_RINGS; CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_HADDR_HI, rcb->bge_hostaddr.bge_addr_hi); CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_HADDR_LO, rcb->bge_hostaddr.bge_addr_lo); CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_MAXLEN_FLAGS, rcb->bge_maxlen_flags); CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_NICADDR, rcb->bge_nicaddr); /* Set up dummy disabled mini ring RCB */ rcb = &sc->bge_ldata.bge_info.bge_mini_rx_rcb; rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(0, BGE_RCB_FLAG_RING_DISABLED); CSR_WRITE_4(sc, BGE_RX_MINI_RCB_MAXLEN_FLAGS, rcb->bge_maxlen_flags); } /* * Set the BD ring replentish thresholds. The recommended * values are 1/8th the number of descriptors allocated to * each ring. * XXX The 5754 requires a lower threshold, so it might be a * requirement of all 575x family chips. The Linux driver sets * the lower threshold for all 5705 family chips as well, but there * are reports that it might not need to be so strict. * * XXX Linux does some extra fiddling here for the 5906 parts as * well. */ if (BGE_IS_5705_PLUS(sc)) val = 8; else val = BGE_STD_RX_RING_CNT / 8; CSR_WRITE_4(sc, BGE_RBDI_STD_REPL_THRESH, val); if (BGE_IS_JUMBO_CAPABLE(sc)) CSR_WRITE_4(sc, BGE_RBDI_JUMBO_REPL_THRESH, BGE_JUMBO_RX_RING_CNT/8); /* * Disable all unused send rings by setting the 'ring disabled' * bit in the flags field of all the TX send ring control blocks. * These are located in NIC memory. */ vrcb = BGE_MEMWIN_START + BGE_SEND_RING_RCB; for (i = 0; i < BGE_TX_RINGS_EXTSSRAM_MAX; i++) { RCB_WRITE_4(sc, vrcb, bge_maxlen_flags, BGE_RCB_MAXLEN_FLAGS(0, BGE_RCB_FLAG_RING_DISABLED)); RCB_WRITE_4(sc, vrcb, bge_nicaddr, 0); vrcb += sizeof(struct bge_rcb); } /* Configure TX RCB 0 (we use only the first ring) */ vrcb = BGE_MEMWIN_START + BGE_SEND_RING_RCB; BGE_HOSTADDR(taddr, sc->bge_ldata.bge_tx_ring_paddr); RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_hi, taddr.bge_addr_hi); RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_lo, taddr.bge_addr_lo); RCB_WRITE_4(sc, vrcb, bge_nicaddr, BGE_NIC_TXRING_ADDR(0, BGE_TX_RING_CNT)); if (!(BGE_IS_5705_PLUS(sc))) RCB_WRITE_4(sc, vrcb, bge_maxlen_flags, BGE_RCB_MAXLEN_FLAGS(BGE_TX_RING_CNT, 0)); /* Disable all unused RX return rings */ vrcb = BGE_MEMWIN_START + BGE_RX_RETURN_RING_RCB; for (i = 0; i < BGE_RX_RINGS_MAX; i++) { RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_hi, 0); RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_lo, 0); RCB_WRITE_4(sc, vrcb, bge_maxlen_flags, BGE_RCB_MAXLEN_FLAGS(sc->bge_return_ring_cnt, BGE_RCB_FLAG_RING_DISABLED)); RCB_WRITE_4(sc, vrcb, bge_nicaddr, 0); bge_writembx(sc, BGE_MBX_RX_CONS0_LO + (i * (sizeof(uint64_t))), 0); vrcb += sizeof(struct bge_rcb); } /* Initialize RX ring indexes */ bge_writembx(sc, BGE_MBX_RX_STD_PROD_LO, 0); if (BGE_IS_JUMBO_CAPABLE(sc)) bge_writembx(sc, BGE_MBX_RX_JUMBO_PROD_LO, 0); if (sc->bge_asicrev == BGE_ASICREV_BCM5700) bge_writembx(sc, BGE_MBX_RX_MINI_PROD_LO, 0); /* * Set up RX return ring 0 * Note that the NIC address for RX return rings is 0x00000000. * The return rings live entirely within the host, so the * nicaddr field in the RCB isn't used. */ vrcb = BGE_MEMWIN_START + BGE_RX_RETURN_RING_RCB; BGE_HOSTADDR(taddr, sc->bge_ldata.bge_rx_return_ring_paddr); RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_hi, taddr.bge_addr_hi); RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_lo, taddr.bge_addr_lo); RCB_WRITE_4(sc, vrcb, bge_nicaddr, 0x00000000); RCB_WRITE_4(sc, vrcb, bge_maxlen_flags, BGE_RCB_MAXLEN_FLAGS(sc->bge_return_ring_cnt, 0)); /* Set random backoff seed for TX */ CSR_WRITE_4(sc, BGE_TX_RANDOM_BACKOFF, IF_LLADDR(sc->bge_ifp)[0] + IF_LLADDR(sc->bge_ifp)[1] + IF_LLADDR(sc->bge_ifp)[2] + IF_LLADDR(sc->bge_ifp)[3] + IF_LLADDR(sc->bge_ifp)[4] + IF_LLADDR(sc->bge_ifp)[5] + BGE_TX_BACKOFF_SEED_MASK); /* Set inter-packet gap */ CSR_WRITE_4(sc, BGE_TX_LENGTHS, 0x2620); /* * Specify which ring to use for packets that don't match * any RX rules. */ CSR_WRITE_4(sc, BGE_RX_RULES_CFG, 0x08); /* * Configure number of RX lists. One interrupt distribution * list, sixteen active lists, one bad frames class. */ CSR_WRITE_4(sc, BGE_RXLP_CFG, 0x181); /* Inialize RX list placement stats mask. */ CSR_WRITE_4(sc, BGE_RXLP_STATS_ENABLE_MASK, 0x007FFFFF); CSR_WRITE_4(sc, BGE_RXLP_STATS_CTL, 0x1); /* Disable host coalescing until we get it set up */ CSR_WRITE_4(sc, BGE_HCC_MODE, 0x00000000); /* Poll to make sure it's shut down. */ for (i = 0; i < BGE_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, BGE_HCC_MODE) & BGE_HCCMODE_ENABLE)) break; } if (i == BGE_TIMEOUT) { device_printf(sc->bge_dev, "host coalescing engine failed to idle\n"); return (ENXIO); } /* Set up host coalescing defaults */ CSR_WRITE_4(sc, BGE_HCC_RX_COAL_TICKS, sc->bge_rx_coal_ticks); CSR_WRITE_4(sc, BGE_HCC_TX_COAL_TICKS, sc->bge_tx_coal_ticks); CSR_WRITE_4(sc, BGE_HCC_RX_MAX_COAL_BDS, sc->bge_rx_max_coal_bds); CSR_WRITE_4(sc, BGE_HCC_TX_MAX_COAL_BDS, sc->bge_tx_max_coal_bds); if (!(BGE_IS_5705_PLUS(sc))) { CSR_WRITE_4(sc, BGE_HCC_RX_COAL_TICKS_INT, 0); CSR_WRITE_4(sc, BGE_HCC_TX_COAL_TICKS_INT, 0); } CSR_WRITE_4(sc, BGE_HCC_RX_MAX_COAL_BDS_INT, 1); CSR_WRITE_4(sc, BGE_HCC_TX_MAX_COAL_BDS_INT, 1); /* Set up address of statistics block */ if (!(BGE_IS_5705_PLUS(sc))) { CSR_WRITE_4(sc, BGE_HCC_STATS_ADDR_HI, BGE_ADDR_HI(sc->bge_ldata.bge_stats_paddr)); CSR_WRITE_4(sc, BGE_HCC_STATS_ADDR_LO, BGE_ADDR_LO(sc->bge_ldata.bge_stats_paddr)); CSR_WRITE_4(sc, BGE_HCC_STATS_BASEADDR, BGE_STATS_BLOCK); CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_BASEADDR, BGE_STATUS_BLOCK); CSR_WRITE_4(sc, BGE_HCC_STATS_TICKS, sc->bge_stat_ticks); } /* Set up address of status block */ CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_ADDR_HI, BGE_ADDR_HI(sc->bge_ldata.bge_status_block_paddr)); CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_ADDR_LO, BGE_ADDR_LO(sc->bge_ldata.bge_status_block_paddr)); sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx = 0; sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx = 0; /* Set up status block size. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5700 && sc->bge_chipid != BGE_CHIPID_BCM5700_C0) val = BGE_STATBLKSZ_FULL; else val = BGE_STATBLKSZ_32BYTE; /* Turn on host coalescing state machine */ CSR_WRITE_4(sc, BGE_HCC_MODE, val | BGE_HCCMODE_ENABLE); /* Turn on RX BD completion state machine and enable attentions */ CSR_WRITE_4(sc, BGE_RBDC_MODE, BGE_RBDCMODE_ENABLE | BGE_RBDCMODE_ATTN); /* Turn on RX list placement state machine */ CSR_WRITE_4(sc, BGE_RXLP_MODE, BGE_RXLPMODE_ENABLE); /* Turn on RX list selector state machine. */ if (!(BGE_IS_5705_PLUS(sc))) CSR_WRITE_4(sc, BGE_RXLS_MODE, BGE_RXLSMODE_ENABLE); val = BGE_MACMODE_TXDMA_ENB | BGE_MACMODE_RXDMA_ENB | BGE_MACMODE_RX_STATS_CLEAR | BGE_MACMODE_TX_STATS_CLEAR | BGE_MACMODE_RX_STATS_ENB | BGE_MACMODE_TX_STATS_ENB | BGE_MACMODE_FRMHDR_DMA_ENB; if (sc->bge_flags & BGE_FLAG_TBI) val |= BGE_PORTMODE_TBI; else if (sc->bge_flags & BGE_FLAG_MII_SERDES) val |= BGE_PORTMODE_GMII; else val |= BGE_PORTMODE_MII; /* Turn on DMA, clear stats */ CSR_WRITE_4(sc, BGE_MAC_MODE, val); /* Set misc. local control, enable interrupts on attentions */ CSR_WRITE_4(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_ONATTN); #ifdef notdef /* Assert GPIO pins for PHY reset */ BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUT0 | BGE_MLC_MISCIO_OUT1 | BGE_MLC_MISCIO_OUT2); BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUTEN0 | BGE_MLC_MISCIO_OUTEN1 | BGE_MLC_MISCIO_OUTEN2); #endif /* Turn on DMA completion state machine */ if (!(BGE_IS_5705_PLUS(sc))) CSR_WRITE_4(sc, BGE_DMAC_MODE, BGE_DMACMODE_ENABLE); val = BGE_WDMAMODE_ENABLE | BGE_WDMAMODE_ALL_ATTNS; /* Enable host coalescing bug fix. */ if (BGE_IS_5755_PLUS(sc)) val |= BGE_WDMAMODE_STATUS_TAG_FIX; /* Turn on write DMA state machine */ CSR_WRITE_4(sc, BGE_WDMA_MODE, val); DELAY(40); /* Turn on read DMA state machine */ val = BGE_RDMAMODE_ENABLE | BGE_RDMAMODE_ALL_ATTNS; if (sc->bge_asicrev == BGE_ASICREV_BCM5784 || sc->bge_asicrev == BGE_ASICREV_BCM5785 || sc->bge_asicrev == BGE_ASICREV_BCM57780) val |= BGE_RDMAMODE_BD_SBD_CRPT_ATTN | BGE_RDMAMODE_MBUF_RBD_CRPT_ATTN | BGE_RDMAMODE_MBUF_SBD_CRPT_ATTN; if (sc->bge_flags & BGE_FLAG_PCIE) val |= BGE_RDMAMODE_FIFO_LONG_BURST; if (sc->bge_flags & BGE_FLAG_TSO) val |= BGE_RDMAMODE_TSO4_ENABLE; CSR_WRITE_4(sc, BGE_RDMA_MODE, val); DELAY(40); /* Turn on RX data completion state machine */ CSR_WRITE_4(sc, BGE_RDC_MODE, BGE_RDCMODE_ENABLE); /* Turn on RX BD initiator state machine */ CSR_WRITE_4(sc, BGE_RBDI_MODE, BGE_RBDIMODE_ENABLE); /* Turn on RX data and RX BD initiator state machine */ CSR_WRITE_4(sc, BGE_RDBDI_MODE, BGE_RDBDIMODE_ENABLE); /* Turn on Mbuf cluster free state machine */ if (!(BGE_IS_5705_PLUS(sc))) CSR_WRITE_4(sc, BGE_MBCF_MODE, BGE_MBCFMODE_ENABLE); /* Turn on send BD completion state machine */ CSR_WRITE_4(sc, BGE_SBDC_MODE, BGE_SBDCMODE_ENABLE); /* Turn on send data completion state machine */ val = BGE_SDCMODE_ENABLE; if (sc->bge_asicrev == BGE_ASICREV_BCM5761) val |= BGE_SDCMODE_CDELAY; CSR_WRITE_4(sc, BGE_SDC_MODE, val); /* Turn on send data initiator state machine */ if (sc->bge_flags & BGE_FLAG_TSO) CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE | 0x08); else CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE); /* Turn on send BD initiator state machine */ CSR_WRITE_4(sc, BGE_SBDI_MODE, BGE_SBDIMODE_ENABLE); /* Turn on send BD selector state machine */ CSR_WRITE_4(sc, BGE_SRS_MODE, BGE_SRSMODE_ENABLE); CSR_WRITE_4(sc, BGE_SDI_STATS_ENABLE_MASK, 0x007FFFFF); CSR_WRITE_4(sc, BGE_SDI_STATS_CTL, BGE_SDISTATSCTL_ENABLE | BGE_SDISTATSCTL_FASTER); /* ack/clear link change events */ CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED | BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE | BGE_MACSTAT_LINK_CHANGED); CSR_WRITE_4(sc, BGE_MI_STS, 0); /* Enable PHY auto polling (for MII/GMII only) */ if (sc->bge_flags & BGE_FLAG_TBI) { CSR_WRITE_4(sc, BGE_MI_STS, BGE_MISTS_LINK); } else { BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL | (10 << 16)); if (sc->bge_asicrev == BGE_ASICREV_BCM5700 && sc->bge_chipid != BGE_CHIPID_BCM5700_B2) CSR_WRITE_4(sc, BGE_MAC_EVT_ENB, BGE_EVTENB_MI_INTERRUPT); } /* * Clear any pending link state attention. * Otherwise some link state change events may be lost until attention * is cleared by bge_intr() -> bge_link_upd() sequence. * It's not necessary on newer BCM chips - perhaps enabling link * state change attentions implies clearing pending attention. */ CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED | BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE | BGE_MACSTAT_LINK_CHANGED); /* Enable link state change attentions. */ BGE_SETBIT(sc, BGE_MAC_EVT_ENB, BGE_EVTENB_LINK_CHANGED); return (0); } const struct bge_revision * bge_lookup_rev(uint32_t chipid) { const struct bge_revision *br; for (br = bge_revisions; br->br_name != NULL; br++) { if (br->br_chipid == chipid) return (br); } for (br = bge_majorrevs; br->br_name != NULL; br++) { if (br->br_chipid == BGE_ASICREV(chipid)) return (br); } return (NULL); } const struct bge_vendor * bge_lookup_vendor(uint16_t vid) { const struct bge_vendor *v; for (v = bge_vendors; v->v_name != NULL; v++) if (v->v_id == vid) return (v); panic("%s: unknown vendor %d", __func__, vid); return (NULL); } /* * Probe for a Broadcom chip. Check the PCI vendor and device IDs * against our list and return its name if we find a match. * * Note that since the Broadcom controller contains VPD support, we * try to get the device name string from the controller itself instead * of the compiled-in string. It guarantees we'll always announce the * right product name. We fall back to the compiled-in string when * VPD is unavailable or corrupt. */ static int bge_probe(device_t dev) { const struct bge_type *t = bge_devs; struct bge_softc *sc = device_get_softc(dev); uint16_t vid, did; sc->bge_dev = dev; vid = pci_get_vendor(dev); did = pci_get_device(dev); while(t->bge_vid != 0) { if ((vid == t->bge_vid) && (did == t->bge_did)) { char model[64], buf[96]; const struct bge_revision *br; const struct bge_vendor *v; uint32_t id; id = pci_read_config(dev, BGE_PCI_MISC_CTL, 4) >> BGE_PCIMISCCTL_ASICREV_SHIFT; if (BGE_ASICREV(id) == BGE_ASICREV_USE_PRODID_REG) id = pci_read_config(dev, BGE_PCI_PRODID_ASICREV, 4); br = bge_lookup_rev(id); v = bge_lookup_vendor(vid); { #if __FreeBSD_version > 700024 const char *pname; if (bge_has_eaddr(sc) && pci_get_vpd_ident(dev, &pname) == 0) snprintf(model, 64, "%s", pname); else #endif snprintf(model, 64, "%s %s", v->v_name, br != NULL ? br->br_name : "NetXtreme Ethernet Controller"); } snprintf(buf, 96, "%s, %sASIC rev. %#08x", model, br != NULL ? "" : "unknown ", id); device_set_desc_copy(dev, buf); return (0); } t++; } return (ENXIO); } static void bge_dma_free(struct bge_softc *sc) { int i; /* Destroy DMA maps for RX buffers. */ for (i = 0; i < BGE_STD_RX_RING_CNT; i++) { if (sc->bge_cdata.bge_rx_std_dmamap[i]) bus_dmamap_destroy(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_dmamap[i]); } if (sc->bge_cdata.bge_rx_std_sparemap) bus_dmamap_destroy(sc->bge_cdata.bge_rx_mtag, sc->bge_cdata.bge_rx_std_sparemap); /* Destroy DMA maps for jumbo RX buffers. */ for (i = 0; i < BGE_JUMBO_RX_RING_CNT; i++) { if (sc->bge_cdata.bge_rx_jumbo_dmamap[i]) bus_dmamap_destroy(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_dmamap[i]); } if (sc->bge_cdata.bge_rx_jumbo_sparemap) bus_dmamap_destroy(sc->bge_cdata.bge_mtag_jumbo, sc->bge_cdata.bge_rx_jumbo_sparemap); /* Destroy DMA maps for TX buffers. */ for (i = 0; i < BGE_TX_RING_CNT; i++) { if (sc->bge_cdata.bge_tx_dmamap[i]) bus_dmamap_destroy(sc->bge_cdata.bge_tx_mtag, sc->bge_cdata.bge_tx_dmamap[i]); } if (sc->bge_cdata.bge_rx_mtag) bus_dma_tag_destroy(sc->bge_cdata.bge_rx_mtag); if (sc->bge_cdata.bge_tx_mtag) bus_dma_tag_destroy(sc->bge_cdata.bge_tx_mtag); /* Destroy standard RX ring. */ if (sc->bge_cdata.bge_rx_std_ring_map) bus_dmamap_unload(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map); if (sc->bge_cdata.bge_rx_std_ring_map && sc->bge_ldata.bge_rx_std_ring) bus_dmamem_free(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_ldata.bge_rx_std_ring, sc->bge_cdata.bge_rx_std_ring_map); if (sc->bge_cdata.bge_rx_std_ring_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_rx_std_ring_tag); /* Destroy jumbo RX ring. */ if (sc->bge_cdata.bge_rx_jumbo_ring_map) bus_dmamap_unload(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_cdata.bge_rx_jumbo_ring_map); if (sc->bge_cdata.bge_rx_jumbo_ring_map && sc->bge_ldata.bge_rx_jumbo_ring) bus_dmamem_free(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_ldata.bge_rx_jumbo_ring, sc->bge_cdata.bge_rx_jumbo_ring_map); if (sc->bge_cdata.bge_rx_jumbo_ring_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_rx_jumbo_ring_tag); /* Destroy RX return ring. */ if (sc->bge_cdata.bge_rx_return_ring_map) bus_dmamap_unload(sc->bge_cdata.bge_rx_return_ring_tag, sc->bge_cdata.bge_rx_return_ring_map); if (sc->bge_cdata.bge_rx_return_ring_map && sc->bge_ldata.bge_rx_return_ring) bus_dmamem_free(sc->bge_cdata.bge_rx_return_ring_tag, sc->bge_ldata.bge_rx_return_ring, sc->bge_cdata.bge_rx_return_ring_map); if (sc->bge_cdata.bge_rx_return_ring_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_rx_return_ring_tag); /* Destroy TX ring. */ if (sc->bge_cdata.bge_tx_ring_map) bus_dmamap_unload(sc->bge_cdata.bge_tx_ring_tag, sc->bge_cdata.bge_tx_ring_map); if (sc->bge_cdata.bge_tx_ring_map && sc->bge_ldata.bge_tx_ring) bus_dmamem_free(sc->bge_cdata.bge_tx_ring_tag, sc->bge_ldata.bge_tx_ring, sc->bge_cdata.bge_tx_ring_map); if (sc->bge_cdata.bge_tx_ring_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_tx_ring_tag); /* Destroy status block. */ if (sc->bge_cdata.bge_status_map) bus_dmamap_unload(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map); if (sc->bge_cdata.bge_status_map && sc->bge_ldata.bge_status_block) bus_dmamem_free(sc->bge_cdata.bge_status_tag, sc->bge_ldata.bge_status_block, sc->bge_cdata.bge_status_map); if (sc->bge_cdata.bge_status_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_status_tag); /* Destroy statistics block. */ if (sc->bge_cdata.bge_stats_map) bus_dmamap_unload(sc->bge_cdata.bge_stats_tag, sc->bge_cdata.bge_stats_map); if (sc->bge_cdata.bge_stats_map && sc->bge_ldata.bge_stats) bus_dmamem_free(sc->bge_cdata.bge_stats_tag, sc->bge_ldata.bge_stats, sc->bge_cdata.bge_stats_map); if (sc->bge_cdata.bge_stats_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_stats_tag); /* Destroy the parent tag. */ if (sc->bge_cdata.bge_parent_tag) bus_dma_tag_destroy(sc->bge_cdata.bge_parent_tag); } static int bge_dma_alloc(device_t dev) { struct bge_dmamap_arg ctx; struct bge_softc *sc; bus_addr_t lowaddr; bus_size_t sbsz, txsegsz, txmaxsegsz; int i, error; sc = device_get_softc(dev); lowaddr = BUS_SPACE_MAXADDR; if ((sc->bge_flags & BGE_FLAG_40BIT_BUG) != 0) lowaddr = BGE_DMA_MAXADDR; if ((sc->bge_flags & BGE_FLAG_4G_BNDRY_BUG) != 0) lowaddr = BUS_SPACE_MAXADDR_32BIT; /* * Allocate the parent bus DMA tag appropriate for PCI. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->bge_dev), 1, 0, lowaddr, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->bge_cdata.bge_parent_tag); if (error != 0) { device_printf(sc->bge_dev, "could not allocate parent dma tag\n"); return (ENOMEM); } /* * Create tag for Tx mbufs. */ if (sc->bge_flags & BGE_FLAG_TSO) { txsegsz = BGE_TSOSEG_SZ; txmaxsegsz = 65535 + sizeof(struct ether_vlan_header); } else { txsegsz = MCLBYTES; txmaxsegsz = MCLBYTES * BGE_NSEG_NEW; } error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, txmaxsegsz, BGE_NSEG_NEW, txsegsz, 0, NULL, NULL, &sc->bge_cdata.bge_tx_mtag); if (error) { device_printf(sc->bge_dev, "could not allocate TX dma tag\n"); return (ENOMEM); } /* * Create tag for Rx mbufs. */ error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->bge_cdata.bge_rx_mtag); if (error) { device_printf(sc->bge_dev, "could not allocate RX dma tag\n"); return (ENOMEM); } /* Create DMA maps for RX buffers. */ error = bus_dmamap_create(sc->bge_cdata.bge_rx_mtag, 0, &sc->bge_cdata.bge_rx_std_sparemap); if (error) { device_printf(sc->bge_dev, "can't create spare DMA map for RX\n"); return (ENOMEM); } for (i = 0; i < BGE_STD_RX_RING_CNT; i++) { error = bus_dmamap_create(sc->bge_cdata.bge_rx_mtag, 0, &sc->bge_cdata.bge_rx_std_dmamap[i]); if (error) { device_printf(sc->bge_dev, "can't create DMA map for RX\n"); return (ENOMEM); } } /* Create DMA maps for TX buffers. */ for (i = 0; i < BGE_TX_RING_CNT; i++) { error = bus_dmamap_create(sc->bge_cdata.bge_tx_mtag, 0, &sc->bge_cdata.bge_tx_dmamap[i]); if (error) { device_printf(sc->bge_dev, "can't create DMA map for TX\n"); return (ENOMEM); } } /* Create tag for standard RX ring. */ error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BGE_STD_RX_RING_SZ, 1, BGE_STD_RX_RING_SZ, 0, NULL, NULL, &sc->bge_cdata.bge_rx_std_ring_tag); if (error) { device_printf(sc->bge_dev, "could not allocate dma tag\n"); return (ENOMEM); } /* Allocate DMA'able memory for standard RX ring. */ error = bus_dmamem_alloc(sc->bge_cdata.bge_rx_std_ring_tag, (void **)&sc->bge_ldata.bge_rx_std_ring, BUS_DMA_NOWAIT, &sc->bge_cdata.bge_rx_std_ring_map); if (error) return (ENOMEM); bzero((char *)sc->bge_ldata.bge_rx_std_ring, BGE_STD_RX_RING_SZ); /* Load the address of the standard RX ring. */ ctx.bge_maxsegs = 1; ctx.sc = sc; error = bus_dmamap_load(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map, sc->bge_ldata.bge_rx_std_ring, BGE_STD_RX_RING_SZ, bge_dma_map_addr, &ctx, BUS_DMA_NOWAIT); if (error) return (ENOMEM); sc->bge_ldata.bge_rx_std_ring_paddr = ctx.bge_busaddr; /* Create tags for jumbo mbufs. */ if (BGE_IS_JUMBO_CAPABLE(sc)) { error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MJUM9BYTES, BGE_NSEG_JUMBO, PAGE_SIZE, 0, NULL, NULL, &sc->bge_cdata.bge_mtag_jumbo); if (error) { device_printf(sc->bge_dev, "could not allocate jumbo dma tag\n"); return (ENOMEM); } /* Create tag for jumbo RX ring. */ error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BGE_JUMBO_RX_RING_SZ, 1, BGE_JUMBO_RX_RING_SZ, 0, NULL, NULL, &sc->bge_cdata.bge_rx_jumbo_ring_tag); if (error) { device_printf(sc->bge_dev, "could not allocate jumbo ring dma tag\n"); return (ENOMEM); } /* Allocate DMA'able memory for jumbo RX ring. */ error = bus_dmamem_alloc(sc->bge_cdata.bge_rx_jumbo_ring_tag, (void **)&sc->bge_ldata.bge_rx_jumbo_ring, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->bge_cdata.bge_rx_jumbo_ring_map); if (error) return (ENOMEM); /* Load the address of the jumbo RX ring. */ ctx.bge_maxsegs = 1; ctx.sc = sc; error = bus_dmamap_load(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_cdata.bge_rx_jumbo_ring_map, sc->bge_ldata.bge_rx_jumbo_ring, BGE_JUMBO_RX_RING_SZ, bge_dma_map_addr, &ctx, BUS_DMA_NOWAIT); if (error) return (ENOMEM); sc->bge_ldata.bge_rx_jumbo_ring_paddr = ctx.bge_busaddr; /* Create DMA maps for jumbo RX buffers. */ error = bus_dmamap_create(sc->bge_cdata.bge_mtag_jumbo, 0, &sc->bge_cdata.bge_rx_jumbo_sparemap); if (error) { device_printf(sc->bge_dev, "can't create spare DMA map for jumbo RX\n"); return (ENOMEM); } for (i = 0; i < BGE_JUMBO_RX_RING_CNT; i++) { error = bus_dmamap_create(sc->bge_cdata.bge_mtag_jumbo, 0, &sc->bge_cdata.bge_rx_jumbo_dmamap[i]); if (error) { device_printf(sc->bge_dev, "can't create DMA map for jumbo RX\n"); return (ENOMEM); } } } /* Create tag for RX return ring. */ error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BGE_RX_RTN_RING_SZ(sc), 1, BGE_RX_RTN_RING_SZ(sc), 0, NULL, NULL, &sc->bge_cdata.bge_rx_return_ring_tag); if (error) { device_printf(sc->bge_dev, "could not allocate dma tag\n"); return (ENOMEM); } /* Allocate DMA'able memory for RX return ring. */ error = bus_dmamem_alloc(sc->bge_cdata.bge_rx_return_ring_tag, (void **)&sc->bge_ldata.bge_rx_return_ring, BUS_DMA_NOWAIT, &sc->bge_cdata.bge_rx_return_ring_map); if (error) return (ENOMEM); bzero((char *)sc->bge_ldata.bge_rx_return_ring, BGE_RX_RTN_RING_SZ(sc)); /* Load the address of the RX return ring. */ ctx.bge_maxsegs = 1; ctx.sc = sc; error = bus_dmamap_load(sc->bge_cdata.bge_rx_return_ring_tag, sc->bge_cdata.bge_rx_return_ring_map, sc->bge_ldata.bge_rx_return_ring, BGE_RX_RTN_RING_SZ(sc), bge_dma_map_addr, &ctx, BUS_DMA_NOWAIT); if (error) return (ENOMEM); sc->bge_ldata.bge_rx_return_ring_paddr = ctx.bge_busaddr; /* Create tag for TX ring. */ error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BGE_TX_RING_SZ, 1, BGE_TX_RING_SZ, 0, NULL, NULL, &sc->bge_cdata.bge_tx_ring_tag); if (error) { device_printf(sc->bge_dev, "could not allocate dma tag\n"); return (ENOMEM); } /* Allocate DMA'able memory for TX ring. */ error = bus_dmamem_alloc(sc->bge_cdata.bge_tx_ring_tag, (void **)&sc->bge_ldata.bge_tx_ring, BUS_DMA_NOWAIT, &sc->bge_cdata.bge_tx_ring_map); if (error) return (ENOMEM); bzero((char *)sc->bge_ldata.bge_tx_ring, BGE_TX_RING_SZ); /* Load the address of the TX ring. */ ctx.bge_maxsegs = 1; ctx.sc = sc; error = bus_dmamap_load(sc->bge_cdata.bge_tx_ring_tag, sc->bge_cdata.bge_tx_ring_map, sc->bge_ldata.bge_tx_ring, BGE_TX_RING_SZ, bge_dma_map_addr, &ctx, BUS_DMA_NOWAIT); if (error) return (ENOMEM); sc->bge_ldata.bge_tx_ring_paddr = ctx.bge_busaddr; /* * Create tag for status block. * Because we only use single Tx/Rx/Rx return ring, use * minimum status block size except BCM5700 AX/BX which * seems to want to see full status block size regardless * of configured number of ring. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5700 && sc->bge_chipid != BGE_CHIPID_BCM5700_C0) sbsz = BGE_STATUS_BLK_SZ; else sbsz = 32; error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, sbsz, 1, sbsz, 0, NULL, NULL, &sc->bge_cdata.bge_status_tag); if (error) { device_printf(sc->bge_dev, "could not allocate status dma tag\n"); return (ENOMEM); } /* Allocate DMA'able memory for status block. */ error = bus_dmamem_alloc(sc->bge_cdata.bge_status_tag, (void **)&sc->bge_ldata.bge_status_block, BUS_DMA_NOWAIT, &sc->bge_cdata.bge_status_map); if (error) return (ENOMEM); bzero((char *)sc->bge_ldata.bge_status_block, sbsz); /* Load the address of the status block. */ ctx.sc = sc; ctx.bge_maxsegs = 1; error = bus_dmamap_load(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, sc->bge_ldata.bge_status_block, sbsz, bge_dma_map_addr, &ctx, BUS_DMA_NOWAIT); if (error) return (ENOMEM); sc->bge_ldata.bge_status_block_paddr = ctx.bge_busaddr; /* Create tag for statistics block. */ error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BGE_STATS_SZ, 1, BGE_STATS_SZ, 0, NULL, NULL, &sc->bge_cdata.bge_stats_tag); if (error) { device_printf(sc->bge_dev, "could not allocate dma tag\n"); return (ENOMEM); } /* Allocate DMA'able memory for statistics block. */ error = bus_dmamem_alloc(sc->bge_cdata.bge_stats_tag, (void **)&sc->bge_ldata.bge_stats, BUS_DMA_NOWAIT, &sc->bge_cdata.bge_stats_map); if (error) return (ENOMEM); bzero((char *)sc->bge_ldata.bge_stats, BGE_STATS_SZ); /* Load the address of the statstics block. */ ctx.sc = sc; ctx.bge_maxsegs = 1; error = bus_dmamap_load(sc->bge_cdata.bge_stats_tag, sc->bge_cdata.bge_stats_map, sc->bge_ldata.bge_stats, BGE_STATS_SZ, bge_dma_map_addr, &ctx, BUS_DMA_NOWAIT); if (error) return (ENOMEM); sc->bge_ldata.bge_stats_paddr = ctx.bge_busaddr; return (0); } /* * Return true if this device has more than one port. */ static int bge_has_multiple_ports(struct bge_softc *sc) { device_t dev = sc->bge_dev; u_int b, d, f, fscan, s; d = pci_get_domain(dev); b = pci_get_bus(dev); s = pci_get_slot(dev); f = pci_get_function(dev); for (fscan = 0; fscan <= PCI_FUNCMAX; fscan++) if (fscan != f && pci_find_dbsf(d, b, s, fscan) != NULL) return (1); return (0); } /* * Return true if MSI can be used with this device. */ static int bge_can_use_msi(struct bge_softc *sc) { int can_use_msi = 0; switch (sc->bge_asicrev) { case BGE_ASICREV_BCM5714_A0: case BGE_ASICREV_BCM5714: /* * Apparently, MSI doesn't work when these chips are * configured in single-port mode. */ if (bge_has_multiple_ports(sc)) can_use_msi = 1; break; case BGE_ASICREV_BCM5750: if (sc->bge_chiprev != BGE_CHIPREV_5750_AX && sc->bge_chiprev != BGE_CHIPREV_5750_BX) can_use_msi = 1; break; default: if (BGE_IS_575X_PLUS(sc)) can_use_msi = 1; } return (can_use_msi); } static int bge_attach(device_t dev) { struct ifnet *ifp; struct bge_softc *sc; uint32_t hwcfg = 0, misccfg; u_char eaddr[ETHER_ADDR_LEN]; int error, msicount, reg, rid, trys; sc = device_get_softc(dev); sc->bge_dev = dev; TASK_INIT(&sc->bge_intr_task, 0, bge_intr_task, sc); /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = BGE_PCI_BAR0; sc->bge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bge_res == NULL) { device_printf (sc->bge_dev, "couldn't map memory\n"); error = ENXIO; goto fail; } /* Save various chip information. */ sc->bge_chipid = pci_read_config(dev, BGE_PCI_MISC_CTL, 4) >> BGE_PCIMISCCTL_ASICREV_SHIFT; if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_USE_PRODID_REG) sc->bge_chipid = pci_read_config(dev, BGE_PCI_PRODID_ASICREV, 4); sc->bge_asicrev = BGE_ASICREV(sc->bge_chipid); sc->bge_chiprev = BGE_CHIPREV(sc->bge_chipid); /* * Don't enable Ethernet@WireSpeed for the 5700, 5906, or the * 5705 A0 and A1 chips. */ if (sc->bge_asicrev != BGE_ASICREV_BCM5700 && sc->bge_asicrev != BGE_ASICREV_BCM5906 && sc->bge_chipid != BGE_CHIPID_BCM5705_A0 && sc->bge_chipid != BGE_CHIPID_BCM5705_A1) sc->bge_flags |= BGE_FLAG_WIRESPEED; if (bge_has_eaddr(sc)) sc->bge_flags |= BGE_FLAG_EADDR; /* Save chipset family. */ switch (sc->bge_asicrev) { case BGE_ASICREV_BCM5755: case BGE_ASICREV_BCM5761: case BGE_ASICREV_BCM5784: case BGE_ASICREV_BCM5785: case BGE_ASICREV_BCM5787: case BGE_ASICREV_BCM57780: sc->bge_flags |= BGE_FLAG_5755_PLUS | BGE_FLAG_575X_PLUS | BGE_FLAG_5705_PLUS; break; case BGE_ASICREV_BCM5700: case BGE_ASICREV_BCM5701: case BGE_ASICREV_BCM5703: case BGE_ASICREV_BCM5704: sc->bge_flags |= BGE_FLAG_5700_FAMILY | BGE_FLAG_JUMBO; break; case BGE_ASICREV_BCM5714_A0: case BGE_ASICREV_BCM5780: case BGE_ASICREV_BCM5714: sc->bge_flags |= BGE_FLAG_5714_FAMILY /* | BGE_FLAG_JUMBO */; /* FALLTHROUGH */ case BGE_ASICREV_BCM5750: case BGE_ASICREV_BCM5752: case BGE_ASICREV_BCM5906: sc->bge_flags |= BGE_FLAG_575X_PLUS; /* FALLTHROUGH */ case BGE_ASICREV_BCM5705: sc->bge_flags |= BGE_FLAG_5705_PLUS; break; } /* Set various bug flags. */ if (sc->bge_chipid == BGE_CHIPID_BCM5701_A0 || sc->bge_chipid == BGE_CHIPID_BCM5701_B0) sc->bge_flags |= BGE_FLAG_CRC_BUG; if (sc->bge_chiprev == BGE_CHIPREV_5703_AX || sc->bge_chiprev == BGE_CHIPREV_5704_AX) sc->bge_flags |= BGE_FLAG_ADC_BUG; if (sc->bge_chipid == BGE_CHIPID_BCM5704_A0) sc->bge_flags |= BGE_FLAG_5704_A0_BUG; if (pci_get_subvendor(dev) == DELL_VENDORID) sc->bge_flags |= BGE_FLAG_NO_3LED; if (pci_get_device(dev) == BCOM_DEVICEID_BCM5755M) sc->bge_flags |= BGE_FLAG_ADJUST_TRIM; if (BGE_IS_5705_PLUS(sc) && !(sc->bge_flags & BGE_FLAG_ADJUST_TRIM)) { if (sc->bge_asicrev == BGE_ASICREV_BCM5755 || sc->bge_asicrev == BGE_ASICREV_BCM5761 || sc->bge_asicrev == BGE_ASICREV_BCM5784 || sc->bge_asicrev == BGE_ASICREV_BCM5787) { if (pci_get_device(dev) != BCOM_DEVICEID_BCM5722 && pci_get_device(dev) != BCOM_DEVICEID_BCM5756) sc->bge_flags |= BGE_FLAG_JITTER_BUG; } else if (sc->bge_asicrev != BGE_ASICREV_BCM5906) sc->bge_flags |= BGE_FLAG_BER_BUG; } /* * All controllers that are not 5755 or higher have 4GB * boundary DMA bug. * Whenever an address crosses a multiple of the 4GB boundary * (including 4GB, 8Gb, 12Gb, etc.) and makes the transition * from 0xX_FFFF_FFFF to 0x(X+1)_0000_0000 an internal DMA * state machine will lockup and cause the device to hang. */ if (BGE_IS_5755_PLUS(sc) == 0) sc->bge_flags |= BGE_FLAG_4G_BNDRY_BUG; /* * We could possibly check for BCOM_DEVICEID_BCM5788 in bge_probe() * but I do not know the DEVICEID for the 5788M. */ misccfg = CSR_READ_4(sc, BGE_MISC_CFG) & BGE_MISCCFG_BOARD_ID; if (misccfg == BGE_MISCCFG_BOARD_ID_5788 || misccfg == BGE_MISCCFG_BOARD_ID_5788M) sc->bge_flags |= BGE_FLAG_5788; /* * Some controllers seem to require a special firmware to use * TSO. But the firmware is not available to FreeBSD and Linux * claims that the TSO performed by the firmware is slower than * hardware based TSO. Moreover the firmware based TSO has one * known bug which can't handle TSO if ethernet header + IP/TCP * header is greater than 80 bytes. The workaround for the TSO * bug exist but it seems it's too expensive than not using * TSO at all. Some hardwares also have the TSO bug so limit * the TSO to the controllers that are not affected TSO issues * (e.g. 5755 or higher). */ if (BGE_IS_5755_PLUS(sc)) { /* * BCM5754 and BCM5787 shares the same ASIC id so * explicit device id check is required. */ if (pci_get_device(dev) != BCOM_DEVICEID_BCM5754 && pci_get_device(dev) != BCOM_DEVICEID_BCM5754M) sc->bge_flags |= BGE_FLAG_TSO; } /* * Check if this is a PCI-X or PCI Express device. */ if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { /* * Found a PCI Express capabilities register, this * must be a PCI Express device. */ sc->bge_flags |= BGE_FLAG_PCIE; sc->bge_expcap = reg; bge_set_max_readrq(sc); } else { /* * Check if the device is in PCI-X Mode. * (This bit is not valid on PCI Express controllers.) */ if (pci_find_extcap(dev, PCIY_PCIX, ®) == 0) sc->bge_pcixcap = reg; if ((pci_read_config(dev, BGE_PCI_PCISTATE, 4) & BGE_PCISTATE_PCI_BUSMODE) == 0) sc->bge_flags |= BGE_FLAG_PCIX; } /* * The 40bit DMA bug applies to the 5714/5715 controllers and is * not actually a MAC controller bug but an issue with the embedded * PCIe to PCI-X bridge in the device. Use 40bit DMA workaround. */ if (BGE_IS_5714_FAMILY(sc) && (sc->bge_flags & BGE_FLAG_PCIX)) sc->bge_flags |= BGE_FLAG_40BIT_BUG; /* * Allocate the interrupt, using MSI if possible. These devices * support 8 MSI messages, but only the first one is used in * normal operation. */ rid = 0; if (pci_find_extcap(sc->bge_dev, PCIY_MSI, ®) == 0) { sc->bge_msicap = reg; if (bge_can_use_msi(sc)) { msicount = pci_msi_count(dev); if (msicount > 1) msicount = 1; } else msicount = 0; if (msicount == 1 && pci_alloc_msi(dev, &msicount) == 0) { rid = 1; sc->bge_flags |= BGE_FLAG_MSI; } } sc->bge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->bge_irq == NULL) { device_printf(sc->bge_dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } if (bootverbose) device_printf(dev, "CHIP ID 0x%08x; ASIC REV 0x%02x; CHIP REV 0x%02x; %s\n", sc->bge_chipid, sc->bge_asicrev, sc->bge_chiprev, (sc->bge_flags & BGE_FLAG_PCIX) ? "PCI-X" : ((sc->bge_flags & BGE_FLAG_PCIE) ? "PCI-E" : "PCI")); BGE_LOCK_INIT(sc, device_get_nameunit(dev)); /* Try to reset the chip. */ if (bge_reset(sc)) { device_printf(sc->bge_dev, "chip reset failed\n"); error = ENXIO; goto fail; } sc->bge_asf_mode = 0; if (bge_allow_asf && (bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM_SIG) == BGE_MAGIC_NUMBER)) { if (bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM_NICCFG) & BGE_HWCFG_ASF) { sc->bge_asf_mode |= ASF_ENABLE; sc->bge_asf_mode |= ASF_STACKUP; if (BGE_IS_575X_PLUS(sc)) sc->bge_asf_mode |= ASF_NEW_HANDSHAKE; } } /* Try to reset the chip again the nice way. */ bge_stop_fw(sc); bge_sig_pre_reset(sc, BGE_RESET_STOP); if (bge_reset(sc)) { device_printf(sc->bge_dev, "chip reset failed\n"); error = ENXIO; goto fail; } bge_sig_legacy(sc, BGE_RESET_STOP); bge_sig_post_reset(sc, BGE_RESET_STOP); if (bge_chipinit(sc)) { device_printf(sc->bge_dev, "chip initialization failed\n"); error = ENXIO; goto fail; } error = bge_get_eaddr(sc, eaddr); if (error) { device_printf(sc->bge_dev, "failed to read station address\n"); error = ENXIO; goto fail; } /* 5705 limits RX return ring to 512 entries. */ if (BGE_IS_5705_PLUS(sc)) sc->bge_return_ring_cnt = BGE_RETURN_RING_CNT_5705; else sc->bge_return_ring_cnt = BGE_RETURN_RING_CNT; if (bge_dma_alloc(dev)) { device_printf(sc->bge_dev, "failed to allocate DMA resources\n"); error = ENXIO; goto fail; } /* Set default tuneable values. */ sc->bge_stat_ticks = BGE_TICKS_PER_SEC; sc->bge_rx_coal_ticks = 150; sc->bge_tx_coal_ticks = 150; sc->bge_rx_max_coal_bds = 10; sc->bge_tx_max_coal_bds = 10; /* Set up ifnet structure */ ifp = sc->bge_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(sc->bge_dev, "failed to if_alloc()\n"); error = ENXIO; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = bge_ioctl; ifp->if_start = bge_start; ifp->if_init = bge_init; ifp->if_snd.ifq_drv_maxlen = BGE_TX_RING_CNT - 1; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); ifp->if_hwassist = BGE_CSUM_FEATURES; ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; if ((sc->bge_flags & BGE_FLAG_TSO) != 0) { ifp->if_hwassist |= CSUM_TSO; ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_VLAN_HWTSO; } #ifdef IFCAP_VLAN_HWCSUM ifp->if_capabilities |= IFCAP_VLAN_HWCSUM; #endif ifp->if_capenable = ifp->if_capabilities; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif /* * 5700 B0 chips do not support checksumming correctly due * to hardware bugs. */ if (sc->bge_chipid == BGE_CHIPID_BCM5700_B0) { ifp->if_capabilities &= ~IFCAP_HWCSUM; ifp->if_capenable &= ~IFCAP_HWCSUM; ifp->if_hwassist = 0; } /* * Figure out what sort of media we have by checking the * hardware config word in the first 32k of NIC internal memory, * or fall back to examining the EEPROM if necessary. * Note: on some BCM5700 cards, this value appears to be unset. * If that's the case, we have to rely on identifying the NIC * by its PCI subsystem ID, as we do below for the SysKonnect * SK-9D41. */ if (bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM_SIG) == BGE_MAGIC_NUMBER) hwcfg = bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM_NICCFG); else if ((sc->bge_flags & BGE_FLAG_EADDR) && (sc->bge_asicrev != BGE_ASICREV_BCM5906)) { if (bge_read_eeprom(sc, (caddr_t)&hwcfg, BGE_EE_HWCFG_OFFSET, sizeof(hwcfg))) { device_printf(sc->bge_dev, "failed to read EEPROM\n"); error = ENXIO; goto fail; } hwcfg = ntohl(hwcfg); } /* The SysKonnect SK-9D41 is a 1000baseSX card. */ if ((pci_read_config(dev, BGE_PCI_SUBSYS, 4) >> 16) == SK_SUBSYSID_9D41 || (hwcfg & BGE_HWCFG_MEDIA) == BGE_MEDIA_FIBER) { if (BGE_IS_5714_FAMILY(sc)) sc->bge_flags |= BGE_FLAG_MII_SERDES; else sc->bge_flags |= BGE_FLAG_TBI; } if (sc->bge_flags & BGE_FLAG_TBI) { ifmedia_init(&sc->bge_ifmedia, IFM_IMASK, bge_ifmedia_upd, bge_ifmedia_sts); ifmedia_add(&sc->bge_ifmedia, IFM_ETHER | IFM_1000_SX, 0, NULL); ifmedia_add(&sc->bge_ifmedia, IFM_ETHER | IFM_1000_SX | IFM_FDX, 0, NULL); ifmedia_add(&sc->bge_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->bge_ifmedia, IFM_ETHER | IFM_AUTO); sc->bge_ifmedia.ifm_media = sc->bge_ifmedia.ifm_cur->ifm_media; } else { /* * Do transceiver setup and tell the firmware the * driver is down so we can try to get access the * probe if ASF is running. Retry a couple of times * if we get a conflict with the ASF firmware accessing * the PHY. */ trys = 0; BGE_CLRBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); again: bge_asf_driver_up(sc); if (mii_phy_probe(dev, &sc->bge_miibus, bge_ifmedia_upd, bge_ifmedia_sts)) { if (trys++ < 4) { device_printf(sc->bge_dev, "Try again\n"); bge_miibus_writereg(sc->bge_dev, 1, MII_BMCR, BMCR_RESET); goto again; } device_printf(sc->bge_dev, "MII without any PHY!\n"); error = ENXIO; goto fail; } /* * Now tell the firmware we are going up after probing the PHY */ if (sc->bge_asf_mode & ASF_STACKUP) BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); } /* * When using the BCM5701 in PCI-X mode, data corruption has * been observed in the first few bytes of some received packets. * Aligning the packet buffer in memory eliminates the corruption. * Unfortunately, this misaligns the packet payloads. On platforms * which do not support unaligned accesses, we will realign the * payloads by copying the received packets. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5701 && sc->bge_flags & BGE_FLAG_PCIX) sc->bge_flags |= BGE_FLAG_RX_ALIGNBUG; /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); callout_init_mtx(&sc->bge_stat_ch, &sc->bge_mtx, 0); /* Tell upper layer we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); /* * Hookup IRQ last. */ #if __FreeBSD_version > 700030 if (BGE_IS_5755_PLUS(sc) && sc->bge_flags & BGE_FLAG_MSI) { /* Take advantage of single-shot MSI. */ CSR_WRITE_4(sc, BGE_MSI_MODE, CSR_READ_4(sc, BGE_MSI_MODE) & ~BGE_MSIMODE_ONE_SHOT_DISABLE); sc->bge_tq = taskqueue_create_fast("bge_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->bge_tq); if (sc->bge_tq == NULL) { device_printf(dev, "could not create taskqueue.\n"); ether_ifdetach(ifp); error = ENXIO; goto fail; } taskqueue_start_threads(&sc->bge_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->bge_dev)); error = bus_setup_intr(dev, sc->bge_irq, INTR_TYPE_NET | INTR_MPSAFE, bge_msi_intr, NULL, sc, &sc->bge_intrhand); if (error) ether_ifdetach(ifp); } else error = bus_setup_intr(dev, sc->bge_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, bge_intr, sc, &sc->bge_intrhand); #else error = bus_setup_intr(dev, sc->bge_irq, INTR_TYPE_NET | INTR_MPSAFE, bge_intr, sc, &sc->bge_intrhand); #endif if (error) { bge_detach(dev); device_printf(sc->bge_dev, "couldn't set up irq\n"); } bge_add_sysctls(sc); return (0); fail: bge_release_resources(sc); return (error); } static int bge_detach(device_t dev) { struct bge_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->bge_ifp; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif BGE_LOCK(sc); bge_stop(sc); bge_reset(sc); BGE_UNLOCK(sc); callout_drain(&sc->bge_stat_ch); if (sc->bge_tq) taskqueue_drain(sc->bge_tq, &sc->bge_intr_task); ether_ifdetach(ifp); if (sc->bge_flags & BGE_FLAG_TBI) { ifmedia_removeall(&sc->bge_ifmedia); } else { bus_generic_detach(dev); device_delete_child(dev, sc->bge_miibus); } bge_release_resources(sc); return (0); } static void bge_release_resources(struct bge_softc *sc) { device_t dev; dev = sc->bge_dev; if (sc->bge_tq != NULL) taskqueue_free(sc->bge_tq); if (sc->bge_intrhand != NULL) bus_teardown_intr(dev, sc->bge_irq, sc->bge_intrhand); if (sc->bge_irq != NULL) bus_release_resource(dev, SYS_RES_IRQ, sc->bge_flags & BGE_FLAG_MSI ? 1 : 0, sc->bge_irq); if (sc->bge_flags & BGE_FLAG_MSI) pci_release_msi(dev); if (sc->bge_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, BGE_PCI_BAR0, sc->bge_res); if (sc->bge_ifp != NULL) if_free(sc->bge_ifp); bge_dma_free(sc); if (mtx_initialized(&sc->bge_mtx)) /* XXX */ BGE_LOCK_DESTROY(sc); } static int bge_reset(struct bge_softc *sc) { device_t dev; uint32_t cachesize, command, pcistate, reset, val; void (*write_op)(struct bge_softc *, int, int); uint16_t devctl; int i; dev = sc->bge_dev; if (BGE_IS_575X_PLUS(sc) && !BGE_IS_5714_FAMILY(sc) && (sc->bge_asicrev != BGE_ASICREV_BCM5906)) { if (sc->bge_flags & BGE_FLAG_PCIE) write_op = bge_writemem_direct; else write_op = bge_writemem_ind; } else write_op = bge_writereg_ind; /* Save some important PCI state. */ cachesize = pci_read_config(dev, BGE_PCI_CACHESZ, 4); command = pci_read_config(dev, BGE_PCI_CMD, 4); pcistate = pci_read_config(dev, BGE_PCI_PCISTATE, 4); pci_write_config(dev, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_INDIRECT_ACCESS | BGE_PCIMISCCTL_MASK_PCI_INTR | BGE_HIF_SWAP_OPTIONS | BGE_PCIMISCCTL_PCISTATE_RW, 4); /* Disable fastboot on controllers that support it. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5752 || BGE_IS_5755_PLUS(sc)) { if (bootverbose) device_printf(sc->bge_dev, "Disabling fastboot\n"); CSR_WRITE_4(sc, BGE_FASTBOOT_PC, 0x0); } /* * Write the magic number to SRAM at offset 0xB50. * When firmware finishes its initialization it will * write ~BGE_MAGIC_NUMBER to the same location. */ bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM, BGE_MAGIC_NUMBER); reset = BGE_MISCCFG_RESET_CORE_CLOCKS | BGE_32BITTIME_66MHZ; /* XXX: Broadcom Linux driver. */ if (sc->bge_flags & BGE_FLAG_PCIE) { if (CSR_READ_4(sc, 0x7E2C) == 0x60) /* PCIE 1.0 */ CSR_WRITE_4(sc, 0x7E2C, 0x20); if (sc->bge_chipid != BGE_CHIPID_BCM5750_A0) { /* Prevent PCIE link training during global reset */ CSR_WRITE_4(sc, BGE_MISC_CFG, 1 << 29); reset |= 1 << 29; } } /* * Set GPHY Power Down Override to leave GPHY * powered up in D0 uninitialized. */ if (BGE_IS_5705_PLUS(sc)) reset |= 0x04000000; /* Issue global reset */ write_op(sc, BGE_MISC_CFG, reset); if (sc->bge_asicrev == BGE_ASICREV_BCM5906) { val = CSR_READ_4(sc, BGE_VCPU_STATUS); CSR_WRITE_4(sc, BGE_VCPU_STATUS, val | BGE_VCPU_STATUS_DRV_RESET); val = CSR_READ_4(sc, BGE_VCPU_EXT_CTRL); CSR_WRITE_4(sc, BGE_VCPU_EXT_CTRL, val & ~BGE_VCPU_EXT_CTRL_HALT_CPU); } DELAY(1000); /* XXX: Broadcom Linux driver. */ if (sc->bge_flags & BGE_FLAG_PCIE) { if (sc->bge_chipid == BGE_CHIPID_BCM5750_A0) { DELAY(500000); /* wait for link training to complete */ val = pci_read_config(dev, 0xC4, 4); pci_write_config(dev, 0xC4, val | (1 << 15), 4); } devctl = pci_read_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_CTL, 2); /* Clear enable no snoop and disable relaxed ordering. */ devctl &= ~(PCIM_EXP_CTL_RELAXED_ORD_ENABLE | PCIM_EXP_CTL_NOSNOOP_ENABLE); /* Set PCIE max payload size to 128. */ devctl &= ~PCIM_EXP_CTL_MAX_PAYLOAD; pci_write_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_CTL, devctl, 2); /* Clear error status. */ pci_write_config(dev, sc->bge_expcap + PCIR_EXPRESS_DEVICE_STA, PCIM_EXP_STA_CORRECTABLE_ERROR | PCIM_EXP_STA_NON_FATAL_ERROR | PCIM_EXP_STA_FATAL_ERROR | PCIM_EXP_STA_UNSUPPORTED_REQ, 2); } /* Reset some of the PCI state that got zapped by reset. */ pci_write_config(dev, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_INDIRECT_ACCESS | BGE_PCIMISCCTL_MASK_PCI_INTR | BGE_HIF_SWAP_OPTIONS | BGE_PCIMISCCTL_PCISTATE_RW, 4); pci_write_config(dev, BGE_PCI_CACHESZ, cachesize, 4); pci_write_config(dev, BGE_PCI_CMD, command, 4); write_op(sc, BGE_MISC_CFG, BGE_32BITTIME_66MHZ); /* Re-enable MSI, if neccesary, and enable the memory arbiter. */ if (BGE_IS_5714_FAMILY(sc)) { /* This chip disables MSI on reset. */ if (sc->bge_flags & BGE_FLAG_MSI) { val = pci_read_config(dev, sc->bge_msicap + PCIR_MSI_CTRL, 2); pci_write_config(dev, sc->bge_msicap + PCIR_MSI_CTRL, val | PCIM_MSICTRL_MSI_ENABLE, 2); val = CSR_READ_4(sc, BGE_MSI_MODE); CSR_WRITE_4(sc, BGE_MSI_MODE, val | BGE_MSIMODE_ENABLE); } val = CSR_READ_4(sc, BGE_MARB_MODE); CSR_WRITE_4(sc, BGE_MARB_MODE, BGE_MARBMODE_ENABLE | val); } else CSR_WRITE_4(sc, BGE_MARB_MODE, BGE_MARBMODE_ENABLE); if (sc->bge_asicrev == BGE_ASICREV_BCM5906) { for (i = 0; i < BGE_TIMEOUT; i++) { val = CSR_READ_4(sc, BGE_VCPU_STATUS); if (val & BGE_VCPU_STATUS_INIT_DONE) break; DELAY(100); } if (i == BGE_TIMEOUT) { device_printf(sc->bge_dev, "reset timed out\n"); return (1); } } else { /* * Poll until we see the 1's complement of the magic number. * This indicates that the firmware initialization is complete. * We expect this to fail if no chip containing the Ethernet * address is fitted though. */ for (i = 0; i < BGE_TIMEOUT; i++) { DELAY(10); val = bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM); if (val == ~BGE_MAGIC_NUMBER) break; } if ((sc->bge_flags & BGE_FLAG_EADDR) && i == BGE_TIMEOUT) device_printf(sc->bge_dev, "firmware handshake timed out, " "found 0x%08x\n", val); } /* * XXX Wait for the value of the PCISTATE register to * return to its original pre-reset state. This is a * fairly good indicator of reset completion. If we don't * wait for the reset to fully complete, trying to read * from the device's non-PCI registers may yield garbage * results. */ for (i = 0; i < BGE_TIMEOUT; i++) { if (pci_read_config(dev, BGE_PCI_PCISTATE, 4) == pcistate) break; DELAY(10); } if (sc->bge_flags & BGE_FLAG_PCIE) { reset = bge_readmem_ind(sc, 0x7C00); bge_writemem_ind(sc, 0x7C00, reset | (1 << 25)); } /* Fix up byte swapping. */ CSR_WRITE_4(sc, BGE_MODE_CTL, BGE_DMA_SWAP_OPTIONS | BGE_MODECTL_BYTESWAP_DATA); /* Tell the ASF firmware we are up */ if (sc->bge_asf_mode & ASF_STACKUP) BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); CSR_WRITE_4(sc, BGE_MAC_MODE, 0); /* * The 5704 in TBI mode apparently needs some special * adjustment to insure the SERDES drive level is set * to 1.2V. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5704 && sc->bge_flags & BGE_FLAG_TBI) { val = CSR_READ_4(sc, BGE_SERDES_CFG); val = (val & ~0xFFF) | 0x880; CSR_WRITE_4(sc, BGE_SERDES_CFG, val); } /* XXX: Broadcom Linux driver. */ if (sc->bge_flags & BGE_FLAG_PCIE && sc->bge_chipid != BGE_CHIPID_BCM5750_A0) { val = CSR_READ_4(sc, 0x7C00); CSR_WRITE_4(sc, 0x7C00, val | (1 << 25)); } DELAY(10000); return(0); } /* * Frame reception handling. This is called if there's a frame * on the receive return list. * * Note: we have to be able to handle two possibilities here: * 1) the frame is from the jumbo receive ring * 2) the frame is from the standard receive ring */ static int bge_rxeof(struct bge_softc *sc, uint16_t rx_prod, int holdlck) { struct ifnet *ifp; int rx_npkts = 0, stdcnt = 0, jumbocnt = 0; uint16_t rx_cons; rx_cons = sc->bge_rx_saved_considx; /* Nothing to do. */ if (rx_cons == rx_prod) return (rx_npkts); ifp = sc->bge_ifp; bus_dmamap_sync(sc->bge_cdata.bge_rx_return_ring_tag, sc->bge_cdata.bge_rx_return_ring_map, BUS_DMASYNC_POSTREAD); bus_dmamap_sync(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map, BUS_DMASYNC_POSTWRITE); if (ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN > (MCLBYTES - ETHER_ALIGN)) bus_dmamap_sync(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_cdata.bge_rx_jumbo_ring_map, BUS_DMASYNC_POSTWRITE); while (rx_cons != rx_prod) { struct bge_rx_bd *cur_rx; uint32_t rxidx; struct mbuf *m = NULL; uint16_t vlan_tag = 0; int have_tag = 0; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif cur_rx = &sc->bge_ldata.bge_rx_return_ring[rx_cons]; rxidx = cur_rx->bge_idx; BGE_INC(rx_cons, sc->bge_return_ring_cnt); if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING && cur_rx->bge_flags & BGE_RXBDFLAG_VLAN_TAG) { have_tag = 1; vlan_tag = cur_rx->bge_vlan_tag; } if (cur_rx->bge_flags & BGE_RXBDFLAG_JUMBO_RING) { jumbocnt++; m = sc->bge_cdata.bge_rx_jumbo_chain[rxidx]; if (cur_rx->bge_flags & BGE_RXBDFLAG_ERROR) { BGE_INC(sc->bge_jumbo, BGE_JUMBO_RX_RING_CNT); continue; } if (bge_newbuf_jumbo(sc, rxidx) != 0) { BGE_INC(sc->bge_jumbo, BGE_JUMBO_RX_RING_CNT); ifp->if_iqdrops++; continue; } BGE_INC(sc->bge_jumbo, BGE_JUMBO_RX_RING_CNT); } else { stdcnt++; if (cur_rx->bge_flags & BGE_RXBDFLAG_ERROR) { BGE_INC(sc->bge_std, BGE_STD_RX_RING_CNT); continue; } m = sc->bge_cdata.bge_rx_std_chain[rxidx]; if (bge_newbuf_std(sc, rxidx) != 0) { BGE_INC(sc->bge_std, BGE_STD_RX_RING_CNT); ifp->if_iqdrops++; continue; } BGE_INC(sc->bge_std, BGE_STD_RX_RING_CNT); } ifp->if_ipackets++; #ifndef __NO_STRICT_ALIGNMENT /* * For architectures with strict alignment we must make sure * the payload is aligned. */ if (sc->bge_flags & BGE_FLAG_RX_ALIGNBUG) { bcopy(m->m_data, m->m_data + ETHER_ALIGN, cur_rx->bge_len); m->m_data += ETHER_ALIGN; } #endif m->m_pkthdr.len = m->m_len = cur_rx->bge_len - ETHER_CRC_LEN; m->m_pkthdr.rcvif = ifp; if (ifp->if_capenable & IFCAP_RXCSUM) { if (cur_rx->bge_flags & BGE_RXBDFLAG_IP_CSUM) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if ((cur_rx->bge_ip_csum ^ 0xFFFF) == 0) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; } if (cur_rx->bge_flags & BGE_RXBDFLAG_TCP_UDP_CSUM && m->m_pkthdr.len >= ETHER_MIN_NOPAD) { m->m_pkthdr.csum_data = cur_rx->bge_tcp_udp_csum; m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; } } /* * If we received a packet with a vlan tag, * attach that information to the packet. */ if (have_tag) { #if __FreeBSD_version > 700022 m->m_pkthdr.ether_vtag = vlan_tag; m->m_flags |= M_VLANTAG; #else VLAN_INPUT_TAG_NEW(ifp, m, vlan_tag); if (m == NULL) continue; #endif } if (holdlck != 0) { BGE_UNLOCK(sc); (*ifp->if_input)(ifp, m); BGE_LOCK(sc); } else (*ifp->if_input)(ifp, m); rx_npkts++; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) return (rx_npkts); } bus_dmamap_sync(sc->bge_cdata.bge_rx_return_ring_tag, sc->bge_cdata.bge_rx_return_ring_map, BUS_DMASYNC_PREREAD); if (stdcnt > 0) bus_dmamap_sync(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map, BUS_DMASYNC_PREWRITE); if (jumbocnt > 0) bus_dmamap_sync(sc->bge_cdata.bge_rx_jumbo_ring_tag, sc->bge_cdata.bge_rx_jumbo_ring_map, BUS_DMASYNC_PREWRITE); sc->bge_rx_saved_considx = rx_cons; bge_writembx(sc, BGE_MBX_RX_CONS0_LO, sc->bge_rx_saved_considx); if (stdcnt) bge_writembx(sc, BGE_MBX_RX_STD_PROD_LO, sc->bge_std); if (jumbocnt) bge_writembx(sc, BGE_MBX_RX_JUMBO_PROD_LO, sc->bge_jumbo); #ifdef notyet /* * This register wraps very quickly under heavy packet drops. * If you need correct statistics, you can enable this check. */ if (BGE_IS_5705_PLUS(sc)) ifp->if_ierrors += CSR_READ_4(sc, BGE_RXLP_LOCSTAT_IFIN_DROPS); #endif return (rx_npkts); } static void bge_txeof(struct bge_softc *sc, uint16_t tx_cons) { struct bge_tx_bd *cur_tx = NULL; struct ifnet *ifp; BGE_LOCK_ASSERT(sc); /* Nothing to do. */ if (sc->bge_tx_saved_considx == tx_cons) return; ifp = sc->bge_ifp; bus_dmamap_sync(sc->bge_cdata.bge_tx_ring_tag, sc->bge_cdata.bge_tx_ring_map, BUS_DMASYNC_POSTWRITE); /* * Go through our tx ring and free mbufs for those * frames that have been sent. */ while (sc->bge_tx_saved_considx != tx_cons) { uint32_t idx = 0; idx = sc->bge_tx_saved_considx; cur_tx = &sc->bge_ldata.bge_tx_ring[idx]; if (cur_tx->bge_flags & BGE_TXBDFLAG_END) ifp->if_opackets++; if (sc->bge_cdata.bge_tx_chain[idx] != NULL) { bus_dmamap_sync(sc->bge_cdata.bge_tx_mtag, sc->bge_cdata.bge_tx_dmamap[idx], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->bge_cdata.bge_tx_mtag, sc->bge_cdata.bge_tx_dmamap[idx]); m_freem(sc->bge_cdata.bge_tx_chain[idx]); sc->bge_cdata.bge_tx_chain[idx] = NULL; } sc->bge_txcnt--; BGE_INC(sc->bge_tx_saved_considx, BGE_TX_RING_CNT); } if (cur_tx != NULL) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (sc->bge_txcnt == 0) sc->bge_timer = 0; } #ifdef DEVICE_POLLING static int bge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct bge_softc *sc = ifp->if_softc; uint16_t rx_prod, tx_cons; uint32_t statusword; int rx_npkts = 0; BGE_LOCK(sc); if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { BGE_UNLOCK(sc); return (rx_npkts); } bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); rx_prod = sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx; tx_cons = sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx; statusword = atomic_readandclear_32( &sc->bge_ldata.bge_status_block->bge_status); bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Note link event. It will be processed by POLL_AND_CHECK_STATUS. */ if (statusword & BGE_STATFLAG_LINKSTATE_CHANGED) sc->bge_link_evt++; if (cmd == POLL_AND_CHECK_STATUS) if ((sc->bge_asicrev == BGE_ASICREV_BCM5700 && sc->bge_chipid != BGE_CHIPID_BCM5700_B2) || sc->bge_link_evt || (sc->bge_flags & BGE_FLAG_TBI)) bge_link_upd(sc); sc->rxcycles = count; rx_npkts = bge_rxeof(sc, rx_prod, 1); if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { BGE_UNLOCK(sc); return (rx_npkts); } bge_txeof(sc, tx_cons); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) bge_start_locked(ifp); BGE_UNLOCK(sc); return (rx_npkts); } #endif /* DEVICE_POLLING */ static int bge_msi_intr(void *arg) { struct bge_softc *sc; sc = (struct bge_softc *)arg; /* * This interrupt is not shared and controller already * disabled further interrupt. */ taskqueue_enqueue(sc->bge_tq, &sc->bge_intr_task); return (FILTER_HANDLED); } static void bge_intr_task(void *arg, int pending) { struct bge_softc *sc; struct ifnet *ifp; uint32_t status; uint16_t rx_prod, tx_cons; sc = (struct bge_softc *)arg; ifp = sc->bge_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; /* Get updated status block. */ bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); /* Save producer/consumer indexess. */ rx_prod = sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx; tx_cons = sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx; status = sc->bge_ldata.bge_status_block->bge_status; sc->bge_ldata.bge_status_block->bge_status = 0; bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Let controller work. */ bge_writembx(sc, BGE_MBX_IRQ0_LO, 0); if ((status & BGE_STATFLAG_LINKSTATE_CHANGED) != 0) { BGE_LOCK(sc); bge_link_upd(sc); BGE_UNLOCK(sc); } if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* Check RX return ring producer/consumer. */ bge_rxeof(sc, rx_prod, 0); } if (ifp->if_drv_flags & IFF_DRV_RUNNING) { BGE_LOCK(sc); /* Check TX ring producer/consumer. */ bge_txeof(sc, tx_cons); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) bge_start_locked(ifp); BGE_UNLOCK(sc); } } static void bge_intr(void *xsc) { struct bge_softc *sc; struct ifnet *ifp; uint32_t statusword; uint16_t rx_prod, tx_cons; sc = xsc; BGE_LOCK(sc); ifp = sc->bge_ifp; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { BGE_UNLOCK(sc); return; } #endif /* * Ack the interrupt by writing something to BGE_MBX_IRQ0_LO. Don't * disable interrupts by writing nonzero like we used to, since with * our current organization this just gives complications and * pessimizations for re-enabling interrupts. We used to have races * instead of the necessary complications. Disabling interrupts * would just reduce the chance of a status update while we are * running (by switching to the interrupt-mode coalescence * parameters), but this chance is already very low so it is more * efficient to get another interrupt than prevent it. * * We do the ack first to ensure another interrupt if there is a * status update after the ack. We don't check for the status * changing later because it is more efficient to get another * interrupt than prevent it, not quite as above (not checking is * a smaller optimization than not toggling the interrupt enable, * since checking doesn't involve PCI accesses and toggling require * the status check). So toggling would probably be a pessimization * even with MSI. It would only be needed for using a task queue. */ bge_writembx(sc, BGE_MBX_IRQ0_LO, 0); /* * Do the mandatory PCI flush as well as get the link status. */ statusword = CSR_READ_4(sc, BGE_MAC_STS) & BGE_MACSTAT_LINK_CHANGED; /* Make sure the descriptor ring indexes are coherent. */ bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); rx_prod = sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx; tx_cons = sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx; sc->bge_ldata.bge_status_block->bge_status = 0; bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); if ((sc->bge_asicrev == BGE_ASICREV_BCM5700 && sc->bge_chipid != BGE_CHIPID_BCM5700_B2) || statusword || sc->bge_link_evt) bge_link_upd(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* Check RX return ring producer/consumer. */ bge_rxeof(sc, rx_prod, 1); } if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* Check TX ring producer/consumer. */ bge_txeof(sc, tx_cons); } if (ifp->if_drv_flags & IFF_DRV_RUNNING && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) bge_start_locked(ifp); BGE_UNLOCK(sc); } static void bge_asf_driver_up(struct bge_softc *sc) { if (sc->bge_asf_mode & ASF_STACKUP) { /* Send ASF heartbeat aprox. every 2s */ if (sc->bge_asf_count) sc->bge_asf_count --; else { sc->bge_asf_count = 2; bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM_FW, BGE_FW_DRV_ALIVE); bge_writemem_ind(sc, BGE_SOFTWARE_GENNCOMM_FW_LEN, 4); bge_writemem_ind(sc, BGE_SOFTWARE_GENNCOMM_FW_DATA, 3); CSR_WRITE_4(sc, BGE_CPU_EVENT, CSR_READ_4(sc, BGE_CPU_EVENT) | (1 << 14)); } } } static void bge_tick(void *xsc) { struct bge_softc *sc = xsc; struct mii_data *mii = NULL; BGE_LOCK_ASSERT(sc); /* Synchronize with possible callout reset/stop. */ if (callout_pending(&sc->bge_stat_ch) || !callout_active(&sc->bge_stat_ch)) return; if (BGE_IS_5705_PLUS(sc)) bge_stats_update_regs(sc); else bge_stats_update(sc); if ((sc->bge_flags & BGE_FLAG_TBI) == 0) { mii = device_get_softc(sc->bge_miibus); /* * Do not touch PHY if we have link up. This could break * IPMI/ASF mode or produce extra input errors * (extra errors was reported for bcm5701 & bcm5704). */ if (!sc->bge_link) mii_tick(mii); } else { /* * Since in TBI mode auto-polling can't be used we should poll * link status manually. Here we register pending link event * and trigger interrupt. */ #ifdef DEVICE_POLLING /* In polling mode we poll link state in bge_poll(). */ if (!(sc->bge_ifp->if_capenable & IFCAP_POLLING)) #endif { sc->bge_link_evt++; if (sc->bge_asicrev == BGE_ASICREV_BCM5700 || sc->bge_flags & BGE_FLAG_5788) BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_SET); else BGE_SETBIT(sc, BGE_HCC_MODE, BGE_HCCMODE_COAL_NOW); } } bge_asf_driver_up(sc); bge_watchdog(sc); callout_reset(&sc->bge_stat_ch, hz, bge_tick, sc); } static void bge_stats_update_regs(struct bge_softc *sc) { struct ifnet *ifp; ifp = sc->bge_ifp; ifp->if_collisions += CSR_READ_4(sc, BGE_MAC_STATS + offsetof(struct bge_mac_stats_regs, etherStatsCollisions)); ifp->if_ierrors += CSR_READ_4(sc, BGE_RXLP_LOCSTAT_OUT_OF_BDS); ifp->if_ierrors += CSR_READ_4(sc, BGE_RXLP_LOCSTAT_IFIN_DROPS); ifp->if_ierrors += CSR_READ_4(sc, BGE_RXLP_LOCSTAT_IFIN_ERRORS); } static void bge_stats_update(struct bge_softc *sc) { struct ifnet *ifp; bus_size_t stats; uint32_t cnt; /* current register value */ ifp = sc->bge_ifp; stats = BGE_MEMWIN_START + BGE_STATS_BLOCK; #define READ_STAT(sc, stats, stat) \ CSR_READ_4(sc, stats + offsetof(struct bge_stats, stat)) cnt = READ_STAT(sc, stats, txstats.etherStatsCollisions.bge_addr_lo); ifp->if_collisions += (uint32_t)(cnt - sc->bge_tx_collisions); sc->bge_tx_collisions = cnt; cnt = READ_STAT(sc, stats, ifInDiscards.bge_addr_lo); ifp->if_ierrors += (uint32_t)(cnt - sc->bge_rx_discards); sc->bge_rx_discards = cnt; cnt = READ_STAT(sc, stats, txstats.ifOutDiscards.bge_addr_lo); ifp->if_oerrors += (uint32_t)(cnt - sc->bge_tx_discards); sc->bge_tx_discards = cnt; #undef READ_STAT } /* * Pad outbound frame to ETHER_MIN_NOPAD for an unusual reason. * The bge hardware will pad out Tx runts to ETHER_MIN_NOPAD, * but when such padded frames employ the bge IP/TCP checksum offload, * the hardware checksum assist gives incorrect results (possibly * from incorporating its own padding into the UDP/TCP checksum; who knows). * If we pad such runts with zeros, the onboard checksum comes out correct. */ static __inline int bge_cksum_pad(struct mbuf *m) { int padlen = ETHER_MIN_NOPAD - m->m_pkthdr.len; struct mbuf *last; /* If there's only the packet-header and we can pad there, use it. */ if (m->m_pkthdr.len == m->m_len && M_WRITABLE(m) && M_TRAILINGSPACE(m) >= padlen) { last = m; } else { /* * Walk packet chain to find last mbuf. We will either * pad there, or append a new mbuf and pad it. */ for (last = m; last->m_next != NULL; last = last->m_next); if (!(M_WRITABLE(last) && M_TRAILINGSPACE(last) >= padlen)) { /* Allocate new empty mbuf, pad it. Compact later. */ struct mbuf *n; MGET(n, M_DONTWAIT, MT_DATA); if (n == NULL) return (ENOBUFS); n->m_len = 0; last->m_next = n; last = n; } } /* Now zero the pad area, to avoid the bge cksum-assist bug. */ memset(mtod(last, caddr_t) + last->m_len, 0, padlen); last->m_len += padlen; m->m_pkthdr.len += padlen; return (0); } static struct mbuf * bge_setup_tso(struct bge_softc *sc, struct mbuf *m, uint16_t *mss) { - struct ether_header *eh; struct ip *ip; struct tcphdr *tcp; struct mbuf *n; uint16_t hlen; - uint32_t ip_off, poff; + uint32_t poff; if (M_WRITABLE(m) == 0) { /* Get a writable copy. */ n = m_dup(m, M_DONTWAIT); m_freem(m); if (n == NULL) return (NULL); m = n; } - ip_off = sizeof(struct ether_header); - m = m_pullup(m, ip_off); + m = m_pullup(m, sizeof(struct ether_header) + sizeof(struct ip)); if (m == NULL) return (NULL); - eh = mtod(m, struct ether_header *); - /* Check the existence of VLAN tag. */ - if (eh->ether_type == htons(ETHERTYPE_VLAN)) { - ip_off = sizeof(struct ether_vlan_header); - m = m_pullup(m, ip_off); - if (m == NULL) - return (NULL); - } - m = m_pullup(m, ip_off + sizeof(struct ip)); - if (m == NULL) - return (NULL); - ip = (struct ip *)(mtod(m, char *) + ip_off); - poff = ip_off + (ip->ip_hl << 2); + ip = (struct ip *)(mtod(m, char *) + sizeof(struct ether_header)); + poff = sizeof(struct ether_header) + (ip->ip_hl << 2); m = m_pullup(m, poff + sizeof(struct tcphdr)); if (m == NULL) return (NULL); tcp = (struct tcphdr *)(mtod(m, char *) + poff); - m = m_pullup(m, poff + sizeof(struct tcphdr) + tcp->th_off); + m = m_pullup(m, poff + (tcp->th_off << 2)); if (m == NULL) return (NULL); /* * It seems controller doesn't modify IP length and TCP pseudo * checksum. These checksum computed by upper stack should be 0. */ *mss = m->m_pkthdr.tso_segsz; ip->ip_sum = 0; ip->ip_len = htons(*mss + (ip->ip_hl << 2) + (tcp->th_off << 2)); /* Clear pseudo checksum computed by TCP stack. */ tcp->th_sum = 0; /* * Broadcom controllers uses different descriptor format for * TSO depending on ASIC revision. Due to TSO-capable firmware * license issue and lower performance of firmware based TSO * we only support hardware based TSO which is applicable for * BCM5755 or newer controllers. Hardware based TSO uses 11 * bits to store MSS and upper 5 bits are used to store IP/TCP * header length(including IP/TCP options). The header length * is expressed as 32 bits unit. */ hlen = ((ip->ip_hl << 2) + (tcp->th_off << 2)) >> 2; *mss |= (hlen << 11); return (m); } /* * Encapsulate an mbuf chain in the tx ring by coupling the mbuf data * pointers to descriptors. */ static int bge_encap(struct bge_softc *sc, struct mbuf **m_head, uint32_t *txidx) { bus_dma_segment_t segs[BGE_NSEG_NEW]; bus_dmamap_t map; struct bge_tx_bd *d; struct mbuf *m = *m_head; uint32_t idx = *txidx; uint16_t csum_flags, mss, vlan_tag; int nsegs, i, error; csum_flags = 0; mss = 0; vlan_tag = 0; if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { *m_head = m = bge_setup_tso(sc, m, &mss); if (*m_head == NULL) return (ENOBUFS); csum_flags |= BGE_TXBDFLAG_CPU_PRE_DMA | BGE_TXBDFLAG_CPU_POST_DMA; } else if ((m->m_pkthdr.csum_flags & BGE_CSUM_FEATURES) != 0) { if (m->m_pkthdr.csum_flags & CSUM_IP) csum_flags |= BGE_TXBDFLAG_IP_CSUM; if (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) { csum_flags |= BGE_TXBDFLAG_TCP_UDP_CSUM; if (m->m_pkthdr.len < ETHER_MIN_NOPAD && (error = bge_cksum_pad(m)) != 0) { m_freem(m); *m_head = NULL; return (error); } } if (m->m_flags & M_LASTFRAG) csum_flags |= BGE_TXBDFLAG_IP_FRAG_END; else if (m->m_flags & M_FRAG) csum_flags |= BGE_TXBDFLAG_IP_FRAG; } if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0 && sc->bge_forced_collapse > 0 && (sc->bge_flags & BGE_FLAG_PCIE) != 0 && m->m_next != NULL) { /* * Forcedly collapse mbuf chains to overcome hardware * limitation which only support a single outstanding * DMA read operation. */ if (sc->bge_forced_collapse == 1) m = m_defrag(m, M_DONTWAIT); else m = m_collapse(m, M_DONTWAIT, sc->bge_forced_collapse); if (m == NULL) m = *m_head; *m_head = m; } map = sc->bge_cdata.bge_tx_dmamap[idx]; error = bus_dmamap_load_mbuf_sg(sc->bge_cdata.bge_tx_mtag, map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { m = m_collapse(m, M_DONTWAIT, BGE_NSEG_NEW); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } *m_head = m; error = bus_dmamap_load_mbuf_sg(sc->bge_cdata.bge_tx_mtag, map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error) { m_freem(m); *m_head = NULL; return (error); } } else if (error != 0) return (error); /* Check if we have enough free send BDs. */ if (sc->bge_txcnt + nsegs >= BGE_TX_RING_CNT) { bus_dmamap_unload(sc->bge_cdata.bge_tx_mtag, map); return (ENOBUFS); } bus_dmamap_sync(sc->bge_cdata.bge_tx_mtag, map, BUS_DMASYNC_PREWRITE); #if __FreeBSD_version > 700022 if (m->m_flags & M_VLANTAG) { csum_flags |= BGE_TXBDFLAG_VLAN_TAG; vlan_tag = m->m_pkthdr.ether_vtag; } #else { struct m_tag *mtag; if ((mtag = VLAN_OUTPUT_TAG(sc->bge_ifp, m)) != NULL) { csum_flags |= BGE_TXBDFLAG_VLAN_TAG; vlan_tag = VLAN_TAG_VALUE(mtag); } } #endif for (i = 0; ; i++) { d = &sc->bge_ldata.bge_tx_ring[idx]; d->bge_addr.bge_addr_lo = BGE_ADDR_LO(segs[i].ds_addr); d->bge_addr.bge_addr_hi = BGE_ADDR_HI(segs[i].ds_addr); d->bge_len = segs[i].ds_len; d->bge_flags = csum_flags; d->bge_vlan_tag = vlan_tag; d->bge_mss = mss; if (i == nsegs - 1) break; BGE_INC(idx, BGE_TX_RING_CNT); } /* Mark the last segment as end of packet... */ d->bge_flags |= BGE_TXBDFLAG_END; /* * Insure that the map for this transmission * is placed at the array index of the last descriptor * in this chain. */ sc->bge_cdata.bge_tx_dmamap[*txidx] = sc->bge_cdata.bge_tx_dmamap[idx]; sc->bge_cdata.bge_tx_dmamap[idx] = map; sc->bge_cdata.bge_tx_chain[idx] = m; sc->bge_txcnt += nsegs; BGE_INC(idx, BGE_TX_RING_CNT); *txidx = idx; return (0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit descriptors. */ static void bge_start_locked(struct ifnet *ifp) { struct bge_softc *sc; struct mbuf *m_head; uint32_t prodidx; int count; sc = ifp->if_softc; BGE_LOCK_ASSERT(sc); if (!sc->bge_link || (ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; prodidx = sc->bge_tx_prodidx; for (count = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { if (sc->bge_txcnt > BGE_TX_RING_CNT - 16) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * XXX * The code inside the if() block is never reached since we * must mark CSUM_IP_FRAGS in our if_hwassist to start getting * requests to checksum TCP/UDP in a fragmented packet. * * XXX * safety overkill. If this is a fragmented packet chain * with delayed TCP/UDP checksums, then only encapsulate * it if we have enough descriptors to handle the entire * chain at once. * (paranoia -- may not actually be needed) */ if (m_head->m_flags & M_FIRSTFRAG && m_head->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { if ((BGE_TX_RING_CNT - sc->bge_txcnt) < m_head->m_pkthdr.csum_data + 16) { IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } } /* * Pack the data into the transmit ring. If we * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ if (bge_encap(sc, &m_head, &prodidx)) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ++count; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ #ifdef ETHER_BPF_MTAP ETHER_BPF_MTAP(ifp, m_head); #else BPF_MTAP(ifp, m_head); #endif } if (count > 0) { bus_dmamap_sync(sc->bge_cdata.bge_tx_ring_tag, sc->bge_cdata.bge_tx_ring_map, BUS_DMASYNC_PREWRITE); /* Transmit. */ bge_writembx(sc, BGE_MBX_TX_HOST_PROD0_LO, prodidx); /* 5700 b2 errata */ if (sc->bge_chiprev == BGE_CHIPREV_5700_BX) bge_writembx(sc, BGE_MBX_TX_HOST_PROD0_LO, prodidx); sc->bge_tx_prodidx = prodidx; /* * Set a timeout in case the chip goes out to lunch. */ sc->bge_timer = 5; } } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit descriptors. */ static void bge_start(struct ifnet *ifp) { struct bge_softc *sc; sc = ifp->if_softc; BGE_LOCK(sc); bge_start_locked(ifp); BGE_UNLOCK(sc); } static void bge_init_locked(struct bge_softc *sc) { struct ifnet *ifp; uint16_t *m; BGE_LOCK_ASSERT(sc); ifp = sc->bge_ifp; if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; /* Cancel pending I/O and flush buffers. */ bge_stop(sc); bge_stop_fw(sc); bge_sig_pre_reset(sc, BGE_RESET_START); bge_reset(sc); bge_sig_legacy(sc, BGE_RESET_START); bge_sig_post_reset(sc, BGE_RESET_START); bge_chipinit(sc); /* * Init the various state machines, ring * control blocks and firmware. */ if (bge_blockinit(sc)) { device_printf(sc->bge_dev, "initialization failure\n"); return; } ifp = sc->bge_ifp; /* Specify MTU. */ CSR_WRITE_4(sc, BGE_RX_MTU, ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN + (ifp->if_capenable & IFCAP_VLAN_MTU ? ETHER_VLAN_ENCAP_LEN : 0)); /* Load our MAC address. */ m = (uint16_t *)IF_LLADDR(sc->bge_ifp); CSR_WRITE_4(sc, BGE_MAC_ADDR1_LO, htons(m[0])); CSR_WRITE_4(sc, BGE_MAC_ADDR1_HI, (htons(m[1]) << 16) | htons(m[2])); /* Program promiscuous mode. */ bge_setpromisc(sc); /* Program multicast filter. */ bge_setmulti(sc); /* Program VLAN tag stripping. */ bge_setvlan(sc); /* Init RX ring. */ if (bge_init_rx_ring_std(sc) != 0) { device_printf(sc->bge_dev, "no memory for std Rx buffers.\n"); bge_stop(sc); return; } /* * Workaround for a bug in 5705 ASIC rev A0. Poll the NIC's * memory to insure that the chip has in fact read the first * entry of the ring. */ if (sc->bge_chipid == BGE_CHIPID_BCM5705_A0) { uint32_t v, i; for (i = 0; i < 10; i++) { DELAY(20); v = bge_readmem_ind(sc, BGE_STD_RX_RINGS + 8); if (v == (MCLBYTES - ETHER_ALIGN)) break; } if (i == 10) device_printf (sc->bge_dev, "5705 A0 chip failed to load RX ring\n"); } /* Init jumbo RX ring. */ if (ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN > (MCLBYTES - ETHER_ALIGN)) { if (bge_init_rx_ring_jumbo(sc) != 0) { device_printf(sc->bge_dev, "no memory for std Rx buffers.\n"); bge_stop(sc); return; } } /* Init our RX return ring index. */ sc->bge_rx_saved_considx = 0; /* Init our RX/TX stat counters. */ sc->bge_rx_discards = sc->bge_tx_discards = sc->bge_tx_collisions = 0; /* Init TX ring. */ bge_init_tx_ring(sc); /* Turn on transmitter. */ BGE_SETBIT(sc, BGE_TX_MODE, BGE_TXMODE_ENABLE); /* Turn on receiver. */ BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_ENABLE); /* Tell firmware we're alive. */ BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); #ifdef DEVICE_POLLING /* Disable interrupts if we are polling. */ if (ifp->if_capenable & IFCAP_POLLING) { BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); bge_writembx(sc, BGE_MBX_IRQ0_LO, 1); } else #endif /* Enable host interrupts. */ { BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_CLEAR_INTA); BGE_CLRBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); bge_writembx(sc, BGE_MBX_IRQ0_LO, 0); } bge_ifmedia_upd_locked(ifp); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; callout_reset(&sc->bge_stat_ch, hz, bge_tick, sc); } static void bge_init(void *xsc) { struct bge_softc *sc = xsc; BGE_LOCK(sc); bge_init_locked(sc); BGE_UNLOCK(sc); } /* * Set media options. */ static int bge_ifmedia_upd(struct ifnet *ifp) { struct bge_softc *sc = ifp->if_softc; int res; BGE_LOCK(sc); res = bge_ifmedia_upd_locked(ifp); BGE_UNLOCK(sc); return (res); } static int bge_ifmedia_upd_locked(struct ifnet *ifp) { struct bge_softc *sc = ifp->if_softc; struct mii_data *mii; struct mii_softc *miisc; struct ifmedia *ifm; BGE_LOCK_ASSERT(sc); ifm = &sc->bge_ifmedia; /* If this is a 1000baseX NIC, enable the TBI port. */ if (sc->bge_flags & BGE_FLAG_TBI) { if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); switch(IFM_SUBTYPE(ifm->ifm_media)) { case IFM_AUTO: /* * The BCM5704 ASIC appears to have a special * mechanism for programming the autoneg * advertisement registers in TBI mode. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5704) { uint32_t sgdig; sgdig = CSR_READ_4(sc, BGE_SGDIG_STS); if (sgdig & BGE_SGDIGSTS_DONE) { CSR_WRITE_4(sc, BGE_TX_TBI_AUTONEG, 0); sgdig = CSR_READ_4(sc, BGE_SGDIG_CFG); sgdig |= BGE_SGDIGCFG_AUTO | BGE_SGDIGCFG_PAUSE_CAP | BGE_SGDIGCFG_ASYM_PAUSE; CSR_WRITE_4(sc, BGE_SGDIG_CFG, sgdig | BGE_SGDIGCFG_SEND); DELAY(5); CSR_WRITE_4(sc, BGE_SGDIG_CFG, sgdig); } } break; case IFM_1000_SX: if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) { BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX); } else { BGE_SETBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX); } break; default: return (EINVAL); } return (0); } sc->bge_link_evt++; mii = device_get_softc(sc->bge_miibus); if (mii->mii_instance) LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); mii_mediachg(mii); /* * Force an interrupt so that we will call bge_link_upd * if needed and clear any pending link state attention. * Without this we are not getting any further interrupts * for link state changes and thus will not UP the link and * not be able to send in bge_start_locked. The only * way to get things working was to receive a packet and * get an RX intr. * bge_tick should help for fiber cards and we might not * need to do this here if BGE_FLAG_TBI is set but as * we poll for fiber anyway it should not harm. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5700 || sc->bge_flags & BGE_FLAG_5788) BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_SET); else BGE_SETBIT(sc, BGE_HCC_MODE, BGE_HCCMODE_COAL_NOW); return (0); } /* * Report current media status. */ static void bge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct bge_softc *sc = ifp->if_softc; struct mii_data *mii; BGE_LOCK(sc); if (sc->bge_flags & BGE_FLAG_TBI) { ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; if (CSR_READ_4(sc, BGE_MAC_STS) & BGE_MACSTAT_TBI_PCS_SYNCHED) ifmr->ifm_status |= IFM_ACTIVE; else { ifmr->ifm_active |= IFM_NONE; BGE_UNLOCK(sc); return; } ifmr->ifm_active |= IFM_1000_SX; if (CSR_READ_4(sc, BGE_MAC_MODE) & BGE_MACMODE_HALF_DUPLEX) ifmr->ifm_active |= IFM_HDX; else ifmr->ifm_active |= IFM_FDX; BGE_UNLOCK(sc); return; } mii = device_get_softc(sc->bge_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; BGE_UNLOCK(sc); } static int bge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct bge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int flags, mask, error = 0; switch (command) { case SIOCSIFMTU: if (ifr->ifr_mtu < ETHERMIN || ((BGE_IS_JUMBO_CAPABLE(sc)) && ifr->ifr_mtu > BGE_JUMBO_MTU) || ((!BGE_IS_JUMBO_CAPABLE(sc)) && ifr->ifr_mtu > ETHERMTU)) error = EINVAL; else if (ifp->if_mtu != ifr->ifr_mtu) { ifp->if_mtu = ifr->ifr_mtu; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; bge_init(sc); } break; case SIOCSIFFLAGS: BGE_LOCK(sc); if (ifp->if_flags & IFF_UP) { /* * If only the state of the PROMISC flag changed, * then just use the 'set promisc mode' command * instead of reinitializing the entire NIC. Doing * a full re-init means reloading the firmware and * waiting for it to start up, which may take a * second or two. Similarly for ALLMULTI. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) { flags = ifp->if_flags ^ sc->bge_if_flags; if (flags & IFF_PROMISC) bge_setpromisc(sc); if (flags & IFF_ALLMULTI) bge_setmulti(sc); } else bge_init_locked(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { bge_stop(sc); } } sc->bge_if_flags = ifp->if_flags; BGE_UNLOCK(sc); error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: if (ifp->if_drv_flags & IFF_DRV_RUNNING) { BGE_LOCK(sc); bge_setmulti(sc); BGE_UNLOCK(sc); error = 0; } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: if (sc->bge_flags & BGE_FLAG_TBI) { error = ifmedia_ioctl(ifp, ifr, &sc->bge_ifmedia, command); } else { mii = device_get_softc(sc->bge_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); } break; case SIOCSIFCAP: mask = ifr->ifr_reqcap ^ ifp->if_capenable; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(bge_poll, ifp); if (error) return (error); BGE_LOCK(sc); BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); bge_writembx(sc, BGE_MBX_IRQ0_LO, 1); ifp->if_capenable |= IFCAP_POLLING; BGE_UNLOCK(sc); } else { error = ether_poll_deregister(ifp); /* Enable interrupt even in error case */ BGE_LOCK(sc); BGE_CLRBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); bge_writembx(sc, BGE_MBX_IRQ0_LO, 0); ifp->if_capenable &= ~IFCAP_POLLING; BGE_UNLOCK(sc); } } #endif if (mask & IFCAP_HWCSUM) { ifp->if_capenable ^= IFCAP_HWCSUM; if (IFCAP_HWCSUM & ifp->if_capenable && IFCAP_HWCSUM & ifp->if_capabilities) ifp->if_hwassist |= BGE_CSUM_FEATURES; else ifp->if_hwassist &= ~BGE_CSUM_FEATURES; } if ((mask & IFCAP_TSO4) != 0 && (ifp->if_capabilities & IFCAP_TSO4) != 0) { ifp->if_capenable ^= IFCAP_TSO4; if ((ifp->if_capenable & IFCAP_TSO4) != 0) ifp->if_hwassist |= CSUM_TSO; else ifp->if_hwassist &= ~CSUM_TSO; } if (mask & IFCAP_VLAN_MTU) { ifp->if_capenable ^= IFCAP_VLAN_MTU; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; bge_init(sc); } if ((mask & IFCAP_VLAN_HWTSO) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWTSO; if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) ifp->if_capenable &= ~IFCAP_VLAN_HWTSO; BGE_LOCK(sc); bge_setvlan(sc); BGE_UNLOCK(sc); } #ifdef VLAN_CAPABILITIES VLAN_CAPABILITIES(ifp); #endif break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void bge_watchdog(struct bge_softc *sc) { struct ifnet *ifp; BGE_LOCK_ASSERT(sc); if (sc->bge_timer == 0 || --sc->bge_timer) return; ifp = sc->bge_ifp; if_printf(ifp, "watchdog timeout -- resetting\n"); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; bge_init_locked(sc); ifp->if_oerrors++; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void bge_stop(struct bge_softc *sc) { struct ifnet *ifp; BGE_LOCK_ASSERT(sc); ifp = sc->bge_ifp; callout_stop(&sc->bge_stat_ch); /* Disable host interrupts. */ BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); bge_writembx(sc, BGE_MBX_IRQ0_LO, 1); /* * Tell firmware we're shutting down. */ bge_stop_fw(sc); bge_sig_pre_reset(sc, BGE_RESET_STOP); /* * Disable all of the receiver blocks. */ BGE_CLRBIT(sc, BGE_RX_MODE, BGE_RXMODE_ENABLE); BGE_CLRBIT(sc, BGE_RBDI_MODE, BGE_RBDIMODE_ENABLE); BGE_CLRBIT(sc, BGE_RXLP_MODE, BGE_RXLPMODE_ENABLE); if (!(BGE_IS_5705_PLUS(sc))) BGE_CLRBIT(sc, BGE_RXLS_MODE, BGE_RXLSMODE_ENABLE); BGE_CLRBIT(sc, BGE_RDBDI_MODE, BGE_RBDIMODE_ENABLE); BGE_CLRBIT(sc, BGE_RDC_MODE, BGE_RDCMODE_ENABLE); BGE_CLRBIT(sc, BGE_RBDC_MODE, BGE_RBDCMODE_ENABLE); /* * Disable all of the transmit blocks. */ BGE_CLRBIT(sc, BGE_SRS_MODE, BGE_SRSMODE_ENABLE); BGE_CLRBIT(sc, BGE_SBDI_MODE, BGE_SBDIMODE_ENABLE); BGE_CLRBIT(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE); BGE_CLRBIT(sc, BGE_RDMA_MODE, BGE_RDMAMODE_ENABLE); BGE_CLRBIT(sc, BGE_SDC_MODE, BGE_SDCMODE_ENABLE); if (!(BGE_IS_5705_PLUS(sc))) BGE_CLRBIT(sc, BGE_DMAC_MODE, BGE_DMACMODE_ENABLE); BGE_CLRBIT(sc, BGE_SBDC_MODE, BGE_SBDCMODE_ENABLE); /* * Shut down all of the memory managers and related * state machines. */ BGE_CLRBIT(sc, BGE_HCC_MODE, BGE_HCCMODE_ENABLE); BGE_CLRBIT(sc, BGE_WDMA_MODE, BGE_WDMAMODE_ENABLE); if (!(BGE_IS_5705_PLUS(sc))) BGE_CLRBIT(sc, BGE_MBCF_MODE, BGE_MBCFMODE_ENABLE); CSR_WRITE_4(sc, BGE_FTQ_RESET, 0xFFFFFFFF); CSR_WRITE_4(sc, BGE_FTQ_RESET, 0); if (!(BGE_IS_5705_PLUS(sc))) { BGE_CLRBIT(sc, BGE_BMAN_MODE, BGE_BMANMODE_ENABLE); BGE_CLRBIT(sc, BGE_MARB_MODE, BGE_MARBMODE_ENABLE); } bge_reset(sc); bge_sig_legacy(sc, BGE_RESET_STOP); bge_sig_post_reset(sc, BGE_RESET_STOP); /* * Keep the ASF firmware running if up. */ if (sc->bge_asf_mode & ASF_STACKUP) BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); else BGE_CLRBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); /* Free the RX lists. */ bge_free_rx_ring_std(sc); /* Free jumbo RX list. */ if (BGE_IS_JUMBO_CAPABLE(sc)) bge_free_rx_ring_jumbo(sc); /* Free TX buffers. */ bge_free_tx_ring(sc); sc->bge_tx_saved_considx = BGE_TXCONS_UNSET; /* Clear MAC's link state (PHY may still have link UP). */ if (bootverbose && sc->bge_link) if_printf(sc->bge_ifp, "link DOWN\n"); sc->bge_link = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int bge_shutdown(device_t dev) { struct bge_softc *sc; sc = device_get_softc(dev); BGE_LOCK(sc); bge_stop(sc); bge_reset(sc); BGE_UNLOCK(sc); return (0); } static int bge_suspend(device_t dev) { struct bge_softc *sc; sc = device_get_softc(dev); BGE_LOCK(sc); bge_stop(sc); BGE_UNLOCK(sc); return (0); } static int bge_resume(device_t dev) { struct bge_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); BGE_LOCK(sc); ifp = sc->bge_ifp; if (ifp->if_flags & IFF_UP) { bge_init_locked(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) bge_start_locked(ifp); } BGE_UNLOCK(sc); return (0); } static void bge_link_upd(struct bge_softc *sc) { struct mii_data *mii; uint32_t link, status; BGE_LOCK_ASSERT(sc); /* Clear 'pending link event' flag. */ sc->bge_link_evt = 0; /* * Process link state changes. * Grrr. The link status word in the status block does * not work correctly on the BCM5700 rev AX and BX chips, * according to all available information. Hence, we have * to enable MII interrupts in order to properly obtain * async link changes. Unfortunately, this also means that * we have to read the MAC status register to detect link * changes, thereby adding an additional register access to * the interrupt handler. * * XXX: perhaps link state detection procedure used for * BGE_CHIPID_BCM5700_B2 can be used for others BCM5700 revisions. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5700 && sc->bge_chipid != BGE_CHIPID_BCM5700_B2) { status = CSR_READ_4(sc, BGE_MAC_STS); if (status & BGE_MACSTAT_MI_INTERRUPT) { mii = device_get_softc(sc->bge_miibus); mii_pollstat(mii); if (!sc->bge_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->bge_link++; if (bootverbose) if_printf(sc->bge_ifp, "link UP\n"); } else if (sc->bge_link && (!(mii->mii_media_status & IFM_ACTIVE) || IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE)) { sc->bge_link = 0; if (bootverbose) if_printf(sc->bge_ifp, "link DOWN\n"); } /* Clear the interrupt. */ CSR_WRITE_4(sc, BGE_MAC_EVT_ENB, BGE_EVTENB_MI_INTERRUPT); bge_miibus_readreg(sc->bge_dev, 1, BRGPHY_MII_ISR); bge_miibus_writereg(sc->bge_dev, 1, BRGPHY_MII_IMR, BRGPHY_INTRS); } return; } if (sc->bge_flags & BGE_FLAG_TBI) { status = CSR_READ_4(sc, BGE_MAC_STS); if (status & BGE_MACSTAT_TBI_PCS_SYNCHED) { if (!sc->bge_link) { sc->bge_link++; if (sc->bge_asicrev == BGE_ASICREV_BCM5704) BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_TBI_SEND_CFGS); CSR_WRITE_4(sc, BGE_MAC_STS, 0xFFFFFFFF); if (bootverbose) if_printf(sc->bge_ifp, "link UP\n"); if_link_state_change(sc->bge_ifp, LINK_STATE_UP); } } else if (sc->bge_link) { sc->bge_link = 0; if (bootverbose) if_printf(sc->bge_ifp, "link DOWN\n"); if_link_state_change(sc->bge_ifp, LINK_STATE_DOWN); } } else if (CSR_READ_4(sc, BGE_MI_MODE) & BGE_MIMODE_AUTOPOLL) { /* * Some broken BCM chips have BGE_STATFLAG_LINKSTATE_CHANGED bit * in status word always set. Workaround this bug by reading * PHY link status directly. */ link = (CSR_READ_4(sc, BGE_MI_STS) & BGE_MISTS_LINK) ? 1 : 0; if (link != sc->bge_link || sc->bge_asicrev == BGE_ASICREV_BCM5700) { mii = device_get_softc(sc->bge_miibus); mii_pollstat(mii); if (!sc->bge_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->bge_link++; if (bootverbose) if_printf(sc->bge_ifp, "link UP\n"); } else if (sc->bge_link && (!(mii->mii_media_status & IFM_ACTIVE) || IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE)) { sc->bge_link = 0; if (bootverbose) if_printf(sc->bge_ifp, "link DOWN\n"); } } } else { /* * Discard link events for MII/GMII controllers * if MI auto-polling is disabled. */ } /* Clear the attention. */ CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED | BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE | BGE_MACSTAT_LINK_CHANGED); } #define BGE_SYSCTL_STAT(sc, ctx, desc, parent, node, oid) \ SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, oid, CTLTYPE_UINT|CTLFLAG_RD, \ sc, offsetof(struct bge_stats, node), bge_sysctl_stats, "IU", \ desc) static void bge_add_sysctls(struct bge_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children, *schildren; struct sysctl_oid *tree; ctx = device_get_sysctl_ctx(sc->bge_dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->bge_dev)); #ifdef BGE_REGISTER_DEBUG SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "debug_info", CTLTYPE_INT | CTLFLAG_RW, sc, 0, bge_sysctl_debug_info, "I", "Debug Information"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "reg_read", CTLTYPE_INT | CTLFLAG_RW, sc, 0, bge_sysctl_reg_read, "I", "Register Read"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "mem_read", CTLTYPE_INT | CTLFLAG_RW, sc, 0, bge_sysctl_mem_read, "I", "Memory Read"); #endif /* * A common design characteristic for many Broadcom client controllers * is that they only support a single outstanding DMA read operation * on the PCIe bus. This means that it will take twice as long to fetch * a TX frame that is split into header and payload buffers as it does * to fetch a single, contiguous TX frame (2 reads vs. 1 read). For * these controllers, coalescing buffers to reduce the number of memory * reads is effective way to get maximum performance(about 940Mbps). * Without collapsing TX buffers the maximum TCP bulk transfer * performance is about 850Mbps. However forcing coalescing mbufs * consumes a lot of CPU cycles, so leave it off by default. */ SYSCTL_ADD_INT(ctx, children, OID_AUTO, "forced_collapse", CTLFLAG_RW, &sc->bge_forced_collapse, 0, "Number of fragmented TX buffers of a frame allowed before " "forced collapsing"); resource_int_value(device_get_name(sc->bge_dev), device_get_unit(sc->bge_dev), "forced_collapse", &sc->bge_forced_collapse); if (BGE_IS_5705_PLUS(sc)) return; tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "stats", CTLFLAG_RD, NULL, "BGE Statistics"); schildren = children = SYSCTL_CHILDREN(tree); BGE_SYSCTL_STAT(sc, ctx, "Frames Dropped Due To Filters", children, COSFramesDroppedDueToFilters, "FramesDroppedDueToFilters"); BGE_SYSCTL_STAT(sc, ctx, "NIC DMA Write Queue Full", children, nicDmaWriteQueueFull, "DmaWriteQueueFull"); BGE_SYSCTL_STAT(sc, ctx, "NIC DMA Write High Priority Queue Full", children, nicDmaWriteHighPriQueueFull, "DmaWriteHighPriQueueFull"); BGE_SYSCTL_STAT(sc, ctx, "NIC No More RX Buffer Descriptors", children, nicNoMoreRxBDs, "NoMoreRxBDs"); BGE_SYSCTL_STAT(sc, ctx, "Discarded Input Frames", children, ifInDiscards, "InputDiscards"); BGE_SYSCTL_STAT(sc, ctx, "Input Errors", children, ifInErrors, "InputErrors"); BGE_SYSCTL_STAT(sc, ctx, "NIC Recv Threshold Hit", children, nicRecvThresholdHit, "RecvThresholdHit"); BGE_SYSCTL_STAT(sc, ctx, "NIC DMA Read Queue Full", children, nicDmaReadQueueFull, "DmaReadQueueFull"); BGE_SYSCTL_STAT(sc, ctx, "NIC DMA Read High Priority Queue Full", children, nicDmaReadHighPriQueueFull, "DmaReadHighPriQueueFull"); BGE_SYSCTL_STAT(sc, ctx, "NIC Send Data Complete Queue Full", children, nicSendDataCompQueueFull, "SendDataCompQueueFull"); BGE_SYSCTL_STAT(sc, ctx, "NIC Ring Set Send Producer Index", children, nicRingSetSendProdIndex, "RingSetSendProdIndex"); BGE_SYSCTL_STAT(sc, ctx, "NIC Ring Status Update", children, nicRingStatusUpdate, "RingStatusUpdate"); BGE_SYSCTL_STAT(sc, ctx, "NIC Interrupts", children, nicInterrupts, "Interrupts"); BGE_SYSCTL_STAT(sc, ctx, "NIC Avoided Interrupts", children, nicAvoidedInterrupts, "AvoidedInterrupts"); BGE_SYSCTL_STAT(sc, ctx, "NIC Send Threshold Hit", children, nicSendThresholdHit, "SendThresholdHit"); tree = SYSCTL_ADD_NODE(ctx, schildren, OID_AUTO, "rx", CTLFLAG_RD, NULL, "BGE RX Statistics"); children = SYSCTL_CHILDREN(tree); BGE_SYSCTL_STAT(sc, ctx, "Inbound Octets", children, rxstats.ifHCInOctets, "Octets"); BGE_SYSCTL_STAT(sc, ctx, "Fragments", children, rxstats.etherStatsFragments, "Fragments"); BGE_SYSCTL_STAT(sc, ctx, "Inbound Unicast Packets", children, rxstats.ifHCInUcastPkts, "UcastPkts"); BGE_SYSCTL_STAT(sc, ctx, "Inbound Multicast Packets", children, rxstats.ifHCInMulticastPkts, "MulticastPkts"); BGE_SYSCTL_STAT(sc, ctx, "FCS Errors", children, rxstats.dot3StatsFCSErrors, "FCSErrors"); BGE_SYSCTL_STAT(sc, ctx, "Alignment Errors", children, rxstats.dot3StatsAlignmentErrors, "AlignmentErrors"); BGE_SYSCTL_STAT(sc, ctx, "XON Pause Frames Received", children, rxstats.xonPauseFramesReceived, "xonPauseFramesReceived"); BGE_SYSCTL_STAT(sc, ctx, "XOFF Pause Frames Received", children, rxstats.xoffPauseFramesReceived, "xoffPauseFramesReceived"); BGE_SYSCTL_STAT(sc, ctx, "MAC Control Frames Received", children, rxstats.macControlFramesReceived, "ControlFramesReceived"); BGE_SYSCTL_STAT(sc, ctx, "XOFF State Entered", children, rxstats.xoffStateEntered, "xoffStateEntered"); BGE_SYSCTL_STAT(sc, ctx, "Frames Too Long", children, rxstats.dot3StatsFramesTooLong, "FramesTooLong"); BGE_SYSCTL_STAT(sc, ctx, "Jabbers", children, rxstats.etherStatsJabbers, "Jabbers"); BGE_SYSCTL_STAT(sc, ctx, "Undersized Packets", children, rxstats.etherStatsUndersizePkts, "UndersizePkts"); BGE_SYSCTL_STAT(sc, ctx, "Inbound Range Length Errors", children, rxstats.inRangeLengthError, "inRangeLengthError"); BGE_SYSCTL_STAT(sc, ctx, "Outbound Range Length Errors", children, rxstats.outRangeLengthError, "outRangeLengthError"); tree = SYSCTL_ADD_NODE(ctx, schildren, OID_AUTO, "tx", CTLFLAG_RD, NULL, "BGE TX Statistics"); children = SYSCTL_CHILDREN(tree); BGE_SYSCTL_STAT(sc, ctx, "Outbound Octets", children, txstats.ifHCOutOctets, "Octets"); BGE_SYSCTL_STAT(sc, ctx, "TX Collisions", children, txstats.etherStatsCollisions, "Collisions"); BGE_SYSCTL_STAT(sc, ctx, "XON Sent", children, txstats.outXonSent, "XonSent"); BGE_SYSCTL_STAT(sc, ctx, "XOFF Sent", children, txstats.outXoffSent, "XoffSent"); BGE_SYSCTL_STAT(sc, ctx, "Flow Control Done", children, txstats.flowControlDone, "flowControlDone"); BGE_SYSCTL_STAT(sc, ctx, "Internal MAC TX errors", children, txstats.dot3StatsInternalMacTransmitErrors, "InternalMacTransmitErrors"); BGE_SYSCTL_STAT(sc, ctx, "Single Collision Frames", children, txstats.dot3StatsSingleCollisionFrames, "SingleCollisionFrames"); BGE_SYSCTL_STAT(sc, ctx, "Multiple Collision Frames", children, txstats.dot3StatsMultipleCollisionFrames, "MultipleCollisionFrames"); BGE_SYSCTL_STAT(sc, ctx, "Deferred Transmissions", children, txstats.dot3StatsDeferredTransmissions, "DeferredTransmissions"); BGE_SYSCTL_STAT(sc, ctx, "Excessive Collisions", children, txstats.dot3StatsExcessiveCollisions, "ExcessiveCollisions"); BGE_SYSCTL_STAT(sc, ctx, "Late Collisions", children, txstats.dot3StatsLateCollisions, "LateCollisions"); BGE_SYSCTL_STAT(sc, ctx, "Outbound Unicast Packets", children, txstats.ifHCOutUcastPkts, "UcastPkts"); BGE_SYSCTL_STAT(sc, ctx, "Outbound Multicast Packets", children, txstats.ifHCOutMulticastPkts, "MulticastPkts"); BGE_SYSCTL_STAT(sc, ctx, "Outbound Broadcast Packets", children, txstats.ifHCOutBroadcastPkts, "BroadcastPkts"); BGE_SYSCTL_STAT(sc, ctx, "Carrier Sense Errors", children, txstats.dot3StatsCarrierSenseErrors, "CarrierSenseErrors"); BGE_SYSCTL_STAT(sc, ctx, "Outbound Discards", children, txstats.ifOutDiscards, "Discards"); BGE_SYSCTL_STAT(sc, ctx, "Outbound Errors", children, txstats.ifOutErrors, "Errors"); } static int bge_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct bge_softc *sc; uint32_t result; int offset; sc = (struct bge_softc *)arg1; offset = arg2; result = CSR_READ_4(sc, BGE_MEMWIN_START + BGE_STATS_BLOCK + offset + offsetof(bge_hostaddr, bge_addr_lo)); return (sysctl_handle_int(oidp, &result, 0, req)); } #ifdef BGE_REGISTER_DEBUG static int bge_sysctl_debug_info(SYSCTL_HANDLER_ARGS) { struct bge_softc *sc; uint16_t *sbdata; int error; int result; int i, j; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || (req->newptr == NULL)) return (error); if (result == 1) { sc = (struct bge_softc *)arg1; sbdata = (uint16_t *)sc->bge_ldata.bge_status_block; printf("Status Block:\n"); for (i = 0x0; i < (BGE_STATUS_BLK_SZ / 4); ) { printf("%06x:", i); for (j = 0; j < 8; j++) { printf(" %04x", sbdata[i]); i += 4; } printf("\n"); } printf("Registers:\n"); for (i = 0x800; i < 0xA00; ) { printf("%06x:", i); for (j = 0; j < 8; j++) { printf(" %08x", CSR_READ_4(sc, i)); i += 4; } printf("\n"); } printf("Hardware Flags:\n"); if (BGE_IS_5755_PLUS(sc)) printf(" - 5755 Plus\n"); if (BGE_IS_575X_PLUS(sc)) printf(" - 575X Plus\n"); if (BGE_IS_5705_PLUS(sc)) printf(" - 5705 Plus\n"); if (BGE_IS_5714_FAMILY(sc)) printf(" - 5714 Family\n"); if (BGE_IS_5700_FAMILY(sc)) printf(" - 5700 Family\n"); if (sc->bge_flags & BGE_FLAG_JUMBO) printf(" - Supports Jumbo Frames\n"); if (sc->bge_flags & BGE_FLAG_PCIX) printf(" - PCI-X Bus\n"); if (sc->bge_flags & BGE_FLAG_PCIE) printf(" - PCI Express Bus\n"); if (sc->bge_flags & BGE_FLAG_NO_3LED) printf(" - No 3 LEDs\n"); if (sc->bge_flags & BGE_FLAG_RX_ALIGNBUG) printf(" - RX Alignment Bug\n"); } return (error); } static int bge_sysctl_reg_read(SYSCTL_HANDLER_ARGS) { struct bge_softc *sc; int error; uint16_t result; uint32_t val; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || (req->newptr == NULL)) return (error); if (result < 0x8000) { sc = (struct bge_softc *)arg1; val = CSR_READ_4(sc, result); printf("reg 0x%06X = 0x%08X\n", result, val); } return (error); } static int bge_sysctl_mem_read(SYSCTL_HANDLER_ARGS) { struct bge_softc *sc; int error; uint16_t result; uint32_t val; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || (req->newptr == NULL)) return (error); if (result < 0x8000) { sc = (struct bge_softc *)arg1; val = bge_readmem_ind(sc, result); printf("mem 0x%06X = 0x%08X\n", result, val); } return (error); } #endif static int bge_get_eaddr_fw(struct bge_softc *sc, uint8_t ether_addr[]) { if (sc->bge_flags & BGE_FLAG_EADDR) return (1); #ifdef __sparc64__ OF_getetheraddr(sc->bge_dev, ether_addr); return (0); #endif return (1); } static int bge_get_eaddr_mem(struct bge_softc *sc, uint8_t ether_addr[]) { uint32_t mac_addr; mac_addr = bge_readmem_ind(sc, 0x0c14); if ((mac_addr >> 16) == 0x484b) { ether_addr[0] = (uint8_t)(mac_addr >> 8); ether_addr[1] = (uint8_t)mac_addr; mac_addr = bge_readmem_ind(sc, 0x0c18); ether_addr[2] = (uint8_t)(mac_addr >> 24); ether_addr[3] = (uint8_t)(mac_addr >> 16); ether_addr[4] = (uint8_t)(mac_addr >> 8); ether_addr[5] = (uint8_t)mac_addr; return (0); } return (1); } static int bge_get_eaddr_nvram(struct bge_softc *sc, uint8_t ether_addr[]) { int mac_offset = BGE_EE_MAC_OFFSET; if (sc->bge_asicrev == BGE_ASICREV_BCM5906) mac_offset = BGE_EE_MAC_OFFSET_5906; return (bge_read_nvram(sc, ether_addr, mac_offset + 2, ETHER_ADDR_LEN)); } static int bge_get_eaddr_eeprom(struct bge_softc *sc, uint8_t ether_addr[]) { if (sc->bge_asicrev == BGE_ASICREV_BCM5906) return (1); return (bge_read_eeprom(sc, ether_addr, BGE_EE_MAC_OFFSET + 2, ETHER_ADDR_LEN)); } static int bge_get_eaddr(struct bge_softc *sc, uint8_t eaddr[]) { static const bge_eaddr_fcn_t bge_eaddr_funcs[] = { /* NOTE: Order is critical */ bge_get_eaddr_fw, bge_get_eaddr_mem, bge_get_eaddr_nvram, bge_get_eaddr_eeprom, NULL }; const bge_eaddr_fcn_t *func; for (func = bge_eaddr_funcs; *func != NULL; ++func) { if ((*func)(sc, eaddr) == 0) break; } return (*func == NULL ? ENXIO : 0); } Index: projects/ppc64/sys/dev/bwn/if_bwn.c =================================================================== --- projects/ppc64/sys/dev/bwn/if_bwn.c (revision 204271) +++ projects/ppc64/sys/dev/bwn/if_bwn.c (revision 204272) @@ -1,14322 +1,14340 @@ /*- * Copyright (c) 2009-2010 Weongyo Jeong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * The Broadcom Wireless LAN controller driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_NODE(_hw, OID_AUTO, bwn, CTLFLAG_RD, 0, "Broadcom driver parameters"); /* * Tunable & sysctl variables. */ #ifdef BWN_DEBUG static int bwn_debug = 0; SYSCTL_INT(_hw_bwn, OID_AUTO, debug, CTLFLAG_RW, &bwn_debug, 0, "Broadcom debugging printfs"); TUNABLE_INT("hw.bwn.debug", &bwn_debug); enum { BWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ BWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ BWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ BWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ BWN_DEBUG_RESET = 0x00000010, /* reset processing */ BWN_DEBUG_OPS = 0x00000020, /* bwn_ops processing */ BWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ BWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ BWN_DEBUG_INTR = 0x00000100, /* ISR */ BWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ BWN_DEBUG_NODE = 0x00000400, /* node management */ BWN_DEBUG_LED = 0x00000800, /* led management */ BWN_DEBUG_CMD = 0x00001000, /* cmd submission */ BWN_DEBUG_LO = 0x00002000, /* LO */ BWN_DEBUG_FW = 0x00004000, /* firmware */ BWN_DEBUG_WME = 0x00008000, /* WME */ BWN_DEBUG_RF = 0x00010000, /* RF */ BWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ BWN_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) #endif static int bwn_bfp = 0; /* use "Bad Frames Preemption" */ SYSCTL_INT(_hw_bwn, OID_AUTO, bfp, CTLFLAG_RW, &bwn_bfp, 0, "uses Bad Frames Preemption"); static int bwn_bluetooth = 1; SYSCTL_INT(_hw_bwn, OID_AUTO, bluetooth, CTLFLAG_RW, &bwn_bluetooth, 0, "turns on Bluetooth Coexistence"); static int bwn_hwpctl = 0; SYSCTL_INT(_hw_bwn, OID_AUTO, hwpctl, CTLFLAG_RW, &bwn_hwpctl, 0, "uses H/W power control"); static int bwn_msi_disable = 0; /* MSI disabled */ TUNABLE_INT("hw.bwn.msi_disable", &bwn_msi_disable); static int bwn_usedma = 1; SYSCTL_INT(_hw_bwn, OID_AUTO, usedma, CTLFLAG_RD, &bwn_usedma, 0, "uses DMA"); TUNABLE_INT("hw.bwn.usedma", &bwn_usedma); static int bwn_wme = 1; SYSCTL_INT(_hw_bwn, OID_AUTO, wme, CTLFLAG_RW, &bwn_wme, 0, "uses WME support"); static int bwn_attach_pre(struct bwn_softc *); static int bwn_attach_post(struct bwn_softc *); static void bwn_sprom_bugfixes(struct siba_softc *); static void bwn_init(void *); static int bwn_init_locked(struct bwn_softc *); static int bwn_ioctl(struct ifnet *, u_long, caddr_t); static void bwn_start(struct ifnet *); static int bwn_attach_core(struct bwn_mac *); static void bwn_reset_core(struct bwn_mac *, uint32_t); static int bwn_phy_getinfo(struct bwn_mac *, int); static int bwn_chiptest(struct bwn_mac *); static int bwn_setup_channels(struct bwn_mac *, int, int); static int bwn_phy_g_attach(struct bwn_mac *); static void bwn_phy_g_detach(struct bwn_mac *); static void bwn_phy_g_init_pre(struct bwn_mac *); static int bwn_phy_g_prepare_hw(struct bwn_mac *); static int bwn_phy_g_init(struct bwn_mac *); static void bwn_phy_g_exit(struct bwn_mac *); static uint16_t bwn_phy_g_read(struct bwn_mac *, uint16_t); static void bwn_phy_g_write(struct bwn_mac *, uint16_t, uint16_t); static uint16_t bwn_phy_g_rf_read(struct bwn_mac *, uint16_t); static void bwn_phy_g_rf_write(struct bwn_mac *, uint16_t, uint16_t); static int bwn_phy_g_hwpctl(struct bwn_mac *); static void bwn_phy_g_rf_onoff(struct bwn_mac *, int); static int bwn_phy_g_switch_channel(struct bwn_mac *, uint32_t); static uint32_t bwn_phy_g_get_default_chan(struct bwn_mac *); static void bwn_phy_g_set_antenna(struct bwn_mac *, int); static int bwn_phy_g_im(struct bwn_mac *, int); static int bwn_phy_g_recalc_txpwr(struct bwn_mac *, int); static void bwn_phy_g_set_txpwr(struct bwn_mac *); static void bwn_phy_g_task_15s(struct bwn_mac *); static void bwn_phy_g_task_60s(struct bwn_mac *); static uint16_t bwn_phy_g_txctl(struct bwn_mac *); static void bwn_phy_switch_analog(struct bwn_mac *, int); static uint16_t bwn_shm_read_2(struct bwn_mac *, uint16_t, uint16_t); static void bwn_shm_write_2(struct bwn_mac *, uint16_t, uint16_t, uint16_t); static uint32_t bwn_shm_read_4(struct bwn_mac *, uint16_t, uint16_t); static void bwn_shm_write_4(struct bwn_mac *, uint16_t, uint16_t, uint32_t); static void bwn_shm_ctlword(struct bwn_mac *, uint16_t, uint16_t); static void bwn_addchannels(struct ieee80211_channel [], int, int *, const struct bwn_channelinfo *, int); static int bwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void bwn_newassoc(struct ieee80211_node *, int); static void bwn_updateslot(struct ifnet *); static void bwn_update_promisc(struct ifnet *); static void bwn_wme_init(struct bwn_mac *); static int bwn_wme_update(struct ieee80211com *); static struct ieee80211_node *bwn_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); static void bwn_wme_clear(struct bwn_softc *); static void bwn_wme_load(struct bwn_mac *); static void bwn_wme_loadparams(struct bwn_mac *, const struct wmeParams *, uint16_t); static void bwn_node_cleanup(struct ieee80211_node *); static void bwn_scan_start(struct ieee80211com *); static void bwn_scan_end(struct ieee80211com *); static void bwn_set_channel(struct ieee80211com *); static struct ieee80211vap *bwn_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, int, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void bwn_vap_delete(struct ieee80211vap *); static void bwn_stop(struct bwn_softc *, int); static void bwn_stop_locked(struct bwn_softc *, int); static int bwn_core_init(struct bwn_mac *); static void bwn_core_start(struct bwn_mac *); static void bwn_core_exit(struct bwn_mac *); static void bwn_fix_imcfglobug(struct bwn_mac *); static void bwn_bt_disable(struct bwn_mac *); static int bwn_chip_init(struct bwn_mac *); static uint64_t bwn_hf_read(struct bwn_mac *); static void bwn_hf_write(struct bwn_mac *, uint64_t); static void bwn_set_txretry(struct bwn_mac *, int, int); static void bwn_rate_init(struct bwn_mac *); static void bwn_set_phytxctl(struct bwn_mac *); static void bwn_spu_setdelay(struct bwn_mac *, int); static void bwn_bt_enable(struct bwn_mac *); static void bwn_set_macaddr(struct bwn_mac *); static void bwn_crypt_init(struct bwn_mac *); static void bwn_chip_exit(struct bwn_mac *); static int bwn_fw_fillinfo(struct bwn_mac *); static int bwn_fw_loaducode(struct bwn_mac *); static int bwn_gpio_init(struct bwn_mac *); static int bwn_fw_loadinitvals(struct bwn_mac *); static int bwn_phy_init(struct bwn_mac *); static void bwn_set_txantenna(struct bwn_mac *, int); static void bwn_set_opmode(struct bwn_mac *); static void bwn_gpio_cleanup(struct bwn_mac *); static void bwn_rate_write(struct bwn_mac *, uint16_t, int); static uint8_t bwn_plcp_getcck(const uint8_t); static uint8_t bwn_plcp_getofdm(const uint8_t); static void bwn_pio_init(struct bwn_mac *); static uint16_t bwn_pio_idx2base(struct bwn_mac *, int); static void bwn_pio_set_txqueue(struct bwn_mac *, struct bwn_pio_txqueue *, int); static void bwn_pio_setupqueue_rx(struct bwn_mac *, struct bwn_pio_rxqueue *, int); static void bwn_destroy_queue_tx(struct bwn_pio_txqueue *); static uint16_t bwn_pio_read_2(struct bwn_mac *, struct bwn_pio_txqueue *, uint16_t); static void bwn_pio_cancel_tx_packets(struct bwn_pio_txqueue *); static int bwn_pio_rx(struct bwn_pio_rxqueue *); static uint8_t bwn_pio_rxeof(struct bwn_pio_rxqueue *); static void bwn_pio_handle_txeof(struct bwn_mac *, const struct bwn_txstatus *); static uint16_t bwn_pio_rx_read_2(struct bwn_pio_rxqueue *, uint16_t); static uint32_t bwn_pio_rx_read_4(struct bwn_pio_rxqueue *, uint16_t); static void bwn_pio_rx_write_2(struct bwn_pio_rxqueue *, uint16_t, uint16_t); static void bwn_pio_rx_write_4(struct bwn_pio_rxqueue *, uint16_t, uint32_t); static int bwn_pio_tx_start(struct bwn_mac *, struct ieee80211_node *, struct mbuf *); static struct bwn_pio_txqueue *bwn_pio_select(struct bwn_mac *, uint8_t); static uint32_t bwn_pio_write_multi_4(struct bwn_mac *, struct bwn_pio_txqueue *, uint32_t, const void *, int); static void bwn_pio_write_4(struct bwn_mac *, struct bwn_pio_txqueue *, uint16_t, uint32_t); static uint16_t bwn_pio_write_multi_2(struct bwn_mac *, struct bwn_pio_txqueue *, uint16_t, const void *, int); static uint16_t bwn_pio_write_mbuf_2(struct bwn_mac *, struct bwn_pio_txqueue *, uint16_t, struct mbuf *); static struct bwn_pio_txqueue *bwn_pio_parse_cookie(struct bwn_mac *, uint16_t, struct bwn_pio_txpkt **); static void bwn_dma_init(struct bwn_mac *); static void bwn_dma_rxdirectfifo(struct bwn_mac *, int, uint8_t); static int bwn_dma_mask2type(uint64_t); static uint64_t bwn_dma_mask(struct bwn_mac *); static uint16_t bwn_dma_base(int, int); static void bwn_dma_ringfree(struct bwn_dma_ring **); static void bwn_dma_32_getdesc(struct bwn_dma_ring *, int, struct bwn_dmadesc_generic **, struct bwn_dmadesc_meta **); static void bwn_dma_32_setdesc(struct bwn_dma_ring *, struct bwn_dmadesc_generic *, bus_addr_t, uint16_t, int, int, int); static void bwn_dma_32_start_transfer(struct bwn_dma_ring *, int); static void bwn_dma_32_suspend(struct bwn_dma_ring *); static void bwn_dma_32_resume(struct bwn_dma_ring *); static int bwn_dma_32_get_curslot(struct bwn_dma_ring *); static void bwn_dma_32_set_curslot(struct bwn_dma_ring *, int); static void bwn_dma_64_getdesc(struct bwn_dma_ring *, int, struct bwn_dmadesc_generic **, struct bwn_dmadesc_meta **); static void bwn_dma_64_setdesc(struct bwn_dma_ring *, struct bwn_dmadesc_generic *, bus_addr_t, uint16_t, int, int, int); static void bwn_dma_64_start_transfer(struct bwn_dma_ring *, int); static void bwn_dma_64_suspend(struct bwn_dma_ring *); static void bwn_dma_64_resume(struct bwn_dma_ring *); static int bwn_dma_64_get_curslot(struct bwn_dma_ring *); static void bwn_dma_64_set_curslot(struct bwn_dma_ring *, int); static int bwn_dma_allocringmemory(struct bwn_dma_ring *); static void bwn_dma_setup(struct bwn_dma_ring *); static void bwn_dma_free_ringmemory(struct bwn_dma_ring *); static void bwn_dma_cleanup(struct bwn_dma_ring *); static void bwn_dma_free_descbufs(struct bwn_dma_ring *); static int bwn_dma_tx_reset(struct bwn_mac *, uint16_t, int); static void bwn_dma_rx(struct bwn_dma_ring *); static int bwn_dma_rx_reset(struct bwn_mac *, uint16_t, int); static void bwn_dma_free_descbuf(struct bwn_dma_ring *, struct bwn_dmadesc_meta *); static void bwn_dma_set_redzone(struct bwn_dma_ring *, struct mbuf *); static int bwn_dma_gettype(struct bwn_mac *); static void bwn_dma_ring_addr(void *, bus_dma_segment_t *, int, int); static int bwn_dma_freeslot(struct bwn_dma_ring *); static int bwn_dma_nextslot(struct bwn_dma_ring *, int); static void bwn_dma_rxeof(struct bwn_dma_ring *, int *); static int bwn_dma_newbuf(struct bwn_dma_ring *, struct bwn_dmadesc_generic *, struct bwn_dmadesc_meta *, int); static void bwn_dma_buf_addr(void *, bus_dma_segment_t *, int, bus_size_t, int); static uint8_t bwn_dma_check_redzone(struct bwn_dma_ring *, struct mbuf *); static void bwn_dma_handle_txeof(struct bwn_mac *, const struct bwn_txstatus *); static int bwn_dma_tx_start(struct bwn_mac *, struct ieee80211_node *, struct mbuf *); static int bwn_dma_getslot(struct bwn_dma_ring *); static struct bwn_dma_ring *bwn_dma_select(struct bwn_mac *, uint8_t); static int bwn_dma_attach(struct bwn_mac *); static struct bwn_dma_ring *bwn_dma_ringsetup(struct bwn_mac *, int, int, int); static struct bwn_dma_ring *bwn_dma_parse_cookie(struct bwn_mac *, const struct bwn_txstatus *, uint16_t, int *); static void bwn_dma_free(struct bwn_mac *); static void bwn_phy_g_init_sub(struct bwn_mac *); static uint8_t bwn_has_hwpctl(struct bwn_mac *); static void bwn_phy_init_b5(struct bwn_mac *); static void bwn_phy_init_b6(struct bwn_mac *); static void bwn_phy_init_a(struct bwn_mac *); static void bwn_loopback_calcgain(struct bwn_mac *); static uint16_t bwn_rf_init_bcm2050(struct bwn_mac *); static void bwn_lo_g_init(struct bwn_mac *); static void bwn_lo_g_adjust(struct bwn_mac *); static void bwn_lo_get_powervector(struct bwn_mac *); static struct bwn_lo_calib *bwn_lo_calibset(struct bwn_mac *, const struct bwn_bbatt *, const struct bwn_rfatt *); static void bwn_lo_write(struct bwn_mac *, struct bwn_loctl *); static void bwn_phy_hwpctl_init(struct bwn_mac *); static void bwn_phy_g_switch_chan(struct bwn_mac *, int, uint8_t); static void bwn_phy_g_set_txpwr_sub(struct bwn_mac *, const struct bwn_bbatt *, const struct bwn_rfatt *, uint8_t); static void bwn_phy_g_set_bbatt(struct bwn_mac *, uint16_t); static uint16_t bwn_rf_2050_rfoverval(struct bwn_mac *, uint16_t, uint32_t); static void bwn_spu_workaround(struct bwn_mac *, uint8_t); static void bwn_wa_init(struct bwn_mac *); static void bwn_ofdmtab_write_2(struct bwn_mac *, uint16_t, uint16_t, uint16_t); static void bwn_dummy_transmission(struct bwn_mac *, int, int); static void bwn_ofdmtab_write_4(struct bwn_mac *, uint16_t, uint16_t, uint32_t); static void bwn_gtab_write(struct bwn_mac *, uint16_t, uint16_t, uint16_t); static void bwn_ram_write(struct bwn_mac *, uint16_t, uint32_t); static void bwn_mac_suspend(struct bwn_mac *); static void bwn_mac_enable(struct bwn_mac *); static void bwn_psctl(struct bwn_mac *, uint32_t); static int16_t bwn_nrssi_read(struct bwn_mac *, uint16_t); static void bwn_nrssi_offset(struct bwn_mac *); static void bwn_nrssi_threshold(struct bwn_mac *); static void bwn_nrssi_slope_11g(struct bwn_mac *); static void bwn_set_all_gains(struct bwn_mac *, int16_t, int16_t, int16_t); static void bwn_set_original_gains(struct bwn_mac *); static void bwn_hwpctl_early_init(struct bwn_mac *); static void bwn_hwpctl_init_gphy(struct bwn_mac *); static uint16_t bwn_phy_g_chan2freq(uint8_t); static int bwn_fw_gets(struct bwn_mac *, enum bwn_fwtype); static int bwn_fw_get(struct bwn_mac *, enum bwn_fwtype, const char *, struct bwn_fwfile *); static void bwn_release_firmware(struct bwn_mac *); static void bwn_do_release_fw(struct bwn_fwfile *); static uint16_t bwn_fwcaps_read(struct bwn_mac *); static int bwn_fwinitvals_write(struct bwn_mac *, const struct bwn_fwinitvals *, size_t, size_t); static int bwn_switch_channel(struct bwn_mac *, int); static uint16_t bwn_ant2phy(int); static void bwn_mac_write_bssid(struct bwn_mac *); static void bwn_mac_setfilter(struct bwn_mac *, uint16_t, const uint8_t *); static void bwn_key_dowrite(struct bwn_mac *, uint8_t, uint8_t, const uint8_t *, size_t, const uint8_t *); static void bwn_key_macwrite(struct bwn_mac *, uint8_t, const uint8_t *); static void bwn_key_write(struct bwn_mac *, uint8_t, uint8_t, const uint8_t *); static void bwn_phy_exit(struct bwn_mac *); static void bwn_core_stop(struct bwn_mac *); static int bwn_switch_band(struct bwn_softc *, struct ieee80211_channel *); static void bwn_phy_reset(struct bwn_mac *); static int bwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void bwn_set_pretbtt(struct bwn_mac *); static int bwn_intr(void *); static void bwn_intrtask(void *, int); static void bwn_restart(struct bwn_mac *, const char *); static void bwn_intr_ucode_debug(struct bwn_mac *); static void bwn_intr_tbtt_indication(struct bwn_mac *); static void bwn_intr_atim_end(struct bwn_mac *); static void bwn_intr_beacon(struct bwn_mac *); static void bwn_intr_pmq(struct bwn_mac *); static void bwn_intr_noise(struct bwn_mac *); static void bwn_intr_txeof(struct bwn_mac *); static void bwn_hwreset(void *, int); static void bwn_handle_fwpanic(struct bwn_mac *); static void bwn_load_beacon0(struct bwn_mac *); static void bwn_load_beacon1(struct bwn_mac *); static uint32_t bwn_jssi_read(struct bwn_mac *); static void bwn_noise_gensample(struct bwn_mac *); static void bwn_handle_txeof(struct bwn_mac *, const struct bwn_txstatus *); static void bwn_rxeof(struct bwn_mac *, struct mbuf *, const void *); static void bwn_phy_txpower_check(struct bwn_mac *, uint32_t); static void bwn_start_locked(struct ifnet *); static int bwn_tx_start(struct bwn_softc *, struct ieee80211_node *, struct mbuf *); static int bwn_tx_isfull(struct bwn_softc *, struct mbuf *); static int bwn_set_txhdr(struct bwn_mac *, struct ieee80211_node *, struct mbuf *, struct bwn_txhdr *, uint16_t); static void bwn_plcp_genhdr(struct bwn_plcp4 *, const uint16_t, const uint8_t); static uint8_t bwn_antenna_sanitize(struct bwn_mac *, uint8_t); static uint8_t bwn_get_fbrate(uint8_t); static int bwn_phy_shm_tssi_read(struct bwn_mac *, uint16_t); static void bwn_phy_g_setatt(struct bwn_mac *, int *, int *); static void bwn_phy_lock(struct bwn_mac *); static void bwn_phy_unlock(struct bwn_mac *); static void bwn_rf_lock(struct bwn_mac *); static void bwn_rf_unlock(struct bwn_mac *); static void bwn_txpwr(void *, int); static void bwn_tasks(void *); static void bwn_task_15s(struct bwn_mac *); static void bwn_task_30s(struct bwn_mac *); static void bwn_task_60s(struct bwn_mac *); static int bwn_plcp_get_ofdmrate(struct bwn_mac *, struct bwn_plcp6 *, uint8_t); static int bwn_plcp_get_cckrate(struct bwn_mac *, struct bwn_plcp6 *); static void bwn_rx_radiotap(struct bwn_mac *, struct mbuf *, const struct bwn_rxhdr4 *, struct bwn_plcp6 *, int, int, int); static void bwn_tsf_read(struct bwn_mac *, uint64_t *); static void bwn_phy_g_dc_lookup_init(struct bwn_mac *, uint8_t); static void bwn_set_slot_time(struct bwn_mac *, uint16_t); static void bwn_watchdog(void *); static void bwn_dma_stop(struct bwn_mac *); static void bwn_pio_stop(struct bwn_mac *); static void bwn_dma_ringstop(struct bwn_dma_ring **); static void bwn_led_attach(struct bwn_mac *); static void bwn_led_newstate(struct bwn_mac *, enum ieee80211_state); static void bwn_led_event(struct bwn_mac *, int); static void bwn_led_blink_start(struct bwn_mac *, int, int); static void bwn_led_blink_next(void *); static void bwn_led_blink_end(void *); static void bwn_rfswitch(void *); static void bwn_rf_turnon(struct bwn_mac *); static void bwn_rf_turnoff(struct bwn_mac *); static void bwn_phy_lp_init_pre(struct bwn_mac *); static int bwn_phy_lp_init(struct bwn_mac *); static uint16_t bwn_phy_lp_read(struct bwn_mac *, uint16_t); static void bwn_phy_lp_write(struct bwn_mac *, uint16_t, uint16_t); static void bwn_phy_lp_maskset(struct bwn_mac *, uint16_t, uint16_t, uint16_t); static uint16_t bwn_phy_lp_rf_read(struct bwn_mac *, uint16_t); static void bwn_phy_lp_rf_write(struct bwn_mac *, uint16_t, uint16_t); static void bwn_phy_lp_rf_onoff(struct bwn_mac *, int); static int bwn_phy_lp_switch_channel(struct bwn_mac *, uint32_t); static uint32_t bwn_phy_lp_get_default_chan(struct bwn_mac *); static void bwn_phy_lp_set_antenna(struct bwn_mac *, int); static void bwn_phy_lp_task_60s(struct bwn_mac *); static void bwn_phy_lp_readsprom(struct bwn_mac *); static void bwn_phy_lp_bbinit(struct bwn_mac *); static void bwn_phy_lp_txpctl_init(struct bwn_mac *); static void bwn_phy_lp_calib(struct bwn_mac *); static void bwn_phy_lp_switch_analog(struct bwn_mac *, int); static int bwn_phy_lp_b2062_switch_channel(struct bwn_mac *, uint8_t); static int bwn_phy_lp_b2063_switch_channel(struct bwn_mac *, uint8_t); static void bwn_phy_lp_set_anafilter(struct bwn_mac *, uint8_t); static void bwn_phy_lp_set_gaintbl(struct bwn_mac *, uint32_t); static void bwn_phy_lp_digflt_save(struct bwn_mac *); static void bwn_phy_lp_get_txpctlmode(struct bwn_mac *); static void bwn_phy_lp_set_txpctlmode(struct bwn_mac *, uint8_t); static void bwn_phy_lp_bugfix(struct bwn_mac *); static void bwn_phy_lp_digflt_restore(struct bwn_mac *); static void bwn_phy_lp_tblinit(struct bwn_mac *); static void bwn_phy_lp_bbinit_r2(struct bwn_mac *); static void bwn_phy_lp_bbinit_r01(struct bwn_mac *); static void bwn_phy_lp_b2062_init(struct bwn_mac *); static void bwn_phy_lp_b2063_init(struct bwn_mac *); static void bwn_phy_lp_rxcal_r2(struct bwn_mac *); static void bwn_phy_lp_rccal_r12(struct bwn_mac *); static void bwn_phy_lp_set_rccap(struct bwn_mac *); static uint32_t bwn_phy_lp_roundup(uint32_t, uint32_t, uint8_t); static void bwn_phy_lp_b2062_reset_pllbias(struct bwn_mac *); static void bwn_phy_lp_b2062_vco_calib(struct bwn_mac *); static void bwn_tab_write_multi(struct bwn_mac *, uint32_t, int, const void *); static void bwn_tab_read_multi(struct bwn_mac *, uint32_t, int, void *); static struct bwn_txgain bwn_phy_lp_get_txgain(struct bwn_mac *); static uint8_t bwn_phy_lp_get_bbmult(struct bwn_mac *); static void bwn_phy_lp_set_txgain(struct bwn_mac *, struct bwn_txgain *); static void bwn_phy_lp_set_bbmult(struct bwn_mac *, uint8_t); static void bwn_phy_lp_set_trsw_over(struct bwn_mac *, uint8_t, uint8_t); static void bwn_phy_lp_set_rxgain(struct bwn_mac *, uint32_t); static void bwn_phy_lp_set_deaf(struct bwn_mac *, uint8_t); static int bwn_phy_lp_calc_rx_iq_comp(struct bwn_mac *, uint16_t); static void bwn_phy_lp_clear_deaf(struct bwn_mac *, uint8_t); static void bwn_phy_lp_tblinit_r01(struct bwn_mac *); static void bwn_phy_lp_tblinit_r2(struct bwn_mac *); static void bwn_phy_lp_tblinit_txgain(struct bwn_mac *); static void bwn_tab_write(struct bwn_mac *, uint32_t, uint32_t); static void bwn_phy_lp_b2062_tblinit(struct bwn_mac *); static void bwn_phy_lp_b2063_tblinit(struct bwn_mac *); static int bwn_phy_lp_loopback(struct bwn_mac *); static void bwn_phy_lp_set_rxgain_idx(struct bwn_mac *, uint16_t); static void bwn_phy_lp_ddfs_turnon(struct bwn_mac *, int, int, int, int, int); static uint8_t bwn_phy_lp_rx_iq_est(struct bwn_mac *, uint16_t, uint8_t, struct bwn_phy_lp_iq_est *); static void bwn_phy_lp_ddfs_turnoff(struct bwn_mac *); static uint32_t bwn_tab_read(struct bwn_mac *, uint32_t); static void bwn_phy_lp_set_txgain_dac(struct bwn_mac *, uint16_t); static void bwn_phy_lp_set_txgain_pa(struct bwn_mac *, uint16_t); static void bwn_phy_lp_set_txgain_override(struct bwn_mac *); static uint16_t bwn_phy_lp_get_pa_gain(struct bwn_mac *); static uint8_t bwn_nbits(int32_t); static void bwn_phy_lp_gaintbl_write_multi(struct bwn_mac *, int, int, struct bwn_txgain_entry *); static void bwn_phy_lp_gaintbl_write(struct bwn_mac *, int, struct bwn_txgain_entry); static void bwn_phy_lp_gaintbl_write_r2(struct bwn_mac *, int, struct bwn_txgain_entry); static void bwn_phy_lp_gaintbl_write_r01(struct bwn_mac *, int, struct bwn_txgain_entry); +static void bwn_sysctl_node(struct bwn_softc *); static struct resource_spec bwn_res_spec_legacy[] = { { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0, 0 } }; static struct resource_spec bwn_res_spec_msi[] = { { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0, 0 } }; static const struct bwn_channelinfo bwn_chantable_bg = { .channels = { { 2412, 1, 30 }, { 2417, 2, 30 }, { 2422, 3, 30 }, { 2427, 4, 30 }, { 2432, 5, 30 }, { 2437, 6, 30 }, { 2442, 7, 30 }, { 2447, 8, 30 }, { 2452, 9, 30 }, { 2457, 10, 30 }, { 2462, 11, 30 }, { 2467, 12, 30 }, { 2472, 13, 30 }, { 2484, 14, 30 } }, .nchannels = 14 }; static const struct bwn_channelinfo bwn_chantable_a = { .channels = { { 5170, 34, 30 }, { 5180, 36, 30 }, { 5190, 38, 30 }, { 5200, 40, 30 }, { 5210, 42, 30 }, { 5220, 44, 30 }, { 5230, 46, 30 }, { 5240, 48, 30 }, { 5260, 52, 30 }, { 5280, 56, 30 }, { 5300, 60, 30 }, { 5320, 64, 30 }, { 5500, 100, 30 }, { 5520, 104, 30 }, { 5540, 108, 30 }, { 5560, 112, 30 }, { 5580, 116, 30 }, { 5600, 120, 30 }, { 5620, 124, 30 }, { 5640, 128, 30 }, { 5660, 132, 30 }, { 5680, 136, 30 }, { 5700, 140, 30 }, { 5745, 149, 30 }, { 5765, 153, 30 }, { 5785, 157, 30 }, { 5805, 161, 30 }, { 5825, 165, 30 }, { 5920, 184, 30 }, { 5940, 188, 30 }, { 5960, 192, 30 }, { 5980, 196, 30 }, { 6000, 200, 30 }, { 6020, 204, 30 }, { 6040, 208, 30 }, { 6060, 212, 30 }, { 6080, 216, 30 } }, .nchannels = 37 }; static const struct bwn_channelinfo bwn_chantable_n = { .channels = { { 5160, 32, 30 }, { 5170, 34, 30 }, { 5180, 36, 30 }, { 5190, 38, 30 }, { 5200, 40, 30 }, { 5210, 42, 30 }, { 5220, 44, 30 }, { 5230, 46, 30 }, { 5240, 48, 30 }, { 5250, 50, 30 }, { 5260, 52, 30 }, { 5270, 54, 30 }, { 5280, 56, 30 }, { 5290, 58, 30 }, { 5300, 60, 30 }, { 5310, 62, 30 }, { 5320, 64, 30 }, { 5330, 66, 30 }, { 5340, 68, 30 }, { 5350, 70, 30 }, { 5360, 72, 30 }, { 5370, 74, 30 }, { 5380, 76, 30 }, { 5390, 78, 30 }, { 5400, 80, 30 }, { 5410, 82, 30 }, { 5420, 84, 30 }, { 5430, 86, 30 }, { 5440, 88, 30 }, { 5450, 90, 30 }, { 5460, 92, 30 }, { 5470, 94, 30 }, { 5480, 96, 30 }, { 5490, 98, 30 }, { 5500, 100, 30 }, { 5510, 102, 30 }, { 5520, 104, 30 }, { 5530, 106, 30 }, { 5540, 108, 30 }, { 5550, 110, 30 }, { 5560, 112, 30 }, { 5570, 114, 30 }, { 5580, 116, 30 }, { 5590, 118, 30 }, { 5600, 120, 30 }, { 5610, 122, 30 }, { 5620, 124, 30 }, { 5630, 126, 30 }, { 5640, 128, 30 }, { 5650, 130, 30 }, { 5660, 132, 30 }, { 5670, 134, 30 }, { 5680, 136, 30 }, { 5690, 138, 30 }, { 5700, 140, 30 }, { 5710, 142, 30 }, { 5720, 144, 30 }, { 5725, 145, 30 }, { 5730, 146, 30 }, { 5735, 147, 30 }, { 5740, 148, 30 }, { 5745, 149, 30 }, { 5750, 150, 30 }, { 5755, 151, 30 }, { 5760, 152, 30 }, { 5765, 153, 30 }, { 5770, 154, 30 }, { 5775, 155, 30 }, { 5780, 156, 30 }, { 5785, 157, 30 }, { 5790, 158, 30 }, { 5795, 159, 30 }, { 5800, 160, 30 }, { 5805, 161, 30 }, { 5810, 162, 30 }, { 5815, 163, 30 }, { 5820, 164, 30 }, { 5825, 165, 30 }, { 5830, 166, 30 }, { 5840, 168, 30 }, { 5850, 170, 30 }, { 5860, 172, 30 }, { 5870, 174, 30 }, { 5880, 176, 30 }, { 5890, 178, 30 }, { 5900, 180, 30 }, { 5910, 182, 30 }, { 5920, 184, 30 }, { 5930, 186, 30 }, { 5940, 188, 30 }, { 5950, 190, 30 }, { 5960, 192, 30 }, { 5970, 194, 30 }, { 5980, 196, 30 }, { 5990, 198, 30 }, { 6000, 200, 30 }, { 6010, 202, 30 }, { 6020, 204, 30 }, { 6030, 206, 30 }, { 6040, 208, 30 }, { 6050, 210, 30 }, { 6060, 212, 30 }, { 6070, 214, 30 }, { 6080, 216, 30 }, { 6090, 218, 30 }, { 6100, 220, 30 }, { 6110, 222, 30 }, { 6120, 224, 30 }, { 6130, 226, 30 }, { 6140, 228, 30 } }, .nchannels = 110 }; static const uint8_t bwn_b2063_chantable_data[33][12] = { { 0x6f, 0x3c, 0x3c, 0x4, 0x5, 0x5, 0x5, 0x5, 0x77, 0x80, 0x80, 0x70 }, { 0x6f, 0x2c, 0x2c, 0x4, 0x5, 0x5, 0x5, 0x5, 0x77, 0x80, 0x80, 0x70 }, { 0x6f, 0x1c, 0x1c, 0x4, 0x5, 0x5, 0x5, 0x5, 0x77, 0x80, 0x80, 0x70 }, { 0x6e, 0x1c, 0x1c, 0x4, 0x5, 0x5, 0x5, 0x5, 0x77, 0x80, 0x80, 0x70 }, { 0x6e, 0xc, 0xc, 0x4, 0x5, 0x5, 0x5, 0x5, 0x77, 0x80, 0x80, 0x70 }, { 0x6a, 0xc, 0xc, 0, 0x2, 0x5, 0xd, 0xd, 0x77, 0x80, 0x20, 0 }, { 0x6a, 0xc, 0xc, 0, 0x1, 0x5, 0xd, 0xc, 0x77, 0x80, 0x20, 0 }, { 0x6a, 0xc, 0xc, 0, 0x1, 0x4, 0xc, 0xc, 0x77, 0x80, 0x20, 0 }, { 0x69, 0xc, 0xc, 0, 0x1, 0x4, 0xc, 0xc, 0x77, 0x70, 0x20, 0 }, { 0x69, 0xc, 0xc, 0, 0x1, 0x4, 0xb, 0xc, 0x77, 0x70, 0x20, 0 }, { 0x69, 0xc, 0xc, 0, 0, 0x4, 0xb, 0xb, 0x77, 0x60, 0x20, 0 }, { 0x69, 0xc, 0xc, 0, 0, 0x3, 0xa, 0xb, 0x77, 0x60, 0x20, 0 }, { 0x69, 0xc, 0xc, 0, 0, 0x3, 0xa, 0xa, 0x77, 0x60, 0x20, 0 }, { 0x68, 0xc, 0xc, 0, 0, 0x2, 0x9, 0x9, 0x77, 0x60, 0x20, 0 }, { 0x68, 0xc, 0xc, 0, 0, 0x1, 0x8, 0x8, 0x77, 0x50, 0x10, 0 }, { 0x67, 0xc, 0xc, 0, 0, 0, 0x8, 0x8, 0x77, 0x50, 0x10, 0 }, { 0x64, 0xc, 0xc, 0, 0, 0, 0x2, 0x1, 0x77, 0x20, 0, 0 }, { 0x64, 0xc, 0xc, 0, 0, 0, 0x1, 0x1, 0x77, 0x20, 0, 0 }, { 0x63, 0xc, 0xc, 0, 0, 0, 0x1, 0, 0x77, 0x10, 0, 0 }, { 0x63, 0xc, 0xc, 0, 0, 0, 0, 0, 0x77, 0x10, 0, 0 }, { 0x62, 0xc, 0xc, 0, 0, 0, 0, 0, 0x77, 0x10, 0, 0 }, { 0x62, 0xc, 0xc, 0, 0, 0, 0, 0, 0x77, 0, 0, 0 }, { 0x61, 0xc, 0xc, 0, 0, 0, 0, 0, 0x77, 0, 0, 0 }, { 0x60, 0xc, 0xc, 0, 0, 0, 0, 0, 0x77, 0, 0, 0 }, { 0x6e, 0xc, 0xc, 0, 0x9, 0xe, 0xf, 0xf, 0x77, 0xc0, 0x50, 0 }, { 0x6e, 0xc, 0xc, 0, 0x9, 0xd, 0xf, 0xf, 0x77, 0xb0, 0x50, 0 }, { 0x6e, 0xc, 0xc, 0, 0x8, 0xc, 0xf, 0xf, 0x77, 0xb0, 0x50, 0 }, { 0x6d, 0xc, 0xc, 0, 0x8, 0xc, 0xf, 0xf, 0x77, 0xa0, 0x40, 0 }, { 0x6d, 0xc, 0xc, 0, 0x8, 0xb, 0xf, 0xf, 0x77, 0xa0, 0x40, 0 }, { 0x6d, 0xc, 0xc, 0, 0x8, 0xa, 0xf, 0xf, 0x77, 0xa0, 0x40, 0 }, { 0x6c, 0xc, 0xc, 0, 0x7, 0x9, 0xf, 0xf, 0x77, 0x90, 0x40, 0 }, { 0x6c, 0xc, 0xc, 0, 0x6, 0x8, 0xf, 0xf, 0x77, 0x90, 0x40, 0 }, { 0x6c, 0xc, 0xc, 0, 0x5, 0x8, 0xf, 0xf, 0x77, 0x90, 0x40, 0 } }; static const struct bwn_b206x_chan bwn_b2063_chantable[] = { { 1, 2412, bwn_b2063_chantable_data[0] }, { 2, 2417, bwn_b2063_chantable_data[0] }, { 3, 2422, bwn_b2063_chantable_data[0] }, { 4, 2427, bwn_b2063_chantable_data[1] }, { 5, 2432, bwn_b2063_chantable_data[1] }, { 6, 2437, bwn_b2063_chantable_data[1] }, { 7, 2442, bwn_b2063_chantable_data[1] }, { 8, 2447, bwn_b2063_chantable_data[1] }, { 9, 2452, bwn_b2063_chantable_data[2] }, { 10, 2457, bwn_b2063_chantable_data[2] }, { 11, 2462, bwn_b2063_chantable_data[3] }, { 12, 2467, bwn_b2063_chantable_data[3] }, { 13, 2472, bwn_b2063_chantable_data[3] }, { 14, 2484, bwn_b2063_chantable_data[4] }, { 34, 5170, bwn_b2063_chantable_data[5] }, { 36, 5180, bwn_b2063_chantable_data[6] }, { 38, 5190, bwn_b2063_chantable_data[7] }, { 40, 5200, bwn_b2063_chantable_data[8] }, { 42, 5210, bwn_b2063_chantable_data[9] }, { 44, 5220, bwn_b2063_chantable_data[10] }, { 46, 5230, bwn_b2063_chantable_data[11] }, { 48, 5240, bwn_b2063_chantable_data[12] }, { 52, 5260, bwn_b2063_chantable_data[13] }, { 56, 5280, bwn_b2063_chantable_data[14] }, { 60, 5300, bwn_b2063_chantable_data[14] }, { 64, 5320, bwn_b2063_chantable_data[15] }, { 100, 5500, bwn_b2063_chantable_data[16] }, { 104, 5520, bwn_b2063_chantable_data[17] }, { 108, 5540, bwn_b2063_chantable_data[18] }, { 112, 5560, bwn_b2063_chantable_data[19] }, { 116, 5580, bwn_b2063_chantable_data[20] }, { 120, 5600, bwn_b2063_chantable_data[21] }, { 124, 5620, bwn_b2063_chantable_data[21] }, { 128, 5640, bwn_b2063_chantable_data[22] }, { 132, 5660, bwn_b2063_chantable_data[22] }, { 136, 5680, bwn_b2063_chantable_data[22] }, { 140, 5700, bwn_b2063_chantable_data[23] }, { 149, 5745, bwn_b2063_chantable_data[23] }, { 153, 5765, bwn_b2063_chantable_data[23] }, { 157, 5785, bwn_b2063_chantable_data[23] }, { 161, 5805, bwn_b2063_chantable_data[23] }, { 165, 5825, bwn_b2063_chantable_data[23] }, { 184, 4920, bwn_b2063_chantable_data[24] }, { 188, 4940, bwn_b2063_chantable_data[25] }, { 192, 4960, bwn_b2063_chantable_data[26] }, { 196, 4980, bwn_b2063_chantable_data[27] }, { 200, 5000, bwn_b2063_chantable_data[28] }, { 204, 5020, bwn_b2063_chantable_data[29] }, { 208, 5040, bwn_b2063_chantable_data[30] }, { 212, 5060, bwn_b2063_chantable_data[31] }, { 216, 5080, bwn_b2063_chantable_data[32] } }; static const uint8_t bwn_b2062_chantable_data[22][12] = { { 0xff, 0xff, 0xb5, 0x1b, 0x24, 0x32, 0x32, 0x88, 0x88, 0, 0, 0 }, { 0, 0x22, 0x20, 0x84, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0x11, 0x10, 0x83, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0x83, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0x11, 0x20, 0x83, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0x11, 0x10, 0x84, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0x11, 0, 0x83, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0x63, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0x62, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0x30, 0x3c, 0x77, 0x37, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0x20, 0x3c, 0x77, 0x37, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0x10, 0x3c, 0x77, 0x37, 0xff, 0x88, 0, 0, 0 }, { 0, 0, 0, 0, 0x3c, 0x77, 0x37, 0xff, 0x88, 0, 0, 0 }, { 0x55, 0x77, 0x90, 0xf7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x44, 0x77, 0x80, 0xe7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x44, 0x66, 0x80, 0xe7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x33, 0x66, 0x70, 0xc7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x22, 0x55, 0x60, 0xd7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x22, 0x55, 0x60, 0xc7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x22, 0x44, 0x50, 0xc7, 0x3c, 0x77, 0x35, 0xff, 0xff, 0, 0, 0 }, { 0x11, 0x44, 0x50, 0xa5, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 }, { 0, 0x44, 0x40, 0xb6, 0x3c, 0x77, 0x35, 0xff, 0x88, 0, 0, 0 } }; static const struct bwn_b206x_chan bwn_b2062_chantable[] = { { 1, 2412, bwn_b2062_chantable_data[0] }, { 2, 2417, bwn_b2062_chantable_data[0] }, { 3, 2422, bwn_b2062_chantable_data[0] }, { 4, 2427, bwn_b2062_chantable_data[0] }, { 5, 2432, bwn_b2062_chantable_data[0] }, { 6, 2437, bwn_b2062_chantable_data[0] }, { 7, 2442, bwn_b2062_chantable_data[0] }, { 8, 2447, bwn_b2062_chantable_data[0] }, { 9, 2452, bwn_b2062_chantable_data[0] }, { 10, 2457, bwn_b2062_chantable_data[0] }, { 11, 2462, bwn_b2062_chantable_data[0] }, { 12, 2467, bwn_b2062_chantable_data[0] }, { 13, 2472, bwn_b2062_chantable_data[0] }, { 14, 2484, bwn_b2062_chantable_data[0] }, { 34, 5170, bwn_b2062_chantable_data[1] }, { 38, 5190, bwn_b2062_chantable_data[2] }, { 42, 5210, bwn_b2062_chantable_data[2] }, { 46, 5230, bwn_b2062_chantable_data[3] }, { 36, 5180, bwn_b2062_chantable_data[4] }, { 40, 5200, bwn_b2062_chantable_data[5] }, { 44, 5220, bwn_b2062_chantable_data[6] }, { 48, 5240, bwn_b2062_chantable_data[3] }, { 52, 5260, bwn_b2062_chantable_data[3] }, { 56, 5280, bwn_b2062_chantable_data[3] }, { 60, 5300, bwn_b2062_chantable_data[7] }, { 64, 5320, bwn_b2062_chantable_data[8] }, { 100, 5500, bwn_b2062_chantable_data[9] }, { 104, 5520, bwn_b2062_chantable_data[10] }, { 108, 5540, bwn_b2062_chantable_data[10] }, { 112, 5560, bwn_b2062_chantable_data[10] }, { 116, 5580, bwn_b2062_chantable_data[11] }, { 120, 5600, bwn_b2062_chantable_data[12] }, { 124, 5620, bwn_b2062_chantable_data[12] }, { 128, 5640, bwn_b2062_chantable_data[12] }, { 132, 5660, bwn_b2062_chantable_data[12] }, { 136, 5680, bwn_b2062_chantable_data[12] }, { 140, 5700, bwn_b2062_chantable_data[12] }, { 149, 5745, bwn_b2062_chantable_data[12] }, { 153, 5765, bwn_b2062_chantable_data[12] }, { 157, 5785, bwn_b2062_chantable_data[12] }, { 161, 5805, bwn_b2062_chantable_data[12] }, { 165, 5825, bwn_b2062_chantable_data[12] }, { 184, 4920, bwn_b2062_chantable_data[13] }, { 188, 4940, bwn_b2062_chantable_data[14] }, { 192, 4960, bwn_b2062_chantable_data[15] }, { 196, 4980, bwn_b2062_chantable_data[16] }, { 200, 5000, bwn_b2062_chantable_data[17] }, { 204, 5020, bwn_b2062_chantable_data[18] }, { 208, 5040, bwn_b2062_chantable_data[19] }, { 212, 5060, bwn_b2062_chantable_data[20] }, { 216, 5080, bwn_b2062_chantable_data[21] } }; /* for LP PHY */ static const struct bwn_rxcompco bwn_rxcompco_5354[] = { { 1, -66, 15 }, { 2, -66, 15 }, { 3, -66, 15 }, { 4, -66, 15 }, { 5, -66, 15 }, { 6, -66, 15 }, { 7, -66, 14 }, { 8, -66, 14 }, { 9, -66, 14 }, { 10, -66, 14 }, { 11, -66, 14 }, { 12, -66, 13 }, { 13, -66, 13 }, { 14, -66, 13 }, }; /* for LP PHY */ static const struct bwn_rxcompco bwn_rxcompco_r12[] = { { 1, -64, 13 }, { 2, -64, 13 }, { 3, -64, 13 }, { 4, -64, 13 }, { 5, -64, 12 }, { 6, -64, 12 }, { 7, -64, 12 }, { 8, -64, 12 }, { 9, -64, 12 }, { 10, -64, 11 }, { 11, -64, 11 }, { 12, -64, 11 }, { 13, -64, 11 }, { 14, -64, 10 }, { 34, -62, 24 }, { 38, -62, 24 }, { 42, -62, 24 }, { 46, -62, 23 }, { 36, -62, 24 }, { 40, -62, 24 }, { 44, -62, 23 }, { 48, -62, 23 }, { 52, -62, 23 }, { 56, -62, 22 }, { 60, -62, 22 }, { 64, -62, 22 }, { 100, -62, 16 }, { 104, -62, 16 }, { 108, -62, 15 }, { 112, -62, 14 }, { 116, -62, 14 }, { 120, -62, 13 }, { 124, -62, 12 }, { 128, -62, 12 }, { 132, -62, 12 }, { 136, -62, 11 }, { 140, -62, 10 }, { 149, -61, 9 }, { 153, -61, 9 }, { 157, -61, 9 }, { 161, -61, 8 }, { 165, -61, 8 }, { 184, -62, 25 }, { 188, -62, 25 }, { 192, -62, 25 }, { 196, -62, 25 }, { 200, -62, 25 }, { 204, -62, 25 }, { 208, -62, 25 }, { 212, -62, 25 }, { 216, -62, 26 }, }; static const struct bwn_rxcompco bwn_rxcompco_r2 = { 0, -64, 0 }; static const uint8_t bwn_tab_sigsq_tbl[] = { 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, 0xca, 0xc7, 0xc4, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0x00, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, 0xcf, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, }; static const uint8_t bwn_tab_pllfrac_tbl[] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; static const uint16_t bwn_tabl_iqlocal_tbl[] = { 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const uint16_t bwn_tab_noise_g1[] = BWN_TAB_NOISE_G1; static const uint16_t bwn_tab_noise_g2[] = BWN_TAB_NOISE_G2; static const uint16_t bwn_tab_noisescale_g1[] = BWN_TAB_NOISESCALE_G1; static const uint16_t bwn_tab_noisescale_g2[] = BWN_TAB_NOISESCALE_G2; static const uint16_t bwn_tab_noisescale_g3[] = BWN_TAB_NOISESCALE_G3; const uint8_t bwn_bitrev_table[256] = BWN_BITREV_TABLE; #define VENDOR_LED_ACT(vendor) \ { \ .vid = PCI_VENDOR_##vendor, \ .led_act = { BWN_VENDOR_LED_ACT_##vendor } \ } static const struct { uint16_t vid; uint8_t led_act[BWN_LED_MAX]; } bwn_vendor_led_act[] = { VENDOR_LED_ACT(COMPAQ), VENDOR_LED_ACT(ASUSTEK) }; static const uint8_t bwn_default_led_act[BWN_LED_MAX] = { BWN_VENDOR_LED_ACT_DEFAULT }; #undef VENDOR_LED_ACT static const struct { int on_dur; int off_dur; } bwn_led_duration[109] = { [0] = { 400, 100 }, [2] = { 150, 75 }, [4] = { 90, 45 }, [11] = { 66, 34 }, [12] = { 53, 26 }, [18] = { 42, 21 }, [22] = { 35, 17 }, [24] = { 32, 16 }, [36] = { 21, 10 }, [48] = { 16, 8 }, [72] = { 11, 5 }, [96] = { 9, 4 }, [108] = { 7, 3 } }; static const uint16_t bwn_wme_shm_offsets[] = { [0] = BWN_WME_BESTEFFORT, [1] = BWN_WME_BACKGROUND, [2] = BWN_WME_VOICE, [3] = BWN_WME_VIDEO, }; static const struct siba_devid bwn_devs[] = { SIBA_DEV(BROADCOM, 80211, 5, "Revision 5"), SIBA_DEV(BROADCOM, 80211, 6, "Revision 6"), SIBA_DEV(BROADCOM, 80211, 7, "Revision 7"), SIBA_DEV(BROADCOM, 80211, 9, "Revision 9"), SIBA_DEV(BROADCOM, 80211, 10, "Revision 10"), SIBA_DEV(BROADCOM, 80211, 11, "Revision 11"), SIBA_DEV(BROADCOM, 80211, 13, "Revision 13"), SIBA_DEV(BROADCOM, 80211, 15, "Revision 15"), SIBA_DEV(BROADCOM, 80211, 16, "Revision 16") }; static int bwn_probe(device_t dev) { struct siba_dev_softc *sd = device_get_ivars(dev); int i; for (i = 0; i < sizeof(bwn_devs) / sizeof(bwn_devs[0]); i++) { if (sd->sd_id.sd_vendor == bwn_devs[i].sd_vendor && sd->sd_id.sd_device == bwn_devs[i].sd_device && sd->sd_id.sd_rev == bwn_devs[i].sd_rev) return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int bwn_attach(device_t dev) { struct bwn_mac *mac; struct bwn_softc *sc = device_get_softc(dev); struct siba_dev_softc *sd = device_get_ivars(dev); struct siba_softc *siba = sd->sd_bus; int error, i, msic, reg; sc->sc_dev = dev; sc->sc_sd = sd; #ifdef BWN_DEBUG sc->sc_debug = bwn_debug; #endif if ((sc->sc_flags & BWN_FLAG_ATTACHED) == 0) { error = bwn_attach_pre(sc); if (error != 0) return (error); bwn_sprom_bugfixes(sd->sd_bus); sc->sc_flags |= BWN_FLAG_ATTACHED; } if (!TAILQ_EMPTY(&sc->sc_maclist)) { if (siba->siba_pci_did != 0x4313 && siba->siba_pci_did != 0x431a && siba->siba_pci_did != 0x4321) { device_printf(sc->sc_dev, "skip 802.11 cores\n"); return (ENODEV); } } mac = (struct bwn_mac *)malloc(sizeof(*mac), M_DEVBUF, M_NOWAIT | M_ZERO); if (mac == NULL) return (ENOMEM); mac->mac_sc = sc; mac->mac_sd = sd; mac->mac_status = BWN_MAC_STATUS_UNINIT; if (bwn_bfp != 0) mac->mac_flags |= BWN_MAC_FLAG_BADFRAME_PREEMP; TASK_INIT(&mac->mac_hwreset, 0, bwn_hwreset, mac); TASK_INIT(&mac->mac_intrtask, 0, bwn_intrtask, mac); TASK_INIT(&mac->mac_txpower, 0, bwn_txpwr, mac); error = bwn_attach_core(mac); if (error) goto fail0; bwn_led_attach(mac); device_printf(sc->sc_dev, "WLAN (chipid %#x rev %u) " "PHY (analog %d type %d rev %d) RADIO (manuf %#x ver %#x rev %d)\n", sd->sd_bus->siba_chipid, sd->sd_id.sd_rev, mac->mac_phy.analog, mac->mac_phy.type, mac->mac_phy.rev, mac->mac_phy.rf_manuf, mac->mac_phy.rf_ver, mac->mac_phy.rf_rev); if (mac->mac_flags & BWN_MAC_FLAG_DMA) device_printf(sc->sc_dev, "DMA (%d bits)\n", mac->mac_method.dma.dmatype); else device_printf(sc->sc_dev, "PIO\n"); /* * setup PCI resources and interrupt. */ if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { msic = pci_msi_count(dev); if (bootverbose) device_printf(sc->sc_dev, "MSI count : %d\n", msic); } else msic = 0; mac->mac_intr_spec = bwn_res_spec_legacy; if (msic == BWN_MSI_MESSAGES && bwn_msi_disable == 0) { if (pci_alloc_msi(dev, &msic) == 0) { device_printf(sc->sc_dev, "Using %d MSI messages\n", msic); mac->mac_intr_spec = bwn_res_spec_msi; mac->mac_msi = 1; } } error = bus_alloc_resources(dev, mac->mac_intr_spec, mac->mac_res_irq); if (error) { device_printf(sc->sc_dev, "couldn't allocate IRQ resources (%d)\n", error); goto fail1; } if (mac->mac_msi == 0) error = bus_setup_intr(dev, mac->mac_res_irq[0], INTR_TYPE_NET | INTR_MPSAFE, bwn_intr, NULL, mac, &mac->mac_intrhand[0]); else { for (i = 0; i < BWN_MSI_MESSAGES; i++) { error = bus_setup_intr(dev, mac->mac_res_irq[i], INTR_TYPE_NET | INTR_MPSAFE, bwn_intr, NULL, mac, &mac->mac_intrhand[i]); if (error != 0) { device_printf(sc->sc_dev, "couldn't setup interrupt (%d)\n", error); break; } } } TAILQ_INSERT_TAIL(&sc->sc_maclist, mac, mac_list); /* * calls attach-post routine */ if ((sc->sc_flags & BWN_FLAG_ATTACHED) != 0) bwn_attach_post(sc); return (0); fail1: if (msic == BWN_MSI_MESSAGES && bwn_msi_disable == 0) pci_release_msi(dev); fail0: free(mac, M_DEVBUF); return (error); } static int bwn_is_valid_ether_addr(uint8_t *addr) { char zero_addr[6] = { 0, 0, 0, 0, 0, 0 }; if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) return (FALSE); return (TRUE); } static int bwn_attach_post(struct bwn_softc *sc) { struct ieee80211com *ic; struct ifnet *ifp = sc->sc_ifp; struct siba_dev_softc *sd = sc->sc_sd; struct siba_sprom *sprom = &sd->sd_bus->siba_sprom; -#ifdef BWN_DEBUG - device_t dev = sc->sc_dev; -#endif ic = ifp->if_l2com; ic->ic_ifp = ifp; /* XXX not right but it's not used anywhere important */ ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WME /* WME/WMM supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_TXPMGT /* capable of txpow mgt */ ; /* call MI attach routine. */ ieee80211_ifattach(ic, bwn_is_valid_ether_addr(sprom->mac_80211a) ? sprom->mac_80211a : sprom->mac_80211bg); ic->ic_headroom = sizeof(struct bwn_txhdr); /* override default methods */ ic->ic_raw_xmit = bwn_raw_xmit; ic->ic_newassoc = bwn_newassoc; ic->ic_updateslot = bwn_updateslot; ic->ic_update_promisc = bwn_update_promisc; ic->ic_wme.wme_update = bwn_wme_update; ic->ic_node_alloc = bwn_node_alloc; sc->sc_node_cleanup = ic->ic_node_cleanup; ic->ic_node_cleanup = bwn_node_cleanup; ic->ic_scan_start = bwn_scan_start; ic->ic_scan_end = bwn_scan_end; ic->ic_set_channel = bwn_set_channel; ic->ic_vap_create = bwn_vap_create; ic->ic_vap_delete = bwn_vap_delete; ieee80211_radiotap_attach(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), BWN_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), BWN_RX_RADIOTAP_PRESENT); -#ifdef BWN_DEBUG - SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, - "debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags"); -#endif + bwn_sysctl_node(sc); if (bootverbose) ieee80211_announce(ic); return (0); } static void bwn_phy_detach(struct bwn_mac *mac) { if (mac->mac_phy.detach != NULL) mac->mac_phy.detach(mac); } static int bwn_detach(device_t dev) { struct bwn_softc *sc = device_get_softc(dev); struct bwn_mac *mac = sc->sc_curmac; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; sc->sc_flags |= BWN_FLAG_INVALID; if (device_is_attached(sc->sc_dev)) { bwn_stop(sc, 1); bwn_dma_free(mac); callout_drain(&sc->sc_led_blink_ch); callout_drain(&sc->sc_rfswitch_ch); callout_drain(&sc->sc_task_ch); callout_drain(&sc->sc_watchdog_ch); bwn_phy_detach(mac); if (ifp != NULL) { ieee80211_draintask(ic, &mac->mac_hwreset); ieee80211_draintask(ic, &mac->mac_txpower); ieee80211_ifdetach(ic); if_free(ifp); } } taskqueue_drain(sc->sc_tq, &mac->mac_intrtask); taskqueue_free(sc->sc_tq); for (i = 0; i < BWN_MSI_MESSAGES; i++) { if (mac->mac_intrhand[i] != NULL) { bus_teardown_intr(dev, mac->mac_res_irq[i], mac->mac_intrhand[i]); mac->mac_intrhand[i] = NULL; } } bus_release_resources(dev, mac->mac_intr_spec, mac->mac_res_irq); if (mac->mac_msi != 0) pci_release_msi(dev); BWN_LOCK_DESTROY(sc); return (0); } static int bwn_attach_pre(struct bwn_softc *sc) { struct ifnet *ifp; int error = 0; BWN_LOCK_INIT(sc); TAILQ_INIT(&sc->sc_maclist); callout_init_mtx(&sc->sc_rfswitch_ch, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_task_ch, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_watchdog_ch, &sc->sc_mtx, 0); sc->sc_tq = taskqueue_create_fast("bwn_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->sc_dev)); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(sc->sc_dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } /* set these up early for if_printf use */ if_initname(ifp, device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = bwn_init; ifp->if_ioctl = bwn_ioctl; ifp->if_start = bwn_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); return (0); fail: BWN_LOCK_DESTROY(sc); return (error); } static void bwn_sprom_bugfixes(struct siba_softc *siba) { #define BWN_ISDEV(_vendor, _device, _subvendor, _subdevice) \ ((siba->siba_pci_vid == PCI_VENDOR_##_vendor) && \ (siba->siba_pci_did == _device) && \ (siba->siba_pci_subvid == PCI_VENDOR_##_subvendor) && \ (siba->siba_pci_subdid == _subdevice)) if (siba->siba_board_vendor == PCI_VENDOR_APPLE && siba->siba_board_type == 0x4e && siba->siba_board_rev > 0x40) siba->siba_sprom.bf_lo |= BWN_BFL_PACTRL; if (siba->siba_board_vendor == SIBA_BOARDVENDOR_DELL && siba->siba_chipid == 0x4301 && siba->siba_board_rev == 0x74) siba->siba_sprom.bf_lo |= BWN_BFL_BTCOEXIST; if (siba->siba_type == SIBA_TYPE_PCI) { if (BWN_ISDEV(BROADCOM, 0x4318, ASUSTEK, 0x100f) || BWN_ISDEV(BROADCOM, 0x4320, DELL, 0x0003) || BWN_ISDEV(BROADCOM, 0x4320, HP, 0x12f8) || BWN_ISDEV(BROADCOM, 0x4320, LINKSYS, 0x0013) || BWN_ISDEV(BROADCOM, 0x4320, LINKSYS, 0x0014) || BWN_ISDEV(BROADCOM, 0x4320, LINKSYS, 0x0015) || BWN_ISDEV(BROADCOM, 0x4320, MOTOROLA, 0x7010)) siba->siba_sprom.bf_lo &= ~BWN_BFL_BTCOEXIST; } #undef BWN_ISDEV } static int bwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { #define IS_RUNNING(ifp) \ ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) struct bwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *)data; int error = 0, startall; switch (cmd) { case SIOCSIFFLAGS: startall = 0; if (IS_RUNNING(ifp)) { bwn_update_promisc(ifp); } else if (ifp->if_flags & IFF_UP) { if ((sc->sc_flags & BWN_FLAG_INVALID) == 0) { bwn_init(sc); startall = 1; } } else bwn_stop(sc, 1); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return (error); } static void bwn_start(struct ifnet *ifp) { struct bwn_softc *sc = ifp->if_softc; BWN_LOCK(sc); bwn_start_locked(ifp); BWN_UNLOCK(sc); } static void bwn_start_locked(struct ifnet *ifp) { struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac = sc->sc_curmac; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_key *k; struct mbuf *m; BWN_ASSERT_LOCKED(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || mac == NULL || mac->mac_status < BWN_MAC_STATUS_STARTED) return; for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); /* XXX: LOCK */ if (m == NULL) break; if (bwn_tx_isfull(sc, m)) break; ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (ni == NULL) { device_printf(sc->sc_dev, "unexpected NULL ni\n"); m_freem(m); ifp->if_oerrors++; continue; } KASSERT(ni != NULL, ("%s:%d: fail", __func__, __LINE__)); wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { ieee80211_free_node(ni); m_freem(m); ifp->if_oerrors++; continue; } } wh = NULL; /* Catch any invalid use */ if (bwn_tx_start(sc, ni, m) != 0) { if (ni != NULL) ieee80211_free_node(ni); ifp->if_oerrors++; continue; } sc->sc_watchdog_timer = 5; } } static int bwn_tx_isfull(struct bwn_softc *sc, struct mbuf *m) { struct bwn_dma_ring *dr; struct bwn_mac *mac = sc->sc_curmac; struct bwn_pio_txqueue *tq; struct ifnet *ifp = sc->sc_ifp; int pktlen = roundup(m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); BWN_ASSERT_LOCKED(sc); if (mac->mac_flags & BWN_MAC_FLAG_DMA) { dr = bwn_dma_select(mac, M_WME_GETAC(m)); if (dr->dr_stop == 1 || bwn_dma_freeslot(dr) < BWN_TX_SLOTS_PER_FRAME) { dr->dr_stop = 1; goto full; } } else { tq = bwn_pio_select(mac, M_WME_GETAC(m)); if (tq->tq_free == 0 || pktlen > tq->tq_size || pktlen > (tq->tq_size - tq->tq_used)) { tq->tq_stop = 1; goto full; } } return (0); full: IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; return (1); } static int bwn_tx_start(struct bwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m) { struct bwn_mac *mac = sc->sc_curmac; int error; BWN_ASSERT_LOCKED(sc); if (m->m_pkthdr.len < IEEE80211_MIN_LEN || mac == NULL) { m_freem(m); return (ENXIO); } error = (mac->mac_flags & BWN_MAC_FLAG_DMA) ? bwn_dma_tx_start(mac, ni, m) : bwn_pio_tx_start(mac, ni, m); if (error) { m_freem(m); return (error); } return (0); } static int bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, struct mbuf *m) { struct bwn_pio_txpkt *tp; struct bwn_pio_txqueue *tq = bwn_pio_select(mac, M_WME_GETAC(m)); struct bwn_softc *sc = mac->mac_sc; struct bwn_txhdr txhdr; struct mbuf *m_new; uint32_t ctl32; int error; uint16_t ctl16; BWN_ASSERT_LOCKED(sc); /* XXX TODO send packets after DTIM */ KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__)); tp = TAILQ_FIRST(&tq->tq_pktlist); tp->tp_ni = ni; tp->tp_m = m; error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp)); if (error) { device_printf(sc->sc_dev, "tx fail\n"); return (error); } TAILQ_REMOVE(&tq->tq_pktlist, tp, tp_list); tq->tq_used += roundup(m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); tq->tq_free--; if (mac->mac_sd->sd_id.sd_rev >= 8) { /* * XXX please removes m_defrag(9) */ m_new = m_defrag(m, M_DONTWAIT); if (m_new == NULL) { device_printf(sc->sc_dev, "%s: can't defrag TX buffer\n", __func__); return (ENOBUFS); } if (m_new->m_next != NULL) device_printf(sc->sc_dev, "TODO: fragmented packets for PIO\n"); tp->tp_m = m_new; /* send HEADER */ ctl32 = bwn_pio_write_multi_4(mac, tq, (BWN_PIO_READ_4(mac, tq, BWN_PIO8_TXCTL) | BWN_PIO8_TXCTL_FRAMEREADY) & ~BWN_PIO8_TXCTL_EOF, (const uint8_t *)&txhdr, BWN_HDRSIZE(mac)); /* send BODY */ ctl32 = bwn_pio_write_multi_4(mac, tq, ctl32, mtod(m_new, const void *), m_new->m_pkthdr.len); bwn_pio_write_4(mac, tq, BWN_PIO_TXCTL, ctl32 | BWN_PIO8_TXCTL_EOF); } else { ctl16 = bwn_pio_write_multi_2(mac, tq, (bwn_pio_read_2(mac, tq, BWN_PIO_TXCTL) | BWN_PIO_TXCTL_FRAMEREADY) & ~BWN_PIO_TXCTL_EOF, (const uint8_t *)&txhdr, BWN_HDRSIZE(mac)); ctl16 = bwn_pio_write_mbuf_2(mac, tq, ctl16, m); BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl16 | BWN_PIO_TXCTL_EOF); } return (0); } static struct bwn_pio_txqueue * bwn_pio_select(struct bwn_mac *mac, uint8_t prio) { if ((mac->mac_flags & BWN_MAC_FLAG_WME) == 0) return (&mac->mac_method.pio.wme[WME_AC_BE]); switch (prio) { case 0: return (&mac->mac_method.pio.wme[WME_AC_BE]); case 1: return (&mac->mac_method.pio.wme[WME_AC_BK]); case 2: return (&mac->mac_method.pio.wme[WME_AC_VI]); case 3: return (&mac->mac_method.pio.wme[WME_AC_VO]); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); + return (NULL); } static int bwn_dma_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, struct mbuf *m) { #define BWN_GET_TXHDRCACHE(slot) \ &(txhdr_cache[(slot / BWN_TX_SLOTS_PER_FRAME) * BWN_HDRSIZE(mac)]) struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_dma_ring *dr = bwn_dma_select(mac, M_WME_GETAC(m)); struct bwn_dmadesc_generic *desc; struct bwn_dmadesc_meta *mt; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; uint8_t *txhdr_cache = (uint8_t *)dr->dr_txhdr_cache; int error, slot, backup[2] = { dr->dr_curslot, dr->dr_usedslot }; BWN_ASSERT_LOCKED(sc); KASSERT(!dr->dr_stop, ("%s:%d: fail", __func__, __LINE__)); /* XXX send after DTIM */ slot = bwn_dma_getslot(dr); dr->getdesc(dr, slot, &desc, &mt); KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_HEADER, ("%s:%d: fail", __func__, __LINE__)); error = bwn_set_txhdr(dr->dr_mac, ni, m, (struct bwn_txhdr *)BWN_GET_TXHDRCACHE(slot), BWN_DMA_COOKIE(dr, slot)); if (error) goto fail; error = bus_dmamap_load(dr->dr_txring_dtag, mt->mt_dmap, BWN_GET_TXHDRCACHE(slot), BWN_HDRSIZE(mac), bwn_dma_ring_addr, &mt->mt_paddr, BUS_DMA_NOWAIT); if (error) { if_printf(ifp, "%s: can't load TX buffer (1) %d\n", __func__, error); goto fail; } bus_dmamap_sync(dr->dr_txring_dtag, mt->mt_dmap, BUS_DMASYNC_PREWRITE); dr->setdesc(dr, desc, mt->mt_paddr, BWN_HDRSIZE(mac), 1, 0, 0); bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap, BUS_DMASYNC_PREWRITE); slot = bwn_dma_getslot(dr); dr->getdesc(dr, slot, &desc, &mt); KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_BODY && mt->mt_islast == 1, ("%s:%d: fail", __func__, __LINE__)); mt->mt_m = m; mt->mt_ni = ni; error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m, bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT); if (error && error != EFBIG) { if_printf(ifp, "%s: can't load TX buffer (1) %d\n", __func__, error); goto fail; } if (error) { /* error == EFBIG */ struct mbuf *m_new; m_new = m_defrag(m, M_DONTWAIT); if (m_new == NULL) { if_printf(ifp, "%s: can't defrag TX buffer\n", __func__); error = ENOBUFS; goto fail; } else { m = m_new; } mt->mt_m = m; error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m, bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT); if (error) { if_printf(ifp, "%s: can't load TX buffer (2) %d\n", __func__, error); goto fail; } } bus_dmamap_sync(dma->txbuf_dtag, mt->mt_dmap, BUS_DMASYNC_PREWRITE); dr->setdesc(dr, desc, mt->mt_paddr, m->m_pkthdr.len, 0, 1, 1); bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap, BUS_DMASYNC_PREWRITE); /* XXX send after DTIM */ dr->start_transfer(dr, bwn_dma_nextslot(dr, slot)); return (0); fail: dr->dr_curslot = backup[0]; dr->dr_usedslot = backup[1]; return (error); #undef BWN_GET_TXHDRCACHE } static void bwn_watchdog(void *arg) { struct bwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; if (sc->sc_watchdog_timer != 0 && --sc->sc_watchdog_timer == 0) { if_printf(ifp, "device timeout\n"); ifp->if_oerrors++; } callout_schedule(&sc->sc_watchdog_ch, hz); } static int bwn_attach_core(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; int error, have_bg = 0, have_a = 0; uint32_t high; KASSERT(sd->sd_id.sd_rev >= 5, ("unsupported revision %d", sd->sd_id.sd_rev)); siba_powerup(siba, 0); high = siba_read_4(sd, SIBA_TGSHIGH); bwn_reset_core(mac, (high & BWN_TGSHIGH_HAVE_2GHZ) ? BWN_TGSLOW_SUPPORT_G : 0); error = bwn_phy_getinfo(mac, high); if (error) goto fail; have_a = (high & BWN_TGSHIGH_HAVE_5GHZ) ? 1 : 0; have_bg = (high & BWN_TGSHIGH_HAVE_2GHZ) ? 1 : 0; if (siba->siba_pci_did != 0x4312 && siba->siba_pci_did != 0x4319 && siba->siba_pci_did != 0x4324) { have_a = have_bg = 0; if (mac->mac_phy.type == BWN_PHYTYPE_A) have_a = 1; else if (mac->mac_phy.type == BWN_PHYTYPE_G || mac->mac_phy.type == BWN_PHYTYPE_N || mac->mac_phy.type == BWN_PHYTYPE_LP) have_bg = 1; else KASSERT(0 == 1, ("%s: unknown phy type (%d)", __func__, mac->mac_phy.type)); } /* XXX turns off PHY A because it's not supported */ if (mac->mac_phy.type != BWN_PHYTYPE_LP && mac->mac_phy.type != BWN_PHYTYPE_N) { have_a = 0; have_bg = 1; } if (mac->mac_phy.type == BWN_PHYTYPE_G) { mac->mac_phy.attach = bwn_phy_g_attach; mac->mac_phy.detach = bwn_phy_g_detach; mac->mac_phy.prepare_hw = bwn_phy_g_prepare_hw; mac->mac_phy.init_pre = bwn_phy_g_init_pre; mac->mac_phy.init = bwn_phy_g_init; mac->mac_phy.exit = bwn_phy_g_exit; mac->mac_phy.phy_read = bwn_phy_g_read; mac->mac_phy.phy_write = bwn_phy_g_write; mac->mac_phy.rf_read = bwn_phy_g_rf_read; mac->mac_phy.rf_write = bwn_phy_g_rf_write; mac->mac_phy.use_hwpctl = bwn_phy_g_hwpctl; mac->mac_phy.rf_onoff = bwn_phy_g_rf_onoff; mac->mac_phy.switch_analog = bwn_phy_switch_analog; mac->mac_phy.switch_channel = bwn_phy_g_switch_channel; mac->mac_phy.get_default_chan = bwn_phy_g_get_default_chan; mac->mac_phy.set_antenna = bwn_phy_g_set_antenna; mac->mac_phy.set_im = bwn_phy_g_im; mac->mac_phy.recalc_txpwr = bwn_phy_g_recalc_txpwr; mac->mac_phy.set_txpwr = bwn_phy_g_set_txpwr; mac->mac_phy.task_15s = bwn_phy_g_task_15s; mac->mac_phy.task_60s = bwn_phy_g_task_60s; } else if (mac->mac_phy.type == BWN_PHYTYPE_LP) { mac->mac_phy.init_pre = bwn_phy_lp_init_pre; mac->mac_phy.init = bwn_phy_lp_init; mac->mac_phy.phy_read = bwn_phy_lp_read; mac->mac_phy.phy_write = bwn_phy_lp_write; mac->mac_phy.phy_maskset = bwn_phy_lp_maskset; mac->mac_phy.rf_read = bwn_phy_lp_rf_read; mac->mac_phy.rf_write = bwn_phy_lp_rf_write; mac->mac_phy.rf_onoff = bwn_phy_lp_rf_onoff; mac->mac_phy.switch_analog = bwn_phy_lp_switch_analog; mac->mac_phy.switch_channel = bwn_phy_lp_switch_channel; mac->mac_phy.get_default_chan = bwn_phy_lp_get_default_chan; mac->mac_phy.set_antenna = bwn_phy_lp_set_antenna; mac->mac_phy.task_60s = bwn_phy_lp_task_60s; } else { device_printf(sc->sc_dev, "unsupported PHY type (%d)\n", mac->mac_phy.type); error = ENXIO; goto fail; } mac->mac_phy.gmode = have_bg; if (mac->mac_phy.attach != NULL) { error = mac->mac_phy.attach(mac); if (error) { device_printf(sc->sc_dev, "failed\n"); goto fail; } } bwn_reset_core(mac, have_bg ? BWN_TGSLOW_SUPPORT_G : 0); error = bwn_chiptest(mac); if (error) goto fail; error = bwn_setup_channels(mac, have_bg, have_a); if (error) { device_printf(sc->sc_dev, "failed to setup channels\n"); goto fail; } if (sc->sc_curmac == NULL) sc->sc_curmac = mac; error = bwn_dma_attach(mac); if (error != 0) { device_printf(sc->sc_dev, "failed to initialize DMA\n"); goto fail; } mac->mac_phy.switch_analog(mac, 0); siba_dev_down(sd, 0); fail: siba_powerdown(siba); return (error); } static void bwn_reset_core(struct bwn_mac *mac, uint32_t flags) { struct siba_dev_softc *sd = mac->mac_sd; uint32_t low, ctl; flags |= (BWN_TGSLOW_PHYCLOCK_ENABLE | BWN_TGSLOW_PHYRESET); siba_dev_up(sd, flags); DELAY(2000); low = (siba_read_4(sd, SIBA_TGSLOW) | SIBA_TGSLOW_FGC) & ~BWN_TGSLOW_PHYRESET; siba_write_4(sd, SIBA_TGSLOW, low); siba_read_4(sd, SIBA_TGSLOW); DELAY(1000); siba_write_4(sd, SIBA_TGSLOW, low & ~SIBA_TGSLOW_FGC); siba_read_4(sd, SIBA_TGSLOW); DELAY(1000); if (mac->mac_phy.switch_analog != NULL) mac->mac_phy.switch_analog(mac, 1); ctl = BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_GMODE; if (flags & BWN_TGSLOW_SUPPORT_G) ctl |= BWN_MACCTL_GMODE; BWN_WRITE_4(mac, BWN_MACCTL, ctl | BWN_MACCTL_IHR_ON); } static int bwn_phy_getinfo(struct bwn_mac *mac, int tgshigh) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_softc *sc = mac->mac_sc; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; uint32_t tmp; /* PHY */ tmp = BWN_READ_2(mac, BWN_PHYVER); phy->gmode = (tgshigh & BWN_TGSHIGH_HAVE_2GHZ) ? 1 : 0; phy->rf_on = 1; phy->analog = (tmp & BWN_PHYVER_ANALOG) >> 12; phy->type = (tmp & BWN_PHYVER_TYPE) >> 8; phy->rev = (tmp & BWN_PHYVER_VERSION); if ((phy->type == BWN_PHYTYPE_A && phy->rev >= 4) || (phy->type == BWN_PHYTYPE_B && phy->rev != 2 && phy->rev != 4 && phy->rev != 6 && phy->rev != 7) || (phy->type == BWN_PHYTYPE_G && phy->rev > 9) || (phy->type == BWN_PHYTYPE_N && phy->rev > 4) || (phy->type == BWN_PHYTYPE_LP && phy->rev > 2)) goto unsupphy; /* RADIO */ if (siba->siba_chipid == 0x4317) { if (siba->siba_chiprev == 0) tmp = 0x3205017f; else if (siba->siba_chiprev == 1) tmp = 0x4205017f; else tmp = 0x5205017f; } else { BWN_WRITE_2(mac, BWN_RFCTL, BWN_RFCTL_ID); tmp = BWN_READ_2(mac, BWN_RFDATALO); BWN_WRITE_2(mac, BWN_RFCTL, BWN_RFCTL_ID); tmp |= (uint32_t)BWN_READ_2(mac, BWN_RFDATAHI) << 16; } phy->rf_rev = (tmp & 0xf0000000) >> 28; phy->rf_ver = (tmp & 0x0ffff000) >> 12; phy->rf_manuf = (tmp & 0x00000fff); if (phy->rf_manuf != 0x17f) /* 0x17f is broadcom */ goto unsupradio; if ((phy->type == BWN_PHYTYPE_A && (phy->rf_ver != 0x2060 || phy->rf_rev != 1 || phy->rf_manuf != 0x17f)) || (phy->type == BWN_PHYTYPE_B && (phy->rf_ver & 0xfff0) != 0x2050) || (phy->type == BWN_PHYTYPE_G && phy->rf_ver != 0x2050) || (phy->type == BWN_PHYTYPE_N && phy->rf_ver != 0x2055 && phy->rf_ver != 0x2056) || (phy->type == BWN_PHYTYPE_LP && phy->rf_ver != 0x2062 && phy->rf_ver != 0x2063)) goto unsupradio; return (0); unsupphy: device_printf(sc->sc_dev, "unsupported PHY (type %#x, rev %#x, " "analog %#x)\n", phy->type, phy->rev, phy->analog); return (ENXIO); unsupradio: device_printf(sc->sc_dev, "unsupported radio (manuf %#x, ver %#x, " "rev %#x)\n", phy->rf_manuf, phy->rf_ver, phy->rf_rev); return (ENXIO); } static int bwn_chiptest(struct bwn_mac *mac) { #define TESTVAL0 0x55aaaa55 #define TESTVAL1 0xaa5555aa struct bwn_softc *sc = mac->mac_sc; struct siba_dev_softc *sd = mac->mac_sd; uint32_t v, backup; BWN_LOCK(sc); backup = bwn_shm_read_4(mac, BWN_SHARED, 0); bwn_shm_write_4(mac, BWN_SHARED, 0, TESTVAL0); if (bwn_shm_read_4(mac, BWN_SHARED, 0) != TESTVAL0) goto error; bwn_shm_write_4(mac, BWN_SHARED, 0, TESTVAL1); if (bwn_shm_read_4(mac, BWN_SHARED, 0) != TESTVAL1) goto error; bwn_shm_write_4(mac, BWN_SHARED, 0, backup); if ((sd->sd_id.sd_rev >= 3) && (sd->sd_id.sd_rev <= 10)) { BWN_WRITE_2(mac, BWN_TSF_CFP_START, 0xaaaa); BWN_WRITE_4(mac, BWN_TSF_CFP_START, 0xccccbbbb); if (BWN_READ_2(mac, BWN_TSF_CFP_START_LOW) != 0xbbbb) goto error; if (BWN_READ_2(mac, BWN_TSF_CFP_START_HIGH) != 0xcccc) goto error; } BWN_WRITE_4(mac, BWN_TSF_CFP_START, 0); v = BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_GMODE; if (v != (BWN_MACCTL_GMODE | BWN_MACCTL_IHR_ON)) goto error; BWN_UNLOCK(sc); return (0); error: BWN_UNLOCK(sc); device_printf(sc->sc_dev, "failed to validate the chipaccess\n"); return (ENODEV); } #define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT | IEEE80211_CHAN_G) #define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT | IEEE80211_CHAN_A) static int bwn_setup_channels(struct bwn_mac *mac, int have_bg, int have_a) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; memset(ic->ic_channels, 0, sizeof(ic->ic_channels)); ic->ic_nchans = 0; if (have_bg) bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, &bwn_chantable_bg, IEEE80211_CHAN_G); if (mac->mac_phy.type == BWN_PHYTYPE_N) { if (have_a) bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, &bwn_chantable_n, IEEE80211_CHAN_HTA); } else { if (have_a) bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, &bwn_chantable_a, IEEE80211_CHAN_A); } mac->mac_phy.supports_2ghz = have_bg; mac->mac_phy.supports_5ghz = have_a; return (ic->ic_nchans == 0 ? ENXIO : 0); } static uint32_t bwn_shm_read_4(struct bwn_mac *mac, uint16_t way, uint16_t offset) { - struct bwn_softc *sc = mac->mac_sc; uint32_t ret; - BWN_ASSERT_LOCKED(sc); + BWN_ASSERT_LOCKED(mac->mac_sc); if (way == BWN_SHARED) { KASSERT((offset & 0x0001) == 0, ("%s:%d warn", __func__, __LINE__)); if (offset & 0x0003) { bwn_shm_ctlword(mac, way, offset >> 2); ret = BWN_READ_2(mac, BWN_SHM_DATA_UNALIGNED); ret <<= 16; bwn_shm_ctlword(mac, way, (offset >> 2) + 1); ret |= BWN_READ_2(mac, BWN_SHM_DATA); goto out; } offset >>= 2; } bwn_shm_ctlword(mac, way, offset); ret = BWN_READ_4(mac, BWN_SHM_DATA); out: return (ret); } static uint16_t bwn_shm_read_2(struct bwn_mac *mac, uint16_t way, uint16_t offset) { - struct bwn_softc *sc = mac->mac_sc; uint16_t ret; - BWN_ASSERT_LOCKED(sc); + BWN_ASSERT_LOCKED(mac->mac_sc); if (way == BWN_SHARED) { KASSERT((offset & 0x0001) == 0, ("%s:%d warn", __func__, __LINE__)); if (offset & 0x0003) { bwn_shm_ctlword(mac, way, offset >> 2); ret = BWN_READ_2(mac, BWN_SHM_DATA_UNALIGNED); goto out; } offset >>= 2; } bwn_shm_ctlword(mac, way, offset); ret = BWN_READ_2(mac, BWN_SHM_DATA); out: return (ret); } static void bwn_shm_ctlword(struct bwn_mac *mac, uint16_t way, uint16_t offset) { uint32_t control; control = way; control <<= 16; control |= offset; BWN_WRITE_4(mac, BWN_SHM_CONTROL, control); } static void bwn_shm_write_4(struct bwn_mac *mac, uint16_t way, uint16_t offset, uint32_t value) { - struct bwn_softc *sc = mac->mac_sc; + BWN_ASSERT_LOCKED(mac->mac_sc); - BWN_ASSERT_LOCKED(sc); - if (way == BWN_SHARED) { KASSERT((offset & 0x0001) == 0, ("%s:%d warn", __func__, __LINE__)); if (offset & 0x0003) { bwn_shm_ctlword(mac, way, offset >> 2); BWN_WRITE_2(mac, BWN_SHM_DATA_UNALIGNED, (value >> 16) & 0xffff); bwn_shm_ctlword(mac, way, (offset >> 2) + 1); BWN_WRITE_2(mac, BWN_SHM_DATA, value & 0xffff); return; } offset >>= 2; } bwn_shm_ctlword(mac, way, offset); BWN_WRITE_4(mac, BWN_SHM_DATA, value); } static void bwn_shm_write_2(struct bwn_mac *mac, uint16_t way, uint16_t offset, uint16_t value) { - struct bwn_softc *sc = mac->mac_sc; + BWN_ASSERT_LOCKED(mac->mac_sc); - BWN_ASSERT_LOCKED(sc); - if (way == BWN_SHARED) { KASSERT((offset & 0x0001) == 0, ("%s:%d warn", __func__, __LINE__)); if (offset & 0x0003) { bwn_shm_ctlword(mac, way, offset >> 2); BWN_WRITE_2(mac, BWN_SHM_DATA_UNALIGNED, value); return; } offset >>= 2; } bwn_shm_ctlword(mac, way, offset); BWN_WRITE_2(mac, BWN_SHM_DATA, value); } static void bwn_addchan(struct ieee80211_channel *c, int freq, int flags, int ieee, int txpow) { c->ic_freq = freq; c->ic_flags = flags; c->ic_ieee = ieee; c->ic_minpower = 0; c->ic_maxpower = 2 * txpow; c->ic_maxregpower = txpow; } static void bwn_addchannels(struct ieee80211_channel chans[], int maxchans, int *nchans, const struct bwn_channelinfo *ci, int flags) { struct ieee80211_channel *c; int i; c = &chans[*nchans]; for (i = 0; i < ci->nchannels; i++) { const struct bwn_channel *hc; hc = &ci->channels[i]; if (*nchans >= maxchans) break; bwn_addchan(c, hc->freq, flags, hc->ieee, hc->maxTxPow); c++, (*nchans)++; if (flags == IEEE80211_CHAN_G || flags == IEEE80211_CHAN_HTG) { /* g channel have a separate b-only entry */ if (*nchans >= maxchans) break; c[0] = c[-1]; c[-1].ic_flags = IEEE80211_CHAN_B; c++, (*nchans)++; } if (flags == IEEE80211_CHAN_HTG) { /* HT g channel have a separate g-only entry */ if (*nchans >= maxchans) break; c[-1].ic_flags = IEEE80211_CHAN_G; c[0] = c[-1]; c[0].ic_flags &= ~IEEE80211_CHAN_HT; c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */ c++, (*nchans)++; } if (flags == IEEE80211_CHAN_HTA) { /* HT a channel have a separate a-only entry */ if (*nchans >= maxchans) break; c[-1].ic_flags = IEEE80211_CHAN_A; c[0] = c[-1]; c[0].ic_flags &= ~IEEE80211_CHAN_HT; c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */ c++, (*nchans)++; } } } static int bwn_phy_g_attach(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct siba_dev_softc *sd = mac->mac_sd; struct siba_sprom *sprom = &sd->sd_bus->siba_sprom; unsigned int i; int16_t pab0 = (int16_t)(sprom->pa0b0), pab1 = (int16_t)(sprom->pa0b1), pab2 = (int16_t)(sprom->pa0b2); static int8_t bwn_phy_g_tssi2dbm_table[] = BWN_PHY_G_TSSI2DBM_TABLE; int8_t bg = (int8_t)sprom->tssi_bg; if ((sd->sd_bus->siba_chipid == 0x4301) && (phy->rf_ver != 0x2050)) device_printf(sc->sc_dev, "not supported anymore\n"); pg->pg_flags = 0; if (pab0 == 0 || pab1 == 0 || pab2 == 0 || pab0 == -1 || pab1 == -1 || pab2 == -1) { pg->pg_idletssi = 52; pg->pg_tssi2dbm = bwn_phy_g_tssi2dbm_table; return (0); } pg->pg_idletssi = (bg == 0 || bg == -1) ? 62 : bg; pg->pg_tssi2dbm = (uint8_t *)malloc(64, M_DEVBUF, M_NOWAIT | M_ZERO); if (pg->pg_tssi2dbm == NULL) { device_printf(sc->sc_dev, "failed to allocate buffer\n"); return (ENOMEM); } for (i = 0; i < 64; i++) { int32_t m1, m2, f, q, delta; int8_t j = 0; m1 = BWN_TSSI2DBM(16 * pab0 + i * pab1, 32); m2 = MAX(BWN_TSSI2DBM(32768 + i * pab2, 256), 1); f = 256; do { if (j > 15) { device_printf(sc->sc_dev, "failed to generate tssi2dBm\n"); free(pg->pg_tssi2dbm, M_DEVBUF); return (ENOMEM); } q = BWN_TSSI2DBM(f * 4096 - BWN_TSSI2DBM(m2 * f, 16) * f, 2048); delta = abs(q - f); f = q; j++; } while (delta >= 2); pg->pg_tssi2dbm[i] = MIN(MAX(BWN_TSSI2DBM(m1 * f, 8192), -127), 128); } pg->pg_flags |= BWN_PHY_G_FLAG_TSSITABLE_ALLOC; return (0); } static void bwn_phy_g_detach(struct bwn_mac *mac) { struct bwn_phy_g *pg = &mac->mac_phy.phy_g; if (pg->pg_flags & BWN_PHY_G_FLAG_TSSITABLE_ALLOC) { free(pg->pg_tssi2dbm, M_DEVBUF); pg->pg_tssi2dbm = NULL; } pg->pg_flags = 0; } static void bwn_phy_g_init_pre(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; void *tssi2dbm; int idletssi; unsigned int i; tssi2dbm = pg->pg_tssi2dbm; idletssi = pg->pg_idletssi; memset(pg, 0, sizeof(*pg)); pg->pg_tssi2dbm = tssi2dbm; pg->pg_idletssi = idletssi; memset(pg->pg_minlowsig, 0xff, sizeof(pg->pg_minlowsig)); for (i = 0; i < N(pg->pg_nrssi); i++) pg->pg_nrssi[i] = -1000; for (i = 0; i < N(pg->pg_nrssi_lt); i++) pg->pg_nrssi_lt[i] = i; pg->pg_lofcal = 0xffff; pg->pg_initval = 0xffff; pg->pg_immode = BWN_IMMODE_NONE; pg->pg_ofdmtab_dir = BWN_OFDMTAB_DIR_UNKNOWN; pg->pg_avgtssi = 0xff; pg->pg_loctl.tx_bias = 0xff; TAILQ_INIT(&pg->pg_loctl.calib_list); } static int bwn_phy_g_prepare_hw(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; struct siba_softc *bus = mac->mac_sd->sd_bus; static const struct bwn_rfatt rfatt0[] = { { 3, 0 }, { 1, 0 }, { 5, 0 }, { 7, 0 }, { 9, 0 }, { 2, 0 }, { 0, 0 }, { 4, 0 }, { 6, 0 }, { 8, 0 }, { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 } }; static const struct bwn_rfatt rfatt1[] = { { 2, 1 }, { 4, 1 }, { 6, 1 }, { 8, 1 }, { 10, 1 }, { 12, 1 }, { 14, 1 } }; static const struct bwn_rfatt rfatt2[] = { { 0, 1 }, { 2, 1 }, { 4, 1 }, { 6, 1 }, { 8, 1 }, { 9, 1 }, { 9, 1 } }; static const struct bwn_bbatt bbatt_0[] = { { 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 } }; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s fail", __func__)); if (phy->rf_ver == 0x2050 && phy->rf_rev < 6) pg->pg_bbatt.att = 0; else pg->pg_bbatt.att = 2; /* prepare Radio Attenuation */ pg->pg_rfatt.padmix = 0; if (bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM && bus->siba_board_type == SIBA_BOARD_BCM4309G) { if (bus->siba_board_rev < 0x43) { pg->pg_rfatt.att = 2; goto done; } else if (bus->siba_board_rev < 0x51) { pg->pg_rfatt.att = 3; goto done; } } if (phy->type == BWN_PHYTYPE_A) { pg->pg_rfatt.att = 0x60; goto done; } switch (phy->rf_ver) { case 0x2050: switch (phy->rf_rev) { case 0: pg->pg_rfatt.att = 5; goto done; case 1: if (phy->type == BWN_PHYTYPE_G) { if (bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM && bus->siba_board_type == SIBA_BOARD_BCM4309G && bus->siba_board_rev >= 30) pg->pg_rfatt.att = 3; else if (bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM && bus->siba_board_type == SIBA_BOARD_BU4306) pg->pg_rfatt.att = 3; else pg->pg_rfatt.att = 1; } else { if (bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM && bus->siba_board_type == SIBA_BOARD_BCM4309G && bus->siba_board_rev >= 30) pg->pg_rfatt.att = 7; else pg->pg_rfatt.att = 6; } goto done; case 2: if (phy->type == BWN_PHYTYPE_G) { if (bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM && bus->siba_board_type == SIBA_BOARD_BCM4309G && bus->siba_board_rev >= 30) pg->pg_rfatt.att = 3; else if (bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM && bus->siba_board_type == SIBA_BOARD_BU4306) pg->pg_rfatt.att = 5; else if (bus->siba_chipid == 0x4320) pg->pg_rfatt.att = 4; else pg->pg_rfatt.att = 3; } else pg->pg_rfatt.att = 6; goto done; case 3: pg->pg_rfatt.att = 5; goto done; case 4: case 5: pg->pg_rfatt.att = 1; goto done; case 6: case 7: pg->pg_rfatt.att = 5; goto done; case 8: pg->pg_rfatt.att = 0xa; pg->pg_rfatt.padmix = 1; goto done; case 9: default: pg->pg_rfatt.att = 5; goto done; } break; case 0x2053: switch (phy->rf_rev) { case 1: pg->pg_rfatt.att = 6; goto done; } break; } pg->pg_rfatt.att = 5; done: pg->pg_txctl = (bwn_phy_g_txctl(mac) << 4); if (!bwn_has_hwpctl(mac)) { lo->rfatt.array = rfatt0; lo->rfatt.len = N(rfatt0); lo->rfatt.min = 0; lo->rfatt.max = 9; goto genbbatt; } if (phy->rf_ver == 0x2050 && phy->rf_rev == 8) { lo->rfatt.array = rfatt1; lo->rfatt.len = N(rfatt1); lo->rfatt.min = 0; lo->rfatt.max = 14; goto genbbatt; } lo->rfatt.array = rfatt2; lo->rfatt.len = N(rfatt2); lo->rfatt.min = 0; lo->rfatt.max = 9; genbbatt: lo->bbatt.array = bbatt_0; lo->bbatt.len = N(bbatt_0); lo->bbatt.min = 0; lo->bbatt.max = 8; BWN_READ_4(mac, BWN_MACCTL); if (phy->rev == 1) { phy->gmode = 0; bwn_reset_core(mac, 0); bwn_phy_g_init_sub(mac); phy->gmode = 1; bwn_reset_core(mac, BWN_TGSLOW_SUPPORT_G); } return (0); } static uint16_t bwn_phy_g_txctl(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; if (phy->rf_ver != 0x2050) return (0); if (phy->rf_rev == 1) return (BWN_TXCTL_PA2DB | BWN_TXCTL_TXMIX); if (phy->rf_rev < 6) return (BWN_TXCTL_PA2DB); if (phy->rf_rev == 8) return (BWN_TXCTL_TXMIX); return (0); } static int bwn_phy_g_init(struct bwn_mac *mac) { bwn_phy_g_init_sub(mac); return (0); } static void bwn_phy_g_exit(struct bwn_mac *mac) { struct bwn_txpwr_loctl *lo = &mac->mac_phy.phy_g.pg_loctl; struct bwn_lo_calib *cal, *tmp; if (lo == NULL) return; TAILQ_FOREACH_SAFE(cal, &lo->calib_list, list, tmp) { TAILQ_REMOVE(&lo->calib_list, cal, list); free(cal, M_DEVBUF); } } static uint16_t bwn_phy_g_read(struct bwn_mac *mac, uint16_t reg) { BWN_WRITE_2(mac, BWN_PHYCTL, reg); return (BWN_READ_2(mac, BWN_PHYDATA)); } static void bwn_phy_g_write(struct bwn_mac *mac, uint16_t reg, uint16_t value) { BWN_WRITE_2(mac, BWN_PHYCTL, reg); BWN_WRITE_2(mac, BWN_PHYDATA, value); } static uint16_t bwn_phy_g_rf_read(struct bwn_mac *mac, uint16_t reg) { KASSERT(reg != 1, ("%s:%d: fail", __func__, __LINE__)); BWN_WRITE_2(mac, BWN_RFCTL, reg | 0x80); return (BWN_READ_2(mac, BWN_RFDATALO)); } static void bwn_phy_g_rf_write(struct bwn_mac *mac, uint16_t reg, uint16_t value) { KASSERT(reg != 1, ("%s:%d: fail", __func__, __LINE__)); BWN_WRITE_2(mac, BWN_RFCTL, reg); BWN_WRITE_2(mac, BWN_RFDATALO, value); } static int bwn_phy_g_hwpctl(struct bwn_mac *mac) { return (mac->mac_phy.rev >= 6); } static void bwn_phy_g_rf_onoff(struct bwn_mac *mac, int on) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; unsigned int channel; uint16_t rfover, rfoverval; if (on) { if (phy->rf_on) return; BWN_PHY_WRITE(mac, 0x15, 0x8000); BWN_PHY_WRITE(mac, 0x15, 0xcc00); BWN_PHY_WRITE(mac, 0x15, (phy->gmode ? 0xc0 : 0x0)); if (pg->pg_flags & BWN_PHY_G_FLAG_RADIOCTX_VALID) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, pg->pg_radioctx_over); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, pg->pg_radioctx_overval); pg->pg_flags &= ~BWN_PHY_G_FLAG_RADIOCTX_VALID; } channel = phy->chan; bwn_phy_g_switch_chan(mac, 6, 1); bwn_phy_g_switch_chan(mac, channel, 0); return; } rfover = BWN_PHY_READ(mac, BWN_PHY_RFOVER); rfoverval = BWN_PHY_READ(mac, BWN_PHY_RFOVERVAL); pg->pg_radioctx_over = rfover; pg->pg_radioctx_overval = rfoverval; pg->pg_flags |= BWN_PHY_G_FLAG_RADIOCTX_VALID; BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, rfover | 0x008c); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, rfoverval & 0xff73); } static int bwn_phy_g_switch_channel(struct bwn_mac *mac, uint32_t newchan) { if ((newchan < 1) || (newchan > 14)) return (EINVAL); bwn_phy_g_switch_chan(mac, newchan, 0); return (0); } static uint32_t bwn_phy_g_get_default_chan(struct bwn_mac *mac) { return (1); } static void bwn_phy_g_set_antenna(struct bwn_mac *mac, int antenna) { struct bwn_phy *phy = &mac->mac_phy; uint64_t hf; int autodiv = 0; uint16_t tmp; if (antenna == BWN_ANTAUTO0 || antenna == BWN_ANTAUTO1) autodiv = 1; hf = bwn_hf_read(mac) & ~BWN_HF_UCODE_ANTDIV_HELPER; bwn_hf_write(mac, hf); BWN_PHY_WRITE(mac, BWN_PHY_BBANDCFG, (BWN_PHY_READ(mac, BWN_PHY_BBANDCFG) & ~BWN_PHY_BBANDCFG_RXANT) | ((autodiv ? BWN_ANTAUTO1 : antenna) << BWN_PHY_BBANDCFG_RXANT_SHIFT)); if (autodiv) { tmp = BWN_PHY_READ(mac, BWN_PHY_ANTDWELL); if (antenna == BWN_ANTAUTO1) tmp &= ~BWN_PHY_ANTDWELL_AUTODIV1; else tmp |= BWN_PHY_ANTDWELL_AUTODIV1; BWN_PHY_WRITE(mac, BWN_PHY_ANTDWELL, tmp); } tmp = BWN_PHY_READ(mac, BWN_PHY_ANTWRSETT); if (autodiv) tmp |= BWN_PHY_ANTWRSETT_ARXDIV; else tmp &= ~BWN_PHY_ANTWRSETT_ARXDIV; BWN_PHY_WRITE(mac, BWN_PHY_ANTWRSETT, tmp); if (phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_OFDM61, BWN_PHY_READ(mac, BWN_PHY_OFDM61) | BWN_PHY_OFDM61_10); BWN_PHY_WRITE(mac, BWN_PHY_DIVSRCHGAINBACK, (BWN_PHY_READ(mac, BWN_PHY_DIVSRCHGAINBACK) & 0xff00) | 0x15); if (phy->rev == 2) BWN_PHY_WRITE(mac, BWN_PHY_ADIVRELATED, 8); else BWN_PHY_WRITE(mac, BWN_PHY_ADIVRELATED, (BWN_PHY_READ(mac, BWN_PHY_ADIVRELATED) & 0xff00) | 8); } if (phy->rev >= 6) BWN_PHY_WRITE(mac, BWN_PHY_OFDM9B, 0xdc); hf |= BWN_HF_UCODE_ANTDIV_HELPER; bwn_hf_write(mac, hf); } static int bwn_phy_g_im(struct bwn_mac *mac, int mode) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s: fail", __func__)); KASSERT(mode == BWN_IMMODE_NONE, ("%s: fail", __func__)); if (phy->rev == 0 || !phy->gmode) return (ENODEV); pg->pg_aci_wlan_automatic = 0; return (0); } static int bwn_phy_g_recalc_txpwr(struct bwn_mac *mac, int ignore_tssi) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_softc *sc = mac->mac_sc; struct siba_softc *siba = mac->mac_sd->sd_bus; unsigned int tssi; int cck, ofdm; int power; int rfatt, bbatt; unsigned int max; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s: fail", __func__)); cck = bwn_phy_shm_tssi_read(mac, BWN_SHARED_TSSI_CCK); ofdm = bwn_phy_shm_tssi_read(mac, BWN_SHARED_TSSI_OFDM_G); if (cck < 0 && ofdm < 0) { if (ignore_tssi == 0) return (BWN_TXPWR_RES_DONE); cck = 0; ofdm = 0; } tssi = (cck < 0) ? ofdm : ((ofdm < 0) ? cck : (cck + ofdm) / 2); if (pg->pg_avgtssi != 0xff) tssi = (tssi + pg->pg_avgtssi) / 2; pg->pg_avgtssi = tssi; KASSERT(tssi < BWN_TSSI_MAX, ("%s:%d: fail", __func__, __LINE__)); max = siba->siba_sprom.maxpwr_bg; if (siba->siba_sprom.bf_lo & BWN_BFL_PACTRL) max -= 3; if (max >= 120) { device_printf(sc->sc_dev, "invalid max TX-power value\n"); siba->siba_sprom.maxpwr_bg = max = 80; } power = MIN(MAX((phy->txpower < 0) ? 0 : (phy->txpower << 2), 0), max) - (pg->pg_tssi2dbm[MIN(MAX(pg->pg_idletssi - pg->pg_curtssi + tssi, 0x00), 0x3f)]); if (power == 0) return (BWN_TXPWR_RES_DONE); rfatt = -((power + 7) / 8); bbatt = (-(power / 2)) - (4 * rfatt); if ((rfatt == 0) && (bbatt == 0)) return (BWN_TXPWR_RES_DONE); pg->pg_bbatt_delta = bbatt; pg->pg_rfatt_delta = rfatt; return (BWN_TXPWR_RES_NEED_ADJUST); } static void bwn_phy_g_set_txpwr(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_softc *sc = mac->mac_sc; int rfatt, bbatt; uint8_t txctl; bwn_mac_suspend(mac); BWN_ASSERT_LOCKED(sc); bbatt = pg->pg_bbatt.att; bbatt += pg->pg_bbatt_delta; rfatt = pg->pg_rfatt.att; rfatt += pg->pg_rfatt_delta; bwn_phy_g_setatt(mac, &bbatt, &rfatt); txctl = pg->pg_txctl; if ((phy->rf_ver == 0x2050) && (phy->rf_rev == 2)) { if (rfatt <= 1) { if (txctl == 0) { txctl = BWN_TXCTL_PA2DB | BWN_TXCTL_TXMIX; rfatt += 2; bbatt += 2; } else if (mac->mac_sd->sd_bus->siba_sprom. bf_lo & BWN_BFL_PACTRL) { bbatt += 4 * (rfatt - 2); rfatt = 2; } } else if (rfatt > 4 && txctl) { txctl = 0; if (bbatt < 3) { rfatt -= 3; bbatt += 2; } else { rfatt -= 2; bbatt -= 2; } } } pg->pg_txctl = txctl; bwn_phy_g_setatt(mac, &bbatt, &rfatt); pg->pg_rfatt.att = rfatt; pg->pg_bbatt.att = bbatt; DPRINTF(sc, BWN_DEBUG_TXPOW, "%s: adjust TX power\n", __func__); bwn_phy_lock(mac); bwn_rf_lock(mac); bwn_phy_g_set_txpwr_sub(mac, &pg->pg_bbatt, &pg->pg_rfatt, pg->pg_txctl); bwn_rf_unlock(mac); bwn_phy_unlock(mac); bwn_mac_enable(mac); } static void bwn_phy_g_task_15s(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_softc *sc = mac->mac_sc; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; unsigned long expire, now; struct bwn_lo_calib *cal, *tmp; uint8_t expired = 0; bwn_mac_suspend(mac); if (lo == NULL) goto fail; BWN_GETTIME(now); if (bwn_has_hwpctl(mac)) { expire = now - BWN_LO_PWRVEC_EXPIRE; if (time_before(lo->pwr_vec_read_time, expire)) { bwn_lo_get_powervector(mac); bwn_phy_g_dc_lookup_init(mac, 0); } goto fail; } expire = now - BWN_LO_CALIB_EXPIRE; TAILQ_FOREACH_SAFE(cal, &lo->calib_list, list, tmp) { if (!time_before(cal->calib_time, expire)) continue; if (BWN_BBATTCMP(&cal->bbatt, &pg->pg_bbatt) && BWN_RFATTCMP(&cal->rfatt, &pg->pg_rfatt)) { KASSERT(!expired, ("%s:%d: fail", __func__, __LINE__)); expired = 1; } DPRINTF(sc, BWN_DEBUG_LO, "expired BB %u RF %u %u I %d Q %d\n", cal->bbatt.att, cal->rfatt.att, cal->rfatt.padmix, cal->ctl.i, cal->ctl.q); TAILQ_REMOVE(&lo->calib_list, cal, list); free(cal, M_DEVBUF); } if (expired || TAILQ_EMPTY(&lo->calib_list)) { cal = bwn_lo_calibset(mac, &pg->pg_bbatt, &pg->pg_rfatt); if (cal == NULL) { device_printf(sc->sc_dev, "failed to recalibrate LO\n"); goto fail; } TAILQ_INSERT_TAIL(&lo->calib_list, cal, list); bwn_lo_write(mac, &cal->ctl); } fail: bwn_mac_enable(mac); } static void bwn_phy_g_task_60s(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; uint8_t old = phy->chan; if (!(mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_RSSI)) return; bwn_mac_suspend(mac); bwn_nrssi_slope_11g(mac); if ((phy->rf_ver == 0x2050) && (phy->rf_rev == 8)) { bwn_switch_channel(mac, (old >= 8) ? 1 : 13); bwn_switch_channel(mac, old); } bwn_mac_enable(mac); } static void bwn_phy_switch_analog(struct bwn_mac *mac, int on) { BWN_WRITE_2(mac, BWN_PHY0, on ? 0 : 0xf4); } static int bwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac = sc->sc_curmac; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || mac->mac_status < BWN_MAC_STATUS_STARTED) { ieee80211_free_node(ni); m_freem(m); return (ENETDOWN); } BWN_LOCK(sc); if (bwn_tx_isfull(sc, m)) { ieee80211_free_node(ni); m_freem(m); ifp->if_oerrors++; BWN_UNLOCK(sc); return (ENOBUFS); } if (bwn_tx_start(sc, ni, m) != 0) { if (ni != NULL) ieee80211_free_node(ni); ifp->if_oerrors++; } sc->sc_watchdog_timer = 5; BWN_UNLOCK(sc); return (0); } /* * Setup driver-specific state for a newly associated node. * Note that we're called also on a re-associate, the isnew * param tells us if this is the first time or not. */ static void bwn_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&BWN_VAP(vap)->bv_amrr, &BWN_NODE(ni)->bn_amn, ni); } /* * Callback from the 802.11 layer to update the slot time * based on the current setting. We use it to notify the * firmware of ERP changes and the f/w takes care of things * like slot time and preamble. */ static void bwn_updateslot(struct ifnet *ifp) { struct bwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct bwn_mac *mac; BWN_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { mac = (struct bwn_mac *)sc->sc_curmac; bwn_set_slot_time(mac, (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20); } BWN_UNLOCK(sc); } /* * Callback from the 802.11 layer after a promiscuous mode change. * Note this interface does not check the operating mode as this * is an internal callback and we are expected to honor the current * state (e.g. this is used for setting the interface in promiscuous * mode when operating in hostap mode to do ACS). */ static void bwn_update_promisc(struct ifnet *ifp) { struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac = sc->sc_curmac; BWN_LOCK(sc); mac = sc->sc_curmac; if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) { if (ifp->if_flags & IFF_PROMISC) sc->sc_filters |= BWN_MACCTL_PROMISC; else sc->sc_filters &= ~BWN_MACCTL_PROMISC; bwn_set_opmode(mac); } BWN_UNLOCK(sc); } /* * Callback from the 802.11 layer to update WME parameters. */ static int bwn_wme_update(struct ieee80211com *ic) { struct bwn_softc *sc = ic->ic_ifp->if_softc; struct bwn_mac *mac = sc->sc_curmac; struct wmeParams *wmep; int i; BWN_LOCK(sc); mac = sc->sc_curmac; if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) { bwn_mac_suspend(mac); for (i = 0; i < N(sc->sc_wmeParams); i++) { wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[i]; bwn_wme_loadparams(mac, wmep, bwn_wme_shm_offsets[i]); } bwn_mac_enable(mac); } BWN_UNLOCK(sc); return (0); } static struct ieee80211_node * bwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic = vap->iv_ic; struct bwn_softc *sc = ic->ic_ifp->if_softc; const size_t space = sizeof(struct bwn_node); struct bwn_node *bn; bn = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); if (bn == NULL) { /* XXX stat+msg */ return (NULL); } DPRINTF(sc, BWN_DEBUG_NODE, "%s: bn %p\n", __func__, bn); return (&bn->bn_node); } static void bwn_node_cleanup(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct bwn_softc *sc = ic->ic_ifp->if_softc; sc->sc_node_cleanup(ni); } static void bwn_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac; BWN_LOCK(sc); mac = sc->sc_curmac; if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) { sc->sc_filters |= BWN_MACCTL_BEACON_PROMISC; bwn_set_opmode(mac); /* disable CFP update during scan */ bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_SKIP_CFP_UPDATE); } BWN_UNLOCK(sc); } static void bwn_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac; BWN_LOCK(sc); mac = sc->sc_curmac; if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) { sc->sc_filters &= ~BWN_MACCTL_BEACON_PROMISC; bwn_set_opmode(mac); bwn_hf_write(mac, bwn_hf_read(mac) & ~BWN_HF_SKIP_CFP_UPDATE); } BWN_UNLOCK(sc); } static void bwn_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac = sc->sc_curmac; struct bwn_phy *phy = &mac->mac_phy; int chan, error; BWN_LOCK(sc); error = bwn_switch_band(sc, ic->ic_curchan); if (error) goto fail;; bwn_mac_suspend(mac); bwn_set_txretry(mac, BWN_RETRY_SHORT, BWN_RETRY_LONG); chan = ieee80211_chan2ieee(ic, ic->ic_curchan); if (chan != phy->chan) bwn_switch_channel(mac, chan); /* TX power level */ if (ic->ic_curchan->ic_maxpower != 0 && ic->ic_curchan->ic_maxpower != phy->txpower) { phy->txpower = ic->ic_curchan->ic_maxpower / 2; bwn_phy_txpower_check(mac, BWN_TXPWR_IGNORE_TIME | BWN_TXPWR_IGNORE_TSSI); } bwn_set_txantenna(mac, BWN_ANT_DEFAULT); if (phy->set_antenna) phy->set_antenna(mac, BWN_ANT_DEFAULT); if (sc->sc_rf_enabled != phy->rf_on) { if (sc->sc_rf_enabled) { bwn_rf_turnon(mac); if (!(mac->mac_flags & BWN_MAC_FLAG_RADIO_ON)) device_printf(sc->sc_dev, "please turns on the RF switch\n"); } else bwn_rf_turnoff(mac); } bwn_mac_enable(mac); fail: /* * Setup radio tap channel freq and flags */ sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = htole16(ic->ic_curchan->ic_freq); sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = htole16(ic->ic_curchan->ic_flags & 0xffff); BWN_UNLOCK(sc); } static struct ieee80211vap * bwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac0[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = ic->ic_ifp; struct bwn_softc *sc = ifp->if_softc; struct ieee80211vap *vap; struct bwn_vap *bvp; uint8_t mac[IEEE80211_ADDR_LEN]; IEEE80211_ADDR_COPY(mac, mac0); switch (opmode) { case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: case IEEE80211_M_STA: case IEEE80211_M_WDS: case IEEE80211_M_MONITOR: case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: break; default: return (NULL); } IEEE80211_ADDR_COPY(sc->sc_macaddr, mac0); bvp = (struct bwn_vap *) malloc(sizeof(struct bwn_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (bvp == NULL) { device_printf(sc->sc_dev, "failed to allocate a buffer\n"); return (NULL); } vap = &bvp->bv_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); IEEE80211_ADDR_COPY(vap->iv_myaddr, mac); /* override with driver methods */ bvp->bv_newstate = vap->iv_newstate; vap->iv_newstate = bwn_newstate; /* override max aid so sta's cannot assoc when we're out of sta id's */ vap->iv_max_aid = BWN_STAID_MAX; ieee80211_amrr_init(&bvp->bv_amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 500 /*ms*/); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); return (vap); } static void bwn_vap_delete(struct ieee80211vap *vap) { struct bwn_vap *bvp = BWN_VAP(vap); ieee80211_amrr_cleanup(&bvp->bv_amrr); ieee80211_vap_detach(vap); free(bvp, M_80211_VAP); } static void bwn_init(void *arg) { struct bwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int error = 0; DPRINTF(sc, BWN_DEBUG_ANY, "%s: if_flags 0x%x\n", __func__, ifp->if_flags); BWN_LOCK(sc); error = bwn_init_locked(sc); BWN_UNLOCK(sc); if (error == 0) ieee80211_start_all(ic); /* start all vap's */ } static int bwn_init_locked(struct bwn_softc *sc) { struct bwn_mac *mac; struct ifnet *ifp = sc->sc_ifp; int error; BWN_ASSERT_LOCKED(sc); bzero(sc->sc_bssid, IEEE80211_ADDR_LEN); sc->sc_flags |= BWN_FLAG_NEED_BEACON_TP; sc->sc_filters = 0; bwn_wme_clear(sc); sc->sc_beacons[0] = sc->sc_beacons[1] = 0; sc->sc_rf_enabled = 1; mac = sc->sc_curmac; if (mac->mac_status == BWN_MAC_STATUS_UNINIT) { error = bwn_core_init(mac); if (error != 0) return (error); } if (mac->mac_status == BWN_MAC_STATUS_INITED) bwn_core_start(mac); bwn_set_opmode(mac); bwn_set_pretbtt(mac); bwn_spu_setdelay(mac, 0); bwn_set_macaddr(mac); ifp->if_drv_flags |= IFF_DRV_RUNNING; callout_reset(&sc->sc_rfswitch_ch, hz, bwn_rfswitch, sc); callout_reset(&sc->sc_watchdog_ch, hz, bwn_watchdog, sc); return (0); } static void bwn_stop(struct bwn_softc *sc, int statechg) { BWN_LOCK(sc); bwn_stop_locked(sc, statechg); BWN_UNLOCK(sc); } static void bwn_stop_locked(struct bwn_softc *sc, int statechg) { struct bwn_mac *mac = sc->sc_curmac; struct ifnet *ifp = sc->sc_ifp; BWN_ASSERT_LOCKED(sc); if (mac->mac_status >= BWN_MAC_STATUS_INITED) { /* XXX FIXME opmode not based on VAP */ bwn_set_opmode(mac); bwn_set_macaddr(mac); } if (mac->mac_status >= BWN_MAC_STATUS_STARTED) bwn_core_stop(mac); callout_stop(&sc->sc_led_blink_ch); sc->sc_led_blinking = 0; bwn_core_exit(mac); sc->sc_rf_enabled = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } static void bwn_wme_clear(struct bwn_softc *sc) { #define MS(_v, _f) (((_v) & _f) >> _f##_S) struct wmeParams *p; unsigned int i; KASSERT(N(bwn_wme_shm_offsets) == N(sc->sc_wmeParams), ("%s:%d: fail", __func__, __LINE__)); for (i = 0; i < N(sc->sc_wmeParams); i++) { p = &(sc->sc_wmeParams[i]); switch (bwn_wme_shm_offsets[i]) { case BWN_WME_VOICE: p->wmep_txopLimit = 0; p->wmep_aifsn = 2; /* XXX FIXME: log2(cwmin) */ p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); p->wmep_logcwmax = MS(0x0001, WME_PARAM_LOGCWMAX); break; case BWN_WME_VIDEO: p->wmep_txopLimit = 0; p->wmep_aifsn = 2; /* XXX FIXME: log2(cwmin) */ p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); p->wmep_logcwmax = MS(0x0001, WME_PARAM_LOGCWMAX); break; case BWN_WME_BESTEFFORT: p->wmep_txopLimit = 0; p->wmep_aifsn = 3; /* XXX FIXME: log2(cwmin) */ p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); p->wmep_logcwmax = MS(0x03ff, WME_PARAM_LOGCWMAX); break; case BWN_WME_BACKGROUND: p->wmep_txopLimit = 0; p->wmep_aifsn = 7; /* XXX FIXME: log2(cwmin) */ p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); p->wmep_logcwmax = MS(0x03ff, WME_PARAM_LOGCWMAX); break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } } } static int bwn_core_init(struct bwn_mac *mac) { #ifdef BWN_DEBUG struct bwn_softc *sc = mac->mac_sc; #endif struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct siba_sprom *sprom = &siba->siba_sprom; uint64_t hf; int error; KASSERT(mac->mac_status == BWN_MAC_STATUS_UNINIT, ("%s:%d: fail", __func__, __LINE__)); siba_powerup(siba, 0); if (!siba_dev_isup(sd)) bwn_reset_core(mac, mac->mac_phy.gmode ? BWN_TGSLOW_SUPPORT_G : 0); mac->mac_flags &= ~BWN_MAC_FLAG_DFQVALID; mac->mac_flags |= BWN_MAC_FLAG_RADIO_ON; mac->mac_phy.hwpctl = (bwn_hwpctl) ? 1 : 0; BWN_GETTIME(mac->mac_phy.nexttime); mac->mac_phy.txerrors = BWN_TXERROR_MAX; bzero(&mac->mac_stats, sizeof(mac->mac_stats)); mac->mac_stats.link_noise = -95; mac->mac_reason_intr = 0; bzero(mac->mac_reason, sizeof(mac->mac_reason)); mac->mac_intr_mask = BWN_INTR_MASKTEMPLATE; #ifdef BWN_DEBUG if (sc->sc_debug & BWN_DEBUG_XMIT) mac->mac_intr_mask &= ~BWN_INTR_PHY_TXERR; #endif mac->mac_suspended = 1; mac->mac_task_state = 0; memset(&mac->mac_noise, 0, sizeof(mac->mac_noise)); mac->mac_phy.init_pre(mac); siba_pcicore_intr(&siba->siba_pci, sd); bwn_fix_imcfglobug(mac); bwn_bt_disable(mac); if (mac->mac_phy.prepare_hw) { error = mac->mac_phy.prepare_hw(mac); if (error) goto fail0; } error = bwn_chip_init(mac); if (error) goto fail0; bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_COREREV, mac->mac_sd->sd_id.sd_rev); hf = bwn_hf_read(mac); if (mac->mac_phy.type == BWN_PHYTYPE_G) { hf |= BWN_HF_GPHY_SYM_WORKAROUND; if (sprom->bf_lo & BWN_BFL_PACTRL) hf |= BWN_HF_PAGAINBOOST_OFDM_ON; if (mac->mac_phy.rev == 1) hf |= BWN_HF_GPHY_DC_CANCELFILTER; } if (mac->mac_phy.rf_ver == 0x2050) { if (mac->mac_phy.rf_rev < 6) hf |= BWN_HF_FORCE_VCO_RECALC; if (mac->mac_phy.rf_rev == 6) hf |= BWN_HF_4318_TSSI; } if (sprom->bf_lo & BWN_BFL_CRYSTAL_NOSLOW) hf |= BWN_HF_SLOWCLOCK_REQ_OFF; if ((siba->siba_type == SIBA_TYPE_PCI) && (siba->siba_pci.spc_dev->sd_id.sd_rev <= 10)) hf |= BWN_HF_PCI_SLOWCLOCK_WORKAROUND; hf &= ~BWN_HF_SKIP_CFP_UPDATE; bwn_hf_write(mac, hf); bwn_set_txretry(mac, BWN_RETRY_SHORT, BWN_RETRY_LONG); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_SHORT_RETRY_FALLBACK, 3); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_LONG_RETRY_FALLBACK, 2); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_MAXTIME, 1); bwn_rate_init(mac); bwn_set_phytxctl(mac); bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_CONT_MIN, (mac->mac_phy.type == BWN_PHYTYPE_B) ? 0x1f : 0xf); bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_CONT_MAX, 0x3ff); if (siba->siba_type == SIBA_TYPE_PCMCIA || bwn_usedma == 0) bwn_pio_init(mac); else bwn_dma_init(mac); if (error) goto fail1; bwn_wme_init(mac); bwn_spu_setdelay(mac, 1); bwn_bt_enable(mac); siba_powerup(siba, !(sprom->bf_lo & BWN_BFL_CRYSTAL_NOSLOW)); bwn_set_macaddr(mac); bwn_crypt_init(mac); /* XXX LED initializatin */ mac->mac_status = BWN_MAC_STATUS_INITED; return (error); fail1: bwn_chip_exit(mac); fail0: siba_powerdown(siba); KASSERT(mac->mac_status == BWN_MAC_STATUS_UNINIT, ("%s:%d: fail", __func__, __LINE__)); return (error); } static void bwn_core_start(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; uint32_t tmp; KASSERT(mac->mac_status == BWN_MAC_STATUS_INITED, ("%s:%d: fail", __func__, __LINE__)); if (mac->mac_sd->sd_id.sd_rev < 5) return; while (1) { tmp = BWN_READ_4(mac, BWN_XMITSTAT_0); if (!(tmp & 0x00000001)) break; tmp = BWN_READ_4(mac, BWN_XMITSTAT_1); } bwn_mac_enable(mac); BWN_WRITE_4(mac, BWN_INTR_MASK, mac->mac_intr_mask); callout_reset(&sc->sc_task_ch, hz * 15, bwn_tasks, mac); mac->mac_status = BWN_MAC_STATUS_STARTED; } static void bwn_core_exit(struct bwn_mac *mac) { - struct bwn_softc *sc = mac->mac_sc; uint32_t macctl; - BWN_ASSERT_LOCKED(sc); + BWN_ASSERT_LOCKED(mac->mac_sc); KASSERT(mac->mac_status <= BWN_MAC_STATUS_INITED, ("%s:%d: fail", __func__, __LINE__)); if (mac->mac_status != BWN_MAC_STATUS_INITED) return; mac->mac_status = BWN_MAC_STATUS_UNINIT; macctl = BWN_READ_4(mac, BWN_MACCTL); macctl &= ~BWN_MACCTL_MCODE_RUN; macctl |= BWN_MACCTL_MCODE_JMP0; BWN_WRITE_4(mac, BWN_MACCTL, macctl); bwn_dma_stop(mac); bwn_pio_stop(mac); bwn_chip_exit(mac); mac->mac_phy.switch_analog(mac, 0); siba_dev_down(mac->mac_sd, 0); siba_powerdown(mac->mac_sd->sd_bus); } static void bwn_fix_imcfglobug(struct bwn_mac *mac) { struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; uint32_t tmp; if (siba->siba_pci.spc_dev == NULL) return; if (siba->siba_pci.spc_dev->sd_id.sd_device != SIBA_DEVID_PCI || siba->siba_pci.spc_dev->sd_id.sd_rev > 5) return; tmp = siba_read_4(sd, SIBA_IMCFGLO) & ~(SIBA_IMCFGLO_REQTO | SIBA_IMCFGLO_SERTO); switch (siba->siba_type) { case SIBA_TYPE_PCI: case SIBA_TYPE_PCMCIA: tmp |= 0x32; break; case SIBA_TYPE_SSB: tmp |= 0x53; break; } siba_write_4(sd, SIBA_IMCFGLO, tmp); } static void bwn_bt_disable(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; (void)sc; /* XXX do nothing yet */ } static int bwn_chip_init(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; uint32_t macctl; int error; macctl = BWN_MACCTL_IHR_ON | BWN_MACCTL_SHM_ON | BWN_MACCTL_STA; if (phy->gmode) macctl |= BWN_MACCTL_GMODE; BWN_WRITE_4(mac, BWN_MACCTL, macctl); error = bwn_fw_fillinfo(mac); if (error) return (error); error = bwn_fw_loaducode(mac); if (error) return (error); error = bwn_gpio_init(mac); if (error) return (error); error = bwn_fw_loadinitvals(mac); if (error) { bwn_gpio_cleanup(mac); return (error); } phy->switch_analog(mac, 1); error = bwn_phy_init(mac); if (error) { bwn_gpio_cleanup(mac); return (error); } if (phy->set_im) phy->set_im(mac, BWN_IMMODE_NONE); if (phy->set_antenna) phy->set_antenna(mac, BWN_ANT_DEFAULT); bwn_set_txantenna(mac, BWN_ANT_DEFAULT); if (phy->type == BWN_PHYTYPE_B) BWN_WRITE_2(mac, 0x005e, BWN_READ_2(mac, 0x005e) | 0x0004); BWN_WRITE_4(mac, 0x0100, 0x01000000); if (mac->mac_sd->sd_id.sd_rev < 5) BWN_WRITE_4(mac, 0x010c, 0x01000000); BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_STA); BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_STA); bwn_shm_write_2(mac, BWN_SHARED, 0x0074, 0x0000); bwn_set_opmode(mac); if (mac->mac_sd->sd_id.sd_rev < 3) { BWN_WRITE_2(mac, 0x060e, 0x0000); BWN_WRITE_2(mac, 0x0610, 0x8000); BWN_WRITE_2(mac, 0x0604, 0x0000); BWN_WRITE_2(mac, 0x0606, 0x0200); } else { BWN_WRITE_4(mac, 0x0188, 0x80000000); BWN_WRITE_4(mac, 0x018c, 0x02000000); } BWN_WRITE_4(mac, BWN_INTR_REASON, 0x00004000); BWN_WRITE_4(mac, BWN_DMA0_INTR_MASK, 0x0001dc00); BWN_WRITE_4(mac, BWN_DMA1_INTR_MASK, 0x0000dc00); BWN_WRITE_4(mac, BWN_DMA2_INTR_MASK, 0x0000dc00); BWN_WRITE_4(mac, BWN_DMA3_INTR_MASK, 0x0001dc00); BWN_WRITE_4(mac, BWN_DMA4_INTR_MASK, 0x0000dc00); BWN_WRITE_4(mac, BWN_DMA5_INTR_MASK, 0x0000dc00); siba_write_4(mac->mac_sd, SIBA_TGSLOW, siba_read_4(mac->mac_sd, SIBA_TGSLOW) | 0x00100000); BWN_WRITE_2(mac, BWN_POWERUP_DELAY, mac->mac_sd->sd_bus->siba_cc.scc_powerup_delay); return (error); } /* read hostflags */ static uint64_t bwn_hf_read(struct bwn_mac *mac) { uint64_t ret; ret = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFHI); ret <<= 16; ret |= bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFMI); ret <<= 16; ret |= bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFLO); return (ret); } static void bwn_hf_write(struct bwn_mac *mac, uint64_t value) { bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_HFLO, (value & 0x00000000ffffull)); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_HFMI, (value & 0x0000ffff0000ull) >> 16); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_HFHI, (value & 0xffff00000000ULL) >> 32); } static void bwn_set_txretry(struct bwn_mac *mac, int s, int l) { bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_SHORT_RETRY, MIN(s, 0xf)); bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_LONG_RETRY, MIN(l, 0xf)); } static void bwn_rate_init(struct bwn_mac *mac) { switch (mac->mac_phy.type) { case BWN_PHYTYPE_A: case BWN_PHYTYPE_G: case BWN_PHYTYPE_LP: case BWN_PHYTYPE_N: bwn_rate_write(mac, BWN_OFDM_RATE_6MB, 1); bwn_rate_write(mac, BWN_OFDM_RATE_12MB, 1); bwn_rate_write(mac, BWN_OFDM_RATE_18MB, 1); bwn_rate_write(mac, BWN_OFDM_RATE_24MB, 1); bwn_rate_write(mac, BWN_OFDM_RATE_36MB, 1); bwn_rate_write(mac, BWN_OFDM_RATE_48MB, 1); bwn_rate_write(mac, BWN_OFDM_RATE_54MB, 1); if (mac->mac_phy.type == BWN_PHYTYPE_A) break; /* FALLTHROUGH */ case BWN_PHYTYPE_B: bwn_rate_write(mac, BWN_CCK_RATE_1MB, 0); bwn_rate_write(mac, BWN_CCK_RATE_2MB, 0); bwn_rate_write(mac, BWN_CCK_RATE_5MB, 0); bwn_rate_write(mac, BWN_CCK_RATE_11MB, 0); break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } } static void bwn_rate_write(struct bwn_mac *mac, uint16_t rate, int ofdm) { uint16_t offset; if (ofdm) { offset = 0x480; offset += (bwn_plcp_getofdm(rate) & 0x000f) * 2; } else { offset = 0x4c0; offset += (bwn_plcp_getcck(rate) & 0x000f) * 2; } bwn_shm_write_2(mac, BWN_SHARED, offset + 0x20, bwn_shm_read_2(mac, BWN_SHARED, offset)); } static uint8_t bwn_plcp_getcck(const uint8_t bitrate) { switch (bitrate) { case BWN_CCK_RATE_1MB: return (0x0a); case BWN_CCK_RATE_2MB: return (0x14); case BWN_CCK_RATE_5MB: return (0x37); case BWN_CCK_RATE_11MB: return (0x6e); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (0); } static uint8_t bwn_plcp_getofdm(const uint8_t bitrate) { switch (bitrate) { case BWN_OFDM_RATE_6MB: return (0xb); case BWN_OFDM_RATE_9MB: return (0xf); case BWN_OFDM_RATE_12MB: return (0xa); case BWN_OFDM_RATE_18MB: return (0xe); case BWN_OFDM_RATE_24MB: return (0x9); case BWN_OFDM_RATE_36MB: return (0xd); case BWN_OFDM_RATE_48MB: return (0x8); case BWN_OFDM_RATE_54MB: return (0xc); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (0); } static void bwn_set_phytxctl(struct bwn_mac *mac) { uint16_t ctl; ctl = (BWN_TX_PHY_ENC_CCK | BWN_TX_PHY_ANT01AUTO | BWN_TX_PHY_TXPWR); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_BEACON_PHYCTL, ctl); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL, ctl); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL, ctl); } static void bwn_pio_init(struct bwn_mac *mac) { struct bwn_pio *pio = &mac->mac_method.pio; BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_BIGENDIAN); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_RX_PADOFFSET, 0); bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_BK], 0); bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_BE], 1); bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_VI], 2); bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_VO], 3); bwn_pio_set_txqueue(mac, &pio->mcast, 4); bwn_pio_setupqueue_rx(mac, &pio->rx, 0); } static void bwn_pio_set_txqueue(struct bwn_mac *mac, struct bwn_pio_txqueue *tq, int index) { struct bwn_pio_txpkt *tp; unsigned int i; tq->tq_base = bwn_pio_idx2base(mac, index) + BWN_PIO_TXQOFFSET(mac); tq->tq_index = index; tq->tq_free = BWN_PIO_MAX_TXPACKETS; if (mac->mac_sd->sd_id.sd_rev >= 8) tq->tq_size = 1920; else { tq->tq_size = bwn_pio_read_2(mac, tq, BWN_PIO_TXQBUFSIZE); tq->tq_size -= 80; } TAILQ_INIT(&tq->tq_pktlist); for (i = 0; i < N(tq->tq_pkts); i++) { tp = &(tq->tq_pkts[i]); tp->tp_index = i; tp->tp_queue = tq; TAILQ_INSERT_TAIL(&tq->tq_pktlist, tp, tp_list); } } static uint16_t bwn_pio_idx2base(struct bwn_mac *mac, int index) { struct bwn_softc *sc = mac->mac_sc; static const uint16_t bases[] = { BWN_PIO_BASE0, BWN_PIO_BASE1, BWN_PIO_BASE2, BWN_PIO_BASE3, BWN_PIO_BASE4, BWN_PIO_BASE5, BWN_PIO_BASE6, BWN_PIO_BASE7, }; static const uint16_t bases_rev11[] = { BWN_PIO11_BASE0, BWN_PIO11_BASE1, BWN_PIO11_BASE2, BWN_PIO11_BASE3, BWN_PIO11_BASE4, BWN_PIO11_BASE5, }; if (mac->mac_sd->sd_id.sd_rev >= 11) { if (index >= N(bases_rev11)) device_printf(sc->sc_dev, "%s: warning\n", __func__); return (bases_rev11[index]); } if (index >= N(bases)) device_printf(sc->sc_dev, "%s: warning\n", __func__); return (bases[index]); } static void bwn_pio_setupqueue_rx(struct bwn_mac *mac, struct bwn_pio_rxqueue *prq, int index) { prq->prq_mac = mac; prq->prq_rev = mac->mac_sd->sd_id.sd_rev; prq->prq_base = bwn_pio_idx2base(mac, index) + BWN_PIO_RXQOFFSET(mac); bwn_dma_rxdirectfifo(mac, index, 1); } static void bwn_destroy_pioqueue_tx(struct bwn_pio_txqueue *tq) { if (tq == NULL) return; bwn_pio_cancel_tx_packets(tq); } static void bwn_destroy_queue_tx(struct bwn_pio_txqueue *pio) { bwn_destroy_pioqueue_tx(pio); } static uint16_t bwn_pio_read_2(struct bwn_mac *mac, struct bwn_pio_txqueue *tq, uint16_t offset) { return (BWN_READ_2(mac, tq->tq_base + offset)); } static void bwn_dma_rxdirectfifo(struct bwn_mac *mac, int idx, uint8_t enable) { uint32_t ctl; int type; uint16_t base; type = bwn_dma_mask2type(bwn_dma_mask(mac)); base = bwn_dma_base(type, idx); if (type == BWN_DMA_64BIT) { ctl = BWN_READ_4(mac, base + BWN_DMA64_RXCTL); ctl &= ~BWN_DMA64_RXDIRECTFIFO; if (enable) ctl |= BWN_DMA64_RXDIRECTFIFO; BWN_WRITE_4(mac, base + BWN_DMA64_RXCTL, ctl); } else { ctl = BWN_READ_4(mac, base + BWN_DMA32_RXCTL); ctl &= ~BWN_DMA32_RXDIRECTFIFO; if (enable) ctl |= BWN_DMA32_RXDIRECTFIFO; BWN_WRITE_4(mac, base + BWN_DMA32_RXCTL, ctl); } } static uint64_t bwn_dma_mask(struct bwn_mac *mac) { uint32_t tmp; uint16_t base; tmp = BWN_READ_4(mac, SIBA_TGSHIGH); if (tmp & SIBA_TGSHIGH_DMA64) return (BWN_DMA_BIT_MASK(64)); base = bwn_dma_base(0, 0); BWN_WRITE_4(mac, base + BWN_DMA32_TXCTL, BWN_DMA32_TXADDREXT_MASK); tmp = BWN_READ_4(mac, base + BWN_DMA32_TXCTL); if (tmp & BWN_DMA32_TXADDREXT_MASK) return (BWN_DMA_BIT_MASK(32)); return (BWN_DMA_BIT_MASK(30)); } static int bwn_dma_mask2type(uint64_t dmamask) { if (dmamask == BWN_DMA_BIT_MASK(30)) return (BWN_DMA_30BIT); if (dmamask == BWN_DMA_BIT_MASK(32)) return (BWN_DMA_32BIT); if (dmamask == BWN_DMA_BIT_MASK(64)) return (BWN_DMA_64BIT); KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (BWN_DMA_30BIT); } static void bwn_pio_cancel_tx_packets(struct bwn_pio_txqueue *tq) { struct bwn_pio_txpkt *tp; unsigned int i; for (i = 0; i < N(tq->tq_pkts); i++) { tp = &(tq->tq_pkts[i]); if (tp->tp_m) { m_freem(tp->tp_m); tp->tp_m = NULL; } } } static uint16_t bwn_dma_base(int type, int controller_idx) { static const uint16_t map64[] = { BWN_DMA64_BASE0, BWN_DMA64_BASE1, BWN_DMA64_BASE2, BWN_DMA64_BASE3, BWN_DMA64_BASE4, BWN_DMA64_BASE5, }; static const uint16_t map32[] = { BWN_DMA32_BASE0, BWN_DMA32_BASE1, BWN_DMA32_BASE2, BWN_DMA32_BASE3, BWN_DMA32_BASE4, BWN_DMA32_BASE5, }; if (type == BWN_DMA_64BIT) { KASSERT(controller_idx >= 0 && controller_idx < N(map64), ("%s:%d: fail", __func__, __LINE__)); return (map64[controller_idx]); } KASSERT(controller_idx >= 0 && controller_idx < N(map32), ("%s:%d: fail", __func__, __LINE__)); return (map32[controller_idx]); } static void bwn_dma_init(struct bwn_mac *mac) { struct bwn_dma *dma = &mac->mac_method.dma; /* setup TX DMA channels. */ bwn_dma_setup(dma->wme[WME_AC_BK]); bwn_dma_setup(dma->wme[WME_AC_BE]); bwn_dma_setup(dma->wme[WME_AC_VI]); bwn_dma_setup(dma->wme[WME_AC_VO]); bwn_dma_setup(dma->mcast); /* setup RX DMA channel. */ bwn_dma_setup(dma->rx); } static struct bwn_dma_ring * bwn_dma_ringsetup(struct bwn_mac *mac, int controller_index, int for_tx, int type) { struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_dma_ring *dr; struct bwn_dmadesc_generic *desc; struct bwn_dmadesc_meta *mt; struct bwn_softc *sc = mac->mac_sc; int error, i; dr = malloc(sizeof(*dr), M_DEVBUF, M_NOWAIT | M_ZERO); if (dr == NULL) goto out; dr->dr_numslots = BWN_RXRING_SLOTS; if (for_tx) dr->dr_numslots = BWN_TXRING_SLOTS; dr->dr_meta = malloc(dr->dr_numslots * sizeof(struct bwn_dmadesc_meta), M_DEVBUF, M_NOWAIT | M_ZERO); if (dr->dr_meta == NULL) goto fail0; dr->dr_type = type; dr->dr_mac = mac; dr->dr_base = bwn_dma_base(type, controller_index); dr->dr_index = controller_index; if (type == BWN_DMA_64BIT) { dr->getdesc = bwn_dma_64_getdesc; dr->setdesc = bwn_dma_64_setdesc; dr->start_transfer = bwn_dma_64_start_transfer; dr->suspend = bwn_dma_64_suspend; dr->resume = bwn_dma_64_resume; dr->get_curslot = bwn_dma_64_get_curslot; dr->set_curslot = bwn_dma_64_set_curslot; } else { dr->getdesc = bwn_dma_32_getdesc; dr->setdesc = bwn_dma_32_setdesc; dr->start_transfer = bwn_dma_32_start_transfer; dr->suspend = bwn_dma_32_suspend; dr->resume = bwn_dma_32_resume; dr->get_curslot = bwn_dma_32_get_curslot; dr->set_curslot = bwn_dma_32_set_curslot; } if (for_tx) { dr->dr_tx = 1; dr->dr_curslot = -1; } else { if (dr->dr_index == 0) { dr->dr_rx_bufsize = BWN_DMA0_RX_BUFFERSIZE; dr->dr_frameoffset = BWN_DMA0_RX_FRAMEOFFSET; } else KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } error = bwn_dma_allocringmemory(dr); if (error) goto fail2; if (for_tx) { /* * Assumption: BWN_TXRING_SLOTS can be divided by * BWN_TX_SLOTS_PER_FRAME */ KASSERT(BWN_TXRING_SLOTS % BWN_TX_SLOTS_PER_FRAME == 0, ("%s:%d: fail", __func__, __LINE__)); dr->dr_txhdr_cache = malloc((dr->dr_numslots / BWN_TX_SLOTS_PER_FRAME) * BWN_HDRSIZE(mac), M_DEVBUF, M_NOWAIT | M_ZERO); KASSERT(dr->dr_txhdr_cache != NULL, ("%s:%d: fail", __func__, __LINE__)); /* * Create TX ring DMA stuffs */ error = bus_dma_tag_create(dma->parent_dtag, BWN_ALIGN, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BWN_HDRSIZE(mac), 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &dr->dr_txring_dtag); if (error) { device_printf(sc->sc_dev, "can't create TX ring DMA tag: TODO frees\n"); goto fail1; } for (i = 0; i < dr->dr_numslots; i += 2) { dr->getdesc(dr, i, &desc, &mt); mt->mt_txtype = BWN_DMADESC_METATYPE_HEADER; mt->mt_m = NULL; mt->mt_ni = NULL; mt->mt_islast = 0; error = bus_dmamap_create(dr->dr_txring_dtag, 0, &mt->mt_dmap); if (error) { device_printf(sc->sc_dev, "can't create RX buf DMA map\n"); goto fail1; } dr->getdesc(dr, i + 1, &desc, &mt); mt->mt_txtype = BWN_DMADESC_METATYPE_BODY; mt->mt_m = NULL; mt->mt_ni = NULL; mt->mt_islast = 1; error = bus_dmamap_create(dma->txbuf_dtag, 0, &mt->mt_dmap); if (error) { device_printf(sc->sc_dev, "can't create RX buf DMA map\n"); goto fail1; } } } else { error = bus_dmamap_create(dma->rxbuf_dtag, 0, &dr->dr_spare_dmap); if (error) { device_printf(sc->sc_dev, "can't create RX buf DMA map\n"); goto out; /* XXX wrong! */ } for (i = 0; i < dr->dr_numslots; i++) { dr->getdesc(dr, i, &desc, &mt); error = bus_dmamap_create(dma->rxbuf_dtag, 0, &mt->mt_dmap); if (error) { device_printf(sc->sc_dev, "can't create RX buf DMA map\n"); goto out; /* XXX wrong! */ } error = bwn_dma_newbuf(dr, desc, mt, 1); if (error) { device_printf(sc->sc_dev, "failed to allocate RX buf\n"); goto out; /* XXX wrong! */ } } bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap, BUS_DMASYNC_PREWRITE); dr->dr_usedslot = dr->dr_numslots; } out: return (dr); fail2: free(dr->dr_txhdr_cache, M_DEVBUF); fail1: free(dr->dr_meta, M_DEVBUF); fail0: free(dr, M_DEVBUF); return (NULL); } static void bwn_dma_ringfree(struct bwn_dma_ring **dr) { if (dr == NULL) return; bwn_dma_free_descbufs(*dr); bwn_dma_free_ringmemory(*dr); free((*dr)->dr_txhdr_cache, M_DEVBUF); free((*dr)->dr_meta, M_DEVBUF); free(*dr, M_DEVBUF); *dr = NULL; } static void bwn_dma_32_getdesc(struct bwn_dma_ring *dr, int slot, struct bwn_dmadesc_generic **gdesc, struct bwn_dmadesc_meta **meta) { struct bwn_dmadesc32 *desc; *meta = &(dr->dr_meta[slot]); desc = dr->dr_ring_descbase; desc = &(desc[slot]); *gdesc = (struct bwn_dmadesc_generic *)desc; } static void bwn_dma_32_setdesc(struct bwn_dma_ring *dr, struct bwn_dmadesc_generic *desc, bus_addr_t dmaaddr, uint16_t bufsize, int start, int end, int irq) { struct bwn_dmadesc32 *descbase = dr->dr_ring_descbase; uint32_t addr, addrext, ctl; int slot; slot = (int)(&(desc->dma.dma32) - descbase); KASSERT(slot >= 0 && slot < dr->dr_numslots, ("%s:%d: fail", __func__, __LINE__)); addr = (uint32_t) (dmaaddr & ~SIBA_DMA_TRANSLATION_MASK); addrext = (uint32_t) (dmaaddr & SIBA_DMA_TRANSLATION_MASK) >> 30; addr |= siba_dma_translation(dr->dr_mac->mac_sd); ctl = bufsize & BWN_DMA32_DCTL_BYTECNT; if (slot == dr->dr_numslots - 1) ctl |= BWN_DMA32_DCTL_DTABLEEND; if (start) ctl |= BWN_DMA32_DCTL_FRAMESTART; if (end) ctl |= BWN_DMA32_DCTL_FRAMEEND; if (irq) ctl |= BWN_DMA32_DCTL_IRQ; ctl |= (addrext << BWN_DMA32_DCTL_ADDREXT_SHIFT) & BWN_DMA32_DCTL_ADDREXT_MASK; desc->dma.dma32.control = htole32(ctl); desc->dma.dma32.address = htole32(addr); } static void bwn_dma_32_start_transfer(struct bwn_dma_ring *dr, int slot) { BWN_DMA_WRITE(dr, BWN_DMA32_TXINDEX, (uint32_t)(slot * sizeof(struct bwn_dmadesc32))); } static void bwn_dma_32_suspend(struct bwn_dma_ring *dr) { BWN_DMA_WRITE(dr, BWN_DMA32_TXCTL, BWN_DMA_READ(dr, BWN_DMA32_TXCTL) | BWN_DMA32_TXSUSPEND); } static void bwn_dma_32_resume(struct bwn_dma_ring *dr) { BWN_DMA_WRITE(dr, BWN_DMA32_TXCTL, BWN_DMA_READ(dr, BWN_DMA32_TXCTL) & ~BWN_DMA32_TXSUSPEND); } static int bwn_dma_32_get_curslot(struct bwn_dma_ring *dr) { uint32_t val; val = BWN_DMA_READ(dr, BWN_DMA32_RXSTATUS); val &= BWN_DMA32_RXDPTR; return (val / sizeof(struct bwn_dmadesc32)); } static void bwn_dma_32_set_curslot(struct bwn_dma_ring *dr, int slot) { BWN_DMA_WRITE(dr, BWN_DMA32_RXINDEX, (uint32_t) (slot * sizeof(struct bwn_dmadesc32))); } static void bwn_dma_64_getdesc(struct bwn_dma_ring *dr, int slot, struct bwn_dmadesc_generic **gdesc, struct bwn_dmadesc_meta **meta) { struct bwn_dmadesc64 *desc; *meta = &(dr->dr_meta[slot]); desc = dr->dr_ring_descbase; desc = &(desc[slot]); *gdesc = (struct bwn_dmadesc_generic *)desc; } static void bwn_dma_64_setdesc(struct bwn_dma_ring *dr, struct bwn_dmadesc_generic *desc, bus_addr_t dmaaddr, uint16_t bufsize, int start, int end, int irq) { struct bwn_dmadesc64 *descbase = dr->dr_ring_descbase; int slot; uint32_t ctl0 = 0, ctl1 = 0; uint32_t addrlo, addrhi; uint32_t addrext; slot = (int)(&(desc->dma.dma64) - descbase); KASSERT(slot >= 0 && slot < dr->dr_numslots, ("%s:%d: fail", __func__, __LINE__)); addrlo = (uint32_t) (dmaaddr & 0xffffffff); addrhi = (((uint64_t) dmaaddr >> 32) & ~SIBA_DMA_TRANSLATION_MASK); addrext = (((uint64_t) dmaaddr >> 32) & SIBA_DMA_TRANSLATION_MASK) >> 30; addrhi |= (siba_dma_translation(dr->dr_mac->mac_sd) << 1); if (slot == dr->dr_numslots - 1) ctl0 |= BWN_DMA64_DCTL0_DTABLEEND; if (start) ctl0 |= BWN_DMA64_DCTL0_FRAMESTART; if (end) ctl0 |= BWN_DMA64_DCTL0_FRAMEEND; if (irq) ctl0 |= BWN_DMA64_DCTL0_IRQ; ctl1 |= bufsize & BWN_DMA64_DCTL1_BYTECNT; ctl1 |= (addrext << BWN_DMA64_DCTL1_ADDREXT_SHIFT) & BWN_DMA64_DCTL1_ADDREXT_MASK; desc->dma.dma64.control0 = htole32(ctl0); desc->dma.dma64.control1 = htole32(ctl1); desc->dma.dma64.address_low = htole32(addrlo); desc->dma.dma64.address_high = htole32(addrhi); } static void bwn_dma_64_start_transfer(struct bwn_dma_ring *dr, int slot) { BWN_DMA_WRITE(dr, BWN_DMA64_TXINDEX, (uint32_t)(slot * sizeof(struct bwn_dmadesc64))); } static void bwn_dma_64_suspend(struct bwn_dma_ring *dr) { BWN_DMA_WRITE(dr, BWN_DMA64_TXCTL, BWN_DMA_READ(dr, BWN_DMA64_TXCTL) | BWN_DMA64_TXSUSPEND); } static void bwn_dma_64_resume(struct bwn_dma_ring *dr) { BWN_DMA_WRITE(dr, BWN_DMA64_TXCTL, BWN_DMA_READ(dr, BWN_DMA64_TXCTL) & ~BWN_DMA64_TXSUSPEND); } static int bwn_dma_64_get_curslot(struct bwn_dma_ring *dr) { uint32_t val; val = BWN_DMA_READ(dr, BWN_DMA64_RXSTATUS); val &= BWN_DMA64_RXSTATDPTR; return (val / sizeof(struct bwn_dmadesc64)); } static void bwn_dma_64_set_curslot(struct bwn_dma_ring *dr, int slot) { BWN_DMA_WRITE(dr, BWN_DMA64_RXINDEX, (uint32_t)(slot * sizeof(struct bwn_dmadesc64))); } static int bwn_dma_allocringmemory(struct bwn_dma_ring *dr) { struct bwn_mac *mac = dr->dr_mac; struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_softc *sc = mac->mac_sc; int error; error = bus_dma_tag_create(dma->parent_dtag, BWN_ALIGN, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BWN_DMA_RINGMEMSIZE, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &dr->dr_ring_dtag); if (error) { device_printf(sc->sc_dev, "can't create TX ring DMA tag: TODO frees\n"); return (-1); } error = bus_dmamem_alloc(dr->dr_ring_dtag, &dr->dr_ring_descbase, BUS_DMA_WAITOK | BUS_DMA_ZERO, &dr->dr_ring_dmap); if (error) { device_printf(sc->sc_dev, "can't allocate DMA mem: TODO frees\n"); return (-1); } error = bus_dmamap_load(dr->dr_ring_dtag, dr->dr_ring_dmap, dr->dr_ring_descbase, BWN_DMA_RINGMEMSIZE, bwn_dma_ring_addr, &dr->dr_ring_dmabase, BUS_DMA_NOWAIT); if (error) { device_printf(sc->sc_dev, "can't load DMA mem: TODO free\n"); return (-1); } return (0); } static void bwn_dma_setup(struct bwn_dma_ring *dr) { uint64_t ring64; uint32_t addrext, ring32, value; uint32_t trans = siba_dma_translation(dr->dr_mac->mac_sd); if (dr->dr_tx) { dr->dr_curslot = -1; if (dr->dr_type == BWN_DMA_64BIT) { ring64 = (uint64_t)(dr->dr_ring_dmabase); addrext = ((ring64 >> 32) & SIBA_DMA_TRANSLATION_MASK) >> 30; value = BWN_DMA64_TXENABLE; value |= (addrext << BWN_DMA64_TXADDREXT_SHIFT) & BWN_DMA64_TXADDREXT_MASK; BWN_DMA_WRITE(dr, BWN_DMA64_TXCTL, value); BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGLO, (ring64 & 0xffffffff)); BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGHI, ((ring64 >> 32) & ~SIBA_DMA_TRANSLATION_MASK) | (trans << 1)); } else { ring32 = (uint32_t)(dr->dr_ring_dmabase); addrext = (ring32 & SIBA_DMA_TRANSLATION_MASK) >> 30; value = BWN_DMA32_TXENABLE; value |= (addrext << BWN_DMA32_TXADDREXT_SHIFT) & BWN_DMA32_TXADDREXT_MASK; BWN_DMA_WRITE(dr, BWN_DMA32_TXCTL, value); BWN_DMA_WRITE(dr, BWN_DMA32_TXRING, (ring32 & ~SIBA_DMA_TRANSLATION_MASK) | trans); } return; } /* * set for RX */ dr->dr_usedslot = dr->dr_numslots; if (dr->dr_type == BWN_DMA_64BIT) { ring64 = (uint64_t)(dr->dr_ring_dmabase); addrext = ((ring64 >> 32) & SIBA_DMA_TRANSLATION_MASK) >> 30; value = (dr->dr_frameoffset << BWN_DMA64_RXFROFF_SHIFT); value |= BWN_DMA64_RXENABLE; value |= (addrext << BWN_DMA64_RXADDREXT_SHIFT) & BWN_DMA64_RXADDREXT_MASK; BWN_DMA_WRITE(dr, BWN_DMA64_RXCTL, value); BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGLO, (ring64 & 0xffffffff)); BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGHI, ((ring64 >> 32) & ~SIBA_DMA_TRANSLATION_MASK) | (trans << 1)); BWN_DMA_WRITE(dr, BWN_DMA64_RXINDEX, dr->dr_numslots * sizeof(struct bwn_dmadesc64)); } else { ring32 = (uint32_t)(dr->dr_ring_dmabase); addrext = (ring32 & SIBA_DMA_TRANSLATION_MASK) >> 30; value = (dr->dr_frameoffset << BWN_DMA32_RXFROFF_SHIFT); value |= BWN_DMA32_RXENABLE; value |= (addrext << BWN_DMA32_RXADDREXT_SHIFT) & BWN_DMA32_RXADDREXT_MASK; BWN_DMA_WRITE(dr, BWN_DMA32_RXCTL, value); BWN_DMA_WRITE(dr, BWN_DMA32_RXRING, (ring32 & ~SIBA_DMA_TRANSLATION_MASK) | trans); BWN_DMA_WRITE(dr, BWN_DMA32_RXINDEX, dr->dr_numslots * sizeof(struct bwn_dmadesc32)); } } static void bwn_dma_free_ringmemory(struct bwn_dma_ring *dr) { bus_dmamap_unload(dr->dr_ring_dtag, dr->dr_ring_dmap); bus_dmamem_free(dr->dr_ring_dtag, dr->dr_ring_descbase, dr->dr_ring_dmap); } static void bwn_dma_cleanup(struct bwn_dma_ring *dr) { if (dr->dr_tx) { bwn_dma_tx_reset(dr->dr_mac, dr->dr_base, dr->dr_type); if (dr->dr_type == BWN_DMA_64BIT) { BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGLO, 0); BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGHI, 0); } else BWN_DMA_WRITE(dr, BWN_DMA32_TXRING, 0); } else { bwn_dma_rx_reset(dr->dr_mac, dr->dr_base, dr->dr_type); if (dr->dr_type == BWN_DMA_64BIT) { BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGLO, 0); BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGHI, 0); } else BWN_DMA_WRITE(dr, BWN_DMA32_RXRING, 0); } } static void bwn_dma_free_descbufs(struct bwn_dma_ring *dr) { struct bwn_dmadesc_generic *desc; struct bwn_dmadesc_meta *meta; struct bwn_mac *mac = dr->dr_mac; struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_softc *sc = mac->mac_sc; int i; if (!dr->dr_usedslot) return; for (i = 0; i < dr->dr_numslots; i++) { dr->getdesc(dr, i, &desc, &meta); if (meta->mt_m == NULL) { if (!dr->dr_tx) device_printf(sc->sc_dev, "%s: not TX?\n", __func__); continue; } if (dr->dr_tx) { if (meta->mt_txtype == BWN_DMADESC_METATYPE_HEADER) bus_dmamap_unload(dr->dr_txring_dtag, meta->mt_dmap); else if (meta->mt_txtype == BWN_DMADESC_METATYPE_BODY) bus_dmamap_unload(dma->txbuf_dtag, meta->mt_dmap); } else bus_dmamap_unload(dma->rxbuf_dtag, meta->mt_dmap); bwn_dma_free_descbuf(dr, meta); } } static int bwn_dma_tx_reset(struct bwn_mac *mac, uint16_t base, int type) { struct bwn_softc *sc = mac->mac_sc; uint32_t value; int i; uint16_t offset; for (i = 0; i < 10; i++) { offset = (type == BWN_DMA_64BIT) ? BWN_DMA64_TXSTATUS : BWN_DMA32_TXSTATUS; value = BWN_READ_4(mac, base + offset); if (type == BWN_DMA_64BIT) { value &= BWN_DMA64_TXSTAT; if (value == BWN_DMA64_TXSTAT_DISABLED || value == BWN_DMA64_TXSTAT_IDLEWAIT || value == BWN_DMA64_TXSTAT_STOPPED) break; } else { value &= BWN_DMA32_TXSTATE; if (value == BWN_DMA32_TXSTAT_DISABLED || value == BWN_DMA32_TXSTAT_IDLEWAIT || value == BWN_DMA32_TXSTAT_STOPPED) break; } DELAY(1000); } offset = (type == BWN_DMA_64BIT) ? BWN_DMA64_TXCTL : BWN_DMA32_TXCTL; BWN_WRITE_4(mac, base + offset, 0); for (i = 0; i < 10; i++) { offset = (type == BWN_DMA_64BIT) ? BWN_DMA64_TXSTATUS : BWN_DMA32_TXSTATUS; value = BWN_READ_4(mac, base + offset); if (type == BWN_DMA_64BIT) { value &= BWN_DMA64_TXSTAT; if (value == BWN_DMA64_TXSTAT_DISABLED) { i = -1; break; } } else { value &= BWN_DMA32_TXSTATE; if (value == BWN_DMA32_TXSTAT_DISABLED) { i = -1; break; } } DELAY(1000); } if (i != -1) { device_printf(sc->sc_dev, "%s: timed out\n", __func__); return (ENODEV); } DELAY(1000); return (0); } static int bwn_dma_rx_reset(struct bwn_mac *mac, uint16_t base, int type) { struct bwn_softc *sc = mac->mac_sc; uint32_t value; int i; uint16_t offset; offset = (type == BWN_DMA_64BIT) ? BWN_DMA64_RXCTL : BWN_DMA32_RXCTL; BWN_WRITE_4(mac, base + offset, 0); for (i = 0; i < 10; i++) { offset = (type == BWN_DMA_64BIT) ? BWN_DMA64_RXSTATUS : BWN_DMA32_RXSTATUS; value = BWN_READ_4(mac, base + offset); if (type == BWN_DMA_64BIT) { value &= BWN_DMA64_RXSTAT; if (value == BWN_DMA64_RXSTAT_DISABLED) { i = -1; break; } } else { value &= BWN_DMA32_RXSTATE; if (value == BWN_DMA32_RXSTAT_DISABLED) { i = -1; break; } } DELAY(1000); } if (i != -1) { device_printf(sc->sc_dev, "%s: timed out\n", __func__); return (ENODEV); } return (0); } static void bwn_dma_free_descbuf(struct bwn_dma_ring *dr, struct bwn_dmadesc_meta *meta) { if (meta->mt_m != NULL) { m_freem(meta->mt_m); meta->mt_m = NULL; } if (meta->mt_ni != NULL) { ieee80211_free_node(meta->mt_ni); meta->mt_ni = NULL; } } static void bwn_dma_set_redzone(struct bwn_dma_ring *dr, struct mbuf *m) { struct bwn_rxhdr4 *rxhdr; unsigned char *frame; rxhdr = mtod(m, struct bwn_rxhdr4 *); rxhdr->frame_len = 0; KASSERT(dr->dr_rx_bufsize >= dr->dr_frameoffset + sizeof(struct bwn_plcp6) + 2, ("%s:%d: fail", __func__, __LINE__)); frame = mtod(m, char *) + dr->dr_frameoffset; memset(frame, 0xff, sizeof(struct bwn_plcp6) + 2 /* padding */); } static uint8_t bwn_dma_check_redzone(struct bwn_dma_ring *dr, struct mbuf *m) { unsigned char *f = mtod(m, char *) + dr->dr_frameoffset; return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xff); } static void bwn_wme_init(struct bwn_mac *mac) { bwn_wme_load(mac); /* enable WME support. */ bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_EDCF); BWN_WRITE_2(mac, BWN_IFSCTL, BWN_READ_2(mac, BWN_IFSCTL) | BWN_IFSCTL_USE_EDCF); } static void bwn_spu_setdelay(struct bwn_mac *mac, int idle) { struct bwn_softc *sc = mac->mac_sc; struct ieee80211com *ic = sc->sc_ifp->if_l2com; uint16_t delay; /* microsec */ delay = (mac->mac_phy.type == BWN_PHYTYPE_A) ? 3700 : 1050; if (ic->ic_opmode == IEEE80211_M_IBSS || idle) delay = 500; if ((mac->mac_phy.rf_ver == 0x2050) && (mac->mac_phy.rf_rev == 8)) delay = max(delay, (uint16_t)2400); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_SPU_WAKEUP, delay); } static void bwn_bt_enable(struct bwn_mac *mac) { struct siba_sprom *sprom = &mac->mac_sd->sd_bus->siba_sprom; uint64_t hf; if (bwn_bluetooth == 0) return; if ((sprom->bf_lo & BWN_BFL_BTCOEXIST) == 0) return; if (mac->mac_phy.type != BWN_PHYTYPE_B && !mac->mac_phy.gmode) return; hf = bwn_hf_read(mac); if (sprom->bf_lo & BWN_BFL_BTCMOD) hf |= BWN_HF_BT_COEXISTALT; else hf |= BWN_HF_BT_COEXIST; bwn_hf_write(mac, hf); } static void bwn_set_macaddr(struct bwn_mac *mac) { bwn_mac_write_bssid(mac); bwn_mac_setfilter(mac, BWN_MACFILTER_SELF, mac->mac_sc->sc_macaddr); } static void bwn_clear_keys(struct bwn_mac *mac) { int i; for (i = 0; i < mac->mac_max_nr_keys; i++) { KASSERT(i >= 0 && i < mac->mac_max_nr_keys, ("%s:%d: fail", __func__, __LINE__)); bwn_key_dowrite(mac, i, BWN_SEC_ALGO_NONE, NULL, BWN_SEC_KEYSIZE, NULL); if ((i <= 3) && !BWN_SEC_NEWAPI(mac)) { bwn_key_dowrite(mac, i + 4, BWN_SEC_ALGO_NONE, NULL, BWN_SEC_KEYSIZE, NULL); } mac->mac_key[i].keyconf = NULL; } } static void bwn_crypt_init(struct bwn_mac *mac) { mac->mac_max_nr_keys = (mac->mac_sd->sd_id.sd_rev >= 5) ? 58 : 20; KASSERT(mac->mac_max_nr_keys <= N(mac->mac_key), ("%s:%d: fail", __func__, __LINE__)); mac->mac_ktp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_KEY_TABLEP); mac->mac_ktp *= 2; if (mac->mac_sd->sd_id.sd_rev >= 5) { BWN_WRITE_2(mac, BWN_RCMTA_COUNT, mac->mac_max_nr_keys - 8); } bwn_clear_keys(mac); } static void bwn_chip_exit(struct bwn_mac *mac) { bwn_phy_exit(mac); bwn_gpio_cleanup(mac); } static int bwn_fw_fillinfo(struct bwn_mac *mac) { int error; error = bwn_fw_gets(mac, BWN_FWTYPE_DEFAULT); if (error == 0) return (0); error = bwn_fw_gets(mac, BWN_FWTYPE_OPENSOURCE); if (error == 0) return (0); return (error); } static int bwn_gpio_init(struct bwn_mac *mac) { struct siba_softc *bus = mac->mac_sd->sd_bus; struct siba_dev_softc *sd; uint32_t mask = 0x0000001f, set = 0x0000000f; BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_GPOUT_MASK); BWN_WRITE_2(mac, BWN_GPIO_MASK, BWN_READ_2(mac, BWN_GPIO_MASK) | 0x000f); if (bus->siba_chipid == 0x4301) { mask |= 0x0060; set |= 0x0060; } if (bus->siba_sprom.bf_lo & BWN_BFL_PACTRL) { BWN_WRITE_2(mac, BWN_GPIO_MASK, BWN_READ_2(mac, BWN_GPIO_MASK) | 0x0200); mask |= 0x0200; set |= 0x0200; } if (mac->mac_sd->sd_id.sd_rev >= 2) mask |= 0x0010; sd = (bus->siba_cc.scc_dev != NULL) ? bus->siba_cc.scc_dev : bus->siba_pci.spc_dev; if (sd == NULL) return (0); siba_write_4(sd, BWN_GPIOCTL, (siba_read_4(sd, BWN_GPIOCTL) & mask) | set); return (0); } static int bwn_fw_loadinitvals(struct bwn_mac *mac) { #define GETFWOFFSET(fwp, offset) \ ((const struct bwn_fwinitvals *)((const char *)fwp.fw->data + offset)) const size_t hdr_len = sizeof(struct bwn_fwhdr); const struct bwn_fwhdr *hdr; struct bwn_fw *fw = &mac->mac_fw; int error; hdr = (const struct bwn_fwhdr *)(fw->initvals.fw->data); error = bwn_fwinitvals_write(mac, GETFWOFFSET(fw->initvals, hdr_len), be32toh(hdr->size), fw->initvals.fw->datasize - hdr_len); if (error) return (error); if (fw->initvals_band.fw) { hdr = (const struct bwn_fwhdr *)(fw->initvals_band.fw->data); error = bwn_fwinitvals_write(mac, GETFWOFFSET(fw->initvals_band, hdr_len), be32toh(hdr->size), fw->initvals_band.fw->datasize - hdr_len); } return (error); #undef GETFWOFFSET } static int bwn_phy_init(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; int error; mac->mac_phy.chan = mac->mac_phy.get_default_chan(mac); mac->mac_phy.rf_onoff(mac, 1); error = mac->mac_phy.init(mac); if (error) { device_printf(sc->sc_dev, "PHY init failed\n"); goto fail0; } error = bwn_switch_channel(mac, mac->mac_phy.get_default_chan(mac)); if (error) { device_printf(sc->sc_dev, "failed to switch default channel\n"); goto fail1; } return (0); fail1: if (mac->mac_phy.exit) mac->mac_phy.exit(mac); fail0: mac->mac_phy.rf_onoff(mac, 0); return (error); } static void bwn_set_txantenna(struct bwn_mac *mac, int antenna) { uint16_t ant; uint16_t tmp; ant = bwn_ant2phy(antenna); /* For ACK/CTS */ tmp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL); tmp = (tmp & ~BWN_TX_PHY_ANT) | ant; bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL, tmp); /* For Probe Resposes */ tmp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL); tmp = (tmp & ~BWN_TX_PHY_ANT) | ant; bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL, tmp); } static void bwn_set_opmode(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t ctl; uint16_t cfp_pretbtt; ctl = BWN_READ_4(mac, BWN_MACCTL); ctl &= ~(BWN_MACCTL_HOSTAP | BWN_MACCTL_PASS_CTL | BWN_MACCTL_PASS_BADPLCP | BWN_MACCTL_PASS_BADFCS | BWN_MACCTL_PROMISC | BWN_MACCTL_BEACON_PROMISC); ctl |= BWN_MACCTL_STA; if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) ctl |= BWN_MACCTL_HOSTAP; else if (ic->ic_opmode == IEEE80211_M_IBSS) ctl &= ~BWN_MACCTL_STA; ctl |= sc->sc_filters; if (mac->mac_sd->sd_id.sd_rev <= 4) ctl |= BWN_MACCTL_PROMISC; BWN_WRITE_4(mac, BWN_MACCTL, ctl); cfp_pretbtt = 2; if ((ctl & BWN_MACCTL_STA) && !(ctl & BWN_MACCTL_HOSTAP)) { if (mac->mac_sd->sd_bus->siba_chipid == 0x4306 && mac->mac_sd->sd_bus->siba_chiprev == 3) cfp_pretbtt = 100; else cfp_pretbtt = 50; } BWN_WRITE_2(mac, 0x612, cfp_pretbtt); } static void bwn_gpio_cleanup(struct bwn_mac *mac) { struct siba_softc *bus = mac->mac_sd->sd_bus; struct siba_dev_softc *gpiodev, *pcidev = NULL; pcidev = bus->siba_pci.spc_dev; gpiodev = bus->siba_cc.scc_dev ? bus->siba_cc.scc_dev : pcidev; if (!gpiodev) return; siba_write_4(gpiodev, BWN_GPIOCTL, 0); } static int bwn_dma_gettype(struct bwn_mac *mac) { uint32_t tmp; uint16_t base; tmp = BWN_READ_4(mac, SIBA_TGSHIGH); if (tmp & SIBA_TGSHIGH_DMA64) return (BWN_DMA_64BIT); base = bwn_dma_base(0, 0); BWN_WRITE_4(mac, base + BWN_DMA32_TXCTL, BWN_DMA32_TXADDREXT_MASK); tmp = BWN_READ_4(mac, base + BWN_DMA32_TXCTL); if (tmp & BWN_DMA32_TXADDREXT_MASK) return (BWN_DMA_32BIT); return (BWN_DMA_30BIT); } static void bwn_dma_ring_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error) { if (!error) { KASSERT(nseg == 1, ("too many segments(%d)\n", nseg)); *((bus_addr_t *)arg) = seg->ds_addr; } } static void bwn_phy_g_init_sub(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; uint16_t i, tmp; if (phy->rev == 1) bwn_phy_init_b5(mac); else bwn_phy_init_b6(mac); if (phy->rev >= 2 || phy->gmode) bwn_phy_init_a(mac); if (phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVER, 0); BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVERVAL, 0); } if (phy->rev == 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, 0); BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xc0); } if (phy->rev > 5) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, 0x400); BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xc0); } if (phy->gmode || phy->rev >= 2) { tmp = BWN_PHY_READ(mac, BWN_PHY_VERSION_OFDM); tmp &= BWN_PHYVER_VERSION; if (tmp == 3 || tmp == 5) { BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xc2), 0x1816); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xc3), 0x8006); } if (tmp == 5) { BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xcc), 0x00ff, 0x1f00); } } if ((phy->rev <= 2 && phy->gmode) || phy->rev >= 2) BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x7e), 0x78); if (phy->rf_rev == 8) { BWN_PHY_SET(mac, BWN_PHY_EXTG(0x01), 0x80); BWN_PHY_SET(mac, BWN_PHY_OFDM(0x3e), 0x4); } if (BWN_HAS_LOOPBACK(phy)) bwn_loopback_calcgain(mac); if (phy->rf_rev != 8) { if (pg->pg_initval == 0xffff) pg->pg_initval = bwn_rf_init_bcm2050(mac); else BWN_RF_WRITE(mac, 0x0078, pg->pg_initval); } bwn_lo_g_init(mac); if (BWN_HAS_TXMAG(phy)) { BWN_RF_WRITE(mac, 0x52, (BWN_RF_READ(mac, 0x52) & 0xff00) | pg->pg_loctl.tx_bias | pg->pg_loctl.tx_magn); } else { BWN_RF_SETMASK(mac, 0x52, 0xfff0, pg->pg_loctl.tx_bias); } if (phy->rev >= 6) { BWN_PHY_SETMASK(mac, BWN_PHY_CCK(0x36), 0x0fff, (pg->pg_loctl.tx_bias << 12)); } if (mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_PACTRL) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2e), 0x8075); else BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2e), 0x807f); if (phy->rev < 2) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2f), 0x101); else BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2f), 0x202); if (phy->gmode || phy->rev >= 2) { bwn_lo_g_adjust(mac); BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0x8078); } if (!(mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_RSSI)) { for (i = 0; i < 64; i++) { BWN_PHY_WRITE(mac, BWN_PHY_NRSSI_CTRL, i); BWN_PHY_WRITE(mac, BWN_PHY_NRSSI_DATA, (uint16_t)MIN(MAX(bwn_nrssi_read(mac, i) - 0xffff, -32), 31)); } bwn_nrssi_threshold(mac); } else if (phy->gmode || phy->rev >= 2) { if (pg->pg_nrssi[0] == -1000) { KASSERT(pg->pg_nrssi[1] == -1000, ("%s:%d: fail", __func__, __LINE__)); bwn_nrssi_slope_11g(mac); } else bwn_nrssi_threshold(mac); } if (phy->rf_rev == 8) BWN_PHY_WRITE(mac, BWN_PHY_EXTG(0x05), 0x3230); bwn_phy_hwpctl_init(mac); if ((mac->mac_sd->sd_bus->siba_chipid == 0x4306 && mac->mac_sd->sd_bus->siba_chippkg == 2) || 0) { BWN_PHY_MASK(mac, BWN_PHY_CRS0, 0xbfff); BWN_PHY_MASK(mac, BWN_PHY_OFDM(0xc3), 0x7fff); } } static uint8_t bwn_has_hwpctl(struct bwn_mac *mac) { if (mac->mac_phy.hwpctl == 0 || mac->mac_phy.use_hwpctl == NULL) return (0); return (mac->mac_phy.use_hwpctl(mac)); } static void bwn_phy_init_b5(struct bwn_mac *mac) { struct siba_softc *bus = mac->mac_sd->sd_bus; struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; uint16_t offset, value; uint8_t old_channel; if (phy->analog == 1) BWN_RF_SET(mac, 0x007a, 0x0050); if ((bus->siba_board_vendor != SIBA_BOARDVENDOR_BCM) && (bus->siba_board_type != SIBA_BOARD_BU4306)) { value = 0x2120; for (offset = 0x00a8; offset < 0x00c7; offset++) { BWN_PHY_WRITE(mac, offset, value); value += 0x202; } } BWN_PHY_SETMASK(mac, 0x0035, 0xf0ff, 0x0700); if (phy->rf_ver == 0x2050) BWN_PHY_WRITE(mac, 0x0038, 0x0667); if (phy->gmode || phy->rev >= 2) { if (phy->rf_ver == 0x2050) { BWN_RF_SET(mac, 0x007a, 0x0020); BWN_RF_SET(mac, 0x0051, 0x0004); } BWN_WRITE_2(mac, BWN_PHY_RADIO, 0x0000); BWN_PHY_SET(mac, 0x0802, 0x0100); BWN_PHY_SET(mac, 0x042b, 0x2000); BWN_PHY_WRITE(mac, 0x001c, 0x186a); BWN_PHY_SETMASK(mac, 0x0013, 0x00ff, 0x1900); BWN_PHY_SETMASK(mac, 0x0035, 0xffc0, 0x0064); BWN_PHY_SETMASK(mac, 0x005d, 0xff80, 0x000a); } if (mac->mac_flags & BWN_MAC_FLAG_BADFRAME_PREEMP) BWN_PHY_SET(mac, BWN_PHY_RADIO_BITFIELD, (1 << 11)); if (phy->analog == 1) { BWN_PHY_WRITE(mac, 0x0026, 0xce00); BWN_PHY_WRITE(mac, 0x0021, 0x3763); BWN_PHY_WRITE(mac, 0x0022, 0x1bc3); BWN_PHY_WRITE(mac, 0x0023, 0x06f9); BWN_PHY_WRITE(mac, 0x0024, 0x037e); } else BWN_PHY_WRITE(mac, 0x0026, 0xcc00); BWN_PHY_WRITE(mac, 0x0030, 0x00c6); BWN_WRITE_2(mac, 0x03ec, 0x3f22); if (phy->analog == 1) BWN_PHY_WRITE(mac, 0x0020, 0x3e1c); else BWN_PHY_WRITE(mac, 0x0020, 0x301c); if (phy->analog == 0) BWN_WRITE_2(mac, 0x03e4, 0x3000); old_channel = phy->chan; bwn_phy_g_switch_chan(mac, 7, 0); if (phy->rf_ver != 0x2050) { BWN_RF_WRITE(mac, 0x0075, 0x0080); BWN_RF_WRITE(mac, 0x0079, 0x0081); } BWN_RF_WRITE(mac, 0x0050, 0x0020); BWN_RF_WRITE(mac, 0x0050, 0x0023); if (phy->rf_ver == 0x2050) { BWN_RF_WRITE(mac, 0x0050, 0x0020); BWN_RF_WRITE(mac, 0x005a, 0x0070); } BWN_RF_WRITE(mac, 0x005b, 0x007b); BWN_RF_WRITE(mac, 0x005c, 0x00b0); BWN_RF_SET(mac, 0x007a, 0x0007); bwn_phy_g_switch_chan(mac, old_channel, 0); BWN_PHY_WRITE(mac, 0x0014, 0x0080); BWN_PHY_WRITE(mac, 0x0032, 0x00ca); BWN_PHY_WRITE(mac, 0x002a, 0x88a3); bwn_phy_g_set_txpwr_sub(mac, &pg->pg_bbatt, &pg->pg_rfatt, pg->pg_txctl); if (phy->rf_ver == 0x2050) BWN_RF_WRITE(mac, 0x005d, 0x000d); BWN_WRITE_2(mac, 0x03e4, (BWN_READ_2(mac, 0x03e4) & 0xffc0) | 0x0004); } static void bwn_loopback_calcgain(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; uint16_t backup_phy[16] = { 0 }; uint16_t backup_radio[3]; uint16_t backup_bband; uint16_t i, j, loop_i_max; uint16_t trsw_rx; uint16_t loop1_outer_done, loop1_inner_done; backup_phy[0] = BWN_PHY_READ(mac, BWN_PHY_CRS0); backup_phy[1] = BWN_PHY_READ(mac, BWN_PHY_CCKBBANDCFG); backup_phy[2] = BWN_PHY_READ(mac, BWN_PHY_RFOVER); backup_phy[3] = BWN_PHY_READ(mac, BWN_PHY_RFOVERVAL); if (phy->rev != 1) { backup_phy[4] = BWN_PHY_READ(mac, BWN_PHY_ANALOGOVER); backup_phy[5] = BWN_PHY_READ(mac, BWN_PHY_ANALOGOVERVAL); } backup_phy[6] = BWN_PHY_READ(mac, BWN_PHY_CCK(0x5a)); backup_phy[7] = BWN_PHY_READ(mac, BWN_PHY_CCK(0x59)); backup_phy[8] = BWN_PHY_READ(mac, BWN_PHY_CCK(0x58)); backup_phy[9] = BWN_PHY_READ(mac, BWN_PHY_CCK(0x0a)); backup_phy[10] = BWN_PHY_READ(mac, BWN_PHY_CCK(0x03)); backup_phy[11] = BWN_PHY_READ(mac, BWN_PHY_LO_MASK); backup_phy[12] = BWN_PHY_READ(mac, BWN_PHY_LO_CTL); backup_phy[13] = BWN_PHY_READ(mac, BWN_PHY_CCK(0x2b)); backup_phy[14] = BWN_PHY_READ(mac, BWN_PHY_PGACTL); backup_phy[15] = BWN_PHY_READ(mac, BWN_PHY_LO_LEAKAGE); backup_bband = pg->pg_bbatt.att; backup_radio[0] = BWN_RF_READ(mac, 0x52); backup_radio[1] = BWN_RF_READ(mac, 0x43); backup_radio[2] = BWN_RF_READ(mac, 0x7a); BWN_PHY_MASK(mac, BWN_PHY_CRS0, 0x3fff); BWN_PHY_SET(mac, BWN_PHY_CCKBBANDCFG, 0x8000); BWN_PHY_SET(mac, BWN_PHY_RFOVER, 0x0002); BWN_PHY_MASK(mac, BWN_PHY_RFOVERVAL, 0xfffd); BWN_PHY_SET(mac, BWN_PHY_RFOVER, 0x0001); BWN_PHY_MASK(mac, BWN_PHY_RFOVERVAL, 0xfffe); if (phy->rev != 1) { BWN_PHY_SET(mac, BWN_PHY_ANALOGOVER, 0x0001); BWN_PHY_MASK(mac, BWN_PHY_ANALOGOVERVAL, 0xfffe); BWN_PHY_SET(mac, BWN_PHY_ANALOGOVER, 0x0002); BWN_PHY_MASK(mac, BWN_PHY_ANALOGOVERVAL, 0xfffd); } BWN_PHY_SET(mac, BWN_PHY_RFOVER, 0x000c); BWN_PHY_SET(mac, BWN_PHY_RFOVERVAL, 0x000c); BWN_PHY_SET(mac, BWN_PHY_RFOVER, 0x0030); BWN_PHY_SETMASK(mac, BWN_PHY_RFOVERVAL, 0xffcf, 0x10); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x5a), 0x0780); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x59), 0xc810); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0x000d); BWN_PHY_SET(mac, BWN_PHY_CCK(0x0a), 0x2000); if (phy->rev != 1) { BWN_PHY_SET(mac, BWN_PHY_ANALOGOVER, 0x0004); BWN_PHY_MASK(mac, BWN_PHY_ANALOGOVERVAL, 0xfffb); } BWN_PHY_SETMASK(mac, BWN_PHY_CCK(0x03), 0xff9f, 0x40); if (phy->rf_rev == 8) BWN_RF_WRITE(mac, 0x43, 0x000f); else { BWN_RF_WRITE(mac, 0x52, 0); BWN_RF_SETMASK(mac, 0x43, 0xfff0, 0x9); } bwn_phy_g_set_bbatt(mac, 11); if (phy->rev >= 3) BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0xc020); else BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0x8020); BWN_PHY_WRITE(mac, BWN_PHY_LO_CTL, 0); BWN_PHY_SETMASK(mac, BWN_PHY_CCK(0x2b), 0xffc0, 0x01); BWN_PHY_SETMASK(mac, BWN_PHY_CCK(0x2b), 0xc0ff, 0x800); BWN_PHY_SET(mac, BWN_PHY_RFOVER, 0x0100); BWN_PHY_MASK(mac, BWN_PHY_RFOVERVAL, 0xcfff); if (mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_EXTLNA) { if (phy->rev >= 7) { BWN_PHY_SET(mac, BWN_PHY_RFOVER, 0x0800); BWN_PHY_SET(mac, BWN_PHY_RFOVERVAL, 0x8000); } } BWN_RF_MASK(mac, 0x7a, 0x00f7); j = 0; loop_i_max = (phy->rf_rev == 8) ? 15 : 9; for (i = 0; i < loop_i_max; i++) { for (j = 0; j < 16; j++) { BWN_RF_WRITE(mac, 0x43, i); BWN_PHY_SETMASK(mac, BWN_PHY_RFOVERVAL, 0xf0ff, (j << 8)); BWN_PHY_SETMASK(mac, BWN_PHY_PGACTL, 0x0fff, 0xa000); BWN_PHY_SET(mac, BWN_PHY_PGACTL, 0xf000); DELAY(20); if (BWN_PHY_READ(mac, BWN_PHY_LO_LEAKAGE) >= 0xdfc) goto done0; } } done0: loop1_outer_done = i; loop1_inner_done = j; if (j >= 8) { BWN_PHY_SET(mac, BWN_PHY_RFOVERVAL, 0x30); trsw_rx = 0x1b; for (j = j - 8; j < 16; j++) { BWN_PHY_SETMASK(mac, BWN_PHY_RFOVERVAL, 0xf0ff, j << 8); BWN_PHY_SETMASK(mac, BWN_PHY_PGACTL, 0x0fff, 0xa000); BWN_PHY_SET(mac, BWN_PHY_PGACTL, 0xf000); DELAY(20); trsw_rx -= 3; if (BWN_PHY_READ(mac, BWN_PHY_LO_LEAKAGE) >= 0xdfc) goto done1; } } else trsw_rx = 0x18; done1: if (phy->rev != 1) { BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVER, backup_phy[4]); BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVERVAL, backup_phy[5]); } BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x5a), backup_phy[6]); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x59), backup_phy[7]); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), backup_phy[8]); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x0a), backup_phy[9]); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x03), backup_phy[10]); BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, backup_phy[11]); BWN_PHY_WRITE(mac, BWN_PHY_LO_CTL, backup_phy[12]); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2b), backup_phy[13]); BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, backup_phy[14]); bwn_phy_g_set_bbatt(mac, backup_bband); BWN_RF_WRITE(mac, 0x52, backup_radio[0]); BWN_RF_WRITE(mac, 0x43, backup_radio[1]); BWN_RF_WRITE(mac, 0x7a, backup_radio[2]); BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, backup_phy[2] | 0x0003); DELAY(10); BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, backup_phy[2]); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, backup_phy[3]); BWN_PHY_WRITE(mac, BWN_PHY_CRS0, backup_phy[0]); BWN_PHY_WRITE(mac, BWN_PHY_CCKBBANDCFG, backup_phy[1]); pg->pg_max_lb_gain = ((loop1_inner_done * 6) - (loop1_outer_done * 4)) - 11; pg->pg_trsw_rx_gain = trsw_rx * 2; } static uint16_t bwn_rf_init_bcm2050(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; uint32_t tmp1 = 0, tmp2 = 0; uint16_t rcc, i, j, pgactl, cck0, cck1, cck2, cck3, rfover, rfoverval, analogover, analogoverval, crs0, classctl, lomask, loctl, syncctl, radio0, radio1, radio2, reg0, reg1, reg2, radio78, reg, index; static const uint8_t rcc_table[] = { 0x02, 0x03, 0x01, 0x0f, 0x06, 0x07, 0x05, 0x0f, 0x0a, 0x0b, 0x09, 0x0f, 0x0e, 0x0f, 0x0d, 0x0f, }; + loctl = lomask = reg0 = classctl = crs0 = analogoverval = analogover = + rfoverval = rfover = cck3 = 0; radio0 = BWN_RF_READ(mac, 0x43); radio1 = BWN_RF_READ(mac, 0x51); radio2 = BWN_RF_READ(mac, 0x52); pgactl = BWN_PHY_READ(mac, BWN_PHY_PGACTL); cck0 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x5a)); cck1 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x59)); cck2 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x58)); if (phy->type == BWN_PHYTYPE_B) { cck3 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x30)); reg0 = BWN_READ_2(mac, 0x3ec); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x30), 0xff); BWN_WRITE_2(mac, 0x3ec, 0x3f3f); } else if (phy->gmode || phy->rev >= 2) { rfover = BWN_PHY_READ(mac, BWN_PHY_RFOVER); rfoverval = BWN_PHY_READ(mac, BWN_PHY_RFOVERVAL); analogover = BWN_PHY_READ(mac, BWN_PHY_ANALOGOVER); analogoverval = BWN_PHY_READ(mac, BWN_PHY_ANALOGOVERVAL); crs0 = BWN_PHY_READ(mac, BWN_PHY_CRS0); classctl = BWN_PHY_READ(mac, BWN_PHY_CLASSCTL); BWN_PHY_SET(mac, BWN_PHY_ANALOGOVER, 0x0003); BWN_PHY_MASK(mac, BWN_PHY_ANALOGOVERVAL, 0xfffc); BWN_PHY_MASK(mac, BWN_PHY_CRS0, 0x7fff); BWN_PHY_MASK(mac, BWN_PHY_CLASSCTL, 0xfffc); if (BWN_HAS_LOOPBACK(phy)) { lomask = BWN_PHY_READ(mac, BWN_PHY_LO_MASK); loctl = BWN_PHY_READ(mac, BWN_PHY_LO_CTL); if (phy->rev >= 3) BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0xc020); else BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0x8020); BWN_PHY_WRITE(mac, BWN_PHY_LO_CTL, 0); } BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(0, 1, 1))); BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVER, 0)); } BWN_WRITE_2(mac, 0x3e2, BWN_READ_2(mac, 0x3e2) | 0x8000); syncctl = BWN_PHY_READ(mac, BWN_PHY_SYNCCTL); BWN_PHY_MASK(mac, BWN_PHY_SYNCCTL, 0xff7f); reg1 = BWN_READ_2(mac, 0x3e6); reg2 = BWN_READ_2(mac, 0x3f4); if (phy->analog == 0) BWN_WRITE_2(mac, 0x03e6, 0x0122); else { if (phy->analog >= 2) BWN_PHY_SETMASK(mac, BWN_PHY_CCK(0x03), 0xffbf, 0x40); BWN_WRITE_2(mac, BWN_CHANNEL_EXT, (BWN_READ_2(mac, BWN_CHANNEL_EXT) | 0x2000)); } reg = BWN_RF_READ(mac, 0x60); index = (reg & 0x001e) >> 1; rcc = (((rcc_table[index] << 1) | (reg & 0x0001)) | 0x0020); if (phy->type == BWN_PHYTYPE_B) BWN_RF_WRITE(mac, 0x78, 0x26); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(0, 1, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xbfaf); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2b), 0x1403); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(0, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xbfa0); BWN_RF_SET(mac, 0x51, 0x0004); if (phy->rf_rev == 8) BWN_RF_WRITE(mac, 0x43, 0x1f); else { BWN_RF_WRITE(mac, 0x52, 0); BWN_RF_SETMASK(mac, 0x43, 0xfff0, 0x0009); } BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0); for (i = 0; i < 16; i++) { BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x5a), 0x0480); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x59), 0xc810); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0x000d); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xafb0); DELAY(10); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xefb0); DELAY(10); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 0))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xfff0); DELAY(20); tmp1 += BWN_PHY_READ(mac, BWN_PHY_LO_LEAKAGE); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xafb0); } DELAY(10); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0); tmp1++; tmp1 >>= 9; for (i = 0; i < 16; i++) { radio78 = (BWN_BITREV4(i) << 1) | 0x0020; BWN_RF_WRITE(mac, 0x78, radio78); DELAY(10); for (j = 0; j < 16; j++) { BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x5a), 0x0d80); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x59), 0xc810); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0x000d); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xafb0); DELAY(10); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xefb0); DELAY(10); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 0))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xfff0); DELAY(10); tmp2 += BWN_PHY_READ(mac, BWN_PHY_LO_LEAKAGE); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), 0); if (phy->gmode || phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, bwn_rf_2050_rfoverval(mac, BWN_PHY_RFOVERVAL, BWN_LPD(1, 0, 1))); } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xafb0); } tmp2++; tmp2 >>= 8; if (tmp1 < tmp2) break; } BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, pgactl); BWN_RF_WRITE(mac, 0x51, radio1); BWN_RF_WRITE(mac, 0x52, radio2); BWN_RF_WRITE(mac, 0x43, radio0); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x5a), cck0); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x59), cck1); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x58), cck2); BWN_WRITE_2(mac, 0x3e6, reg1); if (phy->analog != 0) BWN_WRITE_2(mac, 0x3f4, reg2); BWN_PHY_WRITE(mac, BWN_PHY_SYNCCTL, syncctl); bwn_spu_workaround(mac, phy->chan); if (phy->type == BWN_PHYTYPE_B) { BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x30), cck3); BWN_WRITE_2(mac, 0x3ec, reg0); } else if (phy->gmode) { BWN_WRITE_2(mac, BWN_PHY_RADIO, BWN_READ_2(mac, BWN_PHY_RADIO) & 0x7fff); BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, rfover); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, rfoverval); BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVER, analogover); BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVERVAL, analogoverval); BWN_PHY_WRITE(mac, BWN_PHY_CRS0, crs0); BWN_PHY_WRITE(mac, BWN_PHY_CLASSCTL, classctl); if (BWN_HAS_LOOPBACK(phy)) { BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, lomask); BWN_PHY_WRITE(mac, BWN_PHY_LO_CTL, loctl); } } return ((i > 15) ? radio78 : rcc); } static void bwn_phy_init_b6(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; uint16_t offset, val; uint8_t old_channel; KASSERT(!(phy->rf_rev == 6 || phy->rf_rev == 7), ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, 0x003e, 0x817a); BWN_RF_WRITE(mac, 0x007a, BWN_RF_READ(mac, 0x007a) | 0x0058); if (phy->rf_rev == 4 || phy->rf_rev == 5) { BWN_RF_WRITE(mac, 0x51, 0x37); BWN_RF_WRITE(mac, 0x52, 0x70); BWN_RF_WRITE(mac, 0x53, 0xb3); BWN_RF_WRITE(mac, 0x54, 0x9b); BWN_RF_WRITE(mac, 0x5a, 0x88); BWN_RF_WRITE(mac, 0x5b, 0x88); BWN_RF_WRITE(mac, 0x5d, 0x88); BWN_RF_WRITE(mac, 0x5e, 0x88); BWN_RF_WRITE(mac, 0x7d, 0x88); bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_TSSI_RESET_PSM_WORKAROUN); } if (phy->rf_rev == 8) { BWN_RF_WRITE(mac, 0x51, 0); BWN_RF_WRITE(mac, 0x52, 0x40); BWN_RF_WRITE(mac, 0x53, 0xb7); BWN_RF_WRITE(mac, 0x54, 0x98); BWN_RF_WRITE(mac, 0x5a, 0x88); BWN_RF_WRITE(mac, 0x5b, 0x6b); BWN_RF_WRITE(mac, 0x5c, 0x0f); if (mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_ALTIQ) { BWN_RF_WRITE(mac, 0x5d, 0xfa); BWN_RF_WRITE(mac, 0x5e, 0xd8); } else { BWN_RF_WRITE(mac, 0x5d, 0xf5); BWN_RF_WRITE(mac, 0x5e, 0xb8); } BWN_RF_WRITE(mac, 0x0073, 0x0003); BWN_RF_WRITE(mac, 0x007d, 0x00a8); BWN_RF_WRITE(mac, 0x007c, 0x0001); BWN_RF_WRITE(mac, 0x007e, 0x0008); } for (val = 0x1e1f, offset = 0x0088; offset < 0x0098; offset++) { BWN_PHY_WRITE(mac, offset, val); val -= 0x0202; } for (val = 0x3e3f, offset = 0x0098; offset < 0x00a8; offset++) { BWN_PHY_WRITE(mac, offset, val); val -= 0x0202; } for (val = 0x2120, offset = 0x00a8; offset < 0x00c8; offset++) { BWN_PHY_WRITE(mac, offset, (val & 0x3f3f)); val += 0x0202; } if (phy->type == BWN_PHYTYPE_G) { BWN_RF_SET(mac, 0x007a, 0x0020); BWN_RF_SET(mac, 0x0051, 0x0004); BWN_PHY_SET(mac, 0x0802, 0x0100); BWN_PHY_SET(mac, 0x042b, 0x2000); BWN_PHY_WRITE(mac, 0x5b, 0); BWN_PHY_WRITE(mac, 0x5c, 0); } old_channel = phy->chan; bwn_phy_g_switch_chan(mac, (old_channel >= 8) ? 1 : 13, 0); BWN_RF_WRITE(mac, 0x0050, 0x0020); BWN_RF_WRITE(mac, 0x0050, 0x0023); DELAY(40); if (phy->rf_rev < 6 || phy->rf_rev == 8) { BWN_RF_WRITE(mac, 0x7c, BWN_RF_READ(mac, 0x7c) | 0x0002); BWN_RF_WRITE(mac, 0x50, 0x20); } if (phy->rf_rev <= 2) { BWN_RF_WRITE(mac, 0x7c, 0x20); BWN_RF_WRITE(mac, 0x5a, 0x70); BWN_RF_WRITE(mac, 0x5b, 0x7b); BWN_RF_WRITE(mac, 0x5c, 0xb0); } BWN_RF_SETMASK(mac, 0x007a, 0x00f8, 0x0007); bwn_phy_g_switch_chan(mac, old_channel, 0); BWN_PHY_WRITE(mac, 0x0014, 0x0200); if (phy->rf_rev >= 6) BWN_PHY_WRITE(mac, 0x2a, 0x88c2); else BWN_PHY_WRITE(mac, 0x2a, 0x8ac0); BWN_PHY_WRITE(mac, 0x0038, 0x0668); bwn_phy_g_set_txpwr_sub(mac, &pg->pg_bbatt, &pg->pg_rfatt, pg->pg_txctl); if (phy->rf_rev <= 5) BWN_PHY_SETMASK(mac, 0x5d, 0xff80, 0x0003); if (phy->rf_rev <= 2) BWN_RF_WRITE(mac, 0x005d, 0x000d); if (phy->analog == 4) { BWN_WRITE_2(mac, 0x3e4, 9); BWN_PHY_MASK(mac, 0x61, 0x0fff); } else BWN_PHY_SETMASK(mac, 0x0002, 0xffc0, 0x0004); if (phy->type == BWN_PHYTYPE_B) KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); else if (phy->type == BWN_PHYTYPE_G) BWN_WRITE_2(mac, 0x03e6, 0x0); } static void bwn_phy_init_a(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; KASSERT(phy->type == BWN_PHYTYPE_A || phy->type == BWN_PHYTYPE_G, ("%s:%d: fail", __func__, __LINE__)); if (phy->rev >= 6) { if (phy->type == BWN_PHYTYPE_A) BWN_PHY_MASK(mac, BWN_PHY_OFDM(0x1b), ~0x1000); if (BWN_PHY_READ(mac, BWN_PHY_ENCORE) & BWN_PHY_ENCORE_EN) BWN_PHY_SET(mac, BWN_PHY_ENCORE, 0x0010); else BWN_PHY_MASK(mac, BWN_PHY_ENCORE, ~0x1010); } bwn_wa_init(mac); if (phy->type == BWN_PHYTYPE_G && (mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_PACTRL)) BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x6e), 0xe000, 0x3cf); } static void bwn_wa_write_noisescale(struct bwn_mac *mac, const uint16_t *nst) { int i; for (i = 0; i < BWN_TAB_NOISESCALE_SIZE; i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_NOISESCALE, i, nst[i]); } static void bwn_wa_agc(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; if (phy->rev == 1) { bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1_R1, 0, 254); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1_R1, 1, 13); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1_R1, 2, 19); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1_R1, 3, 25); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, 0, 0x2710); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, 1, 0x9b83); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, 2, 0x9b83); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, 3, 0x0f8d); BWN_PHY_WRITE(mac, BWN_PHY_LMS, 4); } else { bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1, 0, 254); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1, 1, 13); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1, 2, 19); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC1, 3, 25); } BWN_PHY_SETMASK(mac, BWN_PHY_CCKSHIFTBITS_WA, (uint16_t)~0xff00, 0x5700); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x1a), ~0x007f, 0x000f); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x1a), ~0x3f80, 0x2b80); BWN_PHY_SETMASK(mac, BWN_PHY_ANTWRSETT, 0xf0ff, 0x0300); BWN_RF_SET(mac, 0x7a, 0x0008); BWN_PHY_SETMASK(mac, BWN_PHY_N1P1GAIN, ~0x000f, 0x0008); BWN_PHY_SETMASK(mac, BWN_PHY_P1P2GAIN, ~0x0f00, 0x0600); BWN_PHY_SETMASK(mac, BWN_PHY_N1N2GAIN, ~0x0f00, 0x0700); BWN_PHY_SETMASK(mac, BWN_PHY_N1P1GAIN, ~0x0f00, 0x0100); if (phy->rev == 1) BWN_PHY_SETMASK(mac, BWN_PHY_N1N2GAIN, ~0x000f, 0x0007); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x88), ~0x00ff, 0x001c); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x88), ~0x3f00, 0x0200); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x96), ~0x00ff, 0x001c); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x89), ~0x00ff, 0x0020); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x89), ~0x3f00, 0x0200); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x82), ~0x00ff, 0x002e); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x96), (uint16_t)~0xff00, 0x1a00); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x81), ~0x00ff, 0x0028); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x81), (uint16_t)~0xff00, 0x2c00); if (phy->rev == 1) { BWN_PHY_WRITE(mac, BWN_PHY_PEAK_COUNT, 0x092b); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x1b), ~0x001e, 0x0002); } else { BWN_PHY_MASK(mac, BWN_PHY_OFDM(0x1b), ~0x001e); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x1f), 0x287a); BWN_PHY_SETMASK(mac, BWN_PHY_LPFGAINCTL, ~0x000f, 0x0004); if (phy->rev >= 6) { BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x22), 0x287a); BWN_PHY_SETMASK(mac, BWN_PHY_LPFGAINCTL, (uint16_t)~0xf000, 0x3000); } } BWN_PHY_SETMASK(mac, BWN_PHY_DIVSRCHIDX, 0x8080, 0x7874); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x8e), 0x1c00); if (phy->rev == 1) { BWN_PHY_SETMASK(mac, BWN_PHY_DIVP1P2GAIN, ~0x0f00, 0x0600); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x8b), 0x005e); BWN_PHY_SETMASK(mac, BWN_PHY_ANTWRSETT, ~0x00ff, 0x001e); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x8d), 0x0002); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3_R1, 0, 0); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3_R1, 1, 7); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3_R1, 2, 16); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3_R1, 3, 28); } else { bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3, 0, 0); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3, 1, 7); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3, 2, 16); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC3, 3, 28); } if (phy->rev >= 6) { BWN_PHY_MASK(mac, BWN_PHY_OFDM(0x26), ~0x0003); BWN_PHY_MASK(mac, BWN_PHY_OFDM(0x26), ~0x1000); } BWN_PHY_READ(mac, BWN_PHY_VERSION_OFDM); } static void bwn_wa_grev1(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; int i; static const uint16_t bwn_tab_finefreqg[] = BWN_TAB_FINEFREQ_G; static const uint32_t bwn_tab_retard[] = BWN_TAB_RETARD; static const uint32_t bwn_tab_rotor[] = BWN_TAB_ROTOR; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s fail", __func__)); /* init CRSTHRES and ANTDWELL */ if (phy->rev == 1) { BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES1_R1, 0x4f19); } else if (phy->rev == 2) { BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES1, 0x1861); BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES2, 0x0271); BWN_PHY_SET(mac, BWN_PHY_ANTDWELL, 0x0800); } else { BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES1, 0x0098); BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES2, 0x0070); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xc9), 0x0080); BWN_PHY_SET(mac, BWN_PHY_ANTDWELL, 0x0800); } BWN_PHY_SETMASK(mac, BWN_PHY_CRS0, ~0x03c0, 0xd000); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0x2c), 0x005a); BWN_PHY_WRITE(mac, BWN_PHY_CCKSHIFTBITS, 0x0026); /* XXX support PHY-A??? */ for (i = 0; i < N(bwn_tab_finefreqg); i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_DACRFPABB, i, bwn_tab_finefreqg[i]); /* XXX support PHY-A??? */ if (phy->rev == 1) for (i = 0; i < N(bwn_tab_noise_g1); i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, i, bwn_tab_noise_g1[i]); else for (i = 0; i < N(bwn_tab_noise_g2); i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, i, bwn_tab_noise_g2[i]); for (i = 0; i < N(bwn_tab_rotor); i++) bwn_ofdmtab_write_4(mac, BWN_OFDMTAB_ROTOR, i, bwn_tab_rotor[i]); /* XXX support PHY-A??? */ if (phy->rev >= 6) { if (BWN_PHY_READ(mac, BWN_PHY_ENCORE) & BWN_PHY_ENCORE_EN) bwn_wa_write_noisescale(mac, bwn_tab_noisescale_g3); else bwn_wa_write_noisescale(mac, bwn_tab_noisescale_g2); } else bwn_wa_write_noisescale(mac, bwn_tab_noisescale_g1); for (i = 0; i < N(bwn_tab_retard); i++) bwn_ofdmtab_write_4(mac, BWN_OFDMTAB_ADVRETARD, i, bwn_tab_retard[i]); if (phy->rev == 1) { for (i = 0; i < 16; i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_WRSSI_R1, i, 0x0020); } else { for (i = 0; i < 32; i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_WRSSI, i, 0x0820); } bwn_wa_agc(mac); } static void bwn_wa_grev26789(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; int i; static const uint16_t bwn_tab_sigmasqr2[] = BWN_TAB_SIGMASQR2; uint16_t ofdmrev; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s fail", __func__)); bwn_gtab_write(mac, BWN_GTAB_ORIGTR, 0, 0xc480); /* init CRSTHRES and ANTDWELL */ if (phy->rev == 1) BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES1_R1, 0x4f19); else if (phy->rev == 2) { BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES1, 0x1861); BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES2, 0x0271); BWN_PHY_SET(mac, BWN_PHY_ANTDWELL, 0x0800); } else { BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES1, 0x0098); BWN_PHY_WRITE(mac, BWN_PHY_CRSTHRES2, 0x0070); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xc9), 0x0080); BWN_PHY_SET(mac, BWN_PHY_ANTDWELL, 0x0800); } for (i = 0; i < 64; i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_RSSI, i, i); /* XXX support PHY-A??? */ if (phy->rev == 1) for (i = 0; i < N(bwn_tab_noise_g1); i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, i, bwn_tab_noise_g1[i]); else for (i = 0; i < N(bwn_tab_noise_g2); i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_AGC2, i, bwn_tab_noise_g2[i]); /* XXX support PHY-A??? */ if (phy->rev >= 6) { if (BWN_PHY_READ(mac, BWN_PHY_ENCORE) & BWN_PHY_ENCORE_EN) bwn_wa_write_noisescale(mac, bwn_tab_noisescale_g3); else bwn_wa_write_noisescale(mac, bwn_tab_noisescale_g2); } else bwn_wa_write_noisescale(mac, bwn_tab_noisescale_g1); for (i = 0; i < N(bwn_tab_sigmasqr2); i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_MINSIGSQ, i, bwn_tab_sigmasqr2[i]); if (phy->rev == 1) { for (i = 0; i < 16; i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_WRSSI_R1, i, 0x0020); } else { for (i = 0; i < 32; i++) bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_WRSSI, i, 0x0820); } bwn_wa_agc(mac); ofdmrev = BWN_PHY_READ(mac, BWN_PHY_VERSION_OFDM) & BWN_PHYVER_VERSION; if (ofdmrev > 2) { if (phy->type == BWN_PHYTYPE_A) BWN_PHY_WRITE(mac, BWN_PHY_PWRDOWN, 0x1808); else BWN_PHY_WRITE(mac, BWN_PHY_PWRDOWN, 0x1000); } else { bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_DAC, 3, 0x1044); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_DAC, 4, 0x7201); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_DAC, 6, 0x0040); } bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_UNKNOWN_0F, 2, 15); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_UNKNOWN_0F, 3, 20); } static void bwn_wa_init(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct siba_softc *bus = mac->mac_sd->sd_bus; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s fail", __func__)); switch (phy->rev) { case 1: bwn_wa_grev1(mac); break; case 2: case 6: case 7: case 8: case 9: bwn_wa_grev26789(mac); break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } if (bus->siba_board_vendor != SIBA_BOARDVENDOR_BCM || bus->siba_board_type != SIBA_BOARD_BU4306 || bus->siba_board_rev != 0x17) { if (phy->rev < 2) { bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX_R1, 1, 0x0002); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX_R1, 2, 0x0001); } else { bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 1, 0x0002); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 2, 0x0001); if ((bus->siba_sprom.bf_lo & BWN_BFL_EXTLNA) && (phy->rev >= 7)) { BWN_PHY_MASK(mac, BWN_PHY_EXTG(0x11), 0xf7ff); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 0x0020, 0x0001); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 0x0021, 0x0001); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 0x0022, 0x0001); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 0x0023, 0x0000); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 0x0000, 0x0000); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_GAINX, 0x0003, 0x0002); } } } if (bus->siba_sprom.bf_lo & BWN_BFL_FEM) { BWN_PHY_WRITE(mac, BWN_PHY_GTABCTL, 0x3120); BWN_PHY_WRITE(mac, BWN_PHY_GTABDATA, 0xc480); } bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_UNKNOWN_11, 0, 0); bwn_ofdmtab_write_2(mac, BWN_OFDMTAB_UNKNOWN_11, 1, 0); } static void bwn_ofdmtab_write_2(struct bwn_mac *mac, uint16_t table, uint16_t offset, uint16_t value) { struct bwn_phy_g *pg = &mac->mac_phy.phy_g; uint16_t addr; addr = table + offset; if ((pg->pg_ofdmtab_dir != BWN_OFDMTAB_DIR_WRITE) || (addr - 1 != pg->pg_ofdmtab_addr)) { BWN_PHY_WRITE(mac, BWN_PHY_OTABLECTL, addr); pg->pg_ofdmtab_dir = BWN_OFDMTAB_DIR_WRITE; } pg->pg_ofdmtab_addr = addr; BWN_PHY_WRITE(mac, BWN_PHY_OTABLEI, value); } static void bwn_ofdmtab_write_4(struct bwn_mac *mac, uint16_t table, uint16_t offset, uint32_t value) { struct bwn_phy_g *pg = &mac->mac_phy.phy_g; uint16_t addr; addr = table + offset; if ((pg->pg_ofdmtab_dir != BWN_OFDMTAB_DIR_WRITE) || (addr - 1 != pg->pg_ofdmtab_addr)) { BWN_PHY_WRITE(mac, BWN_PHY_OTABLECTL, addr); pg->pg_ofdmtab_dir = BWN_OFDMTAB_DIR_WRITE; } pg->pg_ofdmtab_addr = addr; BWN_PHY_WRITE(mac, BWN_PHY_OTABLEI, value); BWN_PHY_WRITE(mac, BWN_PHY_OTABLEQ, (value >> 16)); } static void bwn_gtab_write(struct bwn_mac *mac, uint16_t table, uint16_t offset, uint16_t value) { BWN_PHY_WRITE(mac, BWN_PHY_GTABCTL, table + offset); BWN_PHY_WRITE(mac, BWN_PHY_GTABDATA, value); } static void bwn_dummy_transmission(struct bwn_mac *mac, int ofdm, int paon) { struct bwn_phy *phy = &mac->mac_phy; - struct bwn_softc *sc = mac->mac_sc; unsigned int i, max_loop; uint16_t value; uint32_t buffer[5] = { 0x00000000, 0x00d40000, 0x00000000, 0x01000000, 0x00000000 }; if (ofdm) { max_loop = 0x1e; buffer[0] = 0x000201cc; } else { max_loop = 0xfa; buffer[0] = 0x000b846e; } - BWN_ASSERT_LOCKED(sc); + BWN_ASSERT_LOCKED(mac->mac_sc); for (i = 0; i < 5; i++) bwn_ram_write(mac, i * 4, buffer[i]); BWN_WRITE_2(mac, 0x0568, 0x0000); BWN_WRITE_2(mac, 0x07c0, (mac->mac_sd->sd_id.sd_rev < 11) ? 0x0000 : 0x0100); value = ((phy->type == BWN_PHYTYPE_A) ? 0x41 : 0x40); BWN_WRITE_2(mac, 0x050c, value); if (phy->type == BWN_PHYTYPE_LP) BWN_WRITE_2(mac, 0x0514, 0x1a02); BWN_WRITE_2(mac, 0x0508, 0x0000); BWN_WRITE_2(mac, 0x050a, 0x0000); BWN_WRITE_2(mac, 0x054c, 0x0000); BWN_WRITE_2(mac, 0x056a, 0x0014); BWN_WRITE_2(mac, 0x0568, 0x0826); BWN_WRITE_2(mac, 0x0500, 0x0000); if (phy->type == BWN_PHYTYPE_LP) BWN_WRITE_2(mac, 0x0502, 0x0050); else BWN_WRITE_2(mac, 0x0502, 0x0030); if (phy->rf_ver == 0x2050 && phy->rf_rev <= 0x5) BWN_RF_WRITE(mac, 0x0051, 0x0017); for (i = 0x00; i < max_loop; i++) { value = BWN_READ_2(mac, 0x050e); if (value & 0x0080) break; DELAY(10); } for (i = 0x00; i < 0x0a; i++) { value = BWN_READ_2(mac, 0x050e); if (value & 0x0400) break; DELAY(10); } for (i = 0x00; i < 0x19; i++) { value = BWN_READ_2(mac, 0x0690); if (!(value & 0x0100)) break; DELAY(10); } if (phy->rf_ver == 0x2050 && phy->rf_rev <= 0x5) BWN_RF_WRITE(mac, 0x0051, 0x0037); } static void bwn_ram_write(struct bwn_mac *mac, uint16_t offset, uint32_t val) { uint32_t macctl; KASSERT(offset % 4 == 0, ("%s:%d: fail", __func__, __LINE__)); macctl = BWN_READ_4(mac, BWN_MACCTL); if (macctl & BWN_MACCTL_BIGENDIAN) printf("TODO: need swap\n"); BWN_WRITE_4(mac, BWN_RAM_CONTROL, offset); BWN_BARRIER(mac, BUS_SPACE_BARRIER_WRITE); BWN_WRITE_4(mac, BWN_RAM_DATA, val); } static void bwn_lo_write(struct bwn_mac *mac, struct bwn_loctl *ctl) { - struct bwn_phy *phy = &mac->mac_phy; uint16_t value; - KASSERT(phy->type == BWN_PHYTYPE_G, + KASSERT(mac->mac_phy.type == BWN_PHYTYPE_G, ("%s:%d: fail", __func__, __LINE__)); value = (uint8_t) (ctl->q); value |= ((uint8_t) (ctl->i)) << 8; BWN_PHY_WRITE(mac, BWN_PHY_LO_CTL, value); } static uint16_t bwn_lo_calcfeed(struct bwn_mac *mac, uint16_t lna, uint16_t pga, uint16_t trsw_rx) { struct bwn_phy *phy = &mac->mac_phy; uint16_t rfover; uint16_t feedthrough; if (phy->gmode) { lna <<= BWN_PHY_RFOVERVAL_LNA_SHIFT; pga <<= BWN_PHY_RFOVERVAL_PGA_SHIFT; KASSERT((lna & ~BWN_PHY_RFOVERVAL_LNA) == 0, ("%s:%d: fail", __func__, __LINE__)); KASSERT((pga & ~BWN_PHY_RFOVERVAL_PGA) == 0, ("%s:%d: fail", __func__, __LINE__)); trsw_rx &= (BWN_PHY_RFOVERVAL_TRSWRX | BWN_PHY_RFOVERVAL_BW); rfover = BWN_PHY_RFOVERVAL_UNK | pga | lna | trsw_rx; if ((mac->mac_sd->sd_bus->siba_sprom.bf_lo & BWN_BFL_EXTLNA) && phy->rev > 6) rfover |= BWN_PHY_RFOVERVAL_EXTLNA; BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xe300); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, rfover); DELAY(10); rfover |= BWN_PHY_RFOVERVAL_BW_LBW; BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, rfover); DELAY(10); rfover |= BWN_PHY_RFOVERVAL_BW_LPF; BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, rfover); DELAY(10); BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xf300); } else { pga |= BWN_PHY_PGACTL_UNKNOWN; BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, pga); DELAY(10); pga |= BWN_PHY_PGACTL_LOWBANDW; BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, pga); DELAY(10); pga |= BWN_PHY_PGACTL_LPF; BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, pga); } DELAY(21); feedthrough = BWN_PHY_READ(mac, BWN_PHY_LO_LEAKAGE); return (feedthrough); } static uint16_t bwn_lo_txctl_regtable(struct bwn_mac *mac, uint16_t *value, uint16_t *pad_mix_gain) { struct bwn_phy *phy = &mac->mac_phy; uint16_t reg, v, padmix; if (phy->type == BWN_PHYTYPE_B) { v = 0x30; if (phy->rf_rev <= 5) { reg = 0x43; padmix = 0; } else { reg = 0x52; padmix = 5; } } else { if (phy->rev >= 2 && phy->rf_rev == 8) { reg = 0x43; v = 0x10; padmix = 2; } else { reg = 0x52; v = 0x30; padmix = 5; } } if (value) *value = v; if (pad_mix_gain) *pad_mix_gain = padmix; return (reg); } static void bwn_lo_measure_txctl_values(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; uint16_t reg, mask; uint16_t trsw_rx, pga; uint16_t rf_pctl_reg; static const uint8_t tx_bias_values[] = { 0x09, 0x08, 0x0a, 0x01, 0x00, 0x02, 0x05, 0x04, 0x06, }; static const uint8_t tx_magn_values[] = { 0x70, 0x40, }; if (!BWN_HAS_LOOPBACK(phy)) { rf_pctl_reg = 6; trsw_rx = 2; pga = 0; } else { int lb_gain; trsw_rx = 0; lb_gain = pg->pg_max_lb_gain / 2; if (lb_gain > 10) { rf_pctl_reg = 0; pga = abs(10 - lb_gain) / 6; pga = MIN(MAX(pga, 0), 15); } else { int cmp_val; int tmp; pga = 0; cmp_val = 0x24; if ((phy->rev >= 2) && (phy->rf_ver == 0x2050) && (phy->rf_rev == 8)) cmp_val = 0x3c; tmp = lb_gain; if ((10 - lb_gain) < cmp_val) tmp = (10 - lb_gain); if (tmp < 0) tmp += 6; else tmp += 3; cmp_val /= 4; tmp /= 4; if (tmp >= cmp_val) rf_pctl_reg = cmp_val; else rf_pctl_reg = tmp; } } BWN_RF_SETMASK(mac, 0x43, 0xfff0, rf_pctl_reg); bwn_phy_g_set_bbatt(mac, 2); reg = bwn_lo_txctl_regtable(mac, &mask, NULL); mask = ~mask; BWN_RF_MASK(mac, reg, mask); if (BWN_HAS_TXMAG(phy)) { int i, j; int feedthrough; int min_feedth = 0xffff; uint8_t tx_magn, tx_bias; for (i = 0; i < N(tx_magn_values); i++) { tx_magn = tx_magn_values[i]; BWN_RF_SETMASK(mac, 0x52, 0xff0f, tx_magn); for (j = 0; j < N(tx_bias_values); j++) { tx_bias = tx_bias_values[j]; BWN_RF_SETMASK(mac, 0x52, 0xfff0, tx_bias); feedthrough = bwn_lo_calcfeed(mac, 0, pga, trsw_rx); if (feedthrough < min_feedth) { lo->tx_bias = tx_bias; lo->tx_magn = tx_magn; min_feedth = feedthrough; } if (lo->tx_bias == 0) break; } BWN_RF_WRITE(mac, 0x52, (BWN_RF_READ(mac, 0x52) & 0xff00) | lo->tx_bias | lo-> tx_magn); } } else { lo->tx_magn = 0; lo->tx_bias = 0; BWN_RF_MASK(mac, 0x52, 0xfff0); } BWN_GETTIME(lo->txctl_measured_time); } static void bwn_lo_get_powervector(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; int i; uint64_t tmp; uint64_t power_vector = 0; for (i = 0; i < 8; i += 2) { tmp = bwn_shm_read_2(mac, BWN_SHARED, 0x310 + i); power_vector |= (tmp << (i * 8)); bwn_shm_write_2(mac, BWN_SHARED, 0x310 + i, 0); } if (power_vector) lo->power_vector = power_vector; BWN_GETTIME(lo->pwr_vec_read_time); } static void bwn_lo_measure_gain_values(struct bwn_mac *mac, int16_t max_rx_gain, int use_trsw_rx) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; uint16_t tmp; if (max_rx_gain < 0) max_rx_gain = 0; if (BWN_HAS_LOOPBACK(phy)) { int trsw_rx = 0; int trsw_rx_gain; if (use_trsw_rx) { trsw_rx_gain = pg->pg_trsw_rx_gain / 2; if (max_rx_gain >= trsw_rx_gain) { trsw_rx_gain = max_rx_gain - trsw_rx_gain; trsw_rx = 0x20; } } else trsw_rx_gain = max_rx_gain; if (trsw_rx_gain < 9) { pg->pg_lna_lod_gain = 0; } else { pg->pg_lna_lod_gain = 1; trsw_rx_gain -= 8; } trsw_rx_gain = MIN(MAX(trsw_rx_gain, 0), 0x2d); pg->pg_pga_gain = trsw_rx_gain / 3; if (pg->pg_pga_gain >= 5) { pg->pg_pga_gain -= 5; pg->pg_lna_gain = 2; } else pg->pg_lna_gain = 0; } else { pg->pg_lna_gain = 0; pg->pg_trsw_rx_gain = 0x20; if (max_rx_gain >= 0x14) { pg->pg_lna_lod_gain = 1; pg->pg_pga_gain = 2; } else if (max_rx_gain >= 0x12) { pg->pg_lna_lod_gain = 1; pg->pg_pga_gain = 1; } else if (max_rx_gain >= 0xf) { pg->pg_lna_lod_gain = 1; pg->pg_pga_gain = 0; } else { pg->pg_lna_lod_gain = 0; pg->pg_pga_gain = 0; } } tmp = BWN_RF_READ(mac, 0x7a); if (pg->pg_lna_lod_gain == 0) tmp &= ~0x0008; else tmp |= 0x0008; BWN_RF_WRITE(mac, 0x7a, tmp); } static void bwn_lo_save(struct bwn_mac *mac, struct bwn_lo_g_value *sav) { struct siba_sprom *sprom = &mac->mac_sd->sd_bus->siba_sprom; struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; struct timespec ts; uint16_t tmp; if (bwn_has_hwpctl(mac)) { sav->phy_lomask = BWN_PHY_READ(mac, BWN_PHY_LO_MASK); sav->phy_extg = BWN_PHY_READ(mac, BWN_PHY_EXTG(0x01)); sav->phy_dacctl_hwpctl = BWN_PHY_READ(mac, BWN_PHY_DACCTL); sav->phy_cck4 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x14)); sav->phy_hpwr_tssictl = BWN_PHY_READ(mac, BWN_PHY_HPWR_TSSICTL); BWN_PHY_SET(mac, BWN_PHY_HPWR_TSSICTL, 0x100); BWN_PHY_SET(mac, BWN_PHY_EXTG(0x01), 0x40); BWN_PHY_SET(mac, BWN_PHY_DACCTL, 0x40); BWN_PHY_SET(mac, BWN_PHY_CCK(0x14), 0x200); } if (phy->type == BWN_PHYTYPE_B && phy->rf_ver == 0x2050 && phy->rf_rev < 6) { BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x16), 0x410); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x17), 0x820); } if (phy->rev >= 2) { sav->phy_analogover = BWN_PHY_READ(mac, BWN_PHY_ANALOGOVER); sav->phy_analogoverval = BWN_PHY_READ(mac, BWN_PHY_ANALOGOVERVAL); sav->phy_rfover = BWN_PHY_READ(mac, BWN_PHY_RFOVER); sav->phy_rfoverval = BWN_PHY_READ(mac, BWN_PHY_RFOVERVAL); sav->phy_classctl = BWN_PHY_READ(mac, BWN_PHY_CLASSCTL); sav->phy_cck3 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x3e)); sav->phy_crs0 = BWN_PHY_READ(mac, BWN_PHY_CRS0); BWN_PHY_MASK(mac, BWN_PHY_CLASSCTL, 0xfffc); BWN_PHY_MASK(mac, BWN_PHY_CRS0, 0x7fff); BWN_PHY_SET(mac, BWN_PHY_ANALOGOVER, 0x0003); BWN_PHY_MASK(mac, BWN_PHY_ANALOGOVERVAL, 0xfffc); if (phy->type == BWN_PHYTYPE_G) { if ((phy->rev >= 7) && (sprom->bf_lo & BWN_BFL_EXTLNA)) { BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, 0x933); } else { BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, 0x133); } } else { BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, 0); } BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x3e), 0); } sav->reg0 = BWN_READ_2(mac, 0x3f4); sav->reg1 = BWN_READ_2(mac, 0x3e2); sav->rf0 = BWN_RF_READ(mac, 0x43); sav->rf1 = BWN_RF_READ(mac, 0x7a); sav->phy_pgactl = BWN_PHY_READ(mac, BWN_PHY_PGACTL); sav->phy_cck2 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x2a)); sav->phy_syncctl = BWN_PHY_READ(mac, BWN_PHY_SYNCCTL); sav->phy_dacctl = BWN_PHY_READ(mac, BWN_PHY_DACCTL); if (!BWN_HAS_TXMAG(phy)) { sav->rf2 = BWN_RF_READ(mac, 0x52); sav->rf2 &= 0x00f0; } if (phy->type == BWN_PHYTYPE_B) { sav->phy_cck0 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x30)); sav->phy_cck1 = BWN_PHY_READ(mac, BWN_PHY_CCK(0x06)); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x30), 0x00ff); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x06), 0x3f3f); } else { BWN_WRITE_2(mac, 0x3e2, BWN_READ_2(mac, 0x3e2) | 0x8000); } BWN_WRITE_2(mac, 0x3f4, BWN_READ_2(mac, 0x3f4) & 0xf000); tmp = (phy->type == BWN_PHYTYPE_G) ? BWN_PHY_LO_MASK : BWN_PHY_CCK(0x2e); BWN_PHY_WRITE(mac, tmp, 0x007f); tmp = sav->phy_syncctl; BWN_PHY_WRITE(mac, BWN_PHY_SYNCCTL, tmp & 0xff7f); tmp = sav->rf1; BWN_RF_WRITE(mac, 0x007a, tmp & 0xfff0); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2a), 0x8a3); if (phy->type == BWN_PHYTYPE_G || (phy->type == BWN_PHYTYPE_B && phy->rf_ver == 0x2050 && phy->rf_rev >= 6)) { BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2b), 0x1003); } else BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2b), 0x0802); if (phy->rev >= 2) bwn_dummy_transmission(mac, 0, 1); bwn_phy_g_switch_chan(mac, 6, 0); BWN_RF_READ(mac, 0x51); if (phy->type == BWN_PHYTYPE_G) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2f), 0); nanouptime(&ts); if (time_before(lo->txctl_measured_time, (ts.tv_nsec / 1000000 + ts.tv_sec * 1000) - BWN_LO_TXCTL_EXPIRE)) bwn_lo_measure_txctl_values(mac); if (phy->type == BWN_PHYTYPE_G && phy->rev >= 3) BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0xc078); else { if (phy->type == BWN_PHYTYPE_B) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2e), 0x8078); else BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, 0x8078); } } static void bwn_lo_restore(struct bwn_mac *mac, struct bwn_lo_g_value *sav) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; uint16_t tmp; if (phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, 0xe300); tmp = (pg->pg_pga_gain << 8); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, tmp | 0xa0); DELAY(5); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, tmp | 0xa2); DELAY(2); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, tmp | 0xa3); } else { tmp = (pg->pg_pga_gain | 0xefa0); BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, tmp); } if (phy->type == BWN_PHYTYPE_G) { if (phy->rev >= 3) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2e), 0xc078); else BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2e), 0x8078); if (phy->rev >= 2) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2f), 0x0202); else BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2f), 0x0101); } BWN_WRITE_2(mac, 0x3f4, sav->reg0); BWN_PHY_WRITE(mac, BWN_PHY_PGACTL, sav->phy_pgactl); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2a), sav->phy_cck2); BWN_PHY_WRITE(mac, BWN_PHY_SYNCCTL, sav->phy_syncctl); BWN_PHY_WRITE(mac, BWN_PHY_DACCTL, sav->phy_dacctl); BWN_RF_WRITE(mac, 0x43, sav->rf0); BWN_RF_WRITE(mac, 0x7a, sav->rf1); if (!BWN_HAS_TXMAG(phy)) { tmp = sav->rf2; BWN_RF_SETMASK(mac, 0x52, 0xff0f, tmp); } BWN_WRITE_2(mac, 0x3e2, sav->reg1); if (phy->type == BWN_PHYTYPE_B && phy->rf_ver == 0x2050 && phy->rf_rev <= 5) { BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x30), sav->phy_cck0); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x06), sav->phy_cck1); } if (phy->rev >= 2) { BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVER, sav->phy_analogover); BWN_PHY_WRITE(mac, BWN_PHY_ANALOGOVERVAL, sav->phy_analogoverval); BWN_PHY_WRITE(mac, BWN_PHY_CLASSCTL, sav->phy_classctl); BWN_PHY_WRITE(mac, BWN_PHY_RFOVER, sav->phy_rfover); BWN_PHY_WRITE(mac, BWN_PHY_RFOVERVAL, sav->phy_rfoverval); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x3e), sav->phy_cck3); BWN_PHY_WRITE(mac, BWN_PHY_CRS0, sav->phy_crs0); } if (bwn_has_hwpctl(mac)) { tmp = (sav->phy_lomask & 0xbfff); BWN_PHY_WRITE(mac, BWN_PHY_LO_MASK, tmp); BWN_PHY_WRITE(mac, BWN_PHY_EXTG(0x01), sav->phy_extg); BWN_PHY_WRITE(mac, BWN_PHY_DACCTL, sav->phy_dacctl_hwpctl); BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x14), sav->phy_cck4); BWN_PHY_WRITE(mac, BWN_PHY_HPWR_TSSICTL, sav->phy_hpwr_tssictl); } bwn_phy_g_switch_chan(mac, sav->old_channel, 1); } static int bwn_lo_probe_loctl(struct bwn_mac *mac, struct bwn_loctl *probe, struct bwn_lo_g_sm *d) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_loctl orig, test; struct bwn_loctl prev = { -100, -100 }; static const struct bwn_loctl modifiers[] = { { 1, 1,}, { 1, 0,}, { 1, -1,}, { 0, -1,}, { -1, -1,}, { -1, 0,}, { -1, 1,}, { 0, 1,} }; int begin, end, lower = 0, i; uint16_t feedth; if (d->curstate == 0) { begin = 1; end = 8; } else if (d->curstate % 2 == 0) { begin = d->curstate - 1; end = d->curstate + 1; } else { begin = d->curstate - 2; end = d->curstate + 2; } if (begin < 1) begin += 8; if (end > 8) end -= 8; memcpy(&orig, probe, sizeof(struct bwn_loctl)); i = begin; d->curstate = i; while (1) { KASSERT(i >= 1 && i <= 8, ("%s:%d: fail", __func__, __LINE__)); memcpy(&test, &orig, sizeof(struct bwn_loctl)); test.i += modifiers[i - 1].i * d->multipler; test.q += modifiers[i - 1].q * d->multipler; if ((test.i != prev.i || test.q != prev.q) && (abs(test.i) <= 16 && abs(test.q) <= 16)) { bwn_lo_write(mac, &test); feedth = bwn_lo_calcfeed(mac, pg->pg_lna_gain, pg->pg_pga_gain, pg->pg_trsw_rx_gain); if (feedth < d->feedth) { memcpy(probe, &test, sizeof(struct bwn_loctl)); lower = 1; d->feedth = feedth; if (d->nmeasure < 2 && !BWN_HAS_LOOPBACK(phy)) break; } } memcpy(&prev, &test, sizeof(prev)); if (i == end) break; if (i == 8) i = 1; else i++; d->curstate = i; } return (lower); } static void bwn_lo_probe_sm(struct bwn_mac *mac, struct bwn_loctl *loctl, int *rxgain) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_lo_g_sm d; struct bwn_loctl probe; int lower, repeat, cnt = 0; uint16_t feedth; d.nmeasure = 0; d.multipler = 1; if (BWN_HAS_LOOPBACK(phy)) d.multipler = 3; memcpy(&d.loctl, loctl, sizeof(struct bwn_loctl)); repeat = (BWN_HAS_LOOPBACK(phy)) ? 4 : 1; do { bwn_lo_write(mac, &d.loctl); feedth = bwn_lo_calcfeed(mac, pg->pg_lna_gain, pg->pg_pga_gain, pg->pg_trsw_rx_gain); if (feedth < 0x258) { if (feedth >= 0x12c) *rxgain += 6; else *rxgain += 3; feedth = bwn_lo_calcfeed(mac, pg->pg_lna_gain, pg->pg_pga_gain, pg->pg_trsw_rx_gain); } d.feedth = feedth; d.curstate = 0; do { KASSERT(d.curstate >= 0 && d.curstate <= 8, ("%s:%d: fail", __func__, __LINE__)); memcpy(&probe, &d.loctl, sizeof(struct bwn_loctl)); lower = bwn_lo_probe_loctl(mac, &probe, &d); if (!lower) break; if ((probe.i == d.loctl.i) && (probe.q == d.loctl.q)) break; memcpy(&d.loctl, &probe, sizeof(struct bwn_loctl)); d.nmeasure++; } while (d.nmeasure < 24); memcpy(loctl, &d.loctl, sizeof(struct bwn_loctl)); if (BWN_HAS_LOOPBACK(phy)) { if (d.feedth > 0x1194) *rxgain -= 6; else if (d.feedth < 0x5dc) *rxgain += 3; if (cnt == 0) { if (d.feedth <= 0x5dc) { d.multipler = 1; cnt++; } else d.multipler = 2; } else if (cnt == 2) d.multipler = 1; } bwn_lo_measure_gain_values(mac, *rxgain, BWN_HAS_LOOPBACK(phy)); } while (++cnt < repeat); } static struct bwn_lo_calib * bwn_lo_calibset(struct bwn_mac *mac, const struct bwn_bbatt *bbatt, const struct bwn_rfatt *rfatt) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_loctl loctl = { 0, 0 }; struct bwn_lo_calib *cal; - struct bwn_lo_g_value sval; + struct bwn_lo_g_value sval = { 0 }; int rxgain; uint16_t pad, reg, value; sval.old_channel = phy->chan; bwn_mac_suspend(mac); bwn_lo_save(mac, &sval); reg = bwn_lo_txctl_regtable(mac, &value, &pad); BWN_RF_SETMASK(mac, 0x43, 0xfff0, rfatt->att); BWN_RF_SETMASK(mac, reg, ~value, (rfatt->padmix ? value :0)); rxgain = (rfatt->att * 2) + (bbatt->att / 2); if (rfatt->padmix) rxgain -= pad; if (BWN_HAS_LOOPBACK(phy)) rxgain += pg->pg_max_lb_gain; bwn_lo_measure_gain_values(mac, rxgain, BWN_HAS_LOOPBACK(phy)); bwn_phy_g_set_bbatt(mac, bbatt->att); bwn_lo_probe_sm(mac, &loctl, &rxgain); bwn_lo_restore(mac, &sval); bwn_mac_enable(mac); cal = malloc(sizeof(*cal), M_DEVBUF, M_NOWAIT | M_ZERO); if (!cal) { device_printf(mac->mac_sc->sc_dev, "out of memory\n"); return (NULL); } memcpy(&cal->bbatt, bbatt, sizeof(*bbatt)); memcpy(&cal->rfatt, rfatt, sizeof(*rfatt)); memcpy(&cal->ctl, &loctl, sizeof(loctl)); BWN_GETTIME(cal->calib_time); return (cal); } static struct bwn_lo_calib * bwn_lo_get_calib(struct bwn_mac *mac, const struct bwn_bbatt *bbatt, const struct bwn_rfatt *rfatt) { struct bwn_txpwr_loctl *lo = &mac->mac_phy.phy_g.pg_loctl; struct bwn_lo_calib *c; TAILQ_FOREACH(c, &lo->calib_list, list) { if (!BWN_BBATTCMP(&c->bbatt, bbatt)) continue; if (!BWN_RFATTCMP(&c->rfatt, rfatt)) continue; return (c); } c = bwn_lo_calibset(mac, bbatt, rfatt); if (!c) return (NULL); TAILQ_INSERT_TAIL(&lo->calib_list, c, list); return (c); } static void bwn_phy_g_dc_lookup_init(struct bwn_mac *mac, uint8_t update) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_softc *sc = mac->mac_sc; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; const struct bwn_rfatt *rfatt; const struct bwn_bbatt *bbatt; uint64_t pvector; int i; int rf_offset, bb_offset; uint8_t changed = 0; KASSERT(BWN_DC_LT_SIZE == 32, ("%s:%d: fail", __func__, __LINE__)); KASSERT(lo->rfatt.len * lo->bbatt.len <= 64, ("%s:%d: fail", __func__, __LINE__)); pvector = lo->power_vector; if (!update && !pvector) return; bwn_mac_suspend(mac); for (i = 0; i < BWN_DC_LT_SIZE * 2; i++) { struct bwn_lo_calib *cal; int idx; uint16_t val; if (!update && !(pvector & (((uint64_t)1ULL) << i))) continue; bb_offset = i / lo->rfatt.len; rf_offset = i % lo->rfatt.len; bbatt = &(lo->bbatt.array[bb_offset]); rfatt = &(lo->rfatt.array[rf_offset]); cal = bwn_lo_calibset(mac, bbatt, rfatt); if (!cal) { device_printf(sc->sc_dev, "LO: Could not " "calibrate DC table entry\n"); continue; } val = (uint8_t)(cal->ctl.q); val |= ((uint8_t)(cal->ctl.i)) << 4; free(cal, M_DEVBUF); idx = i / 2; if (i % 2) lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00ff) | ((val & 0x00ff) << 8); else lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xff00) | (val & 0x00ff); changed = 1; } if (changed) { for (i = 0; i < BWN_DC_LT_SIZE; i++) BWN_PHY_WRITE(mac, 0x3a0 + i, lo->dc_lt[i]); } bwn_mac_enable(mac); } static void bwn_lo_fixup_rfatt(struct bwn_rfatt *rf) { if (!rf->padmix) return; if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3)) rf->att = 4; } static void bwn_lo_g_adjust(struct bwn_mac *mac) { struct bwn_phy_g *pg = &mac->mac_phy.phy_g; struct bwn_lo_calib *cal; struct bwn_rfatt rf; memcpy(&rf, &pg->pg_rfatt, sizeof(rf)); bwn_lo_fixup_rfatt(&rf); cal = bwn_lo_get_calib(mac, &pg->pg_bbatt, &rf); if (!cal) return; bwn_lo_write(mac, &cal->ctl); } static void bwn_lo_g_init(struct bwn_mac *mac) { if (!bwn_has_hwpctl(mac)) return; bwn_lo_get_powervector(mac); bwn_phy_g_dc_lookup_init(mac, 1); } static void bwn_mac_suspend(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; int i; uint32_t tmp; KASSERT(mac->mac_suspended >= 0, ("%s:%d: fail", __func__, __LINE__)); if (mac->mac_suspended == 0) { bwn_psctl(mac, BWN_PS_AWAKE); BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_ON); BWN_READ_4(mac, BWN_MACCTL); for (i = 35; i; i--) { tmp = BWN_READ_4(mac, BWN_INTR_REASON); if (tmp & BWN_INTR_MAC_SUSPENDED) goto out; DELAY(10); } for (i = 40; i; i--) { tmp = BWN_READ_4(mac, BWN_INTR_REASON); if (tmp & BWN_INTR_MAC_SUSPENDED) goto out; DELAY(1000); } device_printf(sc->sc_dev, "MAC suspend failed\n"); } out: mac->mac_suspended++; } static void bwn_mac_enable(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; uint16_t state; state = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODESTAT); if (state != BWN_SHARED_UCODESTAT_SUSPEND && state != BWN_SHARED_UCODESTAT_SLEEP) device_printf(sc->sc_dev, "warn: firmware state (%d)\n", state); mac->mac_suspended--; KASSERT(mac->mac_suspended >= 0, ("%s:%d: fail", __func__, __LINE__)); if (mac->mac_suspended == 0) { BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_ON); BWN_WRITE_4(mac, BWN_INTR_REASON, BWN_INTR_MAC_SUSPENDED); BWN_READ_4(mac, BWN_MACCTL); BWN_READ_4(mac, BWN_INTR_REASON); bwn_psctl(mac, 0); } } static void bwn_psctl(struct bwn_mac *mac, uint32_t flags) { int i; uint16_t ucstat; KASSERT(!((flags & BWN_PS_ON) && (flags & BWN_PS_OFF)), ("%s:%d: fail", __func__, __LINE__)); KASSERT(!((flags & BWN_PS_AWAKE) && (flags & BWN_PS_ASLEEP)), ("%s:%d: fail", __func__, __LINE__)); /* XXX forcibly awake and hwps-off */ BWN_WRITE_4(mac, BWN_MACCTL, (BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_AWAKE) & ~BWN_MACCTL_HWPS); BWN_READ_4(mac, BWN_MACCTL); if (mac->mac_sd->sd_id.sd_rev >= 5) { for (i = 0; i < 100; i++) { ucstat = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODESTAT); if (ucstat != BWN_SHARED_UCODESTAT_SLEEP) break; DELAY(10); } } } static int16_t bwn_nrssi_read(struct bwn_mac *mac, uint16_t offset) { BWN_PHY_WRITE(mac, BWN_PHY_NRSSI_CTRL, offset); return ((int16_t)BWN_PHY_READ(mac, BWN_PHY_NRSSI_DATA)); } static void bwn_nrssi_threshold(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct siba_softc *siba = mac->mac_sd->sd_bus; int32_t a, b; int16_t tmp16; uint16_t tmpu16; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s: fail", __func__)); if (phy->gmode && (siba->siba_sprom.bf_lo & BWN_BFL_RSSI)) { if (!pg->pg_aci_wlan_automatic && pg->pg_aci_enable) { a = 0x13; b = 0x12; } else { a = 0xe; b = 0x11; } a = a * (pg->pg_nrssi[1] - pg->pg_nrssi[0]); a += (pg->pg_nrssi[0] << 6); a += (a < 32) ? 31 : 32; a = a >> 6; a = MIN(MAX(a, -31), 31); b = b * (pg->pg_nrssi[1] - pg->pg_nrssi[0]); b += (pg->pg_nrssi[0] << 6); if (b < 32) b += 31; else b += 32; b = b >> 6; b = MIN(MAX(b, -31), 31); tmpu16 = BWN_PHY_READ(mac, 0x048a) & 0xf000; tmpu16 |= ((uint32_t)b & 0x0000003f); tmpu16 |= (((uint32_t)a & 0x0000003f) << 6); BWN_PHY_WRITE(mac, 0x048a, tmpu16); return; } tmp16 = bwn_nrssi_read(mac, 0x20); if (tmp16 >= 0x20) tmp16 -= 0x40; BWN_PHY_SETMASK(mac, 0x048a, 0xf000, (tmp16 < 3) ? 0x09eb : 0x0aed); } static void bwn_nrssi_slope_11g(struct bwn_mac *mac) { #define SAVE_RF_MAX 3 #define SAVE_PHY_COMM_MAX 4 #define SAVE_PHY3_MAX 8 static const uint16_t save_rf_regs[SAVE_RF_MAX] = { 0x7a, 0x52, 0x43 }; static const uint16_t save_phy_comm_regs[SAVE_PHY_COMM_MAX] = { 0x15, 0x5a, 0x59, 0x58 }; static const uint16_t save_phy3_regs[SAVE_PHY3_MAX] = { 0x002e, 0x002f, 0x080f, BWN_PHY_G_LOCTL, 0x0801, 0x0060, 0x0014, 0x0478 }; struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; int32_t i, tmp32, phy3_idx = 0; uint16_t delta, tmp; uint16_t save_rf[SAVE_RF_MAX]; uint16_t save_phy_comm[SAVE_PHY_COMM_MAX]; uint16_t save_phy3[SAVE_PHY3_MAX]; uint16_t ant_div, phy0, chan_ex; int16_t nrssi0, nrssi1; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s:%d: fail", __func__, __LINE__)); if (phy->rf_rev >= 9) return; if (phy->rf_rev == 8) bwn_nrssi_offset(mac); BWN_PHY_MASK(mac, BWN_PHY_G_CRS, 0x7fff); BWN_PHY_MASK(mac, 0x0802, 0xfffc); /* * Save RF/PHY registers for later restoration */ ant_div = BWN_READ_2(mac, 0x03e2); BWN_WRITE_2(mac, 0x03e2, BWN_READ_2(mac, 0x03e2) | 0x8000); for (i = 0; i < SAVE_RF_MAX; ++i) save_rf[i] = BWN_RF_READ(mac, save_rf_regs[i]); for (i = 0; i < SAVE_PHY_COMM_MAX; ++i) save_phy_comm[i] = BWN_PHY_READ(mac, save_phy_comm_regs[i]); phy0 = BWN_READ_2(mac, BWN_PHY0); chan_ex = BWN_READ_2(mac, BWN_CHANNEL_EXT); if (phy->rev >= 3) { for (i = 0; i < SAVE_PHY3_MAX; ++i) save_phy3[i] = BWN_PHY_READ(mac, save_phy3_regs[i]); BWN_PHY_WRITE(mac, 0x002e, 0); BWN_PHY_WRITE(mac, BWN_PHY_G_LOCTL, 0); switch (phy->rev) { case 4: case 6: case 7: BWN_PHY_SET(mac, 0x0478, 0x0100); BWN_PHY_SET(mac, 0x0801, 0x0040); break; case 3: case 5: BWN_PHY_MASK(mac, 0x0801, 0xffbf); break; } BWN_PHY_SET(mac, 0x0060, 0x0040); BWN_PHY_SET(mac, 0x0014, 0x0200); } /* * Calculate nrssi0 */ BWN_RF_SET(mac, 0x007a, 0x0070); bwn_set_all_gains(mac, 0, 8, 0); BWN_RF_MASK(mac, 0x007a, 0x00f7); if (phy->rev >= 2) { BWN_PHY_SETMASK(mac, 0x0811, 0xffcf, 0x0030); BWN_PHY_SETMASK(mac, 0x0812, 0xffcf, 0x0010); } BWN_RF_SET(mac, 0x007a, 0x0080); DELAY(20); nrssi0 = (int16_t) ((BWN_PHY_READ(mac, 0x047f) >> 8) & 0x003f); if (nrssi0 >= 0x0020) nrssi0 -= 0x0040; /* * Calculate nrssi1 */ BWN_RF_MASK(mac, 0x007a, 0x007f); if (phy->rev >= 2) BWN_PHY_SETMASK(mac, 0x0003, 0xff9f, 0x0040); BWN_WRITE_2(mac, BWN_CHANNEL_EXT, BWN_READ_2(mac, BWN_CHANNEL_EXT) | 0x2000); BWN_RF_SET(mac, 0x007a, 0x000f); BWN_PHY_WRITE(mac, 0x0015, 0xf330); if (phy->rev >= 2) { BWN_PHY_SETMASK(mac, 0x0812, 0xffcf, 0x0020); BWN_PHY_SETMASK(mac, 0x0811, 0xffcf, 0x0020); } bwn_set_all_gains(mac, 3, 0, 1); if (phy->rf_rev == 8) { BWN_RF_WRITE(mac, 0x0043, 0x001f); } else { tmp = BWN_RF_READ(mac, 0x0052) & 0xff0f; BWN_RF_WRITE(mac, 0x0052, tmp | 0x0060); tmp = BWN_RF_READ(mac, 0x0043) & 0xfff0; BWN_RF_WRITE(mac, 0x0043, tmp | 0x0009); } BWN_PHY_WRITE(mac, 0x005a, 0x0480); BWN_PHY_WRITE(mac, 0x0059, 0x0810); BWN_PHY_WRITE(mac, 0x0058, 0x000d); DELAY(20); nrssi1 = (int16_t) ((BWN_PHY_READ(mac, 0x047f) >> 8) & 0x003f); /* * Install calculated narrow RSSI values */ if (nrssi1 >= 0x0020) nrssi1 -= 0x0040; if (nrssi0 == nrssi1) pg->pg_nrssi_slope = 0x00010000; else pg->pg_nrssi_slope = 0x00400000 / (nrssi0 - nrssi1); if (nrssi0 >= -4) { pg->pg_nrssi[0] = nrssi1; pg->pg_nrssi[1] = nrssi0; } /* * Restore saved RF/PHY registers */ if (phy->rev >= 3) { for (phy3_idx = 0; phy3_idx < 4; ++phy3_idx) { BWN_PHY_WRITE(mac, save_phy3_regs[phy3_idx], save_phy3[phy3_idx]); } } if (phy->rev >= 2) { BWN_PHY_MASK(mac, 0x0812, 0xffcf); BWN_PHY_MASK(mac, 0x0811, 0xffcf); } for (i = 0; i < SAVE_RF_MAX; ++i) BWN_RF_WRITE(mac, save_rf_regs[i], save_rf[i]); BWN_WRITE_2(mac, 0x03e2, ant_div); BWN_WRITE_2(mac, 0x03e6, phy0); BWN_WRITE_2(mac, BWN_CHANNEL_EXT, chan_ex); for (i = 0; i < SAVE_PHY_COMM_MAX; ++i) BWN_PHY_WRITE(mac, save_phy_comm_regs[i], save_phy_comm[i]); bwn_spu_workaround(mac, phy->chan); BWN_PHY_SET(mac, 0x0802, (0x0001 | 0x0002)); bwn_set_original_gains(mac); BWN_PHY_SET(mac, BWN_PHY_G_CRS, 0x8000); if (phy->rev >= 3) { for (; phy3_idx < SAVE_PHY3_MAX; ++phy3_idx) { BWN_PHY_WRITE(mac, save_phy3_regs[phy3_idx], save_phy3[phy3_idx]); } } delta = 0x1f - pg->pg_nrssi[0]; for (i = 0; i < 64; i++) { tmp32 = (((i - delta) * pg->pg_nrssi_slope) / 0x10000) + 0x3a; tmp32 = MIN(MAX(tmp32, 0), 0x3f); pg->pg_nrssi_lt[i] = tmp32; } bwn_nrssi_threshold(mac); #undef SAVE_RF_MAX #undef SAVE_PHY_COMM_MAX #undef SAVE_PHY3_MAX } static void bwn_nrssi_offset(struct bwn_mac *mac) { #define SAVE_RF_MAX 2 #define SAVE_PHY_COMM_MAX 10 #define SAVE_PHY6_MAX 8 static const uint16_t save_rf_regs[SAVE_RF_MAX] = { 0x7a, 0x43 }; static const uint16_t save_phy_comm_regs[SAVE_PHY_COMM_MAX] = { 0x0001, 0x0811, 0x0812, 0x0814, 0x0815, 0x005a, 0x0059, 0x0058, 0x000a, 0x0003 }; static const uint16_t save_phy6_regs[SAVE_PHY6_MAX] = { 0x002e, 0x002f, 0x080f, 0x0810, 0x0801, 0x0060, 0x0014, 0x0478 }; struct bwn_phy *phy = &mac->mac_phy; int i, phy6_idx = 0; uint16_t save_rf[SAVE_RF_MAX]; uint16_t save_phy_comm[SAVE_PHY_COMM_MAX]; uint16_t save_phy6[SAVE_PHY6_MAX]; int16_t nrssi; uint16_t saved = 0xffff; for (i = 0; i < SAVE_PHY_COMM_MAX; ++i) save_phy_comm[i] = BWN_PHY_READ(mac, save_phy_comm_regs[i]); for (i = 0; i < SAVE_RF_MAX; ++i) save_rf[i] = BWN_RF_READ(mac, save_rf_regs[i]); BWN_PHY_MASK(mac, 0x0429, 0x7fff); BWN_PHY_SETMASK(mac, 0x0001, 0x3fff, 0x4000); BWN_PHY_SET(mac, 0x0811, 0x000c); BWN_PHY_SETMASK(mac, 0x0812, 0xfff3, 0x0004); BWN_PHY_MASK(mac, 0x0802, ~(0x1 | 0x2)); if (phy->rev >= 6) { for (i = 0; i < SAVE_PHY6_MAX; ++i) save_phy6[i] = BWN_PHY_READ(mac, save_phy6_regs[i]); BWN_PHY_WRITE(mac, 0x002e, 0); BWN_PHY_WRITE(mac, 0x002f, 0); BWN_PHY_WRITE(mac, 0x080f, 0); BWN_PHY_WRITE(mac, 0x0810, 0); BWN_PHY_SET(mac, 0x0478, 0x0100); BWN_PHY_SET(mac, 0x0801, 0x0040); BWN_PHY_SET(mac, 0x0060, 0x0040); BWN_PHY_SET(mac, 0x0014, 0x0200); } BWN_RF_SET(mac, 0x007a, 0x0070); BWN_RF_SET(mac, 0x007a, 0x0080); DELAY(30); nrssi = (int16_t) ((BWN_PHY_READ(mac, 0x047f) >> 8) & 0x003f); if (nrssi >= 0x20) nrssi -= 0x40; if (nrssi == 31) { for (i = 7; i >= 4; i--) { BWN_RF_WRITE(mac, 0x007b, i); DELAY(20); nrssi = (int16_t) ((BWN_PHY_READ(mac, 0x047f) >> 8) & 0x003f); if (nrssi >= 0x20) nrssi -= 0x40; if (nrssi < 31 && saved == 0xffff) saved = i; } if (saved == 0xffff) saved = 4; } else { BWN_RF_MASK(mac, 0x007a, 0x007f); if (phy->rev != 1) { BWN_PHY_SET(mac, 0x0814, 0x0001); BWN_PHY_MASK(mac, 0x0815, 0xfffe); } BWN_PHY_SET(mac, 0x0811, 0x000c); BWN_PHY_SET(mac, 0x0812, 0x000c); BWN_PHY_SET(mac, 0x0811, 0x0030); BWN_PHY_SET(mac, 0x0812, 0x0030); BWN_PHY_WRITE(mac, 0x005a, 0x0480); BWN_PHY_WRITE(mac, 0x0059, 0x0810); BWN_PHY_WRITE(mac, 0x0058, 0x000d); if (phy->rev == 0) BWN_PHY_WRITE(mac, 0x0003, 0x0122); else BWN_PHY_SET(mac, 0x000a, 0x2000); if (phy->rev != 1) { BWN_PHY_SET(mac, 0x0814, 0x0004); BWN_PHY_MASK(mac, 0x0815, 0xfffb); } BWN_PHY_SETMASK(mac, 0x0003, 0xff9f, 0x0040); BWN_RF_SET(mac, 0x007a, 0x000f); bwn_set_all_gains(mac, 3, 0, 1); BWN_RF_SETMASK(mac, 0x0043, 0x00f0, 0x000f); DELAY(30); nrssi = (int16_t) ((BWN_PHY_READ(mac, 0x047f) >> 8) & 0x003f); if (nrssi >= 0x20) nrssi -= 0x40; if (nrssi == -32) { for (i = 0; i < 4; i++) { BWN_RF_WRITE(mac, 0x007b, i); DELAY(20); nrssi = (int16_t)((BWN_PHY_READ(mac, 0x047f) >> 8) & 0x003f); if (nrssi >= 0x20) nrssi -= 0x40; if (nrssi > -31 && saved == 0xffff) saved = i; } if (saved == 0xffff) saved = 3; } else saved = 0; } BWN_RF_WRITE(mac, 0x007b, saved); /* * Restore saved RF/PHY registers */ if (phy->rev >= 6) { for (phy6_idx = 0; phy6_idx < 4; ++phy6_idx) { BWN_PHY_WRITE(mac, save_phy6_regs[phy6_idx], save_phy6[phy6_idx]); } } if (phy->rev != 1) { for (i = 3; i < 5; i++) BWN_PHY_WRITE(mac, save_phy_comm_regs[i], save_phy_comm[i]); } for (i = 5; i < SAVE_PHY_COMM_MAX; i++) BWN_PHY_WRITE(mac, save_phy_comm_regs[i], save_phy_comm[i]); for (i = SAVE_RF_MAX - 1; i >= 0; --i) BWN_RF_WRITE(mac, save_rf_regs[i], save_rf[i]); BWN_PHY_WRITE(mac, 0x0802, BWN_PHY_READ(mac, 0x0802) | 0x1 | 0x2); BWN_PHY_SET(mac, 0x0429, 0x8000); bwn_set_original_gains(mac); if (phy->rev >= 6) { for (; phy6_idx < SAVE_PHY6_MAX; ++phy6_idx) { BWN_PHY_WRITE(mac, save_phy6_regs[phy6_idx], save_phy6[phy6_idx]); } } BWN_PHY_WRITE(mac, save_phy_comm_regs[0], save_phy_comm[0]); BWN_PHY_WRITE(mac, save_phy_comm_regs[2], save_phy_comm[2]); BWN_PHY_WRITE(mac, save_phy_comm_regs[1], save_phy_comm[1]); } static void bwn_set_all_gains(struct bwn_mac *mac, int16_t first, int16_t second, int16_t third) { struct bwn_phy *phy = &mac->mac_phy; uint16_t i; uint16_t start = 0x08, end = 0x18; uint16_t tmp; uint16_t table; if (phy->rev <= 1) { start = 0x10; end = 0x20; } table = BWN_OFDMTAB_GAINX; if (phy->rev <= 1) table = BWN_OFDMTAB_GAINX_R1; for (i = 0; i < 4; i++) bwn_ofdmtab_write_2(mac, table, i, first); for (i = start; i < end; i++) bwn_ofdmtab_write_2(mac, table, i, second); if (third != -1) { tmp = ((uint16_t) third << 14) | ((uint16_t) third << 6); BWN_PHY_SETMASK(mac, 0x04a0, 0xbfbf, tmp); BWN_PHY_SETMASK(mac, 0x04a1, 0xbfbf, tmp); BWN_PHY_SETMASK(mac, 0x04a2, 0xbfbf, tmp); } bwn_dummy_transmission(mac, 0, 1); } static void bwn_set_original_gains(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; uint16_t i, tmp; uint16_t table; uint16_t start = 0x0008, end = 0x0018; if (phy->rev <= 1) { start = 0x0010; end = 0x0020; } table = BWN_OFDMTAB_GAINX; if (phy->rev <= 1) table = BWN_OFDMTAB_GAINX_R1; for (i = 0; i < 4; i++) { tmp = (i & 0xfffc); tmp |= (i & 0x0001) << 1; tmp |= (i & 0x0002) >> 1; bwn_ofdmtab_write_2(mac, table, i, tmp); } for (i = start; i < end; i++) bwn_ofdmtab_write_2(mac, table, i, i - start); BWN_PHY_SETMASK(mac, 0x04a0, 0xbfbf, 0x4040); BWN_PHY_SETMASK(mac, 0x04a1, 0xbfbf, 0x4040); BWN_PHY_SETMASK(mac, 0x04a2, 0xbfbf, 0x4000); bwn_dummy_transmission(mac, 0, 1); } static void bwn_phy_hwpctl_init(struct bwn_mac *mac) { struct siba_softc *bus = mac->mac_sd->sd_bus; struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_rfatt old_rfatt, rfatt; struct bwn_bbatt old_bbatt, bbatt; uint8_t old_txctl = 0; KASSERT(phy->type == BWN_PHYTYPE_G, ("%s:%d: fail", __func__, __LINE__)); if ((bus->siba_board_vendor == SIBA_BOARDVENDOR_BCM) && (bus->siba_board_type == SIBA_BOARD_BU4306)) return; BWN_PHY_WRITE(mac, 0x0028, 0x8018); BWN_WRITE_2(mac, BWN_PHY0, BWN_READ_2(mac, BWN_PHY0) & 0xffdf); if (!phy->gmode) return; bwn_hwpctl_early_init(mac); if (pg->pg_curtssi == 0) { if (phy->rf_ver == 0x2050 && phy->analog == 0) { BWN_RF_SETMASK(mac, 0x0076, 0x00f7, 0x0084); } else { memcpy(&old_rfatt, &pg->pg_rfatt, sizeof(old_rfatt)); memcpy(&old_bbatt, &pg->pg_bbatt, sizeof(old_bbatt)); old_txctl = pg->pg_txctl; bbatt.att = 11; if (phy->rf_rev == 8) { rfatt.att = 15; rfatt.padmix = 1; } else { rfatt.att = 9; rfatt.padmix = 0; } bwn_phy_g_set_txpwr_sub(mac, &bbatt, &rfatt, 0); } bwn_dummy_transmission(mac, 0, 1); pg->pg_curtssi = BWN_PHY_READ(mac, BWN_PHY_TSSI); if (phy->rf_ver == 0x2050 && phy->analog == 0) BWN_RF_MASK(mac, 0x0076, 0xff7b); else bwn_phy_g_set_txpwr_sub(mac, &old_bbatt, &old_rfatt, old_txctl); } bwn_hwpctl_init_gphy(mac); /* clear TSSI */ bwn_shm_write_2(mac, BWN_SHARED, 0x0058, 0x7f7f); bwn_shm_write_2(mac, BWN_SHARED, 0x005a, 0x7f7f); bwn_shm_write_2(mac, BWN_SHARED, 0x0070, 0x7f7f); bwn_shm_write_2(mac, BWN_SHARED, 0x0072, 0x7f7f); } static void bwn_hwpctl_early_init(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; if (!bwn_has_hwpctl(mac)) { BWN_PHY_WRITE(mac, 0x047a, 0xc111); return; } BWN_PHY_MASK(mac, 0x0036, 0xfeff); BWN_PHY_WRITE(mac, 0x002f, 0x0202); BWN_PHY_SET(mac, 0x047c, 0x0002); BWN_PHY_SET(mac, 0x047a, 0xf000); if (phy->rf_ver == 0x2050 && phy->rf_rev == 8) { BWN_PHY_SETMASK(mac, 0x047a, 0xff0f, 0x0010); BWN_PHY_SET(mac, 0x005d, 0x8000); BWN_PHY_SETMASK(mac, 0x004e, 0xffc0, 0x0010); BWN_PHY_WRITE(mac, 0x002e, 0xc07f); BWN_PHY_SET(mac, 0x0036, 0x0400); } else { BWN_PHY_SET(mac, 0x0036, 0x0200); BWN_PHY_SET(mac, 0x0036, 0x0400); BWN_PHY_MASK(mac, 0x005d, 0x7fff); BWN_PHY_MASK(mac, 0x004f, 0xfffe); BWN_PHY_SETMASK(mac, 0x004e, 0xffc0, 0x0010); BWN_PHY_WRITE(mac, 0x002e, 0xc07f); BWN_PHY_SETMASK(mac, 0x047a, 0xff0f, 0x0010); } } static void bwn_hwpctl_init_gphy(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; int i; uint16_t nr_written = 0, tmp, value; uint8_t rf, bb; if (!bwn_has_hwpctl(mac)) { bwn_hf_write(mac, bwn_hf_read(mac) & ~BWN_HF_HW_POWERCTL); return; } BWN_PHY_SETMASK(mac, 0x0036, 0xffc0, (pg->pg_idletssi - pg->pg_curtssi)); BWN_PHY_SETMASK(mac, 0x0478, 0xff00, (pg->pg_idletssi - pg->pg_curtssi)); for (i = 0; i < 32; i++) bwn_ofdmtab_write_2(mac, 0x3c20, i, pg->pg_tssi2dbm[i]); for (i = 32; i < 64; i++) bwn_ofdmtab_write_2(mac, 0x3c00, i - 32, pg->pg_tssi2dbm[i]); for (i = 0; i < 64; i += 2) { value = (uint16_t) pg->pg_tssi2dbm[i]; value |= ((uint16_t) pg->pg_tssi2dbm[i + 1]) << 8; BWN_PHY_WRITE(mac, 0x380 + (i / 2), value); } for (rf = 0; rf < lo->rfatt.len; rf++) { for (bb = 0; bb < lo->bbatt.len; bb++) { if (nr_written >= 0x40) return; tmp = lo->bbatt.array[bb].att; tmp <<= 8; if (phy->rf_rev == 8) tmp |= 0x50; else tmp |= 0x40; tmp |= lo->rfatt.array[rf].att; BWN_PHY_WRITE(mac, 0x3c0 + nr_written, tmp); nr_written++; } } BWN_PHY_MASK(mac, 0x0060, 0xffbf); BWN_PHY_WRITE(mac, 0x0014, 0x0000); KASSERT(phy->rev >= 6, ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_SET(mac, 0x0478, 0x0800); BWN_PHY_MASK(mac, 0x0478, 0xfeff); BWN_PHY_MASK(mac, 0x0801, 0xffbf); bwn_phy_g_dc_lookup_init(mac, 1); bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_HW_POWERCTL); } static void bwn_phy_g_switch_chan(struct bwn_mac *mac, int channel, uint8_t spu) { struct siba_softc *siba = mac->mac_sd->sd_bus; if (spu != 0) bwn_spu_workaround(mac, channel); BWN_WRITE_2(mac, BWN_CHANNEL, bwn_phy_g_chan2freq(channel)); if (channel == 14) { if (siba->siba_sprom.ccode == SIBA_CCODE_JAPAN) bwn_hf_write(mac, bwn_hf_read(mac) & ~BWN_HF_JAPAN_CHAN14_OFF); else bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_JAPAN_CHAN14_OFF); BWN_WRITE_2(mac, BWN_CHANNEL_EXT, BWN_READ_2(mac, BWN_CHANNEL_EXT) | (1 << 11)); return; } BWN_WRITE_2(mac, BWN_CHANNEL_EXT, BWN_READ_2(mac, BWN_CHANNEL_EXT) & 0xf7bf); } static uint16_t bwn_phy_g_chan2freq(uint8_t channel) { static const uint8_t bwn_phy_g_rf_channels[] = BWN_PHY_G_RF_CHANNELS; KASSERT(channel >= 1 && channel <= 14, ("%s:%d: fail", __func__, __LINE__)); return (bwn_phy_g_rf_channels[channel - 1]); } static void bwn_phy_g_set_txpwr_sub(struct bwn_mac *mac, const struct bwn_bbatt *bbatt, const struct bwn_rfatt *rfatt, uint8_t txctl) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct bwn_txpwr_loctl *lo = &pg->pg_loctl; uint16_t bb, rf; uint16_t tx_bias, tx_magn; bb = bbatt->att; rf = rfatt->att; tx_bias = lo->tx_bias; tx_magn = lo->tx_magn; if (tx_bias == 0xff) tx_bias = 0; pg->pg_txctl = txctl; memmove(&pg->pg_rfatt, rfatt, sizeof(*rfatt)); pg->pg_rfatt.padmix = (txctl & BWN_TXCTL_TXMIX) ? 1 : 0; memmove(&pg->pg_bbatt, bbatt, sizeof(*bbatt)); bwn_phy_g_set_bbatt(mac, bb); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_RADIO_ATT, rf); if (phy->rf_ver == 0x2050 && phy->rf_rev == 8) BWN_RF_WRITE(mac, 0x43, (rf & 0x000f) | (txctl & 0x0070)); else { BWN_RF_SETMASK(mac, 0x43, 0xfff0, (rf & 0x000f)); BWN_RF_SETMASK(mac, 0x52, ~0x0070, (txctl & 0x0070)); } if (BWN_HAS_TXMAG(phy)) BWN_RF_WRITE(mac, 0x52, tx_magn | tx_bias); else BWN_RF_SETMASK(mac, 0x52, 0xfff0, (tx_bias & 0x000f)); bwn_lo_g_adjust(mac); } static void bwn_phy_g_set_bbatt(struct bwn_mac *mac, uint16_t bbatt) { struct bwn_phy *phy = &mac->mac_phy; if (phy->analog == 0) { BWN_WRITE_2(mac, BWN_PHY0, (BWN_READ_2(mac, BWN_PHY0) & 0xfff0) | bbatt); return; } if (phy->analog > 1) { BWN_PHY_SETMASK(mac, BWN_PHY_DACCTL, 0xffc3, bbatt << 2); return; } BWN_PHY_SETMASK(mac, BWN_PHY_DACCTL, 0xff87, bbatt << 3); } static uint16_t bwn_rf_2050_rfoverval(struct bwn_mac *mac, uint16_t reg, uint32_t lpd) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_g *pg = &phy->phy_g; struct siba_sprom *sprom = &(mac->mac_sd->sd_bus->siba_sprom); int max_lb_gain; uint16_t extlna; uint16_t i; if (phy->gmode == 0) return (0); if (BWN_HAS_LOOPBACK(phy)) { max_lb_gain = pg->pg_max_lb_gain; max_lb_gain += (phy->rf_rev == 8) ? 0x3e : 0x26; if (max_lb_gain >= 0x46) { extlna = 0x3000; max_lb_gain -= 0x46; } else if (max_lb_gain >= 0x3a) { extlna = 0x1000; max_lb_gain -= 0x3a; } else if (max_lb_gain >= 0x2e) { extlna = 0x2000; max_lb_gain -= 0x2e; } else { extlna = 0; max_lb_gain -= 0x10; } for (i = 0; i < 16; i++) { max_lb_gain -= (i * 6); if (max_lb_gain < 6) break; } if ((phy->rev < 7) || !(sprom->bf_lo & BWN_BFL_EXTLNA)) { if (reg == BWN_PHY_RFOVER) { return (0x1b3); } else if (reg == BWN_PHY_RFOVERVAL) { extlna |= (i << 8); switch (lpd) { case BWN_LPD(0, 1, 1): return (0x0f92); case BWN_LPD(0, 0, 1): case BWN_LPD(1, 0, 1): return (0x0092 | extlna); case BWN_LPD(1, 0, 0): return (0x0093 | extlna); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } else { if (reg == BWN_PHY_RFOVER) return (0x9b3); if (reg == BWN_PHY_RFOVERVAL) { if (extlna) extlna |= 0x8000; extlna |= (i << 8); switch (lpd) { case BWN_LPD(0, 1, 1): return (0x8f92); case BWN_LPD(0, 0, 1): return (0x8092 | extlna); case BWN_LPD(1, 0, 1): return (0x2092 | extlna); case BWN_LPD(1, 0, 0): return (0x2093 | extlna); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } return (0); } if ((phy->rev < 7) || !(sprom->bf_lo & BWN_BFL_EXTLNA)) { if (reg == BWN_PHY_RFOVER) { return (0x1b3); } else if (reg == BWN_PHY_RFOVERVAL) { switch (lpd) { case BWN_LPD(0, 1, 1): return (0x0fb2); case BWN_LPD(0, 0, 1): return (0x00b2); case BWN_LPD(1, 0, 1): return (0x30b2); case BWN_LPD(1, 0, 0): return (0x30b3); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } else { if (reg == BWN_PHY_RFOVER) { return (0x9b3); } else if (reg == BWN_PHY_RFOVERVAL) { switch (lpd) { case BWN_LPD(0, 1, 1): return (0x8fb2); case BWN_LPD(0, 0, 1): return (0x80b2); case BWN_LPD(1, 0, 1): return (0x20b2); case BWN_LPD(1, 0, 0): return (0x20b3); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } return (0); } static void bwn_spu_workaround(struct bwn_mac *mac, uint8_t channel) { if (mac->mac_phy.rf_ver != 0x2050 || mac->mac_phy.rf_rev >= 6) return; BWN_WRITE_2(mac, BWN_CHANNEL, (channel <= 10) ? bwn_phy_g_chan2freq(channel + 4) : bwn_phy_g_chan2freq(1)); DELAY(1000); BWN_WRITE_2(mac, BWN_CHANNEL, bwn_phy_g_chan2freq(channel)); } static int bwn_fw_gets(struct bwn_mac *mac, enum bwn_fwtype type) { struct bwn_softc *sc = mac->mac_sc; struct bwn_fw *fw = &mac->mac_fw; const uint8_t rev = mac->mac_sd->sd_id.sd_rev; const char *filename; uint32_t high; int error; /* microcode */ if (rev >= 5 && rev <= 10) filename = "ucode5"; else if (rev >= 11 && rev <= 12) filename = "ucode11"; else if (rev == 13) filename = "ucode13"; else if (rev == 14) filename = "ucode14"; else if (rev >= 15) filename = "ucode15"; else { device_printf(sc->sc_dev, "no ucode for rev %d\n", rev); bwn_release_firmware(mac); return (EOPNOTSUPP); } error = bwn_fw_get(mac, type, filename, &fw->ucode); if (error) { bwn_release_firmware(mac); return (error); } /* PCM */ KASSERT(fw->no_pcmfile == 0, ("%s:%d fail", __func__, __LINE__)); if (rev >= 5 && rev <= 10) { error = bwn_fw_get(mac, type, "pcm5", &fw->pcm); if (error == ENOENT) fw->no_pcmfile = 1; else if (error) { bwn_release_firmware(mac); return (error); } } else if (rev < 11) { device_printf(sc->sc_dev, "no PCM for rev %d\n", rev); return (EOPNOTSUPP); } /* initvals */ high = siba_read_4(mac->mac_sd, SIBA_TGSHIGH); switch (mac->mac_phy.type) { case BWN_PHYTYPE_A: if (rev < 5 || rev > 10) goto fail1; if (high & BWN_TGSHIGH_HAVE_2GHZ) filename = "a0g1initvals5"; else filename = "a0g0initvals5"; break; case BWN_PHYTYPE_G: if (rev >= 5 && rev <= 10) filename = "b0g0initvals5"; else if (rev >= 13) filename = "b0g0initvals13"; else goto fail1; break; case BWN_PHYTYPE_LP: if (rev == 13) filename = "lp0initvals13"; else if (rev == 14) filename = "lp0initvals14"; else if (rev >= 15) filename = "lp0initvals15"; else goto fail1; break; case BWN_PHYTYPE_N: if (rev >= 11 && rev <= 12) filename = "n0initvals11"; else goto fail1; break; default: goto fail1; } error = bwn_fw_get(mac, type, filename, &fw->initvals); if (error) { bwn_release_firmware(mac); return (error); } /* bandswitch initvals */ switch (mac->mac_phy.type) { case BWN_PHYTYPE_A: if (rev >= 5 && rev <= 10) { if (high & BWN_TGSHIGH_HAVE_2GHZ) filename = "a0g1bsinitvals5"; else filename = "a0g0bsinitvals5"; } else if (rev >= 11) filename = NULL; else goto fail1; break; case BWN_PHYTYPE_G: if (rev >= 5 && rev <= 10) filename = "b0g0bsinitvals5"; else if (rev >= 11) filename = NULL; else goto fail1; break; case BWN_PHYTYPE_LP: if (rev == 13) filename = "lp0bsinitvals13"; else if (rev == 14) filename = "lp0bsinitvals14"; else if (rev >= 15) filename = "lp0bsinitvals15"; else goto fail1; break; case BWN_PHYTYPE_N: if (rev >= 11 && rev <= 12) filename = "n0bsinitvals11"; else goto fail1; break; default: goto fail1; } error = bwn_fw_get(mac, type, filename, &fw->initvals_band); if (error) { bwn_release_firmware(mac); return (error); } return (0); fail1: device_printf(sc->sc_dev, "no INITVALS for rev %d\n", rev); bwn_release_firmware(mac); return (EOPNOTSUPP); } static int bwn_fw_get(struct bwn_mac *mac, enum bwn_fwtype type, const char *name, struct bwn_fwfile *bfw) { const struct bwn_fwhdr *hdr; struct bwn_softc *sc = mac->mac_sc; const struct firmware *fw; char namebuf[64]; if (name == NULL) { bwn_do_release_fw(bfw); return (0); } if (bfw->filename != NULL) { if (bfw->type == type && (strcmp(bfw->filename, name) == 0)) return (0); bwn_do_release_fw(bfw); } snprintf(namebuf, sizeof(namebuf), "bwn%s_v4_%s", (type == BWN_FWTYPE_OPENSOURCE) ? "-open" : "", name); /* XXX Sleeping on "fwload" with the non-sleepable locks held */ fw = firmware_get(namebuf); if (fw == NULL) { device_printf(sc->sc_dev, "the fw file(%s) not found\n", namebuf); return (ENOENT); } if (fw->datasize < sizeof(struct bwn_fwhdr)) goto fail; hdr = (const struct bwn_fwhdr *)(fw->data); switch (hdr->type) { case BWN_FWTYPE_UCODE: case BWN_FWTYPE_PCM: if (be32toh(hdr->size) != (fw->datasize - sizeof(struct bwn_fwhdr))) goto fail; /* FALLTHROUGH */ case BWN_FWTYPE_IV: if (hdr->ver != 1) goto fail; break; default: goto fail; } bfw->filename = name; bfw->fw = fw; bfw->type = type; return (0); fail: device_printf(sc->sc_dev, "the fw file(%s) format error\n", namebuf); if (fw != NULL) firmware_put(fw, FIRMWARE_UNLOAD); return (EPROTO); } static void bwn_release_firmware(struct bwn_mac *mac) { bwn_do_release_fw(&mac->mac_fw.ucode); bwn_do_release_fw(&mac->mac_fw.pcm); bwn_do_release_fw(&mac->mac_fw.initvals); bwn_do_release_fw(&mac->mac_fw.initvals_band); } static void bwn_do_release_fw(struct bwn_fwfile *bfw) { if (bfw->fw != NULL) firmware_put(bfw->fw, FIRMWARE_UNLOAD); bfw->fw = NULL; bfw->filename = NULL; } static int bwn_fw_loaducode(struct bwn_mac *mac) { #define GETFWOFFSET(fwp, offset) \ ((const uint32_t *)((const char *)fwp.fw->data + offset)) #define GETFWSIZE(fwp, offset) \ ((fwp.fw->datasize - offset) / sizeof(uint32_t)) struct bwn_softc *sc = mac->mac_sc; const uint32_t *data; unsigned int i; uint32_t ctl; uint16_t date, fwcaps, time; int error = 0; ctl = BWN_READ_4(mac, BWN_MACCTL); ctl |= BWN_MACCTL_MCODE_JMP0; KASSERT(!(ctl & BWN_MACCTL_MCODE_RUN), ("%s:%d: fail", __func__, __LINE__)); BWN_WRITE_4(mac, BWN_MACCTL, ctl); for (i = 0; i < 64; i++) bwn_shm_write_2(mac, BWN_SCRATCH, i, 0); for (i = 0; i < 4096; i += 2) bwn_shm_write_2(mac, BWN_SHARED, i, 0); data = GETFWOFFSET(mac->mac_fw.ucode, sizeof(struct bwn_fwhdr)); bwn_shm_ctlword(mac, BWN_UCODE | BWN_SHARED_AUTOINC, 0x0000); for (i = 0; i < GETFWSIZE(mac->mac_fw.ucode, sizeof(struct bwn_fwhdr)); i++) { BWN_WRITE_4(mac, BWN_SHM_DATA, be32toh(data[i])); DELAY(10); } if (mac->mac_fw.pcm.fw) { data = GETFWOFFSET(mac->mac_fw.pcm, sizeof(struct bwn_fwhdr)); bwn_shm_ctlword(mac, BWN_HW, 0x01ea); BWN_WRITE_4(mac, BWN_SHM_DATA, 0x00004000); bwn_shm_ctlword(mac, BWN_HW, 0x01eb); for (i = 0; i < GETFWSIZE(mac->mac_fw.pcm, sizeof(struct bwn_fwhdr)); i++) { BWN_WRITE_4(mac, BWN_SHM_DATA, be32toh(data[i])); DELAY(10); } } BWN_WRITE_4(mac, BWN_INTR_REASON, BWN_INTR_ALL); BWN_WRITE_4(mac, BWN_MACCTL, (BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_MCODE_JMP0) | BWN_MACCTL_MCODE_RUN); for (i = 0; i < 21; i++) { if (BWN_READ_4(mac, BWN_INTR_REASON) == BWN_INTR_MAC_SUSPENDED) break; if (i >= 20) { device_printf(sc->sc_dev, "ucode timeout\n"); error = ENXIO; goto error; } DELAY(50000); } BWN_READ_4(mac, BWN_INTR_REASON); mac->mac_fw.rev = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_REV); if (mac->mac_fw.rev <= 0x128) { device_printf(sc->sc_dev, "the firmware is too old\n"); error = EOPNOTSUPP; goto error; } mac->mac_fw.patch = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_PATCH); date = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_DATE); mac->mac_fw.opensource = (date == 0xffff); if (bwn_wme != 0) mac->mac_flags |= BWN_MAC_FLAG_WME; mac->mac_flags |= BWN_MAC_FLAG_HWCRYPTO; time = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_TIME); if (mac->mac_fw.opensource == 0) { device_printf(sc->sc_dev, "firmware version (rev %u patch %u date %#x time %#x)\n", mac->mac_fw.rev, mac->mac_fw.patch, date, time); if (mac->mac_fw.no_pcmfile) device_printf(sc->sc_dev, "no HW crypto acceleration due to pcm5\n"); } else { mac->mac_fw.patch = time; fwcaps = bwn_fwcaps_read(mac); if (!(fwcaps & BWN_FWCAPS_HWCRYPTO) || mac->mac_fw.no_pcmfile) { device_printf(sc->sc_dev, "disabling HW crypto acceleration\n"); mac->mac_flags &= ~BWN_MAC_FLAG_HWCRYPTO; } if (!(fwcaps & BWN_FWCAPS_WME)) { device_printf(sc->sc_dev, "disabling WME support\n"); mac->mac_flags &= ~BWN_MAC_FLAG_WME; } } if (BWN_ISOLDFMT(mac)) device_printf(sc->sc_dev, "using old firmware image\n"); return (0); error: BWN_WRITE_4(mac, BWN_MACCTL, (BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_MCODE_RUN) | BWN_MACCTL_MCODE_JMP0); return (error); #undef GETFWSIZE #undef GETFWOFFSET } /* OpenFirmware only */ static uint16_t bwn_fwcaps_read(struct bwn_mac *mac) { KASSERT(mac->mac_fw.opensource == 1, ("%s:%d: fail", __func__, __LINE__)); return (bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_FWCAPS)); } static int bwn_fwinitvals_write(struct bwn_mac *mac, const struct bwn_fwinitvals *ivals, size_t count, size_t array_size) { #define GET_NEXTIV16(iv) \ ((const struct bwn_fwinitvals *)((const uint8_t *)(iv) + \ sizeof(uint16_t) + sizeof(uint16_t))) #define GET_NEXTIV32(iv) \ ((const struct bwn_fwinitvals *)((const uint8_t *)(iv) + \ sizeof(uint16_t) + sizeof(uint32_t))) struct bwn_softc *sc = mac->mac_sc; const struct bwn_fwinitvals *iv; uint16_t offset; size_t i; uint8_t bit32; KASSERT(sizeof(struct bwn_fwinitvals) == 6, ("%s:%d: fail", __func__, __LINE__)); iv = ivals; for (i = 0; i < count; i++) { if (array_size < sizeof(iv->offset_size)) goto fail; array_size -= sizeof(iv->offset_size); offset = be16toh(iv->offset_size); bit32 = (offset & BWN_FWINITVALS_32BIT) ? 1 : 0; offset &= BWN_FWINITVALS_OFFSET_MASK; if (offset >= 0x1000) goto fail; if (bit32) { if (array_size < sizeof(iv->data.d32)) goto fail; array_size -= sizeof(iv->data.d32); BWN_WRITE_4(mac, offset, be32toh(iv->data.d32)); iv = GET_NEXTIV32(iv); } else { if (array_size < sizeof(iv->data.d16)) goto fail; array_size -= sizeof(iv->data.d16); BWN_WRITE_2(mac, offset, be16toh(iv->data.d16)); iv = GET_NEXTIV16(iv); } } if (array_size != 0) goto fail; return (0); fail: device_printf(sc->sc_dev, "initvals: invalid format\n"); return (EPROTO); #undef GET_NEXTIV16 #undef GET_NEXTIV32 } static int bwn_switch_channel(struct bwn_mac *mac, int chan) { struct bwn_phy *phy = &(mac->mac_phy); struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t channelcookie, savedcookie; int error; if (chan == 0xffff) chan = phy->get_default_chan(mac); channelcookie = chan; if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) channelcookie |= 0x100; savedcookie = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_CHAN); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_CHAN, channelcookie); error = phy->switch_channel(mac, chan); if (error) goto fail; mac->mac_phy.chan = chan; DELAY(8000); return (0); fail: device_printf(sc->sc_dev, "failed to switch channel\n"); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_CHAN, savedcookie); return (error); } static uint16_t bwn_ant2phy(int antenna) { switch (antenna) { case BWN_ANT0: return (BWN_TX_PHY_ANT0); case BWN_ANT1: return (BWN_TX_PHY_ANT1); case BWN_ANT2: return (BWN_TX_PHY_ANT2); case BWN_ANT3: return (BWN_TX_PHY_ANT3); case BWN_ANTAUTO: return (BWN_TX_PHY_ANT01AUTO); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (0); } static void bwn_wme_load(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; int i; KASSERT(N(bwn_wme_shm_offsets) == N(sc->sc_wmeParams), ("%s:%d: fail", __func__, __LINE__)); bwn_mac_suspend(mac); for (i = 0; i < N(sc->sc_wmeParams); i++) bwn_wme_loadparams(mac, &(sc->sc_wmeParams[i]), bwn_wme_shm_offsets[i]); bwn_mac_enable(mac); } static void bwn_wme_loadparams(struct bwn_mac *mac, const struct wmeParams *p, uint16_t shm_offset) { #define SM(_v, _f) (((_v) << _f##_S) & _f) struct bwn_softc *sc = mac->mac_sc; uint16_t params[BWN_NR_WMEPARAMS]; int slot, tmp; unsigned int i; slot = BWN_READ_2(mac, BWN_RNG) & SM(p->wmep_logcwmin, WME_PARAM_LOGCWMIN); memset(¶ms, 0, sizeof(params)); DPRINTF(sc, BWN_DEBUG_WME, "wmep_txopLimit %d wmep_logcwmin %d " "wmep_logcwmax %d wmep_aifsn %d\n", p->wmep_txopLimit, p->wmep_logcwmin, p->wmep_logcwmax, p->wmep_aifsn); params[BWN_WMEPARAM_TXOP] = p->wmep_txopLimit * 32; params[BWN_WMEPARAM_CWMIN] = SM(p->wmep_logcwmin, WME_PARAM_LOGCWMIN); params[BWN_WMEPARAM_CWMAX] = SM(p->wmep_logcwmax, WME_PARAM_LOGCWMAX); params[BWN_WMEPARAM_CWCUR] = SM(p->wmep_logcwmin, WME_PARAM_LOGCWMIN); params[BWN_WMEPARAM_AIFS] = p->wmep_aifsn; params[BWN_WMEPARAM_BSLOTS] = slot; params[BWN_WMEPARAM_REGGAP] = slot + p->wmep_aifsn; for (i = 0; i < N(params); i++) { if (i == BWN_WMEPARAM_STATUS) { tmp = bwn_shm_read_2(mac, BWN_SHARED, shm_offset + (i * 2)); tmp |= 0x100; bwn_shm_write_2(mac, BWN_SHARED, shm_offset + (i * 2), tmp); } else { bwn_shm_write_2(mac, BWN_SHARED, shm_offset + (i * 2), params[i]); } } } static void bwn_mac_write_bssid(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; uint32_t tmp; int i; uint8_t mac_bssid[IEEE80211_ADDR_LEN * 2]; bwn_mac_setfilter(mac, BWN_MACFILTER_BSSID, sc->sc_bssid); memcpy(mac_bssid, sc->sc_macaddr, IEEE80211_ADDR_LEN); memcpy(mac_bssid + IEEE80211_ADDR_LEN, sc->sc_bssid, IEEE80211_ADDR_LEN); for (i = 0; i < N(mac_bssid); i += sizeof(uint32_t)) { tmp = (uint32_t) (mac_bssid[i + 0]); tmp |= (uint32_t) (mac_bssid[i + 1]) << 8; tmp |= (uint32_t) (mac_bssid[i + 2]) << 16; tmp |= (uint32_t) (mac_bssid[i + 3]) << 24; bwn_ram_write(mac, 0x20 + i, tmp); } } static void bwn_mac_setfilter(struct bwn_mac *mac, uint16_t offset, const uint8_t *macaddr) { static const uint8_t zero[IEEE80211_ADDR_LEN] = { 0 }; uint16_t data; if (!mac) macaddr = zero; offset |= 0x0020; BWN_WRITE_2(mac, BWN_MACFILTER_CONTROL, offset); data = macaddr[0]; data |= macaddr[1] << 8; BWN_WRITE_2(mac, BWN_MACFILTER_DATA, data); data = macaddr[2]; data |= macaddr[3] << 8; BWN_WRITE_2(mac, BWN_MACFILTER_DATA, data); data = macaddr[4]; data |= macaddr[5] << 8; BWN_WRITE_2(mac, BWN_MACFILTER_DATA, data); } static void bwn_key_dowrite(struct bwn_mac *mac, uint8_t index, uint8_t algorithm, const uint8_t *key, size_t key_len, const uint8_t *mac_addr) { uint8_t buf[BWN_SEC_KEYSIZE] = { 0, }; uint8_t per_sta_keys_start = 8; if (BWN_SEC_NEWAPI(mac)) per_sta_keys_start = 4; KASSERT(index < mac->mac_max_nr_keys, ("%s:%d: fail", __func__, __LINE__)); KASSERT(key_len <= BWN_SEC_KEYSIZE, ("%s:%d: fail", __func__, __LINE__)); if (index >= per_sta_keys_start) bwn_key_macwrite(mac, index, NULL); if (key) memcpy(buf, key, key_len); bwn_key_write(mac, index, algorithm, buf); if (index >= per_sta_keys_start) bwn_key_macwrite(mac, index, mac_addr); mac->mac_key[index].algorithm = algorithm; } static void bwn_key_macwrite(struct bwn_mac *mac, uint8_t index, const uint8_t *addr) { uint32_t addrtmp[2] = { 0, 0 }; uint8_t start = 8; if (BWN_SEC_NEWAPI(mac)) start = 4; KASSERT(index >= start, ("%s:%d: fail", __func__, __LINE__)); index -= start; if (addr) { addrtmp[0] = addr[0]; addrtmp[0] |= ((uint32_t) (addr[1]) << 8); addrtmp[0] |= ((uint32_t) (addr[2]) << 16); addrtmp[0] |= ((uint32_t) (addr[3]) << 24); addrtmp[1] = addr[4]; addrtmp[1] |= ((uint32_t) (addr[5]) << 8); } if (mac->mac_sd->sd_id.sd_rev >= 5) { bwn_shm_write_4(mac, BWN_RCMTA, (index * 2) + 0, addrtmp[0]); bwn_shm_write_2(mac, BWN_RCMTA, (index * 2) + 1, addrtmp[1]); } else { if (index >= 8) { bwn_shm_write_4(mac, BWN_SHARED, BWN_SHARED_PSM + (index * 6) + 0, addrtmp[0]); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PSM + (index * 6) + 4, addrtmp[1]); } } } static void bwn_key_write(struct bwn_mac *mac, uint8_t index, uint8_t algorithm, const uint8_t *key) { unsigned int i; uint32_t offset; uint16_t kidx, value; kidx = BWN_SEC_KEY2FW(mac, index); bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_KEYIDX_BLOCK + (kidx * 2), (kidx << 4) | algorithm); offset = mac->mac_ktp + (index * BWN_SEC_KEYSIZE); for (i = 0; i < BWN_SEC_KEYSIZE; i += 2) { value = key[i]; value |= (uint16_t)(key[i + 1]) << 8; bwn_shm_write_2(mac, BWN_SHARED, offset + i, value); } } static void bwn_phy_exit(struct bwn_mac *mac) { mac->mac_phy.rf_onoff(mac, 0); if (mac->mac_phy.exit != NULL) mac->mac_phy.exit(mac); } static void bwn_dma_free(struct bwn_mac *mac) { struct bwn_dma *dma; if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0) return; dma = &mac->mac_method.dma; bwn_dma_ringfree(&dma->rx); bwn_dma_ringfree(&dma->wme[WME_AC_BK]); bwn_dma_ringfree(&dma->wme[WME_AC_BE]); bwn_dma_ringfree(&dma->wme[WME_AC_VI]); bwn_dma_ringfree(&dma->wme[WME_AC_VO]); bwn_dma_ringfree(&dma->mcast); } static void bwn_core_stop(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; BWN_ASSERT_LOCKED(sc); if (mac->mac_status < BWN_MAC_STATUS_STARTED) return; callout_stop(&sc->sc_rfswitch_ch); callout_stop(&sc->sc_task_ch); callout_stop(&sc->sc_watchdog_ch); sc->sc_watchdog_timer = 0; BWN_WRITE_4(mac, BWN_INTR_MASK, 0); BWN_READ_4(mac, BWN_INTR_MASK); bwn_mac_suspend(mac); mac->mac_status = BWN_MAC_STATUS_INITED; } static int bwn_switch_band(struct bwn_softc *sc, struct ieee80211_channel *chan) { struct bwn_mac *up_dev = NULL; struct bwn_mac *down_dev; struct bwn_mac *mac; int err, status; uint8_t gmode; BWN_ASSERT_LOCKED(sc); TAILQ_FOREACH(mac, &sc->sc_maclist, mac_list) { if (IEEE80211_IS_CHAN_2GHZ(chan) && mac->mac_phy.supports_2ghz) { up_dev = mac; gmode = 1; } else if (IEEE80211_IS_CHAN_5GHZ(chan) && mac->mac_phy.supports_5ghz) { up_dev = mac; gmode = 0; } else { KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (EINVAL); } if (up_dev != NULL) break; } if (up_dev == NULL) { device_printf(sc->sc_dev, "Could not find a device\n"); return (ENODEV); } if (up_dev == sc->sc_curmac && sc->sc_curmac->mac_phy.gmode == gmode) return (0); device_printf(sc->sc_dev, "switching to %s-GHz band\n", IEEE80211_IS_CHAN_2GHZ(chan) ? "2" : "5"); down_dev = sc->sc_curmac;; status = down_dev->mac_status; if (status >= BWN_MAC_STATUS_STARTED) bwn_core_stop(down_dev); if (status >= BWN_MAC_STATUS_INITED) bwn_core_exit(down_dev); if (down_dev != up_dev) bwn_phy_reset(down_dev); up_dev->mac_phy.gmode = gmode; if (status >= BWN_MAC_STATUS_INITED) { err = bwn_core_init(up_dev); if (err) { device_printf(sc->sc_dev, "fatal: failed to initialize for %s-GHz\n", IEEE80211_IS_CHAN_2GHZ(chan) ? "2" : "5"); goto fail; } } if (status >= BWN_MAC_STATUS_STARTED) bwn_core_start(up_dev); KASSERT(up_dev->mac_status == status, ("%s: fail", __func__)); sc->sc_curmac = up_dev; return (0); fail: sc->sc_curmac = NULL; return (err); } static void bwn_rf_turnon(struct bwn_mac *mac) { bwn_mac_suspend(mac); mac->mac_phy.rf_onoff(mac, 1); mac->mac_phy.rf_on = 1; bwn_mac_enable(mac); } static void bwn_rf_turnoff(struct bwn_mac *mac) { bwn_mac_suspend(mac); mac->mac_phy.rf_onoff(mac, 0); mac->mac_phy.rf_on = 0; bwn_mac_enable(mac); } static void bwn_phy_reset(struct bwn_mac *mac) { struct siba_dev_softc *sd = mac->mac_sd; siba_write_4(sd, SIBA_TGSLOW, ((siba_read_4(sd, SIBA_TGSLOW) & ~BWN_TGSLOW_SUPPORT_G) | BWN_TGSLOW_PHYRESET) | SIBA_TGSLOW_FGC); DELAY(1000); siba_write_4(sd, SIBA_TGSLOW, (siba_read_4(sd, SIBA_TGSLOW) & ~SIBA_TGSLOW_FGC) | BWN_TGSLOW_PHYRESET); DELAY(1000); } static int bwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct bwn_vap *bvp = BWN_VAP(vap); struct ieee80211com *ic= vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; enum ieee80211_state ostate = vap->iv_state; struct bwn_softc *sc = ifp->if_softc; struct bwn_mac *mac = sc->sc_curmac; int error; DPRINTF(sc, BWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); error = bvp->bv_newstate(vap, nstate, arg); if (error != 0) return (error); BWN_LOCK(sc); bwn_led_newstate(mac, nstate); /* * Clear the BSSID when we stop a STA */ if (vap->iv_opmode == IEEE80211_M_STA) { if (ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) { /* * Clear out the BSSID. If we reassociate to * the same AP, this will reinialize things * correctly... */ if (ic->ic_opmode == IEEE80211_M_STA && (sc->sc_flags & BWN_FLAG_INVALID) == 0) { memset(sc->sc_bssid, 0, IEEE80211_ADDR_LEN); bwn_set_macaddr(mac); } } } if (vap->iv_opmode == IEEE80211_M_MONITOR) { /* XXX nothing to do? */ } else if (nstate == IEEE80211_S_RUN) { memcpy(sc->sc_bssid, vap->iv_bss->ni_bssid, IEEE80211_ADDR_LEN); memcpy(sc->sc_macaddr, IF_LLADDR(ifp), IEEE80211_ADDR_LEN); bwn_set_opmode(mac); bwn_set_pretbtt(mac); bwn_spu_setdelay(mac, 0); bwn_set_macaddr(mac); } BWN_UNLOCK(sc); return (error); } static void bwn_set_pretbtt(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ieee80211com *ic = sc->sc_ifp->if_l2com; uint16_t pretbtt; if (ic->ic_opmode == IEEE80211_M_IBSS) pretbtt = 2; else pretbtt = (mac->mac_phy.type == BWN_PHYTYPE_A) ? 120 : 250; bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PRETBTT, pretbtt); BWN_WRITE_2(mac, BWN_TSF_CFP_PRETBTT, pretbtt); } static int bwn_intr(void *arg) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; struct siba_softc *siba = mac->mac_sd->sd_bus; uint32_t reason; if (mac->mac_status < BWN_MAC_STATUS_STARTED || siba->siba_invalid) return (FILTER_STRAY); reason = BWN_READ_4(mac, BWN_INTR_REASON); if (reason == 0xffffffff) /* shared IRQ */ return (FILTER_STRAY); reason &= mac->mac_intr_mask; if (reason == 0) return (FILTER_HANDLED); mac->mac_reason[0] = BWN_READ_4(mac, BWN_DMA0_REASON) & 0x0001dc00; mac->mac_reason[1] = BWN_READ_4(mac, BWN_DMA1_REASON) & 0x0000dc00; mac->mac_reason[2] = BWN_READ_4(mac, BWN_DMA2_REASON) & 0x0000dc00; mac->mac_reason[3] = BWN_READ_4(mac, BWN_DMA3_REASON) & 0x0001dc00; mac->mac_reason[4] = BWN_READ_4(mac, BWN_DMA4_REASON) & 0x0000dc00; BWN_WRITE_4(mac, BWN_INTR_REASON, reason); BWN_WRITE_4(mac, BWN_DMA0_REASON, mac->mac_reason[0]); BWN_WRITE_4(mac, BWN_DMA1_REASON, mac->mac_reason[1]); BWN_WRITE_4(mac, BWN_DMA2_REASON, mac->mac_reason[2]); BWN_WRITE_4(mac, BWN_DMA3_REASON, mac->mac_reason[3]); BWN_WRITE_4(mac, BWN_DMA4_REASON, mac->mac_reason[4]); /* Disable interrupts. */ BWN_WRITE_4(mac, BWN_INTR_MASK, 0); mac->mac_reason_intr = reason; BWN_BARRIER(mac, BUS_SPACE_BARRIER_READ); BWN_BARRIER(mac, BUS_SPACE_BARRIER_WRITE); taskqueue_enqueue_fast(sc->sc_tq, &mac->mac_intrtask); return (FILTER_HANDLED); } static void bwn_intrtask(void *arg, int npending) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct siba_softc *siba = mac->mac_sd->sd_bus; uint32_t merged = 0; int i, tx = 0, rx = 0; BWN_LOCK(sc); if (mac->mac_status < BWN_MAC_STATUS_STARTED || siba->siba_invalid) { BWN_UNLOCK(sc); return; } for (i = 0; i < N(mac->mac_reason); i++) merged |= mac->mac_reason[i]; if (mac->mac_reason_intr & BWN_INTR_MAC_TXERR) device_printf(sc->sc_dev, "MAC trans error\n"); if (mac->mac_reason_intr & BWN_INTR_PHY_TXERR) { DPRINTF(sc, BWN_DEBUG_INTR, "%s: PHY trans error\n", __func__); mac->mac_phy.txerrors--; if (mac->mac_phy.txerrors == 0) { mac->mac_phy.txerrors = BWN_TXERROR_MAX; bwn_restart(mac, "PHY TX errors"); } } if (merged & (BWN_DMAINTR_FATALMASK | BWN_DMAINTR_NONFATALMASK)) { if (merged & BWN_DMAINTR_FATALMASK) { device_printf(sc->sc_dev, "Fatal DMA error: %#x %#x %#x %#x %#x %#x\n", mac->mac_reason[0], mac->mac_reason[1], mac->mac_reason[2], mac->mac_reason[3], mac->mac_reason[4], mac->mac_reason[5]); bwn_restart(mac, "DMA error"); BWN_UNLOCK(sc); return; } if (merged & BWN_DMAINTR_NONFATALMASK) { device_printf(sc->sc_dev, "DMA error: %#x %#x %#x %#x %#x %#x\n", mac->mac_reason[0], mac->mac_reason[1], mac->mac_reason[2], mac->mac_reason[3], mac->mac_reason[4], mac->mac_reason[5]); } } if (mac->mac_reason_intr & BWN_INTR_UCODE_DEBUG) bwn_intr_ucode_debug(mac); if (mac->mac_reason_intr & BWN_INTR_TBTT_INDI) bwn_intr_tbtt_indication(mac); if (mac->mac_reason_intr & BWN_INTR_ATIM_END) bwn_intr_atim_end(mac); if (mac->mac_reason_intr & BWN_INTR_BEACON) bwn_intr_beacon(mac); if (mac->mac_reason_intr & BWN_INTR_PMQ) bwn_intr_pmq(mac); if (mac->mac_reason_intr & BWN_INTR_NOISESAMPLE_OK) bwn_intr_noise(mac); if (mac->mac_flags & BWN_MAC_FLAG_DMA) { if (mac->mac_reason[0] & BWN_DMAINTR_RX_DONE) { bwn_dma_rx(mac->mac_method.dma.rx); rx = 1; } } else rx = bwn_pio_rx(&mac->mac_method.pio.rx); KASSERT(!(mac->mac_reason[1] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); KASSERT(!(mac->mac_reason[2] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); KASSERT(!(mac->mac_reason[3] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); KASSERT(!(mac->mac_reason[4] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); KASSERT(!(mac->mac_reason[5] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); if (mac->mac_reason_intr & BWN_INTR_TX_OK) { bwn_intr_txeof(mac); tx = 1; } BWN_WRITE_4(mac, BWN_INTR_MASK, mac->mac_intr_mask); if (sc->sc_blink_led != NULL && sc->sc_led_blink) { int evt = BWN_LED_EVENT_NONE; if (tx && rx) { if (sc->sc_rx_rate > sc->sc_tx_rate) evt = BWN_LED_EVENT_RX; else evt = BWN_LED_EVENT_TX; } else if (tx) { evt = BWN_LED_EVENT_TX; } else if (rx) { evt = BWN_LED_EVENT_RX; } else if (rx == 0) { evt = BWN_LED_EVENT_POLL; } if (evt != BWN_LED_EVENT_NONE) bwn_led_event(mac, evt); } if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { if (!IFQ_IS_EMPTY(&ifp->if_snd)) bwn_start_locked(ifp); } BWN_BARRIER(mac, BUS_SPACE_BARRIER_READ); BWN_BARRIER(mac, BUS_SPACE_BARRIER_WRITE); BWN_UNLOCK(sc); } static void bwn_restart(struct bwn_mac *mac, const char *msg) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; if (mac->mac_status < BWN_MAC_STATUS_INITED) return; device_printf(sc->sc_dev, "HW reset: %s\n", msg); ieee80211_runtask(ic, &mac->mac_hwreset); } static void bwn_intr_ucode_debug(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; uint16_t reason; if (mac->mac_fw.opensource == 0) return; reason = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_DEBUGINTR_REASON_REG); switch (reason) { case BWN_DEBUGINTR_PANIC: bwn_handle_fwpanic(mac); break; case BWN_DEBUGINTR_DUMP_SHM: device_printf(sc->sc_dev, "BWN_DEBUGINTR_DUMP_SHM\n"); break; case BWN_DEBUGINTR_DUMP_REGS: device_printf(sc->sc_dev, "BWN_DEBUGINTR_DUMP_REGS\n"); break; case BWN_DEBUGINTR_MARKER: device_printf(sc->sc_dev, "BWN_DEBUGINTR_MARKER\n"); break; default: device_printf(sc->sc_dev, "ucode debug unknown reason: %#x\n", reason); } bwn_shm_write_2(mac, BWN_SCRATCH, BWN_DEBUGINTR_REASON_REG, BWN_DEBUGINTR_ACK); } static void bwn_intr_tbtt_indication(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ieee80211com *ic = sc->sc_ifp->if_l2com; if (ic->ic_opmode != IEEE80211_M_HOSTAP) bwn_psctl(mac, 0); if (ic->ic_opmode == IEEE80211_M_IBSS) mac->mac_flags |= BWN_MAC_FLAG_DFQVALID; } static void bwn_intr_atim_end(struct bwn_mac *mac) { if (mac->mac_flags & BWN_MAC_FLAG_DFQVALID) { BWN_WRITE_4(mac, BWN_MACCMD, BWN_READ_4(mac, BWN_MACCMD) | BWN_MACCMD_DFQ_VALID); mac->mac_flags &= ~BWN_MAC_FLAG_DFQVALID; } } static void bwn_intr_beacon(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ieee80211com *ic = sc->sc_ifp->if_l2com; uint32_t cmd, beacon0, beacon1; if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) return; mac->mac_intr_mask &= ~BWN_INTR_BEACON; cmd = BWN_READ_4(mac, BWN_MACCMD); beacon0 = (cmd & BWN_MACCMD_BEACON0_VALID); beacon1 = (cmd & BWN_MACCMD_BEACON1_VALID); if (beacon0 && beacon1) { BWN_WRITE_4(mac, BWN_INTR_REASON, BWN_INTR_BEACON); mac->mac_intr_mask |= BWN_INTR_BEACON; return; } if (sc->sc_flags & BWN_FLAG_NEED_BEACON_TP) { sc->sc_flags &= ~BWN_FLAG_NEED_BEACON_TP; bwn_load_beacon0(mac); bwn_load_beacon1(mac); cmd = BWN_READ_4(mac, BWN_MACCMD); cmd |= BWN_MACCMD_BEACON0_VALID; BWN_WRITE_4(mac, BWN_MACCMD, cmd); } else { if (!beacon0) { bwn_load_beacon0(mac); cmd = BWN_READ_4(mac, BWN_MACCMD); cmd |= BWN_MACCMD_BEACON0_VALID; BWN_WRITE_4(mac, BWN_MACCMD, cmd); } else if (!beacon1) { bwn_load_beacon1(mac); cmd = BWN_READ_4(mac, BWN_MACCMD); cmd |= BWN_MACCMD_BEACON1_VALID; BWN_WRITE_4(mac, BWN_MACCMD, cmd); } } } static void bwn_intr_pmq(struct bwn_mac *mac) { uint32_t tmp; while (1) { tmp = BWN_READ_4(mac, BWN_PS_STATUS); if (!(tmp & 0x00000008)) break; } BWN_WRITE_2(mac, BWN_PS_STATUS, 0x0002); } static void bwn_intr_noise(struct bwn_mac *mac) { struct bwn_phy_g *pg = &mac->mac_phy.phy_g; uint16_t tmp; uint8_t noise[4]; uint8_t i, j; int32_t average; if (mac->mac_phy.type != BWN_PHYTYPE_G) return; KASSERT(mac->mac_noise.noi_running, ("%s: fail", __func__)); *((uint32_t *)noise) = htole32(bwn_jssi_read(mac)); if (noise[0] == 0x7f || noise[1] == 0x7f || noise[2] == 0x7f || noise[3] == 0x7f) goto new; KASSERT(mac->mac_noise.noi_nsamples < 8, ("%s:%d: fail", __func__, __LINE__)); i = mac->mac_noise.noi_nsamples; noise[0] = MIN(MAX(noise[0], 0), N(pg->pg_nrssi_lt) - 1); noise[1] = MIN(MAX(noise[1], 0), N(pg->pg_nrssi_lt) - 1); noise[2] = MIN(MAX(noise[2], 0), N(pg->pg_nrssi_lt) - 1); noise[3] = MIN(MAX(noise[3], 0), N(pg->pg_nrssi_lt) - 1); mac->mac_noise.noi_samples[i][0] = pg->pg_nrssi_lt[noise[0]]; mac->mac_noise.noi_samples[i][1] = pg->pg_nrssi_lt[noise[1]]; mac->mac_noise.noi_samples[i][2] = pg->pg_nrssi_lt[noise[2]]; mac->mac_noise.noi_samples[i][3] = pg->pg_nrssi_lt[noise[3]]; mac->mac_noise.noi_nsamples++; if (mac->mac_noise.noi_nsamples == 8) { average = 0; for (i = 0; i < 8; i++) { for (j = 0; j < 4; j++) average += mac->mac_noise.noi_samples[i][j]; } average = (((average / 32) * 125) + 64) / 128; tmp = (bwn_shm_read_2(mac, BWN_SHARED, 0x40c) / 128) & 0x1f; if (tmp >= 8) average += 2; else average -= 25; average -= (tmp == 8) ? 72 : 48; mac->mac_stats.link_noise = average; mac->mac_noise.noi_running = 0; return; } new: bwn_noise_gensample(mac); } static int bwn_pio_rx(struct bwn_pio_rxqueue *prq) { struct bwn_mac *mac = prq->prq_mac; struct bwn_softc *sc = mac->mac_sc; unsigned int i; BWN_ASSERT_LOCKED(sc); if (mac->mac_status < BWN_MAC_STATUS_STARTED) return (0); for (i = 0; i < 5000; i++) { if (bwn_pio_rxeof(prq) == 0) break; } if (i >= 5000) device_printf(sc->sc_dev, "too many RX frames in PIO mode\n"); return ((i > 0) ? 1 : 0); } static void bwn_dma_rx(struct bwn_dma_ring *dr) { int slot, curslot; KASSERT(!dr->dr_tx, ("%s:%d: fail", __func__, __LINE__)); curslot = dr->get_curslot(dr); KASSERT(curslot >= 0 && curslot < dr->dr_numslots, ("%s:%d: fail", __func__, __LINE__)); slot = dr->dr_curslot; for (; slot != curslot; slot = bwn_dma_nextslot(dr, slot)) bwn_dma_rxeof(dr, &slot); bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap, BUS_DMASYNC_PREWRITE); dr->set_curslot(dr, slot); dr->dr_curslot = slot; } static void bwn_intr_txeof(struct bwn_mac *mac) { struct bwn_txstatus stat; uint32_t stat0, stat1; uint16_t tmp; BWN_ASSERT_LOCKED(mac->mac_sc); while (1) { stat0 = BWN_READ_4(mac, BWN_XMITSTAT_0); if (!(stat0 & 0x00000001)) break; stat1 = BWN_READ_4(mac, BWN_XMITSTAT_1); stat.cookie = (stat0 >> 16); stat.seq = (stat1 & 0x0000ffff); stat.phy_stat = ((stat1 & 0x00ff0000) >> 16); tmp = (stat0 & 0x0000ffff); stat.framecnt = ((tmp & 0xf000) >> 12); stat.rtscnt = ((tmp & 0x0f00) >> 8); stat.sreason = ((tmp & 0x001c) >> 2); stat.pm = (tmp & 0x0080) ? 1 : 0; stat.im = (tmp & 0x0040) ? 1 : 0; stat.ampdu = (tmp & 0x0020) ? 1 : 0; stat.ack = (tmp & 0x0002) ? 1 : 0; bwn_handle_txeof(mac, &stat); } } static void bwn_hwreset(void *arg, int npending) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; int error = 0; int prev_status; BWN_LOCK(sc); prev_status = mac->mac_status; if (prev_status >= BWN_MAC_STATUS_STARTED) bwn_core_stop(mac); if (prev_status >= BWN_MAC_STATUS_INITED) bwn_core_exit(mac); if (prev_status >= BWN_MAC_STATUS_INITED) { error = bwn_core_init(mac); if (error) goto out; } if (prev_status >= BWN_MAC_STATUS_STARTED) bwn_core_start(mac); out: if (error) { device_printf(sc->sc_dev, "%s: failed (%d)\n", __func__, error); sc->sc_curmac = NULL; } BWN_UNLOCK(sc); } static void bwn_handle_fwpanic(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; uint16_t reason; reason = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_FWPANIC_REASON_REG); device_printf(sc->sc_dev,"fw panic (%u)\n", reason); if (reason == BWN_FWPANIC_RESTART) bwn_restart(mac, "ucode panic"); } static void bwn_load_beacon0(struct bwn_mac *mac) { KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } static void bwn_load_beacon1(struct bwn_mac *mac) { KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } static uint32_t bwn_jssi_read(struct bwn_mac *mac) { uint32_t val = 0; val = bwn_shm_read_2(mac, BWN_SHARED, 0x08a); val <<= 16; val |= bwn_shm_read_2(mac, BWN_SHARED, 0x088); return (val); } static void bwn_noise_gensample(struct bwn_mac *mac) { uint32_t jssi = 0x7f7f7f7f; bwn_shm_write_2(mac, BWN_SHARED, 0x088, (jssi & 0x0000ffff)); bwn_shm_write_2(mac, BWN_SHARED, 0x08a, (jssi & 0xffff0000) >> 16); BWN_WRITE_4(mac, BWN_MACCMD, BWN_READ_4(mac, BWN_MACCMD) | BWN_MACCMD_BGNOISE); } static int bwn_dma_freeslot(struct bwn_dma_ring *dr) { - struct bwn_mac *mac = dr->dr_mac; + BWN_ASSERT_LOCKED(dr->dr_mac->mac_sc); - BWN_ASSERT_LOCKED(mac->mac_sc); - return (dr->dr_numslots - dr->dr_usedslot); } static int bwn_dma_nextslot(struct bwn_dma_ring *dr, int slot) { - struct bwn_mac *mac = dr->dr_mac; + BWN_ASSERT_LOCKED(dr->dr_mac->mac_sc); - BWN_ASSERT_LOCKED(mac->mac_sc); - KASSERT(slot >= -1 && slot <= dr->dr_numslots - 1, ("%s:%d: fail", __func__, __LINE__)); if (slot == dr->dr_numslots - 1) return (0); return (slot + 1); } static void bwn_dma_rxeof(struct bwn_dma_ring *dr, int *slot) { struct bwn_mac *mac = dr->dr_mac; struct bwn_softc *sc = mac->mac_sc; struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_dmadesc_generic *desc; struct bwn_dmadesc_meta *meta; struct bwn_rxhdr4 *rxhdr; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint32_t macstat; int32_t tmp; int cnt = 0; uint16_t len; dr->getdesc(dr, *slot, &desc, &meta); bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap, BUS_DMASYNC_POSTREAD); m = meta->mt_m; if (bwn_dma_newbuf(dr, desc, meta, 0)) { ifp->if_ierrors++; return; } rxhdr = mtod(m, struct bwn_rxhdr4 *); len = le16toh(rxhdr->frame_len); if (len <= 0) { ifp->if_ierrors++; return; } if (bwn_dma_check_redzone(dr, m)) { device_printf(sc->sc_dev, "redzone error.\n"); bwn_dma_set_redzone(dr, m); bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap, BUS_DMASYNC_PREWRITE); return; } if (len > dr->dr_rx_bufsize) { tmp = len; while (1) { dr->getdesc(dr, *slot, &desc, &meta); bwn_dma_set_redzone(dr, meta->mt_m); bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap, BUS_DMASYNC_PREWRITE); *slot = bwn_dma_nextslot(dr, *slot); cnt++; tmp -= dr->dr_rx_bufsize; if (tmp <= 0) break; } device_printf(sc->sc_dev, "too small buffer " "(len %u buffer %u dropped %d)\n", len, dr->dr_rx_bufsize, cnt); return; } macstat = le32toh(rxhdr->mac_status); if (macstat & BWN_RX_MAC_FCSERR) { if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADFCS)) { device_printf(sc->sc_dev, "RX drop\n"); return; } } m->m_pkthdr.rcvif = ifp; m->m_len = m->m_pkthdr.len = len + dr->dr_frameoffset; m_adj(m, dr->dr_frameoffset); bwn_rxeof(dr->dr_mac, m, rxhdr); } static void bwn_handle_txeof(struct bwn_mac *mac, const struct bwn_txstatus *status) { struct bwn_dma_ring *dr; struct bwn_dmadesc_generic *desc; struct bwn_dmadesc_meta *meta; struct bwn_node *bn; struct bwn_pio_txqueue *tq; struct bwn_pio_txpkt *tp = NULL; struct bwn_softc *sc = mac->mac_sc; + struct bwn_stats *stats = &mac->mac_stats; struct ieee80211_node *ni; int slot; BWN_ASSERT_LOCKED(mac->mac_sc); if (status->im) device_printf(sc->sc_dev, "TODO: STATUS IM\n"); if (status->ampdu) device_printf(sc->sc_dev, "TODO: STATUS AMPDU\n"); if (status->rtscnt) { if (status->rtscnt == 0xf) - device_printf(sc->sc_dev, "TODO: RTS fail\n"); + stats->rtsfail++; else - device_printf(sc->sc_dev, "TODO: RTS ok\n"); + stats->rts++; } if (mac->mac_flags & BWN_MAC_FLAG_DMA) { if (status->ack) { dr = bwn_dma_parse_cookie(mac, status, status->cookie, &slot); if (dr == NULL) { device_printf(sc->sc_dev, "failed to parse cookie\n"); return; } while (1) { dr->getdesc(dr, slot, &desc, &meta); if (meta->mt_islast) { ni = meta->mt_ni; bn = (struct bwn_node *)ni; ieee80211_amrr_tx_complete(&bn->bn_amn, status->ack, 0); break; } slot = bwn_dma_nextslot(dr, slot); } } bwn_dma_handle_txeof(mac, status); } else { if (status->ack) { tq = bwn_pio_parse_cookie(mac, status->cookie, &tp); if (tq == NULL) { device_printf(sc->sc_dev, "failed to parse cookie\n"); return; } ni = tp->tp_ni; bn = (struct bwn_node *)ni; ieee80211_amrr_tx_complete(&bn->bn_amn, status->ack, 0); } bwn_pio_handle_txeof(mac, status); } bwn_phy_txpower_check(mac, 0); } static uint8_t bwn_pio_rxeof(struct bwn_pio_rxqueue *prq) { struct bwn_mac *mac = prq->prq_mac; struct bwn_softc *sc = mac->mac_sc; struct bwn_rxhdr4 rxhdr; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint32_t ctl32, macstat, v32; unsigned int i, padding; uint16_t ctl16, len, v16; unsigned char *mp; char *data; memset(&rxhdr, 0, sizeof(rxhdr)); if (prq->prq_rev >= 8) { ctl32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXCTL); if (!(ctl32 & BWN_PIO8_RXCTL_FRAMEREADY)) return (0); bwn_pio_rx_write_4(prq, BWN_PIO8_RXCTL, BWN_PIO8_RXCTL_FRAMEREADY); for (i = 0; i < 10; i++) { ctl32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXCTL); if (ctl32 & BWN_PIO8_RXCTL_DATAREADY) goto ready; DELAY(10); } } else { ctl16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXCTL); if (!(ctl16 & BWN_PIO_RXCTL_FRAMEREADY)) return (0); bwn_pio_rx_write_2(prq, BWN_PIO_RXCTL, BWN_PIO_RXCTL_FRAMEREADY); for (i = 0; i < 10; i++) { ctl16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXCTL); if (ctl16 & BWN_PIO_RXCTL_DATAREADY) goto ready; DELAY(10); } } device_printf(sc->sc_dev, "%s: timed out\n", __func__); return (1); ready: if (prq->prq_rev >= 8) siba_read_multi_4(mac->mac_sd, &rxhdr, sizeof(rxhdr), prq->prq_base + BWN_PIO8_RXDATA); else siba_read_multi_2(mac->mac_sd, &rxhdr, sizeof(rxhdr), prq->prq_base + BWN_PIO_RXDATA); len = le16toh(rxhdr.frame_len); if (len > 0x700) { device_printf(sc->sc_dev, "%s: len is too big\n", __func__); goto error; } if (len == 0) { device_printf(sc->sc_dev, "%s: len is 0\n", __func__); goto error; } macstat = le32toh(rxhdr.mac_status); if (macstat & BWN_RX_MAC_FCSERR) { if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADFCS)) { device_printf(sc->sc_dev, "%s: FCS error", __func__); goto error; } } padding = (macstat & BWN_RX_MAC_PADDING) ? 2 : 0; KASSERT(len + padding <= MCLBYTES, ("too big..\n")); m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { device_printf(sc->sc_dev, "%s: out of memory", __func__); goto error; } mp = mtod(m, unsigned char *); if (prq->prq_rev >= 8) { siba_read_multi_4(mac->mac_sd, mp + padding, (len & ~3), prq->prq_base + BWN_PIO8_RXDATA); if (len & 3) { v32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXDATA); data = &(mp[len + padding - 1]); switch (len & 3) { case 3: *data = (v32 >> 16); data--; case 2: *data = (v32 >> 8); data--; case 1: *data = v32; } } } else { siba_read_multi_2(mac->mac_sd, mp + padding, (len & ~1), prq->prq_base + BWN_PIO_RXDATA); if (len & 1) { v16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXDATA); mp[len + padding - 1] = v16; } } m->m_pkthdr.rcvif = ifp; m->m_len = m->m_pkthdr.len = len + padding; bwn_rxeof(prq->prq_mac, m, &rxhdr); return (1); error: if (prq->prq_rev >= 8) bwn_pio_rx_write_4(prq, BWN_PIO8_RXCTL, BWN_PIO8_RXCTL_DATAREADY); else bwn_pio_rx_write_2(prq, BWN_PIO_RXCTL, BWN_PIO_RXCTL_DATAREADY); return (1); } static int bwn_dma_newbuf(struct bwn_dma_ring *dr, struct bwn_dmadesc_generic *desc, struct bwn_dmadesc_meta *meta, int init) { struct bwn_mac *mac = dr->dr_mac; struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_rxhdr4 *hdr; bus_dmamap_t map; bus_addr_t paddr; struct mbuf *m; int error; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { error = ENOBUFS; /* * If the NIC is up and running, we need to: * - Clear RX buffer's header. * - Restore RX descriptor settings. */ if (init) return (error); else goto back; } m->m_len = m->m_pkthdr.len = MCLBYTES; bwn_dma_set_redzone(dr, m); /* * Try to load RX buf into temporary DMA map */ error = bus_dmamap_load_mbuf(dma->rxbuf_dtag, dr->dr_spare_dmap, m, bwn_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); if (error) { m_freem(m); /* * See the comment above */ if (init) return (error); else goto back; } if (!init) bus_dmamap_unload(dma->rxbuf_dtag, meta->mt_dmap); meta->mt_m = m; meta->mt_paddr = paddr; /* * Swap RX buf's DMA map with the loaded temporary one */ map = meta->mt_dmap; meta->mt_dmap = dr->dr_spare_dmap; dr->dr_spare_dmap = map; back: /* * Clear RX buf header */ hdr = mtod(meta->mt_m, struct bwn_rxhdr4 *); bzero(hdr, sizeof(*hdr)); bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap, BUS_DMASYNC_PREWRITE); /* * Setup RX buf descriptor */ dr->setdesc(dr, desc, paddr, meta->mt_m->m_len - sizeof(*hdr), 0, 0, 0); return (error); } static void bwn_dma_buf_addr(void *arg, bus_dma_segment_t *seg, int nseg, bus_size_t mapsz __unused, int error) { if (!error) { KASSERT(nseg == 1, ("too many segments(%d)\n", nseg)); *((bus_addr_t *)arg) = seg->ds_addr; } } static int bwn_hwrate2ieeerate(int rate) { switch (rate) { case BWN_CCK_RATE_1MB: return (2); case BWN_CCK_RATE_2MB: return (4); case BWN_CCK_RATE_5MB: return (11); case BWN_CCK_RATE_11MB: return (22); case BWN_OFDM_RATE_6MB: return (12); case BWN_OFDM_RATE_9MB: return (18); case BWN_OFDM_RATE_12MB: return (24); case BWN_OFDM_RATE_18MB: return (36); case BWN_OFDM_RATE_24MB: return (48); case BWN_OFDM_RATE_36MB: return (72); case BWN_OFDM_RATE_48MB: return (96); case BWN_OFDM_RATE_54MB: return (108); default: printf("Ooops\n"); return (0); } } static void bwn_rxeof(struct bwn_mac *mac, struct mbuf *m, const void *_rxhdr) { const struct bwn_rxhdr4 *rxhdr = _rxhdr; struct bwn_plcp6 *plcp; struct bwn_softc *sc = mac->mac_sc; struct ieee80211_frame_min *wh; struct ieee80211_node *ni; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t macstat; - int padding, rate, rssi, noise, type; + int padding, rate, rssi = 0, noise = 0, type; uint16_t phytype, phystat0, phystat3, chanstat; unsigned char *mp = mtod(m, unsigned char *); + static int rx_mac_dec_rpt = 0; BWN_ASSERT_LOCKED(sc); phystat0 = le16toh(rxhdr->phy_status0); phystat3 = le16toh(rxhdr->phy_status3); macstat = le32toh(rxhdr->mac_status); chanstat = le16toh(rxhdr->channel); phytype = chanstat & BWN_RX_CHAN_PHYTYPE; if (macstat & BWN_RX_MAC_FCSERR) device_printf(sc->sc_dev, "TODO RX: RX_FLAG_FAILED_FCS_CRC\n"); if (phystat0 & (BWN_RX_PHYST0_PLCPHCF | BWN_RX_PHYST0_PLCPFV)) device_printf(sc->sc_dev, "TODO RX: RX_FLAG_FAILED_PLCP_CRC\n"); if (phystat0 & BWN_RX_PHYST0_SHORTPRMBL) device_printf(sc->sc_dev, "TODO RX: RX_FLAG_SHORTPRE\n"); if (macstat & BWN_RX_MAC_DECERR) goto drop; padding = (macstat & BWN_RX_MAC_PADDING) ? 2 : 0; if (m->m_pkthdr.len < (sizeof(struct bwn_plcp6) + padding)) { device_printf(sc->sc_dev, "frame too short (length=%d)\n", m->m_pkthdr.len); goto drop; } plcp = (struct bwn_plcp6 *)(mp + padding); m_adj(m, sizeof(struct bwn_plcp6) + padding); if (m->m_pkthdr.len < IEEE80211_MIN_LEN) { device_printf(sc->sc_dev, "frame too short (length=%d)\n", m->m_pkthdr.len); goto drop; } wh = mtod(m, struct ieee80211_frame_min *); - if (macstat & BWN_RX_MAC_DEC) + if (macstat & BWN_RX_MAC_DEC && rx_mac_dec_rpt++ < 50) device_printf(sc->sc_dev, "RX decryption attempted (old %d keyidx %#x)\n", BWN_ISOLDFMT(mac), (macstat & BWN_RX_MAC_KEYIDX) >> BWN_RX_MAC_KEYIDX_SHIFT); /* XXX calculating RSSI & noise & antenna */ if (phystat0 & BWN_RX_PHYST0_OFDM) rate = bwn_plcp_get_ofdmrate(mac, plcp, phytype == BWN_PHYTYPE_A); else rate = bwn_plcp_get_cckrate(mac, plcp); if (rate == -1) { if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADPLCP)) goto drop; } sc->sc_rx_rate = bwn_hwrate2ieeerate(rate); /* RX radio tap */ if (ieee80211_radiotap_active(ic)) bwn_rx_radiotap(mac, m, rxhdr, plcp, rate, rssi, noise); m_adj(m, -IEEE80211_CRC_LEN); rssi = rxhdr->phy.abg.rssi; /* XXX incorrect RSSI calculation? */ noise = mac->mac_stats.link_noise; BWN_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, wh); if (ni != NULL) { type = ieee80211_input(ni, m, rssi, noise); ieee80211_free_node(ni); } else type = ieee80211_input_all(ic, m, rssi, noise); BWN_LOCK(sc); return; drop: device_printf(sc->sc_dev, "%s: dropped\n", __func__); } static void bwn_dma_handle_txeof(struct bwn_mac *mac, const struct bwn_txstatus *status) { struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_dma_ring *dr; struct bwn_dmadesc_generic *desc; struct bwn_dmadesc_meta *meta; struct bwn_softc *sc = mac->mac_sc; struct ieee80211_node *ni; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; int slot; BWN_ASSERT_LOCKED(sc); dr = bwn_dma_parse_cookie(mac, status, status->cookie, &slot); if (dr == NULL) { device_printf(sc->sc_dev, "failed to parse cookie\n"); return; } KASSERT(dr->dr_tx, ("%s:%d: fail", __func__, __LINE__)); while (1) { KASSERT(slot >= 0 && slot < dr->dr_numslots, ("%s:%d: fail", __func__, __LINE__)); dr->getdesc(dr, slot, &desc, &meta); if (meta->mt_txtype == BWN_DMADESC_METATYPE_HEADER) bus_dmamap_unload(dr->dr_txring_dtag, meta->mt_dmap); else if (meta->mt_txtype == BWN_DMADESC_METATYPE_BODY) bus_dmamap_unload(dma->txbuf_dtag, meta->mt_dmap); if (meta->mt_islast) { KASSERT(meta->mt_m != NULL, ("%s:%d: fail", __func__, __LINE__)); ni = meta->mt_ni; m = meta->mt_m; if (ni != NULL) { /* * Do any tx complete callback. Note this must * be done before releasing the node reference. */ if (m->m_flags & M_TXCB) ieee80211_process_callback(ni, m, 0); ieee80211_free_node(ni); meta->mt_ni = NULL; } m_freem(m); meta->mt_m = NULL; } else { KASSERT(meta->mt_m == NULL, ("%s:%d: fail", __func__, __LINE__)); } dr->dr_usedslot--; if (meta->mt_islast) { ifp->if_opackets++; break; } slot = bwn_dma_nextslot(dr, slot); } sc->sc_watchdog_timer = 0; if (dr->dr_stop) { KASSERT(bwn_dma_freeslot(dr) >= BWN_TX_SLOTS_PER_FRAME, ("%s:%d: fail", __func__, __LINE__)); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; dr->dr_stop = 0; } } static void bwn_pio_handle_txeof(struct bwn_mac *mac, const struct bwn_txstatus *status) { struct bwn_pio_txqueue *tq; struct bwn_pio_txpkt *tp = NULL; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; BWN_ASSERT_LOCKED(sc); tq = bwn_pio_parse_cookie(mac, status->cookie, &tp); if (tq == NULL) return; tq->tq_used -= roundup(tp->tp_m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); tq->tq_free++; if (tp->tp_ni != NULL) { /* * Do any tx complete callback. Note this must * be done before releasing the node reference. */ if (tp->tp_m->m_flags & M_TXCB) ieee80211_process_callback(tp->tp_ni, tp->tp_m, 0); ieee80211_free_node(tp->tp_ni); tp->tp_ni = NULL; } m_freem(tp->tp_m); tp->tp_m = NULL; TAILQ_INSERT_TAIL(&tq->tq_pktlist, tp, tp_list); ifp->if_opackets++; sc->sc_watchdog_timer = 0; if (tq->tq_stop) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; tq->tq_stop = 0; } } static void bwn_phy_txpower_check(struct bwn_mac *mac, uint32_t flags) { struct bwn_softc *sc = mac->mac_sc; struct bwn_phy *phy = &mac->mac_phy; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct siba_softc *siba = mac->mac_sd->sd_bus; unsigned long now; int result; BWN_GETTIME(now); if (!(flags & BWN_TXPWR_IGNORE_TIME) && time_before(now, phy->nexttime)) return; phy->nexttime = now + 2 * 1000; if (siba->siba_board_vendor == SIBA_BOARDVENDOR_BCM && siba->siba_board_type == SIBA_BOARD_BU4306) return; if (phy->recalc_txpwr != NULL) { result = phy->recalc_txpwr(mac, (flags & BWN_TXPWR_IGNORE_TSSI) ? 1 : 0); if (result == BWN_TXPWR_RES_DONE) return; KASSERT(result == BWN_TXPWR_RES_NEED_ADJUST, ("%s: fail", __func__)); KASSERT(phy->set_txpwr != NULL, ("%s: fail", __func__)); ieee80211_runtask(ic, &mac->mac_txpower); } } static uint16_t bwn_pio_rx_read_2(struct bwn_pio_rxqueue *prq, uint16_t offset) { return (BWN_READ_2(prq->prq_mac, prq->prq_base + offset)); } static uint32_t bwn_pio_rx_read_4(struct bwn_pio_rxqueue *prq, uint16_t offset) { return (BWN_READ_4(prq->prq_mac, prq->prq_base + offset)); } static void bwn_pio_rx_write_2(struct bwn_pio_rxqueue *prq, uint16_t offset, uint16_t value) { BWN_WRITE_2(prq->prq_mac, prq->prq_base + offset, value); } static void bwn_pio_rx_write_4(struct bwn_pio_rxqueue *prq, uint16_t offset, uint32_t value) { BWN_WRITE_4(prq->prq_mac, prq->prq_base + offset, value); } static int bwn_ieeerate2hwrate(struct bwn_softc *sc, int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (BWN_OFDM_RATE_6MB); case 18: return (BWN_OFDM_RATE_9MB); case 24: return (BWN_OFDM_RATE_12MB); case 36: return (BWN_OFDM_RATE_18MB); case 48: return (BWN_OFDM_RATE_24MB); case 72: return (BWN_OFDM_RATE_36MB); case 96: return (BWN_OFDM_RATE_48MB); case 108: return (BWN_OFDM_RATE_54MB); /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (BWN_CCK_RATE_1MB); case 4: return (BWN_CCK_RATE_2MB); case 11: return (BWN_CCK_RATE_5MB); case 22: return (BWN_CCK_RATE_11MB); } device_printf(sc->sc_dev, "unsupported rate %d\n", rate); return (BWN_CCK_RATE_1MB); } static int bwn_set_txhdr(struct bwn_mac *mac, struct ieee80211_node *ni, struct mbuf *m, struct bwn_txhdr *txhdr, uint16_t cookie) { const struct bwn_phy *phy = &mac->mac_phy; struct bwn_softc *sc = mac->mac_sc; struct ieee80211_frame *wh; struct ieee80211_frame *protwh; struct ieee80211_frame_cts *cts; struct ieee80211_frame_rts *rts; const struct ieee80211_txparam *tp; struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mprot; unsigned int len; uint32_t macctl = 0; int protdur, rts_rate, rts_rate_fb, ismcast, isshort, rix, type; uint16_t phyctl = 0; uint8_t rate, rate_fb; wh = mtod(m, struct ieee80211_frame *); memset(txhdr, 0, sizeof(*txhdr)); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; /* * Find TX rate */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (type != IEEE80211_FC0_TYPE_DATA || (m->m_flags & M_EAPOL)) rate = rate_fb = tp->mgmtrate; else if (ismcast) rate = rate_fb = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = rate_fb = tp->ucastrate; else { rix = ieee80211_amrr_choose(ni, &BWN_NODE(ni)->bn_amn); rate = ni->ni_txrate; if (rix > 0) rate_fb = ni->ni_rates.rs_rates[rix - 1] & IEEE80211_RATE_VAL; else rate_fb = rate; } sc->sc_tx_rate = rate; rate = bwn_ieeerate2hwrate(sc, rate); rate_fb = bwn_ieeerate2hwrate(sc, rate_fb); txhdr->phyrate = (BWN_ISOFDMRATE(rate)) ? bwn_plcp_getofdm(rate) : bwn_plcp_getcck(rate); bcopy(wh->i_fc, txhdr->macfc, sizeof(txhdr->macfc)); bcopy(wh->i_addr1, txhdr->addr1, IEEE80211_ADDR_LEN); if ((rate_fb == rate) || (*(u_int16_t *)wh->i_dur & htole16(0x8000)) || (*(u_int16_t *)wh->i_dur == htole16(0))) txhdr->dur_fb = *(u_int16_t *)wh->i_dur; else txhdr->dur_fb = ieee80211_compute_duration(ic->ic_rt, m->m_pkthdr.len, rate, isshort); /* XXX TX encryption */ bwn_plcp_genhdr(BWN_ISOLDFMT(mac) ? (struct bwn_plcp4 *)(&txhdr->body.old.plcp) : (struct bwn_plcp4 *)(&txhdr->body.new.plcp), m->m_pkthdr.len + IEEE80211_CRC_LEN, rate); bwn_plcp_genhdr((struct bwn_plcp4 *)(&txhdr->plcp_fb), m->m_pkthdr.len + IEEE80211_CRC_LEN, rate_fb); txhdr->eftypes |= (BWN_ISOFDMRATE(rate_fb)) ? BWN_TX_EFT_FB_OFDM : BWN_TX_EFT_FB_CCK; txhdr->chan = phy->chan; phyctl |= (BWN_ISOFDMRATE(rate)) ? BWN_TX_PHY_ENC_OFDM : BWN_TX_PHY_ENC_CCK; if (isshort && (rate == BWN_CCK_RATE_2MB || rate == BWN_CCK_RATE_5MB || rate == BWN_CCK_RATE_11MB)) phyctl |= BWN_TX_PHY_SHORTPRMBL; /* XXX TX antenna selection */ switch (bwn_antenna_sanitize(mac, 0)) { case 0: phyctl |= BWN_TX_PHY_ANT01AUTO; break; case 1: phyctl |= BWN_TX_PHY_ANT0; break; case 2: phyctl |= BWN_TX_PHY_ANT1; break; case 3: phyctl |= BWN_TX_PHY_ANT2; break; case 4: phyctl |= BWN_TX_PHY_ANT3; break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } if (!ismcast) macctl |= BWN_TX_MAC_ACK; macctl |= (BWN_TX_MAC_HWSEQ | BWN_TX_MAC_START_MSDU); if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) macctl |= BWN_TX_MAC_LONGFRAME; if (ic->ic_flags & IEEE80211_F_USEPROT) { /* XXX RTS rate is always 1MB??? */ rts_rate = BWN_CCK_RATE_1MB; rts_rate_fb = bwn_get_fbrate(rts_rate); protdur = ieee80211_compute_duration(ic->ic_rt, m->m_pkthdr.len, rate, isshort) + + ieee80211_ack_duration(ic->ic_rt, rate, isshort); if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { cts = (struct ieee80211_frame_cts *)(BWN_ISOLDFMT(mac) ? (txhdr->body.old.rts_frame) : (txhdr->body.new.rts_frame)); mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, protdur); KASSERT(mprot != NULL, ("failed to alloc mbuf\n")); bcopy(mtod(mprot, uint8_t *), (uint8_t *)cts, mprot->m_pkthdr.len); m_freem(mprot); macctl |= BWN_TX_MAC_SEND_CTSTOSELF; len = sizeof(struct ieee80211_frame_cts); } else { rts = (struct ieee80211_frame_rts *)(BWN_ISOLDFMT(mac) ? (txhdr->body.old.rts_frame) : (txhdr->body.new.rts_frame)); protdur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, protdur); KASSERT(mprot != NULL, ("failed to alloc mbuf\n")); bcopy(mtod(mprot, uint8_t *), (uint8_t *)rts, mprot->m_pkthdr.len); m_freem(mprot); macctl |= BWN_TX_MAC_SEND_RTSCTS; len = sizeof(struct ieee80211_frame_rts); } len += IEEE80211_CRC_LEN; bwn_plcp_genhdr((struct bwn_plcp4 *)((BWN_ISOLDFMT(mac)) ? &txhdr->body.old.rts_plcp : &txhdr->body.new.rts_plcp), len, rts_rate); bwn_plcp_genhdr((struct bwn_plcp4 *)&txhdr->rts_plcp_fb, len, rts_rate_fb); protwh = (struct ieee80211_frame *)(BWN_ISOLDFMT(mac) ? (&txhdr->body.old.rts_frame) : (&txhdr->body.new.rts_frame)); txhdr->rts_dur_fb = *(u_int16_t *)protwh->i_dur; if (BWN_ISOFDMRATE(rts_rate)) { txhdr->eftypes |= BWN_TX_EFT_RTS_OFDM; txhdr->phyrate_rts = bwn_plcp_getofdm(rts_rate); } else { txhdr->eftypes |= BWN_TX_EFT_RTS_CCK; txhdr->phyrate_rts = bwn_plcp_getcck(rts_rate); } txhdr->eftypes |= (BWN_ISOFDMRATE(rts_rate_fb)) ? BWN_TX_EFT_RTS_FBOFDM : BWN_TX_EFT_RTS_FBCCK; } if (BWN_ISOLDFMT(mac)) txhdr->body.old.cookie = htole16(cookie); else txhdr->body.new.cookie = htole16(cookie); txhdr->macctl = htole32(macctl); txhdr->phyctl = htole16(phyctl); /* * TX radio tap */ if (ieee80211_radiotap_active_vap(vap)) { sc->sc_tx_th.wt_flags = 0; if (wh->i_fc[1] & IEEE80211_FC1_WEP) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; if (isshort && (rate == BWN_CCK_RATE_2MB || rate == BWN_CCK_RATE_5MB || rate == BWN_CCK_RATE_11MB)) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; sc->sc_tx_th.wt_rate = rate; ieee80211_radiotap_tx(vap, m); } return (0); } static void bwn_plcp_genhdr(struct bwn_plcp4 *plcp, const uint16_t octets, const uint8_t rate) { uint32_t d, plen; uint8_t *raw = plcp->o.raw; if (BWN_ISOFDMRATE(rate)) { d = bwn_plcp_getofdm(rate); KASSERT(!(octets & 0xf000), ("%s:%d: fail", __func__, __LINE__)); d |= (octets << 5); plcp->o.data = htole32(d); } else { plen = octets * 16 / rate; if ((octets * 16 % rate) > 0) { plen++; if ((rate == BWN_CCK_RATE_11MB) && ((octets * 8 % 11) < 4)) { raw[1] = 0x84; } else raw[1] = 0x04; } else raw[1] = 0x04; plcp->o.data |= htole32(plen << 16); raw[0] = bwn_plcp_getcck(rate); } } static uint8_t bwn_antenna_sanitize(struct bwn_mac *mac, uint8_t n) { uint8_t mask; if (n == 0) return (0); if (mac->mac_phy.gmode) mask = mac->mac_sd->sd_bus->siba_sprom.ant_bg; else mask = mac->mac_sd->sd_bus->siba_sprom.ant_a; if (!(mask & (1 << (n - 1)))) return (0); return (n); } static uint8_t bwn_get_fbrate(uint8_t bitrate) { switch (bitrate) { case BWN_CCK_RATE_1MB: return (BWN_CCK_RATE_1MB); case BWN_CCK_RATE_2MB: return (BWN_CCK_RATE_1MB); case BWN_CCK_RATE_5MB: return (BWN_CCK_RATE_2MB); case BWN_CCK_RATE_11MB: return (BWN_CCK_RATE_5MB); case BWN_OFDM_RATE_6MB: return (BWN_CCK_RATE_5MB); case BWN_OFDM_RATE_9MB: return (BWN_OFDM_RATE_6MB); case BWN_OFDM_RATE_12MB: return (BWN_OFDM_RATE_9MB); case BWN_OFDM_RATE_18MB: return (BWN_OFDM_RATE_12MB); case BWN_OFDM_RATE_24MB: return (BWN_OFDM_RATE_18MB); case BWN_OFDM_RATE_36MB: return (BWN_OFDM_RATE_24MB); case BWN_OFDM_RATE_48MB: return (BWN_OFDM_RATE_36MB); case BWN_OFDM_RATE_54MB: return (BWN_OFDM_RATE_48MB); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (0); } static uint32_t bwn_pio_write_multi_4(struct bwn_mac *mac, struct bwn_pio_txqueue *tq, uint32_t ctl, const void *_data, int len) { uint32_t value = 0; const uint8_t *data = _data; ctl |= BWN_PIO8_TXCTL_0_7 | BWN_PIO8_TXCTL_8_15 | BWN_PIO8_TXCTL_16_23 | BWN_PIO8_TXCTL_24_31; bwn_pio_write_4(mac, tq, BWN_PIO8_TXCTL, ctl); siba_write_multi_4(mac->mac_sd, data, (len & ~3), tq->tq_base + BWN_PIO8_TXDATA); if (len & 3) { ctl &= ~(BWN_PIO8_TXCTL_8_15 | BWN_PIO8_TXCTL_16_23 | BWN_PIO8_TXCTL_24_31); data = &(data[len - 1]); switch (len & 3) { case 3: ctl |= BWN_PIO8_TXCTL_16_23; value |= (uint32_t)(*data) << 16; data--; case 2: ctl |= BWN_PIO8_TXCTL_8_15; value |= (uint32_t)(*data) << 8; data--; case 1: value |= (uint32_t)(*data); } bwn_pio_write_4(mac, tq, BWN_PIO8_TXCTL, ctl); bwn_pio_write_4(mac, tq, BWN_PIO8_TXDATA, value); } return (ctl); } static void bwn_pio_write_4(struct bwn_mac *mac, struct bwn_pio_txqueue *tq, uint16_t offset, uint32_t value) { BWN_WRITE_4(mac, tq->tq_base + offset, value); } static uint16_t bwn_pio_write_multi_2(struct bwn_mac *mac, struct bwn_pio_txqueue *tq, uint16_t ctl, const void *_data, int len) { const uint8_t *data = _data; ctl |= BWN_PIO_TXCTL_WRITELO | BWN_PIO_TXCTL_WRITEHI; BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl); siba_write_multi_2(mac->mac_sd, data, (len & ~1), tq->tq_base + BWN_PIO_TXDATA); if (len & 1) { ctl &= ~BWN_PIO_TXCTL_WRITEHI; BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl); BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXDATA, data[len - 1]); } return (ctl); } static uint16_t bwn_pio_write_mbuf_2(struct bwn_mac *mac, struct bwn_pio_txqueue *tq, uint16_t ctl, struct mbuf *m0) { int i, j = 0; uint16_t data = 0; const uint8_t *buf; struct mbuf *m = m0; ctl |= BWN_PIO_TXCTL_WRITELO | BWN_PIO_TXCTL_WRITEHI; BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl); for (; m != NULL; m = m->m_next) { buf = mtod(m, const uint8_t *); for (i = 0; i < m->m_len; i++) { if (!((j++) % 2)) data |= buf[i]; else { data |= (buf[i] << 8); BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXDATA, data); data = 0; } } } if (m0->m_pkthdr.len % 2) { ctl &= ~BWN_PIO_TXCTL_WRITEHI; BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl); BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXDATA, data); } return (ctl); } static void bwn_set_slot_time(struct bwn_mac *mac, uint16_t time) { if (mac->mac_phy.type != BWN_PHYTYPE_G) return; BWN_WRITE_2(mac, 0x684, 510 + time); bwn_shm_write_2(mac, BWN_SHARED, 0x0010, time); } static struct bwn_dma_ring * bwn_dma_select(struct bwn_mac *mac, uint8_t prio) { if ((mac->mac_flags & BWN_MAC_FLAG_WME) == 0) return (mac->mac_method.dma.wme[WME_AC_BE]); switch (prio) { case 3: return (mac->mac_method.dma.wme[WME_AC_VO]); case 2: return (mac->mac_method.dma.wme[WME_AC_VI]); case 0: return (mac->mac_method.dma.wme[WME_AC_BE]); case 1: return (mac->mac_method.dma.wme[WME_AC_BK]); } KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); + return (NULL); } static int bwn_dma_getslot(struct bwn_dma_ring *dr) { - struct bwn_mac *mac = dr->dr_mac; int slot; - BWN_ASSERT_LOCKED(mac->mac_sc); + BWN_ASSERT_LOCKED(dr->dr_mac->mac_sc); KASSERT(dr->dr_tx, ("%s:%d: fail", __func__, __LINE__)); KASSERT(!(dr->dr_stop), ("%s:%d: fail", __func__, __LINE__)); KASSERT(bwn_dma_freeslot(dr) != 0, ("%s:%d: fail", __func__, __LINE__)); slot = bwn_dma_nextslot(dr, dr->dr_curslot); KASSERT(!(slot & ~0x0fff), ("%s:%d: fail", __func__, __LINE__)); dr->dr_curslot = slot; dr->dr_usedslot++; return (slot); } static int bwn_phy_shm_tssi_read(struct bwn_mac *mac, uint16_t shm_offset) { const uint8_t ofdm = (shm_offset != BWN_SHARED_TSSI_CCK); unsigned int a, b, c, d; unsigned int avg; uint32_t tmp; tmp = bwn_shm_read_4(mac, BWN_SHARED, shm_offset); a = tmp & 0xff; b = (tmp >> 8) & 0xff; c = (tmp >> 16) & 0xff; d = (tmp >> 24) & 0xff; if (a == 0 || a == BWN_TSSI_MAX || b == 0 || b == BWN_TSSI_MAX || c == 0 || c == BWN_TSSI_MAX || d == 0 || d == BWN_TSSI_MAX) return (ENOENT); bwn_shm_write_4(mac, BWN_SHARED, shm_offset, BWN_TSSI_MAX | (BWN_TSSI_MAX << 8) | (BWN_TSSI_MAX << 16) | (BWN_TSSI_MAX << 24)); if (ofdm) { a = (a + 32) & 0x3f; b = (b + 32) & 0x3f; c = (c + 32) & 0x3f; d = (d + 32) & 0x3f; } avg = (a + b + c + d + 2) / 4; if (ofdm) { if (bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFLO) & BWN_HF_4DB_CCK_POWERBOOST) avg = (avg >= 13) ? (avg - 13) : 0; } return (avg); } static void bwn_phy_g_setatt(struct bwn_mac *mac, int *bbattp, int *rfattp) { struct bwn_txpwr_loctl *lo = &mac->mac_phy.phy_g.pg_loctl; int rfatt = *rfattp; int bbatt = *bbattp; while (1) { if (rfatt > lo->rfatt.max && bbatt > lo->bbatt.max - 4) break; if (rfatt < lo->rfatt.min && bbatt < lo->bbatt.min + 4) break; if (bbatt > lo->bbatt.max && rfatt > lo->rfatt.max - 1) break; if (bbatt < lo->bbatt.min && rfatt < lo->rfatt.min + 1) break; if (bbatt > lo->bbatt.max) { bbatt -= 4; rfatt += 1; continue; } if (bbatt < lo->bbatt.min) { bbatt += 4; rfatt -= 1; continue; } if (rfatt > lo->rfatt.max) { rfatt -= 1; bbatt += 4; continue; } if (rfatt < lo->rfatt.min) { rfatt += 1; bbatt -= 4; continue; } break; } *rfattp = MIN(MAX(rfatt, lo->rfatt.min), lo->rfatt.max); *bbattp = MIN(MAX(bbatt, lo->bbatt.min), lo->bbatt.max); } static void bwn_phy_lock(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ieee80211com *ic = sc->sc_ifp->if_l2com; KASSERT(mac->mac_sd->sd_id.sd_rev >= 3, ("%s: unsupported rev %d", __func__, mac->mac_sd->sd_id.sd_rev)); if (ic->ic_opmode != IEEE80211_M_HOSTAP) bwn_psctl(mac, BWN_PS_AWAKE); } static void bwn_phy_unlock(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ieee80211com *ic = sc->sc_ifp->if_l2com; KASSERT(mac->mac_sd->sd_id.sd_rev >= 3, ("%s: unsupported rev %d", __func__, mac->mac_sd->sd_id.sd_rev)); if (ic->ic_opmode != IEEE80211_M_HOSTAP) bwn_psctl(mac, 0); } static void bwn_rf_lock(struct bwn_mac *mac) { BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_RADIO_LOCK); BWN_READ_4(mac, BWN_MACCTL); DELAY(10); } static void bwn_rf_unlock(struct bwn_mac *mac) { BWN_READ_2(mac, BWN_PHYVER); BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_RADIO_LOCK); } static struct bwn_pio_txqueue * bwn_pio_parse_cookie(struct bwn_mac *mac, uint16_t cookie, struct bwn_pio_txpkt **pack) { struct bwn_pio *pio = &mac->mac_method.pio; struct bwn_pio_txqueue *tq = NULL; unsigned int index; switch (cookie & 0xf000) { case 0x1000: tq = &pio->wme[WME_AC_BK]; break; case 0x2000: tq = &pio->wme[WME_AC_BE]; break; case 0x3000: tq = &pio->wme[WME_AC_VI]; break; case 0x4000: tq = &pio->wme[WME_AC_VO]; break; case 0x5000: tq = &pio->mcast; break; } KASSERT(tq != NULL, ("%s:%d: fail", __func__, __LINE__)); if (tq == NULL) return (NULL); index = (cookie & 0x0fff); KASSERT(index < N(tq->tq_pkts), ("%s:%d: fail", __func__, __LINE__)); if (index >= N(tq->tq_pkts)) return (NULL); *pack = &tq->tq_pkts[index]; KASSERT(*pack != NULL, ("%s:%d: fail", __func__, __LINE__)); return (tq); } static void bwn_txpwr(void *arg, int npending) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; BWN_LOCK(sc); if (mac && mac->mac_status >= BWN_MAC_STATUS_STARTED && mac->mac_phy.set_txpwr != NULL) mac->mac_phy.set_txpwr(mac); BWN_UNLOCK(sc); } static void bwn_task_15s(struct bwn_mac *mac) { uint16_t reg; if (mac->mac_fw.opensource) { reg = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_WATCHDOG_REG); if (reg) { bwn_restart(mac, "fw watchdog"); return; } bwn_shm_write_2(mac, BWN_SCRATCH, BWN_WATCHDOG_REG, 1); } if (mac->mac_phy.task_15s) mac->mac_phy.task_15s(mac); mac->mac_phy.txerrors = BWN_TXERROR_MAX; } static void bwn_task_30s(struct bwn_mac *mac) { if (mac->mac_phy.type != BWN_PHYTYPE_G || mac->mac_noise.noi_running) return; mac->mac_noise.noi_running = 1; mac->mac_noise.noi_nsamples = 0; bwn_noise_gensample(mac); } static void bwn_task_60s(struct bwn_mac *mac) { if (mac->mac_phy.task_60s) mac->mac_phy.task_60s(mac); bwn_phy_txpower_check(mac, BWN_TXPWR_IGNORE_TIME); } static void bwn_tasks(void *arg) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; BWN_ASSERT_LOCKED(sc); if (mac->mac_status != BWN_MAC_STATUS_STARTED) return; if (mac->mac_task_state % 4 == 0) bwn_task_60s(mac); if (mac->mac_task_state % 2 == 0) bwn_task_30s(mac); bwn_task_15s(mac); mac->mac_task_state++; callout_reset(&sc->sc_task_ch, hz * 15, bwn_tasks, mac); } static int bwn_plcp_get_ofdmrate(struct bwn_mac *mac, struct bwn_plcp6 *plcp, uint8_t a) { struct bwn_softc *sc = mac->mac_sc; KASSERT(a == 0, ("not support APHY\n")); switch (plcp->o.raw[0] & 0xf) { case 0xb: return (BWN_OFDM_RATE_6MB); case 0xf: return (BWN_OFDM_RATE_9MB); case 0xa: return (BWN_OFDM_RATE_12MB); case 0xe: return (BWN_OFDM_RATE_18MB); case 0x9: return (BWN_OFDM_RATE_24MB); case 0xd: return (BWN_OFDM_RATE_36MB); case 0x8: return (BWN_OFDM_RATE_48MB); case 0xc: return (BWN_OFDM_RATE_54MB); } device_printf(sc->sc_dev, "incorrect OFDM rate %d\n", plcp->o.raw[0] & 0xf); return (-1); } static int bwn_plcp_get_cckrate(struct bwn_mac *mac, struct bwn_plcp6 *plcp) { struct bwn_softc *sc = mac->mac_sc; switch (plcp->o.raw[0]) { case 0x0a: return (BWN_CCK_RATE_1MB); case 0x14: return (BWN_CCK_RATE_2MB); case 0x37: return (BWN_CCK_RATE_5MB); case 0x6e: return (BWN_CCK_RATE_11MB); } device_printf(sc->sc_dev, "incorrect CCK rate %d\n", plcp->o.raw[0]); return (-1); } static void bwn_rx_radiotap(struct bwn_mac *mac, struct mbuf *m, const struct bwn_rxhdr4 *rxhdr, struct bwn_plcp6 *plcp, int rate, int rssi, int noise) { struct bwn_softc *sc = mac->mac_sc; const struct ieee80211_frame_min *wh; uint64_t tsf; uint16_t low_mactime_now; if (htole16(rxhdr->phy_status0) & BWN_RX_PHYST0_SHORTPRMBL) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; wh = mtod(m, const struct ieee80211_frame_min *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP; bwn_tsf_read(mac, &tsf); low_mactime_now = tsf; tsf = tsf & ~0xffffULL; tsf += le16toh(rxhdr->mac_time); if (low_mactime_now < le16toh(rxhdr->mac_time)) tsf -= 0x10000; sc->sc_rx_th.wr_tsf = tsf; sc->sc_rx_th.wr_rate = rate; sc->sc_rx_th.wr_antsignal = rssi; sc->sc_rx_th.wr_antnoise = noise; } static void bwn_tsf_read(struct bwn_mac *mac, uint64_t *tsf) { uint32_t low, high; KASSERT(mac->mac_sd->sd_id.sd_rev >= 3, ("%s:%d: fail", __func__, __LINE__)); low = BWN_READ_4(mac, BWN_REV3PLUS_TSF_LOW); high = BWN_READ_4(mac, BWN_REV3PLUS_TSF_HIGH); *tsf = high; *tsf <<= 32; *tsf |= low; } static int bwn_dma_attach(struct bwn_mac *mac) { struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_softc *sc = mac->mac_sc; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; bus_addr_t lowaddr = 0; int error; if (siba->siba_type == SIBA_TYPE_PCMCIA || bwn_usedma == 0) return (0); KASSERT(mac->mac_sd->sd_id.sd_rev >= 5, ("%s: fail", __func__)); mac->mac_flags |= BWN_MAC_FLAG_DMA; dma->dmatype = bwn_dma_gettype(mac); if (dma->dmatype == BWN_DMA_30BIT) lowaddr = BWN_BUS_SPACE_MAXADDR_30BIT; else if (dma->dmatype == BWN_DMA_32BIT) lowaddr = BUS_SPACE_MAXADDR_32BIT; else lowaddr = BUS_SPACE_MAXADDR; /* * Create top level DMA tag */ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ BWN_ALIGN, 0, /* alignment, bounds */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &dma->parent_dtag); if (error) { device_printf(sc->sc_dev, "can't create parent DMA tag\n"); return (error); } /* * Create TX/RX mbuf DMA tag */ error = bus_dma_tag_create(dma->parent_dtag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &dma->rxbuf_dtag); if (error) { device_printf(sc->sc_dev, "can't create mbuf DMA tag\n"); goto fail0; } error = bus_dma_tag_create(dma->parent_dtag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &dma->txbuf_dtag); if (error) { device_printf(sc->sc_dev, "can't create mbuf DMA tag\n"); goto fail1; } dma->wme[WME_AC_BK] = bwn_dma_ringsetup(mac, 0, 1, dma->dmatype); if (!dma->wme[WME_AC_BK]) goto fail2; dma->wme[WME_AC_BE] = bwn_dma_ringsetup(mac, 1, 1, dma->dmatype); if (!dma->wme[WME_AC_BE]) goto fail3; dma->wme[WME_AC_VI] = bwn_dma_ringsetup(mac, 2, 1, dma->dmatype); if (!dma->wme[WME_AC_VI]) goto fail4; dma->wme[WME_AC_VO] = bwn_dma_ringsetup(mac, 3, 1, dma->dmatype); if (!dma->wme[WME_AC_VO]) goto fail5; dma->mcast = bwn_dma_ringsetup(mac, 4, 1, dma->dmatype); if (!dma->mcast) goto fail6; dma->rx = bwn_dma_ringsetup(mac, 0, 0, dma->dmatype); if (!dma->rx) goto fail7; return (error); fail7: bwn_dma_ringfree(&dma->mcast); fail6: bwn_dma_ringfree(&dma->wme[WME_AC_VO]); fail5: bwn_dma_ringfree(&dma->wme[WME_AC_VI]); fail4: bwn_dma_ringfree(&dma->wme[WME_AC_BE]); fail3: bwn_dma_ringfree(&dma->wme[WME_AC_BK]); fail2: bus_dma_tag_destroy(dma->txbuf_dtag); fail1: bus_dma_tag_destroy(dma->rxbuf_dtag); fail0: bus_dma_tag_destroy(dma->parent_dtag); return (error); } static struct bwn_dma_ring * bwn_dma_parse_cookie(struct bwn_mac *mac, const struct bwn_txstatus *status, uint16_t cookie, int *slot) { struct bwn_dma *dma = &mac->mac_method.dma; struct bwn_dma_ring *dr; struct bwn_softc *sc = mac->mac_sc; BWN_ASSERT_LOCKED(mac->mac_sc); switch (cookie & 0xf000) { case 0x1000: dr = dma->wme[WME_AC_BK]; break; case 0x2000: dr = dma->wme[WME_AC_BE]; break; case 0x3000: dr = dma->wme[WME_AC_VI]; break; case 0x4000: dr = dma->wme[WME_AC_VO]; break; case 0x5000: dr = dma->mcast; break; default: + dr = NULL; KASSERT(0 == 1, ("invalid cookie value %d", cookie & 0xf000)); } *slot = (cookie & 0x0fff); if (*slot < 0 || *slot >= dr->dr_numslots) { /* * XXX FIXME: sometimes H/W returns TX DONE events duplicately * that it occurs events which have same H/W sequence numbers. * When it's occurred just prints a WARNING msgs and ignores. */ KASSERT(status->seq == dma->lastseq, ("%s:%d: fail", __func__, __LINE__)); device_printf(sc->sc_dev, "out of slot ranges (0 < %d < %d)\n", *slot, dr->dr_numslots); return (NULL); } dma->lastseq = status->seq; return (dr); } static void bwn_dma_stop(struct bwn_mac *mac) { struct bwn_dma *dma; if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0) return; dma = &mac->mac_method.dma; bwn_dma_ringstop(&dma->rx); bwn_dma_ringstop(&dma->wme[WME_AC_BK]); bwn_dma_ringstop(&dma->wme[WME_AC_BE]); bwn_dma_ringstop(&dma->wme[WME_AC_VI]); bwn_dma_ringstop(&dma->wme[WME_AC_VO]); bwn_dma_ringstop(&dma->mcast); } static void bwn_dma_ringstop(struct bwn_dma_ring **dr) { if (dr == NULL) return; bwn_dma_cleanup(*dr); } static void bwn_pio_stop(struct bwn_mac *mac) { struct bwn_pio *pio; if (mac->mac_flags & BWN_MAC_FLAG_DMA) return; pio = &mac->mac_method.pio; bwn_destroy_queue_tx(&pio->mcast); bwn_destroy_queue_tx(&pio->wme[WME_AC_VO]); bwn_destroy_queue_tx(&pio->wme[WME_AC_VI]); bwn_destroy_queue_tx(&pio->wme[WME_AC_BE]); bwn_destroy_queue_tx(&pio->wme[WME_AC_BK]); } static void bwn_led_attach(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct siba_softc *siba = mac->mac_sd->sd_bus; const uint8_t *led_act = NULL; uint16_t val[BWN_LED_MAX]; int i; sc->sc_led_idle = (2350 * hz) / 1000; sc->sc_led_blink = 1; for (i = 0; i < N(bwn_vendor_led_act); ++i) { if (siba->siba_pci_subvid == bwn_vendor_led_act[i].vid) { led_act = bwn_vendor_led_act[i].led_act; break; } } if (led_act == NULL) led_act = bwn_default_led_act; val[0] = siba->siba_sprom.gpio0; val[1] = siba->siba_sprom.gpio1; val[2] = siba->siba_sprom.gpio2; val[3] = siba->siba_sprom.gpio3; for (i = 0; i < BWN_LED_MAX; ++i) { struct bwn_led *led = &sc->sc_leds[i]; if (val[i] == 0xff) { led->led_act = led_act[i]; } else { if (val[i] & BWN_LED_ACT_LOW) led->led_flags |= BWN_LED_F_ACTLOW; led->led_act = val[i] & BWN_LED_ACT_MASK; } led->led_mask = (1 << i); if (led->led_act == BWN_LED_ACT_BLINK_SLOW || led->led_act == BWN_LED_ACT_BLINK_POLL || led->led_act == BWN_LED_ACT_BLINK) { led->led_flags |= BWN_LED_F_BLINK; if (led->led_act == BWN_LED_ACT_BLINK_POLL) led->led_flags |= BWN_LED_F_POLLABLE; else if (led->led_act == BWN_LED_ACT_BLINK_SLOW) led->led_flags |= BWN_LED_F_SLOW; if (sc->sc_blink_led == NULL) { sc->sc_blink_led = led; if (led->led_flags & BWN_LED_F_SLOW) BWN_LED_SLOWDOWN(sc->sc_led_idle); } } DPRINTF(sc, BWN_DEBUG_LED, "%dth led, act %d, lowact %d\n", i, led->led_act, led->led_flags & BWN_LED_F_ACTLOW); } callout_init_mtx(&sc->sc_led_blink_ch, &sc->sc_mtx, 0); } static __inline uint16_t bwn_led_onoff(const struct bwn_led *led, uint16_t val, int on) { if (led->led_flags & BWN_LED_F_ACTLOW) on = !on; if (on) val |= led->led_mask; else val &= ~led->led_mask; return val; } static void bwn_led_newstate(struct bwn_mac *mac, enum ieee80211_state nstate) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t val; int i; if (nstate == IEEE80211_S_INIT) { callout_stop(&sc->sc_led_blink_ch); sc->sc_led_blinking = 0; } if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; val = BWN_READ_2(mac, BWN_GPIO_CONTROL); for (i = 0; i < BWN_LED_MAX; ++i) { struct bwn_led *led = &sc->sc_leds[i]; int on; if (led->led_act == BWN_LED_ACT_UNKN || led->led_act == BWN_LED_ACT_NULL) continue; if ((led->led_flags & BWN_LED_F_BLINK) && nstate != IEEE80211_S_INIT) continue; switch (led->led_act) { case BWN_LED_ACT_ON: /* Always on */ on = 1; break; case BWN_LED_ACT_OFF: /* Always off */ case BWN_LED_ACT_5GHZ: /* TODO: 11A */ on = 0; break; default: on = 1; switch (nstate) { case IEEE80211_S_INIT: on = 0; break; case IEEE80211_S_RUN: if (led->led_act == BWN_LED_ACT_11G && ic->ic_curmode != IEEE80211_MODE_11G) on = 0; break; default: if (led->led_act == BWN_LED_ACT_ASSOC) on = 0; break; } break; } val = bwn_led_onoff(led, val, on); } BWN_WRITE_2(mac, BWN_GPIO_CONTROL, val); } static void bwn_led_event(struct bwn_mac *mac, int event) { struct bwn_softc *sc = mac->mac_sc; struct bwn_led *led = sc->sc_blink_led; int rate; if (event == BWN_LED_EVENT_POLL) { if ((led->led_flags & BWN_LED_F_POLLABLE) == 0) return; if (ticks - sc->sc_led_ticks < sc->sc_led_idle) return; } sc->sc_led_ticks = ticks; if (sc->sc_led_blinking) return; switch (event) { case BWN_LED_EVENT_RX: rate = sc->sc_rx_rate; break; case BWN_LED_EVENT_TX: rate = sc->sc_tx_rate; break; case BWN_LED_EVENT_POLL: rate = 0; break; default: panic("unknown LED event %d\n", event); break; } bwn_led_blink_start(mac, bwn_led_duration[rate].on_dur, bwn_led_duration[rate].off_dur); } static void bwn_led_blink_start(struct bwn_mac *mac, int on_dur, int off_dur) { struct bwn_softc *sc = mac->mac_sc; struct bwn_led *led = sc->sc_blink_led; uint16_t val; val = BWN_READ_2(mac, BWN_GPIO_CONTROL); val = bwn_led_onoff(led, val, 1); BWN_WRITE_2(mac, BWN_GPIO_CONTROL, val); if (led->led_flags & BWN_LED_F_SLOW) { BWN_LED_SLOWDOWN(on_dur); BWN_LED_SLOWDOWN(off_dur); } sc->sc_led_blinking = 1; sc->sc_led_blink_offdur = off_dur; callout_reset(&sc->sc_led_blink_ch, on_dur, bwn_led_blink_next, mac); } static void bwn_led_blink_next(void *arg) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; uint16_t val; val = BWN_READ_2(mac, BWN_GPIO_CONTROL); val = bwn_led_onoff(sc->sc_blink_led, val, 0); BWN_WRITE_2(mac, BWN_GPIO_CONTROL, val); callout_reset(&sc->sc_led_blink_ch, sc->sc_led_blink_offdur, bwn_led_blink_end, mac); } static void bwn_led_blink_end(void *arg) { struct bwn_mac *mac = arg; struct bwn_softc *sc = mac->mac_sc; sc->sc_led_blinking = 0; } static int bwn_suspend(device_t dev) { struct bwn_softc *sc = device_get_softc(dev); bwn_stop(sc, 1); return (0); } static int bwn_resume(device_t dev) { struct bwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; if (ifp->if_flags & IFF_UP) bwn_init(sc); return (0); } static void bwn_rfswitch(void *arg) { struct bwn_softc *sc = arg; struct bwn_mac *mac = sc->sc_curmac; int cur = 0, prev = 0; KASSERT(mac->mac_status >= BWN_MAC_STATUS_STARTED, ("%s: invalid MAC status %d", __func__, mac->mac_status)); if (mac->mac_phy.rf_rev >= 3 || mac->mac_phy.type == BWN_PHYTYPE_LP) { if (!(BWN_READ_4(mac, BWN_RF_HWENABLED_HI) & BWN_RF_HWENABLED_HI_MASK)) cur = 1; } else { if (BWN_READ_2(mac, BWN_RF_HWENABLED_LO) & BWN_RF_HWENABLED_LO_MASK) cur = 1; } if (mac->mac_flags & BWN_MAC_FLAG_RADIO_ON) prev = 1; if (cur != prev) { if (cur) mac->mac_flags |= BWN_MAC_FLAG_RADIO_ON; else mac->mac_flags &= ~BWN_MAC_FLAG_RADIO_ON; device_printf(sc->sc_dev, "status of RF switch is changed to %s\n", cur ? "ON" : "OFF"); if (cur != mac->mac_phy.rf_on) { if (cur) bwn_rf_turnon(mac); else bwn_rf_turnoff(mac); } } callout_schedule(&sc->sc_rfswitch_ch, hz); } static void bwn_phy_lp_init_pre(struct bwn_mac *mac) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_lp *plp = &phy->phy_lp; plp->plp_antenna = BWN_ANT_DEFAULT; } static int bwn_phy_lp_init(struct bwn_mac *mac) { static const struct bwn_stxtable tables[] = { { 2, 6, 0x3d, 3, 0x01 }, { 1, 12, 0x4c, 1, 0x01 }, { 1, 8, 0x50, 0, 0x7f }, { 0, 8, 0x44, 0, 0xff }, { 1, 0, 0x4a, 0, 0xff }, { 0, 4, 0x4d, 0, 0xff }, { 1, 4, 0x4e, 0, 0xff }, { 0, 12, 0x4f, 0, 0x0f }, { 1, 0, 0x4f, 4, 0x0f }, { 3, 0, 0x49, 0, 0x0f }, { 4, 3, 0x46, 4, 0x07 }, { 3, 15, 0x46, 0, 0x01 }, { 4, 0, 0x46, 1, 0x07 }, { 3, 8, 0x48, 4, 0x07 }, { 3, 11, 0x48, 0, 0x0f }, { 3, 4, 0x49, 4, 0x0f }, { 2, 15, 0x45, 0, 0x01 }, { 5, 13, 0x52, 4, 0x07 }, { 6, 0, 0x52, 7, 0x01 }, { 5, 3, 0x41, 5, 0x07 }, { 5, 6, 0x41, 0, 0x0f }, { 5, 10, 0x42, 5, 0x07 }, { 4, 15, 0x42, 0, 0x01 }, { 5, 0, 0x42, 1, 0x07 }, { 4, 11, 0x43, 4, 0x0f }, { 4, 7, 0x43, 0, 0x0f }, { 4, 6, 0x45, 1, 0x01 }, { 2, 7, 0x40, 4, 0x0f }, { 2, 11, 0x40, 0, 0x0f } }; struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; const struct bwn_stxtable *st; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i, error; uint16_t tmp; bwn_phy_lp_readsprom(mac); /* XXX bad place */ bwn_phy_lp_bbinit(mac); /* initialize RF */ BWN_PHY_SET(mac, BWN_PHY_4WIRECTL, 0x2); DELAY(1); BWN_PHY_MASK(mac, BWN_PHY_4WIRECTL, 0xfffd); DELAY(1); if (mac->mac_phy.rf_ver == 0x2062) bwn_phy_lp_b2062_init(mac); else { bwn_phy_lp_b2063_init(mac); /* synchronize stx table. */ for (i = 0; i < N(tables); i++) { st = &tables[i]; tmp = BWN_RF_READ(mac, st->st_rfaddr); tmp >>= st->st_rfshift; tmp <<= st->st_physhift; BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xf2 + st->st_phyoffset), ~(st->st_mask << st->st_physhift), tmp); } BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xf0), 0x5f80); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xf1), 0); } /* calibrate RC */ if (mac->mac_phy.rev >= 2) bwn_phy_lp_rxcal_r2(mac); else if (!plp->plp_rccap) { if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) bwn_phy_lp_rccal_r12(mac); } else bwn_phy_lp_set_rccap(mac); error = bwn_phy_lp_switch_channel(mac, 7); if (error) device_printf(sc->sc_dev, "failed to change channel 7 (%d)\n", error); bwn_phy_lp_txpctl_init(mac); bwn_phy_lp_calib(mac); return (0); } static uint16_t bwn_phy_lp_read(struct bwn_mac *mac, uint16_t reg) { BWN_WRITE_2(mac, BWN_PHYCTL, reg); return (BWN_READ_2(mac, BWN_PHYDATA)); } static void bwn_phy_lp_write(struct bwn_mac *mac, uint16_t reg, uint16_t value) { BWN_WRITE_2(mac, BWN_PHYCTL, reg); BWN_WRITE_2(mac, BWN_PHYDATA, value); } static void bwn_phy_lp_maskset(struct bwn_mac *mac, uint16_t reg, uint16_t mask, uint16_t set) { BWN_WRITE_2(mac, BWN_PHYCTL, reg); BWN_WRITE_2(mac, BWN_PHYDATA, (BWN_READ_2(mac, BWN_PHYDATA) & mask) | set); } static uint16_t bwn_phy_lp_rf_read(struct bwn_mac *mac, uint16_t reg) { KASSERT(reg != 1, ("unaccessible register %d", reg)); if (mac->mac_phy.rev < 2 && reg != 0x4001) reg |= 0x100; if (mac->mac_phy.rev >= 2) reg |= 0x200; BWN_WRITE_2(mac, BWN_RFCTL, reg); return BWN_READ_2(mac, BWN_RFDATALO); } static void bwn_phy_lp_rf_write(struct bwn_mac *mac, uint16_t reg, uint16_t value) { KASSERT(reg != 1, ("unaccessible register %d", reg)); BWN_WRITE_2(mac, BWN_RFCTL, reg); BWN_WRITE_2(mac, BWN_RFDATALO, value); } static void bwn_phy_lp_rf_onoff(struct bwn_mac *mac, int on) { if (on) { BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xe0ff); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, (mac->mac_phy.rev >= 2) ? 0xf7f7 : 0xffe7); return; } if (mac->mac_phy.rev >= 2) { BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0x83ff); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x1f00); BWN_PHY_MASK(mac, BWN_PHY_AFE_DDFS, 0x80ff); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xdfff); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x0808); return; } BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xe0ff); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x1f00); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfcff); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x0018); } static int bwn_phy_lp_switch_channel(struct bwn_mac *mac, uint32_t chan) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_lp *plp = &phy->phy_lp; int error; if (phy->rf_ver == 0x2063) { error = bwn_phy_lp_b2063_switch_channel(mac, chan); if (error) return (error); } else { error = bwn_phy_lp_b2062_switch_channel(mac, chan); if (error) return (error); bwn_phy_lp_set_anafilter(mac, chan); bwn_phy_lp_set_gaintbl(mac, ieee80211_ieee2mhz(chan, 0)); } plp->plp_chan = chan; BWN_WRITE_2(mac, BWN_CHANNEL, chan); return (0); } static uint32_t bwn_phy_lp_get_default_chan(struct bwn_mac *mac) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; device_printf(sc->sc_dev, "correct?\n"); return (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? 1 : 36); } static void bwn_phy_lp_set_antenna(struct bwn_mac *mac, int antenna) { struct bwn_phy *phy = &mac->mac_phy; struct bwn_phy_lp *plp = &phy->phy_lp; if (phy->rev >= 2 || antenna > BWN_ANTAUTO1) return; bwn_hf_write(mac, bwn_hf_read(mac) & ~BWN_HF_UCODE_ANTDIV_HELPER); BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xfffd, antenna & 0x2); BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xfffe, antenna & 0x1); bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_UCODE_ANTDIV_HELPER); plp->plp_antenna = antenna; } static void bwn_phy_lp_task_60s(struct bwn_mac *mac) { bwn_phy_lp_calib(mac); } static void bwn_phy_lp_readsprom(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct siba_sprom *sprom = &siba->siba_sprom; device_printf(sc->sc_dev, "XXX using %dghz\n", IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? 2 : 5); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { plp->plp_txisoband_m = sprom->tri2g; plp->plp_bxarch = sprom->bxa2g; plp->plp_rxpwroffset = sprom->rxpo2g; plp->plp_rssivf = sprom->rssismf2g; plp->plp_rssivc = sprom->rssismc2g; plp->plp_rssigs = sprom->rssisav2g; return; } plp->plp_txisoband_l = sprom->tri5gl; plp->plp_txisoband_m = sprom->tri5g; plp->plp_txisoband_h = sprom->tri5gh; plp->plp_bxarch = sprom->bxa5g; plp->plp_rxpwroffset = sprom->rxpo5g; plp->plp_rssivf = sprom->rssismf5g; plp->plp_rssivc = sprom->rssismc5g; plp->plp_rssigs = sprom->rssisav5g; } static void bwn_phy_lp_bbinit(struct bwn_mac *mac) { bwn_phy_lp_tblinit(mac); if (mac->mac_phy.rev >= 2) bwn_phy_lp_bbinit_r2(mac); else bwn_phy_lp_bbinit_r01(mac); } static void bwn_phy_lp_txpctl_init(struct bwn_mac *mac) { struct bwn_txgain gain_2ghz = { 4, 12, 12, 0 }; struct bwn_txgain gain_5ghz = { 7, 15, 14, 0 }; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; bwn_phy_lp_set_txgain(mac, IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? &gain_2ghz : &gain_5ghz); bwn_phy_lp_set_bbmult(mac, 150); } static void bwn_phy_lp_calib(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const struct bwn_rxcompco *rc = NULL; struct bwn_txgain ogain; int i, omode, oafeovr, orf, obbmult; uint8_t mode, fc = 0; if (plp->plp_chanfullcal != plp->plp_chan) { plp->plp_chanfullcal = plp->plp_chan; fc = 1; } bwn_mac_suspend(mac); /* BlueTooth Coexistance Override */ BWN_WRITE_2(mac, BWN_BTCOEX_CTL, 0x3); BWN_WRITE_2(mac, BWN_BTCOEX_TXCTL, 0xff); if (mac->mac_phy.rev >= 2) bwn_phy_lp_digflt_save(mac); bwn_phy_lp_get_txpctlmode(mac); mode = plp->plp_txpctlmode; bwn_phy_lp_set_txpctlmode(mac, BWN_PHYLP_TXPCTL_OFF); if (mac->mac_phy.rev == 0 && mode != BWN_PHYLP_TXPCTL_OFF) bwn_phy_lp_bugfix(mac); if (mac->mac_phy.rev >= 2 && fc == 1) { bwn_phy_lp_get_txpctlmode(mac); omode = plp->plp_txpctlmode; oafeovr = BWN_PHY_READ(mac, BWN_PHY_AFE_CTL_OVR) & 0x40; if (oafeovr) ogain = bwn_phy_lp_get_txgain(mac); orf = BWN_PHY_READ(mac, BWN_PHY_RF_PWR_OVERRIDE) & 0xff; obbmult = bwn_phy_lp_get_bbmult(mac); bwn_phy_lp_set_txpctlmode(mac, BWN_PHYLP_TXPCTL_OFF); if (oafeovr) bwn_phy_lp_set_txgain(mac, &ogain); bwn_phy_lp_set_bbmult(mac, obbmult); bwn_phy_lp_set_txpctlmode(mac, omode); BWN_PHY_SETMASK(mac, BWN_PHY_RF_PWR_OVERRIDE, 0xff00, orf); } bwn_phy_lp_set_txpctlmode(mac, mode); if (mac->mac_phy.rev >= 2) bwn_phy_lp_digflt_restore(mac); /* do RX IQ Calculation; assumes that noise is true. */ if (siba->siba_chipid == 0x5354) { for (i = 0; i < N(bwn_rxcompco_5354); i++) { if (bwn_rxcompco_5354[i].rc_chan == plp->plp_chan) rc = &bwn_rxcompco_5354[i]; } } else if (mac->mac_phy.rev >= 2) rc = &bwn_rxcompco_r2; else { for (i = 0; i < N(bwn_rxcompco_r12); i++) { if (bwn_rxcompco_r12[i].rc_chan == plp->plp_chan) rc = &bwn_rxcompco_r12[i]; } } if (rc == NULL) goto fail; BWN_PHY_SETMASK(mac, BWN_PHY_RX_COMP_COEFF_S, 0xff00, rc->rc_c1); BWN_PHY_SETMASK(mac, BWN_PHY_RX_COMP_COEFF_S, 0x00ff, rc->rc_c0 << 8); bwn_phy_lp_set_trsw_over(mac, 1 /* TX */, 0 /* RX */); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x8); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xfff7, 0); } else { BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x20); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xffdf, 0); } bwn_phy_lp_set_rxgain(mac, 0x2d5d); BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVR, 0xfffe); BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVRVAL, 0xfffe); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x800); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0x800); bwn_phy_lp_set_deaf(mac, 0); /* XXX no checking return value? */ (void)bwn_phy_lp_calc_rx_iq_comp(mac, 0xfff0); bwn_phy_lp_clear_deaf(mac, 0); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xfffc); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xfff7); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xffdf); /* disable RX GAIN override. */ BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xfffe); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xffef); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xffbf); if (mac->mac_phy.rev >= 2) { BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xfeff); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xfbff); BWN_PHY_MASK(mac, BWN_PHY_OFDM(0xe5), 0xfff7); } } else { BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xfdff); } BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVR, 0xfffe); BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVRVAL, 0xf7ff); fail: bwn_mac_enable(mac); } static void bwn_phy_lp_switch_analog(struct bwn_mac *mac, int on) { if (on) { BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVR, 0xfff8); return; } BWN_PHY_SET(mac, BWN_PHY_AFE_CTL_OVRVAL, 0x0007); BWN_PHY_SET(mac, BWN_PHY_AFE_CTL_OVR, 0x0007); } static int bwn_phy_lp_b2063_switch_channel(struct bwn_mac *mac, uint8_t chan) { struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; static const struct bwn_b206x_chan *bc = NULL; uint32_t count, freqref, freqvco, freqxtal, val[3], timeout, timeoutref, tmp[6]; uint16_t old, scale, tmp16; int i, div; for (i = 0; i < N(bwn_b2063_chantable); i++) { if (bwn_b2063_chantable[i].bc_chan == chan) { bc = &bwn_b2063_chantable[i]; break; } } if (bc == NULL) return (EINVAL); BWN_RF_WRITE(mac, BWN_B2063_LOGEN_VCOBUF1, bc->bc_data[0]); BWN_RF_WRITE(mac, BWN_B2063_LOGEN_MIXER2, bc->bc_data[1]); BWN_RF_WRITE(mac, BWN_B2063_LOGEN_BUF2, bc->bc_data[2]); BWN_RF_WRITE(mac, BWN_B2063_LOGEN_RCCR1, bc->bc_data[3]); BWN_RF_WRITE(mac, BWN_B2063_A_RX_1ST3, bc->bc_data[4]); BWN_RF_WRITE(mac, BWN_B2063_A_RX_2ND1, bc->bc_data[5]); BWN_RF_WRITE(mac, BWN_B2063_A_RX_2ND4, bc->bc_data[6]); BWN_RF_WRITE(mac, BWN_B2063_A_RX_2ND7, bc->bc_data[7]); BWN_RF_WRITE(mac, BWN_B2063_A_RX_PS6, bc->bc_data[8]); BWN_RF_WRITE(mac, BWN_B2063_TX_RF_CTL2, bc->bc_data[9]); BWN_RF_WRITE(mac, BWN_B2063_TX_RF_CTL5, bc->bc_data[10]); BWN_RF_WRITE(mac, BWN_B2063_PA_CTL11, bc->bc_data[11]); old = BWN_RF_READ(mac, BWN_B2063_COM15); BWN_RF_SET(mac, BWN_B2063_COM15, 0x1e); freqxtal = siba->siba_cc.scc_pmu.freq * 1000; freqvco = bc->bc_freq << ((bc->bc_freq > 4000) ? 1 : 2); freqref = freqxtal * 3; div = (freqxtal <= 26000000 ? 1 : 2); timeout = ((((8 * freqxtal) / (div * 5000000)) + 1) >> 1) - 1; timeoutref = ((((8 * freqxtal) / (div * (timeout + 1))) + 999999) / 1000000) + 1; BWN_RF_WRITE(mac, BWN_B2063_JTAG_VCO_CALIB3, 0x2); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_VCO_CALIB6, 0xfff8, timeout >> 2); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_VCO_CALIB7, 0xff9f,timeout << 5); BWN_RF_WRITE(mac, BWN_B2063_JTAG_VCO_CALIB5, timeoutref); val[0] = bwn_phy_lp_roundup(freqxtal, 1000000, 16); val[1] = bwn_phy_lp_roundup(freqxtal, 1000000 * div, 16); val[2] = bwn_phy_lp_roundup(freqvco, 3, 16); count = (bwn_phy_lp_roundup(val[2], val[1] + 16, 16) * (timeout + 1) * (timeoutref + 1)) - 1; BWN_RF_SETMASK(mac, BWN_B2063_JTAG_VCO_CALIB7, 0xf0, count >> 8); BWN_RF_WRITE(mac, BWN_B2063_JTAG_VCO_CALIB8, count & 0xff); tmp[0] = ((val[2] * 62500) / freqref) << 4; tmp[1] = ((val[2] * 62500) % freqref) << 4; while (tmp[1] >= freqref) { tmp[0]++; tmp[1] -= freqref; } BWN_RF_SETMASK(mac, BWN_B2063_JTAG_SG1, 0xffe0, tmp[0] >> 4); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_SG2, 0xfe0f, tmp[0] << 4); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_SG2, 0xfff0, tmp[0] >> 16); BWN_RF_WRITE(mac, BWN_B2063_JTAG_SG3, (tmp[1] >> 8) & 0xff); BWN_RF_WRITE(mac, BWN_B2063_JTAG_SG4, tmp[1] & 0xff); BWN_RF_WRITE(mac, BWN_B2063_JTAG_LF1, 0xb9); BWN_RF_WRITE(mac, BWN_B2063_JTAG_LF2, 0x88); BWN_RF_WRITE(mac, BWN_B2063_JTAG_LF3, 0x28); BWN_RF_WRITE(mac, BWN_B2063_JTAG_LF4, 0x63); tmp[2] = ((41 * (val[2] - 3000)) /1200) + 27; tmp[3] = bwn_phy_lp_roundup(132000 * tmp[0], 8451, 16); if ((tmp[3] + tmp[2] - 1) / tmp[2] > 60) { scale = 1; tmp[4] = ((tmp[3] + tmp[2]) / (tmp[2] << 1)) - 8; } else { scale = 0; tmp[4] = ((tmp[3] + (tmp[2] >> 1)) / tmp[2]) - 8; } BWN_RF_SETMASK(mac, BWN_B2063_JTAG_CP2, 0xffc0, tmp[4]); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_CP2, 0xffbf, scale << 6); tmp[5] = bwn_phy_lp_roundup(100 * val[0], val[2], 16) * (tmp[4] * 8) * (scale + 1); if (tmp[5] > 150) tmp[5] = 0; BWN_RF_SETMASK(mac, BWN_B2063_JTAG_CP3, 0xffe0, tmp[5]); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_CP3, 0xffdf, scale << 5); BWN_RF_SETMASK(mac, BWN_B2063_JTAG_XTAL_12, 0xfffb, 0x4); if (freqxtal > 26000000) BWN_RF_SET(mac, BWN_B2063_JTAG_XTAL_12, 0x2); else BWN_RF_MASK(mac, BWN_B2063_JTAG_XTAL_12, 0xfd); if (val[0] == 45) BWN_RF_SET(mac, BWN_B2063_JTAG_VCO1, 0x2); else BWN_RF_MASK(mac, BWN_B2063_JTAG_VCO1, 0xfd); BWN_RF_SET(mac, BWN_B2063_PLL_SP2, 0x3); DELAY(1); BWN_RF_MASK(mac, BWN_B2063_PLL_SP2, 0xfffc); /* VCO Calibration */ BWN_RF_MASK(mac, BWN_B2063_PLL_SP1, ~0x40); tmp16 = BWN_RF_READ(mac, BWN_B2063_JTAG_CALNRST) & 0xf8; BWN_RF_WRITE(mac, BWN_B2063_JTAG_CALNRST, tmp16); DELAY(1); BWN_RF_WRITE(mac, BWN_B2063_JTAG_CALNRST, tmp16 | 0x4); DELAY(1); BWN_RF_WRITE(mac, BWN_B2063_JTAG_CALNRST, tmp16 | 0x6); DELAY(1); BWN_RF_WRITE(mac, BWN_B2063_JTAG_CALNRST, tmp16 | 0x7); DELAY(300); BWN_RF_SET(mac, BWN_B2063_PLL_SP1, 0x40); BWN_RF_WRITE(mac, BWN_B2063_COM15, old); return (0); } static int bwn_phy_lp_b2062_switch_channel(struct bwn_mac *mac, uint8_t chan) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; const struct bwn_b206x_chan *bc = NULL; uint32_t freqxtal = siba->siba_cc.scc_pmu.freq * 1000; uint32_t tmp[9]; int i; for (i = 0; i < N(bwn_b2062_chantable); i++) { if (bwn_b2062_chantable[i].bc_chan == chan) { bc = &bwn_b2062_chantable[i]; break; } } if (bc == NULL) return (EINVAL); BWN_RF_SET(mac, BWN_B2062_S_RFPLLCTL14, 0x04); BWN_RF_WRITE(mac, BWN_B2062_N_LGENATUNE0, bc->bc_data[0]); BWN_RF_WRITE(mac, BWN_B2062_N_LGENATUNE2, bc->bc_data[1]); BWN_RF_WRITE(mac, BWN_B2062_N_LGENATUNE3, bc->bc_data[2]); BWN_RF_WRITE(mac, BWN_B2062_N_TX_TUNE, bc->bc_data[3]); BWN_RF_WRITE(mac, BWN_B2062_S_LGENG_CTL1, bc->bc_data[4]); BWN_RF_WRITE(mac, BWN_B2062_N_LGENACTL5, bc->bc_data[5]); BWN_RF_WRITE(mac, BWN_B2062_N_LGENACTL6, bc->bc_data[6]); BWN_RF_WRITE(mac, BWN_B2062_N_TX_PGA, bc->bc_data[7]); BWN_RF_WRITE(mac, BWN_B2062_N_TX_PAD, bc->bc_data[8]); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL33, 0xcc); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL34, 0x07); bwn_phy_lp_b2062_reset_pllbias(mac); tmp[0] = freqxtal / 1000; tmp[1] = plp->plp_div * 1000; tmp[2] = tmp[1] * ieee80211_ieee2mhz(chan, 0); if (ieee80211_ieee2mhz(chan, 0) < 4000) tmp[2] *= 2; tmp[3] = 48 * tmp[0]; tmp[5] = tmp[2] / tmp[3]; tmp[6] = tmp[2] % tmp[3]; BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL26, tmp[5]); tmp[4] = tmp[6] * 0x100; tmp[5] = tmp[4] / tmp[3]; tmp[6] = tmp[4] % tmp[3]; BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL27, tmp[5]); tmp[4] = tmp[6] * 0x100; tmp[5] = tmp[4] / tmp[3]; tmp[6] = tmp[4] % tmp[3]; BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL28, tmp[5]); tmp[4] = tmp[6] * 0x100; tmp[5] = tmp[4] / tmp[3]; tmp[6] = tmp[4] % tmp[3]; BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL29, tmp[5] + ((2 * tmp[6]) / tmp[3])); tmp[7] = BWN_RF_READ(mac, BWN_B2062_S_RFPLLCTL19); tmp[8] = ((2 * tmp[2] * (tmp[7] + 1)) + (3 * tmp[0])) / (6 * tmp[0]); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL23, (tmp[8] >> 8) + 16); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL24, tmp[8] & 0xff); bwn_phy_lp_b2062_vco_calib(mac); if (BWN_RF_READ(mac, BWN_B2062_S_RFPLLCTL3) & 0x10) { BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL33, 0xfc); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL34, 0); bwn_phy_lp_b2062_reset_pllbias(mac); bwn_phy_lp_b2062_vco_calib(mac); if (BWN_RF_READ(mac, BWN_B2062_S_RFPLLCTL3) & 0x10) { BWN_RF_MASK(mac, BWN_B2062_S_RFPLLCTL14, ~0x04); return (EIO); } } BWN_RF_MASK(mac, BWN_B2062_S_RFPLLCTL14, ~0x04); return (0); } static void bwn_phy_lp_set_anafilter(struct bwn_mac *mac, uint8_t channel) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; uint16_t tmp = (channel == 14); if (mac->mac_phy.rev < 2) { BWN_PHY_SETMASK(mac, BWN_PHY_LP_PHY_CTL, 0xfcff, tmp << 9); if ((mac->mac_phy.rev == 1) && (plp->plp_rccap)) bwn_phy_lp_set_rccap(mac); return; } BWN_RF_WRITE(mac, BWN_B2063_TX_BB_SP3, 0x3f); } static void bwn_phy_lp_set_gaintbl(struct bwn_mac *mac, uint32_t freq) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t iso, tmp[3]; KASSERT(mac->mac_phy.rev < 2, ("%s:%d: fail", __func__, __LINE__)); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) iso = plp->plp_txisoband_m; else if (freq <= 5320) iso = plp->plp_txisoband_l; else if (freq <= 5700) iso = plp->plp_txisoband_m; else iso = plp->plp_txisoband_h; tmp[0] = ((iso - 26) / 12) << 12; tmp[1] = tmp[0] + 0x1000; tmp[2] = tmp[0] + 0x2000; bwn_tab_write_multi(mac, BWN_TAB_2(13, 0), 3, tmp); bwn_tab_write_multi(mac, BWN_TAB_2(12, 0), 3, tmp); } static void bwn_phy_lp_digflt_save(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; int i; static const uint16_t addr[] = { BWN_PHY_OFDM(0xc1), BWN_PHY_OFDM(0xc2), BWN_PHY_OFDM(0xc3), BWN_PHY_OFDM(0xc4), BWN_PHY_OFDM(0xc5), BWN_PHY_OFDM(0xc6), BWN_PHY_OFDM(0xc7), BWN_PHY_OFDM(0xc8), BWN_PHY_OFDM(0xcf), }; static const uint16_t val[] = { 0xde5e, 0xe832, 0xe331, 0x4d26, 0x0026, 0x1420, 0x0020, 0xfe08, 0x0008, }; for (i = 0; i < N(addr); i++) { plp->plp_digfilt[i] = BWN_PHY_READ(mac, addr[i]); BWN_PHY_WRITE(mac, addr[i], val[i]); } } static void bwn_phy_lp_get_txpctlmode(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; uint16_t ctl; ctl = BWN_PHY_READ(mac, BWN_PHY_TX_PWR_CTL_CMD); switch (ctl & BWN_PHY_TX_PWR_CTL_CMD_MODE) { case BWN_PHY_TX_PWR_CTL_CMD_MODE_OFF: plp->plp_txpctlmode = BWN_PHYLP_TXPCTL_OFF; break; case BWN_PHY_TX_PWR_CTL_CMD_MODE_SW: plp->plp_txpctlmode = BWN_PHYLP_TXPCTL_ON_SW; break; case BWN_PHY_TX_PWR_CTL_CMD_MODE_HW: plp->plp_txpctlmode = BWN_PHYLP_TXPCTL_ON_HW; break; default: plp->plp_txpctlmode = BWN_PHYLP_TXPCTL_UNKNOWN; device_printf(sc->sc_dev, "unknown command mode\n"); break; } } static void bwn_phy_lp_set_txpctlmode(struct bwn_mac *mac, uint8_t mode) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; uint16_t ctl; uint8_t old; bwn_phy_lp_get_txpctlmode(mac); old = plp->plp_txpctlmode; if (old == mode) return; plp->plp_txpctlmode = mode; if (old != BWN_PHYLP_TXPCTL_ON_HW && mode == BWN_PHYLP_TXPCTL_ON_HW) { BWN_PHY_SETMASK(mac, BWN_PHY_TX_PWR_CTL_CMD, 0xff80, plp->plp_tssiidx); BWN_PHY_SETMASK(mac, BWN_PHY_TX_PWR_CTL_NNUM, 0x8fff, ((uint16_t)plp->plp_tssinpt << 16)); /* disable TX GAIN override */ if (mac->mac_phy.rev < 2) BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xfeff); else { BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xff7f); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xbfff); } BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVR, 0xffbf); plp->plp_txpwridx = -1; } if (mac->mac_phy.rev >= 2) { if (mode == BWN_PHYLP_TXPCTL_ON_HW) BWN_PHY_SET(mac, BWN_PHY_OFDM(0xd0), 0x2); else BWN_PHY_MASK(mac, BWN_PHY_OFDM(0xd0), 0xfffd); } /* writes TX Power Control mode */ switch (plp->plp_txpctlmode) { case BWN_PHYLP_TXPCTL_OFF: ctl = BWN_PHY_TX_PWR_CTL_CMD_MODE_OFF; break; case BWN_PHYLP_TXPCTL_ON_HW: ctl = BWN_PHY_TX_PWR_CTL_CMD_MODE_HW; break; case BWN_PHYLP_TXPCTL_ON_SW: ctl = BWN_PHY_TX_PWR_CTL_CMD_MODE_SW; break; default: + ctl = 0; KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } BWN_PHY_SETMASK(mac, BWN_PHY_TX_PWR_CTL_CMD, (uint16_t)~BWN_PHY_TX_PWR_CTL_CMD_MODE, ctl); } static void bwn_phy_lp_bugfix(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; const unsigned int size = 256; struct bwn_txgain tg; uint32_t rxcomp, txgain, coeff, rfpwr, *tabs; uint16_t tssinpt, tssiidx, value[2]; uint8_t mode; int8_t txpwridx; tabs = (uint32_t *)malloc(sizeof(uint32_t) * size, M_DEVBUF, M_NOWAIT | M_ZERO); if (tabs == NULL) { device_printf(sc->sc_dev, "failed to allocate buffer.\n"); return; } bwn_phy_lp_get_txpctlmode(mac); mode = plp->plp_txpctlmode; txpwridx = plp->plp_txpwridx; tssinpt = plp->plp_tssinpt; tssiidx = plp->plp_tssiidx; bwn_tab_read_multi(mac, (mac->mac_phy.rev < 2) ? BWN_TAB_4(10, 0x140) : BWN_TAB_4(7, 0x140), size, tabs); bwn_phy_lp_tblinit(mac); bwn_phy_lp_bbinit(mac); bwn_phy_lp_txpctl_init(mac); bwn_phy_lp_rf_onoff(mac, 1); bwn_phy_lp_set_txpctlmode(mac, BWN_PHYLP_TXPCTL_OFF); bwn_tab_write_multi(mac, (mac->mac_phy.rev < 2) ? BWN_TAB_4(10, 0x140) : BWN_TAB_4(7, 0x140), size, tabs); BWN_WRITE_2(mac, BWN_CHANNEL, plp->plp_chan); plp->plp_tssinpt = tssinpt; plp->plp_tssiidx = tssiidx; bwn_phy_lp_set_anafilter(mac, plp->plp_chan); if (txpwridx != -1) { /* set TX power by index */ plp->plp_txpwridx = txpwridx; bwn_phy_lp_get_txpctlmode(mac); if (plp->plp_txpctlmode != BWN_PHYLP_TXPCTL_OFF) bwn_phy_lp_set_txpctlmode(mac, BWN_PHYLP_TXPCTL_ON_SW); if (mac->mac_phy.rev >= 2) { rxcomp = bwn_tab_read(mac, BWN_TAB_4(7, txpwridx + 320)); txgain = bwn_tab_read(mac, BWN_TAB_4(7, txpwridx + 192)); tg.tg_pad = (txgain >> 16) & 0xff; tg.tg_gm = txgain & 0xff; tg.tg_pga = (txgain >> 8) & 0xff; tg.tg_dac = (rxcomp >> 28) & 0xff; bwn_phy_lp_set_txgain(mac, &tg); } else { rxcomp = bwn_tab_read(mac, BWN_TAB_4(10, txpwridx + 320)); txgain = bwn_tab_read(mac, BWN_TAB_4(10, txpwridx + 192)); BWN_PHY_SETMASK(mac, BWN_PHY_TX_GAIN_CTL_OVERRIDE_VAL, 0xf800, (txgain >> 4) & 0x7fff); bwn_phy_lp_set_txgain_dac(mac, txgain & 0x7); bwn_phy_lp_set_txgain_pa(mac, (txgain >> 24) & 0x7f); } bwn_phy_lp_set_bbmult(mac, (rxcomp >> 20) & 0xff); /* set TX IQCC */ value[0] = (rxcomp >> 10) & 0x3ff; value[1] = rxcomp & 0x3ff; bwn_tab_write_multi(mac, BWN_TAB_2(0, 80), 2, value); coeff = bwn_tab_read(mac, (mac->mac_phy.rev >= 2) ? BWN_TAB_4(7, txpwridx + 448) : BWN_TAB_4(10, txpwridx + 448)); bwn_tab_write(mac, BWN_TAB_2(0, 85), coeff & 0xffff); if (mac->mac_phy.rev >= 2) { rfpwr = bwn_tab_read(mac, BWN_TAB_4(7, txpwridx + 576)); BWN_PHY_SETMASK(mac, BWN_PHY_RF_PWR_OVERRIDE, 0xff00, rfpwr & 0xffff); } bwn_phy_lp_set_txgain_override(mac); } if (plp->plp_rccap) bwn_phy_lp_set_rccap(mac); bwn_phy_lp_set_antenna(mac, plp->plp_antenna); bwn_phy_lp_set_txpctlmode(mac, mode); free(tabs, M_DEVBUF); } static void bwn_phy_lp_digflt_restore(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; int i; static const uint16_t addr[] = { BWN_PHY_OFDM(0xc1), BWN_PHY_OFDM(0xc2), BWN_PHY_OFDM(0xc3), BWN_PHY_OFDM(0xc4), BWN_PHY_OFDM(0xc5), BWN_PHY_OFDM(0xc6), BWN_PHY_OFDM(0xc7), BWN_PHY_OFDM(0xc8), BWN_PHY_OFDM(0xcf), }; for (i = 0; i < N(addr); i++) BWN_PHY_WRITE(mac, addr[i], plp->plp_digfilt[i]); } static void bwn_phy_lp_tblinit(struct bwn_mac *mac) { uint32_t freq = ieee80211_ieee2mhz(bwn_phy_lp_get_default_chan(mac), 0); if (mac->mac_phy.rev < 2) { bwn_phy_lp_tblinit_r01(mac); bwn_phy_lp_tblinit_txgain(mac); bwn_phy_lp_set_gaintbl(mac, freq); return; } bwn_phy_lp_tblinit_r2(mac); bwn_phy_lp_tblinit_txgain(mac); } struct bwn_wpair { uint16_t reg; uint16_t value; }; struct bwn_smpair { uint16_t offset; uint16_t mask; uint16_t set; }; static void bwn_phy_lp_bbinit_r2(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static const struct bwn_wpair v1[] = { { BWN_PHY_AFE_DAC_CTL, 0x50 }, { BWN_PHY_AFE_CTL, 0x8800 }, { BWN_PHY_AFE_CTL_OVR, 0 }, { BWN_PHY_AFE_CTL_OVRVAL, 0 }, { BWN_PHY_RF_OVERRIDE_0, 0 }, { BWN_PHY_RF_OVERRIDE_2, 0 }, { BWN_PHY_OFDM(0xf9), 0 }, { BWN_PHY_TR_LOOKUP_1, 0 } }; static const struct bwn_smpair v2[] = { { BWN_PHY_OFDMSYNCTHRESH0, 0xff00, 0xb4 }, { BWN_PHY_DCOFFSETTRANSIENT, 0xf8ff, 0x200 }, { BWN_PHY_DCOFFSETTRANSIENT, 0xff00, 0x7f }, { BWN_PHY_GAINDIRECTMISMATCH, 0xff0f, 0x40 }, { BWN_PHY_PREAMBLECONFIRMTO, 0xff00, 0x2 } }; static const struct bwn_smpair v3[] = { { BWN_PHY_OFDM(0xfe), 0xffe0, 0x1f }, { BWN_PHY_OFDM(0xff), 0xffe0, 0xc }, { BWN_PHY_OFDM(0x100), 0xff00, 0x19 }, { BWN_PHY_OFDM(0xff), 0x03ff, 0x3c00 }, { BWN_PHY_OFDM(0xfe), 0xfc1f, 0x3e0 }, { BWN_PHY_OFDM(0xff), 0xffe0, 0xc }, { BWN_PHY_OFDM(0x100), 0x00ff, 0x1900 }, { BWN_PHY_CLIPCTRTHRESH, 0x83ff, 0x5800 }, { BWN_PHY_CLIPCTRTHRESH, 0xffe0, 0x12 }, { BWN_PHY_GAINMISMATCH, 0x0fff, 0x9000 }, }; int i; for (i = 0; i < N(v1); i++) BWN_PHY_WRITE(mac, v1[i].reg, v1[i].value); BWN_PHY_SET(mac, BWN_PHY_ADC_COMPENSATION_CTL, 0x10); for (i = 0; i < N(v2); i++) BWN_PHY_SETMASK(mac, v2[i].offset, v2[i].mask, v2[i].set); BWN_PHY_MASK(mac, BWN_PHY_CRSGAIN_CTL, ~0x4000); BWN_PHY_MASK(mac, BWN_PHY_CRSGAIN_CTL, ~0x2000); BWN_PHY_SET(mac, BWN_PHY_OFDM(0x10a), 0x1); if (siba->siba_board_rev >= 0x18) { bwn_tab_write(mac, BWN_TAB_4(17, 65), 0xec); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x10a), 0xff01, 0x14); } else { BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0x10a), 0xff01, 0x10); } BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xdf), 0xff00, 0xf4); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xdf), 0x00ff, 0xf100); BWN_PHY_WRITE(mac, BWN_PHY_CLIPTHRESH, 0x48); BWN_PHY_SETMASK(mac, BWN_PHY_HIGAINDB, 0xff00, 0x46); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xe4), 0xff00, 0x10); BWN_PHY_SETMASK(mac, BWN_PHY_PWR_THRESH1, 0xfff0, 0x9); BWN_PHY_MASK(mac, BWN_PHY_GAINDIRECTMISMATCH, ~0xf); BWN_PHY_SETMASK(mac, BWN_PHY_VERYLOWGAINDB, 0x00ff, 0x5500); BWN_PHY_SETMASK(mac, BWN_PHY_CLIPCTRTHRESH, 0xfc1f, 0xa0); BWN_PHY_SETMASK(mac, BWN_PHY_GAINDIRECTMISMATCH, 0xe0ff, 0x300); BWN_PHY_SETMASK(mac, BWN_PHY_HIGAINDB, 0x00ff, 0x2a00); if ((siba->siba_chipid == 0x4325) && (siba->siba_chiprev == 0)) { BWN_PHY_SETMASK(mac, BWN_PHY_LOWGAINDB, 0x00ff, 0x2100); BWN_PHY_SETMASK(mac, BWN_PHY_VERYLOWGAINDB, 0xff00, 0xa); } else { BWN_PHY_SETMASK(mac, BWN_PHY_LOWGAINDB, 0x00ff, 0x1e00); BWN_PHY_SETMASK(mac, BWN_PHY_VERYLOWGAINDB, 0xff00, 0xd); } for (i = 0; i < N(v3); i++) BWN_PHY_SETMASK(mac, v3[i].offset, v3[i].mask, v3[i].set); if ((siba->siba_chipid == 0x4325) && (siba->siba_chiprev == 0)) { bwn_tab_write(mac, BWN_TAB_2(0x08, 0x14), 0); bwn_tab_write(mac, BWN_TAB_2(0x08, 0x12), 0x40); } if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { BWN_PHY_SET(mac, BWN_PHY_CRSGAIN_CTL, 0x40); BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xf0ff, 0xb00); BWN_PHY_SETMASK(mac, BWN_PHY_SYNCPEAKCNT, 0xfff8, 0x6); BWN_PHY_SETMASK(mac, BWN_PHY_MINPWR_LEVEL, 0x00ff, 0x9d00); BWN_PHY_SETMASK(mac, BWN_PHY_MINPWR_LEVEL, 0xff00, 0xa1); BWN_PHY_MASK(mac, BWN_PHY_IDLEAFTERPKTRXTO, 0x00ff); } else BWN_PHY_MASK(mac, BWN_PHY_CRSGAIN_CTL, ~0x40); BWN_PHY_SETMASK(mac, BWN_PHY_CRS_ED_THRESH, 0xff00, 0xb3); BWN_PHY_SETMASK(mac, BWN_PHY_CRS_ED_THRESH, 0x00ff, 0xad00); BWN_PHY_SETMASK(mac, BWN_PHY_INPUT_PWRDB, 0xff00, plp->plp_rxpwroffset); BWN_PHY_SET(mac, BWN_PHY_RESET_CTL, 0x44); BWN_PHY_WRITE(mac, BWN_PHY_RESET_CTL, 0x80); BWN_PHY_WRITE(mac, BWN_PHY_AFE_RSSI_CTL_0, 0xa954); BWN_PHY_WRITE(mac, BWN_PHY_AFE_RSSI_CTL_1, 0x2000 | ((uint16_t)plp->plp_rssigs << 10) | ((uint16_t)plp->plp_rssivc << 4) | plp->plp_rssivf); if ((siba->siba_chipid == 0x4325) && (siba->siba_chiprev == 0)) { BWN_PHY_SET(mac, BWN_PHY_AFE_ADC_CTL_0, 0x1c); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_CTL, 0x00ff, 0x8800); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_ADC_CTL_1, 0xfc3c, 0x0400); } bwn_phy_lp_digflt_save(mac); } static void bwn_phy_lp_bbinit_r01(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static const struct bwn_smpair v1[] = { { BWN_PHY_CLIPCTRTHRESH, 0xffe0, 0x0005 }, { BWN_PHY_CLIPCTRTHRESH, 0xfc1f, 0x0180 }, { BWN_PHY_CLIPCTRTHRESH, 0x83ff, 0x3c00 }, { BWN_PHY_GAINDIRECTMISMATCH, 0xfff0, 0x0005 }, { BWN_PHY_GAIN_MISMATCH_LIMIT, 0xffc0, 0x001a }, { BWN_PHY_CRS_ED_THRESH, 0xff00, 0x00b3 }, { BWN_PHY_CRS_ED_THRESH, 0x00ff, 0xad00 } }; static const struct bwn_smpair v2[] = { { BWN_PHY_TR_LOOKUP_1, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_1, 0x3f00, 0x0900 }, { BWN_PHY_TR_LOOKUP_2, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_2, 0xc0ff, 0x0b00 }, { BWN_PHY_TR_LOOKUP_3, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_3, 0xc0ff, 0x0400 }, { BWN_PHY_TR_LOOKUP_4, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_4, 0xc0ff, 0x0b00 }, { BWN_PHY_TR_LOOKUP_5, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_5, 0xc0ff, 0x0900 }, { BWN_PHY_TR_LOOKUP_6, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_6, 0xc0ff, 0x0b00 }, { BWN_PHY_TR_LOOKUP_7, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_7, 0xc0ff, 0x0900 }, { BWN_PHY_TR_LOOKUP_8, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_8, 0xc0ff, 0x0b00 } }; static const struct bwn_smpair v3[] = { { BWN_PHY_TR_LOOKUP_1, 0xffc0, 0x0001 }, { BWN_PHY_TR_LOOKUP_1, 0xc0ff, 0x0400 }, { BWN_PHY_TR_LOOKUP_2, 0xffc0, 0x0001 }, { BWN_PHY_TR_LOOKUP_2, 0xc0ff, 0x0500 }, { BWN_PHY_TR_LOOKUP_3, 0xffc0, 0x0002 }, { BWN_PHY_TR_LOOKUP_3, 0xc0ff, 0x0800 }, { BWN_PHY_TR_LOOKUP_4, 0xffc0, 0x0002 }, { BWN_PHY_TR_LOOKUP_4, 0xc0ff, 0x0a00 } }; static const struct bwn_smpair v4[] = { { BWN_PHY_TR_LOOKUP_1, 0xffc0, 0x0004 }, { BWN_PHY_TR_LOOKUP_1, 0xc0ff, 0x0800 }, { BWN_PHY_TR_LOOKUP_2, 0xffc0, 0x0004 }, { BWN_PHY_TR_LOOKUP_2, 0xc0ff, 0x0c00 }, { BWN_PHY_TR_LOOKUP_3, 0xffc0, 0x0002 }, { BWN_PHY_TR_LOOKUP_3, 0xc0ff, 0x0100 }, { BWN_PHY_TR_LOOKUP_4, 0xffc0, 0x0002 }, { BWN_PHY_TR_LOOKUP_4, 0xc0ff, 0x0300 } }; static const struct bwn_smpair v5[] = { { BWN_PHY_TR_LOOKUP_1, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_1, 0xc0ff, 0x0900 }, { BWN_PHY_TR_LOOKUP_2, 0xffc0, 0x000a }, { BWN_PHY_TR_LOOKUP_2, 0xc0ff, 0x0b00 }, { BWN_PHY_TR_LOOKUP_3, 0xffc0, 0x0006 }, { BWN_PHY_TR_LOOKUP_3, 0xc0ff, 0x0500 }, { BWN_PHY_TR_LOOKUP_4, 0xffc0, 0x0006 }, { BWN_PHY_TR_LOOKUP_4, 0xc0ff, 0x0700 } }; int i; uint16_t tmp, tmp2; BWN_PHY_MASK(mac, BWN_PHY_AFE_DAC_CTL, 0xf7ff); BWN_PHY_WRITE(mac, BWN_PHY_AFE_CTL, 0); BWN_PHY_WRITE(mac, BWN_PHY_AFE_CTL_OVR, 0); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_0, 0); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_2, 0); BWN_PHY_SET(mac, BWN_PHY_AFE_DAC_CTL, 0x0004); BWN_PHY_SETMASK(mac, BWN_PHY_OFDMSYNCTHRESH0, 0xff00, 0x0078); BWN_PHY_SETMASK(mac, BWN_PHY_CLIPCTRTHRESH, 0x83ff, 0x5800); BWN_PHY_WRITE(mac, BWN_PHY_ADC_COMPENSATION_CTL, 0x0016); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_ADC_CTL_0, 0xfff8, 0x0004); BWN_PHY_SETMASK(mac, BWN_PHY_VERYLOWGAINDB, 0x00ff, 0x5400); BWN_PHY_SETMASK(mac, BWN_PHY_HIGAINDB, 0x00ff, 0x2400); BWN_PHY_SETMASK(mac, BWN_PHY_LOWGAINDB, 0x00ff, 0x2100); BWN_PHY_SETMASK(mac, BWN_PHY_VERYLOWGAINDB, 0xff00, 0x0006); BWN_PHY_MASK(mac, BWN_PHY_RX_RADIO_CTL, 0xfffe); for (i = 0; i < N(v1); i++) BWN_PHY_SETMASK(mac, v1[i].offset, v1[i].mask, v1[i].set); BWN_PHY_SETMASK(mac, BWN_PHY_INPUT_PWRDB, 0xff00, plp->plp_rxpwroffset); if ((siba->siba_sprom.bf_lo & BWN_BFL_FEM) && ((IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) || (siba->siba_sprom.bf_hi & BWN_BFH_LDO_PAREF))) { siba_cc_pmu_set_ldovolt(&siba->siba_cc, SIBA_LDO_PAREF, 0x28); siba_cc_pmu_set_ldoparef(&siba->siba_cc, 1); if (mac->mac_phy.rev == 0) BWN_PHY_SETMASK(mac, BWN_PHY_LP_RF_SIGNAL_LUT, 0xffcf, 0x0010); bwn_tab_write(mac, BWN_TAB_2(11, 7), 60); } else { siba_cc_pmu_set_ldoparef(&siba->siba_cc, 0); BWN_PHY_SETMASK(mac, BWN_PHY_LP_RF_SIGNAL_LUT, 0xffcf, 0x0020); bwn_tab_write(mac, BWN_TAB_2(11, 7), 100); } tmp = plp->plp_rssivf | plp->plp_rssivc << 4 | 0xa000; BWN_PHY_WRITE(mac, BWN_PHY_AFE_RSSI_CTL_0, tmp); if (siba->siba_sprom.bf_hi & BWN_BFH_RSSIINV) BWN_PHY_SETMASK(mac, BWN_PHY_AFE_RSSI_CTL_1, 0xf000, 0x0aaa); else BWN_PHY_SETMASK(mac, BWN_PHY_AFE_RSSI_CTL_1, 0xf000, 0x02aa); bwn_tab_write(mac, BWN_TAB_2(11, 1), 24); BWN_PHY_SETMASK(mac, BWN_PHY_RX_RADIO_CTL, 0xfff9, (plp->plp_bxarch << 1)); if (mac->mac_phy.rev == 1 && (siba->siba_sprom.bf_hi & BWN_BFH_FEM_BT)) { for (i = 0; i < N(v2); i++) BWN_PHY_SETMASK(mac, v2[i].offset, v2[i].mask, v2[i].set); } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) || (siba->siba_board_type == 0x048a) || ((mac->mac_phy.rev == 0) && (siba->siba_sprom.bf_lo & BWN_BFL_FEM))) { for (i = 0; i < N(v3); i++) BWN_PHY_SETMASK(mac, v3[i].offset, v3[i].mask, v3[i].set); } else if (mac->mac_phy.rev == 1 || (siba->siba_sprom.bf_lo & BWN_BFL_FEM)) { for (i = 0; i < N(v4); i++) BWN_PHY_SETMASK(mac, v4[i].offset, v4[i].mask, v4[i].set); } else { for (i = 0; i < N(v5); i++) BWN_PHY_SETMASK(mac, v5[i].offset, v5[i].mask, v5[i].set); } if (mac->mac_phy.rev == 1 && (siba->siba_sprom.bf_hi & BWN_BFH_LDO_PAREF)) { BWN_PHY_COPY(mac, BWN_PHY_TR_LOOKUP_5, BWN_PHY_TR_LOOKUP_1); BWN_PHY_COPY(mac, BWN_PHY_TR_LOOKUP_6, BWN_PHY_TR_LOOKUP_2); BWN_PHY_COPY(mac, BWN_PHY_TR_LOOKUP_7, BWN_PHY_TR_LOOKUP_3); BWN_PHY_COPY(mac, BWN_PHY_TR_LOOKUP_8, BWN_PHY_TR_LOOKUP_4); } if ((siba->siba_sprom.bf_hi & BWN_BFH_FEM_BT) && (siba->siba_chipid == 0x5354) && (siba->siba_chippkg == SIBA_CHIPPACK_BCM4712S)) { BWN_PHY_SET(mac, BWN_PHY_CRSGAIN_CTL, 0x0006); BWN_PHY_WRITE(mac, BWN_PHY_GPIO_SELECT, 0x0005); BWN_PHY_WRITE(mac, BWN_PHY_GPIO_OUTEN, 0xffff); bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_PR45960W); } if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { BWN_PHY_SET(mac, BWN_PHY_LP_PHY_CTL, 0x8000); BWN_PHY_SET(mac, BWN_PHY_CRSGAIN_CTL, 0x0040); BWN_PHY_SETMASK(mac, BWN_PHY_MINPWR_LEVEL, 0x00ff, 0xa400); BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xf0ff, 0x0b00); BWN_PHY_SETMASK(mac, BWN_PHY_SYNCPEAKCNT, 0xfff8, 0x0007); BWN_PHY_SETMASK(mac, BWN_PHY_DSSS_CONFIRM_CNT, 0xfff8, 0x0003); BWN_PHY_SETMASK(mac, BWN_PHY_DSSS_CONFIRM_CNT, 0xffc7, 0x0020); BWN_PHY_MASK(mac, BWN_PHY_IDLEAFTERPKTRXTO, 0x00ff); } else { BWN_PHY_MASK(mac, BWN_PHY_LP_PHY_CTL, 0x7fff); BWN_PHY_MASK(mac, BWN_PHY_CRSGAIN_CTL, 0xffbf); } if (mac->mac_phy.rev == 1) { tmp = BWN_PHY_READ(mac, BWN_PHY_CLIPCTRTHRESH); tmp2 = (tmp & 0x03e0) >> 5; tmp2 |= tmp2 << 5; BWN_PHY_WRITE(mac, BWN_PHY_4C3, tmp2); tmp = BWN_PHY_READ(mac, BWN_PHY_GAINDIRECTMISMATCH); tmp2 = (tmp & 0x1f00) >> 8; tmp2 |= tmp2 << 5; BWN_PHY_WRITE(mac, BWN_PHY_4C4, tmp2); tmp = BWN_PHY_READ(mac, BWN_PHY_VERYLOWGAINDB); tmp2 = tmp & 0x00ff; tmp2 |= tmp << 8; BWN_PHY_WRITE(mac, BWN_PHY_4C5, tmp2); } } struct bwn_b2062_freq { uint16_t freq; uint8_t value[6]; }; static void bwn_phy_lp_b2062_init(struct bwn_mac *mac) { #define CALC_CTL7(freq, div) \ (((800000000 * (div) + (freq)) / (2 * (freq)) - 8) & 0xff) #define CALC_CTL18(freq, div) \ ((((100 * (freq) + 16000000 * (div)) / (32000000 * (div))) - 1) & 0xff) #define CALC_CTL19(freq, div) \ ((((2 * (freq) + 1000000 * (div)) / (2000000 * (div))) - 1) & 0xff) struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static const struct bwn_b2062_freq freqdata_tab[] = { { 12000, { 6, 6, 6, 6, 10, 6 } }, { 13000, { 4, 4, 4, 4, 11, 7 } }, { 14400, { 3, 3, 3, 3, 12, 7 } }, { 16200, { 3, 3, 3, 3, 13, 8 } }, { 18000, { 2, 2, 2, 2, 14, 8 } }, { 19200, { 1, 1, 1, 1, 14, 9 } } }; static const struct bwn_wpair v1[] = { { BWN_B2062_N_TXCTL3, 0 }, { BWN_B2062_N_TXCTL4, 0 }, { BWN_B2062_N_TXCTL5, 0 }, { BWN_B2062_N_TXCTL6, 0 }, { BWN_B2062_N_PDNCTL0, 0x40 }, { BWN_B2062_N_PDNCTL0, 0 }, { BWN_B2062_N_CALIB_TS, 0x10 }, { BWN_B2062_N_CALIB_TS, 0 } }; const struct bwn_b2062_freq *f = NULL; uint32_t xtalfreq, ref; unsigned int i; bwn_phy_lp_b2062_tblinit(mac); for (i = 0; i < N(v1); i++) BWN_RF_WRITE(mac, v1[i].reg, v1[i].value); if (mac->mac_phy.rev > 0) BWN_RF_WRITE(mac, BWN_B2062_S_BG_CTL1, (BWN_RF_READ(mac, BWN_B2062_N_COM2) >> 1) | 0x80); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) BWN_RF_SET(mac, BWN_B2062_N_TSSI_CTL0, 0x1); else BWN_RF_MASK(mac, BWN_B2062_N_TSSI_CTL0, ~0x1); KASSERT(siba->siba_cc.scc_caps & SIBA_CC_CAPS_PMU, ("%s:%d: fail", __func__, __LINE__)); xtalfreq = siba->siba_cc.scc_pmu.freq * 1000; KASSERT(xtalfreq != 0, ("%s:%d: fail", __func__, __LINE__)); if (xtalfreq <= 30000000) { plp->plp_div = 1; BWN_RF_MASK(mac, BWN_B2062_S_RFPLLCTL1, 0xfffb); } else { plp->plp_div = 2; BWN_RF_SET(mac, BWN_B2062_S_RFPLLCTL1, 0x4); } BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL7, CALC_CTL7(xtalfreq, plp->plp_div)); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL18, CALC_CTL18(xtalfreq, plp->plp_div)); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL19, CALC_CTL19(xtalfreq, plp->plp_div)); ref = (1000 * plp->plp_div + 2 * xtalfreq) / (2000 * plp->plp_div); ref &= 0xffff; for (i = 0; i < N(freqdata_tab); i++) { if (ref < freqdata_tab[i].freq) { f = &freqdata_tab[i]; break; } } if (f == NULL) f = &freqdata_tab[N(freqdata_tab) - 1]; BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL8, ((uint16_t)(f->value[1]) << 4) | f->value[0]); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL9, ((uint16_t)(f->value[3]) << 4) | f->value[2]); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL10, f->value[4]); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL11, f->value[5]); #undef CALC_CTL7 #undef CALC_CTL18 #undef CALC_CTL19 } static void bwn_phy_lp_b2063_init(struct bwn_mac *mac) { bwn_phy_lp_b2063_tblinit(mac); BWN_RF_WRITE(mac, BWN_B2063_LOGEN_SP5, 0); BWN_RF_SET(mac, BWN_B2063_COM8, 0x38); BWN_RF_WRITE(mac, BWN_B2063_REG_SP1, 0x56); BWN_RF_MASK(mac, BWN_B2063_RX_BB_CTL2, ~0x2); BWN_RF_WRITE(mac, BWN_B2063_PA_SP7, 0); BWN_RF_WRITE(mac, BWN_B2063_TX_RF_SP6, 0x20); BWN_RF_WRITE(mac, BWN_B2063_TX_RF_SP9, 0x40); if (mac->mac_phy.rev == 2) { BWN_RF_WRITE(mac, BWN_B2063_PA_SP3, 0xa0); BWN_RF_WRITE(mac, BWN_B2063_PA_SP4, 0xa0); BWN_RF_WRITE(mac, BWN_B2063_PA_SP2, 0x18); } else { BWN_RF_WRITE(mac, BWN_B2063_PA_SP3, 0x20); BWN_RF_WRITE(mac, BWN_B2063_PA_SP2, 0x20); } } static void bwn_phy_lp_rxcal_r2(struct bwn_mac *mac) { struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; static const struct bwn_wpair v1[] = { { BWN_B2063_RX_BB_SP8, 0x0 }, { BWN_B2063_RC_CALIB_CTL1, 0x7e }, { BWN_B2063_RC_CALIB_CTL1, 0x7c }, { BWN_B2063_RC_CALIB_CTL2, 0x15 }, { BWN_B2063_RC_CALIB_CTL3, 0x70 }, { BWN_B2063_RC_CALIB_CTL4, 0x52 }, { BWN_B2063_RC_CALIB_CTL5, 0x1 }, { BWN_B2063_RC_CALIB_CTL1, 0x7d } }; static const struct bwn_wpair v2[] = { { BWN_B2063_TX_BB_SP3, 0x0 }, { BWN_B2063_RC_CALIB_CTL1, 0x7e }, { BWN_B2063_RC_CALIB_CTL1, 0x7c }, { BWN_B2063_RC_CALIB_CTL2, 0x55 }, { BWN_B2063_RC_CALIB_CTL3, 0x76 } }; uint32_t freqxtal = siba->siba_cc.scc_pmu.freq * 1000; int i; uint8_t tmp; tmp = BWN_RF_READ(mac, BWN_B2063_RX_BB_SP8) & 0xff; for (i = 0; i < 2; i++) BWN_RF_WRITE(mac, v1[i].reg, v1[i].value); BWN_RF_MASK(mac, BWN_B2063_PLL_SP1, 0xf7); for (i = 2; i < N(v1); i++) BWN_RF_WRITE(mac, v1[i].reg, v1[i].value); for (i = 0; i < 10000; i++) { if (BWN_RF_READ(mac, BWN_B2063_RC_CALIB_CTL6) & 0x2) break; DELAY(1000); } if (!(BWN_RF_READ(mac, BWN_B2063_RC_CALIB_CTL6) & 0x2)) BWN_RF_WRITE(mac, BWN_B2063_RX_BB_SP8, tmp); tmp = BWN_RF_READ(mac, BWN_B2063_TX_BB_SP3) & 0xff; for (i = 0; i < N(v2); i++) BWN_RF_WRITE(mac, v2[i].reg, v2[i].value); if (freqxtal == 24000000) { BWN_RF_WRITE(mac, BWN_B2063_RC_CALIB_CTL4, 0xfc); BWN_RF_WRITE(mac, BWN_B2063_RC_CALIB_CTL5, 0x0); } else { BWN_RF_WRITE(mac, BWN_B2063_RC_CALIB_CTL4, 0x13); BWN_RF_WRITE(mac, BWN_B2063_RC_CALIB_CTL5, 0x1); } BWN_RF_WRITE(mac, BWN_B2063_PA_SP7, 0x7d); for (i = 0; i < 10000; i++) { if (BWN_RF_READ(mac, BWN_B2063_RC_CALIB_CTL6) & 0x2) break; DELAY(1000); } if (!(BWN_RF_READ(mac, BWN_B2063_RC_CALIB_CTL6) & 0x2)) BWN_RF_WRITE(mac, BWN_B2063_TX_BB_SP3, tmp); BWN_RF_WRITE(mac, BWN_B2063_RC_CALIB_CTL1, 0x7e); } static void bwn_phy_lp_rccal_r12(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; struct bwn_phy_lp_iq_est ie; struct bwn_txgain tx_gains; static const uint32_t pwrtbl[21] = { 0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64, 0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35, 0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088, 0x0004c, 0x0002c, 0x0001a, }; uint32_t npwr, ipwr, sqpwr, tmp; int loopback, i, j, sum, error; uint16_t save[7]; uint8_t txo, bbmult, txpctlmode; error = bwn_phy_lp_switch_channel(mac, 7); if (error) device_printf(sc->sc_dev, "failed to change channel to 7 (%d)\n", error); txo = (BWN_PHY_READ(mac, BWN_PHY_AFE_CTL_OVR) & 0x40) ? 1 : 0; bbmult = bwn_phy_lp_get_bbmult(mac); if (txo) tx_gains = bwn_phy_lp_get_txgain(mac); save[0] = BWN_PHY_READ(mac, BWN_PHY_RF_OVERRIDE_0); save[1] = BWN_PHY_READ(mac, BWN_PHY_RF_OVERRIDE_VAL_0); save[2] = BWN_PHY_READ(mac, BWN_PHY_AFE_CTL_OVR); save[3] = BWN_PHY_READ(mac, BWN_PHY_AFE_CTL_OVRVAL); save[4] = BWN_PHY_READ(mac, BWN_PHY_RF_OVERRIDE_2); save[5] = BWN_PHY_READ(mac, BWN_PHY_RF_OVERRIDE_2_VAL); save[6] = BWN_PHY_READ(mac, BWN_PHY_LP_PHY_CTL); bwn_phy_lp_get_txpctlmode(mac); txpctlmode = plp->plp_txpctlmode; bwn_phy_lp_set_txpctlmode(mac, BWN_PHYLP_TXPCTL_OFF); /* disable CRS */ bwn_phy_lp_set_deaf(mac, 1); bwn_phy_lp_set_trsw_over(mac, 0, 1); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xfffb); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x4); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xfff7); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x8); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0x10); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x10); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xffdf); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x20); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xffbf); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x40); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0x7); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0x38); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xff3f); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0x100); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfdff); BWN_PHY_WRITE(mac, BWN_PHY_PS_CTL_OVERRIDE_VAL0, 0); BWN_PHY_WRITE(mac, BWN_PHY_PS_CTL_OVERRIDE_VAL1, 1); BWN_PHY_WRITE(mac, BWN_PHY_PS_CTL_OVERRIDE_VAL2, 0x20); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfbff); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xf7ff); BWN_PHY_WRITE(mac, BWN_PHY_TX_GAIN_CTL_OVERRIDE_VAL, 0); BWN_PHY_WRITE(mac, BWN_PHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45af); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_2, 0x3ff); loopback = bwn_phy_lp_loopback(mac); if (loopback == -1) goto done; bwn_phy_lp_set_rxgain_idx(mac, loopback); BWN_PHY_SETMASK(mac, BWN_PHY_LP_PHY_CTL, 0xffbf, 0x40); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfff8, 0x1); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xffc7, 0x8); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xff3f, 0xc0); tmp = 0; memset(&ie, 0, sizeof(ie)); for (i = 128; i <= 159; i++) { BWN_RF_WRITE(mac, BWN_B2062_N_RXBB_CALIB2, i); sum = 0; for (j = 5; j <= 25; j++) { bwn_phy_lp_ddfs_turnon(mac, 1, 1, j, j, 0); if (!(bwn_phy_lp_rx_iq_est(mac, 1000, 32, &ie))) goto done; sqpwr = ie.ie_ipwr + ie.ie_qpwr; ipwr = ((pwrtbl[j - 5] >> 3) + 1) >> 1; npwr = bwn_phy_lp_roundup(sqpwr, (j == 5) ? sqpwr : 0, 12); sum += ((ipwr - npwr) * (ipwr - npwr)); if ((i == 128) || (sum < tmp)) { plp->plp_rccap = i; tmp = sum; } } } bwn_phy_lp_ddfs_turnoff(mac); done: /* restore CRS */ bwn_phy_lp_clear_deaf(mac, 1); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_0, 0xff80); BWN_PHY_MASK(mac, BWN_PHY_RF_OVERRIDE_2, 0xfc00); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_VAL_0, save[1]); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_0, save[0]); BWN_PHY_WRITE(mac, BWN_PHY_AFE_CTL_OVRVAL, save[3]); BWN_PHY_WRITE(mac, BWN_PHY_AFE_CTL_OVR, save[2]); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_2_VAL, save[5]); BWN_PHY_WRITE(mac, BWN_PHY_RF_OVERRIDE_2, save[4]); BWN_PHY_WRITE(mac, BWN_PHY_LP_PHY_CTL, save[6]); bwn_phy_lp_set_bbmult(mac, bbmult); if (txo) bwn_phy_lp_set_txgain(mac, &tx_gains); bwn_phy_lp_set_txpctlmode(mac, txpctlmode); if (plp->plp_rccap) bwn_phy_lp_set_rccap(mac); } static void bwn_phy_lp_set_rccap(struct bwn_mac *mac) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; uint8_t rc_cap = (plp->plp_rccap & 0x1f) >> 1; if (mac->mac_phy.rev == 1) rc_cap = MIN(rc_cap + 5, 15); BWN_RF_WRITE(mac, BWN_B2062_N_RXBB_CALIB2, MAX(plp->plp_rccap - 4, 0x80)); BWN_RF_WRITE(mac, BWN_B2062_N_TXCTL_A, rc_cap | 0x80); BWN_RF_WRITE(mac, BWN_B2062_S_RXG_CNT16, ((plp->plp_rccap & 0x1f) >> 2) | 0x80); } static uint32_t bwn_phy_lp_roundup(uint32_t value, uint32_t div, uint8_t pre) { uint32_t i, q, r; if (div == 0) return (0); for (i = 0, q = value / div, r = value % div; i < pre; i++) { q <<= 1; if (r << 1 >= div) { q++; r = (r << 1) - div; } } if (r << 1 >= div) q++; return (q); } static void bwn_phy_lp_b2062_reset_pllbias(struct bwn_mac *mac) { struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL2, 0xff); DELAY(20); if (siba->siba_chipid == 0x5354) { BWN_RF_WRITE(mac, BWN_B2062_N_COM1, 4); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL2, 4); } else { BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL2, 0); } DELAY(5); } static void bwn_phy_lp_b2062_vco_calib(struct bwn_mac *mac) { BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL21, 0x42); BWN_RF_WRITE(mac, BWN_B2062_S_RFPLLCTL21, 0x62); DELAY(200); } static void bwn_phy_lp_b2062_tblinit(struct bwn_mac *mac) { #define FLAG_A 0x01 #define FLAG_G 0x02 struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static const struct bwn_b206x_rfinit_entry bwn_b2062_init_tab[] = { { BWN_B2062_N_COM4, 0x1, 0x0, FLAG_A | FLAG_G, }, { BWN_B2062_N_PDNCTL1, 0x0, 0xca, FLAG_G, }, { BWN_B2062_N_PDNCTL3, 0x0, 0x0, FLAG_A | FLAG_G, }, { BWN_B2062_N_PDNCTL4, 0x15, 0x2a, FLAG_A | FLAG_G, }, { BWN_B2062_N_LGENC, 0xDB, 0xff, FLAG_A, }, { BWN_B2062_N_LGENATUNE0, 0xdd, 0x0, FLAG_A | FLAG_G, }, { BWN_B2062_N_LGENATUNE2, 0xdd, 0x0, FLAG_A | FLAG_G, }, { BWN_B2062_N_LGENATUNE3, 0x77, 0xB5, FLAG_A | FLAG_G, }, { BWN_B2062_N_LGENACTL3, 0x0, 0xff, FLAG_A | FLAG_G, }, { BWN_B2062_N_LGENACTL7, 0x33, 0x33, FLAG_A | FLAG_G, }, { BWN_B2062_N_RXA_CTL1, 0x0, 0x0, FLAG_G, }, { BWN_B2062_N_RXBB_CTL0, 0x82, 0x80, FLAG_A | FLAG_G, }, { BWN_B2062_N_RXBB_GAIN1, 0x4, 0x4, FLAG_A | FLAG_G, }, { BWN_B2062_N_RXBB_GAIN2, 0x0, 0x0, FLAG_A | FLAG_G, }, { BWN_B2062_N_TXCTL4, 0x3, 0x3, FLAG_A | FLAG_G, }, { BWN_B2062_N_TXCTL5, 0x2, 0x2, FLAG_A | FLAG_G, }, { BWN_B2062_N_TX_TUNE, 0x88, 0x1b, FLAG_A | FLAG_G, }, { BWN_B2062_S_COM4, 0x1, 0x0, FLAG_A | FLAG_G, }, { BWN_B2062_S_PDS_CTL0, 0xff, 0xff, FLAG_A | FLAG_G, }, { BWN_B2062_S_LGENG_CTL0, 0xf8, 0xd8, FLAG_A | FLAG_G, }, { BWN_B2062_S_LGENG_CTL1, 0x3c, 0x24, FLAG_A | FLAG_G, }, { BWN_B2062_S_LGENG_CTL8, 0x88, 0x80, FLAG_A | FLAG_G, }, { BWN_B2062_S_LGENG_CTL10, 0x88, 0x80, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL0, 0x98, 0x98, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL1, 0x10, 0x10, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL5, 0x43, 0x43, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL6, 0x47, 0x47, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL7, 0xc, 0xc, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL8, 0x11, 0x11, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL9, 0x11, 0x11, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL10, 0xe, 0xe, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL11, 0x8, 0x8, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL12, 0x33, 0x33, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL13, 0xa, 0xa, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL14, 0x6, 0x6, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL18, 0x3e, 0x3e, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL19, 0x13, 0x13, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL21, 0x62, 0x62, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL22, 0x7, 0x7, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL23, 0x16, 0x16, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL24, 0x5c, 0x5c, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL25, 0x95, 0x95, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL30, 0xa0, 0xa0, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL31, 0x4, 0x4, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL33, 0xcc, 0xcc, FLAG_A | FLAG_G, }, { BWN_B2062_S_RFPLLCTL34, 0x7, 0x7, FLAG_A | FLAG_G, }, { BWN_B2062_S_RXG_CNT8, 0xf, 0xf, FLAG_A, }, }; const struct bwn_b206x_rfinit_entry *br; unsigned int i; for (i = 0; i < N(bwn_b2062_init_tab); i++) { br = &bwn_b2062_init_tab[i]; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { if (br->br_flags & FLAG_G) BWN_RF_WRITE(mac, br->br_offset, br->br_valueg); } else { if (br->br_flags & FLAG_A) BWN_RF_WRITE(mac, br->br_offset, br->br_valuea); } } #undef FLAG_A #undef FLAG_B } static void bwn_phy_lp_b2063_tblinit(struct bwn_mac *mac) { #define FLAG_A 0x01 #define FLAG_G 0x02 struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static const struct bwn_b206x_rfinit_entry bwn_b2063_init_tab[] = { { BWN_B2063_COM1, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM10, 0x1, 0x0, FLAG_A, }, { BWN_B2063_COM16, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM17, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM18, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM19, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM20, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM21, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM22, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM23, 0x0, 0x0, FLAG_G, }, { BWN_B2063_COM24, 0x0, 0x0, FLAG_G, }, { BWN_B2063_LOGEN_SP1, 0xe8, 0xd4, FLAG_A | FLAG_G, }, { BWN_B2063_LOGEN_SP2, 0xa7, 0x53, FLAG_A | FLAG_G, }, { BWN_B2063_LOGEN_SP4, 0xf0, 0xf, FLAG_A | FLAG_G, }, { BWN_B2063_G_RX_SP1, 0x1f, 0x5e, FLAG_G, }, { BWN_B2063_G_RX_SP2, 0x7f, 0x7e, FLAG_G, }, { BWN_B2063_G_RX_SP3, 0x30, 0xf0, FLAG_G, }, { BWN_B2063_G_RX_SP7, 0x7f, 0x7f, FLAG_A | FLAG_G, }, { BWN_B2063_G_RX_SP10, 0xc, 0xc, FLAG_A | FLAG_G, }, { BWN_B2063_A_RX_SP1, 0x3c, 0x3f, FLAG_A, }, { BWN_B2063_A_RX_SP2, 0xfc, 0xfe, FLAG_A, }, { BWN_B2063_A_RX_SP7, 0x8, 0x8, FLAG_A | FLAG_G, }, { BWN_B2063_RX_BB_SP4, 0x60, 0x60, FLAG_A | FLAG_G, }, { BWN_B2063_RX_BB_SP8, 0x30, 0x30, FLAG_A | FLAG_G, }, { BWN_B2063_TX_RF_SP3, 0xc, 0xb, FLAG_A | FLAG_G, }, { BWN_B2063_TX_RF_SP4, 0x10, 0xf, FLAG_A | FLAG_G, }, { BWN_B2063_PA_SP1, 0x3d, 0xfd, FLAG_A | FLAG_G, }, { BWN_B2063_TX_BB_SP1, 0x2, 0x2, FLAG_A | FLAG_G, }, { BWN_B2063_BANDGAP_CTL1, 0x56, 0x56, FLAG_A | FLAG_G, }, { BWN_B2063_JTAG_VCO2, 0xF7, 0xF7, FLAG_A | FLAG_G, }, { BWN_B2063_G_RX_MIX3, 0x71, 0x71, FLAG_A | FLAG_G, }, { BWN_B2063_G_RX_MIX4, 0x71, 0x71, FLAG_A | FLAG_G, }, { BWN_B2063_A_RX_1ST2, 0xf0, 0x30, FLAG_A, }, { BWN_B2063_A_RX_PS6, 0x77, 0x77, FLAG_A | FLAG_G, }, { BWN_B2063_A_RX_MIX4, 0x3, 0x3, FLAG_A | FLAG_G, }, { BWN_B2063_A_RX_MIX5, 0xf, 0xf, FLAG_A | FLAG_G, }, { BWN_B2063_A_RX_MIX6, 0xf, 0xf, FLAG_A | FLAG_G, }, { BWN_B2063_RX_TIA_CTL1, 0x77, 0x77, FLAG_A | FLAG_G, }, { BWN_B2063_RX_TIA_CTL3, 0x77, 0x77, FLAG_A | FLAG_G, }, { BWN_B2063_RX_BB_CTL2, 0x4, 0x4, FLAG_A | FLAG_G, }, { BWN_B2063_PA_CTL1, 0x0, 0x4, FLAG_A, }, { BWN_B2063_VREG_CTL1, 0x3, 0x3, FLAG_A | FLAG_G, }, }; const struct bwn_b206x_rfinit_entry *br; unsigned int i; for (i = 0; i < N(bwn_b2063_init_tab); i++) { br = &bwn_b2063_init_tab[i]; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { if (br->br_flags & FLAG_G) BWN_RF_WRITE(mac, br->br_offset, br->br_valueg); } else { if (br->br_flags & FLAG_A) BWN_RF_WRITE(mac, br->br_offset, br->br_valuea); } } #undef FLAG_A #undef FLAG_B } static void bwn_tab_read_multi(struct bwn_mac *mac, uint32_t typenoffset, int count, void *_data) { unsigned int i; uint32_t offset, type; uint8_t *data = _data; type = BWN_TAB_GETTYPE(typenoffset); offset = BWN_TAB_GETOFFSET(typenoffset); KASSERT(offset <= 0xffff, ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); for (i = 0; i < count; i++) { switch (type) { case BWN_TAB_8BIT: *data = BWN_PHY_READ(mac, BWN_PHY_TABLEDATALO) & 0xff; data++; break; case BWN_TAB_16BIT: *((uint16_t *)data) = BWN_PHY_READ(mac, BWN_PHY_TABLEDATALO); data += 2; break; case BWN_TAB_32BIT: *((uint32_t *)data) = BWN_PHY_READ(mac, BWN_PHY_TABLEDATAHI); *((uint32_t *)data) <<= 16; *((uint32_t *)data) |= BWN_PHY_READ(mac, BWN_PHY_TABLEDATALO); data += 4; break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } } } static void bwn_tab_write_multi(struct bwn_mac *mac, uint32_t typenoffset, int count, const void *_data) { uint32_t offset, type, value; const uint8_t *data = _data; unsigned int i; type = BWN_TAB_GETTYPE(typenoffset); offset = BWN_TAB_GETOFFSET(typenoffset); KASSERT(offset <= 0xffff, ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); for (i = 0; i < count; i++) { switch (type) { case BWN_TAB_8BIT: value = *data; data++; KASSERT(!(value & ~0xff), ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATALO, value); break; case BWN_TAB_16BIT: value = *((const uint16_t *)data); data += 2; KASSERT(!(value & ~0xffff), ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATALO, value); break; case BWN_TAB_32BIT: value = *((const uint32_t *)data); data += 4; BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATAHI, value >> 16); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATALO, value); break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } } } static struct bwn_txgain bwn_phy_lp_get_txgain(struct bwn_mac *mac) { struct bwn_txgain tg; uint16_t tmp; tg.tg_dac = (BWN_PHY_READ(mac, BWN_PHY_AFE_DAC_CTL) & 0x380) >> 7; if (mac->mac_phy.rev < 2) { tmp = BWN_PHY_READ(mac, BWN_PHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7ff; tg.tg_gm = tmp & 0x0007; tg.tg_pga = (tmp & 0x0078) >> 3; tg.tg_pad = (tmp & 0x780) >> 7; return (tg); } tmp = BWN_PHY_READ(mac, BWN_PHY_TX_GAIN_CTL_OVERRIDE_VAL); tg.tg_pad = BWN_PHY_READ(mac, BWN_PHY_OFDM(0xfb)) & 0xff; tg.tg_gm = tmp & 0xff; tg.tg_pga = (tmp >> 8) & 0xff; return (tg); } static uint8_t bwn_phy_lp_get_bbmult(struct bwn_mac *mac) { return (bwn_tab_read(mac, BWN_TAB_2(0, 87)) & 0xff00) >> 8; } static void bwn_phy_lp_set_txgain(struct bwn_mac *mac, struct bwn_txgain *tg) { uint16_t pa; if (mac->mac_phy.rev < 2) { BWN_PHY_SETMASK(mac, BWN_PHY_TX_GAIN_CTL_OVERRIDE_VAL, 0xf800, (tg->tg_pad << 7) | (tg->tg_pga << 3) | tg->tg_gm); bwn_phy_lp_set_txgain_dac(mac, tg->tg_dac); bwn_phy_lp_set_txgain_override(mac); return; } pa = bwn_phy_lp_get_pa_gain(mac); BWN_PHY_WRITE(mac, BWN_PHY_TX_GAIN_CTL_OVERRIDE_VAL, (tg->tg_pga << 8) | tg->tg_gm); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xfb), 0x8000, tg->tg_pad | (pa << 6)); BWN_PHY_WRITE(mac, BWN_PHY_OFDM(0xfc), (tg->tg_pga << 8) | tg->tg_gm); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xfd), 0x8000, tg->tg_pad | (pa << 8)); bwn_phy_lp_set_txgain_dac(mac, tg->tg_dac); bwn_phy_lp_set_txgain_override(mac); } static void bwn_phy_lp_set_bbmult(struct bwn_mac *mac, uint8_t bbmult) { bwn_tab_write(mac, BWN_TAB_2(0, 87), (uint16_t)bbmult << 8); } static void bwn_phy_lp_set_trsw_over(struct bwn_mac *mac, uint8_t tx, uint8_t rx) { uint16_t trsw = (tx << 1) | rx; BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xfffc, trsw); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x3); } static void bwn_phy_lp_set_rxgain(struct bwn_mac *mac, uint32_t gain) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t ext_lna, high_gain, lna, low_gain, trsw, tmp; if (mac->mac_phy.rev < 2) { trsw = gain & 0x1; lna = (gain & 0xfffc) | ((gain & 0xc) >> 2); ext_lna = (gain & 2) >> 1; BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xfffe, trsw); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfbff, ext_lna << 10); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xf7ff, ext_lna << 11); BWN_PHY_WRITE(mac, BWN_PHY_RX_GAIN_CTL_OVERRIDE_VAL, lna); } else { low_gain = gain & 0xffff; high_gain = (gain >> 16) & 0xf; ext_lna = (gain >> 21) & 0x1; trsw = ~(gain >> 20) & 0x1; BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0xfffe, trsw); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfdff, ext_lna << 9); BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xfbff, ext_lna << 10); BWN_PHY_WRITE(mac, BWN_PHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DDFS, 0xfff0, high_gain); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { tmp = (gain >> 2) & 0x3; BWN_PHY_SETMASK(mac, BWN_PHY_RF_OVERRIDE_2_VAL, 0xe7ff, tmp<<11); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xe6), 0xffe7, tmp << 3); } } BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x1); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x10); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x40); if (mac->mac_phy.rev >= 2) { BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x100); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x400); BWN_PHY_SET(mac, BWN_PHY_OFDM(0xe5), 0x8); } return; } BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x200); } static void bwn_phy_lp_set_deaf(struct bwn_mac *mac, uint8_t user) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; if (user) plp->plp_crsusr_off = 1; else plp->plp_crssys_off = 1; BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xff1f, 0x80); } static void bwn_phy_lp_clear_deaf(struct bwn_mac *mac, uint8_t user) { struct bwn_phy_lp *plp = &mac->mac_phy.phy_lp; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; if (user) plp->plp_crsusr_off = 0; else plp->plp_crssys_off = 0; if (plp->plp_crsusr_off || plp->plp_crssys_off) return; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xff1f, 0x60); else BWN_PHY_SETMASK(mac, BWN_PHY_CRSGAIN_CTL, 0xff1f, 0x20); } static unsigned int bwn_sqrt(struct bwn_mac *mac, unsigned int x) { struct bwn_softc *sc = mac->mac_sc; /* Table holding (10 * sqrt(x)) for x between 1 and 256. */ static uint8_t sqrt_table[256] = { 10, 14, 17, 20, 22, 24, 26, 28, 30, 31, 33, 34, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 60, 61, 62, 63, 64, 64, 65, 66, 67, 67, 68, 69, 70, 70, 71, 72, 72, 73, 74, 74, 75, 76, 76, 77, 78, 78, 79, 80, 80, 81, 81, 82, 83, 83, 84, 84, 85, 86, 86, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, 100, 100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 114, 115, 115, 116, 116, 117, 117, 117, 118, 118, 119, 119, 120, 120, 120, 121, 121, 122, 122, 122, 123, 123, 124, 124, 124, 125, 125, 126, 126, 126, 127, 127, 128, 128, 128, 129, 129, 130, 130, 130, 131, 131, 131, 132, 132, 133, 133, 133, 134, 134, 134, 135, 135, 136, 136, 136, 137, 137, 137, 138, 138, 138, 139, 139, 140, 140, 140, 141, 141, 141, 142, 142, 142, 143, 143, 143, 144, 144, 144, 145, 145, 145, 146, 146, 146, 147, 147, 147, 148, 148, 148, 149, 149, 150, 150, 150, 150, 151, 151, 151, 152, 152, 152, 153, 153, 153, 154, 154, 154, 155, 155, 155, 156, 156, 156, 157, 157, 157, 158, 158, 158, 159, 159, 159, 160 }; if (x == 0) return (0); if (x >= 256) { device_printf(sc->sc_dev, "out of bounds of the square-root table (%d)\n", x); return (16); } return (sqrt_table[x - 1] / 10); } static int bwn_phy_lp_calc_rx_iq_comp(struct bwn_mac *mac, uint16_t sample) { #define CALC_COEFF(_v, _x, _y, _z) do { \ int _t; \ _t = _x - 20; \ if (_t >= 0) { \ _v = ((_y << (30 - _x)) + (_z >> (1 + _t))) / (_z >> _t); \ } else { \ _v = ((_y << (30 - _x)) + (_z << (-1 - _t))) / (_z << -_t); \ } \ } while (0) #define CALC_COEFF2(_v, _x, _y, _z) do { \ int _t; \ _t = _x - 11; \ if (_t >= 0) \ tmp[3] = (_y << (31 - _x)) / (_z >> _t); \ else \ tmp[3] = (_y << (31 - _x)) / (_z << -_t); \ } while (0) struct bwn_phy_lp_iq_est ie; uint16_t v0, v1; int tmp[2], ret; v1 = BWN_PHY_READ(mac, BWN_PHY_RX_COMP_COEFF_S); v0 = v1 >> 8; v1 |= 0xff; BWN_PHY_SETMASK(mac, BWN_PHY_RX_COMP_COEFF_S, 0xff00, 0x00c0); BWN_PHY_MASK(mac, BWN_PHY_RX_COMP_COEFF_S, 0x00ff); ret = bwn_phy_lp_rx_iq_est(mac, sample, 32, &ie); if (ret == 0) goto done; if (ie.ie_ipwr + ie.ie_qpwr < 2) { ret = 0; goto done; } CALC_COEFF(tmp[0], bwn_nbits(ie.ie_iqprod), ie.ie_iqprod, ie.ie_ipwr); CALC_COEFF2(tmp[1], bwn_nbits(ie.ie_qpwr), ie.ie_qpwr, ie.ie_ipwr); tmp[1] = -bwn_sqrt(mac, tmp[1] - (tmp[0] * tmp[0])); v0 = tmp[0] >> 3; v1 = tmp[1] >> 4; done: BWN_PHY_SETMASK(mac, BWN_PHY_RX_COMP_COEFF_S, 0xff00, v1); BWN_PHY_SETMASK(mac, BWN_PHY_RX_COMP_COEFF_S, 0x00ff, v0 << 8); return ret; #undef CALC_COEFF #undef CALC_COEFF2 } static void bwn_phy_lp_tblinit_r01(struct bwn_mac *mac) { static const uint16_t noisescale[] = { 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa400, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0x00a4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x2d36, 0x0000, 0x0000, 0x4c00, 0x2d36, }; static const uint16_t crsgainnft[] = { 0x0366, 0x036a, 0x036f, 0x0364, 0x0367, 0x036d, 0x0374, 0x037f, 0x036f, 0x037b, 0x038a, 0x0378, 0x0367, 0x036d, 0x0375, 0x0381, 0x0374, 0x0381, 0x0392, 0x03a9, 0x03c4, 0x03e1, 0x0001, 0x001f, 0x0040, 0x005e, 0x007f, 0x009e, 0x00bd, 0x00dd, 0x00fd, 0x011d, 0x013d, }; static const uint16_t filterctl[] = { 0xa0fc, 0x10fc, 0x10db, 0x20b7, 0xff93, 0x10bf, 0x109b, 0x2077, 0xff53, 0x0127, }; static const uint32_t psctl[] = { 0x00010000, 0x000000a0, 0x00040000, 0x00000048, 0x08080101, 0x00000080, 0x08080101, 0x00000040, 0x08080101, 0x000000c0, 0x08a81501, 0x000000c0, 0x0fe8fd01, 0x000000c0, 0x08300105, 0x000000c0, 0x08080201, 0x000000c0, 0x08280205, 0x000000c0, 0xe80802fe, 0x000000c7, 0x28080206, 0x000000c0, 0x08080202, 0x000000c0, 0x0ba87602, 0x000000c0, 0x1068013d, 0x000000c0, 0x10280105, 0x000000c0, 0x08880102, 0x000000c0, 0x08280106, 0x000000c0, 0xe80801fd, 0x000000c7, 0xa8080115, 0x000000c0, }; static const uint16_t ofdmcckgain_r0[] = { 0x0001, 0x0001, 0x0001, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, }; static const uint16_t ofdmcckgain_r1[] = { 0x5000, 0x6000, 0x7000, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, }; static const uint16_t gaindelta[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const uint32_t txpwrctl[] = { 0x00000050, 0x0000004f, 0x0000004e, 0x0000004d, 0x0000004c, 0x0000004b, 0x0000004a, 0x00000049, 0x00000048, 0x00000047, 0x00000046, 0x00000045, 0x00000044, 0x00000043, 0x00000042, 0x00000041, 0x00000040, 0x0000003f, 0x0000003e, 0x0000003d, 0x0000003c, 0x0000003b, 0x0000003a, 0x00000039, 0x00000038, 0x00000037, 0x00000036, 0x00000035, 0x00000034, 0x00000033, 0x00000032, 0x00000031, 0x00000030, 0x0000002f, 0x0000002e, 0x0000002d, 0x0000002c, 0x0000002b, 0x0000002a, 0x00000029, 0x00000028, 0x00000027, 0x00000026, 0x00000025, 0x00000024, 0x00000023, 0x00000022, 0x00000021, 0x00000020, 0x0000001f, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001b, 0x0000001a, 0x00000019, 0x00000018, 0x00000017, 0x00000016, 0x00000015, 0x00000014, 0x00000013, 0x00000012, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000075a0, 0x000075a0, 0x000075a1, 0x000075a1, 0x000075a2, 0x000075a2, 0x000075a3, 0x000075a3, 0x000074b0, 0x000074b0, 0x000074b1, 0x000074b1, 0x000074b2, 0x000074b2, 0x000074b3, 0x000074b3, 0x00006d20, 0x00006d20, 0x00006d21, 0x00006d21, 0x00006d22, 0x00006d22, 0x00006d23, 0x00006d23, 0x00004660, 0x00004660, 0x00004661, 0x00004661, 0x00004662, 0x00004662, 0x00004663, 0x00004663, 0x00003e60, 0x00003e60, 0x00003e61, 0x00003e61, 0x00003e62, 0x00003e62, 0x00003e63, 0x00003e63, 0x00003660, 0x00003660, 0x00003661, 0x00003661, 0x00003662, 0x00003662, 0x00003663, 0x00003663, 0x00002e60, 0x00002e60, 0x00002e61, 0x00002e61, 0x00002e62, 0x00002e62, 0x00002e63, 0x00002e63, 0x00002660, 0x00002660, 0x00002661, 0x00002661, 0x00002662, 0x00002662, 0x00002663, 0x00002663, 0x000025e0, 0x000025e0, 0x000025e1, 0x000025e1, 0x000025e2, 0x000025e2, 0x000025e3, 0x000025e3, 0x00001de0, 0x00001de0, 0x00001de1, 0x00001de1, 0x00001de2, 0x00001de2, 0x00001de3, 0x00001de3, 0x00001d60, 0x00001d60, 0x00001d61, 0x00001d61, 0x00001d62, 0x00001d62, 0x00001d63, 0x00001d63, 0x00001560, 0x00001560, 0x00001561, 0x00001561, 0x00001562, 0x00001562, 0x00001563, 0x00001563, 0x00000d60, 0x00000d60, 0x00000d61, 0x00000d61, 0x00000d62, 0x00000d62, 0x00000d63, 0x00000d63, 0x00000ce0, 0x00000ce0, 0x00000ce1, 0x00000ce1, 0x00000ce2, 0x00000ce2, 0x00000ce3, 0x00000ce3, 0x00000e10, 0x00000e10, 0x00000e11, 0x00000e11, 0x00000e12, 0x00000e12, 0x00000e13, 0x00000e13, 0x00000bf0, 0x00000bf0, 0x00000bf1, 0x00000bf1, 0x00000bf2, 0x00000bf2, 0x00000bf3, 0x00000bf3, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x000000ff, 0x000002fc, 0x0000fa08, 0x00000305, 0x00000206, 0x00000304, 0x0000fb04, 0x0000fcff, 0x000005fb, 0x0000fd01, 0x00000401, 0x00000006, 0x0000ff03, 0x000007fc, 0x0000fc08, 0x00000203, 0x0000fffb, 0x00000600, 0x0000fa01, 0x0000fc03, 0x0000fe06, 0x0000fe00, 0x00000102, 0x000007fd, 0x000004fb, 0x000006ff, 0x000004fd, 0x0000fdfa, 0x000007fb, 0x0000fdfa, 0x0000fa06, 0x00000500, 0x0000f902, 0x000007fa, 0x0000fafa, 0x00000500, 0x000007fa, 0x00000700, 0x00000305, 0x000004ff, 0x00000801, 0x00000503, 0x000005f9, 0x00000404, 0x0000fb08, 0x000005fd, 0x00000501, 0x00000405, 0x0000fb03, 0x000007fc, 0x00000403, 0x00000303, 0x00000402, 0x0000faff, 0x0000fe05, 0x000005fd, 0x0000fe01, 0x000007fa, 0x00000202, 0x00000504, 0x00000102, 0x000008fe, 0x0000fa04, 0x0000fafc, 0x0000fe08, 0x000000f9, 0x000002fa, 0x000003fe, 0x00000304, 0x000004f9, 0x00000100, 0x0000fd06, 0x000008fc, 0x00000701, 0x00000504, 0x0000fdfe, 0x0000fdfc, 0x000003fe, 0x00000704, 0x000002fc, 0x000004f9, 0x0000fdfd, 0x0000fa07, 0x00000205, 0x000003fd, 0x000005fb, 0x000004f9, 0x00000804, 0x0000fc06, 0x0000fcf9, 0x00000100, 0x0000fe05, 0x00000408, 0x0000fb02, 0x00000304, 0x000006fe, 0x000004fa, 0x00000305, 0x000008fc, 0x00000102, 0x000001fd, 0x000004fc, 0x0000fe03, 0x00000701, 0x000001fb, 0x000001f9, 0x00000206, 0x000006fd, 0x00000508, 0x00000700, 0x00000304, 0x000005fe, 0x000005ff, 0x0000fa04, 0x00000303, 0x0000fefb, 0x000007f9, 0x0000fefc, 0x000004fd, 0x000005fc, 0x0000fffd, 0x0000fc08, 0x0000fbf9, 0x0000fd07, 0x000008fb, 0x0000fe02, 0x000006fb, 0x00000702, }; KASSERT(mac->mac_phy.rev < 2, ("%s:%d: fail", __func__, __LINE__)); bwn_tab_write_multi(mac, BWN_TAB_1(2, 0), N(bwn_tab_sigsq_tbl), bwn_tab_sigsq_tbl); bwn_tab_write_multi(mac, BWN_TAB_2(1, 0), N(noisescale), noisescale); bwn_tab_write_multi(mac, BWN_TAB_2(14, 0), N(crsgainnft), crsgainnft); bwn_tab_write_multi(mac, BWN_TAB_2(8, 0), N(filterctl), filterctl); bwn_tab_write_multi(mac, BWN_TAB_4(9, 0), N(psctl), psctl); bwn_tab_write_multi(mac, BWN_TAB_1(6, 0), N(bwn_tab_pllfrac_tbl), bwn_tab_pllfrac_tbl); bwn_tab_write_multi(mac, BWN_TAB_2(0, 0), N(bwn_tabl_iqlocal_tbl), bwn_tabl_iqlocal_tbl); if (mac->mac_phy.rev == 0) { bwn_tab_write_multi(mac, BWN_TAB_2(13, 0), N(ofdmcckgain_r0), ofdmcckgain_r0); bwn_tab_write_multi(mac, BWN_TAB_2(12, 0), N(ofdmcckgain_r0), ofdmcckgain_r0); } else { bwn_tab_write_multi(mac, BWN_TAB_2(13, 0), N(ofdmcckgain_r1), ofdmcckgain_r1); bwn_tab_write_multi(mac, BWN_TAB_2(12, 0), N(ofdmcckgain_r1), ofdmcckgain_r1); } bwn_tab_write_multi(mac, BWN_TAB_2(15, 0), N(gaindelta), gaindelta); bwn_tab_write_multi(mac, BWN_TAB_4(10, 0), N(txpwrctl), txpwrctl); } static void bwn_phy_lp_tblinit_r2(struct bwn_mac *mac) { struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; int i; static const uint16_t noisescale[] = { 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x0000, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4 }; static const uint32_t filterctl[] = { 0x000141fc, 0x000021fc, 0x000021b7, 0x0000416f, 0x0001ff27, 0x0000217f, 0x00002137, 0x000040ef, 0x0001fea7, 0x0000024f }; static const uint32_t psctl[] = { 0x00e38e08, 0x00e08e38, 0x00000000, 0x00000000, 0x00000000, 0x00002080, 0x00006180, 0x00003002, 0x00000040, 0x00002042, 0x00180047, 0x00080043, 0x00000041, 0x000020c1, 0x00046006, 0x00042002, 0x00040000, 0x00002003, 0x00180006, 0x00080002 }; static const uint32_t gainidx[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a }; static const uint16_t auxgainidx[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016 }; static const uint16_t swctl[] = { 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018 }; static const uint8_t hf[] = { 0x4b, 0x36, 0x24, 0x18, 0x49, 0x34, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17 }; static const uint32_t gainval[] = { 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x000000f1, 0x00000000, 0x00000000 }; static const uint16_t gain[] = { 0x0000, 0x0400, 0x0800, 0x0802, 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x080e, 0x080f, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816, 0x0817, 0x081a, 0x081b, 0x081f, 0x0820, 0x0824, 0x0830, 0x0834, 0x0837, 0x083b, 0x083f, 0x0840, 0x0844, 0x0857, 0x085b, 0x085f, 0x08d7, 0x08db, 0x08df, 0x0957, 0x095b, 0x095f, 0x0b57, 0x0b5b, 0x0b5f, 0x0f5f, 0x135f, 0x175f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; static const uint32_t papdeps[] = { 0x00000000, 0x00013ffc, 0x0001dff3, 0x0001bff0, 0x00023fe9, 0x00021fdf, 0x00028fdf, 0x00033fd2, 0x00039fcb, 0x00043fc7, 0x0004efc2, 0x00055fb5, 0x0005cfb0, 0x00063fa8, 0x00068fa3, 0x00071f98, 0x0007ef92, 0x00084f8b, 0x0008df82, 0x00097f77, 0x0009df69, 0x000a3f62, 0x000adf57, 0x000b6f4c, 0x000bff41, 0x000c9f39, 0x000cff30, 0x000dbf27, 0x000e4f1e, 0x000edf16, 0x000f7f13, 0x00102f11, 0x00110f10, 0x0011df11, 0x0012ef15, 0x00143f1c, 0x00158f27, 0x00172f35, 0x00193f47, 0x001baf5f, 0x001e6f7e, 0x0021cfa4, 0x0025bfd2, 0x002a2008, 0x002fb047, 0x00360090, 0x003d40e0, 0x0045c135, 0x004fb189, 0x005ae1d7, 0x0067221d, 0x0075025a, 0x007ff291, 0x007ff2bf, 0x007ff2e3, 0x007ff2ff, 0x007ff315, 0x007ff329, 0x007ff33f, 0x007ff356, 0x007ff36e, 0x007ff39c, 0x007ff441, 0x007ff506 }; static const uint32_t papdmult[] = { 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28 }; static const uint32_t gainidx_a0[] = { 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28 }; static const uint16_t auxgainidx_a0[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014 }; static const uint32_t gainval_a0[] = { 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x000000f7, 0x00000000, 0x00000000 }; static const uint16_t gain_a0[] = { 0x0000, 0x0002, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x000e, 0x000f, 0x0010, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x001a, 0x001b, 0x001f, 0x0020, 0x0024, 0x0030, 0x0034, 0x0037, 0x003b, 0x003f, 0x0040, 0x0044, 0x0057, 0x005b, 0x005f, 0x00d7, 0x00db, 0x00df, 0x0157, 0x015b, 0x015f, 0x0357, 0x035b, 0x035f, 0x075f, 0x0b5f, 0x0f5f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; KASSERT(mac->mac_phy.rev < 2, ("%s:%d: fail", __func__, __LINE__)); for (i = 0; i < 704; i++) bwn_tab_write(mac, BWN_TAB_4(7, i), 0); bwn_tab_write_multi(mac, BWN_TAB_1(2, 0), N(bwn_tab_sigsq_tbl), bwn_tab_sigsq_tbl); bwn_tab_write_multi(mac, BWN_TAB_2(1, 0), N(noisescale), noisescale); bwn_tab_write_multi(mac, BWN_TAB_4(11, 0), N(filterctl), filterctl); bwn_tab_write_multi(mac, BWN_TAB_4(12, 0), N(psctl), psctl); bwn_tab_write_multi(mac, BWN_TAB_4(13, 0), N(gainidx), gainidx); bwn_tab_write_multi(mac, BWN_TAB_2(14, 0), N(auxgainidx), auxgainidx); bwn_tab_write_multi(mac, BWN_TAB_2(15, 0), N(swctl), swctl); bwn_tab_write_multi(mac, BWN_TAB_1(16, 0), N(hf), hf); bwn_tab_write_multi(mac, BWN_TAB_4(17, 0), N(gainval), gainval); bwn_tab_write_multi(mac, BWN_TAB_2(18, 0), N(gain), gain); bwn_tab_write_multi(mac, BWN_TAB_1(6, 0), N(bwn_tab_pllfrac_tbl), bwn_tab_pllfrac_tbl); bwn_tab_write_multi(mac, BWN_TAB_2(0, 0), N(bwn_tabl_iqlocal_tbl), bwn_tabl_iqlocal_tbl); bwn_tab_write_multi(mac, BWN_TAB_4(9, 0), N(papdeps), papdeps); bwn_tab_write_multi(mac, BWN_TAB_4(10, 0), N(papdmult), papdmult); if ((siba->siba_chipid == 0x4325) && (siba->siba_chiprev == 0)) { bwn_tab_write_multi(mac, BWN_TAB_4(13, 0), N(gainidx_a0), gainidx_a0); bwn_tab_write_multi(mac, BWN_TAB_2(14, 0), N(auxgainidx_a0), auxgainidx_a0); bwn_tab_write_multi(mac, BWN_TAB_4(17, 0), N(gainval_a0), gainval_a0); bwn_tab_write_multi(mac, BWN_TAB_2(18, 0), N(gain_a0), gain_a0); } } static void bwn_phy_lp_tblinit_txgain(struct bwn_mac *mac) { struct siba_dev_softc *sd = mac->mac_sd; struct siba_softc *siba = sd->sd_bus; struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static struct bwn_txgain_entry txgain_r2[] = { { 255, 255, 203, 0, 152 }, { 255, 255, 203, 0, 147 }, { 255, 255, 203, 0, 143 }, { 255, 255, 203, 0, 139 }, { 255, 255, 203, 0, 135 }, { 255, 255, 203, 0, 131 }, { 255, 255, 203, 0, 128 }, { 255, 255, 203, 0, 124 }, { 255, 255, 203, 0, 121 }, { 255, 255, 203, 0, 117 }, { 255, 255, 203, 0, 114 }, { 255, 255, 203, 0, 111 }, { 255, 255, 203, 0, 107 }, { 255, 255, 203, 0, 104 }, { 255, 255, 203, 0, 101 }, { 255, 255, 203, 0, 99 }, { 255, 255, 203, 0, 96 }, { 255, 255, 203, 0, 93 }, { 255, 255, 203, 0, 90 }, { 255, 255, 203, 0, 88 }, { 255, 255, 203, 0, 85 }, { 255, 255, 203, 0, 83 }, { 255, 255, 203, 0, 81 }, { 255, 255, 203, 0, 78 }, { 255, 255, 203, 0, 76 }, { 255, 255, 203, 0, 74 }, { 255, 255, 203, 0, 72 }, { 255, 255, 203, 0, 70 }, { 255, 255, 203, 0, 68 }, { 255, 255, 203, 0, 66 }, { 255, 255, 203, 0, 64 }, { 255, 255, 197, 0, 64 }, { 255, 255, 192, 0, 64 }, { 255, 255, 186, 0, 64 }, { 255, 255, 181, 0, 64 }, { 255, 255, 176, 0, 64 }, { 255, 255, 171, 0, 64 }, { 255, 255, 166, 0, 64 }, { 255, 255, 161, 0, 64 }, { 255, 255, 157, 0, 64 }, { 255, 255, 152, 0, 64 }, { 255, 255, 148, 0, 64 }, { 255, 255, 144, 0, 64 }, { 255, 255, 140, 0, 64 }, { 255, 255, 136, 0, 64 }, { 255, 255, 132, 0, 64 }, { 255, 255, 128, 0, 64 }, { 255, 255, 124, 0, 64 }, { 255, 255, 121, 0, 64 }, { 255, 255, 117, 0, 64 }, { 255, 255, 114, 0, 64 }, { 255, 255, 111, 0, 64 }, { 255, 255, 108, 0, 64 }, { 255, 255, 105, 0, 64 }, { 255, 255, 102, 0, 64 }, { 255, 255, 99, 0, 64 }, { 255, 255, 96, 0, 64 }, { 255, 255, 93, 0, 64 }, { 255, 255, 91, 0, 64 }, { 255, 255, 88, 0, 64 }, { 255, 255, 86, 0, 64 }, { 255, 255, 83, 0, 64 }, { 255, 255, 81, 0, 64 }, { 255, 255, 79, 0, 64 }, { 255, 255, 76, 0, 64 }, { 255, 255, 74, 0, 64 }, { 255, 255, 72, 0, 64 }, { 255, 255, 70, 0, 64 }, { 255, 255, 68, 0, 64 }, { 255, 255, 66, 0, 64 }, { 255, 255, 64, 0, 64 }, { 255, 248, 64, 0, 64 }, { 255, 248, 62, 0, 64 }, { 255, 241, 62, 0, 64 }, { 255, 241, 60, 0, 64 }, { 255, 234, 60, 0, 64 }, { 255, 234, 59, 0, 64 }, { 255, 227, 59, 0, 64 }, { 255, 227, 57, 0, 64 }, { 255, 221, 57, 0, 64 }, { 255, 221, 55, 0, 64 }, { 255, 215, 55, 0, 64 }, { 255, 215, 54, 0, 64 }, { 255, 208, 54, 0, 64 }, { 255, 208, 52, 0, 64 }, { 255, 203, 52, 0, 64 }, { 255, 203, 51, 0, 64 }, { 255, 197, 51, 0, 64 }, { 255, 197, 49, 0, 64 }, { 255, 191, 49, 0, 64 }, { 255, 191, 48, 0, 64 }, { 255, 186, 48, 0, 64 }, { 255, 186, 47, 0, 64 }, { 255, 181, 47, 0, 64 }, { 255, 181, 45, 0, 64 }, { 255, 175, 45, 0, 64 }, { 255, 175, 44, 0, 64 }, { 255, 170, 44, 0, 64 }, { 255, 170, 43, 0, 64 }, { 255, 166, 43, 0, 64 }, { 255, 166, 42, 0, 64 }, { 255, 161, 42, 0, 64 }, { 255, 161, 40, 0, 64 }, { 255, 156, 40, 0, 64 }, { 255, 156, 39, 0, 64 }, { 255, 152, 39, 0, 64 }, { 255, 152, 38, 0, 64 }, { 255, 148, 38, 0, 64 }, { 255, 148, 37, 0, 64 }, { 255, 143, 37, 0, 64 }, { 255, 143, 36, 0, 64 }, { 255, 139, 36, 0, 64 }, { 255, 139, 35, 0, 64 }, { 255, 135, 35, 0, 64 }, { 255, 135, 34, 0, 64 }, { 255, 132, 34, 0, 64 }, { 255, 132, 33, 0, 64 }, { 255, 128, 33, 0, 64 }, { 255, 128, 32, 0, 64 }, { 255, 124, 32, 0, 64 }, { 255, 124, 31, 0, 64 }, { 255, 121, 31, 0, 64 }, { 255, 121, 30, 0, 64 }, { 255, 117, 30, 0, 64 }, { 255, 117, 29, 0, 64 }, { 255, 114, 29, 0, 64 }, { 255, 114, 29, 0, 64 }, { 255, 111, 29, 0, 64 }, }; static struct bwn_txgain_entry txgain_2ghz_r2[] = { { 7, 99, 255, 0, 64 }, { 7, 96, 255, 0, 64 }, { 7, 93, 255, 0, 64 }, { 7, 90, 255, 0, 64 }, { 7, 88, 255, 0, 64 }, { 7, 85, 255, 0, 64 }, { 7, 83, 255, 0, 64 }, { 7, 81, 255, 0, 64 }, { 7, 78, 255, 0, 64 }, { 7, 76, 255, 0, 64 }, { 7, 74, 255, 0, 64 }, { 7, 72, 255, 0, 64 }, { 7, 70, 255, 0, 64 }, { 7, 68, 255, 0, 64 }, { 7, 66, 255, 0, 64 }, { 7, 64, 255, 0, 64 }, { 7, 64, 255, 0, 64 }, { 7, 62, 255, 0, 64 }, { 7, 62, 248, 0, 64 }, { 7, 60, 248, 0, 64 }, { 7, 60, 241, 0, 64 }, { 7, 59, 241, 0, 64 }, { 7, 59, 234, 0, 64 }, { 7, 57, 234, 0, 64 }, { 7, 57, 227, 0, 64 }, { 7, 55, 227, 0, 64 }, { 7, 55, 221, 0, 64 }, { 7, 54, 221, 0, 64 }, { 7, 54, 215, 0, 64 }, { 7, 52, 215, 0, 64 }, { 7, 52, 208, 0, 64 }, { 7, 51, 208, 0, 64 }, { 7, 51, 203, 0, 64 }, { 7, 49, 203, 0, 64 }, { 7, 49, 197, 0, 64 }, { 7, 48, 197, 0, 64 }, { 7, 48, 191, 0, 64 }, { 7, 47, 191, 0, 64 }, { 7, 47, 186, 0, 64 }, { 7, 45, 186, 0, 64 }, { 7, 45, 181, 0, 64 }, { 7, 44, 181, 0, 64 }, { 7, 44, 175, 0, 64 }, { 7, 43, 175, 0, 64 }, { 7, 43, 170, 0, 64 }, { 7, 42, 170, 0, 64 }, { 7, 42, 166, 0, 64 }, { 7, 40, 166, 0, 64 }, { 7, 40, 161, 0, 64 }, { 7, 39, 161, 0, 64 }, { 7, 39, 156, 0, 64 }, { 7, 38, 156, 0, 64 }, { 7, 38, 152, 0, 64 }, { 7, 37, 152, 0, 64 }, { 7, 37, 148, 0, 64 }, { 7, 36, 148, 0, 64 }, { 7, 36, 143, 0, 64 }, { 7, 35, 143, 0, 64 }, { 7, 35, 139, 0, 64 }, { 7, 34, 139, 0, 64 }, { 7, 34, 135, 0, 64 }, { 7, 33, 135, 0, 64 }, { 7, 33, 132, 0, 64 }, { 7, 32, 132, 0, 64 }, { 7, 32, 128, 0, 64 }, { 7, 31, 128, 0, 64 }, { 7, 31, 124, 0, 64 }, { 7, 30, 124, 0, 64 }, { 7, 30, 121, 0, 64 }, { 7, 29, 121, 0, 64 }, { 7, 29, 117, 0, 64 }, { 7, 29, 117, 0, 64 }, { 7, 29, 114, 0, 64 }, { 7, 28, 114, 0, 64 }, { 7, 28, 111, 0, 64 }, { 7, 27, 111, 0, 64 }, { 7, 27, 108, 0, 64 }, { 7, 26, 108, 0, 64 }, { 7, 26, 104, 0, 64 }, { 7, 25, 104, 0, 64 }, { 7, 25, 102, 0, 64 }, { 7, 25, 102, 0, 64 }, { 7, 25, 99, 0, 64 }, { 7, 24, 99, 0, 64 }, { 7, 24, 96, 0, 64 }, { 7, 23, 96, 0, 64 }, { 7, 23, 93, 0, 64 }, { 7, 23, 93, 0, 64 }, { 7, 23, 90, 0, 64 }, { 7, 22, 90, 0, 64 }, { 7, 22, 88, 0, 64 }, { 7, 21, 88, 0, 64 }, { 7, 21, 85, 0, 64 }, { 7, 21, 85, 0, 64 }, { 7, 21, 83, 0, 64 }, { 7, 20, 83, 0, 64 }, { 7, 20, 81, 0, 64 }, { 7, 20, 81, 0, 64 }, { 7, 20, 78, 0, 64 }, { 7, 19, 78, 0, 64 }, { 7, 19, 76, 0, 64 }, { 7, 19, 76, 0, 64 }, { 7, 19, 74, 0, 64 }, { 7, 18, 74, 0, 64 }, { 7, 18, 72, 0, 64 }, { 7, 18, 72, 0, 64 }, { 7, 18, 70, 0, 64 }, { 7, 17, 70, 0, 64 }, { 7, 17, 68, 0, 64 }, { 7, 17, 68, 0, 64 }, { 7, 17, 66, 0, 64 }, { 7, 16, 66, 0, 64 }, { 7, 16, 64, 0, 64 }, { 7, 16, 64, 0, 64 }, { 7, 16, 62, 0, 64 }, { 7, 15, 62, 0, 64 }, { 7, 15, 60, 0, 64 }, { 7, 15, 60, 0, 64 }, { 7, 15, 59, 0, 64 }, { 7, 14, 59, 0, 64 }, { 7, 14, 57, 0, 64 }, { 7, 14, 57, 0, 64 }, { 7, 14, 55, 0, 64 }, { 7, 14, 55, 0, 64 }, { 7, 14, 54, 0, 64 }, { 7, 13, 54, 0, 64 }, { 7, 13, 52, 0, 64 }, { 7, 13, 52, 0, 64 }, }; static struct bwn_txgain_entry txgain_5ghz_r2[] = { { 255, 255, 255, 0, 152 }, { 255, 255, 255, 0, 147 }, { 255, 255, 255, 0, 143 }, { 255, 255, 255, 0, 139 }, { 255, 255, 255, 0, 135 }, { 255, 255, 255, 0, 131 }, { 255, 255, 255, 0, 128 }, { 255, 255, 255, 0, 124 }, { 255, 255, 255, 0, 121 }, { 255, 255, 255, 0, 117 }, { 255, 255, 255, 0, 114 }, { 255, 255, 255, 0, 111 }, { 255, 255, 255, 0, 107 }, { 255, 255, 255, 0, 104 }, { 255, 255, 255, 0, 101 }, { 255, 255, 255, 0, 99 }, { 255, 255, 255, 0, 96 }, { 255, 255, 255, 0, 93 }, { 255, 255, 255, 0, 90 }, { 255, 255, 255, 0, 88 }, { 255, 255, 255, 0, 85 }, { 255, 255, 255, 0, 83 }, { 255, 255, 255, 0, 81 }, { 255, 255, 255, 0, 78 }, { 255, 255, 255, 0, 76 }, { 255, 255, 255, 0, 74 }, { 255, 255, 255, 0, 72 }, { 255, 255, 255, 0, 70 }, { 255, 255, 255, 0, 68 }, { 255, 255, 255, 0, 66 }, { 255, 255, 255, 0, 64 }, { 255, 255, 248, 0, 64 }, { 255, 255, 241, 0, 64 }, { 255, 255, 234, 0, 64 }, { 255, 255, 227, 0, 64 }, { 255, 255, 221, 0, 64 }, { 255, 255, 215, 0, 64 }, { 255, 255, 208, 0, 64 }, { 255, 255, 203, 0, 64 }, { 255, 255, 197, 0, 64 }, { 255, 255, 191, 0, 64 }, { 255, 255, 186, 0, 64 }, { 255, 255, 181, 0, 64 }, { 255, 255, 175, 0, 64 }, { 255, 255, 170, 0, 64 }, { 255, 255, 166, 0, 64 }, { 255, 255, 161, 0, 64 }, { 255, 255, 156, 0, 64 }, { 255, 255, 152, 0, 64 }, { 255, 255, 148, 0, 64 }, { 255, 255, 143, 0, 64 }, { 255, 255, 139, 0, 64 }, { 255, 255, 135, 0, 64 }, { 255, 255, 132, 0, 64 }, { 255, 255, 128, 0, 64 }, { 255, 255, 124, 0, 64 }, { 255, 255, 121, 0, 64 }, { 255, 255, 117, 0, 64 }, { 255, 255, 114, 0, 64 }, { 255, 255, 111, 0, 64 }, { 255, 255, 108, 0, 64 }, { 255, 255, 104, 0, 64 }, { 255, 255, 102, 0, 64 }, { 255, 255, 99, 0, 64 }, { 255, 255, 96, 0, 64 }, { 255, 255, 93, 0, 64 }, { 255, 255, 90, 0, 64 }, { 255, 255, 88, 0, 64 }, { 255, 255, 85, 0, 64 }, { 255, 255, 83, 0, 64 }, { 255, 255, 81, 0, 64 }, { 255, 255, 78, 0, 64 }, { 255, 255, 76, 0, 64 }, { 255, 255, 74, 0, 64 }, { 255, 255, 72, 0, 64 }, { 255, 255, 70, 0, 64 }, { 255, 255, 68, 0, 64 }, { 255, 255, 66, 0, 64 }, { 255, 255, 64, 0, 64 }, { 255, 255, 64, 0, 64 }, { 255, 255, 62, 0, 64 }, { 255, 248, 62, 0, 64 }, { 255, 248, 60, 0, 64 }, { 255, 241, 60, 0, 64 }, { 255, 241, 59, 0, 64 }, { 255, 234, 59, 0, 64 }, { 255, 234, 57, 0, 64 }, { 255, 227, 57, 0, 64 }, { 255, 227, 55, 0, 64 }, { 255, 221, 55, 0, 64 }, { 255, 221, 54, 0, 64 }, { 255, 215, 54, 0, 64 }, { 255, 215, 52, 0, 64 }, { 255, 208, 52, 0, 64 }, { 255, 208, 51, 0, 64 }, { 255, 203, 51, 0, 64 }, { 255, 203, 49, 0, 64 }, { 255, 197, 49, 0, 64 }, { 255, 197, 48, 0, 64 }, { 255, 191, 48, 0, 64 }, { 255, 191, 47, 0, 64 }, { 255, 186, 47, 0, 64 }, { 255, 186, 45, 0, 64 }, { 255, 181, 45, 0, 64 }, { 255, 181, 44, 0, 64 }, { 255, 175, 44, 0, 64 }, { 255, 175, 43, 0, 64 }, { 255, 170, 43, 0, 64 }, { 255, 170, 42, 0, 64 }, { 255, 166, 42, 0, 64 }, { 255, 166, 40, 0, 64 }, { 255, 161, 40, 0, 64 }, { 255, 161, 39, 0, 64 }, { 255, 156, 39, 0, 64 }, { 255, 156, 38, 0, 64 }, { 255, 152, 38, 0, 64 }, { 255, 152, 37, 0, 64 }, { 255, 148, 37, 0, 64 }, { 255, 148, 36, 0, 64 }, { 255, 143, 36, 0, 64 }, { 255, 143, 35, 0, 64 }, { 255, 139, 35, 0, 64 }, { 255, 139, 34, 0, 64 }, { 255, 135, 34, 0, 64 }, { 255, 135, 33, 0, 64 }, { 255, 132, 33, 0, 64 }, { 255, 132, 32, 0, 64 }, { 255, 128, 32, 0, 64 } }; static struct bwn_txgain_entry txgain_r0[] = { { 7, 15, 14, 0, 152 }, { 7, 15, 14, 0, 147 }, { 7, 15, 14, 0, 143 }, { 7, 15, 14, 0, 139 }, { 7, 15, 14, 0, 135 }, { 7, 15, 14, 0, 131 }, { 7, 15, 14, 0, 128 }, { 7, 15, 14, 0, 124 }, { 7, 15, 14, 0, 121 }, { 7, 15, 14, 0, 117 }, { 7, 15, 14, 0, 114 }, { 7, 15, 14, 0, 111 }, { 7, 15, 14, 0, 107 }, { 7, 15, 14, 0, 104 }, { 7, 15, 14, 0, 101 }, { 7, 15, 14, 0, 99 }, { 7, 15, 14, 0, 96 }, { 7, 15, 14, 0, 93 }, { 7, 15, 14, 0, 90 }, { 7, 15, 14, 0, 88 }, { 7, 15, 14, 0, 85 }, { 7, 15, 14, 0, 83 }, { 7, 15, 14, 0, 81 }, { 7, 15, 14, 0, 78 }, { 7, 15, 14, 0, 76 }, { 7, 15, 14, 0, 74 }, { 7, 15, 14, 0, 72 }, { 7, 15, 14, 0, 70 }, { 7, 15, 14, 0, 68 }, { 7, 15, 14, 0, 66 }, { 7, 15, 14, 0, 64 }, { 7, 15, 14, 0, 62 }, { 7, 15, 14, 0, 60 }, { 7, 15, 14, 0, 59 }, { 7, 15, 14, 0, 57 }, { 7, 15, 13, 0, 72 }, { 7, 15, 13, 0, 70 }, { 7, 15, 13, 0, 68 }, { 7, 15, 13, 0, 66 }, { 7, 15, 13, 0, 64 }, { 7, 15, 13, 0, 62 }, { 7, 15, 13, 0, 60 }, { 7, 15, 13, 0, 59 }, { 7, 15, 13, 0, 57 }, { 7, 15, 12, 0, 71 }, { 7, 15, 12, 0, 69 }, { 7, 15, 12, 0, 67 }, { 7, 15, 12, 0, 65 }, { 7, 15, 12, 0, 63 }, { 7, 15, 12, 0, 62 }, { 7, 15, 12, 0, 60 }, { 7, 15, 12, 0, 58 }, { 7, 15, 12, 0, 57 }, { 7, 15, 11, 0, 70 }, { 7, 15, 11, 0, 68 }, { 7, 15, 11, 0, 66 }, { 7, 15, 11, 0, 65 }, { 7, 15, 11, 0, 63 }, { 7, 15, 11, 0, 61 }, { 7, 15, 11, 0, 59 }, { 7, 15, 11, 0, 58 }, { 7, 15, 10, 0, 71 }, { 7, 15, 10, 0, 69 }, { 7, 15, 10, 0, 67 }, { 7, 15, 10, 0, 65 }, { 7, 15, 10, 0, 63 }, { 7, 15, 10, 0, 61 }, { 7, 15, 10, 0, 60 }, { 7, 15, 10, 0, 58 }, { 7, 15, 10, 0, 56 }, { 7, 15, 9, 0, 70 }, { 7, 15, 9, 0, 68 }, { 7, 15, 9, 0, 66 }, { 7, 15, 9, 0, 64 }, { 7, 15, 9, 0, 62 }, { 7, 15, 9, 0, 60 }, { 7, 15, 9, 0, 59 }, { 7, 14, 9, 0, 72 }, { 7, 14, 9, 0, 70 }, { 7, 14, 9, 0, 68 }, { 7, 14, 9, 0, 66 }, { 7, 14, 9, 0, 64 }, { 7, 14, 9, 0, 62 }, { 7, 14, 9, 0, 60 }, { 7, 14, 9, 0, 59 }, { 7, 13, 9, 0, 72 }, { 7, 13, 9, 0, 70 }, { 7, 13, 9, 0, 68 }, { 7, 13, 9, 0, 66 }, { 7, 13, 9, 0, 64 }, { 7, 13, 9, 0, 63 }, { 7, 13, 9, 0, 61 }, { 7, 13, 9, 0, 59 }, { 7, 13, 9, 0, 57 }, { 7, 13, 8, 0, 72 }, { 7, 13, 8, 0, 70 }, { 7, 13, 8, 0, 68 }, { 7, 13, 8, 0, 66 }, { 7, 13, 8, 0, 64 }, { 7, 13, 8, 0, 62 }, { 7, 13, 8, 0, 60 }, { 7, 13, 8, 0, 59 }, { 7, 12, 8, 0, 72 }, { 7, 12, 8, 0, 70 }, { 7, 12, 8, 0, 68 }, { 7, 12, 8, 0, 66 }, { 7, 12, 8, 0, 64 }, { 7, 12, 8, 0, 62 }, { 7, 12, 8, 0, 61 }, { 7, 12, 8, 0, 59 }, { 7, 12, 7, 0, 73 }, { 7, 12, 7, 0, 71 }, { 7, 12, 7, 0, 69 }, { 7, 12, 7, 0, 67 }, { 7, 12, 7, 0, 65 }, { 7, 12, 7, 0, 63 }, { 7, 12, 7, 0, 61 }, { 7, 12, 7, 0, 59 }, { 7, 11, 7, 0, 72 }, { 7, 11, 7, 0, 70 }, { 7, 11, 7, 0, 68 }, { 7, 11, 7, 0, 66 }, { 7, 11, 7, 0, 65 }, { 7, 11, 7, 0, 63 }, { 7, 11, 7, 0, 61 }, { 7, 11, 7, 0, 59 }, { 7, 11, 6, 0, 73 }, { 7, 11, 6, 0, 71 } }; static struct bwn_txgain_entry txgain_2ghz_r0[] = { { 4, 15, 9, 0, 64 }, { 4, 15, 9, 0, 62 }, { 4, 15, 9, 0, 60 }, { 4, 15, 9, 0, 59 }, { 4, 14, 9, 0, 72 }, { 4, 14, 9, 0, 70 }, { 4, 14, 9, 0, 68 }, { 4, 14, 9, 0, 66 }, { 4, 14, 9, 0, 64 }, { 4, 14, 9, 0, 62 }, { 4, 14, 9, 0, 60 }, { 4, 14, 9, 0, 59 }, { 4, 13, 9, 0, 72 }, { 4, 13, 9, 0, 70 }, { 4, 13, 9, 0, 68 }, { 4, 13, 9, 0, 66 }, { 4, 13, 9, 0, 64 }, { 4, 13, 9, 0, 63 }, { 4, 13, 9, 0, 61 }, { 4, 13, 9, 0, 59 }, { 4, 13, 9, 0, 57 }, { 4, 13, 8, 0, 72 }, { 4, 13, 8, 0, 70 }, { 4, 13, 8, 0, 68 }, { 4, 13, 8, 0, 66 }, { 4, 13, 8, 0, 64 }, { 4, 13, 8, 0, 62 }, { 4, 13, 8, 0, 60 }, { 4, 13, 8, 0, 59 }, { 4, 12, 8, 0, 72 }, { 4, 12, 8, 0, 70 }, { 4, 12, 8, 0, 68 }, { 4, 12, 8, 0, 66 }, { 4, 12, 8, 0, 64 }, { 4, 12, 8, 0, 62 }, { 4, 12, 8, 0, 61 }, { 4, 12, 8, 0, 59 }, { 4, 12, 7, 0, 73 }, { 4, 12, 7, 0, 71 }, { 4, 12, 7, 0, 69 }, { 4, 12, 7, 0, 67 }, { 4, 12, 7, 0, 65 }, { 4, 12, 7, 0, 63 }, { 4, 12, 7, 0, 61 }, { 4, 12, 7, 0, 59 }, { 4, 11, 7, 0, 72 }, { 4, 11, 7, 0, 70 }, { 4, 11, 7, 0, 68 }, { 4, 11, 7, 0, 66 }, { 4, 11, 7, 0, 65 }, { 4, 11, 7, 0, 63 }, { 4, 11, 7, 0, 61 }, { 4, 11, 7, 0, 59 }, { 4, 11, 6, 0, 73 }, { 4, 11, 6, 0, 71 }, { 4, 11, 6, 0, 69 }, { 4, 11, 6, 0, 67 }, { 4, 11, 6, 0, 65 }, { 4, 11, 6, 0, 63 }, { 4, 11, 6, 0, 61 }, { 4, 11, 6, 0, 60 }, { 4, 10, 6, 0, 72 }, { 4, 10, 6, 0, 70 }, { 4, 10, 6, 0, 68 }, { 4, 10, 6, 0, 66 }, { 4, 10, 6, 0, 64 }, { 4, 10, 6, 0, 62 }, { 4, 10, 6, 0, 60 }, { 4, 10, 6, 0, 59 }, { 4, 10, 5, 0, 72 }, { 4, 10, 5, 0, 70 }, { 4, 10, 5, 0, 68 }, { 4, 10, 5, 0, 66 }, { 4, 10, 5, 0, 64 }, { 4, 10, 5, 0, 62 }, { 4, 10, 5, 0, 60 }, { 4, 10, 5, 0, 59 }, { 4, 9, 5, 0, 70 }, { 4, 9, 5, 0, 68 }, { 4, 9, 5, 0, 66 }, { 4, 9, 5, 0, 64 }, { 4, 9, 5, 0, 63 }, { 4, 9, 5, 0, 61 }, { 4, 9, 5, 0, 59 }, { 4, 9, 4, 0, 71 }, { 4, 9, 4, 0, 69 }, { 4, 9, 4, 0, 67 }, { 4, 9, 4, 0, 65 }, { 4, 9, 4, 0, 63 }, { 4, 9, 4, 0, 62 }, { 4, 9, 4, 0, 60 }, { 4, 9, 4, 0, 58 }, { 4, 8, 4, 0, 70 }, { 4, 8, 4, 0, 68 }, { 4, 8, 4, 0, 66 }, { 4, 8, 4, 0, 65 }, { 4, 8, 4, 0, 63 }, { 4, 8, 4, 0, 61 }, { 4, 8, 4, 0, 59 }, { 4, 7, 4, 0, 68 }, { 4, 7, 4, 0, 66 }, { 4, 7, 4, 0, 64 }, { 4, 7, 4, 0, 62 }, { 4, 7, 4, 0, 61 }, { 4, 7, 4, 0, 59 }, { 4, 7, 3, 0, 67 }, { 4, 7, 3, 0, 65 }, { 4, 7, 3, 0, 63 }, { 4, 7, 3, 0, 62 }, { 4, 7, 3, 0, 60 }, { 4, 6, 3, 0, 65 }, { 4, 6, 3, 0, 63 }, { 4, 6, 3, 0, 61 }, { 4, 6, 3, 0, 60 }, { 4, 6, 3, 0, 58 }, { 4, 5, 3, 0, 68 }, { 4, 5, 3, 0, 66 }, { 4, 5, 3, 0, 64 }, { 4, 5, 3, 0, 62 }, { 4, 5, 3, 0, 60 }, { 4, 5, 3, 0, 59 }, { 4, 5, 3, 0, 57 }, { 4, 4, 2, 0, 83 }, { 4, 4, 2, 0, 81 }, { 4, 4, 2, 0, 78 }, { 4, 4, 2, 0, 76 }, { 4, 4, 2, 0, 74 }, { 4, 4, 2, 0, 72 } }; static struct bwn_txgain_entry txgain_5ghz_r0[] = { { 7, 15, 15, 0, 99 }, { 7, 15, 15, 0, 96 }, { 7, 15, 15, 0, 93 }, { 7, 15, 15, 0, 90 }, { 7, 15, 15, 0, 88 }, { 7, 15, 15, 0, 85 }, { 7, 15, 15, 0, 83 }, { 7, 15, 15, 0, 81 }, { 7, 15, 15, 0, 78 }, { 7, 15, 15, 0, 76 }, { 7, 15, 15, 0, 74 }, { 7, 15, 15, 0, 72 }, { 7, 15, 15, 0, 70 }, { 7, 15, 15, 0, 68 }, { 7, 15, 15, 0, 66 }, { 7, 15, 15, 0, 64 }, { 7, 15, 15, 0, 62 }, { 7, 15, 15, 0, 60 }, { 7, 15, 15, 0, 59 }, { 7, 15, 15, 0, 57 }, { 7, 15, 15, 0, 55 }, { 7, 15, 14, 0, 72 }, { 7, 15, 14, 0, 70 }, { 7, 15, 14, 0, 68 }, { 7, 15, 14, 0, 66 }, { 7, 15, 14, 0, 64 }, { 7, 15, 14, 0, 62 }, { 7, 15, 14, 0, 60 }, { 7, 15, 14, 0, 58 }, { 7, 15, 14, 0, 56 }, { 7, 15, 14, 0, 55 }, { 7, 15, 13, 0, 71 }, { 7, 15, 13, 0, 69 }, { 7, 15, 13, 0, 67 }, { 7, 15, 13, 0, 65 }, { 7, 15, 13, 0, 63 }, { 7, 15, 13, 0, 62 }, { 7, 15, 13, 0, 60 }, { 7, 15, 13, 0, 58 }, { 7, 15, 13, 0, 56 }, { 7, 15, 12, 0, 72 }, { 7, 15, 12, 0, 70 }, { 7, 15, 12, 0, 68 }, { 7, 15, 12, 0, 66 }, { 7, 15, 12, 0, 64 }, { 7, 15, 12, 0, 62 }, { 7, 15, 12, 0, 60 }, { 7, 15, 12, 0, 59 }, { 7, 15, 12, 0, 57 }, { 7, 15, 11, 0, 73 }, { 7, 15, 11, 0, 71 }, { 7, 15, 11, 0, 69 }, { 7, 15, 11, 0, 67 }, { 7, 15, 11, 0, 65 }, { 7, 15, 11, 0, 63 }, { 7, 15, 11, 0, 61 }, { 7, 15, 11, 0, 60 }, { 7, 15, 11, 0, 58 }, { 7, 15, 10, 0, 71 }, { 7, 15, 10, 0, 69 }, { 7, 15, 10, 0, 67 }, { 7, 15, 10, 0, 65 }, { 7, 15, 10, 0, 63 }, { 7, 15, 10, 0, 61 }, { 7, 15, 10, 0, 60 }, { 7, 15, 10, 0, 58 }, { 7, 15, 9, 0, 70 }, { 7, 15, 9, 0, 68 }, { 7, 15, 9, 0, 66 }, { 7, 15, 9, 0, 64 }, { 7, 15, 9, 0, 62 }, { 7, 15, 9, 0, 61 }, { 7, 15, 9, 0, 59 }, { 7, 15, 9, 0, 57 }, { 7, 15, 9, 0, 56 }, { 7, 14, 9, 0, 68 }, { 7, 14, 9, 0, 66 }, { 7, 14, 9, 0, 65 }, { 7, 14, 9, 0, 63 }, { 7, 14, 9, 0, 61 }, { 7, 14, 9, 0, 59 }, { 7, 14, 9, 0, 58 }, { 7, 13, 9, 0, 70 }, { 7, 13, 9, 0, 68 }, { 7, 13, 9, 0, 66 }, { 7, 13, 9, 0, 64 }, { 7, 13, 9, 0, 63 }, { 7, 13, 9, 0, 61 }, { 7, 13, 9, 0, 59 }, { 7, 13, 9, 0, 57 }, { 7, 13, 8, 0, 70 }, { 7, 13, 8, 0, 68 }, { 7, 13, 8, 0, 66 }, { 7, 13, 8, 0, 64 }, { 7, 13, 8, 0, 62 }, { 7, 13, 8, 0, 60 }, { 7, 13, 8, 0, 59 }, { 7, 13, 8, 0, 57 }, { 7, 12, 8, 0, 70 }, { 7, 12, 8, 0, 68 }, { 7, 12, 8, 0, 66 }, { 7, 12, 8, 0, 64 }, { 7, 12, 8, 0, 62 }, { 7, 12, 8, 0, 61 }, { 7, 12, 8, 0, 59 }, { 7, 12, 8, 0, 57 }, { 7, 12, 7, 0, 70 }, { 7, 12, 7, 0, 68 }, { 7, 12, 7, 0, 66 }, { 7, 12, 7, 0, 64 }, { 7, 12, 7, 0, 62 }, { 7, 12, 7, 0, 61 }, { 7, 12, 7, 0, 59 }, { 7, 12, 7, 0, 57 }, { 7, 11, 7, 0, 70 }, { 7, 11, 7, 0, 68 }, { 7, 11, 7, 0, 66 }, { 7, 11, 7, 0, 64 }, { 7, 11, 7, 0, 62 }, { 7, 11, 7, 0, 61 }, { 7, 11, 7, 0, 59 }, { 7, 11, 7, 0, 57 }, { 7, 11, 6, 0, 69 }, { 7, 11, 6, 0, 67 }, { 7, 11, 6, 0, 65 }, { 7, 11, 6, 0, 63 }, { 7, 11, 6, 0, 62 }, { 7, 11, 6, 0, 60 } }; static struct bwn_txgain_entry txgain_r1[] = { { 7, 15, 14, 0, 152 }, { 7, 15, 14, 0, 147 }, { 7, 15, 14, 0, 143 }, { 7, 15, 14, 0, 139 }, { 7, 15, 14, 0, 135 }, { 7, 15, 14, 0, 131 }, { 7, 15, 14, 0, 128 }, { 7, 15, 14, 0, 124 }, { 7, 15, 14, 0, 121 }, { 7, 15, 14, 0, 117 }, { 7, 15, 14, 0, 114 }, { 7, 15, 14, 0, 111 }, { 7, 15, 14, 0, 107 }, { 7, 15, 14, 0, 104 }, { 7, 15, 14, 0, 101 }, { 7, 15, 14, 0, 99 }, { 7, 15, 14, 0, 96 }, { 7, 15, 14, 0, 93 }, { 7, 15, 14, 0, 90 }, { 7, 15, 14, 0, 88 }, { 7, 15, 14, 0, 85 }, { 7, 15, 14, 0, 83 }, { 7, 15, 14, 0, 81 }, { 7, 15, 14, 0, 78 }, { 7, 15, 14, 0, 76 }, { 7, 15, 14, 0, 74 }, { 7, 15, 14, 0, 72 }, { 7, 15, 14, 0, 70 }, { 7, 15, 14, 0, 68 }, { 7, 15, 14, 0, 66 }, { 7, 15, 14, 0, 64 }, { 7, 15, 14, 0, 62 }, { 7, 15, 14, 0, 60 }, { 7, 15, 14, 0, 59 }, { 7, 15, 14, 0, 57 }, { 7, 15, 13, 0, 72 }, { 7, 15, 13, 0, 70 }, { 7, 15, 14, 0, 68 }, { 7, 15, 14, 0, 66 }, { 7, 15, 14, 0, 64 }, { 7, 15, 14, 0, 62 }, { 7, 15, 14, 0, 60 }, { 7, 15, 14, 0, 59 }, { 7, 15, 14, 0, 57 }, { 7, 15, 13, 0, 72 }, { 7, 15, 13, 0, 70 }, { 7, 15, 13, 0, 68 }, { 7, 15, 13, 0, 66 }, { 7, 15, 13, 0, 64 }, { 7, 15, 13, 0, 62 }, { 7, 15, 13, 0, 60 }, { 7, 15, 13, 0, 59 }, { 7, 15, 13, 0, 57 }, { 7, 15, 12, 0, 71 }, { 7, 15, 12, 0, 69 }, { 7, 15, 12, 0, 67 }, { 7, 15, 12, 0, 65 }, { 7, 15, 12, 0, 63 }, { 7, 15, 12, 0, 62 }, { 7, 15, 12, 0, 60 }, { 7, 15, 12, 0, 58 }, { 7, 15, 12, 0, 57 }, { 7, 15, 11, 0, 70 }, { 7, 15, 11, 0, 68 }, { 7, 15, 11, 0, 66 }, { 7, 15, 11, 0, 65 }, { 7, 15, 11, 0, 63 }, { 7, 15, 11, 0, 61 }, { 7, 15, 11, 0, 59 }, { 7, 15, 11, 0, 58 }, { 7, 15, 10, 0, 71 }, { 7, 15, 10, 0, 69 }, { 7, 15, 10, 0, 67 }, { 7, 15, 10, 0, 65 }, { 7, 15, 10, 0, 63 }, { 7, 15, 10, 0, 61 }, { 7, 15, 10, 0, 60 }, { 7, 15, 10, 0, 58 }, { 7, 15, 10, 0, 56 }, { 7, 15, 9, 0, 70 }, { 7, 15, 9, 0, 68 }, { 7, 15, 9, 0, 66 }, { 7, 15, 9, 0, 64 }, { 7, 15, 9, 0, 62 }, { 7, 15, 9, 0, 60 }, { 7, 15, 9, 0, 59 }, { 7, 14, 9, 0, 72 }, { 7, 14, 9, 0, 70 }, { 7, 14, 9, 0, 68 }, { 7, 14, 9, 0, 66 }, { 7, 14, 9, 0, 64 }, { 7, 14, 9, 0, 62 }, { 7, 14, 9, 0, 60 }, { 7, 14, 9, 0, 59 }, { 7, 13, 9, 0, 72 }, { 7, 13, 9, 0, 70 }, { 7, 13, 9, 0, 68 }, { 7, 13, 9, 0, 66 }, { 7, 13, 9, 0, 64 }, { 7, 13, 9, 0, 63 }, { 7, 13, 9, 0, 61 }, { 7, 13, 9, 0, 59 }, { 7, 13, 9, 0, 57 }, { 7, 13, 8, 0, 72 }, { 7, 13, 8, 0, 70 }, { 7, 13, 8, 0, 68 }, { 7, 13, 8, 0, 66 }, { 7, 13, 8, 0, 64 }, { 7, 13, 8, 0, 62 }, { 7, 13, 8, 0, 60 }, { 7, 13, 8, 0, 59 }, { 7, 12, 8, 0, 72 }, { 7, 12, 8, 0, 70 }, { 7, 12, 8, 0, 68 }, { 7, 12, 8, 0, 66 }, { 7, 12, 8, 0, 64 }, { 7, 12, 8, 0, 62 }, { 7, 12, 8, 0, 61 }, { 7, 12, 8, 0, 59 }, { 7, 12, 7, 0, 73 }, { 7, 12, 7, 0, 71 }, { 7, 12, 7, 0, 69 }, { 7, 12, 7, 0, 67 }, { 7, 12, 7, 0, 65 }, { 7, 12, 7, 0, 63 }, { 7, 12, 7, 0, 61 }, { 7, 12, 7, 0, 59 }, { 7, 11, 7, 0, 72 }, { 7, 11, 7, 0, 70 }, { 7, 11, 7, 0, 68 }, { 7, 11, 7, 0, 66 }, { 7, 11, 7, 0, 65 }, { 7, 11, 7, 0, 63 }, { 7, 11, 7, 0, 61 }, { 7, 11, 7, 0, 59 }, { 7, 11, 6, 0, 73 }, { 7, 11, 6, 0, 71 } }; static struct bwn_txgain_entry txgain_2ghz_r1[] = { { 4, 15, 15, 0, 90 }, { 4, 15, 15, 0, 88 }, { 4, 15, 15, 0, 85 }, { 4, 15, 15, 0, 83 }, { 4, 15, 15, 0, 81 }, { 4, 15, 15, 0, 78 }, { 4, 15, 15, 0, 76 }, { 4, 15, 15, 0, 74 }, { 4, 15, 15, 0, 72 }, { 4, 15, 15, 0, 70 }, { 4, 15, 15, 0, 68 }, { 4, 15, 15, 0, 66 }, { 4, 15, 15, 0, 64 }, { 4, 15, 15, 0, 62 }, { 4, 15, 15, 0, 60 }, { 4, 15, 15, 0, 59 }, { 4, 15, 14, 0, 72 }, { 4, 15, 14, 0, 70 }, { 4, 15, 14, 0, 68 }, { 4, 15, 14, 0, 66 }, { 4, 15, 14, 0, 64 }, { 4, 15, 14, 0, 62 }, { 4, 15, 14, 0, 60 }, { 4, 15, 14, 0, 59 }, { 4, 15, 13, 0, 72 }, { 4, 15, 13, 0, 70 }, { 4, 15, 13, 0, 68 }, { 4, 15, 13, 0, 66 }, { 4, 15, 13, 0, 64 }, { 4, 15, 13, 0, 62 }, { 4, 15, 13, 0, 60 }, { 4, 15, 13, 0, 59 }, { 4, 15, 12, 0, 72 }, { 4, 15, 12, 0, 70 }, { 4, 15, 12, 0, 68 }, { 4, 15, 12, 0, 66 }, { 4, 15, 12, 0, 64 }, { 4, 15, 12, 0, 62 }, { 4, 15, 12, 0, 60 }, { 4, 15, 12, 0, 59 }, { 4, 15, 11, 0, 72 }, { 4, 15, 11, 0, 70 }, { 4, 15, 11, 0, 68 }, { 4, 15, 11, 0, 66 }, { 4, 15, 11, 0, 64 }, { 4, 15, 11, 0, 62 }, { 4, 15, 11, 0, 60 }, { 4, 15, 11, 0, 59 }, { 4, 15, 10, 0, 72 }, { 4, 15, 10, 0, 70 }, { 4, 15, 10, 0, 68 }, { 4, 15, 10, 0, 66 }, { 4, 15, 10, 0, 64 }, { 4, 15, 10, 0, 62 }, { 4, 15, 10, 0, 60 }, { 4, 15, 10, 0, 59 }, { 4, 15, 9, 0, 72 }, { 4, 15, 9, 0, 70 }, { 4, 15, 9, 0, 68 }, { 4, 15, 9, 0, 66 }, { 4, 15, 9, 0, 64 }, { 4, 15, 9, 0, 62 }, { 4, 15, 9, 0, 60 }, { 4, 15, 9, 0, 59 }, { 4, 14, 9, 0, 72 }, { 4, 14, 9, 0, 70 }, { 4, 14, 9, 0, 68 }, { 4, 14, 9, 0, 66 }, { 4, 14, 9, 0, 64 }, { 4, 14, 9, 0, 62 }, { 4, 14, 9, 0, 60 }, { 4, 14, 9, 0, 59 }, { 4, 13, 9, 0, 72 }, { 4, 13, 9, 0, 70 }, { 4, 13, 9, 0, 68 }, { 4, 13, 9, 0, 66 }, { 4, 13, 9, 0, 64 }, { 4, 13, 9, 0, 63 }, { 4, 13, 9, 0, 61 }, { 4, 13, 9, 0, 59 }, { 4, 13, 9, 0, 57 }, { 4, 13, 8, 0, 72 }, { 4, 13, 8, 0, 70 }, { 4, 13, 8, 0, 68 }, { 4, 13, 8, 0, 66 }, { 4, 13, 8, 0, 64 }, { 4, 13, 8, 0, 62 }, { 4, 13, 8, 0, 60 }, { 4, 13, 8, 0, 59 }, { 4, 12, 8, 0, 72 }, { 4, 12, 8, 0, 70 }, { 4, 12, 8, 0, 68 }, { 4, 12, 8, 0, 66 }, { 4, 12, 8, 0, 64 }, { 4, 12, 8, 0, 62 }, { 4, 12, 8, 0, 61 }, { 4, 12, 8, 0, 59 }, { 4, 12, 7, 0, 73 }, { 4, 12, 7, 0, 71 }, { 4, 12, 7, 0, 69 }, { 4, 12, 7, 0, 67 }, { 4, 12, 7, 0, 65 }, { 4, 12, 7, 0, 63 }, { 4, 12, 7, 0, 61 }, { 4, 12, 7, 0, 59 }, { 4, 11, 7, 0, 72 }, { 4, 11, 7, 0, 70 }, { 4, 11, 7, 0, 68 }, { 4, 11, 7, 0, 66 }, { 4, 11, 7, 0, 65 }, { 4, 11, 7, 0, 63 }, { 4, 11, 7, 0, 61 }, { 4, 11, 7, 0, 59 }, { 4, 11, 6, 0, 73 }, { 4, 11, 6, 0, 71 }, { 4, 11, 6, 0, 69 }, { 4, 11, 6, 0, 67 }, { 4, 11, 6, 0, 65 }, { 4, 11, 6, 0, 63 }, { 4, 11, 6, 0, 61 }, { 4, 11, 6, 0, 60 }, { 4, 10, 6, 0, 72 }, { 4, 10, 6, 0, 70 }, { 4, 10, 6, 0, 68 }, { 4, 10, 6, 0, 66 }, { 4, 10, 6, 0, 64 }, { 4, 10, 6, 0, 62 }, { 4, 10, 6, 0, 60 } }; static struct bwn_txgain_entry txgain_5ghz_r1[] = { { 7, 15, 15, 0, 99 }, { 7, 15, 15, 0, 96 }, { 7, 15, 15, 0, 93 }, { 7, 15, 15, 0, 90 }, { 7, 15, 15, 0, 88 }, { 7, 15, 15, 0, 85 }, { 7, 15, 15, 0, 83 }, { 7, 15, 15, 0, 81 }, { 7, 15, 15, 0, 78 }, { 7, 15, 15, 0, 76 }, { 7, 15, 15, 0, 74 }, { 7, 15, 15, 0, 72 }, { 7, 15, 15, 0, 70 }, { 7, 15, 15, 0, 68 }, { 7, 15, 15, 0, 66 }, { 7, 15, 15, 0, 64 }, { 7, 15, 15, 0, 62 }, { 7, 15, 15, 0, 60 }, { 7, 15, 15, 0, 59 }, { 7, 15, 15, 0, 57 }, { 7, 15, 15, 0, 55 }, { 7, 15, 14, 0, 72 }, { 7, 15, 14, 0, 70 }, { 7, 15, 14, 0, 68 }, { 7, 15, 14, 0, 66 }, { 7, 15, 14, 0, 64 }, { 7, 15, 14, 0, 62 }, { 7, 15, 14, 0, 60 }, { 7, 15, 14, 0, 58 }, { 7, 15, 14, 0, 56 }, { 7, 15, 14, 0, 55 }, { 7, 15, 13, 0, 71 }, { 7, 15, 13, 0, 69 }, { 7, 15, 13, 0, 67 }, { 7, 15, 13, 0, 65 }, { 7, 15, 13, 0, 63 }, { 7, 15, 13, 0, 62 }, { 7, 15, 13, 0, 60 }, { 7, 15, 13, 0, 58 }, { 7, 15, 13, 0, 56 }, { 7, 15, 12, 0, 72 }, { 7, 15, 12, 0, 70 }, { 7, 15, 12, 0, 68 }, { 7, 15, 12, 0, 66 }, { 7, 15, 12, 0, 64 }, { 7, 15, 12, 0, 62 }, { 7, 15, 12, 0, 60 }, { 7, 15, 12, 0, 59 }, { 7, 15, 12, 0, 57 }, { 7, 15, 11, 0, 73 }, { 7, 15, 11, 0, 71 }, { 7, 15, 11, 0, 69 }, { 7, 15, 11, 0, 67 }, { 7, 15, 11, 0, 65 }, { 7, 15, 11, 0, 63 }, { 7, 15, 11, 0, 61 }, { 7, 15, 11, 0, 60 }, { 7, 15, 11, 0, 58 }, { 7, 15, 10, 0, 71 }, { 7, 15, 10, 0, 69 }, { 7, 15, 10, 0, 67 }, { 7, 15, 10, 0, 65 }, { 7, 15, 10, 0, 63 }, { 7, 15, 10, 0, 61 }, { 7, 15, 10, 0, 60 }, { 7, 15, 10, 0, 58 }, { 7, 15, 9, 0, 70 }, { 7, 15, 9, 0, 68 }, { 7, 15, 9, 0, 66 }, { 7, 15, 9, 0, 64 }, { 7, 15, 9, 0, 62 }, { 7, 15, 9, 0, 61 }, { 7, 15, 9, 0, 59 }, { 7, 15, 9, 0, 57 }, { 7, 15, 9, 0, 56 }, { 7, 14, 9, 0, 68 }, { 7, 14, 9, 0, 66 }, { 7, 14, 9, 0, 65 }, { 7, 14, 9, 0, 63 }, { 7, 14, 9, 0, 61 }, { 7, 14, 9, 0, 59 }, { 7, 14, 9, 0, 58 }, { 7, 13, 9, 0, 70 }, { 7, 13, 9, 0, 68 }, { 7, 13, 9, 0, 66 }, { 7, 13, 9, 0, 64 }, { 7, 13, 9, 0, 63 }, { 7, 13, 9, 0, 61 }, { 7, 13, 9, 0, 59 }, { 7, 13, 9, 0, 57 }, { 7, 13, 8, 0, 70 }, { 7, 13, 8, 0, 68 }, { 7, 13, 8, 0, 66 }, { 7, 13, 8, 0, 64 }, { 7, 13, 8, 0, 62 }, { 7, 13, 8, 0, 60 }, { 7, 13, 8, 0, 59 }, { 7, 13, 8, 0, 57 }, { 7, 12, 8, 0, 70 }, { 7, 12, 8, 0, 68 }, { 7, 12, 8, 0, 66 }, { 7, 12, 8, 0, 64 }, { 7, 12, 8, 0, 62 }, { 7, 12, 8, 0, 61 }, { 7, 12, 8, 0, 59 }, { 7, 12, 8, 0, 57 }, { 7, 12, 7, 0, 70 }, { 7, 12, 7, 0, 68 }, { 7, 12, 7, 0, 66 }, { 7, 12, 7, 0, 64 }, { 7, 12, 7, 0, 62 }, { 7, 12, 7, 0, 61 }, { 7, 12, 7, 0, 59 }, { 7, 12, 7, 0, 57 }, { 7, 11, 7, 0, 70 }, { 7, 11, 7, 0, 68 }, { 7, 11, 7, 0, 66 }, { 7, 11, 7, 0, 64 }, { 7, 11, 7, 0, 62 }, { 7, 11, 7, 0, 61 }, { 7, 11, 7, 0, 59 }, { 7, 11, 7, 0, 57 }, { 7, 11, 6, 0, 69 }, { 7, 11, 6, 0, 67 }, { 7, 11, 6, 0, 65 }, { 7, 11, 6, 0, 63 }, { 7, 11, 6, 0, 62 }, { 7, 11, 6, 0, 60 } }; if (mac->mac_phy.rev != 0 && mac->mac_phy.rev != 1) { if (siba->siba_sprom.bf_hi & BWN_BFH_NOPA) bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_r2); else if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_2ghz_r2); else bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_5ghz_r2); return; } if (mac->mac_phy.rev == 0) { if ((siba->siba_sprom.bf_hi & BWN_BFH_NOPA) || (siba->siba_sprom.bf_lo & BWN_BFL_HGPA)) bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_r0); else if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_2ghz_r0); else bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_5ghz_r0); return; } if ((siba->siba_sprom.bf_hi & BWN_BFH_NOPA) || (siba->siba_sprom.bf_lo & BWN_BFL_HGPA)) bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_r1); else if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_2ghz_r1); else bwn_phy_lp_gaintbl_write_multi(mac, 0, 128, txgain_5ghz_r1); } static void bwn_tab_write(struct bwn_mac *mac, uint32_t typeoffset, uint32_t value) { uint32_t offset, type; type = BWN_TAB_GETTYPE(typeoffset); offset = BWN_TAB_GETOFFSET(typeoffset); KASSERT(offset <= 0xffff, ("%s:%d: fail", __func__, __LINE__)); switch (type) { case BWN_TAB_8BIT: KASSERT(!(value & ~0xff), ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATALO, value); break; case BWN_TAB_16BIT: KASSERT(!(value & ~0xffff), ("%s:%d: fail", __func__, __LINE__)); BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATALO, value); break; case BWN_TAB_32BIT: BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATAHI, value >> 16); BWN_PHY_WRITE(mac, BWN_PHY_TABLEDATALO, value); break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); } } static int bwn_phy_lp_loopback(struct bwn_mac *mac) { struct bwn_phy_lp_iq_est ie; int i, index = -1; uint32_t tmp; memset(&ie, 0, sizeof(ie)); bwn_phy_lp_set_trsw_over(mac, 1, 1); BWN_PHY_SET(mac, BWN_PHY_AFE_CTL_OVR, 1); BWN_PHY_MASK(mac, BWN_PHY_AFE_CTL_OVRVAL, 0xfffe); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x800); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0x800); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x8); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0x8); BWN_RF_WRITE(mac, BWN_B2062_N_TXCTL_A, 0x80); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_0, 0x80); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_VAL_0, 0x80); for (i = 0; i < 32; i++) { bwn_phy_lp_set_rxgain_idx(mac, i); bwn_phy_lp_ddfs_turnon(mac, 1, 1, 5, 5, 0); if (!(bwn_phy_lp_rx_iq_est(mac, 1000, 32, &ie))) continue; tmp = (ie.ie_ipwr + ie.ie_qpwr) / 1000; if ((tmp > 4000) && (tmp < 10000)) { index = i; break; } } bwn_phy_lp_ddfs_turnoff(mac); return (index); } static void bwn_phy_lp_set_rxgain_idx(struct bwn_mac *mac, uint16_t idx) { bwn_phy_lp_set_rxgain(mac, bwn_tab_read(mac, BWN_TAB_2(12, idx))); } static void bwn_phy_lp_ddfs_turnon(struct bwn_mac *mac, int i_on, int q_on, int incr1, int incr2, int scale_idx) { bwn_phy_lp_ddfs_turnoff(mac); BWN_PHY_MASK(mac, BWN_PHY_AFE_DDFS_POINTER_INIT, 0xff80); BWN_PHY_MASK(mac, BWN_PHY_AFE_DDFS_POINTER_INIT, 0x80ff); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DDFS_INCR_INIT, 0xff80, incr1); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DDFS_INCR_INIT, 0x80ff, incr2 << 8); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DDFS, 0xfff7, i_on << 3); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DDFS, 0xffef, q_on << 4); BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DDFS, 0xff9f, scale_idx << 5); BWN_PHY_MASK(mac, BWN_PHY_AFE_DDFS, 0xfffb); BWN_PHY_SET(mac, BWN_PHY_AFE_DDFS, 0x2); BWN_PHY_SET(mac, BWN_PHY_LP_PHY_CTL, 0x20); } static uint8_t bwn_phy_lp_rx_iq_est(struct bwn_mac *mac, uint16_t sample, uint8_t time, struct bwn_phy_lp_iq_est *ie) { int i; BWN_PHY_MASK(mac, BWN_PHY_CRSGAIN_CTL, 0xfff7); BWN_PHY_WRITE(mac, BWN_PHY_IQ_NUM_SMPLS_ADDR, sample); BWN_PHY_SETMASK(mac, BWN_PHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xff00, time); BWN_PHY_MASK(mac, BWN_PHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xfeff); BWN_PHY_SET(mac, BWN_PHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200); for (i = 0; i < 500; i++) { if (!(BWN_PHY_READ(mac, BWN_PHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) break; DELAY(1000); } if ((BWN_PHY_READ(mac, BWN_PHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) { BWN_PHY_SET(mac, BWN_PHY_CRSGAIN_CTL, 0x8); return 0; } ie->ie_iqprod = BWN_PHY_READ(mac, BWN_PHY_IQ_ACC_HI_ADDR); ie->ie_iqprod <<= 16; ie->ie_iqprod |= BWN_PHY_READ(mac, BWN_PHY_IQ_ACC_LO_ADDR); ie->ie_ipwr = BWN_PHY_READ(mac, BWN_PHY_IQ_I_PWR_ACC_HI_ADDR); ie->ie_ipwr <<= 16; ie->ie_ipwr |= BWN_PHY_READ(mac, BWN_PHY_IQ_I_PWR_ACC_LO_ADDR); ie->ie_qpwr = BWN_PHY_READ(mac, BWN_PHY_IQ_Q_PWR_ACC_HI_ADDR); ie->ie_qpwr <<= 16; ie->ie_qpwr |= BWN_PHY_READ(mac, BWN_PHY_IQ_Q_PWR_ACC_LO_ADDR); BWN_PHY_SET(mac, BWN_PHY_CRSGAIN_CTL, 0x8); return 1; } static uint32_t bwn_tab_read(struct bwn_mac *mac, uint32_t typeoffset) { uint32_t offset, type, value; type = BWN_TAB_GETTYPE(typeoffset); offset = BWN_TAB_GETOFFSET(typeoffset); KASSERT(offset <= 0xffff, ("%s:%d: fail", __func__, __LINE__)); switch (type) { case BWN_TAB_8BIT: BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); value = BWN_PHY_READ(mac, BWN_PHY_TABLEDATALO) & 0xff; break; case BWN_TAB_16BIT: BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); value = BWN_PHY_READ(mac, BWN_PHY_TABLEDATALO); break; case BWN_TAB_32BIT: BWN_PHY_WRITE(mac, BWN_PHY_TABLE_ADDR, offset); value = BWN_PHY_READ(mac, BWN_PHY_TABLEDATAHI); value <<= 16; value |= BWN_PHY_READ(mac, BWN_PHY_TABLEDATALO); break; default: KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); value = 0; } return (value); } static void bwn_phy_lp_ddfs_turnoff(struct bwn_mac *mac) { BWN_PHY_MASK(mac, BWN_PHY_AFE_DDFS, 0xfffd); BWN_PHY_MASK(mac, BWN_PHY_LP_PHY_CTL, 0xffdf); } static void bwn_phy_lp_set_txgain_dac(struct bwn_mac *mac, uint16_t dac) { uint16_t ctl; ctl = BWN_PHY_READ(mac, BWN_PHY_AFE_DAC_CTL) & 0xc7f; ctl |= dac << 7; BWN_PHY_SETMASK(mac, BWN_PHY_AFE_DAC_CTL, 0xf000, ctl); } static void bwn_phy_lp_set_txgain_pa(struct bwn_mac *mac, uint16_t gain) { BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xfb), 0xe03f, gain << 6); BWN_PHY_SETMASK(mac, BWN_PHY_OFDM(0xfd), 0x80ff, gain << 8); } static void bwn_phy_lp_set_txgain_override(struct bwn_mac *mac) { if (mac->mac_phy.rev < 2) BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x100); else { BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x80); BWN_PHY_SET(mac, BWN_PHY_RF_OVERRIDE_2, 0x4000); } BWN_PHY_SET(mac, BWN_PHY_AFE_CTL_OVR, 0x40); } static uint16_t bwn_phy_lp_get_pa_gain(struct bwn_mac *mac) { return BWN_PHY_READ(mac, BWN_PHY_OFDM(0xfb)) & 0x7f; } static uint8_t bwn_nbits(int32_t val) { uint32_t tmp; uint8_t nbits = 0; for (tmp = abs(val); tmp != 0; tmp >>= 1) nbits++; return (nbits); } static void bwn_phy_lp_gaintbl_write_multi(struct bwn_mac *mac, int offset, int count, struct bwn_txgain_entry *table) { int i; for (i = offset; i < count; i++) bwn_phy_lp_gaintbl_write(mac, i, table[i]); } static void bwn_phy_lp_gaintbl_write(struct bwn_mac *mac, int offset, struct bwn_txgain_entry data) { if (mac->mac_phy.rev >= 2) bwn_phy_lp_gaintbl_write_r2(mac, offset, data); else bwn_phy_lp_gaintbl_write_r01(mac, offset, data); } static void bwn_phy_lp_gaintbl_write_r2(struct bwn_mac *mac, int offset, struct bwn_txgain_entry te) { struct bwn_softc *sc = mac->mac_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; KASSERT(mac->mac_phy.rev >= 2, ("%s:%d: fail", __func__, __LINE__)); tmp = (te.te_pad << 16) | (te.te_pga << 8) | te.te_gm; if (mac->mac_phy.rev >= 3) { tmp |= ((IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) ? (0x10 << 24) : (0x70 << 24)); } else { tmp |= ((IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) ? (0x14 << 24) : (0x7f << 24)); } bwn_tab_write(mac, BWN_TAB_4(7, 0xc0 + offset), tmp); bwn_tab_write(mac, BWN_TAB_4(7, 0x140 + offset), te.te_bbmult << 20 | te.te_dac << 28); } static void bwn_phy_lp_gaintbl_write_r01(struct bwn_mac *mac, int offset, struct bwn_txgain_entry te) { KASSERT(mac->mac_phy.rev < 2, ("%s:%d: fail", __func__, __LINE__)); bwn_tab_write(mac, BWN_TAB_4(10, 0xc0 + offset), (te.te_pad << 11) | (te.te_pga << 7) | (te.te_gm << 4) | te.te_dac); bwn_tab_write(mac, BWN_TAB_4(10, 0x140 + offset), te.te_bbmult << 20); +} + +static void +bwn_sysctl_node(struct bwn_softc *sc) +{ + device_t dev = sc->sc_dev; + struct bwn_mac *mac; + struct bwn_stats *stats; + + /* XXX assume that count of MAC is only 1. */ + + if ((mac = sc->sc_curmac) == NULL) + return; + stats = &mac->mac_stats; + + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "linknoise", CTLFLAG_RW, &stats->rts, 0, "Noise level"); + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rts", CTLFLAG_RW, &stats->rts, 0, "RTS"); + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rtsfail", CTLFLAG_RW, &stats->rtsfail, 0, "RTS failed to send"); + +#ifdef BWN_DEBUG + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags"); +#endif } static void bwn_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, 0, "bwn", -1); } static device_method_t bwn_methods[] = { /* Device interface */ DEVMETHOD(device_identify, bwn_identify), DEVMETHOD(device_probe, bwn_probe), DEVMETHOD(device_attach, bwn_attach), DEVMETHOD(device_detach, bwn_detach), DEVMETHOD(device_suspend, bwn_suspend), DEVMETHOD(device_resume, bwn_resume), { 0,0 } }; static driver_t bwn_driver = { "bwn", bwn_methods, sizeof(struct bwn_softc) }; static devclass_t bwn_devclass; DRIVER_MODULE(bwn, siba_bwn, bwn_driver, bwn_devclass, 0, 0); MODULE_DEPEND(bwn, siba_bwn, 1, 1, 1); MODULE_DEPEND(bwn, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(bwn, firmware, 1, 1, 1); /* firmware support */ MODULE_DEPEND(bwn, wlan_amrr, 1, 1, 1); Index: projects/ppc64/sys/dev/bwn/if_bwnvar.h =================================================================== --- projects/ppc64/sys/dev/bwn/if_bwnvar.h (revision 204271) +++ projects/ppc64/sys/dev/bwn/if_bwnvar.h (revision 204272) @@ -1,957 +1,959 @@ /*- * Copyright (c) 2009-2010 Weongyo Jeong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _IF_BWNVAR_H #define _IF_BWNVAR_H struct siba_dev_softc; struct bwn_softc; struct bwn_mac; #define N(a) (sizeof(a) / sizeof(a[0])) #define BWN_ALIGN 0x1000 #define BWN_BUS_SPACE_MAXADDR_30BIT 0x3fffffff #define BWN_RETRY_SHORT 7 #define BWN_RETRY_LONG 4 #define BWN_STAID_MAX 64 #define BWN_TXPWR_IGNORE_TIME (1 << 0) #define BWN_TXPWR_IGNORE_TSSI (1 << 1) #define BWN_HAS_TXMAG(phy) \ (((phy)->rev >= 2) && ((phy)->rf_ver == 0x2050) && \ ((phy)->rf_rev == 8)) #define BWN_HAS_LOOPBACK(phy) \ (((phy)->rev > 1) || ((phy)->gmode)) #define BWN_TXERROR_MAX 1000 #define BWN_GETTIME(v) do { \ struct timespec ts; \ nanouptime(&ts); \ (v) = ts.tv_nsec / 1000000 + ts.tv_sec * 1000; \ } while (0) #define BWN_ISOLDFMT(mac) ((mac)->mac_fw.rev <= 351) #define BWN_TSSI2DBM(num, den) \ ((int32_t)((num < 0) ? num / den : (num + den / 2) / den)) #define BWN_HDRSIZE(mac) \ ((BWN_ISOLDFMT(mac)) ? (100 + sizeof(struct bwn_plcp6)) : \ (104 + sizeof(struct bwn_plcp6))) #define BWN_PIO_COOKIE(tq, tp) \ ((uint16_t)((((uint16_t)tq->tq_index + 1) << 12) | tp->tp_index)) #define BWN_DMA_COOKIE(dr, slot) \ ((uint16_t)(((uint16_t)dr->dr_index + 1) << 12) | (uint16_t)slot) #define BWN_READ_2(mac, o) (siba_read_2(mac->mac_sd, o)) #define BWN_READ_4(mac, o) (siba_read_4(mac->mac_sd, o)) #define BWN_WRITE_2(mac, o, v) (siba_write_2(mac->mac_sd, o, v)) #define BWN_WRITE_4(mac, o, v) (siba_write_4(mac->mac_sd, o, v)) #define BWN_PIO_TXQOFFSET(mac) \ ((mac->mac_sd->sd_id.sd_rev >= 11) ? 0x18 : 0) #define BWN_PIO_RXQOFFSET(mac) \ ((mac->mac_sd->sd_id.sd_rev >= 11) ? 0x38 : 8) #define BWN_SEC_NEWAPI(mac) (mac->mac_fw.rev >= 351) #define BWN_SEC_KEY2FW(mac, idx) \ (BWN_SEC_NEWAPI(mac) ? idx : ((idx >= 4) ? idx - 4 : idx)) #define BWN_RF_READ(mac, r) (mac->mac_phy.rf_read(mac, r)) #define BWN_RF_WRITE(mac, r, v) (mac->mac_phy.rf_write(mac, r, v)) #define BWN_RF_MASK(mac, o, m) \ BWN_RF_WRITE(mac, o, BWN_RF_READ(mac, o) & m) #define BWN_RF_SETMASK(mac, offset, mask, set) \ BWN_RF_WRITE(mac, offset, (BWN_RF_READ(mac, offset) & mask) | set) #define BWN_RF_SET(mac, offset, set) \ BWN_RF_WRITE(mac, offset, BWN_RF_READ(mac, offset) | set) #define BWN_PHY_READ(mac, r) (mac->mac_phy.phy_read(mac, r)) #define BWN_PHY_WRITE(mac, r, v) \ (mac->mac_phy.phy_write(mac, r, v)) #define BWN_PHY_SET(mac, offset, set) do { \ if (mac->mac_phy.phy_maskset != NULL) { \ KASSERT(mac->mac_status < BWN_MAC_STATUS_INITED || \ mac->mac_suspended > 0, \ ("dont access PHY or RF registers after turning on MAC")); \ mac->mac_phy.phy_maskset(mac, offset, 0xffff, set); \ } else \ BWN_PHY_WRITE(mac, offset, \ BWN_PHY_READ(mac, offset) | (set)); \ } while (0) #define BWN_PHY_SETMASK(mac, offset, mask, set) do { \ if (mac->mac_phy.phy_maskset != NULL) { \ KASSERT(mac->mac_status < BWN_MAC_STATUS_INITED || \ mac->mac_suspended > 0, \ ("dont access PHY or RF registers after turning on MAC")); \ mac->mac_phy.phy_maskset(mac, offset, mask, set); \ } else \ BWN_PHY_WRITE(mac, offset, \ (BWN_PHY_READ(mac, offset) & (mask)) | (set)); \ } while (0) #define BWN_PHY_MASK(mac, offset, mask) do { \ if (mac->mac_phy.phy_maskset != NULL) { \ KASSERT(mac->mac_status < BWN_MAC_STATUS_INITED || \ mac->mac_suspended > 0, \ ("dont access PHY or RF registers after turning on MAC")); \ mac->mac_phy.phy_maskset(mac, offset, mask, 0); \ } else \ BWN_PHY_WRITE(mac, offset, \ BWN_PHY_READ(mac, offset) & mask); \ } while (0) #define BWN_PHY_COPY(mac, dst, src) do { \ KASSERT(mac->mac_status < BWN_MAC_STATUS_INITED || \ mac->mac_suspended > 0, \ ("dont access PHY or RF registers after turning on MAC")); \ BWN_PHY_WRITE(mac, dst, BWN_PHY_READ(mac, src)); \ } while (0) #define BWN_LO_CALIB_EXPIRE (1000 * (30 - 2)) #define BWN_LO_PWRVEC_EXPIRE (1000 * (30 - 2)) #define BWN_LO_TXCTL_EXPIRE (1000 * (180 - 4)) #define BWN_DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) #define BWN_LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) #define BWN_BITREV4(tmp) (BWN_BITREV8(tmp) >> 4) #define BWN_BITREV8(byte) (bwn_bitrev_table[byte]) #define BWN_BBATTCMP(a, b) ((a)->att == (b)->att) #define BWN_RFATTCMP(a, b) \ (((a)->att == (b)->att) && ((a)->padmix == (b)->padmix)) #define BWN_PIO_WRITE_2(mac, tq, offset, value) \ BWN_WRITE_2(mac, (tq)->tq_base + offset, value) #define BWN_PIO_READ_4(mac, tq, offset) \ BWN_READ_4(mac, tq->tq_base + offset) #define BWN_ISCCKRATE(rate) \ (rate == BWN_CCK_RATE_1MB || rate == BWN_CCK_RATE_2MB || \ rate == BWN_CCK_RATE_5MB || rate == BWN_CCK_RATE_11MB) #define BWN_ISOFDMRATE(rate) (!BWN_ISCCKRATE(rate)) #define BWN_BARRIER(mac, flags) siba_barrier(mac->mac_sd, flags) #define BWN_DMA_READ(dr, offset) \ (BWN_READ_4(dr->dr_mac, dr->dr_base + offset)) #define BWN_DMA_WRITE(dr, offset, value) \ (BWN_WRITE_4(dr->dr_mac, dr->dr_base + offset, value)) struct bwn_rate { uint16_t rateid; uint32_t flags; }; #define BWN_ANT0 0 #define BWN_ANT1 1 #define BWN_ANTAUTO0 2 #define BWN_ANTAUTO1 3 #define BWN_ANT2 4 #define BWN_ANT3 8 #define BWN_ANTAUTO BWN_ANTAUTO0 #define BWN_ANT_DEFAULT BWN_ANTAUTO #define BWN_TX_SLOTS_PER_FRAME 2 struct bwn_channel { unsigned freq; unsigned ieee; unsigned maxTxPow; }; struct bwn_channelinfo { struct bwn_channel channels[IEEE80211_CHAN_MAX]; unsigned nchannels; }; struct bwn_bbatt { uint8_t att; }; struct bwn_bbatt_list { const struct bwn_bbatt *array; uint8_t len; uint8_t min; uint8_t max; }; struct bwn_rfatt { uint8_t att; int padmix; }; struct bwn_rfatt_list { const struct bwn_rfatt *array; uint8_t len; uint8_t min; uint8_t max; }; #define BWN_DC_LT_SIZE 32 struct bwn_loctl { int8_t i; int8_t q; }; struct bwn_lo_calib { struct bwn_bbatt bbatt; struct bwn_rfatt rfatt; struct bwn_loctl ctl; unsigned long calib_time; TAILQ_ENTRY(bwn_lo_calib) list; }; struct bwn_rxhdr4 { uint16_t frame_len; uint8_t pad1[2]; uint16_t phy_status0; union { struct { uint8_t rssi; uint8_t sig_qual; } __packed abg; struct { int8_t power0; int8_t power1; } __packed n; } __packed phy; uint16_t phy_status2; uint16_t phy_status3; uint32_t mac_status; uint16_t mac_time; uint16_t channel; } __packed; struct bwn_txstatus { uint16_t cookie; uint16_t seq; uint8_t phy_stat; uint8_t framecnt; uint8_t rtscnt; uint8_t sreason; uint8_t pm; uint8_t im; uint8_t ampdu; uint8_t ack; }; #define BWN_TXCTL_PA3DB 0x40 #define BWN_TXCTL_PA2DB 0x20 #define BWN_TXCTL_TXMIX 0x10 struct bwn_txpwr_loctl { struct bwn_rfatt_list rfatt; struct bwn_bbatt_list bbatt; uint16_t dc_lt[BWN_DC_LT_SIZE]; TAILQ_HEAD(, bwn_lo_calib) calib_list; unsigned long pwr_vec_read_time; unsigned long txctl_measured_time; uint8_t tx_bias; uint8_t tx_magn; uint64_t power_vector; }; #define BWN_OFDMTAB_DIR_UNKNOWN 0 #define BWN_OFDMTAB_DIR_READ 1 #define BWN_OFDMTAB_DIR_WRITE 2 struct bwn_phy_g { unsigned pg_flags; #define BWN_PHY_G_FLAG_TSSITABLE_ALLOC (1 << 0) #define BWN_PHY_G_FLAG_RADIOCTX_VALID (1 << 1) int pg_aci_enable; int pg_aci_wlan_automatic; int pg_aci_hw_rssi; int pg_rf_on; uint16_t pg_radioctx_over; uint16_t pg_radioctx_overval; uint16_t pg_minlowsig[2]; uint16_t pg_minlowsigpos[2]; int8_t *pg_tssi2dbm; int pg_idletssi; int pg_curtssi; uint8_t pg_avgtssi; struct bwn_bbatt pg_bbatt; struct bwn_rfatt pg_rfatt; uint8_t pg_txctl; int pg_bbatt_delta; int pg_rfatt_delta; struct bwn_txpwr_loctl pg_loctl; int16_t pg_max_lb_gain; int16_t pg_trsw_rx_gain; int16_t pg_lna_lod_gain; int16_t pg_lna_gain; int16_t pg_pga_gain; int pg_immode; #define BWN_INTERFSTACK_SIZE 26 uint32_t pg_interfstack[BWN_INTERFSTACK_SIZE]; int16_t pg_nrssi[2]; int32_t pg_nrssi_slope; int8_t pg_nrssi_lt[64]; uint16_t pg_lofcal; uint16_t pg_initval; uint16_t pg_ofdmtab_addr; unsigned pg_ofdmtab_dir; }; #define BWN_IMMODE_NONE 0 #define BWN_IMMODE_NONWLAN 1 #define BWN_IMMODE_MANUAL 2 #define BWN_IMMODE_AUTO 3 #define BWN_TXPWR_RES_NEED_ADJUST 0 #define BWN_TXPWR_RES_DONE 1 #define BWN_PHYLP_TXPCTL_UNKNOWN 0 #define BWN_PHYLP_TXPCTL_OFF 1 #define BWN_PHYLP_TXPCTL_ON_SW 2 #define BWN_PHYLP_TXPCTL_ON_HW 3 struct bwn_phy_lp { uint8_t plp_chan; uint8_t plp_chanfullcal; int32_t plp_antenna; uint8_t plp_txpctlmode; uint8_t plp_txisoband_h; uint8_t plp_txisoband_m; uint8_t plp_txisoband_l; uint8_t plp_rxpwroffset; int8_t plp_txpwridx; uint16_t plp_tssiidx; uint16_t plp_tssinpt; uint8_t plp_rssivf; uint8_t plp_rssivc; uint8_t plp_rssigs; uint8_t plp_rccap; uint8_t plp_bxarch; uint8_t plp_crsusr_off; uint8_t plp_crssys_off; uint32_t plp_div; int32_t plp_tonefreq; uint16_t plp_digfilt[9]; }; /* for LP */ struct bwn_txgain { uint16_t tg_gm; uint16_t tg_pga; uint16_t tg_pad; uint16_t tg_dac; }; struct bwn_rxcompco { uint8_t rc_chan; int8_t rc_c1; int8_t rc_c0; }; struct bwn_phy_lp_iq_est { uint32_t ie_iqprod; uint32_t ie_ipwr; uint32_t ie_qpwr; }; struct bwn_txgain_entry { uint8_t te_gm; uint8_t te_pga; uint8_t te_pad; uint8_t te_dac; uint8_t te_bbmult; }; /* only for LP PHY */ struct bwn_stxtable { uint16_t st_phyoffset; uint16_t st_physhift; uint16_t st_rfaddr; uint16_t st_rfshift; uint16_t st_mask; }; struct bwn_b206x_chan { uint8_t bc_chan; uint16_t bc_freq; const uint8_t *bc_data; }; struct bwn_b206x_rfinit_entry { uint16_t br_offset; uint16_t br_valuea; uint16_t br_valueg; uint8_t br_flags; }; struct bwn_phy { uint8_t type; uint8_t rev; uint8_t analog; int supports_2ghz; int supports_5ghz; int gmode; struct bwn_phy_g phy_g; struct bwn_phy_lp phy_lp; uint16_t rf_manuf; uint16_t rf_ver; uint8_t rf_rev; int rf_on; int txpower; int hwpctl; unsigned long nexttime; unsigned int chan; int txerrors; int (*attach)(struct bwn_mac *); void (*detach)(struct bwn_mac *); int (*prepare_hw)(struct bwn_mac *); void (*init_pre)(struct bwn_mac *); int (*init)(struct bwn_mac *); void (*exit)(struct bwn_mac *); uint16_t (*phy_read)(struct bwn_mac *, uint16_t); void (*phy_write)(struct bwn_mac *, uint16_t, uint16_t); void (*phy_maskset)(struct bwn_mac *, uint16_t, uint16_t, uint16_t); uint16_t (*rf_read)(struct bwn_mac *, uint16_t); void (*rf_write)(struct bwn_mac *, uint16_t, uint16_t); int (*use_hwpctl)(struct bwn_mac *); void (*rf_onoff)(struct bwn_mac *, int); void (*switch_analog)(struct bwn_mac *, int); int (*switch_channel)(struct bwn_mac *, unsigned int); uint32_t (*get_default_chan)(struct bwn_mac *); void (*set_antenna)(struct bwn_mac *, int); int (*set_im)(struct bwn_mac *, int); int (*recalc_txpwr)(struct bwn_mac *, int); void (*set_txpwr)(struct bwn_mac *); void (*task_15s)(struct bwn_mac *); void (*task_60s)(struct bwn_mac *); }; struct bwn_chan_band { uint32_t flags; uint8_t nchan; #define BWN_MAX_CHAN_PER_BAND 14 uint8_t chan[BWN_MAX_CHAN_PER_BAND]; }; #define BWN_NR_WMEPARAMS 16 enum { BWN_WMEPARAM_TXOP = 0, BWN_WMEPARAM_CWMIN, BWN_WMEPARAM_CWMAX, BWN_WMEPARAM_CWCUR, BWN_WMEPARAM_AIFS, BWN_WMEPARAM_BSLOTS, BWN_WMEPARAM_REGGAP, BWN_WMEPARAM_STATUS, }; #define BWN_WME_PARAMS(queue) \ (BWN_SHARED_EDCFQ + (BWN_NR_WMEPARAMS * sizeof(uint16_t) * (queue))) #define BWN_WME_BACKGROUND BWN_WME_PARAMS(0) #define BWN_WME_BESTEFFORT BWN_WME_PARAMS(1) #define BWN_WME_VIDEO BWN_WME_PARAMS(2) #define BWN_WME_VOICE BWN_WME_PARAMS(3) /* * Radio capture format. */ #define BWN_RX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ 0) struct bwn_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint64_t wr_tsf; u_int8_t wr_flags; u_int8_t wr_rate; u_int16_t wr_chan_freq; u_int16_t wr_chan_flags; int8_t wr_antsignal; int8_t wr_antnoise; u_int8_t wr_antenna; }; #define BWN_TX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ 0) struct bwn_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; u_int8_t wt_flags; u_int8_t wt_rate; u_int16_t wt_chan_freq; u_int16_t wt_chan_flags; u_int8_t wt_txpower; u_int8_t wt_antenna; }; struct bwn_stats { + int32_t rtsfail; + int32_t rts; int32_t link_noise; }; /* Noise Calculation (Link Quality) */ struct bwn_noise { uint8_t noi_running; uint8_t noi_nsamples; int8_t noi_samples[8][4]; }; #define BWN_DMA_30BIT 30 #define BWN_DMA_32BIT 32 #define BWN_DMA_64BIT 64 struct bwn_dmadesc_meta { bus_dmamap_t mt_dmap; bus_addr_t mt_paddr; struct mbuf *mt_m; struct ieee80211_node *mt_ni; uint8_t mt_txtype; #define BWN_DMADESC_METATYPE_HEADER 0 #define BWN_DMADESC_METATYPE_BODY 1 uint8_t mt_islast; }; #define BWN_DMAINTR_FATALMASK \ ((1 << 10) | (1 << 11) | (1 << 12) | (1 << 14) | (1 << 15)) #define BWN_DMAINTR_NONFATALMASK (1 << 13) #define BWN_DMAINTR_RX_DONE (1 << 16) #define BWN_DMA32_DCTL_BYTECNT 0x00001fff #define BWN_DMA32_DCTL_ADDREXT_MASK 0x00030000 #define BWN_DMA32_DCTL_ADDREXT_SHIFT 16 #define BWN_DMA32_DCTL_DTABLEEND 0x10000000 #define BWN_DMA32_DCTL_IRQ 0x20000000 #define BWN_DMA32_DCTL_FRAMEEND 0x40000000 #define BWN_DMA32_DCTL_FRAMESTART 0x80000000 struct bwn_dmadesc32 { uint32_t control; uint32_t address; } __packed; #define BWN_DMA64_DCTL0_DTABLEEND 0x10000000 #define BWN_DMA64_DCTL0_IRQ 0x20000000 #define BWN_DMA64_DCTL0_FRAMEEND 0x40000000 #define BWN_DMA64_DCTL0_FRAMESTART 0x80000000 #define BWN_DMA64_DCTL1_BYTECNT 0x00001fff #define BWN_DMA64_DCTL1_ADDREXT_MASK 0x00030000 #define BWN_DMA64_DCTL1_ADDREXT_SHIFT 16 struct bwn_dmadesc64 { uint32_t control0; uint32_t control1; uint32_t address_low; uint32_t address_high; } __packed; struct bwn_dmadesc_generic { union { struct bwn_dmadesc32 dma32; struct bwn_dmadesc64 dma64; } __packed dma; } __packed; struct bwn_dma_ring; struct bwn_dma_ring { struct bwn_mac *dr_mac; const struct bwn_dma_ops *dr_ops; struct bwn_dmadesc_meta *dr_meta; void *dr_txhdr_cache; bus_dma_tag_t dr_ring_dtag; bus_dma_tag_t dr_txring_dtag; bus_dmamap_t dr_spare_dmap; /* only for RX */ bus_dmamap_t dr_ring_dmap; bus_addr_t dr_txring_paddr; void *dr_ring_descbase; bus_addr_t dr_ring_dmabase; int dr_numslots; int dr_usedslot; int dr_curslot; uint32_t dr_frameoffset; uint16_t dr_rx_bufsize; uint16_t dr_base; int dr_index; uint8_t dr_tx; uint8_t dr_stop; int dr_type; void (*getdesc)(struct bwn_dma_ring *, int, struct bwn_dmadesc_generic **, struct bwn_dmadesc_meta **); void (*setdesc)(struct bwn_dma_ring *, struct bwn_dmadesc_generic *, bus_addr_t, uint16_t, int, int, int); void (*start_transfer)(struct bwn_dma_ring *, int); void (*suspend)(struct bwn_dma_ring *); void (*resume)(struct bwn_dma_ring *); int (*get_curslot)(struct bwn_dma_ring *); void (*set_curslot)(struct bwn_dma_ring *, int); }; struct bwn_dma { int dmatype; bus_dma_tag_t parent_dtag; bus_dma_tag_t rxbuf_dtag; bus_dma_tag_t txbuf_dtag; struct bwn_dma_ring *wme[5]; struct bwn_dma_ring *mcast; struct bwn_dma_ring *rx; uint64_t lastseq; /* XXX FIXME */ }; struct bwn_pio_rxqueue { struct bwn_mac *prq_mac; uint16_t prq_base; uint8_t prq_rev; }; struct bwn_pio_txqueue; struct bwn_pio_txpkt { struct bwn_pio_txqueue *tp_queue; struct ieee80211_node *tp_ni; struct mbuf *tp_m; uint8_t tp_index; TAILQ_ENTRY(bwn_pio_txpkt) tp_list; }; #define BWN_PIO_MAX_TXPACKETS 32 struct bwn_pio_txqueue { uint16_t tq_base; uint16_t tq_size; uint16_t tq_used; uint16_t tq_free; uint8_t tq_stop; uint8_t tq_index; struct bwn_pio_txpkt tq_pkts[BWN_PIO_MAX_TXPACKETS]; TAILQ_HEAD(, bwn_pio_txpkt) tq_pktlist; }; struct bwn_pio { struct bwn_pio_txqueue wme[5]; struct bwn_pio_txqueue mcast; struct bwn_pio_rxqueue rx; }; struct bwn_plcp4 { union { uint32_t data; uint8_t raw[4]; } __packed o; } __packed; struct bwn_plcp6 { union { uint32_t data; uint8_t raw[6]; } __packed o; } __packed; struct bwn_txhdr { uint32_t macctl; uint8_t macfc[2]; uint16_t tx_festime; uint16_t phyctl; uint16_t phyctl_1; uint16_t phyctl_1fb; uint16_t phyctl_1rts; uint16_t phyctl_1rtsfb; uint8_t phyrate; uint8_t phyrate_rts; uint8_t eftypes; /* extra frame types */ uint8_t chan; uint8_t iv[16]; uint8_t addr1[IEEE80211_ADDR_LEN]; uint16_t tx_festime_fb; struct bwn_plcp6 rts_plcp_fb; uint16_t rts_dur_fb; struct bwn_plcp6 plcp_fb; uint16_t dur_fb; uint16_t mimo_modelen; uint16_t mimo_ratelen_fb; uint32_t timeout; union { /* format <= r351 */ struct { uint8_t pad0[2]; uint16_t cookie; uint16_t tx_status; struct bwn_plcp6 rts_plcp; uint8_t rts_frame[16]; uint8_t pad1[2];; struct bwn_plcp6 plcp; } __packed old; /* format > r410 */ struct { uint16_t mimo_antenna; uint16_t preload_size; uint8_t pad0[2]; uint16_t cookie; uint16_t tx_status; struct bwn_plcp6 rts_plcp; uint8_t rts_frame[16]; uint8_t pad1[2]; struct bwn_plcp6 plcp; } __packed new; } __packed body; } __packed; #define BWN_FWTYPE_UCODE 'u' #define BWN_FWTYPE_PCM 'p' #define BWN_FWTYPE_IV 'i' struct bwn_fwhdr { uint8_t type; uint8_t ver; uint8_t pad[2]; uint32_t size; } __packed; #define BWN_FWINITVALS_OFFSET_MASK 0x7fff #define BWN_FWINITVALS_32BIT 0x8000 struct bwn_fwinitvals { uint16_t offset_size; union { uint16_t d16; uint32_t d32; } __packed data; } __packed; enum bwn_fwtype { BWN_FWTYPE_DEFAULT, BWN_FWTYPE_OPENSOURCE, BWN_NR_FWTYPES, }; struct bwn_fwfile { const char *filename; const struct firmware *fw; enum bwn_fwtype type; }; struct bwn_key { void *keyconf; uint8_t algorithm; }; struct bwn_fw { struct bwn_fwfile ucode; struct bwn_fwfile pcm; struct bwn_fwfile initvals; struct bwn_fwfile initvals_band; uint16_t rev; uint16_t patch; uint8_t opensource; uint8_t no_pcmfile; }; struct bwn_lo_g_sm { int curstate; int nmeasure; int multipler; uint16_t feedth; struct bwn_loctl loctl; }; struct bwn_lo_g_value { uint8_t old_channel; uint16_t phy_lomask; uint16_t phy_extg; uint16_t phy_dacctl_hwpctl; uint16_t phy_dacctl; uint16_t phy_hpwr_tssictl; uint16_t phy_analogover; uint16_t phy_analogoverval; uint16_t phy_rfover; uint16_t phy_rfoverval; uint16_t phy_classctl; uint16_t phy_crs0; uint16_t phy_pgactl; uint16_t phy_syncctl; uint16_t phy_cck0; uint16_t phy_cck1; uint16_t phy_cck2; uint16_t phy_cck3; uint16_t phy_cck4; uint16_t reg0; uint16_t reg1; uint16_t rf0; uint16_t rf1; uint16_t rf2; }; #define BWN_LED_MAX 4 #define BWN_LED_EVENT_NONE -1 #define BWN_LED_EVENT_POLL 0 #define BWN_LED_EVENT_TX 1 #define BWN_LED_EVENT_RX 2 #define BWN_LED_SLOWDOWN(dur) (dur) = (((dur) * 3) / 2) struct bwn_led { uint8_t led_flags; /* BWN_LED_F_ */ uint8_t led_act; /* BWN_LED_ACT_ */ uint8_t led_mask; }; #define BWN_LED_F_ACTLOW 0x1 #define BWN_LED_F_BLINK 0x2 #define BWN_LED_F_POLLABLE 0x4 #define BWN_LED_F_SLOW 0x8 struct bwn_mac { struct bwn_softc *mac_sc; struct siba_dev_softc *mac_sd; unsigned mac_status; #define BWN_MAC_STATUS_UNINIT 0 #define BWN_MAC_STATUS_INITED 1 #define BWN_MAC_STATUS_STARTED 2 unsigned mac_flags; /* use "Bad Frames Preemption" */ #define BWN_MAC_FLAG_BADFRAME_PREEMP (1 << 0) #define BWN_MAC_FLAG_DFQVALID (1 << 1) #define BWN_MAC_FLAG_RADIO_ON (1 << 2) #define BWN_MAC_FLAG_DMA (1 << 3) #define BWN_MAC_FLAG_WME (1 << 4) #define BWN_MAC_FLAG_HWCRYPTO (1 << 5) struct resource_spec *mac_intr_spec; #define BWN_MSI_MESSAGES 1 struct resource *mac_res_irq[BWN_MSI_MESSAGES]; void *mac_intrhand[BWN_MSI_MESSAGES]; int mac_msi; struct bwn_noise mac_noise; struct bwn_phy mac_phy; struct bwn_stats mac_stats; uint32_t mac_reason_intr; uint32_t mac_reason[6]; uint32_t mac_intr_mask; int mac_suspended; struct bwn_fw mac_fw; union { struct bwn_dma dma; struct bwn_pio pio; } mac_method; uint16_t mac_ktp; /* Key table pointer */ uint8_t mac_max_nr_keys; struct bwn_key mac_key[58]; unsigned int mac_task_state; struct task mac_intrtask; struct task mac_hwreset; struct task mac_txpower; TAILQ_ENTRY(bwn_mac) mac_list; }; struct bwn_node { struct ieee80211_node bn_node; /* must be the first */ struct ieee80211_amrr_node bn_amn; }; #define BWN_NODE(ni) ((struct bwn_node *)(ni)) /* * Driver-specific vap state. */ struct bwn_vap { struct ieee80211vap bv_vap; /* base class */ struct ieee80211_amrr bv_amrr; int (*bv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define BWN_VAP(vap) ((struct bwn_vap *)(vap)) #define BWN_VAP_CONST(vap) ((const struct mwl_vap *)(vap)) struct bwn_softc { device_t sc_dev; struct siba_dev_softc *sc_sd; struct mtx sc_mtx; struct ifnet *sc_ifp; unsigned sc_flags; #define BWN_FLAG_ATTACHED (1 << 0) #define BWN_FLAG_INVALID (1 << 1) #define BWN_FLAG_NEED_BEACON_TP (1 << 2) unsigned sc_debug; struct bwn_mac *sc_curmac; TAILQ_HEAD(, bwn_mac) sc_maclist; uint8_t sc_macaddr[IEEE80211_ADDR_LEN]; uint8_t sc_bssid[IEEE80211_ADDR_LEN]; unsigned int sc_filters; uint8_t sc_beacons[2]; uint8_t sc_rf_enabled; struct wmeParams sc_wmeParams[4]; struct callout sc_rfswitch_ch; /* for laptop */ struct callout sc_task_ch; struct callout sc_watchdog_ch; int sc_watchdog_timer; struct taskqueue *sc_tq; /* private task queue */ int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); void (*sc_node_cleanup)( struct ieee80211_node *); int sc_rx_rate; int sc_tx_rate; int sc_led_blinking; int sc_led_ticks; struct bwn_led *sc_blink_led; struct callout sc_led_blink_ch; int sc_led_blink_offdur; struct bwn_led sc_leds[BWN_LED_MAX]; int sc_led_idle; int sc_led_blink; struct bwn_tx_radiotap_header sc_tx_th; struct bwn_rx_radiotap_header sc_rx_th; }; #define BWN_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define BWN_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define BWN_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define BWN_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #define BWN_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) #endif /* !_IF_BWNVAR_H */ Index: projects/ppc64/sys/dev/cxgb/cxgb_sge.c =================================================================== --- projects/ppc64/sys/dev/cxgb/cxgb_sge.c (revision 204271) +++ projects/ppc64/sys/dev/cxgb/cxgb_sge.c (revision 204272) @@ -1,3839 +1,3825 @@ /************************************************************************** Copyright (c) 2007-2009, Chelsio 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. 2. Neither the name of the Chelsio Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 #include #include #include #include #include #include #include #include int txq_fills = 0; int multiq_tx_enable = 1; extern struct sysctl_oid_list sysctl__hw_cxgb_children; int cxgb_txq_buf_ring_size = TX_ETH_Q_SIZE; TUNABLE_INT("hw.cxgb.txq_mr_size", &cxgb_txq_buf_ring_size); SYSCTL_UINT(_hw_cxgb, OID_AUTO, txq_mr_size, CTLFLAG_RDTUN, &cxgb_txq_buf_ring_size, 0, "size of per-queue mbuf ring"); static int cxgb_tx_coalesce_force = 0; TUNABLE_INT("hw.cxgb.tx_coalesce_force", &cxgb_tx_coalesce_force); SYSCTL_UINT(_hw_cxgb, OID_AUTO, tx_coalesce_force, CTLFLAG_RW, &cxgb_tx_coalesce_force, 0, "coalesce small packets into a single work request regardless of ring state"); #define COALESCE_START_DEFAULT TX_ETH_Q_SIZE>>1 #define COALESCE_START_MAX (TX_ETH_Q_SIZE-(TX_ETH_Q_SIZE>>3)) #define COALESCE_STOP_DEFAULT TX_ETH_Q_SIZE>>2 #define COALESCE_STOP_MIN TX_ETH_Q_SIZE>>5 #define TX_RECLAIM_DEFAULT TX_ETH_Q_SIZE>>5 #define TX_RECLAIM_MAX TX_ETH_Q_SIZE>>2 #define TX_RECLAIM_MIN TX_ETH_Q_SIZE>>6 static int cxgb_tx_coalesce_enable_start = COALESCE_START_DEFAULT; TUNABLE_INT("hw.cxgb.tx_coalesce_enable_start", &cxgb_tx_coalesce_enable_start); SYSCTL_UINT(_hw_cxgb, OID_AUTO, tx_coalesce_enable_start, CTLFLAG_RW, &cxgb_tx_coalesce_enable_start, 0, "coalesce enable threshold"); static int cxgb_tx_coalesce_enable_stop = COALESCE_STOP_DEFAULT; TUNABLE_INT("hw.cxgb.tx_coalesce_enable_stop", &cxgb_tx_coalesce_enable_stop); SYSCTL_UINT(_hw_cxgb, OID_AUTO, tx_coalesce_enable_stop, CTLFLAG_RW, &cxgb_tx_coalesce_enable_stop, 0, "coalesce disable threshold"); static int cxgb_tx_reclaim_threshold = TX_RECLAIM_DEFAULT; TUNABLE_INT("hw.cxgb.tx_reclaim_threshold", &cxgb_tx_reclaim_threshold); SYSCTL_UINT(_hw_cxgb, OID_AUTO, tx_reclaim_threshold, CTLFLAG_RW, &cxgb_tx_reclaim_threshold, 0, "tx cleaning minimum threshold"); /* * XXX don't re-enable this until TOE stops assuming * we have an m_ext */ static int recycle_enable = 0; int cxgb_ext_freed = 0; int cxgb_ext_inited = 0; int fl_q_size = 0; int jumbo_q_size = 0; extern int cxgb_use_16k_clusters; extern int nmbjumbo4; extern int nmbjumbo9; extern int nmbjumbo16; #define USE_GTS 0 #define SGE_RX_SM_BUF_SIZE 1536 #define SGE_RX_DROP_THRES 16 #define SGE_RX_COPY_THRES 128 /* * Period of the Tx buffer reclaim timer. This timer does not need to run * frequently as Tx buffers are usually reclaimed by new Tx packets. */ #define TX_RECLAIM_PERIOD (hz >> 1) /* * Values for sge_txq.flags */ enum { TXQ_RUNNING = 1 << 0, /* fetch engine is running */ TXQ_LAST_PKT_DB = 1 << 1, /* last packet rang the doorbell */ }; struct tx_desc { uint64_t flit[TX_DESC_FLITS]; } __packed; struct rx_desc { uint32_t addr_lo; uint32_t len_gen; uint32_t gen2; uint32_t addr_hi; } __packed; struct rsp_desc { /* response queue descriptor */ struct rss_header rss_hdr; uint32_t flags; uint32_t len_cq; uint8_t imm_data[47]; uint8_t intr_gen; } __packed; #define RX_SW_DESC_MAP_CREATED (1 << 0) #define TX_SW_DESC_MAP_CREATED (1 << 1) #define RX_SW_DESC_INUSE (1 << 3) #define TX_SW_DESC_MAPPED (1 << 4) #define RSPQ_NSOP_NEOP G_RSPD_SOP_EOP(0) #define RSPQ_EOP G_RSPD_SOP_EOP(F_RSPD_EOP) #define RSPQ_SOP G_RSPD_SOP_EOP(F_RSPD_SOP) #define RSPQ_SOP_EOP G_RSPD_SOP_EOP(F_RSPD_SOP|F_RSPD_EOP) struct tx_sw_desc { /* SW state per Tx descriptor */ struct mbuf *m; bus_dmamap_t map; int flags; }; struct rx_sw_desc { /* SW state per Rx descriptor */ caddr_t rxsd_cl; struct mbuf *m; bus_dmamap_t map; int flags; }; struct txq_state { unsigned int compl; unsigned int gen; unsigned int pidx; }; struct refill_fl_cb_arg { int error; bus_dma_segment_t seg; int nseg; }; /* * Maps a number of flits to the number of Tx descriptors that can hold them. * The formula is * * desc = 1 + (flits - 2) / (WR_FLITS - 1). * * HW allows up to 4 descriptors to be combined into a WR. */ static uint8_t flit_desc_map[] = { 0, #if SGE_NUM_GENBITS == 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 #elif SGE_NUM_GENBITS == 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, #else # error "SGE_NUM_GENBITS must be 1 or 2" #endif }; #define TXQ_LOCK_ASSERT(qs) mtx_assert(&(qs)->lock, MA_OWNED) #define TXQ_TRYLOCK(qs) mtx_trylock(&(qs)->lock) #define TXQ_LOCK(qs) mtx_lock(&(qs)->lock) #define TXQ_UNLOCK(qs) mtx_unlock(&(qs)->lock) #define TXQ_RING_EMPTY(qs) drbr_empty((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) #define TXQ_RING_NEEDS_ENQUEUE(qs) \ drbr_needs_enqueue((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) #define TXQ_RING_FLUSH(qs) drbr_flush((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) #define TXQ_RING_DEQUEUE_COND(qs, func, arg) \ drbr_dequeue_cond((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr, func, arg) #define TXQ_RING_DEQUEUE(qs) \ drbr_dequeue((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) int cxgb_debug = 0; static void sge_timer_cb(void *arg); static void sge_timer_reclaim(void *arg, int ncount); static void sge_txq_reclaim_handler(void *arg, int ncount); static void cxgb_start_locked(struct sge_qset *qs); /* * XXX need to cope with bursty scheduling by looking at a wider * window than we are now for determining the need for coalescing * */ static __inline uint64_t check_pkt_coalesce(struct sge_qset *qs) { struct adapter *sc; struct sge_txq *txq; uint8_t *fill; if (__predict_false(cxgb_tx_coalesce_force)) return (1); txq = &qs->txq[TXQ_ETH]; sc = qs->port->adapter; fill = &sc->tunq_fill[qs->idx]; if (cxgb_tx_coalesce_enable_start > COALESCE_START_MAX) cxgb_tx_coalesce_enable_start = COALESCE_START_MAX; if (cxgb_tx_coalesce_enable_stop < COALESCE_STOP_MIN) cxgb_tx_coalesce_enable_start = COALESCE_STOP_MIN; /* * if the hardware transmit queue is more than 1/8 full * we mark it as coalescing - we drop back from coalescing * when we go below 1/32 full and there are no packets enqueued, * this provides us with some degree of hysteresis */ if (*fill != 0 && (txq->in_use <= cxgb_tx_coalesce_enable_stop) && TXQ_RING_EMPTY(qs) && (qs->coalescing == 0)) *fill = 0; else if (*fill == 0 && (txq->in_use >= cxgb_tx_coalesce_enable_start)) *fill = 1; return (sc->tunq_coalesce); } #ifdef __LP64__ static void set_wr_hdr(struct work_request_hdr *wrp, uint32_t wr_hi, uint32_t wr_lo) { uint64_t wr_hilo; #if _BYTE_ORDER == _LITTLE_ENDIAN wr_hilo = wr_hi; wr_hilo |= (((uint64_t)wr_lo)<<32); #else wr_hilo = wr_lo; wr_hilo |= (((uint64_t)wr_hi)<<32); #endif wrp->wrh_hilo = wr_hilo; } #else static void set_wr_hdr(struct work_request_hdr *wrp, uint32_t wr_hi, uint32_t wr_lo) { wrp->wrh_hi = wr_hi; wmb(); wrp->wrh_lo = wr_lo; } #endif struct coalesce_info { int count; int nbytes; }; static int coalesce_check(struct mbuf *m, void *arg) { struct coalesce_info *ci = arg; int *count = &ci->count; int *nbytes = &ci->nbytes; if ((*nbytes == 0) || ((*nbytes + m->m_len <= 10500) && (*count < 7) && (m->m_next == NULL))) { *count += 1; *nbytes += m->m_len; return (1); } return (0); } static struct mbuf * cxgb_dequeue(struct sge_qset *qs) { struct mbuf *m, *m_head, *m_tail; struct coalesce_info ci; if (check_pkt_coalesce(qs) == 0) return TXQ_RING_DEQUEUE(qs); m_head = m_tail = NULL; ci.count = ci.nbytes = 0; do { m = TXQ_RING_DEQUEUE_COND(qs, coalesce_check, &ci); if (m_head == NULL) { m_tail = m_head = m; } else if (m != NULL) { m_tail->m_nextpkt = m; m_tail = m; } } while (m != NULL); if (ci.count > 7) panic("trying to coalesce %d packets in to one WR", ci.count); return (m_head); } /** * reclaim_completed_tx - reclaims completed Tx descriptors * @adapter: the adapter * @q: the Tx queue to reclaim completed descriptors from * * Reclaims Tx descriptors that the SGE has indicated it has processed, * and frees the associated buffers if possible. Called with the Tx * queue's lock held. */ static __inline int reclaim_completed_tx(struct sge_qset *qs, int reclaim_min, int queue) { struct sge_txq *q = &qs->txq[queue]; int reclaim = desc_reclaimable(q); if ((cxgb_tx_reclaim_threshold > TX_RECLAIM_MAX) || (cxgb_tx_reclaim_threshold < TX_RECLAIM_MIN)) cxgb_tx_reclaim_threshold = TX_RECLAIM_DEFAULT; if (reclaim < reclaim_min) return (0); mtx_assert(&qs->lock, MA_OWNED); if (reclaim > 0) { t3_free_tx_desc(qs, reclaim, queue); q->cleaned += reclaim; q->in_use -= reclaim; } if (isset(&qs->txq_stopped, TXQ_ETH)) clrbit(&qs->txq_stopped, TXQ_ETH); return (reclaim); } /** * should_restart_tx - are there enough resources to restart a Tx queue? * @q: the Tx queue * * Checks if there are enough descriptors to restart a suspended Tx queue. */ static __inline int should_restart_tx(const struct sge_txq *q) { unsigned int r = q->processed - q->cleaned; return q->in_use - r < (q->size >> 1); } /** * t3_sge_init - initialize SGE * @adap: the adapter * @p: the SGE parameters * * Performs SGE initialization needed every time after a chip reset. * We do not initialize any of the queue sets here, instead the driver * top-level must request those individually. We also do not enable DMA * here, that should be done after the queues have been set up. */ void t3_sge_init(adapter_t *adap, struct sge_params *p) { u_int ctrl, ups; ups = 0; /* = ffs(pci_resource_len(adap->pdev, 2) >> 12); */ ctrl = F_DROPPKT | V_PKTSHIFT(2) | F_FLMODE | F_AVOIDCQOVFL | F_CQCRDTCTRL | F_CONGMODE | F_TNLFLMODE | F_FATLPERREN | V_HOSTPAGESIZE(PAGE_SHIFT - 11) | F_BIGENDIANINGRESS | V_USERSPACESIZE(ups ? ups - 1 : 0) | F_ISCSICOALESCING; #if SGE_NUM_GENBITS == 1 ctrl |= F_EGRGENCTRL; #endif if (adap->params.rev > 0) { if (!(adap->flags & (USING_MSIX | USING_MSI))) ctrl |= F_ONEINTMULTQ | F_OPTONEINTMULTQ; } t3_write_reg(adap, A_SG_CONTROL, ctrl); t3_write_reg(adap, A_SG_EGR_RCQ_DRB_THRSH, V_HIRCQDRBTHRSH(512) | V_LORCQDRBTHRSH(512)); t3_write_reg(adap, A_SG_TIMER_TICK, core_ticks_per_usec(adap) / 10); t3_write_reg(adap, A_SG_CMDQ_CREDIT_TH, V_THRESHOLD(32) | V_TIMEOUT(200 * core_ticks_per_usec(adap))); t3_write_reg(adap, A_SG_HI_DRB_HI_THRSH, adap->params.rev < T3_REV_C ? 1000 : 500); t3_write_reg(adap, A_SG_HI_DRB_LO_THRSH, 256); t3_write_reg(adap, A_SG_LO_DRB_HI_THRSH, 1000); t3_write_reg(adap, A_SG_LO_DRB_LO_THRSH, 256); t3_write_reg(adap, A_SG_OCO_BASE, V_BASE1(0xfff)); t3_write_reg(adap, A_SG_DRB_PRI_THRESH, 63 * 1024); } /** * sgl_len - calculates the size of an SGL of the given capacity * @n: the number of SGL entries * * Calculates the number of flits needed for a scatter/gather list that * can hold the given number of entries. */ static __inline unsigned int sgl_len(unsigned int n) { return ((3 * n) / 2 + (n & 1)); } /** * get_imm_packet - return the next ingress packet buffer from a response * @resp: the response descriptor containing the packet data * * Return a packet containing the immediate data of the given response. */ static int get_imm_packet(adapter_t *sc, const struct rsp_desc *resp, struct mbuf *m) { m->m_len = m->m_pkthdr.len = IMMED_PKT_SIZE; m->m_ext.ext_buf = NULL; m->m_ext.ext_type = 0; memcpy(mtod(m, uint8_t *), resp->imm_data, IMMED_PKT_SIZE); return (0); } static __inline u_int flits_to_desc(u_int n) { return (flit_desc_map[n]); } #define SGE_PARERR (F_CPPARITYERROR | F_OCPARITYERROR | F_RCPARITYERROR | \ F_IRPARITYERROR | V_ITPARITYERROR(M_ITPARITYERROR) | \ V_FLPARITYERROR(M_FLPARITYERROR) | F_LODRBPARITYERROR | \ F_HIDRBPARITYERROR | F_LORCQPARITYERROR | \ F_HIRCQPARITYERROR) #define SGE_FRAMINGERR (F_UC_REQ_FRAMINGERROR | F_R_REQ_FRAMINGERROR) #define SGE_FATALERR (SGE_PARERR | SGE_FRAMINGERR | F_RSPQCREDITOVERFOW | \ F_RSPQDISABLED) /** * t3_sge_err_intr_handler - SGE async event interrupt handler * @adapter: the adapter * * Interrupt handler for SGE asynchronous (non-data) events. */ void t3_sge_err_intr_handler(adapter_t *adapter) { unsigned int v, status; status = t3_read_reg(adapter, A_SG_INT_CAUSE); if (status & SGE_PARERR) CH_ALERT(adapter, "SGE parity error (0x%x)\n", status & SGE_PARERR); if (status & SGE_FRAMINGERR) CH_ALERT(adapter, "SGE framing error (0x%x)\n", status & SGE_FRAMINGERR); if (status & F_RSPQCREDITOVERFOW) CH_ALERT(adapter, "SGE response queue credit overflow\n"); if (status & F_RSPQDISABLED) { v = t3_read_reg(adapter, A_SG_RSPQ_FL_STATUS); CH_ALERT(adapter, "packet delivered to disabled response queue (0x%x)\n", (v >> S_RSPQ0DISABLED) & 0xff); } t3_write_reg(adapter, A_SG_INT_CAUSE, status); if (status & SGE_FATALERR) t3_fatal_err(adapter); } void t3_sge_prep(adapter_t *adap, struct sge_params *p) { int i, nqsets; nqsets = min(SGE_QSETS, mp_ncpus*4); fl_q_size = min(nmbclusters/(3*nqsets), FL_Q_SIZE); while (!powerof2(fl_q_size)) fl_q_size--; #if __FreeBSD_version >= 700111 if (cxgb_use_16k_clusters) jumbo_q_size = min(nmbjumbo16/(3*nqsets), JUMBO_Q_SIZE); else jumbo_q_size = min(nmbjumbo9/(3*nqsets), JUMBO_Q_SIZE); #else jumbo_q_size = min(nmbjumbo4/(3*nqsets), JUMBO_Q_SIZE); #endif while (!powerof2(jumbo_q_size)) jumbo_q_size--; if (fl_q_size < (FL_Q_SIZE / 4) || jumbo_q_size < (JUMBO_Q_SIZE / 2)) device_printf(adap->dev, "Insufficient clusters and/or jumbo buffers.\n"); /* XXX Does ETHER_ALIGN need to be accounted for here? */ p->max_pkt_size = adap->sge.qs[0].fl[1].buf_size - sizeof(struct cpl_rx_data); for (i = 0; i < SGE_QSETS; ++i) { struct qset_params *q = p->qset + i; if (adap->params.nports > 2) { q->coalesce_usecs = 50; } else { #ifdef INVARIANTS q->coalesce_usecs = 10; #else q->coalesce_usecs = 5; #endif } q->polling = 0; q->rspq_size = RSPQ_Q_SIZE; q->fl_size = fl_q_size; q->jumbo_size = jumbo_q_size; q->txq_size[TXQ_ETH] = TX_ETH_Q_SIZE; q->txq_size[TXQ_OFLD] = 1024; q->txq_size[TXQ_CTRL] = 256; q->cong_thres = 0; } } int t3_sge_alloc(adapter_t *sc) { /* The parent tag. */ if (bus_dma_tag_create( NULL, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 0, /* flags */ NULL, NULL, /* lock, lockarg */ &sc->parent_dmat)) { device_printf(sc->dev, "Cannot allocate parent DMA tag\n"); return (ENOMEM); } /* * DMA tag for normal sized RX frames */ if (bus_dma_tag_create(sc->parent_dmat, MCLBYTES, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->rx_dmat)) { device_printf(sc->dev, "Cannot allocate RX DMA tag\n"); return (ENOMEM); } /* * DMA tag for jumbo sized RX frames. */ if (bus_dma_tag_create(sc->parent_dmat, MJUM16BYTES, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MJUM16BYTES, 1, MJUM16BYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->rx_jumbo_dmat)) { device_printf(sc->dev, "Cannot allocate RX jumbo DMA tag\n"); return (ENOMEM); } /* * DMA tag for TX frames. */ if (bus_dma_tag_create(sc->parent_dmat, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, TX_MAX_SIZE, TX_MAX_SEGS, TX_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->tx_dmat)) { device_printf(sc->dev, "Cannot allocate TX DMA tag\n"); return (ENOMEM); } return (0); } int t3_sge_free(struct adapter * sc) { if (sc->tx_dmat != NULL) bus_dma_tag_destroy(sc->tx_dmat); if (sc->rx_jumbo_dmat != NULL) bus_dma_tag_destroy(sc->rx_jumbo_dmat); if (sc->rx_dmat != NULL) bus_dma_tag_destroy(sc->rx_dmat); if (sc->parent_dmat != NULL) bus_dma_tag_destroy(sc->parent_dmat); return (0); } void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p) { qs->rspq.holdoff_tmr = max(p->coalesce_usecs * 10, 1U); qs->rspq.polling = 0 /* p->polling */; } #if !defined(__i386__) && !defined(__amd64__) static void refill_fl_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct refill_fl_cb_arg *cb_arg = arg; cb_arg->error = error; cb_arg->seg = segs[0]; cb_arg->nseg = nseg; } #endif /** * refill_fl - refill an SGE free-buffer list * @sc: the controller softc * @q: the free-list to refill * @n: the number of new buffers to allocate * * (Re)populate an SGE free-buffer list with up to @n new packet buffers. * The caller must assure that @n does not exceed the queue's capacity. */ static void refill_fl(adapter_t *sc, struct sge_fl *q, int n) { struct rx_sw_desc *sd = &q->sdesc[q->pidx]; struct rx_desc *d = &q->desc[q->pidx]; struct refill_fl_cb_arg cb_arg; struct mbuf *m; caddr_t cl; int err, count = 0; cb_arg.error = 0; while (n--) { /* * We only allocate a cluster, mbuf allocation happens after rx */ if (q->zone == zone_pack) { if ((m = m_getcl(M_NOWAIT, MT_NOINIT, M_PKTHDR)) == NULL) break; cl = m->m_ext.ext_buf; } else { if ((cl = m_cljget(NULL, M_NOWAIT, q->buf_size)) == NULL) break; if ((m = m_gethdr(M_NOWAIT, MT_NOINIT)) == NULL) { uma_zfree(q->zone, cl); break; } } if ((sd->flags & RX_SW_DESC_MAP_CREATED) == 0) { if ((err = bus_dmamap_create(q->entry_tag, 0, &sd->map))) { log(LOG_WARNING, "bus_dmamap_create failed %d\n", err); uma_zfree(q->zone, cl); goto done; } sd->flags |= RX_SW_DESC_MAP_CREATED; } #if !defined(__i386__) && !defined(__amd64__) err = bus_dmamap_load(q->entry_tag, sd->map, cl, q->buf_size, refill_fl_cb, &cb_arg, 0); if (err != 0 || cb_arg.error) { if (q->zone == zone_pack) uma_zfree(q->zone, cl); m_free(m); goto done; } #else cb_arg.seg.ds_addr = pmap_kextract((vm_offset_t)cl); #endif sd->flags |= RX_SW_DESC_INUSE; sd->rxsd_cl = cl; sd->m = m; d->addr_lo = htobe32(cb_arg.seg.ds_addr & 0xffffffff); d->addr_hi = htobe32(((uint64_t)cb_arg.seg.ds_addr >>32) & 0xffffffff); d->len_gen = htobe32(V_FLD_GEN1(q->gen)); d->gen2 = htobe32(V_FLD_GEN2(q->gen)); d++; sd++; if (++q->pidx == q->size) { q->pidx = 0; q->gen ^= 1; sd = q->sdesc; d = q->desc; } q->credits++; count++; } done: if (count) t3_write_reg(sc, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id)); } /** * free_rx_bufs - free the Rx buffers on an SGE free list * @sc: the controle softc * @q: the SGE free list to clean up * * Release the buffers on an SGE free-buffer Rx queue. HW fetching from * this queue should be stopped before calling this function. */ static void free_rx_bufs(adapter_t *sc, struct sge_fl *q) { u_int cidx = q->cidx; while (q->credits--) { struct rx_sw_desc *d = &q->sdesc[cidx]; if (d->flags & RX_SW_DESC_INUSE) { bus_dmamap_unload(q->entry_tag, d->map); bus_dmamap_destroy(q->entry_tag, d->map); if (q->zone == zone_pack) { m_init(d->m, zone_pack, MCLBYTES, M_NOWAIT, MT_DATA, M_EXT); uma_zfree(zone_pack, d->m); } else { m_init(d->m, zone_mbuf, MLEN, M_NOWAIT, MT_DATA, 0); uma_zfree(zone_mbuf, d->m); uma_zfree(q->zone, d->rxsd_cl); } } d->rxsd_cl = NULL; d->m = NULL; if (++cidx == q->size) cidx = 0; } } static __inline void __refill_fl(adapter_t *adap, struct sge_fl *fl) { refill_fl(adap, fl, min(16U, fl->size - fl->credits)); } static __inline void __refill_fl_lt(adapter_t *adap, struct sge_fl *fl, int max) { if ((fl->size - fl->credits) < max) refill_fl(adap, fl, min(max, fl->size - fl->credits)); } /** * recycle_rx_buf - recycle a receive buffer * @adapter: the adapter * @q: the SGE free list * @idx: index of buffer to recycle * * Recycles the specified buffer on the given free list by adding it at * the next available slot on the list. */ static void recycle_rx_buf(adapter_t *adap, struct sge_fl *q, unsigned int idx) { struct rx_desc *from = &q->desc[idx]; struct rx_desc *to = &q->desc[q->pidx]; q->sdesc[q->pidx] = q->sdesc[idx]; to->addr_lo = from->addr_lo; // already big endian to->addr_hi = from->addr_hi; // likewise wmb(); /* necessary ? */ to->len_gen = htobe32(V_FLD_GEN1(q->gen)); to->gen2 = htobe32(V_FLD_GEN2(q->gen)); q->credits++; if (++q->pidx == q->size) { q->pidx = 0; q->gen ^= 1; } t3_write_reg(adap, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id)); } static void alloc_ring_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { uint32_t *addr; addr = arg; *addr = segs[0].ds_addr; } static int alloc_ring(adapter_t *sc, size_t nelem, size_t elem_size, size_t sw_size, bus_addr_t *phys, void *desc, void *sdesc, bus_dma_tag_t *tag, bus_dmamap_t *map, bus_dma_tag_t parent_entry_tag, bus_dma_tag_t *entry_tag) { size_t len = nelem * elem_size; void *s = NULL; void *p = NULL; int err; if ((err = bus_dma_tag_create(sc->parent_dmat, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, len, 1, len, 0, NULL, NULL, tag)) != 0) { device_printf(sc->dev, "Cannot allocate descriptor tag\n"); return (ENOMEM); } if ((err = bus_dmamem_alloc(*tag, (void **)&p, BUS_DMA_NOWAIT, map)) != 0) { device_printf(sc->dev, "Cannot allocate descriptor memory\n"); return (ENOMEM); } bus_dmamap_load(*tag, *map, p, len, alloc_ring_cb, phys, 0); bzero(p, len); *(void **)desc = p; if (sw_size) { len = nelem * sw_size; s = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO); *(void **)sdesc = s; } if (parent_entry_tag == NULL) return (0); if ((err = bus_dma_tag_create(parent_entry_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, TX_MAX_SIZE, TX_MAX_SEGS, TX_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, entry_tag)) != 0) { device_printf(sc->dev, "Cannot allocate descriptor entry tag\n"); return (ENOMEM); } return (0); } static void sge_slow_intr_handler(void *arg, int ncount) { adapter_t *sc = arg; t3_slow_intr_handler(sc); } /** * sge_timer_cb - perform periodic maintenance of an SGE qset * @data: the SGE queue set to maintain * * Runs periodically from a timer to perform maintenance of an SGE queue * set. It performs two tasks: * * a) Cleans up any completed Tx descriptors that may still be pending. * Normal descriptor cleanup happens when new packets are added to a Tx * queue so this timer is relatively infrequent and does any cleanup only * if the Tx queue has not seen any new packets in a while. We make a * best effort attempt to reclaim descriptors, in that we don't wait * around if we cannot get a queue's lock (which most likely is because * someone else is queueing new packets and so will also handle the clean * up). Since control queues use immediate data exclusively we don't * bother cleaning them up here. * * b) Replenishes Rx queues that have run out due to memory shortage. * Normally new Rx buffers are added when existing ones are consumed but * when out of memory a queue can become empty. We try to add only a few * buffers here, the queue will be replenished fully as these new buffers * are used up if memory shortage has subsided. * * c) Return coalesced response queue credits in case a response queue is * starved. * * d) Ring doorbells for T304 tunnel queues since we have seen doorbell * fifo overflows and the FW doesn't implement any recovery scheme yet. */ static void sge_timer_cb(void *arg) { adapter_t *sc = arg; if ((sc->flags & USING_MSIX) == 0) { struct port_info *pi; struct sge_qset *qs; struct sge_txq *txq; int i, j; int reclaim_ofl, refill_rx; if (sc->open_device_map == 0) return; for (i = 0; i < sc->params.nports; i++) { pi = &sc->port[i]; for (j = 0; j < pi->nqsets; j++) { qs = &sc->sge.qs[pi->first_qset + j]; txq = &qs->txq[0]; reclaim_ofl = txq[TXQ_OFLD].processed - txq[TXQ_OFLD].cleaned; refill_rx = ((qs->fl[0].credits < qs->fl[0].size) || (qs->fl[1].credits < qs->fl[1].size)); if (reclaim_ofl || refill_rx) { taskqueue_enqueue(sc->tq, &pi->timer_reclaim_task); break; } } } } if (sc->params.nports > 2) { int i; for_each_port(sc, i) { struct port_info *pi = &sc->port[i]; t3_write_reg(sc, A_SG_KDOORBELL, F_SELEGRCNTX | (FW_TUNNEL_SGEEC_START + pi->first_qset)); } } if (((sc->flags & USING_MSIX) == 0 || sc->params.nports > 2) && sc->open_device_map != 0) callout_reset(&sc->sge_timer_ch, TX_RECLAIM_PERIOD, sge_timer_cb, sc); } /* * This is meant to be a catch-all function to keep sge state private * to sge.c * */ int t3_sge_init_adapter(adapter_t *sc) { callout_init(&sc->sge_timer_ch, CALLOUT_MPSAFE); callout_reset(&sc->sge_timer_ch, TX_RECLAIM_PERIOD, sge_timer_cb, sc); TASK_INIT(&sc->slow_intr_task, 0, sge_slow_intr_handler, sc); return (0); } int t3_sge_reset_adapter(adapter_t *sc) { callout_reset(&sc->sge_timer_ch, TX_RECLAIM_PERIOD, sge_timer_cb, sc); return (0); } int t3_sge_init_port(struct port_info *pi) { TASK_INIT(&pi->timer_reclaim_task, 0, sge_timer_reclaim, pi); return (0); } /** * refill_rspq - replenish an SGE response queue * @adapter: the adapter * @q: the response queue to replenish * @credits: how many new responses to make available * * Replenishes a response queue by making the supplied number of responses * available to HW. */ static __inline void refill_rspq(adapter_t *sc, const struct sge_rspq *q, u_int credits) { /* mbufs are allocated on demand when a rspq entry is processed. */ t3_write_reg(sc, A_SG_RSPQ_CREDIT_RETURN, V_RSPQ(q->cntxt_id) | V_CREDITS(credits)); } static void sge_txq_reclaim_handler(void *arg, int ncount) { struct sge_qset *qs = arg; int i; for (i = 0; i < 3; i++) reclaim_completed_tx(qs, 16, i); } static void sge_timer_reclaim(void *arg, int ncount) { struct port_info *pi = arg; int i, nqsets = pi->nqsets; adapter_t *sc = pi->adapter; struct sge_qset *qs; struct mtx *lock; KASSERT((sc->flags & USING_MSIX) == 0, ("can't call timer reclaim for msi-x")); for (i = 0; i < nqsets; i++) { qs = &sc->sge.qs[pi->first_qset + i]; reclaim_completed_tx(qs, 16, TXQ_OFLD); lock = (sc->flags & USING_MSIX) ? &qs->rspq.lock : &sc->sge.qs[0].rspq.lock; if (mtx_trylock(lock)) { /* XXX currently assume that we are *NOT* polling */ uint32_t status = t3_read_reg(sc, A_SG_RSPQ_FL_STATUS); if (qs->fl[0].credits < qs->fl[0].size - 16) __refill_fl(sc, &qs->fl[0]); if (qs->fl[1].credits < qs->fl[1].size - 16) __refill_fl(sc, &qs->fl[1]); if (status & (1 << qs->rspq.cntxt_id)) { if (qs->rspq.credits) { refill_rspq(sc, &qs->rspq, 1); qs->rspq.credits--; t3_write_reg(sc, A_SG_RSPQ_FL_STATUS, 1 << qs->rspq.cntxt_id); } } mtx_unlock(lock); } } } /** * init_qset_cntxt - initialize an SGE queue set context info * @qs: the queue set * @id: the queue set id * * Initializes the TIDs and context ids for the queues of a queue set. */ static void init_qset_cntxt(struct sge_qset *qs, u_int id) { qs->rspq.cntxt_id = id; qs->fl[0].cntxt_id = 2 * id; qs->fl[1].cntxt_id = 2 * id + 1; qs->txq[TXQ_ETH].cntxt_id = FW_TUNNEL_SGEEC_START + id; qs->txq[TXQ_ETH].token = FW_TUNNEL_TID_START + id; qs->txq[TXQ_OFLD].cntxt_id = FW_OFLD_SGEEC_START + id; qs->txq[TXQ_CTRL].cntxt_id = FW_CTRL_SGEEC_START + id; qs->txq[TXQ_CTRL].token = FW_CTRL_TID_START + id; mbufq_init(&qs->txq[TXQ_ETH].sendq); mbufq_init(&qs->txq[TXQ_OFLD].sendq); mbufq_init(&qs->txq[TXQ_CTRL].sendq); } static void txq_prod(struct sge_txq *txq, unsigned int ndesc, struct txq_state *txqs) { txq->in_use += ndesc; /* * XXX we don't handle stopping of queue * presumably start handles this when we bump against the end */ txqs->gen = txq->gen; txq->unacked += ndesc; txqs->compl = (txq->unacked & 32) << (S_WR_COMPL - 5); txq->unacked &= 31; txqs->pidx = txq->pidx; txq->pidx += ndesc; #ifdef INVARIANTS if (((txqs->pidx > txq->cidx) && (txq->pidx < txqs->pidx) && (txq->pidx >= txq->cidx)) || ((txqs->pidx < txq->cidx) && (txq->pidx >= txq-> cidx)) || ((txqs->pidx < txq->cidx) && (txq->cidx < txqs->pidx))) panic("txqs->pidx=%d txq->pidx=%d txq->cidx=%d", txqs->pidx, txq->pidx, txq->cidx); #endif if (txq->pidx >= txq->size) { txq->pidx -= txq->size; txq->gen ^= 1; } } /** * calc_tx_descs - calculate the number of Tx descriptors for a packet * @m: the packet mbufs * @nsegs: the number of segments * * Returns the number of Tx descriptors needed for the given Ethernet * packet. Ethernet packets require addition of WR and CPL headers. */ static __inline unsigned int calc_tx_descs(const struct mbuf *m, int nsegs) { unsigned int flits; if (m->m_pkthdr.len <= PIO_LEN) return 1; flits = sgl_len(nsegs) + 2; #ifdef TSO_SUPPORTED if (m->m_pkthdr.csum_flags & CSUM_TSO) flits++; #endif return flits_to_desc(flits); } static unsigned int busdma_map_mbufs(struct mbuf **m, struct sge_txq *txq, struct tx_sw_desc *txsd, bus_dma_segment_t *segs, int *nsegs) { struct mbuf *m0; int err, pktlen, pass = 0; bus_dma_tag_t tag = txq->entry_tag; retry: err = 0; m0 = *m; pktlen = m0->m_pkthdr.len; #if defined(__i386__) || defined(__amd64__) if (busdma_map_sg_collapse(tag, txsd->map, m, segs, nsegs) == 0) { goto done; } else #endif err = bus_dmamap_load_mbuf_sg(tag, txsd->map, m0, segs, nsegs, 0); if (err == 0) { goto done; } if (err == EFBIG && pass == 0) { pass = 1; /* Too many segments, try to defrag */ m0 = m_defrag(m0, M_DONTWAIT); if (m0 == NULL) { m_freem(*m); *m = NULL; return (ENOBUFS); } *m = m0; goto retry; } else if (err == ENOMEM) { return (err); } if (err) { if (cxgb_debug) printf("map failure err=%d pktlen=%d\n", err, pktlen); m_freem(m0); *m = NULL; return (err); } done: #if !defined(__i386__) && !defined(__amd64__) bus_dmamap_sync(tag, txsd->map, BUS_DMASYNC_PREWRITE); #endif txsd->flags |= TX_SW_DESC_MAPPED; return (0); } /** * make_sgl - populate a scatter/gather list for a packet * @sgp: the SGL to populate * @segs: the packet dma segments * @nsegs: the number of segments * * Generates a scatter/gather list for the buffers that make up a packet * and returns the SGL size in 8-byte words. The caller must size the SGL * appropriately. */ static __inline void make_sgl(struct sg_ent *sgp, bus_dma_segment_t *segs, int nsegs) { int i, idx; for (idx = 0, i = 0; i < nsegs; i++) { /* * firmware doesn't like empty segments */ if (segs[i].ds_len == 0) continue; if (i && idx == 0) ++sgp; sgp->len[idx] = htobe32(segs[i].ds_len); sgp->addr[idx] = htobe64(segs[i].ds_addr); idx ^= 1; } if (idx) { sgp->len[idx] = 0; sgp->addr[idx] = 0; } } /** * check_ring_tx_db - check and potentially ring a Tx queue's doorbell * @adap: the adapter * @q: the Tx queue * * Ring the doorbell if a Tx queue is asleep. There is a natural race, * where the HW is going to sleep just after we checked, however, * then the interrupt handler will detect the outstanding TX packet * and ring the doorbell for us. * * When GTS is disabled we unconditionally ring the doorbell. */ static __inline void check_ring_tx_db(adapter_t *adap, struct sge_txq *q) { #if USE_GTS clear_bit(TXQ_LAST_PKT_DB, &q->flags); if (test_and_set_bit(TXQ_RUNNING, &q->flags) == 0) { set_bit(TXQ_LAST_PKT_DB, &q->flags); #ifdef T3_TRACE T3_TRACE1(adap->tb[q->cntxt_id & 7], "doorbell Tx, cntxt %d", q->cntxt_id); #endif t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } #else wmb(); /* write descriptors before telling HW */ t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); #endif } static __inline void wr_gen2(struct tx_desc *d, unsigned int gen) { #if SGE_NUM_GENBITS == 2 d->flit[TX_DESC_FLITS - 1] = htobe64(gen); #endif } /** * write_wr_hdr_sgl - write a WR header and, optionally, SGL * @ndesc: number of Tx descriptors spanned by the SGL * @txd: first Tx descriptor to be written * @txqs: txq state (generation and producer index) * @txq: the SGE Tx queue * @sgl: the SGL * @flits: number of flits to the start of the SGL in the first descriptor * @sgl_flits: the SGL size in flits * @wr_hi: top 32 bits of WR header based on WR type (big endian) * @wr_lo: low 32 bits of WR header based on WR type (big endian) * * Write a work request header and an associated SGL. If the SGL is * small enough to fit into one Tx descriptor it has already been written * and we just need to write the WR header. Otherwise we distribute the * SGL across the number of descriptors it spans. */ static void write_wr_hdr_sgl(unsigned int ndesc, struct tx_desc *txd, struct txq_state *txqs, const struct sge_txq *txq, const struct sg_ent *sgl, unsigned int flits, unsigned int sgl_flits, unsigned int wr_hi, unsigned int wr_lo) { struct work_request_hdr *wrp = (struct work_request_hdr *)txd; struct tx_sw_desc *txsd = &txq->sdesc[txqs->pidx]; if (__predict_true(ndesc == 1)) { set_wr_hdr(wrp, htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) | V_WR_SGLSFLT(flits)) | wr_hi, htonl(V_WR_LEN(flits + sgl_flits) | V_WR_GEN(txqs->gen)) | wr_lo); /* XXX gen? */ wr_gen2(txd, txqs->gen); } else { unsigned int ogen = txqs->gen; const uint64_t *fp = (const uint64_t *)sgl; struct work_request_hdr *wp = wrp; wrp->wrh_hi = htonl(F_WR_SOP | V_WR_DATATYPE(1) | V_WR_SGLSFLT(flits)) | wr_hi; while (sgl_flits) { unsigned int avail = WR_FLITS - flits; if (avail > sgl_flits) avail = sgl_flits; memcpy(&txd->flit[flits], fp, avail * sizeof(*fp)); sgl_flits -= avail; ndesc--; if (!sgl_flits) break; fp += avail; txd++; txsd++; if (++txqs->pidx == txq->size) { txqs->pidx = 0; txqs->gen ^= 1; txd = txq->desc; txsd = txq->sdesc; } /* * when the head of the mbuf chain * is freed all clusters will be freed * with it */ wrp = (struct work_request_hdr *)txd; wrp->wrh_hi = htonl(V_WR_DATATYPE(1) | V_WR_SGLSFLT(1)) | wr_hi; wrp->wrh_lo = htonl(V_WR_LEN(min(WR_FLITS, sgl_flits + 1)) | V_WR_GEN(txqs->gen)) | wr_lo; wr_gen2(txd, txqs->gen); flits = 1; } wrp->wrh_hi |= htonl(F_WR_EOP); wmb(); wp->wrh_lo = htonl(V_WR_LEN(WR_FLITS) | V_WR_GEN(ogen)) | wr_lo; wr_gen2((struct tx_desc *)wp, ogen); } } /* sizeof(*eh) + sizeof(*vhdr) + sizeof(*ip) + sizeof(*tcp) */ #define TCPPKTHDRSIZE (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + 20 + 20) #ifdef VLAN_SUPPORTED #define GET_VTAG(cntrl, m) \ do { \ if ((m)->m_flags & M_VLANTAG) \ cntrl |= F_TXPKT_VLAN_VLD | V_TXPKT_VLAN((m)->m_pkthdr.ether_vtag); \ } while (0) #else #define GET_VTAG(cntrl, m) #endif static int t3_encap(struct sge_qset *qs, struct mbuf **m) { adapter_t *sc; struct mbuf *m0; struct sge_txq *txq; struct txq_state txqs; struct port_info *pi; unsigned int ndesc, flits, cntrl, mlen; int err, nsegs, tso_info = 0; struct work_request_hdr *wrp; struct tx_sw_desc *txsd; struct sg_ent *sgp, *sgl; uint32_t wr_hi, wr_lo, sgl_flits; bus_dma_segment_t segs[TX_MAX_SEGS]; struct tx_desc *txd; pi = qs->port; sc = pi->adapter; txq = &qs->txq[TXQ_ETH]; txd = &txq->desc[txq->pidx]; txsd = &txq->sdesc[txq->pidx]; sgl = txq->txq_sgl; prefetch(txd); m0 = *m; DPRINTF("t3_encap port_id=%d qsidx=%d ", pi->port_id, pi->first_qset); DPRINTF("mlen=%d txpkt_intf=%d tx_chan=%d\n", m[0]->m_pkthdr.len, pi->txpkt_intf, pi->tx_chan); mtx_assert(&qs->lock, MA_OWNED); cntrl = V_TXPKT_INTF(pi->txpkt_intf); KASSERT(m0->m_flags & M_PKTHDR, ("not packet header\n")); #ifdef VLAN_SUPPORTED if (m0->m_nextpkt == NULL && m0->m_next != NULL && m0->m_pkthdr.csum_flags & (CSUM_TSO)) tso_info = V_LSO_MSS(m0->m_pkthdr.tso_segsz); #endif if (m0->m_nextpkt != NULL) { busdma_map_sg_vec(txq->entry_tag, txsd->map, m0, segs, &nsegs); ndesc = 1; mlen = 0; } else { if ((err = busdma_map_sg_collapse(txq->entry_tag, txsd->map, &m0, segs, &nsegs))) { if (cxgb_debug) printf("failed ... err=%d\n", err); return (err); } mlen = m0->m_pkthdr.len; ndesc = calc_tx_descs(m0, nsegs); } txq_prod(txq, ndesc, &txqs); KASSERT(m0->m_pkthdr.len, ("empty packet nsegs=%d", nsegs)); txsd->m = m0; if (m0->m_nextpkt != NULL) { struct cpl_tx_pkt_batch *cpl_batch = (struct cpl_tx_pkt_batch *)txd; int i, fidx; if (nsegs > 7) panic("trying to coalesce %d packets in to one WR", nsegs); txq->txq_coalesced += nsegs; wrp = (struct work_request_hdr *)txd; flits = nsegs*2 + 1; for (fidx = 1, i = 0; i < nsegs; i++, fidx += 2) { struct cpl_tx_pkt_batch_entry *cbe; uint64_t flit; uint32_t *hflit = (uint32_t *)&flit; int cflags = m0->m_pkthdr.csum_flags; cntrl = V_TXPKT_INTF(pi->txpkt_intf); GET_VTAG(cntrl, m0); cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT); if (__predict_false(!(cflags & CSUM_IP))) cntrl |= F_TXPKT_IPCSUM_DIS; if (__predict_false(!(cflags & (CSUM_TCP | CSUM_UDP)))) cntrl |= F_TXPKT_L4CSUM_DIS; hflit[0] = htonl(cntrl); hflit[1] = htonl(segs[i].ds_len | 0x80000000); flit |= htobe64(1 << 24); cbe = &cpl_batch->pkt_entry[i]; cbe->cntrl = hflit[0]; cbe->len = hflit[1]; cbe->addr = htobe64(segs[i].ds_addr); } wr_hi = htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) | V_WR_SGLSFLT(flits)) | htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | txqs.compl); wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(txqs.gen)) | htonl(V_WR_TID(txq->token)); set_wr_hdr(wrp, wr_hi, wr_lo); wmb(); + ETHER_BPF_MTAP(pi->ifp, m0); wr_gen2(txd, txqs.gen); check_ring_tx_db(sc, txq); return (0); } else if (tso_info) { int min_size = TCPPKTHDRSIZE, eth_type, tagged; struct cpl_tx_pkt_lso *hdr = (struct cpl_tx_pkt_lso *)txd; struct ip *ip; struct tcphdr *tcp; char *pkthdr; txd->flit[2] = 0; GET_VTAG(cntrl, m0); cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO); hdr->cntrl = htonl(cntrl); hdr->len = htonl(mlen | 0x80000000); DPRINTF("tso buf len=%d\n", mlen); tagged = m0->m_flags & M_VLANTAG; if (!tagged) min_size -= ETHER_VLAN_ENCAP_LEN; if (__predict_false(mlen < min_size)) { printf("mbuf=%p,len=%d,tso_segsz=%d,csum_flags=%#x,flags=%#x", m0, mlen, m0->m_pkthdr.tso_segsz, m0->m_pkthdr.csum_flags, m0->m_flags); panic("tx tso packet too small"); } /* Make sure that ether, ip, tcp headers are all in m0 */ if (__predict_false(m0->m_len < min_size)) { m0 = m_pullup(m0, min_size); if (__predict_false(m0 == NULL)) { /* XXX panic probably an overreaction */ panic("couldn't fit header into mbuf"); } } pkthdr = m0->m_data; if (tagged) { eth_type = CPL_ETH_II_VLAN; ip = (struct ip *)(pkthdr + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); } else { eth_type = CPL_ETH_II; ip = (struct ip *)(pkthdr + ETHER_HDR_LEN); } tcp = (struct tcphdr *)((uint8_t *)ip + sizeof(*ip)); tso_info |= V_LSO_ETH_TYPE(eth_type) | V_LSO_IPHDR_WORDS(ip->ip_hl) | V_LSO_TCPHDR_WORDS(tcp->th_off); hdr->lso_info = htonl(tso_info); if (__predict_false(mlen <= PIO_LEN)) { /* pkt not undersized but fits in PIO_LEN * Indicates a TSO bug at the higher levels. * */ DPRINTF("**5592 Fix** mbuf=%p,len=%d,tso_segsz=%d,csum_flags=%#x,flags=%#x", m0, mlen, m0->m_pkthdr.tso_segsz, m0->m_pkthdr.csum_flags, m0->m_flags); txsd->m = NULL; m_copydata(m0, 0, mlen, (caddr_t)&txd->flit[3]); flits = (mlen + 7) / 8 + 3; wr_hi = htonl(V_WR_BCNTLFLT(mlen & 7) | V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | F_WR_SOP | F_WR_EOP | txqs.compl); wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(txqs.gen) | V_WR_TID(txq->token)); set_wr_hdr(&hdr->wr, wr_hi, wr_lo); wmb(); + ETHER_BPF_MTAP(pi->ifp, m0); wr_gen2(txd, txqs.gen); check_ring_tx_db(sc, txq); + m_freem(m0); return (0); } flits = 3; } else { struct cpl_tx_pkt *cpl = (struct cpl_tx_pkt *)txd; GET_VTAG(cntrl, m0); cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT); if (__predict_false(!(m0->m_pkthdr.csum_flags & CSUM_IP))) cntrl |= F_TXPKT_IPCSUM_DIS; if (__predict_false(!(m0->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)))) cntrl |= F_TXPKT_L4CSUM_DIS; cpl->cntrl = htonl(cntrl); cpl->len = htonl(mlen | 0x80000000); if (mlen <= PIO_LEN) { txsd->m = NULL; m_copydata(m0, 0, mlen, (caddr_t)&txd->flit[2]); flits = (mlen + 7) / 8 + 2; wr_hi = htonl(V_WR_BCNTLFLT(mlen & 7) | V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | F_WR_SOP | F_WR_EOP | txqs.compl); wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(txqs.gen) | V_WR_TID(txq->token)); set_wr_hdr(&cpl->wr, wr_hi, wr_lo); wmb(); + ETHER_BPF_MTAP(pi->ifp, m0); wr_gen2(txd, txqs.gen); check_ring_tx_db(sc, txq); + m_freem(m0); return (0); } flits = 2; } wrp = (struct work_request_hdr *)txd; sgp = (ndesc == 1) ? (struct sg_ent *)&txd->flit[flits] : sgl; make_sgl(sgp, segs, nsegs); sgl_flits = sgl_len(nsegs); + ETHER_BPF_MTAP(pi->ifp, m0); + KASSERT(ndesc <= 4, ("ndesc too large %d", ndesc)); wr_hi = htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | txqs.compl); wr_lo = htonl(V_WR_TID(txq->token)); write_wr_hdr_sgl(ndesc, txd, &txqs, txq, sgl, flits, sgl_flits, wr_hi, wr_lo); - check_ring_tx_db(pi->adapter, txq); + check_ring_tx_db(sc, txq); return (0); } void cxgb_tx_watchdog(void *arg) { struct sge_qset *qs = arg; struct sge_txq *txq = &qs->txq[TXQ_ETH]; if (qs->coalescing != 0 && (txq->in_use <= cxgb_tx_coalesce_enable_stop) && TXQ_RING_EMPTY(qs)) qs->coalescing = 0; else if (qs->coalescing == 0 && (txq->in_use >= cxgb_tx_coalesce_enable_start)) qs->coalescing = 1; if (TXQ_TRYLOCK(qs)) { qs->qs_flags |= QS_FLUSHING; cxgb_start_locked(qs); qs->qs_flags &= ~QS_FLUSHING; TXQ_UNLOCK(qs); } if (qs->port->ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset_on(&txq->txq_watchdog, hz/4, cxgb_tx_watchdog, qs, txq->txq_watchdog.c_cpu); } static void cxgb_tx_timeout(void *arg) { struct sge_qset *qs = arg; struct sge_txq *txq = &qs->txq[TXQ_ETH]; if (qs->coalescing == 0 && (txq->in_use >= (txq->size>>3))) qs->coalescing = 1; if (TXQ_TRYLOCK(qs)) { qs->qs_flags |= QS_TIMEOUT; cxgb_start_locked(qs); qs->qs_flags &= ~QS_TIMEOUT; TXQ_UNLOCK(qs); } } static void cxgb_start_locked(struct sge_qset *qs) { struct mbuf *m_head = NULL; struct sge_txq *txq = &qs->txq[TXQ_ETH]; int avail, txmax; int in_use_init = txq->in_use; struct port_info *pi = qs->port; struct ifnet *ifp = pi->ifp; avail = txq->size - txq->in_use - 4; txmax = min(TX_START_MAX_DESC, avail); if (qs->qs_flags & (QS_FLUSHING|QS_TIMEOUT)) reclaim_completed_tx(qs, 0, TXQ_ETH); if (!pi->link_config.link_ok) { TXQ_RING_FLUSH(qs); return; } TXQ_LOCK_ASSERT(qs); while ((txq->in_use - in_use_init < txmax) && !TXQ_RING_EMPTY(qs) && (ifp->if_drv_flags & IFF_DRV_RUNNING) && pi->link_config.link_ok) { reclaim_completed_tx(qs, cxgb_tx_reclaim_threshold, TXQ_ETH); if ((m_head = cxgb_dequeue(qs)) == NULL) break; /* * Encapsulation can modify our pointer, and or make it * NULL on failure. In that event, we can't requeue. */ if (t3_encap(qs, &m_head) || m_head == NULL) break; - - /* Send a copy of the frame to the BPF listener */ - ETHER_BPF_MTAP(ifp, m_head); - /* - * We sent via PIO, no longer need a copy - */ - if (m_head->m_nextpkt == NULL && - m_head->m_pkthdr.len <= PIO_LEN) - m_freem(m_head); - m_head = NULL; } if (!TXQ_RING_EMPTY(qs) && callout_pending(&txq->txq_timer) == 0 && pi->link_config.link_ok) callout_reset_on(&txq->txq_timer, 1, cxgb_tx_timeout, qs, txq->txq_timer.c_cpu); if (m_head != NULL) m_freem(m_head); } static int cxgb_transmit_locked(struct ifnet *ifp, struct sge_qset *qs, struct mbuf *m) { struct port_info *pi = qs->port; struct sge_txq *txq = &qs->txq[TXQ_ETH]; struct buf_ring *br = txq->txq_mr; int error, avail; avail = txq->size - txq->in_use; TXQ_LOCK_ASSERT(qs); /* * We can only do a direct transmit if the following are true: * - we aren't coalescing (ring < 3/4 full) * - the link is up -- checked in caller * - there are no packets enqueued already * - there is space in hardware transmit queue */ if (check_pkt_coalesce(qs) == 0 && !TXQ_RING_NEEDS_ENQUEUE(qs) && avail > 4) { if (t3_encap(qs, &m)) { if (m != NULL && (error = drbr_enqueue(ifp, br, m)) != 0) return (error); } else { /* * We've bypassed the buf ring so we need to update * the stats directly */ txq->txq_direct_packets++; txq->txq_direct_bytes += m->m_pkthdr.len; - /* - ** Send a copy of the frame to the BPF - ** listener and set the watchdog on. - */ - ETHER_BPF_MTAP(ifp, m); - /* - * We sent via PIO, no longer need a copy - */ - if (m->m_pkthdr.len <= PIO_LEN) - m_freem(m); - } } else if ((error = drbr_enqueue(ifp, br, m)) != 0) return (error); reclaim_completed_tx(qs, cxgb_tx_reclaim_threshold, TXQ_ETH); if (!TXQ_RING_EMPTY(qs) && pi->link_config.link_ok && (!check_pkt_coalesce(qs) || (drbr_inuse(ifp, br) >= 7))) cxgb_start_locked(qs); else if (!TXQ_RING_EMPTY(qs) && !callout_pending(&txq->txq_timer)) callout_reset_on(&txq->txq_timer, 1, cxgb_tx_timeout, qs, txq->txq_timer.c_cpu); return (0); } int cxgb_transmit(struct ifnet *ifp, struct mbuf *m) { struct sge_qset *qs; struct port_info *pi = ifp->if_softc; int error, qidx = pi->first_qset; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||(!pi->link_config.link_ok)) { m_freem(m); return (0); } if (m->m_flags & M_FLOWID) qidx = (m->m_pkthdr.flowid % pi->nqsets) + pi->first_qset; qs = &pi->adapter->sge.qs[qidx]; if (TXQ_TRYLOCK(qs)) { /* XXX running */ error = cxgb_transmit_locked(ifp, qs, m); TXQ_UNLOCK(qs); } else error = drbr_enqueue(ifp, qs->txq[TXQ_ETH].txq_mr, m); return (error); } void cxgb_start(struct ifnet *ifp) { struct port_info *pi = ifp->if_softc; struct sge_qset *qs = &pi->adapter->sge.qs[pi->first_qset]; if (!pi->link_config.link_ok) return; TXQ_LOCK(qs); cxgb_start_locked(qs); TXQ_UNLOCK(qs); } void cxgb_qflush(struct ifnet *ifp) { /* * flush any enqueued mbufs in the buf_rings * and in the transmit queues * no-op for now */ return; } /** * write_imm - write a packet into a Tx descriptor as immediate data * @d: the Tx descriptor to write * @m: the packet * @len: the length of packet data to write as immediate data * @gen: the generation bit value to write * * Writes a packet as immediate data into a Tx descriptor. The packet * contains a work request at its beginning. We must write the packet * carefully so the SGE doesn't read accidentally before it's written in * its entirety. */ static __inline void write_imm(struct tx_desc *d, struct mbuf *m, unsigned int len, unsigned int gen) { struct work_request_hdr *from = mtod(m, struct work_request_hdr *); struct work_request_hdr *to = (struct work_request_hdr *)d; uint32_t wr_hi, wr_lo; if (len > WR_LEN) panic("len too big %d\n", len); if (len < sizeof(*from)) panic("len too small %d", len); memcpy(&to[1], &from[1], len - sizeof(*from)); wr_hi = from->wrh_hi | htonl(F_WR_SOP | F_WR_EOP | V_WR_BCNTLFLT(len & 7)); wr_lo = from->wrh_lo | htonl(V_WR_GEN(gen) | V_WR_LEN((len + 7) / 8)); set_wr_hdr(to, wr_hi, wr_lo); wmb(); wr_gen2(d, gen); /* * This check is a hack we should really fix the logic so * that this can't happen */ if (m->m_type != MT_DONTFREE) m_freem(m); } /** * check_desc_avail - check descriptor availability on a send queue * @adap: the adapter * @q: the TX queue * @m: the packet needing the descriptors * @ndesc: the number of Tx descriptors needed * @qid: the Tx queue number in its queue set (TXQ_OFLD or TXQ_CTRL) * * Checks if the requested number of Tx descriptors is available on an * SGE send queue. If the queue is already suspended or not enough * descriptors are available the packet is queued for later transmission. * Must be called with the Tx queue locked. * * Returns 0 if enough descriptors are available, 1 if there aren't * enough descriptors and the packet has been queued, and 2 if the caller * needs to retry because there weren't enough descriptors at the * beginning of the call but some freed up in the mean time. */ static __inline int check_desc_avail(adapter_t *adap, struct sge_txq *q, struct mbuf *m, unsigned int ndesc, unsigned int qid) { /* * XXX We currently only use this for checking the control queue * the control queue is only used for binding qsets which happens * at init time so we are guaranteed enough descriptors */ if (__predict_false(!mbufq_empty(&q->sendq))) { addq_exit: mbufq_tail(&q->sendq, m); return 1; } if (__predict_false(q->size - q->in_use < ndesc)) { struct sge_qset *qs = txq_to_qset(q, qid); setbit(&qs->txq_stopped, qid); if (should_restart_tx(q) && test_and_clear_bit(qid, &qs->txq_stopped)) return 2; q->stops++; goto addq_exit; } return 0; } /** * reclaim_completed_tx_imm - reclaim completed control-queue Tx descs * @q: the SGE control Tx queue * * This is a variant of reclaim_completed_tx() that is used for Tx queues * that send only immediate data (presently just the control queues) and * thus do not have any mbufs */ static __inline void reclaim_completed_tx_imm(struct sge_txq *q) { unsigned int reclaim = q->processed - q->cleaned; q->in_use -= reclaim; q->cleaned += reclaim; } static __inline int immediate(const struct mbuf *m) { return m->m_len <= WR_LEN && m->m_pkthdr.len <= WR_LEN ; } /** * ctrl_xmit - send a packet through an SGE control Tx queue * @adap: the adapter * @q: the control queue * @m: the packet * * Send a packet through an SGE control Tx queue. Packets sent through * a control queue must fit entirely as immediate data in a single Tx * descriptor and have no page fragments. */ static int ctrl_xmit(adapter_t *adap, struct sge_qset *qs, struct mbuf *m) { int ret; struct work_request_hdr *wrp = mtod(m, struct work_request_hdr *); struct sge_txq *q = &qs->txq[TXQ_CTRL]; if (__predict_false(!immediate(m))) { m_freem(m); return 0; } wrp->wrh_hi |= htonl(F_WR_SOP | F_WR_EOP); wrp->wrh_lo = htonl(V_WR_TID(q->token)); TXQ_LOCK(qs); again: reclaim_completed_tx_imm(q); ret = check_desc_avail(adap, q, m, 1, TXQ_CTRL); if (__predict_false(ret)) { if (ret == 1) { TXQ_UNLOCK(qs); return (ENOSPC); } goto again; } write_imm(&q->desc[q->pidx], m, m->m_len, q->gen); q->in_use++; if (++q->pidx >= q->size) { q->pidx = 0; q->gen ^= 1; } TXQ_UNLOCK(qs); wmb(); t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); return (0); } /** * restart_ctrlq - restart a suspended control queue * @qs: the queue set cotaining the control queue * * Resumes transmission on a suspended Tx control queue. */ static void restart_ctrlq(void *data, int npending) { struct mbuf *m; struct sge_qset *qs = (struct sge_qset *)data; struct sge_txq *q = &qs->txq[TXQ_CTRL]; adapter_t *adap = qs->port->adapter; TXQ_LOCK(qs); again: reclaim_completed_tx_imm(q); while (q->in_use < q->size && (m = mbufq_dequeue(&q->sendq)) != NULL) { write_imm(&q->desc[q->pidx], m, m->m_len, q->gen); if (++q->pidx >= q->size) { q->pidx = 0; q->gen ^= 1; } q->in_use++; } if (!mbufq_empty(&q->sendq)) { setbit(&qs->txq_stopped, TXQ_CTRL); if (should_restart_tx(q) && test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) goto again; q->stops++; } TXQ_UNLOCK(qs); t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /* * Send a management message through control queue 0 */ int t3_mgmt_tx(struct adapter *adap, struct mbuf *m) { return ctrl_xmit(adap, &adap->sge.qs[0], m); } /** * free_qset - free the resources of an SGE queue set * @sc: the controller owning the queue set * @q: the queue set * * Release the HW and SW resources associated with an SGE queue set, such * as HW contexts, packet buffers, and descriptor rings. Traffic to the * queue set must be quiesced prior to calling this. */ static void t3_free_qset(adapter_t *sc, struct sge_qset *q) { int i; reclaim_completed_tx(q, 0, TXQ_ETH); for (i = 0; i < SGE_TXQ_PER_SET; i++) { if (q->txq[i].txq_mr != NULL) buf_ring_free(q->txq[i].txq_mr, M_DEVBUF); if (q->txq[i].txq_ifq != NULL) { ifq_delete(q->txq[i].txq_ifq); free(q->txq[i].txq_ifq, M_DEVBUF); } } for (i = 0; i < SGE_RXQ_PER_SET; ++i) { if (q->fl[i].desc) { mtx_lock_spin(&sc->sge.reg_lock); t3_sge_disable_fl(sc, q->fl[i].cntxt_id); mtx_unlock_spin(&sc->sge.reg_lock); bus_dmamap_unload(q->fl[i].desc_tag, q->fl[i].desc_map); bus_dmamem_free(q->fl[i].desc_tag, q->fl[i].desc, q->fl[i].desc_map); bus_dma_tag_destroy(q->fl[i].desc_tag); bus_dma_tag_destroy(q->fl[i].entry_tag); } if (q->fl[i].sdesc) { free_rx_bufs(sc, &q->fl[i]); free(q->fl[i].sdesc, M_DEVBUF); } } mtx_unlock(&q->lock); MTX_DESTROY(&q->lock); for (i = 0; i < SGE_TXQ_PER_SET; i++) { if (q->txq[i].desc) { mtx_lock_spin(&sc->sge.reg_lock); t3_sge_enable_ecntxt(sc, q->txq[i].cntxt_id, 0); mtx_unlock_spin(&sc->sge.reg_lock); bus_dmamap_unload(q->txq[i].desc_tag, q->txq[i].desc_map); bus_dmamem_free(q->txq[i].desc_tag, q->txq[i].desc, q->txq[i].desc_map); bus_dma_tag_destroy(q->txq[i].desc_tag); bus_dma_tag_destroy(q->txq[i].entry_tag); } if (q->txq[i].sdesc) { free(q->txq[i].sdesc, M_DEVBUF); } } if (q->rspq.desc) { mtx_lock_spin(&sc->sge.reg_lock); t3_sge_disable_rspcntxt(sc, q->rspq.cntxt_id); mtx_unlock_spin(&sc->sge.reg_lock); bus_dmamap_unload(q->rspq.desc_tag, q->rspq.desc_map); bus_dmamem_free(q->rspq.desc_tag, q->rspq.desc, q->rspq.desc_map); bus_dma_tag_destroy(q->rspq.desc_tag); MTX_DESTROY(&q->rspq.lock); } #ifdef LRO_SUPPORTED tcp_lro_free(&q->lro.ctrl); #endif bzero(q, sizeof(*q)); } /** * t3_free_sge_resources - free SGE resources * @sc: the adapter softc * * Frees resources used by the SGE queue sets. */ void t3_free_sge_resources(adapter_t *sc) { int i, nqsets; for (nqsets = i = 0; i < (sc)->params.nports; i++) nqsets += sc->port[i].nqsets; for (i = 0; i < nqsets; ++i) { TXQ_LOCK(&sc->sge.qs[i]); t3_free_qset(sc, &sc->sge.qs[i]); } } /** * t3_sge_start - enable SGE * @sc: the controller softc * * Enables the SGE for DMAs. This is the last step in starting packet * transfers. */ void t3_sge_start(adapter_t *sc) { t3_set_reg_field(sc, A_SG_CONTROL, F_GLOBALENABLE, F_GLOBALENABLE); } /** * t3_sge_stop - disable SGE operation * @sc: the adapter * * Disables the DMA engine. This can be called in emeregencies (e.g., * from error interrupts) or from normal process context. In the latter * case it also disables any pending queue restart tasklets. Note that * if it is called in interrupt context it cannot disable the restart * tasklets as it cannot wait, however the tasklets will have no effect * since the doorbells are disabled and the driver will call this again * later from process context, at which time the tasklets will be stopped * if they are still running. */ void t3_sge_stop(adapter_t *sc) { int i, nqsets; t3_set_reg_field(sc, A_SG_CONTROL, F_GLOBALENABLE, 0); if (sc->tq == NULL) return; for (nqsets = i = 0; i < (sc)->params.nports; i++) nqsets += sc->port[i].nqsets; #ifdef notyet /* * * XXX */ for (i = 0; i < nqsets; ++i) { struct sge_qset *qs = &sc->sge.qs[i]; taskqueue_drain(sc->tq, &qs->txq[TXQ_OFLD].qresume_task); taskqueue_drain(sc->tq, &qs->txq[TXQ_CTRL].qresume_task); } #endif } /** * t3_free_tx_desc - reclaims Tx descriptors and their buffers * @adapter: the adapter * @q: the Tx queue to reclaim descriptors from * @reclaimable: the number of descriptors to reclaim * @m_vec_size: maximum number of buffers to reclaim * @desc_reclaimed: returns the number of descriptors reclaimed * * Reclaims Tx descriptors from an SGE Tx queue and frees the associated * Tx buffers. Called with the Tx queue lock held. * * Returns number of buffers of reclaimed */ void t3_free_tx_desc(struct sge_qset *qs, int reclaimable, int queue) { struct tx_sw_desc *txsd; unsigned int cidx, mask; struct sge_txq *q = &qs->txq[queue]; #ifdef T3_TRACE T3_TRACE2(sc->tb[q->cntxt_id & 7], "reclaiming %u Tx descriptors at cidx %u", reclaimable, cidx); #endif cidx = q->cidx; mask = q->size - 1; txsd = &q->sdesc[cidx]; mtx_assert(&qs->lock, MA_OWNED); while (reclaimable--) { prefetch(q->sdesc[(cidx + 1) & mask].m); prefetch(q->sdesc[(cidx + 2) & mask].m); if (txsd->m != NULL) { if (txsd->flags & TX_SW_DESC_MAPPED) { bus_dmamap_unload(q->entry_tag, txsd->map); txsd->flags &= ~TX_SW_DESC_MAPPED; } m_freem_list(txsd->m); txsd->m = NULL; } else q->txq_skipped++; ++txsd; if (++cidx == q->size) { cidx = 0; txsd = q->sdesc; } } q->cidx = cidx; } /** * is_new_response - check if a response is newly written * @r: the response descriptor * @q: the response queue * * Returns true if a response descriptor contains a yet unprocessed * response. */ static __inline int is_new_response(const struct rsp_desc *r, const struct sge_rspq *q) { return (r->intr_gen & F_RSPD_GEN2) == q->gen; } #define RSPD_GTS_MASK (F_RSPD_TXQ0_GTS | F_RSPD_TXQ1_GTS) #define RSPD_CTRL_MASK (RSPD_GTS_MASK | \ V_RSPD_TXQ0_CR(M_RSPD_TXQ0_CR) | \ V_RSPD_TXQ1_CR(M_RSPD_TXQ1_CR) | \ V_RSPD_TXQ2_CR(M_RSPD_TXQ2_CR)) /* How long to delay the next interrupt in case of memory shortage, in 0.1us. */ #define NOMEM_INTR_DELAY 2500 /** * write_ofld_wr - write an offload work request * @adap: the adapter * @m: the packet to send * @q: the Tx queue * @pidx: index of the first Tx descriptor to write * @gen: the generation value to use * @ndesc: number of descriptors the packet will occupy * * Write an offload work request to send the supplied packet. The packet * data already carry the work request with most fields populated. */ static void write_ofld_wr(adapter_t *adap, struct mbuf *m, struct sge_txq *q, unsigned int pidx, unsigned int gen, unsigned int ndesc, bus_dma_segment_t *segs, unsigned int nsegs) { unsigned int sgl_flits, flits; struct work_request_hdr *from; struct sg_ent *sgp, sgl[TX_MAX_SEGS / 2 + 1]; struct tx_desc *d = &q->desc[pidx]; struct txq_state txqs; if (immediate(m) && nsegs == 0) { write_imm(d, m, m->m_len, gen); return; } /* Only TX_DATA builds SGLs */ from = mtod(m, struct work_request_hdr *); memcpy(&d->flit[1], &from[1], m->m_len - sizeof(*from)); flits = m->m_len / 8; sgp = (ndesc == 1) ? (struct sg_ent *)&d->flit[flits] : sgl; make_sgl(sgp, segs, nsegs); sgl_flits = sgl_len(nsegs); txqs.gen = gen; txqs.pidx = pidx; txqs.compl = 0; write_wr_hdr_sgl(ndesc, d, &txqs, q, sgl, flits, sgl_flits, from->wrh_hi, from->wrh_lo); } /** * calc_tx_descs_ofld - calculate # of Tx descriptors for an offload packet * @m: the packet * * Returns the number of Tx descriptors needed for the given offload * packet. These packets are already fully constructed. */ static __inline unsigned int calc_tx_descs_ofld(struct mbuf *m, unsigned int nsegs) { unsigned int flits, cnt = 0; int ndescs; if (m->m_len <= WR_LEN && nsegs == 0) return (1); /* packet fits as immediate data */ /* * This needs to be re-visited for TOE */ cnt = nsegs; /* headers */ flits = m->m_len / 8; ndescs = flits_to_desc(flits + sgl_len(cnt)); return (ndescs); } /** * ofld_xmit - send a packet through an offload queue * @adap: the adapter * @q: the Tx offload queue * @m: the packet * * Send an offload packet through an SGE offload queue. */ static int ofld_xmit(adapter_t *adap, struct sge_qset *qs, struct mbuf *m) { int ret, nsegs; unsigned int ndesc; unsigned int pidx, gen; struct sge_txq *q = &qs->txq[TXQ_OFLD]; bus_dma_segment_t segs[TX_MAX_SEGS], *vsegs; struct tx_sw_desc *stx; nsegs = m_get_sgllen(m); vsegs = m_get_sgl(m); ndesc = calc_tx_descs_ofld(m, nsegs); busdma_map_sgl(vsegs, segs, nsegs); stx = &q->sdesc[q->pidx]; TXQ_LOCK(qs); again: reclaim_completed_tx(qs, 16, TXQ_OFLD); ret = check_desc_avail(adap, q, m, ndesc, TXQ_OFLD); if (__predict_false(ret)) { if (ret == 1) { printf("no ofld desc avail\n"); m_set_priority(m, ndesc); /* save for restart */ TXQ_UNLOCK(qs); return (EINTR); } goto again; } gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; } #ifdef T3_TRACE T3_TRACE5(adap->tb[q->cntxt_id & 7], "ofld_xmit: ndesc %u, pidx %u, len %u, main %u, frags %u", ndesc, pidx, skb->len, skb->len - skb->data_len, skb_shinfo(skb)->nr_frags); #endif TXQ_UNLOCK(qs); write_ofld_wr(adap, m, q, pidx, gen, ndesc, segs, nsegs); check_ring_tx_db(adap, q); return (0); } /** * restart_offloadq - restart a suspended offload queue * @qs: the queue set cotaining the offload queue * * Resumes transmission on a suspended Tx offload queue. */ static void restart_offloadq(void *data, int npending) { struct mbuf *m; struct sge_qset *qs = data; struct sge_txq *q = &qs->txq[TXQ_OFLD]; adapter_t *adap = qs->port->adapter; bus_dma_segment_t segs[TX_MAX_SEGS]; struct tx_sw_desc *stx = &q->sdesc[q->pidx]; int nsegs, cleaned; TXQ_LOCK(qs); again: cleaned = reclaim_completed_tx(qs, 16, TXQ_OFLD); while ((m = mbufq_peek(&q->sendq)) != NULL) { unsigned int gen, pidx; unsigned int ndesc = m_get_priority(m); if (__predict_false(q->size - q->in_use < ndesc)) { setbit(&qs->txq_stopped, TXQ_OFLD); if (should_restart_tx(q) && test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) goto again; q->stops++; break; } gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; } (void)mbufq_dequeue(&q->sendq); busdma_map_mbufs(&m, q, stx, segs, &nsegs); TXQ_UNLOCK(qs); write_ofld_wr(adap, m, q, pidx, gen, ndesc, segs, nsegs); TXQ_LOCK(qs); } #if USE_GTS set_bit(TXQ_RUNNING, &q->flags); set_bit(TXQ_LAST_PKT_DB, &q->flags); #endif TXQ_UNLOCK(qs); wmb(); t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /** * queue_set - return the queue set a packet should use * @m: the packet * * Maps a packet to the SGE queue set it should use. The desired queue * set is carried in bits 1-3 in the packet's priority. */ static __inline int queue_set(const struct mbuf *m) { return m_get_priority(m) >> 1; } /** * is_ctrl_pkt - return whether an offload packet is a control packet * @m: the packet * * Determines whether an offload packet should use an OFLD or a CTRL * Tx queue. This is indicated by bit 0 in the packet's priority. */ static __inline int is_ctrl_pkt(const struct mbuf *m) { return m_get_priority(m) & 1; } /** * t3_offload_tx - send an offload packet * @tdev: the offload device to send to * @m: the packet * * Sends an offload packet. We use the packet priority to select the * appropriate Tx queue as follows: bit 0 indicates whether the packet * should be sent as regular or control, bits 1-3 select the queue set. */ int t3_offload_tx(struct t3cdev *tdev, struct mbuf *m) { adapter_t *adap = tdev2adap(tdev); struct sge_qset *qs = &adap->sge.qs[queue_set(m)]; if (__predict_false(is_ctrl_pkt(m))) return ctrl_xmit(adap, qs, m); return ofld_xmit(adap, qs, m); } /** * deliver_partial_bundle - deliver a (partial) bundle of Rx offload pkts * @tdev: the offload device that will be receiving the packets * @q: the SGE response queue that assembled the bundle * @m: the partial bundle * @n: the number of packets in the bundle * * Delivers a (partial) bundle of Rx offload packets to an offload device. */ static __inline void deliver_partial_bundle(struct t3cdev *tdev, struct sge_rspq *q, struct mbuf *mbufs[], int n) { if (n) { q->offload_bundles++; cxgb_ofld_recv(tdev, mbufs, n); } } static __inline int rx_offload(struct t3cdev *tdev, struct sge_rspq *rq, struct mbuf *m, struct mbuf *rx_gather[], unsigned int gather_idx) { rq->offload_pkts++; m->m_pkthdr.header = mtod(m, void *); rx_gather[gather_idx++] = m; if (gather_idx == RX_BUNDLE_SIZE) { cxgb_ofld_recv(tdev, rx_gather, RX_BUNDLE_SIZE); gather_idx = 0; rq->offload_bundles++; } return (gather_idx); } static void restart_tx(struct sge_qset *qs) { struct adapter *sc = qs->port->adapter; if (isset(&qs->txq_stopped, TXQ_OFLD) && should_restart_tx(&qs->txq[TXQ_OFLD]) && test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) { qs->txq[TXQ_OFLD].restarts++; DPRINTF("restarting TXQ_OFLD\n"); taskqueue_enqueue(sc->tq, &qs->txq[TXQ_OFLD].qresume_task); } DPRINTF("stopped=0x%x restart=%d processed=%d cleaned=%d in_use=%d\n", qs->txq_stopped, should_restart_tx(&qs->txq[TXQ_CTRL]), qs->txq[TXQ_CTRL].processed, qs->txq[TXQ_CTRL].cleaned, qs->txq[TXQ_CTRL].in_use); if (isset(&qs->txq_stopped, TXQ_CTRL) && should_restart_tx(&qs->txq[TXQ_CTRL]) && test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) { qs->txq[TXQ_CTRL].restarts++; DPRINTF("restarting TXQ_CTRL\n"); taskqueue_enqueue(sc->tq, &qs->txq[TXQ_CTRL].qresume_task); } } /** * t3_sge_alloc_qset - initialize an SGE queue set * @sc: the controller softc * @id: the queue set id * @nports: how many Ethernet ports will be using this queue set * @irq_vec_idx: the IRQ vector index for response queue interrupts * @p: configuration parameters for this queue set * @ntxq: number of Tx queues for the queue set * @pi: port info for queue set * * Allocate resources and initialize an SGE queue set. A queue set * comprises a response queue, two Rx free-buffer queues, and up to 3 * Tx queues. The Tx queues are assigned roles in the order Ethernet * queue, offload queue, and control queue. */ int t3_sge_alloc_qset(adapter_t *sc, u_int id, int nports, int irq_vec_idx, const struct qset_params *p, int ntxq, struct port_info *pi) { struct sge_qset *q = &sc->sge.qs[id]; int i, ret = 0; MTX_INIT(&q->lock, q->namebuf, NULL, MTX_DEF); q->port = pi; for (i = 0; i < SGE_TXQ_PER_SET; i++) { if ((q->txq[i].txq_mr = buf_ring_alloc(cxgb_txq_buf_ring_size, M_DEVBUF, M_WAITOK, &q->lock)) == NULL) { device_printf(sc->dev, "failed to allocate mbuf ring\n"); goto err; } if ((q->txq[i].txq_ifq = malloc(sizeof(struct ifaltq), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) { device_printf(sc->dev, "failed to allocate ifq\n"); goto err; } ifq_init(q->txq[i].txq_ifq, pi->ifp); callout_init(&q->txq[i].txq_timer, 1); callout_init(&q->txq[i].txq_watchdog, 1); q->txq[i].txq_timer.c_cpu = id % mp_ncpus; q->txq[i].txq_watchdog.c_cpu = id % mp_ncpus; } init_qset_cntxt(q, id); q->idx = id; if ((ret = alloc_ring(sc, p->fl_size, sizeof(struct rx_desc), sizeof(struct rx_sw_desc), &q->fl[0].phys_addr, &q->fl[0].desc, &q->fl[0].sdesc, &q->fl[0].desc_tag, &q->fl[0].desc_map, sc->rx_dmat, &q->fl[0].entry_tag)) != 0) { printf("error %d from alloc ring fl0\n", ret); goto err; } if ((ret = alloc_ring(sc, p->jumbo_size, sizeof(struct rx_desc), sizeof(struct rx_sw_desc), &q->fl[1].phys_addr, &q->fl[1].desc, &q->fl[1].sdesc, &q->fl[1].desc_tag, &q->fl[1].desc_map, sc->rx_jumbo_dmat, &q->fl[1].entry_tag)) != 0) { printf("error %d from alloc ring fl1\n", ret); goto err; } if ((ret = alloc_ring(sc, p->rspq_size, sizeof(struct rsp_desc), 0, &q->rspq.phys_addr, &q->rspq.desc, NULL, &q->rspq.desc_tag, &q->rspq.desc_map, NULL, NULL)) != 0) { printf("error %d from alloc ring rspq\n", ret); goto err; } for (i = 0; i < ntxq; ++i) { size_t sz = i == TXQ_CTRL ? 0 : sizeof(struct tx_sw_desc); if ((ret = alloc_ring(sc, p->txq_size[i], sizeof(struct tx_desc), sz, &q->txq[i].phys_addr, &q->txq[i].desc, &q->txq[i].sdesc, &q->txq[i].desc_tag, &q->txq[i].desc_map, sc->tx_dmat, &q->txq[i].entry_tag)) != 0) { printf("error %d from alloc ring tx %i\n", ret, i); goto err; } mbufq_init(&q->txq[i].sendq); q->txq[i].gen = 1; q->txq[i].size = p->txq_size[i]; } TASK_INIT(&q->txq[TXQ_OFLD].qresume_task, 0, restart_offloadq, q); TASK_INIT(&q->txq[TXQ_CTRL].qresume_task, 0, restart_ctrlq, q); TASK_INIT(&q->txq[TXQ_ETH].qreclaim_task, 0, sge_txq_reclaim_handler, q); TASK_INIT(&q->txq[TXQ_OFLD].qreclaim_task, 0, sge_txq_reclaim_handler, q); q->fl[0].gen = q->fl[1].gen = 1; q->fl[0].size = p->fl_size; q->fl[1].size = p->jumbo_size; q->rspq.gen = 1; q->rspq.cidx = 0; q->rspq.size = p->rspq_size; q->txq[TXQ_ETH].stop_thres = nports * flits_to_desc(sgl_len(TX_MAX_SEGS + 1) + 3); q->fl[0].buf_size = MCLBYTES; q->fl[0].zone = zone_pack; q->fl[0].type = EXT_PACKET; #if __FreeBSD_version > 800000 if (cxgb_use_16k_clusters) { q->fl[1].buf_size = MJUM16BYTES; q->fl[1].zone = zone_jumbo16; q->fl[1].type = EXT_JUMBO16; } else { q->fl[1].buf_size = MJUM9BYTES; q->fl[1].zone = zone_jumbo9; q->fl[1].type = EXT_JUMBO9; } #else q->fl[1].buf_size = MJUMPAGESIZE; q->fl[1].zone = zone_jumbop; q->fl[1].type = EXT_JUMBOP; #endif #ifdef LRO_SUPPORTED /* Allocate and setup the lro_ctrl structure */ q->lro.enabled = !!(pi->ifp->if_capenable & IFCAP_LRO); ret = tcp_lro_init(&q->lro.ctrl); if (ret) { printf("error %d from tcp_lro_init\n", ret); goto err; } q->lro.ctrl.ifp = pi->ifp; #endif mtx_lock_spin(&sc->sge.reg_lock); ret = -t3_sge_init_rspcntxt(sc, q->rspq.cntxt_id, irq_vec_idx, q->rspq.phys_addr, q->rspq.size, q->fl[0].buf_size, 1, 0); if (ret) { printf("error %d from t3_sge_init_rspcntxt\n", ret); goto err_unlock; } for (i = 0; i < SGE_RXQ_PER_SET; ++i) { ret = -t3_sge_init_flcntxt(sc, q->fl[i].cntxt_id, 0, q->fl[i].phys_addr, q->fl[i].size, q->fl[i].buf_size, p->cong_thres, 1, 0); if (ret) { printf("error %d from t3_sge_init_flcntxt for index i=%d\n", ret, i); goto err_unlock; } } ret = -t3_sge_init_ecntxt(sc, q->txq[TXQ_ETH].cntxt_id, USE_GTS, SGE_CNTXT_ETH, id, q->txq[TXQ_ETH].phys_addr, q->txq[TXQ_ETH].size, q->txq[TXQ_ETH].token, 1, 0); if (ret) { printf("error %d from t3_sge_init_ecntxt\n", ret); goto err_unlock; } if (ntxq > 1) { ret = -t3_sge_init_ecntxt(sc, q->txq[TXQ_OFLD].cntxt_id, USE_GTS, SGE_CNTXT_OFLD, id, q->txq[TXQ_OFLD].phys_addr, q->txq[TXQ_OFLD].size, 0, 1, 0); if (ret) { printf("error %d from t3_sge_init_ecntxt\n", ret); goto err_unlock; } } if (ntxq > 2) { ret = -t3_sge_init_ecntxt(sc, q->txq[TXQ_CTRL].cntxt_id, 0, SGE_CNTXT_CTRL, id, q->txq[TXQ_CTRL].phys_addr, q->txq[TXQ_CTRL].size, q->txq[TXQ_CTRL].token, 1, 0); if (ret) { printf("error %d from t3_sge_init_ecntxt\n", ret); goto err_unlock; } } snprintf(q->rspq.lockbuf, RSPQ_NAME_LEN, "t3 rspq lock %d:%d", device_get_unit(sc->dev), irq_vec_idx); MTX_INIT(&q->rspq.lock, q->rspq.lockbuf, NULL, MTX_DEF); mtx_unlock_spin(&sc->sge.reg_lock); t3_update_qset_coalesce(q, p); q->port = pi; refill_fl(sc, &q->fl[0], q->fl[0].size); refill_fl(sc, &q->fl[1], q->fl[1].size); refill_rspq(sc, &q->rspq, q->rspq.size - 1); t3_write_reg(sc, A_SG_GTS, V_RSPQ(q->rspq.cntxt_id) | V_NEWTIMER(q->rspq.holdoff_tmr)); return (0); err_unlock: mtx_unlock_spin(&sc->sge.reg_lock); err: TXQ_LOCK(q); t3_free_qset(sc, q); return (ret); } /* * Remove CPL_RX_PKT headers from the mbuf and reduce it to a regular mbuf with * ethernet data. Hardware assistance with various checksums and any vlan tag * will also be taken into account here. */ void t3_rx_eth(struct adapter *adap, struct sge_rspq *rq, struct mbuf *m, int ethpad) { struct cpl_rx_pkt *cpl = (struct cpl_rx_pkt *)(mtod(m, uint8_t *) + ethpad); struct port_info *pi = &adap->port[adap->rxpkt_map[cpl->iff]]; struct ifnet *ifp = pi->ifp; DPRINTF("rx_eth m=%p m->m_data=%p p->iff=%d\n", m, mtod(m, uint8_t *), cpl->iff); if ((ifp->if_capenable & IFCAP_RXCSUM) && !cpl->fragment && cpl->csum_valid && cpl->csum == 0xffff) { m->m_pkthdr.csum_flags = (CSUM_IP_CHECKED|CSUM_IP_VALID); rspq_to_qset(rq)->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++; m->m_pkthdr.csum_flags = (CSUM_IP_CHECKED|CSUM_IP_VALID|CSUM_DATA_VALID|CSUM_PSEUDO_HDR); m->m_pkthdr.csum_data = 0xffff; } /* * XXX need to add VLAN support for 6.x */ #ifdef VLAN_SUPPORTED if (__predict_false(cpl->vlan_valid)) { m->m_pkthdr.ether_vtag = ntohs(cpl->vlan); m->m_flags |= M_VLANTAG; } #endif m->m_pkthdr.rcvif = ifp; m->m_pkthdr.header = mtod(m, uint8_t *) + sizeof(*cpl) + ethpad; /* * adjust after conversion to mbuf chain */ m->m_pkthdr.len -= (sizeof(*cpl) + ethpad); m->m_len -= (sizeof(*cpl) + ethpad); m->m_data += (sizeof(*cpl) + ethpad); } /** * get_packet - return the next ingress packet buffer from a free list * @adap: the adapter that received the packet * @drop_thres: # of remaining buffers before we start dropping packets * @qs: the qset that the SGE free list holding the packet belongs to * @mh: the mbuf header, contains a pointer to the head and tail of the mbuf chain * @r: response descriptor * * Get the next packet from a free list and complete setup of the * sk_buff. If the packet is small we make a copy and recycle the * original buffer, otherwise we use the original buffer itself. If a * positive drop threshold is supplied packets are dropped and their * buffers recycled if (a) the number of remaining buffers is under the * threshold and the packet is too big to copy, or (b) the packet should * be copied but there is no memory for the copy. */ static int get_packet(adapter_t *adap, unsigned int drop_thres, struct sge_qset *qs, struct t3_mbuf_hdr *mh, struct rsp_desc *r) { unsigned int len_cq = ntohl(r->len_cq); struct sge_fl *fl = (len_cq & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; int mask, cidx = fl->cidx; struct rx_sw_desc *sd = &fl->sdesc[cidx]; uint32_t len = G_RSPD_LEN(len_cq); uint32_t flags = M_EXT; uint8_t sopeop = G_RSPD_SOP_EOP(ntohl(r->flags)); caddr_t cl; struct mbuf *m; int ret = 0; mask = fl->size - 1; prefetch(fl->sdesc[(cidx + 1) & mask].m); prefetch(fl->sdesc[(cidx + 2) & mask].m); prefetch(fl->sdesc[(cidx + 1) & mask].rxsd_cl); prefetch(fl->sdesc[(cidx + 2) & mask].rxsd_cl); fl->credits--; bus_dmamap_sync(fl->entry_tag, sd->map, BUS_DMASYNC_POSTREAD); if (recycle_enable && len <= SGE_RX_COPY_THRES && sopeop == RSPQ_SOP_EOP) { if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) goto skip_recycle; cl = mtod(m, void *); memcpy(cl, sd->rxsd_cl, len); recycle_rx_buf(adap, fl, fl->cidx); m->m_pkthdr.len = m->m_len = len; m->m_flags = 0; mh->mh_head = mh->mh_tail = m; ret = 1; goto done; } else { skip_recycle: bus_dmamap_unload(fl->entry_tag, sd->map); cl = sd->rxsd_cl; m = sd->m; if ((sopeop == RSPQ_SOP_EOP) || (sopeop == RSPQ_SOP)) flags |= M_PKTHDR; m_init(m, fl->zone, fl->buf_size, M_NOWAIT, MT_DATA, flags); if (fl->zone == zone_pack) { /* * restore clobbered data pointer */ m->m_data = m->m_ext.ext_buf; } else { m_cljset(m, cl, fl->type); } m->m_len = len; } switch(sopeop) { case RSPQ_SOP_EOP: ret = 1; /* FALLTHROUGH */ case RSPQ_SOP: mh->mh_head = mh->mh_tail = m; m->m_pkthdr.len = len; break; case RSPQ_EOP: ret = 1; /* FALLTHROUGH */ case RSPQ_NSOP_NEOP: if (mh->mh_tail == NULL) { log(LOG_ERR, "discarding intermediate descriptor entry\n"); m_freem(m); break; } mh->mh_tail->m_next = m; mh->mh_tail = m; mh->mh_head->m_pkthdr.len += len; break; } if (cxgb_debug) printf("len=%d pktlen=%d\n", m->m_len, m->m_pkthdr.len); done: if (++fl->cidx == fl->size) fl->cidx = 0; return (ret); } /** * handle_rsp_cntrl_info - handles control information in a response * @qs: the queue set corresponding to the response * @flags: the response control flags * * Handles the control information of an SGE response, such as GTS * indications and completion credits for the queue set's Tx queues. * HW coalesces credits, we don't do any extra SW coalescing. */ static __inline void handle_rsp_cntrl_info(struct sge_qset *qs, uint32_t flags) { unsigned int credits; #if USE_GTS if (flags & F_RSPD_TXQ0_GTS) clear_bit(TXQ_RUNNING, &qs->txq[TXQ_ETH].flags); #endif credits = G_RSPD_TXQ0_CR(flags); if (credits) qs->txq[TXQ_ETH].processed += credits; credits = G_RSPD_TXQ2_CR(flags); if (credits) qs->txq[TXQ_CTRL].processed += credits; # if USE_GTS if (flags & F_RSPD_TXQ1_GTS) clear_bit(TXQ_RUNNING, &qs->txq[TXQ_OFLD].flags); # endif credits = G_RSPD_TXQ1_CR(flags); if (credits) qs->txq[TXQ_OFLD].processed += credits; } static void check_ring_db(adapter_t *adap, struct sge_qset *qs, unsigned int sleeping) { ; } /** * process_responses - process responses from an SGE response queue * @adap: the adapter * @qs: the queue set to which the response queue belongs * @budget: how many responses can be processed in this round * * Process responses from an SGE response queue up to the supplied budget. * Responses include received packets as well as credits and other events * for the queues that belong to the response queue's queue set. * A negative budget is effectively unlimited. * * Additionally choose the interrupt holdoff time for the next interrupt * on this queue. If the system is under memory shortage use a fairly * long delay to help recovery. */ static int process_responses(adapter_t *adap, struct sge_qset *qs, int budget) { struct sge_rspq *rspq = &qs->rspq; struct rsp_desc *r = &rspq->desc[rspq->cidx]; int budget_left = budget; unsigned int sleeping = 0; #ifdef LRO_SUPPORTED int lro_enabled = qs->lro.enabled; int skip_lro; struct lro_ctrl *lro_ctrl = &qs->lro.ctrl; #endif struct mbuf *offload_mbufs[RX_BUNDLE_SIZE]; int ngathered = 0; #ifdef DEBUG static int last_holdoff = 0; if (cxgb_debug && rspq->holdoff_tmr != last_holdoff) { printf("next_holdoff=%d\n", rspq->holdoff_tmr); last_holdoff = rspq->holdoff_tmr; } #endif rspq->next_holdoff = rspq->holdoff_tmr; while (__predict_true(budget_left && is_new_response(r, rspq))) { int eth, eop = 0, ethpad = 0; uint32_t flags = ntohl(r->flags); uint32_t rss_csum = *(const uint32_t *)r; uint32_t rss_hash = be32toh(r->rss_hdr.rss_hash_val); eth = (r->rss_hdr.opcode == CPL_RX_PKT); if (__predict_false(flags & F_RSPD_ASYNC_NOTIF)) { struct mbuf *m; if (cxgb_debug) printf("async notification\n"); if (rspq->rspq_mh.mh_head == NULL) { rspq->rspq_mh.mh_head = m_gethdr(M_DONTWAIT, MT_DATA); m = rspq->rspq_mh.mh_head; } else { m = m_gethdr(M_DONTWAIT, MT_DATA); } if (m == NULL) goto no_mem; memcpy(mtod(m, char *), r, AN_PKT_SIZE); m->m_len = m->m_pkthdr.len = AN_PKT_SIZE; *mtod(m, char *) = CPL_ASYNC_NOTIF; rss_csum = htonl(CPL_ASYNC_NOTIF << 24); eop = 1; rspq->async_notif++; goto skip; } else if (flags & F_RSPD_IMM_DATA_VALID) { struct mbuf *m = NULL; DPRINTF("IMM DATA VALID opcode=0x%x rspq->cidx=%d\n", r->rss_hdr.opcode, rspq->cidx); if (rspq->rspq_mh.mh_head == NULL) rspq->rspq_mh.mh_head = m_gethdr(M_DONTWAIT, MT_DATA); else m = m_gethdr(M_DONTWAIT, MT_DATA); if (rspq->rspq_mh.mh_head == NULL && m == NULL) { no_mem: rspq->next_holdoff = NOMEM_INTR_DELAY; budget_left--; break; } get_imm_packet(adap, r, rspq->rspq_mh.mh_head); eop = 1; rspq->imm_data++; } else if (r->len_cq) { int drop_thresh = eth ? SGE_RX_DROP_THRES : 0; eop = get_packet(adap, drop_thresh, qs, &rspq->rspq_mh, r); if (eop) { rspq->rspq_mh.mh_head->m_flags |= M_FLOWID; rspq->rspq_mh.mh_head->m_pkthdr.flowid = rss_hash; } ethpad = 2; } else { rspq->pure_rsps++; } skip: if (flags & RSPD_CTRL_MASK) { sleeping |= flags & RSPD_GTS_MASK; handle_rsp_cntrl_info(qs, flags); } r++; if (__predict_false(++rspq->cidx == rspq->size)) { rspq->cidx = 0; rspq->gen ^= 1; r = rspq->desc; } if (++rspq->credits >= (rspq->size / 4)) { refill_rspq(adap, rspq, rspq->credits); rspq->credits = 0; } if (!eth && eop) { rspq->rspq_mh.mh_head->m_pkthdr.csum_data = rss_csum; /* * XXX size mismatch */ m_set_priority(rspq->rspq_mh.mh_head, rss_hash); ngathered = rx_offload(&adap->tdev, rspq, rspq->rspq_mh.mh_head, offload_mbufs, ngathered); rspq->rspq_mh.mh_head = NULL; DPRINTF("received offload packet\n"); } else if (eth && eop) { struct mbuf *m = rspq->rspq_mh.mh_head; t3_rx_eth(adap, rspq, m, ethpad); #ifdef LRO_SUPPORTED /* * The T304 sends incoming packets on any qset. If LRO * is also enabled, we could end up sending packet up * lro_ctrl->ifp's input. That is incorrect. * * The mbuf's rcvif was derived from the cpl header and * is accurate. Skip LRO and just use that. */ skip_lro = __predict_false(qs->port->ifp != m->m_pkthdr.rcvif); if (lro_enabled && lro_ctrl->lro_cnt && !skip_lro && (tcp_lro_rx(lro_ctrl, m, 0) == 0)) { /* successfully queue'd for LRO */ } else #endif { /* * LRO not enabled, packet unsuitable for LRO, * or unable to queue. Pass it up right now in * either case. */ struct ifnet *ifp = m->m_pkthdr.rcvif; (*ifp->if_input)(ifp, m); } rspq->rspq_mh.mh_head = NULL; } __refill_fl_lt(adap, &qs->fl[0], 32); __refill_fl_lt(adap, &qs->fl[1], 32); --budget_left; } deliver_partial_bundle(&adap->tdev, rspq, offload_mbufs, ngathered); #ifdef LRO_SUPPORTED /* Flush LRO */ while (!SLIST_EMPTY(&lro_ctrl->lro_active)) { struct lro_entry *queued = SLIST_FIRST(&lro_ctrl->lro_active); SLIST_REMOVE_HEAD(&lro_ctrl->lro_active, next); tcp_lro_flush(lro_ctrl, queued); } #endif if (sleeping) check_ring_db(adap, qs, sleeping); mb(); /* commit Tx queue processed updates */ if (__predict_false(qs->txq_stopped > 1)) restart_tx(qs); __refill_fl_lt(adap, &qs->fl[0], 512); __refill_fl_lt(adap, &qs->fl[1], 512); budget -= budget_left; return (budget); } /* * A helper function that processes responses and issues GTS. */ static __inline int process_responses_gts(adapter_t *adap, struct sge_rspq *rq) { int work; static int last_holdoff = 0; work = process_responses(adap, rspq_to_qset(rq), -1); if (cxgb_debug && (rq->next_holdoff != last_holdoff)) { printf("next_holdoff=%d\n", rq->next_holdoff); last_holdoff = rq->next_holdoff; } t3_write_reg(adap, A_SG_GTS, V_RSPQ(rq->cntxt_id) | V_NEWTIMER(rq->next_holdoff) | V_NEWINDEX(rq->cidx)); return (work); } /* * Interrupt handler for legacy INTx interrupts for T3B-based cards. * Handles data events from SGE response queues as well as error and other * async events as they all use the same interrupt pin. We use one SGE * response queue per port in this mode and protect all response queues with * queue 0's lock. */ void t3b_intr(void *data) { uint32_t i, map; adapter_t *adap = data; struct sge_rspq *q0 = &adap->sge.qs[0].rspq; t3_write_reg(adap, A_PL_CLI, 0); map = t3_read_reg(adap, A_SG_DATA_INTR); if (!map) return; if (__predict_false(map & F_ERRINTR)) taskqueue_enqueue(adap->tq, &adap->slow_intr_task); mtx_lock(&q0->lock); for_each_port(adap, i) if (map & (1 << i)) process_responses_gts(adap, &adap->sge.qs[i].rspq); mtx_unlock(&q0->lock); } /* * The MSI interrupt handler. This needs to handle data events from SGE * response queues as well as error and other async events as they all use * the same MSI vector. We use one SGE response queue per port in this mode * and protect all response queues with queue 0's lock. */ void t3_intr_msi(void *data) { adapter_t *adap = data; struct sge_rspq *q0 = &adap->sge.qs[0].rspq; int i, new_packets = 0; mtx_lock(&q0->lock); for_each_port(adap, i) if (process_responses_gts(adap, &adap->sge.qs[i].rspq)) new_packets = 1; mtx_unlock(&q0->lock); if (new_packets == 0) taskqueue_enqueue(adap->tq, &adap->slow_intr_task); } void t3_intr_msix(void *data) { struct sge_qset *qs = data; adapter_t *adap = qs->port->adapter; struct sge_rspq *rspq = &qs->rspq; if (process_responses_gts(adap, rspq) == 0) rspq->unhandled_irqs++; } #define QDUMP_SBUF_SIZE 32 * 400 static int t3_dump_rspq(SYSCTL_HANDLER_ARGS) { struct sge_rspq *rspq; struct sge_qset *qs; int i, err, dump_end, idx; static int multiplier = 1; struct sbuf *sb; struct rsp_desc *rspd; uint32_t data[4]; rspq = arg1; qs = rspq_to_qset(rspq); if (rspq->rspq_dump_count == 0) return (0); if (rspq->rspq_dump_count > RSPQ_Q_SIZE) { log(LOG_WARNING, "dump count is too large %d\n", rspq->rspq_dump_count); rspq->rspq_dump_count = 0; return (EINVAL); } if (rspq->rspq_dump_start > (RSPQ_Q_SIZE-1)) { log(LOG_WARNING, "dump start of %d is greater than queue size\n", rspq->rspq_dump_start); rspq->rspq_dump_start = 0; return (EINVAL); } err = t3_sge_read_rspq(qs->port->adapter, rspq->cntxt_id, data); if (err) return (err); retry_sbufops: sb = sbuf_new(NULL, NULL, QDUMP_SBUF_SIZE*multiplier, SBUF_FIXEDLEN); sbuf_printf(sb, " \n index=%u size=%u MSI-X/RspQ=%u intr enable=%u intr armed=%u\n", (data[0] & 0xffff), data[0] >> 16, ((data[2] >> 20) & 0x3f), ((data[2] >> 26) & 1), ((data[2] >> 27) & 1)); sbuf_printf(sb, " generation=%u CQ mode=%u FL threshold=%u\n", ((data[2] >> 28) & 1), ((data[2] >> 31) & 1), data[3]); sbuf_printf(sb, " start=%d -> end=%d\n", rspq->rspq_dump_start, (rspq->rspq_dump_start + rspq->rspq_dump_count) & (RSPQ_Q_SIZE-1)); dump_end = rspq->rspq_dump_start + rspq->rspq_dump_count; for (i = rspq->rspq_dump_start; i < dump_end; i++) { idx = i & (RSPQ_Q_SIZE-1); rspd = &rspq->desc[idx]; sbuf_printf(sb, "\tidx=%04d opcode=%02x cpu_idx=%x hash_type=%x cq_idx=%x\n", idx, rspd->rss_hdr.opcode, rspd->rss_hdr.cpu_idx, rspd->rss_hdr.hash_type, be16toh(rspd->rss_hdr.cq_idx)); sbuf_printf(sb, "\trss_hash_val=%x flags=%08x len_cq=%x intr_gen=%x\n", rspd->rss_hdr.rss_hash_val, be32toh(rspd->flags), be32toh(rspd->len_cq), rspd->intr_gen); } if (sbuf_overflowed(sb)) { sbuf_delete(sb); multiplier++; goto retry_sbufops; } sbuf_finish(sb); err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return (err); } static int t3_dump_txq_eth(SYSCTL_HANDLER_ARGS) { struct sge_txq *txq; struct sge_qset *qs; int i, j, err, dump_end; static int multiplier = 1; struct sbuf *sb; struct tx_desc *txd; uint32_t *WR, wr_hi, wr_lo, gen; uint32_t data[4]; txq = arg1; qs = txq_to_qset(txq, TXQ_ETH); if (txq->txq_dump_count == 0) { return (0); } if (txq->txq_dump_count > TX_ETH_Q_SIZE) { log(LOG_WARNING, "dump count is too large %d\n", txq->txq_dump_count); txq->txq_dump_count = 1; return (EINVAL); } if (txq->txq_dump_start > (TX_ETH_Q_SIZE-1)) { log(LOG_WARNING, "dump start of %d is greater than queue size\n", txq->txq_dump_start); txq->txq_dump_start = 0; return (EINVAL); } err = t3_sge_read_ecntxt(qs->port->adapter, qs->rspq.cntxt_id, data); if (err) return (err); retry_sbufops: sb = sbuf_new(NULL, NULL, QDUMP_SBUF_SIZE*multiplier, SBUF_FIXEDLEN); sbuf_printf(sb, " \n credits=%u GTS=%u index=%u size=%u rspq#=%u cmdq#=%u\n", (data[0] & 0x7fff), ((data[0] >> 15) & 1), (data[0] >> 16), (data[1] & 0xffff), ((data[3] >> 4) & 7), ((data[3] >> 7) & 1)); sbuf_printf(sb, " TUN=%u TOE=%u generation%u uP token=%u valid=%u\n", ((data[3] >> 8) & 1), ((data[3] >> 9) & 1), ((data[3] >> 10) & 1), ((data[3] >> 11) & 0xfffff), ((data[3] >> 31) & 1)); sbuf_printf(sb, " qid=%d start=%d -> end=%d\n", qs->idx, txq->txq_dump_start, (txq->txq_dump_start + txq->txq_dump_count) & (TX_ETH_Q_SIZE-1)); dump_end = txq->txq_dump_start + txq->txq_dump_count; for (i = txq->txq_dump_start; i < dump_end; i++) { txd = &txq->desc[i & (TX_ETH_Q_SIZE-1)]; WR = (uint32_t *)txd->flit; wr_hi = ntohl(WR[0]); wr_lo = ntohl(WR[1]); gen = G_WR_GEN(wr_lo); sbuf_printf(sb," wr_hi %08x wr_lo %08x gen %d\n", wr_hi, wr_lo, gen); for (j = 2; j < 30; j += 4) sbuf_printf(sb, "\t%08x %08x %08x %08x \n", WR[j], WR[j + 1], WR[j + 2], WR[j + 3]); } if (sbuf_overflowed(sb)) { sbuf_delete(sb); multiplier++; goto retry_sbufops; } sbuf_finish(sb); err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return (err); } static int t3_dump_txq_ctrl(SYSCTL_HANDLER_ARGS) { struct sge_txq *txq; struct sge_qset *qs; int i, j, err, dump_end; static int multiplier = 1; struct sbuf *sb; struct tx_desc *txd; uint32_t *WR, wr_hi, wr_lo, gen; txq = arg1; qs = txq_to_qset(txq, TXQ_CTRL); if (txq->txq_dump_count == 0) { return (0); } if (txq->txq_dump_count > 256) { log(LOG_WARNING, "dump count is too large %d\n", txq->txq_dump_count); txq->txq_dump_count = 1; return (EINVAL); } if (txq->txq_dump_start > 255) { log(LOG_WARNING, "dump start of %d is greater than queue size\n", txq->txq_dump_start); txq->txq_dump_start = 0; return (EINVAL); } retry_sbufops: sb = sbuf_new(NULL, NULL, QDUMP_SBUF_SIZE*multiplier, SBUF_FIXEDLEN); sbuf_printf(sb, " qid=%d start=%d -> end=%d\n", qs->idx, txq->txq_dump_start, (txq->txq_dump_start + txq->txq_dump_count) & 255); dump_end = txq->txq_dump_start + txq->txq_dump_count; for (i = txq->txq_dump_start; i < dump_end; i++) { txd = &txq->desc[i & (255)]; WR = (uint32_t *)txd->flit; wr_hi = ntohl(WR[0]); wr_lo = ntohl(WR[1]); gen = G_WR_GEN(wr_lo); sbuf_printf(sb," wr_hi %08x wr_lo %08x gen %d\n", wr_hi, wr_lo, gen); for (j = 2; j < 30; j += 4) sbuf_printf(sb, "\t%08x %08x %08x %08x \n", WR[j], WR[j + 1], WR[j + 2], WR[j + 3]); } if (sbuf_overflowed(sb)) { sbuf_delete(sb); multiplier++; goto retry_sbufops; } sbuf_finish(sb); err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); return (err); } static int t3_set_coalesce_usecs(SYSCTL_HANDLER_ARGS) { adapter_t *sc = arg1; struct qset_params *qsp = &sc->params.sge.qset[0]; int coalesce_usecs; struct sge_qset *qs; int i, j, err, nqsets = 0; struct mtx *lock; if ((sc->flags & FULL_INIT_DONE) == 0) return (ENXIO); coalesce_usecs = qsp->coalesce_usecs; err = sysctl_handle_int(oidp, &coalesce_usecs, arg2, req); if (err != 0) { return (err); } if (coalesce_usecs == qsp->coalesce_usecs) return (0); for (i = 0; i < sc->params.nports; i++) for (j = 0; j < sc->port[i].nqsets; j++) nqsets++; coalesce_usecs = max(1, coalesce_usecs); for (i = 0; i < nqsets; i++) { qs = &sc->sge.qs[i]; qsp = &sc->params.sge.qset[i]; qsp->coalesce_usecs = coalesce_usecs; lock = (sc->flags & USING_MSIX) ? &qs->rspq.lock : &sc->sge.qs[0].rspq.lock; mtx_lock(lock); t3_update_qset_coalesce(qs, qsp); t3_write_reg(sc, A_SG_GTS, V_RSPQ(qs->rspq.cntxt_id) | V_NEWTIMER(qs->rspq.holdoff_tmr)); mtx_unlock(lock); } return (0); } void t3_add_attach_sysctls(adapter_t *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children; ctx = device_get_sysctl_ctx(sc->dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); /* random information */ SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version", CTLFLAG_RD, &sc->fw_version, 0, "firmware version"); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "hw_revision", CTLFLAG_RD, &sc->params.rev, 0, "chip model"); SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "port_types", CTLFLAG_RD, &sc->port_types, 0, "type of ports"); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "enable_debug", CTLFLAG_RW, &cxgb_debug, 0, "enable verbose debugging output"); SYSCTL_ADD_QUAD(ctx, children, OID_AUTO, "tunq_coalesce", CTLFLAG_RD, &sc->tunq_coalesce, "#tunneled packets freed"); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "txq_overrun", CTLFLAG_RD, &txq_fills, 0, "#times txq overrun"); } static const char *rspq_name = "rspq"; static const char *txq_names[] = { "txq_eth", "txq_ofld", "txq_ctrl" }; static int sysctl_handle_macstat(SYSCTL_HANDLER_ARGS) { struct port_info *p = arg1; uint64_t *parg; if (!p) return (EINVAL); parg = (uint64_t *) ((uint8_t *)&p->mac.stats + arg2); PORT_LOCK(p); t3_mac_update_stats(&p->mac); PORT_UNLOCK(p); return (sysctl_handle_quad(oidp, parg, 0, req)); } void t3_add_configured_sysctls(adapter_t *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children; int i, j; ctx = device_get_sysctl_ctx(sc->dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal", CTLTYPE_INT|CTLFLAG_RW, sc, 0, t3_set_coalesce_usecs, "I", "interrupt coalescing timer (us)"); for (i = 0; i < sc->params.nports; i++) { struct port_info *pi = &sc->port[i]; struct sysctl_oid *poid; struct sysctl_oid_list *poidlist; struct mac_stats *mstats = &pi->mac.stats; snprintf(pi->namebuf, PORT_NAME_LEN, "port%d", i); poid = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, pi->namebuf, CTLFLAG_RD, NULL, "port statistics"); poidlist = SYSCTL_CHILDREN(poid); SYSCTL_ADD_INT(ctx, poidlist, OID_AUTO, "nqsets", CTLFLAG_RD, &pi->nqsets, 0, "#queue sets"); for (j = 0; j < pi->nqsets; j++) { struct sge_qset *qs = &sc->sge.qs[pi->first_qset + j]; struct sysctl_oid *qspoid, *rspqpoid, *txqpoid, *ctrlqpoid, *lropoid; struct sysctl_oid_list *qspoidlist, *rspqpoidlist, *txqpoidlist, *ctrlqpoidlist, *lropoidlist; struct sge_txq *txq = &qs->txq[TXQ_ETH]; snprintf(qs->namebuf, QS_NAME_LEN, "qs%d", j); qspoid = SYSCTL_ADD_NODE(ctx, poidlist, OID_AUTO, qs->namebuf, CTLFLAG_RD, NULL, "qset statistics"); qspoidlist = SYSCTL_CHILDREN(qspoid); SYSCTL_ADD_UINT(ctx, qspoidlist, OID_AUTO, "fl0_empty", CTLFLAG_RD, &qs->fl[0].empty, 0, "freelist #0 empty"); SYSCTL_ADD_UINT(ctx, qspoidlist, OID_AUTO, "fl1_empty", CTLFLAG_RD, &qs->fl[1].empty, 0, "freelist #1 empty"); rspqpoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, rspq_name, CTLFLAG_RD, NULL, "rspq statistics"); rspqpoidlist = SYSCTL_CHILDREN(rspqpoid); txqpoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, txq_names[0], CTLFLAG_RD, NULL, "txq statistics"); txqpoidlist = SYSCTL_CHILDREN(txqpoid); ctrlqpoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, txq_names[2], CTLFLAG_RD, NULL, "ctrlq statistics"); ctrlqpoidlist = SYSCTL_CHILDREN(ctrlqpoid); lropoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, "lro_stats", CTLFLAG_RD, NULL, "LRO statistics"); lropoidlist = SYSCTL_CHILDREN(lropoid); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "size", CTLFLAG_RD, &qs->rspq.size, 0, "#entries in response queue"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "cidx", CTLFLAG_RD, &qs->rspq.cidx, 0, "consumer index"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "credits", CTLFLAG_RD, &qs->rspq.credits, 0, "#credits"); SYSCTL_ADD_XLONG(ctx, rspqpoidlist, OID_AUTO, "phys_addr", CTLFLAG_RD, &qs->rspq.phys_addr, "physical_address_of the queue"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "dump_start", CTLFLAG_RW, &qs->rspq.rspq_dump_start, 0, "start rspq dump entry"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "dump_count", CTLFLAG_RW, &qs->rspq.rspq_dump_count, 0, "#rspq entries to dump"); SYSCTL_ADD_PROC(ctx, rspqpoidlist, OID_AUTO, "qdump", CTLTYPE_STRING | CTLFLAG_RD, &qs->rspq, 0, t3_dump_rspq, "A", "dump of the response queue"); SYSCTL_ADD_INT(ctx, txqpoidlist, OID_AUTO, "dropped", CTLFLAG_RD, &qs->txq[TXQ_ETH].txq_drops, 0, "#tunneled packets dropped"); SYSCTL_ADD_INT(ctx, txqpoidlist, OID_AUTO, "sendqlen", CTLFLAG_RD, &qs->txq[TXQ_ETH].sendq.qlen, 0, "#tunneled packets waiting to be sent"); #if 0 SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "queue_pidx", CTLFLAG_RD, (uint32_t *)(uintptr_t)&qs->txq[TXQ_ETH].txq_mr.br_prod, 0, "#tunneled packets queue producer index"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "queue_cidx", CTLFLAG_RD, (uint32_t *)(uintptr_t)&qs->txq[TXQ_ETH].txq_mr.br_cons, 0, "#tunneled packets queue consumer index"); #endif SYSCTL_ADD_INT(ctx, txqpoidlist, OID_AUTO, "processed", CTLFLAG_RD, &qs->txq[TXQ_ETH].processed, 0, "#tunneled packets processed by the card"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "cleaned", CTLFLAG_RD, &txq->cleaned, 0, "#tunneled packets cleaned"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "in_use", CTLFLAG_RD, &txq->in_use, 0, "#tunneled packet slots in use"); SYSCTL_ADD_ULONG(ctx, txqpoidlist, OID_AUTO, "frees", CTLFLAG_RD, &txq->txq_frees, "#tunneled packets freed"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "skipped", CTLFLAG_RD, &txq->txq_skipped, 0, "#tunneled packet descriptors skipped"); SYSCTL_ADD_QUAD(ctx, txqpoidlist, OID_AUTO, "coalesced", CTLFLAG_RD, &txq->txq_coalesced, "#tunneled packets coalesced"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "enqueued", CTLFLAG_RD, &txq->txq_enqueued, 0, "#tunneled packets enqueued to hardware"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "stopped_flags", CTLFLAG_RD, &qs->txq_stopped, 0, "tx queues stopped"); SYSCTL_ADD_XLONG(ctx, txqpoidlist, OID_AUTO, "phys_addr", CTLFLAG_RD, &txq->phys_addr, "physical_address_of the queue"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "qgen", CTLFLAG_RW, &qs->txq[TXQ_ETH].gen, 0, "txq generation"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "hw_cidx", CTLFLAG_RD, &txq->cidx, 0, "hardware queue cidx"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "hw_pidx", CTLFLAG_RD, &txq->pidx, 0, "hardware queue pidx"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "dump_start", CTLFLAG_RW, &qs->txq[TXQ_ETH].txq_dump_start, 0, "txq start idx for dump"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "dump_count", CTLFLAG_RW, &qs->txq[TXQ_ETH].txq_dump_count, 0, "txq #entries to dump"); SYSCTL_ADD_PROC(ctx, txqpoidlist, OID_AUTO, "qdump", CTLTYPE_STRING | CTLFLAG_RD, &qs->txq[TXQ_ETH], 0, t3_dump_txq_eth, "A", "dump of the transmit queue"); SYSCTL_ADD_UINT(ctx, ctrlqpoidlist, OID_AUTO, "dump_start", CTLFLAG_RW, &qs->txq[TXQ_CTRL].txq_dump_start, 0, "ctrlq start idx for dump"); SYSCTL_ADD_UINT(ctx, ctrlqpoidlist, OID_AUTO, "dump_count", CTLFLAG_RW, &qs->txq[TXQ_CTRL].txq_dump_count, 0, "ctrl #entries to dump"); SYSCTL_ADD_PROC(ctx, ctrlqpoidlist, OID_AUTO, "qdump", CTLTYPE_STRING | CTLFLAG_RD, &qs->txq[TXQ_CTRL], 0, t3_dump_txq_ctrl, "A", "dump of the transmit queue"); #ifdef LRO_SUPPORTED SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_queued", CTLFLAG_RD, &qs->lro.ctrl.lro_queued, 0, NULL); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_flushed", CTLFLAG_RD, &qs->lro.ctrl.lro_flushed, 0, NULL); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_bad_csum", CTLFLAG_RD, &qs->lro.ctrl.lro_bad_csum, 0, NULL); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_cnt", CTLFLAG_RD, &qs->lro.ctrl.lro_cnt, 0, NULL); #endif } /* Now add a node for mac stats. */ poid = SYSCTL_ADD_NODE(ctx, poidlist, OID_AUTO, "mac_stats", CTLFLAG_RD, NULL, "MAC statistics"); poidlist = SYSCTL_CHILDREN(poid); /* * We (ab)use the length argument (arg2) to pass on the offset * of the data that we are interested in. This is only required * for the quad counters that are updated from the hardware (we * make sure that we return the latest value). * sysctl_handle_macstat first updates *all* the counters from * the hardware, and then returns the latest value of the * requested counter. Best would be to update only the * requested counter from hardware, but t3_mac_update_stats() * hides all the register details and we don't want to dive into * all that here. */ #define CXGB_SYSCTL_ADD_QUAD(a) SYSCTL_ADD_OID(ctx, poidlist, OID_AUTO, #a, \ (CTLTYPE_QUAD | CTLFLAG_RD), pi, offsetof(struct mac_stats, a), \ sysctl_handle_macstat, "QU", 0) CXGB_SYSCTL_ADD_QUAD(tx_octets); CXGB_SYSCTL_ADD_QUAD(tx_octets_bad); CXGB_SYSCTL_ADD_QUAD(tx_frames); CXGB_SYSCTL_ADD_QUAD(tx_mcast_frames); CXGB_SYSCTL_ADD_QUAD(tx_bcast_frames); CXGB_SYSCTL_ADD_QUAD(tx_pause); CXGB_SYSCTL_ADD_QUAD(tx_deferred); CXGB_SYSCTL_ADD_QUAD(tx_late_collisions); CXGB_SYSCTL_ADD_QUAD(tx_total_collisions); CXGB_SYSCTL_ADD_QUAD(tx_excess_collisions); CXGB_SYSCTL_ADD_QUAD(tx_underrun); CXGB_SYSCTL_ADD_QUAD(tx_len_errs); CXGB_SYSCTL_ADD_QUAD(tx_mac_internal_errs); CXGB_SYSCTL_ADD_QUAD(tx_excess_deferral); CXGB_SYSCTL_ADD_QUAD(tx_fcs_errs); CXGB_SYSCTL_ADD_QUAD(tx_frames_64); CXGB_SYSCTL_ADD_QUAD(tx_frames_65_127); CXGB_SYSCTL_ADD_QUAD(tx_frames_128_255); CXGB_SYSCTL_ADD_QUAD(tx_frames_256_511); CXGB_SYSCTL_ADD_QUAD(tx_frames_512_1023); CXGB_SYSCTL_ADD_QUAD(tx_frames_1024_1518); CXGB_SYSCTL_ADD_QUAD(tx_frames_1519_max); CXGB_SYSCTL_ADD_QUAD(rx_octets); CXGB_SYSCTL_ADD_QUAD(rx_octets_bad); CXGB_SYSCTL_ADD_QUAD(rx_frames); CXGB_SYSCTL_ADD_QUAD(rx_mcast_frames); CXGB_SYSCTL_ADD_QUAD(rx_bcast_frames); CXGB_SYSCTL_ADD_QUAD(rx_pause); CXGB_SYSCTL_ADD_QUAD(rx_fcs_errs); CXGB_SYSCTL_ADD_QUAD(rx_align_errs); CXGB_SYSCTL_ADD_QUAD(rx_symbol_errs); CXGB_SYSCTL_ADD_QUAD(rx_data_errs); CXGB_SYSCTL_ADD_QUAD(rx_sequence_errs); CXGB_SYSCTL_ADD_QUAD(rx_runt); CXGB_SYSCTL_ADD_QUAD(rx_jabber); CXGB_SYSCTL_ADD_QUAD(rx_short); CXGB_SYSCTL_ADD_QUAD(rx_too_long); CXGB_SYSCTL_ADD_QUAD(rx_mac_internal_errs); CXGB_SYSCTL_ADD_QUAD(rx_cong_drops); CXGB_SYSCTL_ADD_QUAD(rx_frames_64); CXGB_SYSCTL_ADD_QUAD(rx_frames_65_127); CXGB_SYSCTL_ADD_QUAD(rx_frames_128_255); CXGB_SYSCTL_ADD_QUAD(rx_frames_256_511); CXGB_SYSCTL_ADD_QUAD(rx_frames_512_1023); CXGB_SYSCTL_ADD_QUAD(rx_frames_1024_1518); CXGB_SYSCTL_ADD_QUAD(rx_frames_1519_max); #undef CXGB_SYSCTL_ADD_QUAD #define CXGB_SYSCTL_ADD_ULONG(a) SYSCTL_ADD_ULONG(ctx, poidlist, OID_AUTO, #a, \ CTLFLAG_RD, &mstats->a, 0) CXGB_SYSCTL_ADD_ULONG(tx_fifo_parity_err); CXGB_SYSCTL_ADD_ULONG(rx_fifo_parity_err); CXGB_SYSCTL_ADD_ULONG(tx_fifo_urun); CXGB_SYSCTL_ADD_ULONG(rx_fifo_ovfl); CXGB_SYSCTL_ADD_ULONG(serdes_signal_loss); CXGB_SYSCTL_ADD_ULONG(xaui_pcs_ctc_err); CXGB_SYSCTL_ADD_ULONG(xaui_pcs_align_change); CXGB_SYSCTL_ADD_ULONG(num_toggled); CXGB_SYSCTL_ADD_ULONG(num_resets); CXGB_SYSCTL_ADD_ULONG(link_faults); #undef CXGB_SYSCTL_ADD_ULONG } } /** * t3_get_desc - dump an SGE descriptor for debugging purposes * @qs: the queue set * @qnum: identifies the specific queue (0..2: Tx, 3:response, 4..5: Rx) * @idx: the descriptor index in the queue * @data: where to dump the descriptor contents * * Dumps the contents of a HW descriptor of an SGE queue. Returns the * size of the descriptor. */ int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx, unsigned char *data) { if (qnum >= 6) return (EINVAL); if (qnum < 3) { if (!qs->txq[qnum].desc || idx >= qs->txq[qnum].size) return -EINVAL; memcpy(data, &qs->txq[qnum].desc[idx], sizeof(struct tx_desc)); return sizeof(struct tx_desc); } if (qnum == 3) { if (!qs->rspq.desc || idx >= qs->rspq.size) return (EINVAL); memcpy(data, &qs->rspq.desc[idx], sizeof(struct rsp_desc)); return sizeof(struct rsp_desc); } qnum -= 4; if (!qs->fl[qnum].desc || idx >= qs->fl[qnum].size) return (EINVAL); memcpy(data, &qs->fl[qnum].desc[idx], sizeof(struct rx_desc)); return sizeof(struct rx_desc); } Index: projects/ppc64/sys/dev/fb/fb.c =================================================================== --- projects/ppc64/sys/dev/fb/fb.c (revision 204271) +++ projects/ppc64/sys/dev/fb/fb.c (revision 204272) @@ -1,761 +1,761 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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_fb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include SET_DECLARE(videodriver_set, const video_driver_t); /* local arrays */ /* * We need at least one entry each in order to initialize a video card * for the kernel console. The arrays will be increased dynamically * when necessary. */ static int vid_malloc; static int adapters = 1; static video_adapter_t *adp_ini; static video_adapter_t **adapter = &adp_ini; static video_switch_t *vidsw_ini; video_switch_t **vidsw = &vidsw_ini; #ifdef FB_INSTALL_CDEV static struct cdevsw *vidcdevsw_ini; static struct cdevsw **vidcdevsw = &vidcdevsw_ini; #endif #define ARRAY_DELTA 4 static int vid_realloc_array(void) { video_adapter_t **new_adp; video_switch_t **new_vidsw; #ifdef FB_INSTALL_CDEV struct cdevsw **new_cdevsw; #endif int newsize; int s; if (!vid_malloc) return ENOMEM; s = spltty(); newsize = ((adapters + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA; new_adp = malloc(sizeof(*new_adp)*newsize, M_DEVBUF, M_WAITOK | M_ZERO); new_vidsw = malloc(sizeof(*new_vidsw)*newsize, M_DEVBUF, M_WAITOK | M_ZERO); #ifdef FB_INSTALL_CDEV new_cdevsw = malloc(sizeof(*new_cdevsw)*newsize, M_DEVBUF, M_WAITOK | M_ZERO); #endif bcopy(adapter, new_adp, sizeof(*adapter)*adapters); bcopy(vidsw, new_vidsw, sizeof(*vidsw)*adapters); #ifdef FB_INSTALL_CDEV bcopy(vidcdevsw, new_cdevsw, sizeof(*vidcdevsw)*adapters); #endif if (adapters > 1) { free(adapter, M_DEVBUF); free(vidsw, M_DEVBUF); #ifdef FB_INSTALL_CDEV free(vidcdevsw, M_DEVBUF); #endif } adapter = new_adp; vidsw = new_vidsw; #ifdef FB_INSTALL_CDEV vidcdevsw = new_cdevsw; #endif adapters = newsize; splx(s); if (bootverbose) printf("fb: new array size %d\n", adapters); return 0; } static void vid_malloc_init(void *arg) { vid_malloc = TRUE; } SYSINIT(vid_mem, SI_SUB_KMEM, SI_ORDER_ANY, vid_malloc_init, NULL); /* * Low-level frame buffer driver functions * frame buffer subdrivers, such as the VGA driver, call these functions * to initialize the video_adapter structure and register it to the virtual * frame buffer driver `fb'. */ /* initialize the video_adapter_t structure */ void vid_init_struct(video_adapter_t *adp, char *name, int type, int unit) { adp->va_flags = 0; adp->va_name = name; adp->va_type = type; adp->va_unit = unit; } /* Register a video adapter */ int vid_register(video_adapter_t *adp) { const video_driver_t **list; const video_driver_t *p; int index; for (index = 0; index < adapters; ++index) { if (adapter[index] == NULL) break; } if (index >= adapters) { if (vid_realloc_array()) return -1; } adp->va_index = index; adp->va_token = NULL; SET_FOREACH(list, videodriver_set) { p = *list; if (strcmp(p->name, adp->va_name) == 0) { adapter[index] = adp; vidsw[index] = p->vidsw; return index; } } return -1; } int vid_unregister(video_adapter_t *adp) { if ((adp->va_index < 0) || (adp->va_index >= adapters)) return ENOENT; if (adapter[adp->va_index] != adp) return ENOENT; adapter[adp->va_index] = NULL; vidsw[adp->va_index] = NULL; return 0; } /* Get video I/O function table */ video_switch_t *vid_get_switch(char *name) { const video_driver_t **list; const video_driver_t *p; SET_FOREACH(list, videodriver_set) { p = *list; if (strcmp(p->name, name) == 0) return p->vidsw; } return NULL; } /* * Video card client functions * Video card clients, such as the console driver `syscons' and the frame * buffer cdev driver, use these functions to claim and release a card for * exclusive use. */ /* find the video card specified by a driver name and a unit number */ int vid_find_adapter(char *driver, int unit) { int i; for (i = 0; i < adapters; ++i) { if (adapter[i] == NULL) continue; if (strcmp("*", driver) && strcmp(adapter[i]->va_name, driver)) continue; if ((unit != -1) && (adapter[i]->va_unit != unit)) continue; return i; } return -1; } /* allocate a video card */ int vid_allocate(char *driver, int unit, void *id) { int index; int s; s = spltty(); index = vid_find_adapter(driver, unit); if (index >= 0) { if (adapter[index]->va_token) { splx(s); return -1; } adapter[index]->va_token = id; } splx(s); return index; } int vid_release(video_adapter_t *adp, void *id) { int error; int s; s = spltty(); if (adp->va_token == NULL) { error = EINVAL; } else if (adp->va_token != id) { error = EPERM; } else { adp->va_token = NULL; error = 0; } splx(s); return error; } /* Get a video adapter structure */ video_adapter_t *vid_get_adapter(int index) { if ((index < 0) || (index >= adapters)) return NULL; return adapter[index]; } /* Configure drivers: this is a backdoor for the console driver XXX */ int vid_configure(int flags) { const video_driver_t **list; const video_driver_t *p; SET_FOREACH(list, videodriver_set) { p = *list; if (p->configure != NULL) (*p->configure)(flags); } return 0; } /* * Virtual frame buffer cdev driver functions * The virtual frame buffer driver dispatches driver functions to * appropriate subdrivers. */ #define FB_DRIVER_NAME "fb" #ifdef FB_INSTALL_CDEV #if 0 /* experimental */ static devclass_t fb_devclass; static int fbprobe(device_t dev); static int fbattach(device_t dev); static device_method_t fb_methods[] = { DEVMETHOD(device_probe, fbprobe), DEVMETHOD(device_attach, fbattach), DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t fb_driver = { FB_DRIVER_NAME, fb_methods, 0, }; static int fbprobe(device_t dev) { int unit; unit = device_get_unit(dev); if (unit >= adapters) return ENXIO; if (adapter[unit] == NULL) return ENXIO; device_set_desc(dev, "generic frame buffer"); return 0; } static int fbattach(device_t dev) { printf("fbattach: about to attach children\n"); bus_generic_attach(dev); return 0; } #endif #define FB_UNIT(dev) dev2unit(dev) #define FB_MKMINOR(unit) (u) #if 0 /* experimental */ static d_open_t fbopen; static d_close_t fbclose; static d_read_t fbread; static d_write_t fbwrite; static d_ioctl_t fbioctl; static d_mmap_t fbmmap; static struct cdevsw fb_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = fbopen, .d_close = fbclose, .d_read = fbread, .d_write = fbwrite, .d_ioctl = fbioctl, .d_mmap = fbmmap, .d_name = FB_DRIVER_NAME, }; #endif static int fb_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: break; case MOD_UNLOAD: printf("fb module unload - not possible for this module type\n"); return EINVAL; default: return EOPNOTSUPP; } return 0; } static moduledata_t fb_mod = { "fb", fb_modevent, NULL }; DECLARE_MODULE(fb, fb_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); int fb_attach(int unit, video_adapter_t *adp, struct cdevsw *cdevsw) { int s; if (adp->va_index >= adapters) return EINVAL; if (adapter[adp->va_index] != adp) return EINVAL; s = spltty(); adp->va_minor = unit; vidcdevsw[adp->va_index] = cdevsw; splx(s); printf("fb%d at %s%d\n", adp->va_index, adp->va_name, adp->va_unit); return 0; } int fb_detach(int unit, video_adapter_t *adp, struct cdevsw *cdevsw) { int s; if (adp->va_index >= adapters) return EINVAL; if (adapter[adp->va_index] != adp) return EINVAL; if (vidcdevsw[adp->va_index] != cdevsw) return EINVAL; s = spltty(); vidcdevsw[adp->va_index] = NULL; splx(s); return 0; } /* * Generic frame buffer cdev driver functions * Frame buffer subdrivers may call these functions to implement common * driver functions. */ int genfbopen(genfb_softc_t *sc, video_adapter_t *adp, int flag, int mode, struct thread *td) { int s; s = spltty(); if (!(sc->gfb_flags & FB_OPEN)) sc->gfb_flags |= FB_OPEN; splx(s); return 0; } int genfbclose(genfb_softc_t *sc, video_adapter_t *adp, int flag, int mode, struct thread *td) { int s; s = spltty(); sc->gfb_flags &= ~FB_OPEN; splx(s); return 0; } int genfbread(genfb_softc_t *sc, video_adapter_t *adp, struct uio *uio, int flag) { int size; int offset; int error; int len; error = 0; size = adp->va_buffer_size/adp->va_info.vi_planes; while (uio->uio_resid > 0) { if (uio->uio_offset >= size) break; offset = uio->uio_offset%adp->va_window_size; len = imin(uio->uio_resid, size - uio->uio_offset); len = imin(len, adp->va_window_size - offset); if (len <= 0) break; vidd_set_win_org(adp, uio->uio_offset); error = uiomove((caddr_t)(adp->va_window + offset), len, uio); if (error) break; } return error; } int genfbwrite(genfb_softc_t *sc, video_adapter_t *adp, struct uio *uio, int flag) { return ENODEV; } int genfbioctl(genfb_softc_t *sc, video_adapter_t *adp, u_long cmd, caddr_t arg, int flag, struct thread *td) { int error; if (adp == NULL) /* XXX */ return ENXIO; error = vidd_ioctl(adp, cmd, arg); if (error == ENOIOCTL) error = ENODEV; return error; } int genfbmmap(genfb_softc_t *sc, video_adapter_t *adp, vm_ooffset_t offset, vm_offset_t *paddr, int prot, vm_memattr_t *memattr) { return vidd_mmap(adp, offset, paddr, prot, memattr); } #endif /* FB_INSTALL_CDEV */ static char *adapter_name(int type) { static struct { int type; char *name; } names[] = { { KD_MONO, "MDA" }, { KD_HERCULES, "Hercules" }, { KD_CGA, "CGA" }, { KD_EGA, "EGA" }, { KD_VGA, "VGA" }, { KD_PC98, "PC-98x1" }, { KD_TGA, "TGA" }, { -1, "Unknown" }, }; int i; for (i = 0; names[i].type != -1; ++i) if (names[i].type == type) break; return names[i].name; } /* * Generic low-level frame buffer functions * The low-level functions in the frame buffer subdriver may use these * functions. */ void fb_dump_adp_info(char *driver, video_adapter_t *adp, int level) { if (level <= 0) return; printf("%s%d: %s%d, %s, type:%s (%d), flags:0x%x\n", FB_DRIVER_NAME, adp->va_index, driver, adp->va_unit, adp->va_name, adapter_name(adp->va_type), adp->va_type, adp->va_flags); printf("%s%d: port:0x%lx-0x%lx, crtc:0x%lx, mem:0x%lx 0x%x\n", FB_DRIVER_NAME, adp->va_index, (u_long)adp->va_io_base, (u_long)adp->va_io_base + adp->va_io_size - 1, (u_long)adp->va_crtc_addr, (u_long)adp->va_mem_base, adp->va_mem_size); printf("%s%d: init mode:%d, bios mode:%d, current mode:%d\n", FB_DRIVER_NAME, adp->va_index, adp->va_initial_mode, adp->va_initial_bios_mode, adp->va_mode); printf("%s%d: window:%p size:%dk gran:%dk, buf:%p size:%dk\n", FB_DRIVER_NAME, adp->va_index, (void *)adp->va_window, (int)adp->va_window_size/1024, (int)adp->va_window_gran/1024, (void *)adp->va_buffer, (int)adp->va_buffer_size/1024); } void fb_dump_mode_info(char *driver, video_adapter_t *adp, video_info_t *info, int level) { if (level <= 0) return; printf("%s%d: %s, mode:%d, flags:0x%x ", driver, adp->va_unit, adp->va_name, info->vi_mode, info->vi_flags); if (info->vi_flags & V_INFO_GRAPHICS) printf("G %dx%dx%d, %d plane(s), font:%dx%d, ", info->vi_width, info->vi_height, info->vi_depth, info->vi_planes, info->vi_cwidth, info->vi_cheight); else printf("T %dx%d, font:%dx%d, ", info->vi_width, info->vi_height, info->vi_cwidth, info->vi_cheight); printf("win:0x%lx\n", (u_long)info->vi_window); } int fb_type(int adp_type) { static struct { int fb_type; int va_type; } types[] = { { FBTYPE_MDA, KD_MONO }, { FBTYPE_HERCULES, KD_HERCULES }, { FBTYPE_CGA, KD_CGA }, { FBTYPE_EGA, KD_EGA }, { FBTYPE_VGA, KD_VGA }, { FBTYPE_PC98, KD_PC98 }, { FBTYPE_TGA, KD_TGA }, }; int i; for (i = 0; i < sizeof(types)/sizeof(types[0]); ++i) { if (types[i].va_type == adp_type) return types[i].fb_type; } return -1; } int fb_commonioctl(video_adapter_t *adp, u_long cmd, caddr_t arg) { int error; int s; /* assert(adp != NULL) */ error = 0; s = spltty(); switch (cmd) { case FBIO_ADAPTER: /* get video adapter index */ *(int *)arg = adp->va_index; break; case FBIO_ADPTYPE: /* get video adapter type */ *(int *)arg = adp->va_type; break; case FBIO_ADPINFO: /* get video adapter info */ ((video_adapter_info_t *)arg)->va_index = adp->va_index; ((video_adapter_info_t *)arg)->va_type = adp->va_type; bcopy(adp->va_name, ((video_adapter_info_t *)arg)->va_name, imin(strlen(adp->va_name) + 1, sizeof(((video_adapter_info_t *)arg)->va_name))); ((video_adapter_info_t *)arg)->va_unit = adp->va_unit; ((video_adapter_info_t *)arg)->va_flags = adp->va_flags; ((video_adapter_info_t *)arg)->va_io_base = adp->va_io_base; ((video_adapter_info_t *)arg)->va_io_size = adp->va_io_size; ((video_adapter_info_t *)arg)->va_crtc_addr = adp->va_crtc_addr; ((video_adapter_info_t *)arg)->va_mem_base = adp->va_mem_base; ((video_adapter_info_t *)arg)->va_mem_size = adp->va_mem_size; ((video_adapter_info_t *)arg)->va_window -#ifdef __i386__ +#if defined(__amd64__) || defined(__i386__) = vtophys(adp->va_window); #else = adp->va_window; #endif ((video_adapter_info_t *)arg)->va_window_size = adp->va_window_size; ((video_adapter_info_t *)arg)->va_window_gran = adp->va_window_gran; ((video_adapter_info_t *)arg)->va_window_orig = adp->va_window_orig; ((video_adapter_info_t *)arg)->va_unused0 -#ifdef __i386__ - = (adp->va_buffer) ? vtophys(adp->va_buffer) : 0; +#if defined(__amd64__) || defined(__i386__) + = adp->va_buffer != 0 ? vtophys(adp->va_buffer) : 0; #else = adp->va_buffer; #endif ((video_adapter_info_t *)arg)->va_buffer_size = adp->va_buffer_size; ((video_adapter_info_t *)arg)->va_mode = adp->va_mode; ((video_adapter_info_t *)arg)->va_initial_mode = adp->va_initial_mode; ((video_adapter_info_t *)arg)->va_initial_bios_mode = adp->va_initial_bios_mode; ((video_adapter_info_t *)arg)->va_line_width = adp->va_line_width; ((video_adapter_info_t *)arg)->va_disp_start.x = adp->va_disp_start.x; ((video_adapter_info_t *)arg)->va_disp_start.y = adp->va_disp_start.y; break; case FBIO_MODEINFO: /* get mode information */ error = vidd_get_info(adp, ((video_info_t *)arg)->vi_mode, (video_info_t *)arg); if (error) error = ENODEV; break; case FBIO_FINDMODE: /* find a matching video mode */ error = vidd_query_mode(adp, (video_info_t *)arg); break; case FBIO_GETMODE: /* get video mode */ *(int *)arg = adp->va_mode; break; case FBIO_SETMODE: /* set video mode */ error = vidd_set_mode(adp, *(int *)arg); if (error) error = ENODEV; /* EINVAL? */ break; case FBIO_GETWINORG: /* get frame buffer window origin */ *(u_int *)arg = adp->va_window_orig; break; case FBIO_GETDISPSTART: /* get display start address */ ((video_display_start_t *)arg)->x = adp->va_disp_start.x; ((video_display_start_t *)arg)->y = adp->va_disp_start.y; break; case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ *(u_int *)arg = adp->va_line_width; break; case FBIO_BLANK: /* blank display */ error = vidd_blank_display(adp, *(int *)arg); break; case FBIO_GETPALETTE: /* get color palette */ case FBIO_SETPALETTE: /* set color palette */ /* XXX */ case FBIOPUTCMAP: case FBIOGETCMAP: case FBIOPUTCMAPI: case FBIOGETCMAPI: /* XXX */ case FBIO_SETWINORG: /* set frame buffer window origin */ case FBIO_SETDISPSTART: /* set display start address */ case FBIO_SETLINEWIDTH: /* set scan line width in pixel */ case FBIOGTYPE: case FBIOGATTR: case FBIOSVIDEO: case FBIOGVIDEO: case FBIOVERTICAL: case FBIOSCURSOR: case FBIOGCURSOR: case FBIOSCURPOS: case FBIOGCURPOS: case FBIOGCURMAX: case FBIOMONINFO: case FBIOGXINFO: default: error = ENODEV; break; } splx(s); return error; } Index: projects/ppc64/sys/dev/fb/vesa.c =================================================================== --- projects/ppc64/sys/dev/fb/vesa.c (revision 204271) +++ projects/ppc64/sys/dev/fb/vesa.c (revision 204272) @@ -1,1817 +1,1866 @@ /*- * Copyright (c) 1998 Kazutaka YOKOTA and Michael Smith * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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_vga.h" #include "opt_vesa.h" #ifndef VGA_NO_MODE_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VESA_VIA_CLE266 "VIA CLE266\r\n" #ifndef VESA_DEBUG #define VESA_DEBUG 0 #endif /* VESA video adapter state buffer stub */ struct adp_state { int sig; #define V_STATE_SIG 0x61736576 u_char regs[1]; }; typedef struct adp_state adp_state_t; /* VESA video adapter */ static video_adapter_t *vesa_adp = NULL; static ssize_t vesa_state_buf_size = -1; /* VESA functions */ #if 0 static int vesa_nop(void); #endif static int vesa_error(void); static vi_probe_t vesa_probe; static vi_init_t vesa_init; static vi_get_info_t vesa_get_info; static vi_query_mode_t vesa_query_mode; static vi_set_mode_t vesa_set_mode; static vi_save_font_t vesa_save_font; static vi_load_font_t vesa_load_font; static vi_show_font_t vesa_show_font; static vi_save_palette_t vesa_save_palette; static vi_load_palette_t vesa_load_palette; static vi_set_border_t vesa_set_border; static vi_save_state_t vesa_save_state; static vi_load_state_t vesa_load_state; static vi_set_win_org_t vesa_set_origin; static vi_read_hw_cursor_t vesa_read_hw_cursor; static vi_set_hw_cursor_t vesa_set_hw_cursor; static vi_set_hw_cursor_shape_t vesa_set_hw_cursor_shape; static vi_blank_display_t vesa_blank_display; static vi_mmap_t vesa_mmap; static vi_ioctl_t vesa_ioctl; static vi_clear_t vesa_clear; static vi_fill_rect_t vesa_fill_rect; static vi_bitblt_t vesa_bitblt; static vi_diag_t vesa_diag; static int vesa_bios_info(int level); static video_switch_t vesavidsw = { vesa_probe, vesa_init, vesa_get_info, vesa_query_mode, vesa_set_mode, vesa_save_font, vesa_load_font, vesa_show_font, vesa_save_palette, vesa_load_palette, vesa_set_border, vesa_save_state, vesa_load_state, vesa_set_origin, vesa_read_hw_cursor, vesa_set_hw_cursor, vesa_set_hw_cursor_shape, vesa_blank_display, vesa_mmap, vesa_ioctl, vesa_clear, vesa_fill_rect, vesa_bitblt, vesa_error, vesa_error, vesa_diag, }; static video_switch_t *prevvidsw; /* VESA BIOS video modes */ #define VESA_MAXMODES 64 #define EOT (-1) #define NA (-2) #define MODE_TABLE_DELTA 8 static int vesa_vmode_max = 0; static video_info_t vesa_vmode_empty = { EOT }; static video_info_t *vesa_vmode = &vesa_vmode_empty; static int vesa_init_done = FALSE; static int has_vesa_bios = FALSE; static struct vesa_info *vesa_adp_info = NULL; static u_int16_t *vesa_vmodetab = NULL; static char *vesa_oemstr = NULL; static char *vesa_venderstr = NULL; static char *vesa_prodstr = NULL; static char *vesa_revstr = NULL; /* local macros and functions */ #define BIOS_SADDRTOLADDR(p) ((((p) & 0xffff0000) >> 12) + ((p) & 0x0000ffff)) static int int10_set_mode(int mode); static int vesa_bios_post(void); static int vesa_bios_get_mode(int mode, struct vesa_mode *vmode); static int vesa_bios_set_mode(int mode); +#if 0 static int vesa_bios_get_dac(void); +#endif static int vesa_bios_set_dac(int bits); static int vesa_bios_save_palette(int start, int colors, u_char *palette, int bits); static int vesa_bios_save_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits); static int vesa_bios_load_palette(int start, int colors, u_char *palette, int bits); static int vesa_bios_load_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits); #define STATE_SIZE 0 #define STATE_SAVE 1 #define STATE_LOAD 2 #define STATE_HW (1<<0) #define STATE_DATA (1<<1) #define STATE_DAC (1<<2) #define STATE_REG (1<<3) #define STATE_MOST (STATE_HW | STATE_DATA | STATE_REG) #define STATE_ALL (STATE_HW | STATE_DATA | STATE_DAC | STATE_REG) static ssize_t vesa_bios_state_buf_size(void); static int vesa_bios_save_restore(int code, void *p, size_t size); #if 0 static int vesa_bios_get_line_length(void); #endif static int vesa_bios_set_line_length(int pixel, int *bytes, int *lines); #if 0 static int vesa_bios_get_start(int *x, int *y); #endif static int vesa_bios_set_start(int x, int y); static int vesa_map_gen_mode_num(int type, int color, int mode); static int vesa_translate_flags(u_int16_t vflags); static int vesa_translate_mmodel(u_int8_t vmodel); static int vesa_bios_init(void); static void vesa_clear_modes(video_info_t *info, int color); #if 0 static int vesa_get_origin(video_adapter_t *adp, off_t *offset); #endif /* INT 10 BIOS calls */ static int int10_set_mode(int mode) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AL = mode; x86bios_intr(®s, 0x10); return (0); } static int vesa_bios_post(void) { x86regs_t regs; devclass_t dc; device_t *devs; device_t dev; int count, i, is_pci; if (x86bios_get_orm(0xc0000) == NULL) return (1); dev = NULL; is_pci = 0; /* Find the matching PCI video controller. */ dc = devclass_find("vgapci"); if (dc != NULL && devclass_get_devices(dc, &devs, &count) == 0) { for (dev = NULL, i = 0; dev == NULL && i < count; devs++, i++) if (device_get_flags(*devs) != 0 && x86bios_match_device(0xc0000, *devs)) { dev = *devs; is_pci = 1; break; } free(devs, M_TEMP); } /* Try VGA if a PCI device is not found. */ if (dev == NULL) { dc = devclass_find(VGA_DRIVER_NAME); if (dc != NULL) dev = devclass_get_device(dc, 0); } if (bootverbose) printf("%s: calling BIOS POST\n", dev == NULL ? "VESA" : device_get_nameunit(dev)); x86bios_init_regs(®s); if (is_pci) { regs.R_AH = pci_get_bus(dev); regs.R_AL = (pci_get_slot(dev) << 3) | (pci_get_function(dev) & 0x07); } regs.R_DL = 0x80; x86bios_call(®s, 0xc000, 0x0003); if (x86bios_get_intr(0x10) == 0) return (1); return (0); } /* VESA BIOS calls */ static int vesa_bios_get_mode(int mode, struct vesa_mode *vmode) { x86regs_t regs; uint32_t offs; void *buf; buf = x86bios_alloc(&offs, sizeof(*vmode)); if (buf == NULL) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f01; regs.R_CX = mode; regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_DI = X86BIOS_PHYSTOOFF(offs); x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) { x86bios_free(buf, sizeof(*vmode)); return (1); } bcopy(buf, vmode, sizeof(*vmode)); x86bios_free(buf, sizeof(*vmode)); return (0); } static int vesa_bios_set_mode(int mode) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f02; regs.R_BX = mode; x86bios_intr(®s, 0x10); return (regs.R_AX != 0x004f); } +#if 0 static int vesa_bios_get_dac(void) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f08; regs.R_BL = 1; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (6); return (regs.R_BH); } +#endif static int vesa_bios_set_dac(int bits) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f08; /* regs.R_BL = 0; */ regs.R_BH = bits; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (6); return (regs.R_BH); } static int vesa_bios_save_palette(int start, int colors, u_char *palette, int bits) { x86regs_t regs; uint32_t offs; u_char *p; int i; p = (u_char *)x86bios_alloc(&offs, colors * 4); if (p == NULL) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f09; regs.R_BL = 1; regs.R_CX = colors; regs.R_DX = start; regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_DI = X86BIOS_PHYSTOOFF(offs); x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) { x86bios_free(p, colors * 4); return (1); } bits = 8 - bits; for (i = 0; i < colors; ++i) { palette[i * 3] = p[i * 4 + 2] << bits; palette[i * 3 + 1] = p[i * 4 + 1] << bits; palette[i * 3 + 2] = p[i * 4] << bits; } x86bios_free(p, colors * 4); return (0); } static int vesa_bios_save_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits) { x86regs_t regs; uint32_t offs; u_char *p; int i; p = (u_char *)x86bios_alloc(&offs, colors * 4); if (p == NULL) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f09; regs.R_BL = 1; regs.R_CX = colors; regs.R_DX = start; regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_DI = X86BIOS_PHYSTOOFF(offs); x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) { x86bios_free(p, colors * 4); return (1); } bits = 8 - bits; for (i = 0; i < colors; ++i) { r[i] = p[i * 4 + 2] << bits; g[i] = p[i * 4 + 1] << bits; b[i] = p[i * 4] << bits; } x86bios_free(p, colors * 4); return (0); } static int vesa_bios_load_palette(int start, int colors, u_char *palette, int bits) { x86regs_t regs; uint32_t offs; u_char *p; int i; p = (u_char *)x86bios_alloc(&offs, colors * 4); if (p == NULL) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f09; /* regs.R_BL = 0; */ regs.R_CX = colors; regs.R_DX = start; regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_DI = X86BIOS_PHYSTOOFF(offs); bits = 8 - bits; for (i = 0; i < colors; ++i) { p[i * 4] = palette[i * 3 + 2] >> bits; p[i * 4 + 1] = palette[i * 3 + 1] >> bits; p[i * 4 + 2] = palette[i * 3] >> bits; p[i * 4 + 3] = 0; } x86bios_intr(®s, 0x10); x86bios_free(p, colors * 4); return (regs.R_AX != 0x004f); } static int vesa_bios_load_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits) { x86regs_t regs; uint32_t offs; u_char *p; int i; p = (u_char *)x86bios_alloc(&offs, colors * 4); if (p == NULL) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f09; /* regs.R_BL = 0; */ regs.R_CX = colors; regs.R_DX = start; regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_DI = X86BIOS_PHYSTOOFF(offs); bits = 8 - bits; for (i = 0; i < colors; ++i) { p[i * 4] = b[i] >> bits; p[i * 4 + 1] = g[i] >> bits; p[i * 4 + 2] = r[i] >> bits; p[i * 4 + 3] = 0; } x86bios_intr(®s, 0x10); x86bios_free(p, colors * 4); return (regs.R_AX != 0x004f); } static ssize_t vesa_bios_state_buf_size(void) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f04; /* regs.R_DL = STATE_SIZE; */ regs.R_CX = STATE_ALL; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (0); return (regs.R_BX * 64); } static int vesa_bios_save_restore(int code, void *p, size_t size) { x86regs_t regs; uint32_t offs; void *buf; if (code != STATE_SAVE && code != STATE_LOAD) return (1); buf = x86bios_alloc(&offs, size); x86bios_init_regs(®s); regs.R_AX = 0x4f04; regs.R_DL = code; regs.R_CX = STATE_ALL; regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_BX = X86BIOS_PHYSTOOFF(offs); switch (code) { case STATE_SAVE: x86bios_intr(®s, 0x10); bcopy(buf, p, size); break; case STATE_LOAD: bcopy(p, buf, size); x86bios_intr(®s, 0x10); break; } x86bios_free(buf, size); return (regs.R_AX != 0x004f); } #if 0 static int vesa_bios_get_line_length(void) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f06; regs.R_BL = 1; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (-1); return (regs.R_BX); } #endif static int vesa_bios_set_line_length(int pixel, int *bytes, int *lines) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f06; /* regs.R_BL = 0; */ regs.R_CX = pixel; x86bios_intr(®s, 0x10); #if VESA_DEBUG > 1 printf("bx:%d, cx:%d, dx:%d\n", regs.R_BX, regs.R_CX, regs.R_DX); #endif if (regs.R_AX != 0x004f) return (-1); if (bytes != NULL) *bytes = regs.R_BX; if (lines != NULL) *lines = regs.R_DX; return (0); } #if 0 static int vesa_bios_get_start(int *x, int *y) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f07; regs.R_BL = 1; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (-1); *x = regs.R_CX; *y = regs.R_DX; return (0); } #endif static int vesa_bios_set_start(int x, int y) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f07; regs.R_BL = 0x80; regs.R_CX = x; regs.R_DX = y; x86bios_intr(®s, 0x10); return (regs.R_AX != 0x004f); } /* map a generic video mode to a known mode */ static int vesa_map_gen_mode_num(int type, int color, int mode) { static struct { int from; int to; } mode_map[] = { { M_TEXT_132x25, M_VESA_C132x25 }, { M_TEXT_132x43, M_VESA_C132x43 }, { M_TEXT_132x50, M_VESA_C132x50 }, { M_TEXT_132x60, M_VESA_C132x60 }, }; int i; for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return (mode_map[i].to); } return (mode); } static int vesa_translate_flags(u_int16_t vflags) { static struct { u_int16_t mask; int set; int reset; } ftable[] = { { V_MODECOLOR, V_INFO_COLOR, 0 }, { V_MODEGRAPHICS, V_INFO_GRAPHICS, 0 }, { V_MODELFB, V_INFO_LINEAR, 0 }, { V_MODENONVGA, V_INFO_NONVGA, 0 }, }; int flags; int i; for (flags = 0, i = 0; i < sizeof(ftable)/sizeof(ftable[0]); ++i) { flags |= (vflags & ftable[i].mask) ? ftable[i].set : ftable[i].reset; } return (flags); } static int vesa_translate_mmodel(u_int8_t vmodel) { static struct { u_int8_t vmodel; int mmodel; } mtable[] = { { V_MMTEXT, V_INFO_MM_TEXT }, { V_MMCGA, V_INFO_MM_CGA }, { V_MMHGC, V_INFO_MM_HGC }, { V_MMEGA, V_INFO_MM_PLANAR }, { V_MMPACKED, V_INFO_MM_PACKED }, { V_MMDIRCOLOR, V_INFO_MM_DIRECT }, }; int i; for (i = 0; mtable[i].mmodel >= 0; ++i) { if (mtable[i].vmodel == vmodel) return (mtable[i].mmodel); } return (V_INFO_MM_OTHER); } #define VESA_MAXSTR 256 #define VESA_STRCPY(dst, src) do { \ char *str; \ int i; \ dst = malloc(VESA_MAXSTR, M_DEVBUF, M_WAITOK); \ str = x86bios_offset(BIOS_SADDRTOLADDR(src)); \ for (i = 0; i < VESA_MAXSTR - 1 && str[i] != '\0'; i++) \ dst[i] = str[i]; \ dst[i] = '\0'; \ } while (0) static int vesa_bios_init(void) { static struct vesa_info buf; struct vesa_mode vmode; video_info_t *p; x86regs_t regs; size_t bsize; size_t msize; void *vmbuf; uint32_t offs; uint16_t vers; int bpsl; int is_via_cle266; int modes; int i; if (vesa_init_done) return (0); has_vesa_bios = FALSE; vesa_adp_info = NULL; vesa_vmode_max = 0; vesa_vmode[0].vi_mode = EOT; /* * If the VBE real mode interrupt vector is not found, try BIOS POST. */ if (x86bios_get_intr(0x10) == 0) { if (vesa_bios_post() != 0) return (1); if (bootverbose) { offs = x86bios_get_intr(0x10); printf("VESA: interrupt vector installed (0x%x)\n", BIOS_SADDRTOLADDR(offs)); } } x86bios_init_regs(®s); regs.R_AX = 0x4f00; vmbuf = x86bios_alloc(&offs, sizeof(buf)); if (vmbuf == NULL) return (1); regs.R_ES = X86BIOS_PHYSTOSEG(offs); regs.R_DI = X86BIOS_PHYSTOOFF(offs); bcopy("VBE2", vmbuf, 4); /* try for VBE2 data */ x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f || bcmp("VESA", vmbuf, 4) != 0) goto fail; bcopy(vmbuf, &buf, sizeof(buf)); vesa_adp_info = &buf; if (bootverbose) { printf("VESA: information block\n"); hexdump(&buf, sizeof(buf), NULL, HD_OMIT_CHARS); } vers = buf.v_version = le16toh(buf.v_version); buf.v_oemstr = le32toh(buf.v_oemstr); buf.v_flags = le32toh(buf.v_flags); buf.v_modetable = le32toh(buf.v_modetable); buf.v_memsize = le16toh(buf.v_memsize); buf.v_revision = le16toh(buf.v_revision); buf.v_venderstr = le32toh(buf.v_venderstr); buf.v_prodstr = le32toh(buf.v_prodstr); buf.v_revstr = le32toh(buf.v_revstr); if (vers < 0x0102) { printf("VESA: VBE version %d.%d is not supported; " "version 1.2 or later is required.\n", ((vers & 0xf000) >> 12) * 10 + ((vers & 0x0f00) >> 8), ((vers & 0x00f0) >> 4) * 10 + (vers & 0x000f)); return (1); } VESA_STRCPY(vesa_oemstr, buf.v_oemstr); if (vers >= 0x0200) { VESA_STRCPY(vesa_venderstr, buf.v_venderstr); VESA_STRCPY(vesa_prodstr, buf.v_prodstr); VESA_STRCPY(vesa_revstr, buf.v_revstr); } is_via_cle266 = strncmp(vesa_oemstr, VESA_VIA_CLE266, sizeof(VESA_VIA_CLE266)) == 0; if (buf.v_modetable == 0) goto fail; msize = (size_t)buf.v_memsize * 64 * 1024; vesa_vmodetab = x86bios_offset(BIOS_SADDRTOLADDR(buf.v_modetable)); for (i = 0, modes = 0; (i < (M_VESA_MODE_MAX - M_VESA_BASE + 1)) && (vesa_vmodetab[i] != 0xffff); ++i) { vesa_vmodetab[i] = le16toh(vesa_vmodetab[i]); if (vesa_bios_get_mode(vesa_vmodetab[i], &vmode)) continue; vmode.v_modeattr = le16toh(vmode.v_modeattr); vmode.v_wgran = le16toh(vmode.v_wgran); vmode.v_wsize = le16toh(vmode.v_wsize); vmode.v_waseg = le16toh(vmode.v_waseg); vmode.v_wbseg = le16toh(vmode.v_wbseg); vmode.v_posfunc = le32toh(vmode.v_posfunc); vmode.v_bpscanline = le16toh(vmode.v_bpscanline); vmode.v_width = le16toh(vmode.v_width); vmode.v_height = le16toh(vmode.v_height); vmode.v_lfb = le32toh(vmode.v_lfb); vmode.v_offscreen = le32toh(vmode.v_offscreen); vmode.v_offscreensize = le16toh(vmode.v_offscreensize); vmode.v_linbpscanline = le16toh(vmode.v_linbpscanline); vmode.v_maxpixelclock = le32toh(vmode.v_maxpixelclock); /* reject unsupported modes */ #if 0 if ((vmode.v_modeattr & (V_MODESUPP | V_MODEOPTINFO | V_MODENONVGA)) != (V_MODESUPP | V_MODEOPTINFO)) continue; #else if ((vmode.v_modeattr & V_MODEOPTINFO) == 0) { #if VESA_DEBUG > 1 printf("Rejecting VESA %s mode: %d x %d x %d bpp " " attr = %x\n", vmode.v_modeattr & V_MODEGRAPHICS ? "graphics" : "text", vmode.v_width, vmode.v_height, vmode.v_bpp, vmode.v_modeattr); #endif continue; } #endif bpsl = (vmode.v_modeattr & V_MODELFB) != 0 && vers >= 0x0300 ? vmode.v_linbpscanline : vmode.v_bpscanline; bsize = bpsl * vmode.v_height; if ((vmode.v_modeattr & V_MODEGRAPHICS) != 0) bsize *= vmode.v_planes; /* Does it have enough memory to support this mode? */ if (msize < bsize) { #if VESA_DEBUG > 1 printf("Rejecting VESA %s mode: %d x %d x %d bpp " " attr = %x, not enough memory\n", vmode.v_modeattr & V_MODEGRAPHICS ? "graphics" : "text", vmode.v_width, vmode.v_height, vmode.v_bpp, vmode.v_modeattr); #endif continue; } /* expand the array if necessary */ if (modes >= vesa_vmode_max) { vesa_vmode_max += MODE_TABLE_DELTA; p = malloc(sizeof(*vesa_vmode) * (vesa_vmode_max + 1), M_DEVBUF, M_WAITOK); #if VESA_DEBUG > 1 printf("vesa_bios_init(): modes:%d, vesa_mode_max:%d\n", modes, vesa_vmode_max); #endif if (modes > 0) { bcopy(vesa_vmode, p, sizeof(*vesa_vmode)*modes); free(vesa_vmode, M_DEVBUF); } vesa_vmode = p; } #if VESA_DEBUG > 1 printf("Found VESA %s mode: %d x %d x %d bpp\n", vmode.v_modeattr & V_MODEGRAPHICS ? "graphics" : "text", vmode.v_width, vmode.v_height, vmode.v_bpp); #endif if (is_via_cle266) { if ((vmode.v_width & 0xff00) >> 8 == vmode.v_height - 1) { vmode.v_width &= 0xff; vmode.v_waseg = 0xb8000 >> 4; } } /* copy some fields */ bzero(&vesa_vmode[modes], sizeof(vesa_vmode[modes])); vesa_vmode[modes].vi_mode = vesa_vmodetab[i]; vesa_vmode[modes].vi_width = vmode.v_width; vesa_vmode[modes].vi_height = vmode.v_height; vesa_vmode[modes].vi_depth = vmode.v_bpp; vesa_vmode[modes].vi_planes = vmode.v_planes; vesa_vmode[modes].vi_cwidth = vmode.v_cwidth; vesa_vmode[modes].vi_cheight = vmode.v_cheight; vesa_vmode[modes].vi_window = (vm_offset_t)vmode.v_waseg << 4; /* XXX window B */ vesa_vmode[modes].vi_window_size = vmode.v_wsize * 1024; vesa_vmode[modes].vi_window_gran = vmode.v_wgran * 1024; if (vmode.v_modeattr & V_MODELFB) vesa_vmode[modes].vi_buffer = vmode.v_lfb; vesa_vmode[modes].vi_buffer_size = bsize; vesa_vmode[modes].vi_mem_model = vesa_translate_mmodel(vmode.v_memmodel); - if (vesa_vmode[modes].vi_mem_model == V_INFO_MM_PACKED || - vesa_vmode[modes].vi_mem_model == V_INFO_MM_DIRECT) + switch (vesa_vmode[modes].vi_mem_model) { + case V_INFO_MM_DIRECT: + if ((vmode.v_modeattr & V_MODELFB) != 0 && + vers >= 0x0300) { + vesa_vmode[modes].vi_pixel_fields[0] = + vmode.v_linredfieldpos; + vesa_vmode[modes].vi_pixel_fields[1] = + vmode.v_lingreenfieldpos; + vesa_vmode[modes].vi_pixel_fields[2] = + vmode.v_linbluefieldpos; + vesa_vmode[modes].vi_pixel_fields[3] = + vmode.v_linresfieldpos; + vesa_vmode[modes].vi_pixel_fsizes[0] = + vmode.v_linredmasksize; + vesa_vmode[modes].vi_pixel_fsizes[1] = + vmode.v_lingreenmasksize; + vesa_vmode[modes].vi_pixel_fsizes[2] = + vmode.v_linbluemasksize; + vesa_vmode[modes].vi_pixel_fsizes[3] = + vmode.v_linresmasksize; + } else { + vesa_vmode[modes].vi_pixel_fields[0] = + vmode.v_redfieldpos; + vesa_vmode[modes].vi_pixel_fields[1] = + vmode.v_greenfieldpos; + vesa_vmode[modes].vi_pixel_fields[2] = + vmode.v_bluefieldpos; + vesa_vmode[modes].vi_pixel_fields[3] = + vmode.v_resfieldpos; + vesa_vmode[modes].vi_pixel_fsizes[0] = + vmode.v_redmasksize; + vesa_vmode[modes].vi_pixel_fsizes[1] = + vmode.v_greenmasksize; + vesa_vmode[modes].vi_pixel_fsizes[2] = + vmode.v_bluemasksize; + vesa_vmode[modes].vi_pixel_fsizes[3] = + vmode.v_resmasksize; + } + /* FALLTHROUGH */ + case V_INFO_MM_PACKED: vesa_vmode[modes].vi_pixel_size = (vmode.v_bpp + 7) / 8; + break; + } vesa_vmode[modes].vi_flags = vesa_translate_flags(vmode.v_modeattr) | V_INFO_VESA; ++modes; } vesa_vmode[modes].vi_mode = EOT; if (bootverbose) printf("VESA: %d mode(s) found\n", modes); has_vesa_bios = (modes > 0); if (!has_vesa_bios) goto fail; x86bios_free(vmbuf, sizeof(buf)); return (0); fail: if (vmbuf != NULL) x86bios_free(vmbuf, sizeof(buf)); if (vesa_oemstr != NULL) { free(vesa_oemstr, M_DEVBUF); vesa_oemstr = NULL; } if (vesa_venderstr != NULL) { free(vesa_venderstr, M_DEVBUF); vesa_venderstr = NULL; } if (vesa_prodstr != NULL) { free(vesa_prodstr, M_DEVBUF); vesa_prodstr = NULL; } if (vesa_revstr != NULL) { free(vesa_revstr, M_DEVBUF); vesa_revstr = NULL; } return (1); } static void vesa_clear_modes(video_info_t *info, int color) { while (info->vi_mode != EOT) { if ((info->vi_flags & V_INFO_COLOR) != color) info->vi_mode = NA; ++info; } } /* entry points */ static int vesa_configure(int flags) { video_adapter_t *adp; int adapters; int error; int i; if (vesa_init_done) return (0); if (flags & VIO_PROBE_ONLY) return (0); /* * If the VESA module has already been loaded, abort loading * the module this time. */ for (i = 0; (adp = vid_get_adapter(i)) != NULL; ++i) { if (adp->va_flags & V_ADP_VESA) return (ENXIO); if (adp->va_type == KD_VGA) break; } /* * The VGA adapter is not found. This is because either * 1) the VGA driver has not been initialized, or 2) the VGA card * is not present. If 1) is the case, we shall defer * initialization for now and try again later. */ if (adp == NULL) { vga_sub_configure = vesa_configure; return (ENODEV); } /* count number of registered adapters */ for (++i; vid_get_adapter(i) != NULL; ++i) ; adapters = i; /* call VESA BIOS */ vesa_adp = adp; if (vesa_bios_init()) { vesa_adp = NULL; return (ENXIO); } vesa_adp->va_flags |= V_ADP_VESA; /* remove conflicting modes if we have more than one adapter */ if (adapters > 1) { vesa_clear_modes(vesa_vmode, (vesa_adp->va_flags & V_ADP_COLOR) ? V_INFO_COLOR : 0); } if ((error = vesa_load_ioctl()) == 0) { prevvidsw = vidsw[vesa_adp->va_index]; vidsw[vesa_adp->va_index] = &vesavidsw; vesa_init_done = TRUE; } else { vesa_adp = NULL; return (error); } return (0); } #if 0 static int vesa_nop(void) { return (0); } #endif static int vesa_error(void) { return (1); } static int vesa_probe(int unit, video_adapter_t **adpp, void *arg, int flags) { return ((*prevvidsw->probe)(unit, adpp, arg, flags)); } static int vesa_init(int unit, video_adapter_t *adp, int flags) { return ((*prevvidsw->init)(unit, adp, flags)); } static int vesa_get_info(video_adapter_t *adp, int mode, video_info_t *info) { int i; if ((*prevvidsw->get_info)(adp, mode, info) == 0) return (0); if (adp != vesa_adp) return (1); mode = vesa_map_gen_mode_num(vesa_adp->va_type, vesa_adp->va_flags & V_ADP_COLOR, mode); for (i = 0; vesa_vmode[i].vi_mode != EOT; ++i) { if (vesa_vmode[i].vi_mode == NA) continue; if (vesa_vmode[i].vi_mode == mode) { *info = vesa_vmode[i]; return (0); } } return (1); } static int vesa_query_mode(video_adapter_t *adp, video_info_t *info) { int i; if ((*prevvidsw->query_mode)(adp, info) == 0) return (0); if (adp != vesa_adp) return (ENODEV); for (i = 0; vesa_vmode[i].vi_mode != EOT; ++i) { if ((info->vi_width != 0) && (info->vi_width != vesa_vmode[i].vi_width)) continue; if ((info->vi_height != 0) && (info->vi_height != vesa_vmode[i].vi_height)) continue; if ((info->vi_cwidth != 0) && (info->vi_cwidth != vesa_vmode[i].vi_cwidth)) continue; if ((info->vi_cheight != 0) && (info->vi_cheight != vesa_vmode[i].vi_cheight)) continue; if ((info->vi_depth != 0) && (info->vi_depth != vesa_vmode[i].vi_depth)) continue; if ((info->vi_planes != 0) && (info->vi_planes != vesa_vmode[i].vi_planes)) continue; /* pixel format, memory model */ if ((info->vi_flags != 0) && (info->vi_flags != vesa_vmode[i].vi_flags)) continue; *info = vesa_vmode[i]; return (0); } return (ENODEV); } static int vesa_set_mode(video_adapter_t *adp, int mode) { video_info_t info; if (adp != vesa_adp) return ((*prevvidsw->set_mode)(adp, mode)); mode = vesa_map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); #if VESA_DEBUG > 0 printf("VESA: set_mode(): %d(%x) -> %d(%x)\n", adp->va_mode, adp->va_mode, mode, mode); #endif /* * If the current mode is a VESA mode and the new mode is not, * restore the state of the adapter first by setting one of the * standard VGA mode, so that non-standard, extended SVGA registers * are set to the state compatible with the standard VGA modes. * Otherwise (*prevvidsw->set_mode)() may not be able to set up * the new mode correctly. */ if (VESA_MODE(adp->va_mode)) { if (!VESA_MODE(mode) && (*prevvidsw->get_info)(adp, mode, &info) == 0) { + if ((adp->va_flags & V_ADP_DAC8) != 0) { + vesa_bios_set_dac(6); + adp->va_flags &= ~V_ADP_DAC8; + } int10_set_mode(adp->va_initial_bios_mode); if (adp->va_info.vi_flags & V_INFO_LINEAR) pmap_unmapdev(adp->va_buffer, adp->va_buffer_size); /* * Once (*prevvidsw->get_info)() succeeded, * (*prevvidsw->set_mode)() below won't fail... */ } } /* we may not need to handle this mode after all... */ if (!VESA_MODE(mode) && (*prevvidsw->set_mode)(adp, mode) == 0) return (0); /* is the new mode supported? */ if (vesa_get_info(adp, mode, &info)) return (1); /* assert(VESA_MODE(mode)); */ #if VESA_DEBUG > 0 printf("VESA: about to set a VESA mode...\n"); #endif /* don't use the linear frame buffer for text modes. XXX */ if (!(info.vi_flags & V_INFO_GRAPHICS)) info.vi_flags &= ~V_INFO_LINEAR; if (vesa_bios_set_mode(mode | ((info.vi_flags & V_INFO_LINEAR) ? 0x4000 : 0))) return (1); - if ((vesa_adp_info->v_flags & V_DAC8) != 0) - vesa_bios_set_dac(8); + /* Palette format is reset by the above VBE function call. */ + adp->va_flags &= ~V_ADP_DAC8; + if ((vesa_adp_info->v_flags & V_DAC8) != 0 && + (info.vi_flags & V_INFO_GRAPHICS) != 0 && + (info.vi_flags & V_INFO_NONVGA) != 0 && + vesa_bios_set_dac(8) > 6) + adp->va_flags |= V_ADP_DAC8; + if (adp->va_info.vi_flags & V_INFO_LINEAR) pmap_unmapdev(adp->va_buffer, adp->va_buffer_size); #if VESA_DEBUG > 0 printf("VESA: mode set!\n"); #endif vesa_adp->va_mode = mode; vesa_adp->va_flags &= ~V_ADP_COLOR; vesa_adp->va_flags |= (info.vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0; vesa_adp->va_crtc_addr = (vesa_adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC; if (info.vi_flags & V_INFO_LINEAR) { #if VESA_DEBUG > 1 printf("VESA: setting up LFB\n"); #endif vesa_adp->va_buffer = (vm_offset_t)pmap_mapdev_attr(info.vi_buffer, info.vi_buffer_size, PAT_WRITE_COMBINING); vesa_adp->va_window = vesa_adp->va_buffer; vesa_adp->va_window_size = info.vi_buffer_size / info.vi_planes; vesa_adp->va_window_gran = info.vi_buffer_size / info.vi_planes; } else { vesa_adp->va_buffer = 0; vesa_adp->va_window = (vm_offset_t)x86bios_offset(info.vi_window); vesa_adp->va_window_size = info.vi_window_size; vesa_adp->va_window_gran = info.vi_window_gran; } vesa_adp->va_buffer_size = info.vi_buffer_size; vesa_adp->va_window_orig = 0; vesa_adp->va_line_width = info.vi_buffer_size / info.vi_height; if ((info.vi_flags & V_INFO_GRAPHICS) != 0) vesa_adp->va_line_width /= info.vi_planes; vesa_adp->va_disp_start.x = 0; vesa_adp->va_disp_start.y = 0; #if VESA_DEBUG > 0 printf("vesa_set_mode(): vi_width:%d, line_width:%d\n", info.vi_width, vesa_adp->va_line_width); #endif bcopy(&info, &vesa_adp->va_info, sizeof(vesa_adp->va_info)); /* move hardware cursor out of the way */ (*vidsw[vesa_adp->va_index]->set_hw_cursor)(vesa_adp, -1, -1); return (0); } static int vesa_save_font(video_adapter_t *adp, int page, int fontsize, int fontwidth, u_char *data, int ch, int count) { return ((*prevvidsw->save_font)(adp, page, fontsize, fontwidth, data, ch, count)); } static int vesa_load_font(video_adapter_t *adp, int page, int fontsize, int fontwidth, u_char *data, int ch, int count) { return ((*prevvidsw->load_font)(adp, page, fontsize, fontwidth, data, ch, count)); } static int vesa_show_font(video_adapter_t *adp, int page) { return ((*prevvidsw->show_font)(adp, page)); } static int vesa_save_palette(video_adapter_t *adp, u_char *palette) { int bits; - if (adp == vesa_adp && VESA_MODE(adp->va_mode)) { - bits = vesa_bios_get_dac(); - if ((adp->va_info.vi_flags & V_INFO_NONVGA) != 0 || bits > 6) - return (vesa_bios_save_palette(0, 256, palette, bits)); + if (adp == vesa_adp && VESA_MODE(adp->va_mode) && + (adp->va_info.vi_flags & V_INFO_NONVGA) != 0) { + bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 8 : 6; + return (vesa_bios_save_palette(0, 256, palette, bits)); } return ((*prevvidsw->save_palette)(adp, palette)); } static int vesa_load_palette(video_adapter_t *adp, u_char *palette) { int bits; - if (adp == vesa_adp && VESA_MODE(adp->va_mode)) { - bits = vesa_bios_get_dac(); - if ((adp->va_info.vi_flags & V_INFO_NONVGA) != 0 || bits > 6) - return (vesa_bios_load_palette(0, 256, palette, bits)); + if (adp == vesa_adp && VESA_MODE(adp->va_mode) && + (adp->va_info.vi_flags & V_INFO_NONVGA) != 0) { + bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 8 : 6; + return (vesa_bios_load_palette(0, 256, palette, bits)); } return ((*prevvidsw->load_palette)(adp, palette)); } static int vesa_set_border(video_adapter_t *adp, int color) { return ((*prevvidsw->set_border)(adp, color)); } static int vesa_save_state(video_adapter_t *adp, void *p, size_t size) { if (adp != vesa_adp) return ((*prevvidsw->save_state)(adp, p, size)); if (vesa_state_buf_size == -1) { vesa_state_buf_size = vesa_bios_state_buf_size(); if (vesa_state_buf_size == 0) return (1); } if (size == 0) return (offsetof(adp_state_t, regs) + vesa_state_buf_size); else if (size < (offsetof(adp_state_t, regs) + vesa_state_buf_size)) return (1); ((adp_state_t *)p)->sig = V_STATE_SIG; bzero(((adp_state_t *)p)->regs, vesa_state_buf_size); return (vesa_bios_save_restore(STATE_SAVE, ((adp_state_t *)p)->regs, vesa_state_buf_size)); } static int vesa_load_state(video_adapter_t *adp, void *p) { if ((adp != vesa_adp) || (((adp_state_t *)p)->sig != V_STATE_SIG)) return ((*prevvidsw->load_state)(adp, p)); if (vesa_state_buf_size <= 0) return (1); /* Try BIOS POST to restore a sane state. */ (void)vesa_bios_post(); (void)int10_set_mode(adp->va_initial_bios_mode); return (vesa_bios_save_restore(STATE_LOAD, ((adp_state_t *)p)->regs, vesa_state_buf_size)); } #if 0 static int vesa_get_origin(video_adapter_t *adp, off_t *offset) { x86regs_t regs; x86bios_init_regs(®s); regs.R_AX = 0x4f05; regs.R_BL = 0x10; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (1); *offset = regs.DX * adp->va_window_gran; return (0); } #endif static int vesa_set_origin(video_adapter_t *adp, off_t offset) { x86regs_t regs; /* * This function should return as quickly as possible to * maintain good performance of the system. For this reason, * error checking is kept minimal and let the VESA BIOS to * detect error. */ if (adp != vesa_adp) return ((*prevvidsw->set_win_org)(adp, offset)); /* if this is a linear frame buffer, do nothing */ if (adp->va_info.vi_flags & V_INFO_LINEAR) return (0); /* XXX */ if (adp->va_window_gran == 0) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f05; regs.R_DX = offset / adp->va_window_gran; x86bios_intr(®s, 0x10); if (regs.R_AX != 0x004f) return (1); x86bios_init_regs(®s); regs.R_AX = 0x4f05; regs.R_BL = 1; regs.R_DX = offset / adp->va_window_gran; x86bios_intr(®s, 0x10); adp->va_window_orig = (offset/adp->va_window_gran)*adp->va_window_gran; return (0); /* XXX */ } static int vesa_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { return ((*prevvidsw->read_hw_cursor)(adp, col, row)); } static int vesa_set_hw_cursor(video_adapter_t *adp, int col, int row) { return ((*prevvidsw->set_hw_cursor)(adp, col, row)); } static int vesa_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { return ((*prevvidsw->set_hw_cursor_shape)(adp, base, height, celsize, blink)); } static int vesa_blank_display(video_adapter_t *adp, int mode) { /* XXX: use VESA DPMS */ return ((*prevvidsw->blank_display)(adp, mode)); } static int vesa_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { #if VESA_DEBUG > 0 printf("vesa_mmap(): window:0x%tx, buffer:0x%tx, offset:0x%jx\n", adp->va_info.vi_window, adp->va_info.vi_buffer, offset); #endif if ((adp == vesa_adp) && (adp->va_info.vi_flags & V_INFO_LINEAR) != 0) { /* va_window_size == va_buffer_size/vi_planes */ /* XXX: is this correct? */ if (offset > adp->va_window_size - PAGE_SIZE) return (-1); *paddr = adp->va_info.vi_buffer + offset; return (0); } return ((*prevvidsw->mmap)(adp, offset, paddr, prot, memattr)); } static int vesa_clear(video_adapter_t *adp) { return ((*prevvidsw->clear)(adp)); } static int vesa_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { return ((*prevvidsw->fill_rect)(adp, val, x, y, cx, cy)); } static int vesa_bitblt(video_adapter_t *adp,...) { /* FIXME */ return (1); } static int get_palette(video_adapter_t *adp, int base, int count, u_char *red, u_char *green, u_char *blue, u_char *trans) { u_char *r; u_char *g; u_char *b; int bits; int error; if (base < 0 || base >= 256 || count < 0 || count > 256) return (1); if ((base + count) > 256) return (1); if (!VESA_MODE(adp->va_mode)) return (1); - bits = vesa_bios_get_dac(); - if ((adp->va_info.vi_flags & V_INFO_NONVGA) == 0 && bits <= 6) + if ((adp->va_info.vi_flags & V_INFO_NONVGA) == 0) return (1); + bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 8 : 6; r = malloc(count * 3, M_DEVBUF, M_WAITOK); g = r + count; b = g + count; error = vesa_bios_save_palette2(base, count, r, g, b, bits); if (error == 0) { copyout(r, red, count); copyout(g, green, count); copyout(b, blue, count); if (trans != NULL) { bzero(r, count); copyout(r, trans, count); } } free(r, M_DEVBUF); return (error); } static int set_palette(video_adapter_t *adp, int base, int count, u_char *red, u_char *green, u_char *blue, u_char *trans) { u_char *r; u_char *g; u_char *b; int bits; int error; if (base < 0 || base >= 256 || count < 0 || count > 256) return (1); if ((base + count) > 256) return (1); if (!VESA_MODE(adp->va_mode)) return (1); - bits = vesa_bios_get_dac(); - if ((adp->va_info.vi_flags & V_INFO_NONVGA) == 0 && bits <= 6) + if ((adp->va_info.vi_flags & V_INFO_NONVGA) == 0) return (1); + bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 8 : 6; r = malloc(count * 3, M_DEVBUF, M_WAITOK); g = r + count; b = g + count; copyin(red, r, count); copyin(green, g, count); copyin(blue, b, count); error = vesa_bios_load_palette2(base, count, r, g, b, bits); free(r, M_DEVBUF); return (error); } static int vesa_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg) { int bytes; if (adp != vesa_adp) return ((*prevvidsw->ioctl)(adp, cmd, arg)); switch (cmd) { case FBIO_SETWINORG: /* set frame buffer window origin */ if (!VESA_MODE(adp->va_mode)) return (*prevvidsw->ioctl)(adp, cmd, arg); return (vesa_set_origin(adp, *(off_t *)arg) ? ENODEV : 0); case FBIO_SETDISPSTART: /* set display start address */ if (!VESA_MODE(adp->va_mode)) return ((*prevvidsw->ioctl)(adp, cmd, arg)); if (vesa_bios_set_start(((video_display_start_t *)arg)->x, ((video_display_start_t *)arg)->y)) return (ENODEV); adp->va_disp_start.x = ((video_display_start_t *)arg)->x; adp->va_disp_start.y = ((video_display_start_t *)arg)->y; return (0); case FBIO_SETLINEWIDTH: /* set line length in pixel */ if (!VESA_MODE(adp->va_mode)) return ((*prevvidsw->ioctl)(adp, cmd, arg)); if (vesa_bios_set_line_length(*(u_int *)arg, &bytes, NULL)) return (ENODEV); adp->va_line_width = bytes; #if VESA_DEBUG > 1 printf("new line width:%d\n", adp->va_line_width); #endif return (0); case FBIO_GETPALETTE: /* get color palette */ if (get_palette(adp, ((video_color_palette_t *)arg)->index, ((video_color_palette_t *)arg)->count, ((video_color_palette_t *)arg)->red, ((video_color_palette_t *)arg)->green, ((video_color_palette_t *)arg)->blue, ((video_color_palette_t *)arg)->transparent)) return ((*prevvidsw->ioctl)(adp, cmd, arg)); return (0); case FBIO_SETPALETTE: /* set color palette */ if (set_palette(adp, ((video_color_palette_t *)arg)->index, ((video_color_palette_t *)arg)->count, ((video_color_palette_t *)arg)->red, ((video_color_palette_t *)arg)->green, ((video_color_palette_t *)arg)->blue, ((video_color_palette_t *)arg)->transparent)) return ((*prevvidsw->ioctl)(adp, cmd, arg)); return (0); case FBIOGETCMAP: /* get color palette */ if (get_palette(adp, ((struct fbcmap *)arg)->index, ((struct fbcmap *)arg)->count, ((struct fbcmap *)arg)->red, ((struct fbcmap *)arg)->green, ((struct fbcmap *)arg)->blue, NULL)) return ((*prevvidsw->ioctl)(adp, cmd, arg)); return (0); case FBIOPUTCMAP: /* set color palette */ if (set_palette(adp, ((struct fbcmap *)arg)->index, ((struct fbcmap *)arg)->count, ((struct fbcmap *)arg)->red, ((struct fbcmap *)arg)->green, ((struct fbcmap *)arg)->blue, NULL)) return ((*prevvidsw->ioctl)(adp, cmd, arg)); return (0); default: return ((*prevvidsw->ioctl)(adp, cmd, arg)); } } static int vesa_diag(video_adapter_t *adp, int level) { int error; /* call the previous handler first */ error = (*prevvidsw->diag)(adp, level); if (error) return (error); if (adp != vesa_adp) return (1); if (level <= 0) return (0); return (0); } static int vesa_bios_info(int level) { #if VESA_DEBUG > 1 struct vesa_mode vmode; int i; #endif uint16_t vers; vers = vesa_adp_info->v_version; if (bootverbose) { /* general adapter information */ printf( "VESA: v%d.%d, %dk memory, flags:0x%x, mode table:%p (%x)\n", (vers >> 12) * 10 + ((vers & 0x0f00) >> 8), ((vers & 0x00f0) >> 4) * 10 + (vers & 0x000f), vesa_adp_info->v_memsize * 64, vesa_adp_info->v_flags, vesa_vmodetab, vesa_adp_info->v_modetable); /* OEM string */ if (vesa_oemstr != NULL) printf("VESA: %s\n", vesa_oemstr); } if (level <= 0) return (0); if (vers >= 0x0200 && bootverbose) { /* vender name, product name, product revision */ printf("VESA: %s %s %s\n", (vesa_venderstr != NULL) ? vesa_venderstr : "unknown", (vesa_prodstr != NULL) ? vesa_prodstr : "unknown", (vesa_revstr != NULL) ? vesa_revstr : "?"); } #if VESA_DEBUG > 1 /* mode information */ for (i = 0; (i < (M_VESA_MODE_MAX - M_VESA_BASE + 1)) && (vesa_vmodetab[i] != 0xffff); ++i) { if (vesa_bios_get_mode(vesa_vmodetab[i], &vmode)) continue; /* print something for diagnostic purpose */ printf("VESA: mode:0x%03x, flags:0x%04x", vesa_vmodetab[i], vmode.v_modeattr); if (vmode.v_modeattr & V_MODEOPTINFO) { if (vmode.v_modeattr & V_MODEGRAPHICS) { printf(", G %dx%dx%d %d, ", vmode.v_width, vmode.v_height, vmode.v_bpp, vmode.v_planes); } else { printf(", T %dx%d, ", vmode.v_width, vmode.v_height); } printf("font:%dx%d, ", vmode.v_cwidth, vmode.v_cheight); printf("pages:%d, mem:%d", vmode.v_ipages + 1, vmode.v_memmodel); } if (vmode.v_modeattr & V_MODELFB) { printf("\nVESA: LFB:0x%x, off:0x%x, off_size:0x%x", vmode.v_lfb, vmode.v_offscreen, vmode.v_offscreensize*1024); } printf("\n"); printf("VESA: window A:0x%x (%x), window B:0x%x (%x), ", vmode.v_waseg, vmode.v_waattr, vmode.v_wbseg, vmode.v_wbattr); printf("size:%dk, gran:%dk\n", vmode.v_wsize, vmode.v_wgran); } #endif /* VESA_DEBUG > 1 */ return (0); } /* module loading */ static int vesa_load(void) { int error; int s; if (vesa_init_done) return (0); /* locate a VGA adapter */ s = spltty(); vesa_adp = NULL; error = vesa_configure(0); splx(s); if (error == 0) vesa_bios_info(bootverbose); return (error); } static int vesa_unload(void) { u_char palette[256*3]; int error; - int bits; int s; /* if the adapter is currently in a VESA mode, don't unload */ if ((vesa_adp != NULL) && VESA_MODE(vesa_adp->va_mode)) return (EBUSY); /* * FIXME: if there is at least one vty which is in a VESA mode, * we shouldn't be unloading! XXX */ s = spltty(); if ((error = vesa_unload_ioctl()) == 0) { if (vesa_adp != NULL) { - if (vesa_adp_info->v_flags & V_DAC8) { - bits = vesa_bios_get_dac(); - if (bits > 6) { - vesa_bios_save_palette(0, 256, - palette, bits); - vesa_bios_set_dac(6); - vesa_bios_load_palette(0, 256, - palette, 6); - } + if ((vesa_adp->va_flags & V_ADP_DAC8) != 0) { + vesa_bios_save_palette(0, 256, palette, 8); + vesa_bios_set_dac(6); + vesa_adp->va_flags &= ~V_ADP_DAC8; + vesa_bios_load_palette(0, 256, palette, 6); } vesa_adp->va_flags &= ~V_ADP_VESA; vidsw[vesa_adp->va_index] = prevvidsw; } } splx(s); if (vesa_oemstr != NULL) free(vesa_oemstr, M_DEVBUF); if (vesa_venderstr != NULL) free(vesa_venderstr, M_DEVBUF); if (vesa_prodstr != NULL) free(vesa_prodstr, M_DEVBUF); if (vesa_revstr != NULL) free(vesa_revstr, M_DEVBUF); if (vesa_vmode != &vesa_vmode_empty) free(vesa_vmode, M_DEVBUF); return (error); } static int vesa_mod_event(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: return (vesa_load()); case MOD_UNLOAD: return (vesa_unload()); } return (EOPNOTSUPP); } static moduledata_t vesa_mod = { "vesa", vesa_mod_event, NULL, }; DECLARE_MODULE(vesa, vesa_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_DEPEND(vesa, x86bios, 1, 1, 1); #endif /* VGA_NO_MODE_CHANGE */ Index: projects/ppc64/sys/dev/gem/if_gem.c =================================================================== --- projects/ppc64/sys/dev/gem/if_gem.c (revision 204271) +++ projects/ppc64/sys/dev/gem/if_gem.c (revision 204272) @@ -1,2229 +1,2232 @@ /*- * Copyright (C) 2001 Eduardo Horvath. * Copyright (c) 2001-2003 Thomas Moestl * Copyright (c) 2007 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 ``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. * * from: NetBSD: gem.c,v 1.21 2002/06/01 23:50:58 lukem Exp */ #include __FBSDID("$FreeBSD$"); /* * Driver for Apple GMAC, Sun ERI and Sun GEM Ethernet controllers */ #if 0 #define GEM_DEBUG #endif #if 0 /* XXX: In case of emergency, re-enable this. */ #define GEM_RINT_TIMEOUT #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(powerof2(GEM_NRXDESC) && GEM_NRXDESC >= 32 && GEM_NRXDESC <= 8192); CTASSERT(powerof2(GEM_NTXDESC) && GEM_NTXDESC >= 32 && GEM_NTXDESC <= 8192); #define GEM_TRIES 10000 /* * The hardware supports basic TCP/UDP checksum offloading. However, * the hardware doesn't compensate the checksum for UDP datagram which * can yield to 0x0. As a safe guard, UDP checksum offload is disabled * by default. It can be reactivated by setting special link option * link0 with ifconfig(8). */ #define GEM_CSUM_FEATURES (CSUM_TCP) static int gem_add_rxbuf(struct gem_softc *sc, int idx); static int gem_bitwait(struct gem_softc *sc, u_int bank, bus_addr_t r, uint32_t clr, uint32_t set); static void gem_cddma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static int gem_disable_rx(struct gem_softc *sc); static int gem_disable_tx(struct gem_softc *sc); static void gem_eint(struct gem_softc *sc, u_int status); static void gem_init(void *xsc); static void gem_init_locked(struct gem_softc *sc); static void gem_init_regs(struct gem_softc *sc); static int gem_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static int gem_load_txmbuf(struct gem_softc *sc, struct mbuf **m_head); static int gem_meminit(struct gem_softc *sc); static void gem_mifinit(struct gem_softc *sc); static void gem_reset(struct gem_softc *sc); static int gem_reset_rx(struct gem_softc *sc); static void gem_reset_rxdma(struct gem_softc *sc); static int gem_reset_tx(struct gem_softc *sc); static u_int gem_ringsize(u_int sz); static void gem_rint(struct gem_softc *sc); #ifdef GEM_RINT_TIMEOUT static void gem_rint_timeout(void *arg); #endif static inline void gem_rxcksum(struct mbuf *m, uint64_t flags); static void gem_rxdrain(struct gem_softc *sc); static void gem_setladrf(struct gem_softc *sc); static void gem_start(struct ifnet *ifp); static void gem_start_locked(struct ifnet *ifp); static void gem_stop(struct ifnet *ifp, int disable); static void gem_tick(void *arg); static void gem_tint(struct gem_softc *sc); static inline void gem_txkick(struct gem_softc *sc); static int gem_watchdog(struct gem_softc *sc); devclass_t gem_devclass; DRIVER_MODULE(miibus, gem, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(gem, miibus, 1, 1, 1); #ifdef GEM_DEBUG #include #define KTR_GEM KTR_CT2 #endif #define GEM_BANK1_BITWAIT(sc, r, clr, set) \ gem_bitwait((sc), GEM_RES_BANK1, (r), (clr), (set)) #define GEM_BANK2_BITWAIT(sc, r, clr, set) \ gem_bitwait((sc), GEM_RES_BANK2, (r), (clr), (set)) int gem_attach(struct gem_softc *sc) { struct gem_txsoft *txs; struct ifnet *ifp; int error, i; uint32_t v; if (bootverbose) device_printf(sc->sc_dev, "flags=0x%x\n", sc->sc_flags); /* Set up ifnet structure. */ ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) return (ENOSPC); sc->sc_csum_features = GEM_CSUM_FEATURES; ifp->if_softc = sc; if_initname(ifp, device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = gem_start; ifp->if_ioctl = gem_ioctl; ifp->if_init = gem_init; IFQ_SET_MAXLEN(&ifp->if_snd, GEM_TXQUEUELEN); ifp->if_snd.ifq_drv_maxlen = GEM_TXQUEUELEN; IFQ_SET_READY(&ifp->if_snd); callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0); #ifdef GEM_RINT_TIMEOUT callout_init_mtx(&sc->sc_rx_ch, &sc->sc_mtx, 0); #endif /* Make sure the chip is stopped. */ gem_reset(sc); error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->sc_pdmatag); if (error != 0) goto fail_ifnet; error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_rdmatag); if (error != 0) goto fail_ptag; error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * GEM_NTXSEGS, GEM_NTXSEGS, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_tdmatag); if (error != 0) goto fail_rtag; error = bus_dma_tag_create(sc->sc_pdmatag, PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct gem_control_data), 1, sizeof(struct gem_control_data), 0, NULL, NULL, &sc->sc_cdmatag); if (error != 0) goto fail_ttag; /* * Allocate the control data structures, create and load the * DMA map for it. */ if ((error = bus_dmamem_alloc(sc->sc_cdmatag, (void **)&sc->sc_control_data, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->sc_cddmamap)) != 0) { device_printf(sc->sc_dev, "unable to allocate control data, error = %d\n", error); goto fail_ctag; } sc->sc_cddma = 0; if ((error = bus_dmamap_load(sc->sc_cdmatag, sc->sc_cddmamap, sc->sc_control_data, sizeof(struct gem_control_data), gem_cddma_callback, sc, 0)) != 0 || sc->sc_cddma == 0) { device_printf(sc->sc_dev, "unable to load control data DMA map, error = %d\n", error); goto fail_cmem; } /* * Initialize the transmit job descriptors. */ STAILQ_INIT(&sc->sc_txfreeq); STAILQ_INIT(&sc->sc_txdirtyq); /* * Create the transmit buffer DMA maps. */ error = ENOMEM; for (i = 0; i < GEM_TXQUEUELEN; i++) { txs = &sc->sc_txsoft[i]; txs->txs_mbuf = NULL; txs->txs_ndescs = 0; if ((error = bus_dmamap_create(sc->sc_tdmatag, 0, &txs->txs_dmamap)) != 0) { device_printf(sc->sc_dev, "unable to create TX DMA map %d, error = %d\n", i, error); goto fail_txd; } STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); } /* * Create the receive buffer DMA maps. */ for (i = 0; i < GEM_NRXDESC; i++) { if ((error = bus_dmamap_create(sc->sc_rdmatag, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) { device_printf(sc->sc_dev, "unable to create RX DMA map %d, error = %d\n", i, error); goto fail_rxd; } sc->sc_rxsoft[i].rxs_mbuf = NULL; } /* Bad things will happen when touching this register on ERI. */ if (sc->sc_variant != GEM_SUN_ERI) GEM_BANK1_WRITE_4(sc, GEM_MII_DATAPATH_MODE, GEM_MII_DATAPATH_MII); gem_mifinit(sc); /* * Look for an external PHY. */ error = ENXIO; v = GEM_BANK1_READ_4(sc, GEM_MIF_CONFIG); if ((v & GEM_MIF_CONFIG_MDI1) != 0) { v |= GEM_MIF_CONFIG_PHY_SEL; GEM_BANK1_WRITE_4(sc, GEM_MIF_CONFIG, v); switch (sc->sc_variant) { case GEM_SUN_ERI: sc->sc_phyad = GEM_PHYAD_EXTERNAL; break; default: sc->sc_phyad = -1; break; } error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, gem_mediachange, gem_mediastatus); } /* * Fall back on an internal PHY if no external PHY was found. + * Note that with Apple (K2) GMACs GEM_MIF_CONFIG_MDI0 can't be + * trusted when the firmware has powered down the chip. */ - if (error != 0 && (v & GEM_MIF_CONFIG_MDI0) != 0) { + if (error != 0 && + ((v & GEM_MIF_CONFIG_MDI0) != 0 || GEM_IS_APPLE(sc))) { v &= ~GEM_MIF_CONFIG_PHY_SEL; GEM_BANK1_WRITE_4(sc, GEM_MIF_CONFIG, v); switch (sc->sc_variant) { case GEM_SUN_ERI: case GEM_APPLE_K2_GMAC: sc->sc_phyad = GEM_PHYAD_INTERNAL; break; case GEM_APPLE_GMAC: sc->sc_phyad = GEM_PHYAD_EXTERNAL; break; default: sc->sc_phyad = -1; break; } error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, gem_mediachange, gem_mediastatus); } /* * Try the external PCS SERDES if we didn't find any PHYs. */ if (error != 0 && sc->sc_variant == GEM_SUN_GEM) { GEM_BANK1_WRITE_4(sc, GEM_MII_DATAPATH_MODE, GEM_MII_DATAPATH_SERDES); GEM_BANK1_WRITE_4(sc, GEM_MII_SLINK_CONTROL, GEM_MII_SLINK_LOOPBACK | GEM_MII_SLINK_EN_SYNC_D); GEM_BANK1_WRITE_4(sc, GEM_MII_CONFIG, GEM_MII_CONFIG_ENABLE); sc->sc_flags |= GEM_SERDES; sc->sc_phyad = GEM_PHYAD_EXTERNAL; error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, gem_mediachange, gem_mediastatus); } if (error != 0) { device_printf(sc->sc_dev, "PHY probe failed: %d\n", error); goto fail_rxd; } sc->sc_mii = device_get_softc(sc->sc_miibus); /* * From this point forward, the attachment cannot fail. A failure * before this point releases all resources that may have been * allocated. */ /* Get RX FIFO size. */ sc->sc_rxfifosize = 64 * GEM_BANK1_READ_4(sc, GEM_RX_FIFO_SIZE); /* Get TX FIFO size. */ v = GEM_BANK1_READ_4(sc, GEM_TX_FIFO_SIZE); device_printf(sc->sc_dev, "%ukB RX FIFO, %ukB TX FIFO\n", sc->sc_rxfifosize / 1024, v / 16); /* Attach the interface. */ ether_ifattach(ifp, sc->sc_enaddr); /* * Tell the upper layer(s) we support long frames/checksum offloads. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_HWCSUM; ifp->if_hwassist |= sc->sc_csum_features; ifp->if_capenable |= IFCAP_VLAN_MTU | IFCAP_HWCSUM; return (0); /* * Free any resources we've allocated during the failed attach * attempt. Do this in reverse order and fall through. */ fail_rxd: for (i = 0; i < GEM_NRXDESC; i++) if (sc->sc_rxsoft[i].rxs_dmamap != NULL) bus_dmamap_destroy(sc->sc_rdmatag, sc->sc_rxsoft[i].rxs_dmamap); fail_txd: for (i = 0; i < GEM_TXQUEUELEN; i++) if (sc->sc_txsoft[i].txs_dmamap != NULL) bus_dmamap_destroy(sc->sc_tdmatag, sc->sc_txsoft[i].txs_dmamap); bus_dmamap_unload(sc->sc_cdmatag, sc->sc_cddmamap); fail_cmem: bus_dmamem_free(sc->sc_cdmatag, sc->sc_control_data, sc->sc_cddmamap); fail_ctag: bus_dma_tag_destroy(sc->sc_cdmatag); fail_ttag: bus_dma_tag_destroy(sc->sc_tdmatag); fail_rtag: bus_dma_tag_destroy(sc->sc_rdmatag); fail_ptag: bus_dma_tag_destroy(sc->sc_pdmatag); fail_ifnet: if_free(ifp); return (error); } void gem_detach(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; int i; ether_ifdetach(ifp); GEM_LOCK(sc); gem_stop(ifp, 1); GEM_UNLOCK(sc); callout_drain(&sc->sc_tick_ch); #ifdef GEM_RINT_TIMEOUT callout_drain(&sc->sc_rx_ch); #endif if_free(ifp); device_delete_child(sc->sc_dev, sc->sc_miibus); for (i = 0; i < GEM_NRXDESC; i++) if (sc->sc_rxsoft[i].rxs_dmamap != NULL) bus_dmamap_destroy(sc->sc_rdmatag, sc->sc_rxsoft[i].rxs_dmamap); for (i = 0; i < GEM_TXQUEUELEN; i++) if (sc->sc_txsoft[i].txs_dmamap != NULL) bus_dmamap_destroy(sc->sc_tdmatag, sc->sc_txsoft[i].txs_dmamap); GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_cdmatag, sc->sc_cddmamap); bus_dmamem_free(sc->sc_cdmatag, sc->sc_control_data, sc->sc_cddmamap); bus_dma_tag_destroy(sc->sc_cdmatag); bus_dma_tag_destroy(sc->sc_tdmatag); bus_dma_tag_destroy(sc->sc_rdmatag); bus_dma_tag_destroy(sc->sc_pdmatag); } void gem_suspend(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; GEM_LOCK(sc); gem_stop(ifp, 0); GEM_UNLOCK(sc); } void gem_resume(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; GEM_LOCK(sc); /* * On resume all registers have to be initialized again like * after power-on. */ sc->sc_flags &= ~GEM_INITED; if (ifp->if_flags & IFF_UP) gem_init_locked(sc); GEM_UNLOCK(sc); } static inline void gem_rxcksum(struct mbuf *m, uint64_t flags) { struct ether_header *eh; struct ip *ip; struct udphdr *uh; uint16_t *opts; int32_t hlen, len, pktlen; uint32_t temp32; uint16_t cksum; pktlen = m->m_pkthdr.len; if (pktlen < sizeof(struct ether_header) + sizeof(struct ip)) return; eh = mtod(m, struct ether_header *); if (eh->ether_type != htons(ETHERTYPE_IP)) return; ip = (struct ip *)(eh + 1); if (ip->ip_v != IPVERSION) return; hlen = ip->ip_hl << 2; pktlen -= sizeof(struct ether_header); if (hlen < sizeof(struct ip)) return; if (ntohs(ip->ip_len) < hlen) return; if (ntohs(ip->ip_len) != pktlen) return; if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) return; /* Cannot handle fragmented packet. */ switch (ip->ip_p) { case IPPROTO_TCP: if (pktlen < (hlen + sizeof(struct tcphdr))) return; break; case IPPROTO_UDP: if (pktlen < (hlen + sizeof(struct udphdr))) return; uh = (struct udphdr *)((uint8_t *)ip + hlen); if (uh->uh_sum == 0) return; /* no checksum */ break; default: return; } cksum = ~(flags & GEM_RD_CHECKSUM); /* checksum fixup for IP options */ len = hlen - sizeof(struct ip); if (len > 0) { opts = (uint16_t *)(ip + 1); for (; len > 0; len -= sizeof(uint16_t), opts++) { temp32 = cksum - *opts; temp32 = (temp32 >> 16) + (temp32 & 65535); cksum = temp32 & 65535; } } m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; m->m_pkthdr.csum_data = cksum; } static void gem_cddma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct gem_softc *sc = xsc; if (error != 0) return; if (nsegs != 1) panic("%s: bad control buffer segment count", __func__); sc->sc_cddma = segs[0].ds_addr; } static void gem_tick(void *arg) { struct gem_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; uint32_t v; GEM_LOCK_ASSERT(sc, MA_OWNED); /* * Unload collision and error counters. */ ifp->if_collisions += GEM_BANK1_READ_4(sc, GEM_MAC_NORM_COLL_CNT) + GEM_BANK1_READ_4(sc, GEM_MAC_FIRST_COLL_CNT); v = GEM_BANK1_READ_4(sc, GEM_MAC_EXCESS_COLL_CNT) + GEM_BANK1_READ_4(sc, GEM_MAC_LATE_COLL_CNT); ifp->if_collisions += v; ifp->if_oerrors += v; ifp->if_ierrors += GEM_BANK1_READ_4(sc, GEM_MAC_RX_LEN_ERR_CNT) + GEM_BANK1_READ_4(sc, GEM_MAC_RX_ALIGN_ERR) + GEM_BANK1_READ_4(sc, GEM_MAC_RX_CRC_ERR_CNT) + GEM_BANK1_READ_4(sc, GEM_MAC_RX_CODE_VIOL); /* * Then clear the hardware counters. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_NORM_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_FIRST_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_EXCESS_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_LATE_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_LEN_ERR_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_ALIGN_ERR, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CRC_ERR_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CODE_VIOL, 0); mii_tick(sc->sc_mii); if (gem_watchdog(sc) == EJUSTRETURN) return; callout_reset(&sc->sc_tick_ch, hz, gem_tick, sc); } static int gem_bitwait(struct gem_softc *sc, u_int bank, bus_addr_t r, uint32_t clr, uint32_t set) { int i; uint32_t reg; for (i = GEM_TRIES; i--; DELAY(100)) { reg = GEM_BANKN_READ_M(bank, 4, sc, r); if ((reg & clr) == 0 && (reg & set) == set) return (1); } return (0); } static void gem_reset(struct gem_softc *sc) { #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif gem_reset_rx(sc); gem_reset_tx(sc); /* Do a full reset. */ GEM_BANK2_WRITE_4(sc, GEM_RESET, GEM_RESET_RX | GEM_RESET_TX); GEM_BANK2_BARRIER(sc, GEM_RESET, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK2_BITWAIT(sc, GEM_RESET, GEM_RESET_RX | GEM_RESET_TX, 0)) device_printf(sc->sc_dev, "cannot reset device\n"); } static void gem_rxdrain(struct gem_softc *sc) { struct gem_rxsoft *rxs; int i; for (i = 0; i < GEM_NRXDESC; i++) { rxs = &sc->sc_rxsoft[i]; if (rxs->rxs_mbuf != NULL) { bus_dmamap_sync(sc->sc_rdmatag, rxs->rxs_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_rdmatag, rxs->rxs_dmamap); m_freem(rxs->rxs_mbuf); rxs->rxs_mbuf = NULL; } } } static void gem_stop(struct ifnet *ifp, int disable) { struct gem_softc *sc = ifp->if_softc; struct gem_txsoft *txs; #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif callout_stop(&sc->sc_tick_ch); #ifdef GEM_RINT_TIMEOUT callout_stop(&sc->sc_rx_ch); #endif gem_reset_tx(sc); gem_reset_rx(sc); /* * Release any queued transmit buffers. */ while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); if (txs->txs_ndescs != 0) { bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); if (txs->txs_mbuf != NULL) { m_freem(txs->txs_mbuf); txs->txs_mbuf = NULL; } } STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); } if (disable) gem_rxdrain(sc); /* * Mark the interface down and cancel the watchdog timer. */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->sc_flags &= ~GEM_LINK; sc->sc_wdog_timer = 0; } static int gem_reset_rx(struct gem_softc *sc) { /* * Resetting while DMA is in progress can cause a bus hang, so we * disable DMA first. */ gem_disable_rx(sc); GEM_BANK1_WRITE_4(sc, GEM_RX_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_RX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_RX_CONFIG, GEM_RX_CONFIG_RXDMA_EN, 0)) device_printf(sc->sc_dev, "cannot disable RX DMA\n"); /* Finally, reset the ERX. */ GEM_BANK2_WRITE_4(sc, GEM_RESET, GEM_RESET_RX); GEM_BANK2_BARRIER(sc, GEM_RESET, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK2_BITWAIT(sc, GEM_RESET, GEM_RESET_RX | GEM_RESET_TX, 0)) { device_printf(sc->sc_dev, "cannot reset receiver\n"); return (1); } return (0); } /* * Reset the receiver DMA engine. * * Intended to be used in case of GEM_INTR_RX_TAG_ERR, GEM_MAC_RX_OVERFLOW * etc in order to reset the receiver DMA engine only and not do a full * reset which amongst others also downs the link and clears the FIFOs. */ static void gem_reset_rxdma(struct gem_softc *sc) { int i; if (gem_reset_rx(sc) != 0) return (gem_init_locked(sc)); for (i = 0; i < GEM_NRXDESC; i++) if (sc->sc_rxsoft[i].rxs_mbuf != NULL) GEM_UPDATE_RXDESC(sc, i); sc->sc_rxptr = 0; GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* NOTE: we use only 32-bit DMA addresses here. */ GEM_BANK1_WRITE_4(sc, GEM_RX_RING_PTR_HI, 0); GEM_BANK1_WRITE_4(sc, GEM_RX_RING_PTR_LO, GEM_CDRXADDR(sc, 0)); GEM_BANK1_WRITE_4(sc, GEM_RX_KICK, GEM_NRXDESC - 4); GEM_BANK1_WRITE_4(sc, GEM_RX_CONFIG, gem_ringsize(GEM_NRXDESC /* XXX */) | ((ETHER_HDR_LEN + sizeof(struct ip)) << GEM_RX_CONFIG_CXM_START_SHFT) | (GEM_THRSH_1024 << GEM_RX_CONFIG_FIFO_THRS_SHIFT) | (ETHER_ALIGN << GEM_RX_CONFIG_FBOFF_SHFT)); /* Adjust for the SBus clock probably isn't worth the fuzz. */ GEM_BANK1_WRITE_4(sc, GEM_RX_BLANKING, ((6 * (sc->sc_flags & GEM_PCI66) != 0 ? 2 : 1) << GEM_RX_BLANKING_TIME_SHIFT) | 6); GEM_BANK1_WRITE_4(sc, GEM_RX_PAUSE_THRESH, (3 * sc->sc_rxfifosize / 256) | ((sc->sc_rxfifosize / 256) << 12)); GEM_BANK1_WRITE_4(sc, GEM_RX_CONFIG, GEM_BANK1_READ_4(sc, GEM_RX_CONFIG) | GEM_RX_CONFIG_RXDMA_EN); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_MASK, GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, GEM_BANK1_READ_4(sc, GEM_MAC_RX_CONFIG) | GEM_MAC_RX_ENABLE); } static int gem_reset_tx(struct gem_softc *sc) { /* * Resetting while DMA is in progress can cause a bus hang, so we * disable DMA first. */ gem_disable_tx(sc); GEM_BANK1_WRITE_4(sc, GEM_TX_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_TX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_TX_CONFIG, GEM_TX_CONFIG_TXDMA_EN, 0)) device_printf(sc->sc_dev, "cannot disable TX DMA\n"); /* Finally, reset the ETX. */ GEM_BANK2_WRITE_4(sc, GEM_RESET, GEM_RESET_TX); GEM_BANK2_BARRIER(sc, GEM_RESET, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK2_BITWAIT(sc, GEM_RESET, GEM_RESET_RX | GEM_RESET_TX, 0)) { device_printf(sc->sc_dev, "cannot reset transmitter\n"); return (1); } return (0); } static int gem_disable_rx(struct gem_softc *sc) { GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, GEM_BANK1_READ_4(sc, GEM_MAC_RX_CONFIG) & ~GEM_MAC_RX_ENABLE); GEM_BANK1_BARRIER(sc, GEM_MAC_RX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); return (GEM_BANK1_BITWAIT(sc, GEM_MAC_RX_CONFIG, GEM_MAC_RX_ENABLE, 0)); } static int gem_disable_tx(struct gem_softc *sc) { GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, GEM_BANK1_READ_4(sc, GEM_MAC_TX_CONFIG) & ~GEM_MAC_TX_ENABLE); GEM_BANK1_BARRIER(sc, GEM_MAC_TX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); return (GEM_BANK1_BITWAIT(sc, GEM_MAC_TX_CONFIG, GEM_MAC_TX_ENABLE, 0)); } static int gem_meminit(struct gem_softc *sc) { struct gem_rxsoft *rxs; int error, i; GEM_LOCK_ASSERT(sc, MA_OWNED); /* * Initialize the transmit descriptor ring. */ for (i = 0; i < GEM_NTXDESC; i++) { sc->sc_txdescs[i].gd_flags = 0; sc->sc_txdescs[i].gd_addr = 0; } sc->sc_txfree = GEM_MAXTXFREE; sc->sc_txnext = 0; sc->sc_txwin = 0; /* * Initialize the receive descriptor and receive job * descriptor rings. */ for (i = 0; i < GEM_NRXDESC; i++) { rxs = &sc->sc_rxsoft[i]; if (rxs->rxs_mbuf == NULL) { if ((error = gem_add_rxbuf(sc, i)) != 0) { device_printf(sc->sc_dev, "unable to allocate or map RX buffer %d, " "error = %d\n", i, error); /* * XXX we should attempt to run with fewer * receive buffers instead of just failing. */ gem_rxdrain(sc); return (1); } } else GEM_INIT_RXDESC(sc, i); } sc->sc_rxptr = 0; GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static u_int gem_ringsize(u_int sz) { switch (sz) { case 32: return (GEM_RING_SZ_32); case 64: return (GEM_RING_SZ_64); case 128: return (GEM_RING_SZ_128); case 256: return (GEM_RING_SZ_256); case 512: return (GEM_RING_SZ_512); case 1024: return (GEM_RING_SZ_1024); case 2048: return (GEM_RING_SZ_2048); case 4096: return (GEM_RING_SZ_4096); case 8192: return (GEM_RING_SZ_8192); default: printf("%s: invalid ring size %d\n", __func__, sz); return (GEM_RING_SZ_32); } } static void gem_init(void *xsc) { struct gem_softc *sc = xsc; GEM_LOCK(sc); gem_init_locked(sc); GEM_UNLOCK(sc); } /* * Initialization of interface; set up initialization block * and transmit/receive descriptor rings. */ static void gem_init_locked(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t v; GEM_LOCK_ASSERT(sc, MA_OWNED); #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: %s: calling stop", device_get_name(sc->sc_dev), __func__); #endif /* * Initialization sequence. The numbered steps below correspond * to the sequence outlined in section 6.3.5.1 in the Ethernet * Channel Engine manual (part of the PCIO manual). * See also the STP2002-STQ document from Sun Microsystems. */ /* step 1 & 2. Reset the Ethernet Channel. */ gem_stop(ifp, 0); gem_reset(sc); #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: %s: restarting", device_get_name(sc->sc_dev), __func__); #endif /* Re-initialize the MIF. */ gem_mifinit(sc); /* step 3. Setup data structures in host memory. */ if (gem_meminit(sc) != 0) return; /* step 4. TX MAC registers & counters */ gem_init_regs(sc); /* step 5. RX MAC registers & counters */ gem_setladrf(sc); /* step 6 & 7. Program Descriptor Ring Base Addresses. */ /* NOTE: we use only 32-bit DMA addresses here. */ GEM_BANK1_WRITE_4(sc, GEM_TX_RING_PTR_HI, 0); GEM_BANK1_WRITE_4(sc, GEM_TX_RING_PTR_LO, GEM_CDTXADDR(sc, 0)); GEM_BANK1_WRITE_4(sc, GEM_RX_RING_PTR_HI, 0); GEM_BANK1_WRITE_4(sc, GEM_RX_RING_PTR_LO, GEM_CDRXADDR(sc, 0)); #ifdef GEM_DEBUG CTR3(KTR_GEM, "loading RX ring %lx, TX ring %lx, cddma %lx", GEM_CDRXADDR(sc, 0), GEM_CDTXADDR(sc, 0), sc->sc_cddma); #endif /* step 8. Global Configuration & Interrupt Mask */ /* * Set the internal arbitration to "infinite" bursts of the * maximum length of 31 * 64 bytes so DMA transfers aren't * split up in cache line size chunks. This greatly improves * RX performance. * Enable silicon bug workarounds for the Apple variants. */ GEM_BANK1_WRITE_4(sc, GEM_CONFIG, GEM_CONFIG_TXDMA_LIMIT | GEM_CONFIG_RXDMA_LIMIT | ((sc->sc_flags & GEM_PCI) != 0 ? GEM_CONFIG_BURST_INF : GEM_CONFIG_BURST_64) | (GEM_IS_APPLE(sc) ? GEM_CONFIG_RONPAULBIT | GEM_CONFIG_BUG2FIX : 0)); GEM_BANK1_WRITE_4(sc, GEM_INTMASK, ~(GEM_INTR_TX_INTME | GEM_INTR_TX_EMPTY | GEM_INTR_RX_DONE | GEM_INTR_RX_NOBUF | GEM_INTR_RX_TAG_ERR | GEM_INTR_PERR | GEM_INTR_BERR #ifdef GEM_DEBUG | GEM_INTR_PCS | GEM_INTR_MIF #endif )); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_MASK, GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT); GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_MASK, GEM_MAC_TX_XMIT_DONE | GEM_MAC_TX_DEFER_EXP | GEM_MAC_TX_PEAK_EXP); #ifdef GEM_DEBUG GEM_BANK1_WRITE_4(sc, GEM_MAC_CONTROL_MASK, ~(GEM_MAC_PAUSED | GEM_MAC_PAUSE | GEM_MAC_RESUME)); #else GEM_BANK1_WRITE_4(sc, GEM_MAC_CONTROL_MASK, GEM_MAC_PAUSED | GEM_MAC_PAUSE | GEM_MAC_RESUME); #endif /* step 9. ETX Configuration: use mostly default values. */ /* Enable DMA. */ v = gem_ringsize(GEM_NTXDESC); /* Set TX FIFO threshold and enable DMA. */ v |= ((sc->sc_variant == GEM_SUN_ERI ? 0x100 : 0x4ff) << 10) & GEM_TX_CONFIG_TXFIFO_TH; GEM_BANK1_WRITE_4(sc, GEM_TX_CONFIG, v | GEM_TX_CONFIG_TXDMA_EN); /* step 10. ERX Configuration */ /* Encode Receive Descriptor ring size. */ v = gem_ringsize(GEM_NRXDESC /* XXX */); /* RX TCP/UDP checksum offset */ v |= ((ETHER_HDR_LEN + sizeof(struct ip)) << GEM_RX_CONFIG_CXM_START_SHFT); /* Set RX FIFO threshold, set first byte offset and enable DMA. */ GEM_BANK1_WRITE_4(sc, GEM_RX_CONFIG, v | (GEM_THRSH_1024 << GEM_RX_CONFIG_FIFO_THRS_SHIFT) | (ETHER_ALIGN << GEM_RX_CONFIG_FBOFF_SHFT) | GEM_RX_CONFIG_RXDMA_EN); /* Adjust for the SBus clock probably isn't worth the fuzz. */ GEM_BANK1_WRITE_4(sc, GEM_RX_BLANKING, ((6 * (sc->sc_flags & GEM_PCI66) != 0 ? 2 : 1) << GEM_RX_BLANKING_TIME_SHIFT) | 6); /* * The following value is for an OFF Threshold of about 3/4 full * and an ON Threshold of 1/4 full. */ GEM_BANK1_WRITE_4(sc, GEM_RX_PAUSE_THRESH, (3 * sc->sc_rxfifosize / 256) | ((sc->sc_rxfifosize / 256) << 12)); /* step 11. Configure Media. */ /* step 12. RX_MAC Configuration Register */ v = GEM_BANK1_READ_4(sc, GEM_MAC_RX_CONFIG); v |= GEM_MAC_RX_ENABLE | GEM_MAC_RX_STRIP_CRC; GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_MAC_RX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_MAC_RX_CONFIG, GEM_MAC_RX_ENABLE, 0)) device_printf(sc->sc_dev, "cannot configure RX MAC\n"); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, v); /* step 13. TX_MAC Configuration Register */ v = GEM_BANK1_READ_4(sc, GEM_MAC_TX_CONFIG); v |= GEM_MAC_TX_ENABLE; GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_MAC_TX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_MAC_TX_CONFIG, GEM_MAC_TX_ENABLE, 0)) device_printf(sc->sc_dev, "cannot configure TX MAC\n"); GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, v); /* step 14. Issue Transmit Pending command. */ /* step 15. Give the reciever a swift kick. */ GEM_BANK1_WRITE_4(sc, GEM_RX_KICK, GEM_NRXDESC - 4); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mii_mediachg(sc->sc_mii); /* Start the one second timer. */ sc->sc_wdog_timer = 0; callout_reset(&sc->sc_tick_ch, hz, gem_tick, sc); } static int gem_load_txmbuf(struct gem_softc *sc, struct mbuf **m_head) { bus_dma_segment_t txsegs[GEM_NTXSEGS]; struct gem_txsoft *txs; struct ip *ip; struct mbuf *m; uint64_t cflags, flags; int error, nexttx, nsegs, offset, seg; GEM_LOCK_ASSERT(sc, MA_OWNED); /* Get a work queue entry. */ if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) { /* Ran out of descriptors. */ return (ENOBUFS); } cflags = 0; if (((*m_head)->m_pkthdr.csum_flags & sc->sc_csum_features) != 0) { if (M_WRITABLE(*m_head) == 0) { m = m_dup(*m_head, M_DONTWAIT); m_freem(*m_head); *m_head = m; if (m == NULL) return (ENOBUFS); } offset = sizeof(struct ether_header); m = m_pullup(*m_head, offset + sizeof(struct ip)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } ip = (struct ip *)(mtod(m, caddr_t) + offset); offset += (ip->ip_hl << 2); cflags = offset << GEM_TD_CXSUM_STARTSHFT | ((offset + m->m_pkthdr.csum_data) << GEM_TD_CXSUM_STUFFSHFT) | GEM_TD_CXSUM_ENABLE; *m_head = m; } error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, txs->txs_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { m = m_collapse(*m_head, M_DONTWAIT, GEM_NTXSEGS); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } *m_head = m; error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, txs->txs_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (error); } } else if (error != 0) return (error); /* If nsegs is wrong then the stack is corrupt. */ KASSERT(nsegs <= GEM_NTXSEGS, ("%s: too many DMA segments (%d)", __func__, nsegs)); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* * Ensure we have enough descriptors free to describe * the packet. Note, we always reserve one descriptor * at the end of the ring as a termination point, in * order to prevent wrap-around. */ if (nsegs > sc->sc_txfree - 1) { txs->txs_ndescs = 0; bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); return (ENOBUFS); } txs->txs_ndescs = nsegs; txs->txs_firstdesc = sc->sc_txnext; nexttx = txs->txs_firstdesc; for (seg = 0; seg < nsegs; seg++, nexttx = GEM_NEXTTX(nexttx)) { #ifdef GEM_DEBUG CTR6(KTR_GEM, "%s: mapping seg %d (txd %d), len %lx, addr %#lx (%#lx)", __func__, seg, nexttx, txsegs[seg].ds_len, txsegs[seg].ds_addr, GEM_DMA_WRITE(sc, txsegs[seg].ds_addr)); #endif sc->sc_txdescs[nexttx].gd_addr = GEM_DMA_WRITE(sc, txsegs[seg].ds_addr); KASSERT(txsegs[seg].ds_len < GEM_TD_BUFSIZE, ("%s: segment size too large!", __func__)); flags = txsegs[seg].ds_len & GEM_TD_BUFSIZE; sc->sc_txdescs[nexttx].gd_flags = GEM_DMA_WRITE(sc, flags | cflags); txs->txs_lastdesc = nexttx; } /* Set EOP on the last descriptor. */ #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: end of packet at segment %d, TX %d", __func__, seg, nexttx); #endif sc->sc_txdescs[txs->txs_lastdesc].gd_flags |= GEM_DMA_WRITE(sc, GEM_TD_END_OF_PACKET); /* Lastly set SOP on the first descriptor. */ #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: start of packet at segment %d, TX %d", __func__, seg, nexttx); #endif if (++sc->sc_txwin > GEM_NTXSEGS * 2 / 3) { sc->sc_txwin = 0; sc->sc_txdescs[txs->txs_firstdesc].gd_flags |= GEM_DMA_WRITE(sc, GEM_TD_INTERRUPT_ME | GEM_TD_START_OF_PACKET); } else sc->sc_txdescs[txs->txs_firstdesc].gd_flags |= GEM_DMA_WRITE(sc, GEM_TD_START_OF_PACKET); /* Sync the DMA map. */ bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, BUS_DMASYNC_PREWRITE); #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: setting firstdesc=%d, lastdesc=%d, ndescs=%d", __func__, txs->txs_firstdesc, txs->txs_lastdesc, txs->txs_ndescs); #endif STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q); STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q); txs->txs_mbuf = *m_head; sc->sc_txnext = GEM_NEXTTX(txs->txs_lastdesc); sc->sc_txfree -= txs->txs_ndescs; return (0); } static void gem_init_regs(struct gem_softc *sc) { const u_char *laddr = IF_LLADDR(sc->sc_ifp); GEM_LOCK_ASSERT(sc, MA_OWNED); /* These registers are not cleared on reset. */ if ((sc->sc_flags & GEM_INITED) == 0) { /* magic values */ GEM_BANK1_WRITE_4(sc, GEM_MAC_IPG0, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_IPG1, 8); GEM_BANK1_WRITE_4(sc, GEM_MAC_IPG2, 4); /* min frame length */ GEM_BANK1_WRITE_4(sc, GEM_MAC_MAC_MIN_FRAME, ETHER_MIN_LEN); /* max frame length and max burst size */ GEM_BANK1_WRITE_4(sc, GEM_MAC_MAC_MAX_FRAME, (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) | (0x2000 << 16)); /* more magic values */ GEM_BANK1_WRITE_4(sc, GEM_MAC_PREAMBLE_LEN, 0x7); GEM_BANK1_WRITE_4(sc, GEM_MAC_JAM_SIZE, 0x4); GEM_BANK1_WRITE_4(sc, GEM_MAC_ATTEMPT_LIMIT, 0x10); GEM_BANK1_WRITE_4(sc, GEM_MAC_CONTROL_TYPE, 0x8088); /* random number seed */ GEM_BANK1_WRITE_4(sc, GEM_MAC_RANDOM_SEED, ((laddr[5] << 8) | laddr[4]) & 0x3ff); /* secondary MAC address: 0:0:0:0:0:0 */ GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR3, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR4, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR5, 0); /* MAC control address: 01:80:c2:00:00:01 */ GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR6, 0x0001); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR7, 0xc200); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR8, 0x0180); /* MAC filter address: 0:0:0:0:0:0 */ GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR_FILTER0, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR_FILTER1, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR_FILTER2, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADR_FLT_MASK1_2, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADR_FLT_MASK0, 0); sc->sc_flags |= GEM_INITED; } /* Counters need to be zeroed. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_NORM_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_FIRST_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_EXCESS_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_LATE_COLL_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_DEFER_TMR_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_PEAK_ATTEMPTS, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_FRAME_COUNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_LEN_ERR_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_ALIGN_ERR, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CRC_ERR_CNT, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CODE_VIOL, 0); /* Set XOFF PAUSE time. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_SEND_PAUSE_CMD, 0x1BF0); /* Set the station address. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR0, (laddr[4] << 8) | laddr[5]); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR1, (laddr[2] << 8) | laddr[3]); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR2, (laddr[0] << 8) | laddr[1]); /* Enable MII outputs. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_XIF_CONFIG, GEM_MAC_XIF_TX_MII_ENA); } static void gem_start(struct ifnet *ifp) { struct gem_softc *sc = ifp->if_softc; GEM_LOCK(sc); gem_start_locked(ifp); GEM_UNLOCK(sc); } static inline void gem_txkick(struct gem_softc *sc) { /* * Update the TX kick register. This register has to point to the * descriptor after the last valid one and for optimum performance * should be incremented in multiples of 4 (the DMA engine fetches/ * updates descriptors in batches of 4). */ #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: %s: kicking TX %d", device_get_name(sc->sc_dev), __func__, sc->sc_txnext); #endif GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); GEM_BANK1_WRITE_4(sc, GEM_TX_KICK, sc->sc_txnext); } static void gem_start_locked(struct ifnet *ifp) { struct gem_softc *sc = ifp->if_softc; struct mbuf *m; int kicked, ntx; GEM_LOCK_ASSERT(sc, MA_OWNED); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->sc_flags & GEM_LINK) == 0) return; #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: %s: txfree %d, txnext %d", device_get_name(sc->sc_dev), __func__, sc->sc_txfree, sc->sc_txnext); #endif ntx = 0; kicked = 0; for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->sc_txfree > 1;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (gem_load_txmbuf(sc, &m) != 0) { if (m == NULL) break; ifp->if_drv_flags |= IFF_DRV_OACTIVE; IFQ_DRV_PREPEND(&ifp->if_snd, m); break; } if ((sc->sc_txnext % 4) == 0) { gem_txkick(sc); kicked = 1; } else kicked = 0; ntx++; BPF_MTAP(ifp, m); } if (ntx > 0) { if (kicked == 0) gem_txkick(sc); #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: packets enqueued, OWN on %d", device_get_name(sc->sc_dev), sc->sc_txnext); #endif /* Set a watchdog timer in case the chip flakes out. */ sc->sc_wdog_timer = 5; #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: %s: watchdog %d", device_get_name(sc->sc_dev), __func__, sc->sc_wdog_timer); #endif } } static void gem_tint(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct gem_txsoft *txs; int progress; uint32_t txlast; #ifdef GEM_DEBUG int i; GEM_LOCK_ASSERT(sc, MA_OWNED); CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif /* * Go through our TX list and free mbufs for those * frames that have been transmitted. */ progress = 0; GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD); while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { #ifdef GEM_DEBUG if ((ifp->if_flags & IFF_DEBUG) != 0) { printf(" txsoft %p transmit chain:\n", txs); for (i = txs->txs_firstdesc;; i = GEM_NEXTTX(i)) { printf("descriptor %d: ", i); printf("gd_flags: 0x%016llx\t", (long long)GEM_DMA_READ(sc, sc->sc_txdescs[i].gd_flags)); printf("gd_addr: 0x%016llx\n", (long long)GEM_DMA_READ(sc, sc->sc_txdescs[i].gd_addr)); if (i == txs->txs_lastdesc) break; } } #endif /* * In theory, we could harvest some descriptors before * the ring is empty, but that's a bit complicated. * * GEM_TX_COMPLETION points to the last descriptor * processed + 1. */ txlast = GEM_BANK1_READ_4(sc, GEM_TX_COMPLETION); #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: txs->txs_firstdesc = %d, " "txs->txs_lastdesc = %d, txlast = %d", __func__, txs->txs_firstdesc, txs->txs_lastdesc, txlast); #endif if (txs->txs_firstdesc <= txs->txs_lastdesc) { if ((txlast >= txs->txs_firstdesc) && (txlast <= txs->txs_lastdesc)) break; } else { /* Ick -- this command wraps. */ if ((txlast >= txs->txs_firstdesc) || (txlast <= txs->txs_lastdesc)) break; } #ifdef GEM_DEBUG CTR1(KTR_GEM, "%s: releasing a descriptor", __func__); #endif STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); sc->sc_txfree += txs->txs_ndescs; bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); if (txs->txs_mbuf != NULL) { m_freem(txs->txs_mbuf); txs->txs_mbuf = NULL; } STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); ifp->if_opackets++; progress = 1; } #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: GEM_TX_STATE_MACHINE %x GEM_TX_DATA_PTR %llx " "GEM_TX_COMPLETION %x", __func__, GEM_BANK1_READ_4(sc, GEM_TX_STATE_MACHINE), ((long long)GEM_BANK1_READ_4(sc, GEM_TX_DATA_PTR_HI) << 32) | GEM_BANK1_READ_4(sc, GEM_TX_DATA_PTR_LO), GEM_BANK1_READ_4(sc, GEM_TX_COMPLETION)); #endif if (progress) { if (sc->sc_txfree == GEM_NTXDESC - 1) sc->sc_txwin = 0; /* * We freed some descriptors, so reset IFF_DRV_OACTIVE * and restart. */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (STAILQ_EMPTY(&sc->sc_txdirtyq)) sc->sc_wdog_timer = 0; gem_start_locked(ifp); } #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: %s: watchdog %d", device_get_name(sc->sc_dev), __func__, sc->sc_wdog_timer); #endif } #ifdef GEM_RINT_TIMEOUT static void gem_rint_timeout(void *arg) { struct gem_softc *sc = arg; GEM_LOCK_ASSERT(sc, MA_OWNED); gem_rint(sc); } #endif static void gem_rint(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint64_t rxstat; uint32_t rxcomp; GEM_LOCK_ASSERT(sc, MA_OWNED); #ifdef GEM_RINT_TIMEOUT callout_stop(&sc->sc_rx_ch); #endif #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif /* * Read the completion register once. This limits * how long the following loop can execute. */ rxcomp = GEM_BANK1_READ_4(sc, GEM_RX_COMPLETION); #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: sc->sc_rxptr %d, complete %d", __func__, sc->sc_rxptr, rxcomp); #endif GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (; sc->sc_rxptr != rxcomp;) { m = sc->sc_rxsoft[sc->sc_rxptr].rxs_mbuf; rxstat = GEM_DMA_READ(sc, sc->sc_rxdescs[sc->sc_rxptr].gd_flags); if (rxstat & GEM_RD_OWN) { #ifdef GEM_RINT_TIMEOUT /* * The descriptor is still marked as owned, although * it is supposed to have completed. This has been * observed on some machines. Just exiting here * might leave the packet sitting around until another * one arrives to trigger a new interrupt, which is * generally undesirable, so set up a timeout. */ callout_reset(&sc->sc_rx_ch, GEM_RXOWN_TICKS, gem_rint_timeout, sc); #endif m = NULL; goto kickit; } if (rxstat & GEM_RD_BAD_CRC) { ifp->if_ierrors++; device_printf(sc->sc_dev, "receive error: CRC error\n"); GEM_INIT_RXDESC(sc, sc->sc_rxptr); m = NULL; goto kickit; } #ifdef GEM_DEBUG if ((ifp->if_flags & IFF_DEBUG) != 0) { printf(" rxsoft %p descriptor %d: ", &sc->sc_rxsoft[sc->sc_rxptr], sc->sc_rxptr); printf("gd_flags: 0x%016llx\t", (long long)GEM_DMA_READ(sc, sc->sc_rxdescs[sc->sc_rxptr].gd_flags)); printf("gd_addr: 0x%016llx\n", (long long)GEM_DMA_READ(sc, sc->sc_rxdescs[sc->sc_rxptr].gd_addr)); } #endif /* * Allocate a new mbuf cluster. If that fails, we are * out of memory, and must drop the packet and recycle * the buffer that's already attached to this descriptor. */ if (gem_add_rxbuf(sc, sc->sc_rxptr) != 0) { ifp->if_ierrors++; GEM_INIT_RXDESC(sc, sc->sc_rxptr); m = NULL; } kickit: /* * Update the RX kick register. This register has to point * to the descriptor after the last valid one (before the * current batch) and for optimum performance should be * incremented in multiples of 4 (the DMA engine fetches/ * updates descriptors in batches of 4). */ sc->sc_rxptr = GEM_NEXTRX(sc->sc_rxptr); if ((sc->sc_rxptr % 4) == 0) { GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); GEM_BANK1_WRITE_4(sc, GEM_RX_KICK, (sc->sc_rxptr + GEM_NRXDESC - 4) & GEM_NRXDESC_MASK); } if (m == NULL) { if (rxstat & GEM_RD_OWN) break; continue; } ifp->if_ipackets++; m->m_data += ETHER_ALIGN; /* first byte offset */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = GEM_RD_BUFLEN(rxstat); if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) gem_rxcksum(m, rxstat); /* Pass it on. */ GEM_UNLOCK(sc); (*ifp->if_input)(ifp, m); GEM_LOCK(sc); } #ifdef GEM_DEBUG CTR3(KTR_GEM, "%s: done sc->sc_rxptr %d, complete %d", __func__, sc->sc_rxptr, GEM_BANK1_READ_4(sc, GEM_RX_COMPLETION)); #endif } static int gem_add_rxbuf(struct gem_softc *sc, int idx) { struct gem_rxsoft *rxs = &sc->sc_rxsoft[idx]; struct mbuf *m; bus_dma_segment_t segs[1]; int error, nsegs; GEM_LOCK_ASSERT(sc, MA_OWNED); m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; #ifdef GEM_DEBUG /* Bzero the packet to check DMA. */ memset(m->m_ext.ext_buf, 0, m->m_ext.ext_size); #endif if (rxs->rxs_mbuf != NULL) { bus_dmamap_sync(sc->sc_rdmatag, rxs->rxs_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_rdmatag, rxs->rxs_dmamap); } error = bus_dmamap_load_mbuf_sg(sc->sc_rdmatag, rxs->rxs_dmamap, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "cannot load RS DMA map %d, error = %d\n", idx, error); m_freem(m); return (error); } /* If nsegs is wrong then the stack is corrupt. */ KASSERT(nsegs == 1, ("%s: too many DMA segments (%d)", __func__, nsegs)); rxs->rxs_mbuf = m; rxs->rxs_paddr = segs[0].ds_addr; bus_dmamap_sync(sc->sc_rdmatag, rxs->rxs_dmamap, BUS_DMASYNC_PREREAD); GEM_INIT_RXDESC(sc, idx); return (0); } static void gem_eint(struct gem_softc *sc, u_int status) { sc->sc_ifp->if_ierrors++; if ((status & GEM_INTR_RX_TAG_ERR) != 0) { gem_reset_rxdma(sc); return; } device_printf(sc->sc_dev, "%s: status 0x%x", __func__, status); if ((status & GEM_INTR_BERR) != 0) { if ((sc->sc_flags & GEM_PCI) != 0) printf(", PCI bus error 0x%x\n", GEM_BANK1_READ_4(sc, GEM_PCI_ERROR_STATUS)); else printf(", SBus error 0x%x\n", GEM_BANK1_READ_4(sc, GEM_SBUS_STATUS)); } } void gem_intr(void *v) { struct gem_softc *sc = v; uint32_t status, status2; GEM_LOCK(sc); status = GEM_BANK1_READ_4(sc, GEM_STATUS); #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: %s: cplt %x, status %x", device_get_name(sc->sc_dev), __func__, (status >> GEM_STATUS_TX_COMPLETION_SHFT), (u_int)status); /* * PCS interrupts must be cleared, otherwise no traffic is passed! */ if ((status & GEM_INTR_PCS) != 0) { status2 = GEM_BANK1_READ_4(sc, GEM_MII_INTERRUP_STATUS) | GEM_BANK1_READ_4(sc, GEM_MII_INTERRUP_STATUS); if ((status2 & GEM_MII_INTERRUP_LINK) != 0) device_printf(sc->sc_dev, "%s: PCS link status changed\n", __func__); } if ((status & GEM_MAC_CONTROL_STATUS) != 0) { status2 = GEM_BANK1_READ_4(sc, GEM_MAC_CONTROL_STATUS); if ((status2 & GEM_MAC_PAUSED) != 0) device_printf(sc->sc_dev, "%s: PAUSE received (PAUSE time %d slots)\n", __func__, GEM_MAC_PAUSE_TIME(status2)); if ((status2 & GEM_MAC_PAUSE) != 0) device_printf(sc->sc_dev, "%s: transited to PAUSE state\n", __func__); if ((status2 & GEM_MAC_RESUME) != 0) device_printf(sc->sc_dev, "%s: transited to non-PAUSE state\n", __func__); } if ((status & GEM_INTR_MIF) != 0) device_printf(sc->sc_dev, "%s: MIF interrupt\n", __func__); #endif if (__predict_false(status & (GEM_INTR_RX_TAG_ERR | GEM_INTR_PERR | GEM_INTR_BERR)) != 0) gem_eint(sc, status); if ((status & (GEM_INTR_RX_DONE | GEM_INTR_RX_NOBUF)) != 0) gem_rint(sc); if ((status & (GEM_INTR_TX_EMPTY | GEM_INTR_TX_INTME)) != 0) gem_tint(sc); if (__predict_false((status & GEM_INTR_TX_MAC) != 0)) { status2 = GEM_BANK1_READ_4(sc, GEM_MAC_TX_STATUS); if ((status2 & ~(GEM_MAC_TX_XMIT_DONE | GEM_MAC_TX_DEFER_EXP | GEM_MAC_TX_PEAK_EXP)) != 0) device_printf(sc->sc_dev, "MAC TX fault, status %x\n", status2); if ((status2 & (GEM_MAC_TX_UNDERRUN | GEM_MAC_TX_PKT_TOO_LONG)) != 0) { sc->sc_ifp->if_oerrors++; gem_init_locked(sc); } } if (__predict_false((status & GEM_INTR_RX_MAC) != 0)) { status2 = GEM_BANK1_READ_4(sc, GEM_MAC_RX_STATUS); /* * At least with GEM_SUN_GEM and some GEM_SUN_ERI * revisions GEM_MAC_RX_OVERFLOW happen often due to a * silicon bug so handle them silently. Moreover, it's * likely that the receiver has hung so we reset it. */ if ((status2 & GEM_MAC_RX_OVERFLOW) != 0) { sc->sc_ifp->if_ierrors++; gem_reset_rxdma(sc); } else if ((status2 & ~(GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT)) != 0) device_printf(sc->sc_dev, "MAC RX fault, status %x\n", status2); } GEM_UNLOCK(sc); } static int gem_watchdog(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; GEM_LOCK_ASSERT(sc, MA_OWNED); #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: GEM_RX_CONFIG %x GEM_MAC_RX_STATUS %x GEM_MAC_RX_CONFIG %x", __func__, GEM_BANK1_READ_4(sc, GEM_RX_CONFIG), GEM_BANK1_READ_4(sc, GEM_MAC_RX_STATUS), GEM_BANK1_READ_4(sc, GEM_MAC_RX_CONFIG)); CTR4(KTR_GEM, "%s: GEM_TX_CONFIG %x GEM_MAC_TX_STATUS %x GEM_MAC_TX_CONFIG %x", __func__, GEM_BANK1_READ_4(sc, GEM_TX_CONFIG), GEM_BANK1_READ_4(sc, GEM_MAC_TX_STATUS), GEM_BANK1_READ_4(sc, GEM_MAC_TX_CONFIG)); #endif if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) return (0); if ((sc->sc_flags & GEM_LINK) != 0) device_printf(sc->sc_dev, "device timeout\n"); else if (bootverbose) device_printf(sc->sc_dev, "device timeout (no link)\n"); ++ifp->if_oerrors; /* Try to get more packets going. */ gem_init_locked(sc); gem_start_locked(ifp); return (EJUSTRETURN); } static void gem_mifinit(struct gem_softc *sc) { /* Configure the MIF in frame mode. */ GEM_BANK1_WRITE_4(sc, GEM_MIF_CONFIG, GEM_BANK1_READ_4(sc, GEM_MIF_CONFIG) & ~GEM_MIF_CONFIG_BB_ENA); } /* * MII interface * * The MII interface supports at least three different operating modes: * * Bitbang mode is implemented using data, clock and output enable registers. * * Frame mode is implemented by loading a complete frame into the frame * register and polling the valid bit for completion. * * Polling mode uses the frame register but completion is indicated by * an interrupt. * */ int gem_mii_readreg(device_t dev, int phy, int reg) { struct gem_softc *sc; int n; uint32_t v; #ifdef GEM_DEBUG_PHY printf("%s: phy %d reg %d\n", __func__, phy, reg); #endif sc = device_get_softc(dev); if (sc->sc_phyad != -1 && phy != sc->sc_phyad) return (0); if ((sc->sc_flags & GEM_SERDES) != 0) { switch (reg) { case MII_BMCR: reg = GEM_MII_CONTROL; break; case MII_BMSR: reg = GEM_MII_STATUS; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); case MII_ANAR: reg = GEM_MII_ANAR; break; case MII_ANLPAR: reg = GEM_MII_ANLPAR; break; case MII_EXTSR: return (EXTSR_1000XFDX | EXTSR_1000XHDX); default: device_printf(sc->sc_dev, "%s: unhandled register %d\n", __func__, reg); return (0); } return (GEM_BANK1_READ_4(sc, reg)); } /* Construct the frame command. */ v = GEM_MIF_FRAME_READ | (phy << GEM_MIF_PHY_SHIFT) | (reg << GEM_MIF_REG_SHIFT); GEM_BANK1_WRITE_4(sc, GEM_MIF_FRAME, v); GEM_BANK1_BARRIER(sc, GEM_MIF_FRAME, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); for (n = 0; n < 100; n++) { DELAY(1); v = GEM_BANK1_READ_4(sc, GEM_MIF_FRAME); if (v & GEM_MIF_FRAME_TA0) return (v & GEM_MIF_FRAME_DATA); } device_printf(sc->sc_dev, "%s: timed out\n", __func__); return (0); } int gem_mii_writereg(device_t dev, int phy, int reg, int val) { struct gem_softc *sc; int n; uint32_t v; #ifdef GEM_DEBUG_PHY printf("%s: phy %d reg %d val %x\n", phy, reg, val, __func__); #endif sc = device_get_softc(dev); if (sc->sc_phyad != -1 && phy != sc->sc_phyad) return (0); if ((sc->sc_flags & GEM_SERDES) != 0) { switch (reg) { case MII_BMSR: reg = GEM_MII_STATUS; break; case MII_BMCR: reg = GEM_MII_CONTROL; if ((val & GEM_MII_CONTROL_RESET) == 0) break; GEM_BANK1_WRITE_4(sc, GEM_MII_CONTROL, val); GEM_BANK1_BARRIER(sc, GEM_MII_CONTROL, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_MII_CONTROL, GEM_MII_CONTROL_RESET, 0)) device_printf(sc->sc_dev, "cannot reset PCS\n"); /* FALLTHROUGH */ case MII_ANAR: GEM_BANK1_WRITE_4(sc, GEM_MII_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_MII_CONFIG, 4, BUS_SPACE_BARRIER_WRITE); GEM_BANK1_WRITE_4(sc, GEM_MII_ANAR, val); GEM_BANK1_WRITE_4(sc, GEM_MII_SLINK_CONTROL, GEM_MII_SLINK_LOOPBACK | GEM_MII_SLINK_EN_SYNC_D); GEM_BANK1_WRITE_4(sc, GEM_MII_CONFIG, GEM_MII_CONFIG_ENABLE); return (0); case MII_ANLPAR: reg = GEM_MII_ANLPAR; break; default: device_printf(sc->sc_dev, "%s: unhandled register %d\n", __func__, reg); return (0); } GEM_BANK1_WRITE_4(sc, reg, val); return (0); } /* Construct the frame command. */ v = GEM_MIF_FRAME_WRITE | (phy << GEM_MIF_PHY_SHIFT) | (reg << GEM_MIF_REG_SHIFT) | (val & GEM_MIF_FRAME_DATA); GEM_BANK1_WRITE_4(sc, GEM_MIF_FRAME, v); GEM_BANK1_BARRIER(sc, GEM_MIF_FRAME, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); for (n = 0; n < 100; n++) { DELAY(1); v = GEM_BANK1_READ_4(sc, GEM_MIF_FRAME); if (v & GEM_MIF_FRAME_TA0) return (1); } device_printf(sc->sc_dev, "%s: timed out\n", __func__); return (0); } void gem_mii_statchg(device_t dev) { struct gem_softc *sc; int gigabit; uint32_t rxcfg, txcfg, v; sc = device_get_softc(dev); GEM_LOCK_ASSERT(sc, MA_OWNED); #ifdef GEM_DEBUG if ((sc->sc_ifp->if_flags & IFF_DEBUG) != 0) device_printf(sc->sc_dev, "%s: status change: PHY = %d\n", __func__, sc->sc_phyad); #endif if ((sc->sc_mii->mii_media_status & IFM_ACTIVE) != 0 && IFM_SUBTYPE(sc->sc_mii->mii_media_active) != IFM_NONE) sc->sc_flags |= GEM_LINK; else sc->sc_flags &= ~GEM_LINK; switch (IFM_SUBTYPE(sc->sc_mii->mii_media_active)) { case IFM_1000_SX: case IFM_1000_LX: case IFM_1000_CX: case IFM_1000_T: gigabit = 1; break; default: gigabit = 0; } /* * The configuration done here corresponds to the steps F) and * G) and as far as enabling of RX and TX MAC goes also step H) * of the initialization sequence outlined in section 3.2.1 of * the GEM Gigabit Ethernet ASIC Specification. */ rxcfg = GEM_BANK1_READ_4(sc, GEM_MAC_RX_CONFIG); rxcfg &= ~(GEM_MAC_RX_CARR_EXTEND | GEM_MAC_RX_ENABLE); txcfg = GEM_MAC_TX_ENA_IPG0 | GEM_MAC_TX_NGU | GEM_MAC_TX_NGU_LIMIT; if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) != 0) txcfg |= GEM_MAC_TX_IGN_CARRIER | GEM_MAC_TX_IGN_COLLIS; else if (gigabit != 0) { rxcfg |= GEM_MAC_RX_CARR_EXTEND; txcfg |= GEM_MAC_TX_CARR_EXTEND; } GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_MAC_TX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_MAC_TX_CONFIG, GEM_MAC_TX_ENABLE, 0)) device_printf(sc->sc_dev, "cannot disable TX MAC\n"); GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, txcfg); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, 0); GEM_BANK1_BARRIER(sc, GEM_MAC_RX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_MAC_RX_CONFIG, GEM_MAC_RX_ENABLE, 0)) device_printf(sc->sc_dev, "cannot disable RX MAC\n"); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, rxcfg); v = GEM_BANK1_READ_4(sc, GEM_MAC_CONTROL_CONFIG) & ~(GEM_MAC_CC_RX_PAUSE | GEM_MAC_CC_TX_PAUSE); #ifdef notyet if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) v |= GEM_MAC_CC_RX_PAUSE; if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) v |= GEM_MAC_CC_TX_PAUSE; #endif GEM_BANK1_WRITE_4(sc, GEM_MAC_CONTROL_CONFIG, v); if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0 && gigabit != 0) GEM_BANK1_WRITE_4(sc, GEM_MAC_SLOT_TIME, GEM_MAC_SLOT_TIME_CARR_EXTEND); else GEM_BANK1_WRITE_4(sc, GEM_MAC_SLOT_TIME, GEM_MAC_SLOT_TIME_NORMAL); /* XIF Configuration */ v = GEM_MAC_XIF_LINK_LED; v |= GEM_MAC_XIF_TX_MII_ENA; if ((sc->sc_flags & GEM_SERDES) == 0) { if ((GEM_BANK1_READ_4(sc, GEM_MIF_CONFIG) & GEM_MIF_CONFIG_PHY_SEL) != 0) { /* External MII needs echo disable if half duplex. */ if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) v |= GEM_MAC_XIF_ECHO_DISABL; } else /* * Internal MII needs buffer enable. * XXX buffer enable makes only sense for an * external PHY. */ v |= GEM_MAC_XIF_MII_BUF_ENA; } if (gigabit != 0) v |= GEM_MAC_XIF_GMII_MODE; if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) != 0) v |= GEM_MAC_XIF_FDPLX_LED; GEM_BANK1_WRITE_4(sc, GEM_MAC_XIF_CONFIG, v); if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && (sc->sc_flags & GEM_LINK) != 0) { GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, txcfg | GEM_MAC_TX_ENABLE); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, rxcfg | GEM_MAC_RX_ENABLE); } } int gem_mediachange(struct ifnet *ifp) { struct gem_softc *sc = ifp->if_softc; int error; /* XXX add support for serial media. */ GEM_LOCK(sc); error = mii_mediachg(sc->sc_mii); GEM_UNLOCK(sc); return (error); } void gem_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { struct gem_softc *sc = ifp->if_softc; GEM_LOCK(sc); if ((ifp->if_flags & IFF_UP) == 0) { GEM_UNLOCK(sc); return; } mii_pollstat(sc->sc_mii); ifmr->ifm_active = sc->sc_mii->mii_media_active; ifmr->ifm_status = sc->sc_mii->mii_media_status; GEM_UNLOCK(sc); } static int gem_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct gem_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error; error = 0; switch (cmd) { case SIOCSIFFLAGS: GEM_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && ((ifp->if_flags ^ sc->sc_ifflags) & (IFF_ALLMULTI | IFF_PROMISC)) != 0) gem_setladrf(sc); else gem_init_locked(sc); } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) gem_stop(ifp, 0); if ((ifp->if_flags & IFF_LINK0) != 0) sc->sc_csum_features |= CSUM_UDP; else sc->sc_csum_features &= ~CSUM_UDP; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) ifp->if_hwassist = sc->sc_csum_features; sc->sc_ifflags = ifp->if_flags; GEM_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: GEM_LOCK(sc); gem_setladrf(sc); GEM_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd); break; case SIOCSIFCAP: GEM_LOCK(sc); ifp->if_capenable = ifr->ifr_reqcap; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) ifp->if_hwassist = sc->sc_csum_features; else ifp->if_hwassist = 0; GEM_UNLOCK(sc); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void gem_setladrf(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ifmultiaddr *inm; int i; uint32_t hash[16]; uint32_t crc, v; GEM_LOCK_ASSERT(sc, MA_OWNED); /* Get the current RX configuration. */ v = GEM_BANK1_READ_4(sc, GEM_MAC_RX_CONFIG); /* * Turn off promiscuous mode, promiscuous group mode (all multicast), * and hash filter. Depending on the case, the right bit will be * enabled. */ v &= ~(GEM_MAC_RX_PROMISCUOUS | GEM_MAC_RX_HASH_FILTER | GEM_MAC_RX_PROMISC_GRP); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, v); GEM_BANK1_BARRIER(sc, GEM_MAC_RX_CONFIG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); if (!GEM_BANK1_BITWAIT(sc, GEM_MAC_RX_CONFIG, GEM_MAC_RX_HASH_FILTER, 0)) device_printf(sc->sc_dev, "cannot disable RX hash filter\n"); if ((ifp->if_flags & IFF_PROMISC) != 0) { v |= GEM_MAC_RX_PROMISCUOUS; goto chipit; } if ((ifp->if_flags & IFF_ALLMULTI) != 0) { v |= GEM_MAC_RX_PROMISC_GRP; goto chipit; } /* * Set up multicast address filter by passing all multicast * addresses through a crc generator, and then using the high * order 8 bits as an index into the 256 bit logical address * filter. The high order 4 bits selects the word, while the * other 4 bits select the bit within the word (where bit 0 * is the MSB). */ /* Clear the hash table. */ memset(hash, 0, sizeof(hash)); if_maddr_rlock(ifp); TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) { if (inm->ifma_addr->sa_family != AF_LINK) continue; crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) inm->ifma_addr), ETHER_ADDR_LEN); /* We just want the 8 most significant bits. */ crc >>= 24; /* Set the corresponding bit in the filter. */ hash[crc >> 4] |= 1 << (15 - (crc & 15)); } if_maddr_runlock(ifp); v |= GEM_MAC_RX_HASH_FILTER; /* Now load the hash table into the chip (if we are using it). */ for (i = 0; i < 16; i++) GEM_BANK1_WRITE_4(sc, GEM_MAC_HASH0 + i * (GEM_MAC_HASH1 - GEM_MAC_HASH0), hash[i]); chipit: GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, v); } Index: projects/ppc64/sys/dev/isp/isp_freebsd.c =================================================================== --- projects/ppc64/sys/dev/isp/isp_freebsd.c (revision 204271) +++ projects/ppc64/sys/dev/isp/isp_freebsd.c (revision 204272) @@ -1,5579 +1,5584 @@ /*- * Copyright (c) 1997-2009 by Matthew Jacob * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. 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 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. */ /* * Platform (FreeBSD) dependent common attachment code for Qlogic adapters. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 800002 #define THREAD_CREATE kthread_create #else #define THREAD_CREATE kproc_create #endif MODULE_VERSION(isp, 1); MODULE_DEPEND(isp, cam, 1, 1, 1); int isp_announced = 0; int isp_fabric_hysteresis = 3; int isp_loop_down_limit = 60; /* default loop down limit */ int isp_change_is_bad = 0; /* "changed" devices are bad */ int isp_quickboot_time = 7; /* don't wait more than N secs for loop up */ int isp_gone_device_time = 30; /* grace time before reporting device lost */ int isp_autoconfig = 1; /* automatically attach/detach devices */ static const char *roles[4] = { "(none)", "Target", "Initiator", "Target/Initiator" }; static const char prom3[] = "Chan %d PortID 0x%06x Departed from Target %u because of %s"; static const char rqo[] = "%s: Request Queue Overflow\n"; static void isp_freeze_loopdown(ispsoftc_t *, int, char *); static d_ioctl_t ispioctl; static void isp_intr_enable(void *); static void isp_cam_async(void *, uint32_t, struct cam_path *, void *); static void isp_poll(struct cam_sim *); static timeout_t isp_watchdog; static timeout_t isp_ldt; static void isp_kthread(void *); static void isp_action(struct cam_sim *, union ccb *); #ifdef ISP_INTERNAL_TARGET static void isp_target_thread_pi(void *); static void isp_target_thread_fc(void *); #endif static void isp_timer(void *); static struct cdevsw isp_cdevsw = { .d_version = D_VERSION, .d_ioctl = ispioctl, .d_name = "isp", }; static int isp_attach_chan(ispsoftc_t *isp, struct cam_devq *devq, int chan) { struct ccb_setasync csa; struct cam_sim *sim; struct cam_path *path; /* * Construct our SIM entry. */ sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, device_get_unit(isp->isp_dev), &isp->isp_osinfo.lock, isp->isp_maxcmds, isp->isp_maxcmds, devq); if (sim == NULL) { return (ENOMEM); } ISP_LOCK(isp); if (xpt_bus_register(sim, isp->isp_dev, chan) != CAM_SUCCESS) { ISP_UNLOCK(isp); cam_sim_free(sim, FALSE); return (EIO); } ISP_UNLOCK(isp); if (xpt_create_path(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { ISP_LOCK(isp); xpt_bus_deregister(cam_sim_path(sim)); ISP_UNLOCK(isp); cam_sim_free(sim, FALSE); return (ENXIO); } xpt_setup_ccb(&csa.ccb_h, path, 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = isp_cam_async; csa.callback_arg = sim; xpt_action((union ccb *)&csa); if (IS_SCSI(isp)) { struct isp_spi *spi = ISP_SPI_PC(isp, chan); spi->sim = sim; spi->path = path; #ifdef ISP_INTERNAL_TARGET ISP_SET_PC(isp, chan, proc_active, 1); if (THREAD_CREATE(isp_target_thread_pi, spi, &spi->target_proc, 0, 0, "%s: isp_test_tgt%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) { ISP_SET_PC(isp, chan, proc_active, 0); isp_prt(isp, ISP_LOGERR, "cannot create test target thread"); } #endif } else { fcparam *fcp = FCPARAM(isp, chan); struct isp_fc *fc = ISP_FC_PC(isp, chan); ISP_LOCK(isp); fc->sim = sim; fc->path = path; fc->isp = isp; fc->ready = 1; callout_init_mtx(&fc->ldt, &isp->isp_osinfo.lock, 0); callout_init_mtx(&fc->gdt, &isp->isp_osinfo.lock, 0); /* * We start by being "loop down" if we have an initiator role */ if (fcp->role & ISP_ROLE_INITIATOR) { isp_freeze_loopdown(isp, chan, "isp_attach"); callout_reset(&fc->ldt, isp_quickboot_time * hz, isp_ldt, fc); isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Starting Initial Loop Down Timer @ %lu", (unsigned long) time_uptime); } ISP_UNLOCK(isp); if (THREAD_CREATE(isp_kthread, fc, &fc->kproc, 0, 0, "%s: fc_thrd%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) { xpt_free_path(fc->path); ISP_LOCK(isp); if (callout_active(&fc->ldt)) { callout_stop(&fc->ldt); } xpt_bus_deregister(cam_sim_path(fc->sim)); ISP_UNLOCK(isp); cam_sim_free(fc->sim, FALSE); return (ENOMEM); } #ifdef ISP_INTERNAL_TARGET ISP_SET_PC(isp, chan, proc_active, 1); if (THREAD_CREATE(isp_target_thread_fc, fc, &fc->target_proc, 0, 0, "%s: isp_test_tgt%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) { ISP_SET_PC(isp, chan, proc_active, 0); isp_prt(isp, ISP_LOGERR, "cannot create test target thread"); } #endif } return (0); } int isp_attach(ispsoftc_t *isp) { const char *nu = device_get_nameunit(isp->isp_osinfo.dev); int du = device_get_unit(isp->isp_dev); int chan; isp->isp_osinfo.ehook.ich_func = isp_intr_enable; isp->isp_osinfo.ehook.ich_arg = isp; if (config_intrhook_establish(&isp->isp_osinfo.ehook) != 0) { isp_prt(isp, ISP_LOGERR, "could not establish interrupt enable hook"); return (-EIO); } isp->isp_osinfo.ehook_active = 1; /* * Create the device queue for our SIM(s). */ isp->isp_osinfo.devq = cam_simq_alloc(isp->isp_maxcmds); if (isp->isp_osinfo.devq == NULL) { config_intrhook_disestablish(&isp->isp_osinfo.ehook); return (EIO); } for (chan = 0; chan < isp->isp_nchan; chan++) { if (isp_attach_chan(isp, isp->isp_osinfo.devq, chan)) { goto unwind; } } callout_init_mtx(&isp->isp_osinfo.tmo, &isp->isp_osinfo.lock, 0); callout_reset(&isp->isp_osinfo.tmo, hz, isp_timer, isp); isp->isp_osinfo.timer_active = 1; isp->isp_osinfo.cdev = make_dev(&isp_cdevsw, du, UID_ROOT, GID_OPERATOR, 0600, "%s", nu); if (isp->isp_osinfo.cdev) { isp->isp_osinfo.cdev->si_drv1 = isp; } return (0); unwind: while (--chan >= 0) { struct cam_sim *sim; struct cam_path *path; if (IS_FC(isp)) { sim = ISP_FC_PC(isp, chan)->sim; path = ISP_FC_PC(isp, chan)->path; } else { sim = ISP_SPI_PC(isp, chan)->sim; path = ISP_SPI_PC(isp, chan)->path; } xpt_free_path(path); ISP_LOCK(isp); xpt_bus_deregister(cam_sim_path(sim)); ISP_UNLOCK(isp); cam_sim_free(sim, FALSE); } if (isp->isp_osinfo.ehook_active) { config_intrhook_disestablish(&isp->isp_osinfo.ehook); isp->isp_osinfo.ehook_active = 0; } if (isp->isp_osinfo.cdev) { destroy_dev(isp->isp_osinfo.cdev); isp->isp_osinfo.cdev = NULL; } cam_simq_free(isp->isp_osinfo.devq); isp->isp_osinfo.devq = NULL; return (-1); } void isp_detach(ispsoftc_t *isp) { int chan; ISP_LOCK(isp); if (isp->isp_osinfo.timer_active) { callout_stop(&isp->isp_osinfo.tmo); isp->isp_osinfo.timer_active = 0; } ISP_UNLOCK(isp); for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) { struct cam_sim *sim; struct cam_path *path; if (IS_FC(isp)) { sim = ISP_FC_PC(isp, chan)->sim; path = ISP_FC_PC(isp, chan)->path; } else { sim = ISP_SPI_PC(isp, chan)->sim; path = ISP_SPI_PC(isp, chan)->path; } xpt_free_path(path); ISP_LOCK(isp); xpt_bus_deregister(cam_sim_path(sim)); ISP_UNLOCK(isp); cam_sim_free(sim, FALSE); } if (isp->isp_osinfo.cdev) { destroy_dev(isp->isp_osinfo.cdev); isp->isp_osinfo.cdev = NULL; } if (isp->isp_osinfo.ehook_active) { config_intrhook_disestablish(&isp->isp_osinfo.ehook); isp->isp_osinfo.ehook_active = 0; } if (isp->isp_osinfo.devq == NULL) { cam_simq_free(isp->isp_osinfo.devq); isp->isp_osinfo.devq = NULL; } } static void isp_freeze_loopdown(ispsoftc_t *isp, int chan, char *msg) { if (IS_FC(isp)) { struct isp_fc *fc = ISP_FC_PC(isp, chan); if (fc->simqfrozen == 0) { isp_prt(isp, ISP_LOGDEBUG0, "%s: freeze simq (loopdown) chan %d", msg, chan); fc->simqfrozen = SIMQFRZ_LOOPDOWN; xpt_freeze_simq(fc->sim, 1); } else { isp_prt(isp, ISP_LOGDEBUG0, "%s: mark frozen (loopdown) chan %d", msg, chan); fc->simqfrozen |= SIMQFRZ_LOOPDOWN; } } } static int ispioctl(struct cdev *dev, u_long c, caddr_t addr, int flags, struct thread *td) { ispsoftc_t *isp; int nr, chan, retval = ENOTTY; isp = dev->si_drv1; switch (c) { case ISP_SDBLEV: { int olddblev = isp->isp_dblev; isp->isp_dblev = *(int *)addr; *(int *)addr = olddblev; retval = 0; break; } case ISP_GETROLE: chan = *(int *)addr; if (chan < 0 || chan >= isp->isp_nchan) { retval = -ENXIO; break; } if (IS_FC(isp)) { *(int *)addr = FCPARAM(isp, chan)->role; } else { *(int *)addr = SDPARAM(isp, chan)->role; } retval = 0; break; case ISP_SETROLE: nr = *(int *)addr; chan = nr >> 8; if (chan < 0 || chan >= isp->isp_nchan) { retval = -ENXIO; break; } nr &= 0xff; if (nr & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) { retval = EINVAL; break; } if (IS_FC(isp)) { /* * We don't really support dual role at present on FC cards. * * We should, but a bunch of things are currently broken, * so don't allow it. */ if (nr == ISP_ROLE_BOTH) { isp_prt(isp, ISP_LOGERR, "cannot support dual role at present"); retval = EINVAL; break; } *(int *)addr = FCPARAM(isp, chan)->role; #ifdef ISP_INTERNAL_TARGET ISP_LOCK(isp); retval = isp_fc_change_role(isp, chan, nr); ISP_UNLOCK(isp); #else FCPARAM(isp, chan)->role = nr; #endif } else { *(int *)addr = SDPARAM(isp, chan)->role; SDPARAM(isp, chan)->role = nr; } retval = 0; break; case ISP_RESETHBA: ISP_LOCK(isp); #ifdef ISP_TARGET_MODE isp_del_all_wwn_entries(isp, ISP_NOCHAN); #endif isp_reinit(isp, 0); ISP_UNLOCK(isp); retval = 0; break; case ISP_RESCAN: if (IS_FC(isp)) { chan = *(int *)addr; if (chan < 0 || chan >= isp->isp_nchan) { retval = -ENXIO; break; } ISP_LOCK(isp); if (isp_fc_runstate(isp, chan, 5 * 1000000)) { retval = EIO; } else { retval = 0; } ISP_UNLOCK(isp); } break; case ISP_FC_LIP: if (IS_FC(isp)) { chan = *(int *)addr; if (chan < 0 || chan >= isp->isp_nchan) { retval = -ENXIO; break; } ISP_LOCK(isp); if (isp_control(isp, ISPCTL_SEND_LIP, chan)) { retval = EIO; } else { retval = 0; } ISP_UNLOCK(isp); } break; case ISP_FC_GETDINFO: { struct isp_fc_device *ifc = (struct isp_fc_device *) addr; fcportdb_t *lp; if (IS_SCSI(isp)) { break; } if (ifc->loopid >= MAX_FC_TARG) { retval = EINVAL; break; } lp = &FCPARAM(isp, ifc->chan)->portdb[ifc->loopid]; if (lp->state == FC_PORTDB_STATE_VALID || lp->target_mode) { ifc->role = lp->roles; ifc->loopid = lp->handle; ifc->portid = lp->portid; ifc->node_wwn = lp->node_wwn; ifc->port_wwn = lp->port_wwn; retval = 0; } else { retval = ENODEV; } break; } case ISP_GET_STATS: { isp_stats_t *sp = (isp_stats_t *) addr; ISP_MEMZERO(sp, sizeof (*sp)); sp->isp_stat_version = ISP_STATS_VERSION; sp->isp_type = isp->isp_type; sp->isp_revision = isp->isp_revision; ISP_LOCK(isp); sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt; sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus; sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc; sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync; sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt; sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt; sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater; sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater; ISP_UNLOCK(isp); retval = 0; break; } case ISP_CLR_STATS: ISP_LOCK(isp); isp->isp_intcnt = 0; isp->isp_intbogus = 0; isp->isp_intmboxc = 0; isp->isp_intoasync = 0; isp->isp_rsltccmplt = 0; isp->isp_fphccmplt = 0; isp->isp_rscchiwater = 0; isp->isp_fpcchiwater = 0; ISP_UNLOCK(isp); retval = 0; break; case ISP_FC_GETHINFO: { struct isp_hba_device *hba = (struct isp_hba_device *) addr; int chan = hba->fc_channel; if (chan < 0 || chan >= isp->isp_nchan) { retval = ENXIO; break; } hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev); hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev); hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev); hba->fc_nchannels = isp->isp_nchan; if (IS_FC(isp)) { hba->fc_nports = MAX_FC_TARG; hba->fc_speed = FCPARAM(isp, hba->fc_channel)->isp_gbspeed; hba->fc_topology = FCPARAM(isp, chan)->isp_topo + 1; hba->fc_loopid = FCPARAM(isp, chan)->isp_loopid; hba->nvram_node_wwn = FCPARAM(isp, chan)->isp_wwnn_nvram; hba->nvram_port_wwn = FCPARAM(isp, chan)->isp_wwpn_nvram; hba->active_node_wwn = FCPARAM(isp, chan)->isp_wwnn; hba->active_port_wwn = FCPARAM(isp, chan)->isp_wwpn; } else { hba->fc_nports = MAX_TARGETS; hba->fc_speed = 0; hba->fc_topology = 0; hba->nvram_node_wwn = 0ull; hba->nvram_port_wwn = 0ull; hba->active_node_wwn = 0ull; hba->active_port_wwn = 0ull; } retval = 0; break; } case ISP_TSK_MGMT: { int needmarker; struct isp_fc_tsk_mgmt *fct = (struct isp_fc_tsk_mgmt *) addr; uint16_t loopid; mbreg_t mbs; if (IS_SCSI(isp)) { break; } chan = fct->chan; if (chan < 0 || chan >= isp->isp_nchan) { retval = -ENXIO; break; } needmarker = retval = 0; loopid = fct->loopid; ISP_LOCK(isp); if (IS_24XX(isp)) { uint8_t local[QENTRY_LEN]; isp24xx_tmf_t *tmf; isp24xx_statusreq_t *sp; fcparam *fcp = FCPARAM(isp, chan); fcportdb_t *lp; int i; for (i = 0; i < MAX_FC_TARG; i++) { lp = &fcp->portdb[i]; if (lp->handle == loopid) { break; } } if (i == MAX_FC_TARG) { retval = ENXIO; ISP_UNLOCK(isp); break; } /* XXX VALIDATE LP XXX */ tmf = (isp24xx_tmf_t *) local; ISP_MEMZERO(tmf, QENTRY_LEN); tmf->tmf_header.rqs_entry_type = RQSTYPE_TSK_MGMT; tmf->tmf_header.rqs_entry_count = 1; tmf->tmf_nphdl = lp->handle; tmf->tmf_delay = 2; tmf->tmf_timeout = 2; tmf->tmf_tidlo = lp->portid; tmf->tmf_tidhi = lp->portid >> 16; tmf->tmf_vpidx = ISP_GET_VPIDX(isp, chan); tmf->tmf_lun[1] = fct->lun & 0xff; if (fct->lun >= 256) { tmf->tmf_lun[0] = 0x40 | (fct->lun >> 8); } switch (fct->action) { case IPT_CLEAR_ACA: tmf->tmf_flags = ISP24XX_TMF_CLEAR_ACA; break; case IPT_TARGET_RESET: tmf->tmf_flags = ISP24XX_TMF_TARGET_RESET; needmarker = 1; break; case IPT_LUN_RESET: tmf->tmf_flags = ISP24XX_TMF_LUN_RESET; needmarker = 1; break; case IPT_CLEAR_TASK_SET: tmf->tmf_flags = ISP24XX_TMF_CLEAR_TASK_SET; needmarker = 1; break; case IPT_ABORT_TASK_SET: tmf->tmf_flags = ISP24XX_TMF_ABORT_TASK_SET; needmarker = 1; break; default: retval = EINVAL; break; } if (retval) { ISP_UNLOCK(isp); break; } MBSINIT(&mbs, MBOX_EXEC_COMMAND_IOCB_A64, MBLOGALL, 5000000); mbs.param[1] = QENTRY_LEN; mbs.param[2] = DMA_WD1(fcp->isp_scdma); mbs.param[3] = DMA_WD0(fcp->isp_scdma); mbs.param[6] = DMA_WD3(fcp->isp_scdma); mbs.param[7] = DMA_WD2(fcp->isp_scdma); if (FC_SCRATCH_ACQUIRE(isp, chan)) { ISP_UNLOCK(isp); retval = ENOMEM; break; } isp_put_24xx_tmf(isp, tmf, fcp->isp_scratch); MEMORYBARRIER(isp, SYNC_SFORDEV, 0, QENTRY_LEN); sp = (isp24xx_statusreq_t *) local; sp->req_completion_status = 1; retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); MEMORYBARRIER(isp, SYNC_SFORCPU, QENTRY_LEN, QENTRY_LEN); isp_get_24xx_response(isp, &((isp24xx_statusreq_t *)fcp->isp_scratch)[1], sp); FC_SCRATCH_RELEASE(isp, chan); if (retval || sp->req_completion_status != 0) { FC_SCRATCH_RELEASE(isp, chan); retval = EIO; } if (retval == 0) { if (needmarker) { fcp->sendmarker = 1; } } } else { MBSINIT(&mbs, 0, MBLOGALL, 0); if (ISP_CAP_2KLOGIN(isp) == 0) { loopid <<= 8; } switch (fct->action) { case IPT_CLEAR_ACA: mbs.param[0] = MBOX_CLEAR_ACA; mbs.param[1] = loopid; mbs.param[2] = fct->lun; break; case IPT_TARGET_RESET: mbs.param[0] = MBOX_TARGET_RESET; mbs.param[1] = loopid; needmarker = 1; break; case IPT_LUN_RESET: mbs.param[0] = MBOX_LUN_RESET; mbs.param[1] = loopid; mbs.param[2] = fct->lun; needmarker = 1; break; case IPT_CLEAR_TASK_SET: mbs.param[0] = MBOX_CLEAR_TASK_SET; mbs.param[1] = loopid; mbs.param[2] = fct->lun; needmarker = 1; break; case IPT_ABORT_TASK_SET: mbs.param[0] = MBOX_ABORT_TASK_SET; mbs.param[1] = loopid; mbs.param[2] = fct->lun; needmarker = 1; break; default: retval = EINVAL; break; } if (retval == 0) { if (needmarker) { FCPARAM(isp, chan)->sendmarker = 1; } retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); if (retval) { retval = EIO; } } } ISP_UNLOCK(isp); break; } default: break; } return (retval); } static void isp_intr_enable(void *arg) { int chan; ispsoftc_t *isp = arg; ISP_LOCK(isp); for (chan = 0; chan < isp->isp_nchan; chan++) { if (IS_FC(isp)) { if (FCPARAM(isp, chan)->role != ISP_ROLE_NONE) { ISP_ENABLE_INTS(isp); break; } } else { if (SDPARAM(isp, chan)->role != ISP_ROLE_NONE) { ISP_ENABLE_INTS(isp); break; } } } ISP_UNLOCK(isp); /* Release our hook so that the boot can continue. */ config_intrhook_disestablish(&isp->isp_osinfo.ehook); } /* * Local Inlines */ static ISP_INLINE int isp_get_pcmd(ispsoftc_t *, union ccb *); static ISP_INLINE void isp_free_pcmd(ispsoftc_t *, union ccb *); static ISP_INLINE int isp_get_pcmd(ispsoftc_t *isp, union ccb *ccb) { ISP_PCMD(ccb) = isp->isp_osinfo.pcmd_free; if (ISP_PCMD(ccb) == NULL) { return (-1); } isp->isp_osinfo.pcmd_free = ((struct isp_pcmd *)ISP_PCMD(ccb))->next; return (0); } static ISP_INLINE void isp_free_pcmd(ispsoftc_t *isp, union ccb *ccb) { ((struct isp_pcmd *)ISP_PCMD(ccb))->next = isp->isp_osinfo.pcmd_free; isp->isp_osinfo.pcmd_free = ISP_PCMD(ccb); ISP_PCMD(ccb) = NULL; } /* * Put the target mode functions here, because some are inlines */ #ifdef ISP_TARGET_MODE static ISP_INLINE int is_lun_enabled(ispsoftc_t *, int, lun_id_t); static ISP_INLINE tstate_t *get_lun_statep(ispsoftc_t *, int, lun_id_t); static ISP_INLINE tstate_t *get_lun_statep_from_tag(ispsoftc_t *, int, uint32_t); static ISP_INLINE void rls_lun_statep(ispsoftc_t *, tstate_t *); static ISP_INLINE inot_private_data_t *get_ntp_from_tagdata(ispsoftc_t *, uint32_t, uint32_t, tstate_t **); static ISP_INLINE atio_private_data_t *isp_get_atpd(ispsoftc_t *, tstate_t *, uint32_t); static ISP_INLINE void isp_put_atpd(ispsoftc_t *, tstate_t *, atio_private_data_t *); static ISP_INLINE inot_private_data_t *isp_get_ntpd(ispsoftc_t *, tstate_t *); static ISP_INLINE inot_private_data_t *isp_find_ntpd(ispsoftc_t *, tstate_t *, uint32_t, uint32_t); static ISP_INLINE void isp_put_ntpd(ispsoftc_t *, tstate_t *, inot_private_data_t *); static cam_status create_lun_state(ispsoftc_t *, int, struct cam_path *, tstate_t **); static void destroy_lun_state(ispsoftc_t *, tstate_t *); static void isp_enable_lun(ispsoftc_t *, union ccb *); static void isp_enable_deferred_luns(ispsoftc_t *, int); static cam_status isp_enable_deferred(ispsoftc_t *, int, lun_id_t); static void isp_disable_lun(ispsoftc_t *, union ccb *); static int isp_enable_target_mode(ispsoftc_t *, int); static void isp_ledone(ispsoftc_t *, lun_entry_t *); static timeout_t isp_refire_putback_atio; static void isp_complete_ctio(union ccb *); static void isp_target_putback_atio(union ccb *); static void isp_target_start_ctio(ispsoftc_t *, union ccb *); static void isp_handle_platform_atio(ispsoftc_t *, at_entry_t *); static void isp_handle_platform_atio2(ispsoftc_t *, at2_entry_t *); static void isp_handle_platform_atio7(ispsoftc_t *, at7_entry_t *); static void isp_handle_platform_ctio(ispsoftc_t *, void *); static void isp_handle_platform_notify_scsi(ispsoftc_t *, in_entry_t *); static void isp_handle_platform_notify_fc(ispsoftc_t *, in_fcentry_t *); static void isp_handle_platform_notify_24xx(ispsoftc_t *, in_fcentry_24xx_t *); static int isp_handle_platform_target_notify_ack(ispsoftc_t *, isp_notify_t *); static void isp_handle_platform_target_tmf(ispsoftc_t *, isp_notify_t *); static void isp_target_mark_aborted(ispsoftc_t *, union ccb *); static void isp_target_mark_aborted_early(ispsoftc_t *, tstate_t *, uint32_t); static ISP_INLINE int is_lun_enabled(ispsoftc_t *isp, int bus, lun_id_t lun) { tstate_t *tptr; struct tslist *lhp; ISP_GET_PC_ADDR(isp, bus, lun_hash[LUN_HASH_FUNC(lun)], lhp); SLIST_FOREACH(tptr, lhp, next) { if (xpt_path_lun_id(tptr->owner) == lun) { return (1); } } return (0); } static void dump_tstates(ispsoftc_t *isp, int bus) { int i, j; struct tslist *lhp; tstate_t *tptr = NULL; if (bus >= isp->isp_nchan) { return; } for (i = 0; i < LUN_HASH_SIZE; i++) { ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); j = 0; SLIST_FOREACH(tptr, lhp, next) { xpt_print(tptr->owner, "[%d, %d] atio_cnt=%d inot_cnt=%d\n", i, j, tptr->atio_count, tptr->inot_count); j++; } } } static ISP_INLINE tstate_t * get_lun_statep(ispsoftc_t *isp, int bus, lun_id_t lun) { tstate_t *tptr = NULL; struct tslist *lhp; int i; if (bus < isp->isp_nchan) { for (i = 0; i < LUN_HASH_SIZE; i++) { ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); SLIST_FOREACH(tptr, lhp, next) { if (xpt_path_lun_id(tptr->owner) == lun) { tptr->hold++; return (tptr); } } } } return (NULL); } static ISP_INLINE tstate_t * get_lun_statep_from_tag(ispsoftc_t *isp, int bus, uint32_t tagval) { tstate_t *tptr = NULL; atio_private_data_t *atp; struct tslist *lhp; int i; if (bus < isp->isp_nchan && tagval != 0) { for (i = 0; i < LUN_HASH_SIZE; i++) { ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); SLIST_FOREACH(tptr, lhp, next) { atp = isp_get_atpd(isp, tptr, tagval); if (atp && atp->tag == tagval) { tptr->hold++; return (tptr); } } } } return (NULL); } static ISP_INLINE inot_private_data_t * get_ntp_from_tagdata(ispsoftc_t *isp, uint32_t tag_id, uint32_t seq_id, tstate_t **rslt) { inot_private_data_t *ntp; tstate_t *tptr; struct tslist *lhp; int bus, i; for (bus = 0; bus < isp->isp_nchan; bus++) { for (i = 0; i < LUN_HASH_SIZE; i++) { ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); SLIST_FOREACH(tptr, lhp, next) { ntp = isp_find_ntpd(isp, tptr, tag_id, seq_id); if (ntp) { *rslt = tptr; tptr->hold++; return (ntp); } } } } return (NULL); } static ISP_INLINE void rls_lun_statep(ispsoftc_t *isp, tstate_t *tptr) { KASSERT((tptr->hold), ("tptr not held")); tptr->hold--; } static void isp_tmcmd_restart(ispsoftc_t *isp) { inot_private_data_t *ntp; tstate_t *tptr; struct tslist *lhp; int bus, i; for (bus = 0; bus < isp->isp_nchan; bus++) { for (i = 0; i < LUN_HASH_SIZE; i++) { ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); SLIST_FOREACH(tptr, lhp, next) { inot_private_data_t *restart_queue = tptr->restart_queue; tptr->restart_queue = NULL; while (restart_queue) { ntp = restart_queue; restart_queue = ntp->rd.nt.nt_hba; if (IS_24XX(isp)) { isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at7_entry_t *)ntp->rd.data)->at_rxid); isp_handle_platform_atio7(isp, (at7_entry_t *) ntp->rd.data); } else { isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at2_entry_t *)ntp->rd.data)->at_rxid); isp_handle_platform_atio2(isp, (at2_entry_t *) ntp->rd.data); } isp_put_ntpd(isp, tptr, ntp); if (tptr->restart_queue && restart_queue != NULL) { ntp = tptr->restart_queue; tptr->restart_queue = restart_queue; while (restart_queue->rd.nt.nt_hba) { restart_queue = restart_queue->rd.nt.nt_hba; } restart_queue->rd.nt.nt_hba = ntp; break; } } } } } } static ISP_INLINE atio_private_data_t * isp_get_atpd(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag) { atio_private_data_t *atp; if (tag == 0) { atp = tptr->atfree; if (atp) { tptr->atfree = atp->next; } return (atp); } for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) { if (atp->tag == tag) { return (atp); } } return (NULL); } static ISP_INLINE void isp_put_atpd(ispsoftc_t *isp, tstate_t *tptr, atio_private_data_t *atp) { atp->tag = 0; atp->dead = 0; atp->next = tptr->atfree; tptr->atfree = atp; } static void isp_dump_atpd(ispsoftc_t *isp, tstate_t *tptr) { atio_private_data_t *atp; const char *states[8] = { "Free", "ATIO", "CAM", "CTIO", "LAST_CTIO", "PDON", "?6", "7" }; for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) { if (atp->tag == 0) { continue; } xpt_print(tptr->owner, "ATP: [0x%x] origdlen %u bytes_xfrd %u last_xfr %u lun %u nphdl 0x%04x s_id 0x%06x d_id 0x%06x oxid 0x%04x state %s\n", atp->tag, atp->orig_datalen, atp->bytes_xfered, atp->last_xframt, atp->lun, atp->nphdl, atp->sid, atp->portid, atp->oxid, states[atp->state & 0x7]); } } static ISP_INLINE inot_private_data_t * isp_get_ntpd(ispsoftc_t *isp, tstate_t *tptr) { inot_private_data_t *ntp; ntp = tptr->ntfree; if (ntp) { tptr->ntfree = ntp->next; } return (ntp); } static ISP_INLINE inot_private_data_t * isp_find_ntpd(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag_id, uint32_t seq_id) { inot_private_data_t *ntp; for (ntp = tptr->ntpool; ntp < &tptr->ntpool[ATPDPSIZE]; ntp++) { if (ntp->rd.tag_id == tag_id && ntp->rd.seq_id == seq_id) { return (ntp); } } return (NULL); } static ISP_INLINE void isp_put_ntpd(ispsoftc_t *isp, tstate_t *tptr, inot_private_data_t *ntp) { ntp->rd.tag_id = ntp->rd.seq_id = 0; ntp->next = tptr->ntfree; tptr->ntfree = ntp; } static cam_status create_lun_state(ispsoftc_t *isp, int bus, struct cam_path *path, tstate_t **rslt) { cam_status status; lun_id_t lun; struct tslist *lhp; tstate_t *tptr; int i; lun = xpt_path_lun_id(path); if (lun != CAM_LUN_WILDCARD) { if (lun >= ISP_MAX_LUNS(isp)) { return (CAM_LUN_INVALID); } } if (is_lun_enabled(isp, bus, lun)) { return (CAM_LUN_ALRDY_ENA); } tptr = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT|M_ZERO); if (tptr == NULL) { return (CAM_RESRC_UNAVAIL); } status = xpt_create_path(&tptr->owner, NULL, xpt_path_path_id(path), xpt_path_target_id(path), lun); if (status != CAM_REQ_CMP) { free(tptr, M_DEVBUF); return (status); } SLIST_INIT(&tptr->atios); SLIST_INIT(&tptr->inots); for (i = 0; i < ATPDPSIZE-1; i++) { tptr->atpool[i].next = &tptr->atpool[i+1]; tptr->ntpool[i].next = &tptr->ntpool[i+1]; } tptr->atfree = tptr->atpool; tptr->ntfree = tptr->ntpool; tptr->hold = 1; ISP_GET_PC_ADDR(isp, bus, lun_hash[LUN_HASH_FUNC(xpt_path_lun_id(tptr->owner))], lhp); SLIST_INSERT_HEAD(lhp, tptr, next); *rslt = tptr; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, path, "created tstate\n"); return (CAM_REQ_CMP); } static ISP_INLINE void destroy_lun_state(ispsoftc_t *isp, tstate_t *tptr) { struct tslist *lhp; KASSERT((tptr->hold == 0), ("tptr still held")); ISP_GET_PC_ADDR(isp, xpt_path_path_id(tptr->owner), lun_hash[LUN_HASH_FUNC(xpt_path_lun_id(tptr->owner))], lhp); SLIST_REMOVE(lhp, tptr, tstate, next); xpt_free_path(tptr->owner); free(tptr, M_DEVBUF); } /* * Enable a lun. */ static void isp_enable_lun(ispsoftc_t *isp, union ccb *ccb) { tstate_t *tptr = NULL; int bus, tm_enabled, target_role; target_id_t target; lun_id_t lun; /* * We only support either a wildcard target/lun or a target ID of zero and a non-wildcard lun */ bus = XS_CHANNEL(ccb); target = ccb->ccb_h.target_id; lun = ccb->ccb_h.target_lun; if (target != CAM_TARGET_WILDCARD && target != 0) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } if (target == CAM_TARGET_WILDCARD && lun != CAM_LUN_WILDCARD) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return; } if (target != CAM_TARGET_WILDCARD && lun == CAM_LUN_WILDCARD) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return; } if (isp->isp_dblev & ISP_LOGTDEBUG0) { xpt_print(ccb->ccb_h.path, "enabling lun 0x%x on channel %d\n", lun, bus); } /* * Wait until we're not busy with the lun enables subsystem */ while (isp->isp_osinfo.tmbusy) { isp->isp_osinfo.tmwanted = 1; mtx_sleep(isp, &isp->isp_lock, PRIBIO, "want_isp_enable_lun", 0); } isp->isp_osinfo.tmbusy = 1; /* * This is as a good a place as any to check f/w capabilities. */ if (IS_FC(isp)) { if (ISP_CAP_TMODE(isp) == 0) { xpt_print(ccb->ccb_h.path, "firmware does not support target mode\n"); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; goto done; } /* * We *could* handle non-SCCLUN f/w, but we'd have to * dork with our already fragile enable/disable code. */ if (ISP_CAP_SCCFW(isp) == 0) { xpt_print(ccb->ccb_h.path, "firmware not SCCLUN capable\n"); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; goto done; } target_role = (FCPARAM(isp, bus)->role & ISP_ROLE_TARGET) != 0; } else { target_role = (SDPARAM(isp, bus)->role & ISP_ROLE_TARGET) != 0; } /* * Create the state pointer. * It should not already exist. */ tptr = get_lun_statep(isp, bus, lun); if (tptr) { ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; goto done; } ccb->ccb_h.status = create_lun_state(isp, bus, ccb->ccb_h.path, &tptr); if (ccb->ccb_h.status != CAM_REQ_CMP) { goto done; } /* * We have a tricky maneuver to perform here. * * If target mode isn't already enabled here, * *and* our current role includes target mode, * we enable target mode here. * */ ISP_GET_PC(isp, bus, tm_enabled, tm_enabled); if (tm_enabled == 0 && target_role != 0) { if (isp_enable_target_mode(isp, bus)) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; destroy_lun_state(isp, tptr); tptr = NULL; goto done; } tm_enabled = 1; } /* * Now check to see whether this bus is in target mode already. * * If not, a later role change into target mode will finish the job. */ if (tm_enabled == 0) { ISP_SET_PC(isp, bus, tm_enable_defer, 1); ccb->ccb_h.status = CAM_REQ_CMP; xpt_print(ccb->ccb_h.path, "Target Mode Not Enabled Yet- Lun Enables Deferred\n"); goto done; } /* * Enable the lun. */ ccb->ccb_h.status = isp_enable_deferred(isp, bus, lun); done: if (ccb->ccb_h.status != CAM_REQ_CMP && tptr) { destroy_lun_state(isp, tptr); tptr = NULL; } if (tptr) { rls_lun_statep(isp, tptr); } isp->isp_osinfo.tmbusy = 0; if (isp->isp_osinfo.tmwanted) { isp->isp_osinfo.tmwanted = 0; wakeup(isp); } xpt_done(ccb); } static void isp_enable_deferred_luns(ispsoftc_t *isp, int bus) { /* * XXX: not entirely implemented yet */ (void) isp_enable_deferred(isp, bus, 0); } static uint32_t isp_enable_deferred(ispsoftc_t *isp, int bus, lun_id_t lun) { cam_status status; isp_prt(isp, ISP_LOGTINFO, "%s: bus %d lun %u", __func__, bus, lun); if (IS_24XX(isp) || (IS_FC(isp) && ISP_FC_PC(isp, bus)->tm_luns_enabled)) { status = CAM_REQ_CMP; } else { int cmd_cnt, not_cnt; if (IS_23XX(isp)) { cmd_cnt = DFLT_CMND_CNT; not_cnt = DFLT_INOT_CNT; } else { cmd_cnt = 64; not_cnt = 8; } status = CAM_REQ_INPROG; isp->isp_osinfo.rptr = &status; if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, lun, DFLT_CMND_CNT, DFLT_INOT_CNT)) { status = CAM_RESRC_UNAVAIL; } else { mtx_sleep(&status, &isp->isp_lock, PRIBIO, "isp_enable_deferred", 0); } isp->isp_osinfo.rptr = NULL; } if (status == CAM_REQ_CMP) { ISP_SET_PC(isp, bus, tm_luns_enabled, 1); isp_prt(isp, ISP_LOGTINFO, "bus %d lun %u now enabled for target mode", bus, lun); } return (status); } static void isp_disable_lun(ispsoftc_t *isp, union ccb *ccb) { tstate_t *tptr = NULL; int bus; cam_status status; target_id_t target; lun_id_t lun; bus = XS_CHANNEL(ccb); target = ccb->ccb_h.target_id; lun = ccb->ccb_h.target_lun; if (target != CAM_TARGET_WILDCARD && target != 0) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } if (target == CAM_TARGET_WILDCARD && lun != CAM_LUN_WILDCARD) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return; } if (target != CAM_TARGET_WILDCARD && lun == CAM_LUN_WILDCARD) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return; } if (isp->isp_dblev & ISP_LOGTDEBUG0) { xpt_print(ccb->ccb_h.path, "enabling lun 0x%x on channel %d\n", lun, bus); } /* * See if we're busy disabling a lun now. */ while (isp->isp_osinfo.tmbusy) { isp->isp_osinfo.tmwanted = 1; mtx_sleep(isp, &isp->isp_lock, PRIBIO, "want_isp_disable_lun", 0); } isp->isp_osinfo.tmbusy = 1; /* * Find the state pointer. */ if ((tptr = get_lun_statep(isp, bus, lun)) == NULL) { ccb->ccb_h.status = CAM_PATH_INVALID; goto done; } /* * If we're a 24XX card, we're done. */ if (IS_24XX(isp)) { status = CAM_REQ_CMP; goto done; } /* * For SCC FW, we only deal with lun zero. */ if (IS_FC(isp)) { lun = 0; } isp->isp_osinfo.rptr = &status; status = CAM_REQ_INPROG; if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, lun, 0, 0)) { status = CAM_RESRC_UNAVAIL; } else { mtx_sleep(ccb, &isp->isp_lock, PRIBIO, "isp_disable_lun", 0); } done: if (status == CAM_REQ_CMP) { xpt_print(ccb->ccb_h.path, "now disabled for target mode\n"); } if (tptr) { rls_lun_statep(isp, tptr); } isp->isp_osinfo.rptr = NULL; isp->isp_osinfo.tmbusy = 0; if (isp->isp_osinfo.tmwanted) { isp->isp_osinfo.tmwanted = 0; wakeup(isp); } xpt_done(ccb); } static int isp_enable_target_mode(ispsoftc_t *isp, int bus) { int ct; ISP_GET_PC(isp, bus, tm_enabled, ct); if (ct != 0) { return (0); } if (IS_SCSI(isp)) { mbreg_t mbs; MBSINIT(&mbs, MBOX_ENABLE_TARGET_MODE, MBLOGALL, 0); mbs.param[0] = MBOX_ENABLE_TARGET_MODE; mbs.param[1] = ENABLE_TARGET_FLAG|ENABLE_TQING_FLAG; mbs.param[2] = bus << 7; if (isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs) < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGERR, "Unable to add Target Role to Bus %d", bus); return (EIO); } SDPARAM(isp, bus)->role |= ISP_ROLE_TARGET; } ISP_SET_PC(isp, bus, tm_enabled, 1); isp_prt(isp, ISP_LOGINFO, "Target Role added to Bus %d", bus); return (0); } #ifdef NEEDED static int isp_disable_target_mode(ispsoftc_t *isp, int bus) { int ct; ISP_GET_PC(isp, bus, tm_enabled, ct); if (ct == 0) { return (0); } if (IS_SCSI(isp)) { mbreg_t mbs; MBSINIT(&mbs, MBOX_ENABLE_TARGET_MODE, MBLOGALL, 0); mbs.param[2] = bus << 7; if (isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs) < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) { isp_prt(isp, ISP_LOGERR, "Unable to subtract Target Role to Bus %d", bus); return (EIO); } SDPARAM(isp, bus)->role &= ~ISP_ROLE_TARGET; } ISP_SET_PC(isp, bus, tm_enabled, 0); isp_prt(isp, ISP_LOGINFO, "Target Role subtracted from Bus %d", bus); return (0); } #endif static void isp_ledone(ispsoftc_t *isp, lun_entry_t *lep) { uint32_t *rptr; rptr = isp->isp_osinfo.rptr; if (lep->le_status != LUN_OK) { isp_prt(isp, ISP_LOGERR, "ENABLE/MODIFY LUN returned 0x%x", lep->le_status); if (rptr) { *rptr = CAM_REQ_CMP_ERR; wakeup_one(rptr); } } else { if (rptr) { *rptr = CAM_REQ_CMP; wakeup_one(rptr); } } } static void isp_target_start_ctio(ispsoftc_t *isp, union ccb *ccb) { void *qe; tstate_t *tptr; atio_private_data_t *atp; struct ccb_scsiio *cso = &ccb->csio; uint32_t dmaresult, handle; uint8_t local[QENTRY_LEN]; /* * Do some sanity checks. */ if (cso->dxfer_len == 0) { if ((ccb->ccb_h.flags & CAM_SEND_STATUS) == 0) { xpt_print(ccb->ccb_h.path, "a data transfer length of zero but no status to send is wrong\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } } tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb)); if (tptr == NULL) { tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD); if (tptr == NULL) { xpt_print(ccb->ccb_h.path, "%s: [0x%x] cannot find tstate pointer in %s\n", __func__, cso->tag_id); dump_tstates(isp, XS_CHANNEL(ccb)); ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } } atp = isp_get_atpd(isp, tptr, cso->tag_id); if (atp == NULL) { xpt_print(ccb->ccb_h.path, "%s: [0x%x] cannot find private data adjunct\n", __func__, cso->tag_id); isp_dump_atpd(isp, tptr); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); return; } if (atp->dead) { xpt_print(ccb->ccb_h.path, "%s: [0x%x] stopping sending a CTIO for a dead command\n", __func__, cso->tag_id); ccb->ccb_h.status = CAM_REQ_ABORTED; xpt_done(ccb); return; } /* * Check to make sure we're still in target mode. */ if ((FCPARAM(isp, XS_CHANNEL(ccb))->role & ISP_ROLE_TARGET) == 0) { xpt_print(ccb->ccb_h.path, "%s: [0x%x] stopping sending a CTIO because we're no longer in target mode\n", __func__, cso->tag_id); ccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(ccb); return; } /* * Get some resources */ if (isp_get_pcmd(isp, ccb)) { rls_lun_statep(isp, tptr); xpt_print(ccb->ccb_h.path, "out of PCMDs\n"); cam_freeze_devq(ccb->ccb_h.path); cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } qe = isp_getrqentry(isp); if (qe == NULL) { xpt_print(ccb->ccb_h.path, rqo, __func__); cam_freeze_devq(ccb->ccb_h.path); cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0); ccb->ccb_h.status = CAM_REQUEUE_REQ; goto out; } memset(local, 0, QENTRY_LEN); /* * We're either moving data or completing a command here. */ if (IS_24XX(isp)) { ct7_entry_t *cto = (ct7_entry_t *) local; cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7; cto->ct_header.rqs_entry_count = 1; cto->ct_header.rqs_seqno = 1; cto->ct_nphdl = atp->nphdl; cto->ct_rxid = atp->tag; cto->ct_iid_lo = atp->portid; cto->ct_iid_hi = atp->portid >> 16; cto->ct_oxid = atp->oxid; cto->ct_vpidx = ISP_GET_VPIDX(isp, XS_CHANNEL(ccb)); cto->ct_scsi_status = cso->scsi_status; cto->ct_timeout = 120; cto->ct_flags = atp->tattr << CT7_TASK_ATTR_SHIFT; if (ccb->ccb_h.flags & CAM_SEND_STATUS) { cto->ct_flags |= CT7_SENDSTATUS; } if (cso->dxfer_len == 0) { cto->ct_flags |= CT7_FLAG_MODE1 | CT7_NO_DATA; if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { int m = min(cso->sense_len, sizeof (struct scsi_sense_data)); cto->rsp.m1.ct_resplen = cto->ct_senselen = min(m, MAXRESPLEN_24XX); memcpy(cto->rsp.m1.ct_resp, &cso->sense_data, cto->ct_senselen); cto->ct_scsi_status |= (FCP_SNSLEN_VALID << 8); } } else { cto->ct_flags |= CT7_FLAG_MODE0; if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cto->ct_flags |= CT7_DATA_IN; } else { cto->ct_flags |= CT7_DATA_OUT; } cto->rsp.m0.reloff = atp->bytes_xfered; /* * Don't overrun the limits placed on us */ if (atp->bytes_xfered + cso->dxfer_len > atp->orig_datalen) { cso->dxfer_len = atp->orig_datalen - atp->bytes_xfered; } atp->last_xframt = cso->dxfer_len; cto->rsp.m0.ct_xfrlen = cso->dxfer_len; } if (cto->ct_flags & CT7_SENDSTATUS) { int lvl = (cso->scsi_status)? ISP_LOGTINFO : ISP_LOGTDEBUG0; cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len); if (cto->ct_resid < 0) { cto->ct_scsi_status |= (FCP_RESID_OVERFLOW << 8); } else if (cto->ct_resid > 0) { cto->ct_scsi_status |= (FCP_RESID_UNDERFLOW << 8); } atp->state = ATPD_STATE_LAST_CTIO; ISP_PATH_PRT(isp, lvl, cso->ccb_h.path, "%s: CTIO7[%x] CDB0=%x scsi status %x flags %x resid %d xfrlen %u offset %u\n", __func__, cto->ct_rxid, atp->cdb0, cto->ct_scsi_status, cto->ct_flags, cto->ct_resid, cso->dxfer_len, atp->bytes_xfered); } else { cto->ct_resid = 0; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, cso->ccb_h.path, "%s: CTIO7[%x] flags %x xfrlen %u offset %u\n", __func__, cto->ct_rxid, cto->ct_flags, cso->dxfer_len, atp->bytes_xfered); atp->state = ATPD_STATE_CTIO; } } else if (IS_FC(isp)) { ct2_entry_t *cto = (ct2_entry_t *) local; cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; cto->ct_header.rqs_entry_count = 1; cto->ct_header.rqs_seqno = 1; if (ISP_CAP_2KLOGIN(isp) == 0) { ((ct2e_entry_t *)cto)->ct_iid = cso->init_id; } else { cto->ct_iid = cso->init_id; if (ISP_CAP_SCCFW(isp) == 0) { cto->ct_lun = ccb->ccb_h.target_lun; } } cto->ct_rxid = cso->tag_id; if (cso->dxfer_len == 0) { cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA | CT2_SENDSTATUS; cto->rsp.m1.ct_scsi_status = cso->scsi_status; cto->ct_resid = atp->orig_datalen - atp->bytes_xfered; if (cto->ct_resid < 0) { cto->rsp.m1.ct_scsi_status |= CT2_DATA_OVER; } else if (cto->ct_resid > 0) { cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER; } if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { int m = min(cso->sense_len, MAXRESPLEN); memcpy(cto->rsp.m1.ct_resp, &cso->sense_data, m); cto->rsp.m1.ct_senselen = m; cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID; } else if (cso->scsi_status == SCSI_STATUS_CHECK_COND) { /* * XXX: DEBUG */ xpt_print(ccb->ccb_h.path, "CHECK CONDITION being sent without associated SENSE DATA for CDB=0x%x\n", atp->cdb0); } } else { cto->ct_flags |= CT2_FLAG_MODE0; if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cto->ct_flags |= CT2_DATA_IN; } else { cto->ct_flags |= CT2_DATA_OUT; } cto->ct_reloff = atp->bytes_xfered; cto->rsp.m0.ct_xfrlen = cso->dxfer_len; /* * Don't overrun the limits placed on us */ if (atp->bytes_xfered + cso->dxfer_len > atp->orig_datalen) { cso->dxfer_len = atp->orig_datalen - atp->bytes_xfered; } if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { cto->ct_flags |= CT2_SENDSTATUS; cto->rsp.m0.ct_scsi_status = cso->scsi_status; cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len); if (cto->ct_resid < 0) { cto->rsp.m0.ct_scsi_status |= CT2_DATA_OVER; } else if (cto->ct_resid > 0) { cto->rsp.m0.ct_scsi_status |= CT2_DATA_UNDER; } } else { atp->last_xframt = cso->dxfer_len; } /* * If we're sending data and status back together, * we can't also send back sense data as well. */ ccb->ccb_h.flags &= ~CAM_SEND_SENSE; } if (cto->ct_flags & CT2_SENDSTATUS) { int lvl = (cso->scsi_status)? ISP_LOGTINFO : ISP_LOGTDEBUG0; cto->ct_flags |= CT2_CCINCR; atp->state = ATPD_STATE_LAST_CTIO; ISP_PATH_PRT(isp, lvl, cso->ccb_h.path, "%s: CTIO2[%x] CDB0=%x scsi status %x flags %x resid %d xfrlen %u offset %u\n", __func__, cto->ct_rxid, atp->cdb0, cto->rsp.m0.ct_scsi_status, cto->ct_flags, cto->ct_resid, cso->dxfer_len, atp->bytes_xfered); } else { cto->ct_resid = 0; atp->state = ATPD_STATE_CTIO; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: CTIO2[%x] flags %x xfrlen %u offset %u\n", __func__, cto->ct_rxid, cto->ct_flags, cso->dxfer_len, atp->bytes_xfered); } cto->ct_timeout = 10; } else { ct_entry_t *cto = (ct_entry_t *) local; cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; cto->ct_header.rqs_entry_count = 1; cto->ct_header.rqs_seqno = 1; cto->ct_iid = cso->init_id; cto->ct_iid |= XS_CHANNEL(ccb) << 7; cto->ct_tgt = ccb->ccb_h.target_id; cto->ct_lun = ccb->ccb_h.target_lun; cto->ct_fwhandle = cso->tag_id >> 16; if (AT_HAS_TAG(cso->tag_id)) { cto->ct_tag_val = cso->tag_id; cto->ct_flags |= CT_TQAE; } if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) { cto->ct_flags |= CT_NODISC; } if (cso->dxfer_len == 0) { cto->ct_flags |= CT_NO_DATA; } else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cto->ct_flags |= CT_DATA_IN; } else { cto->ct_flags |= CT_DATA_OUT; } if (ccb->ccb_h.flags & CAM_SEND_STATUS) { cto->ct_flags |= CT_SENDSTATUS|CT_CCINCR; cto->ct_scsi_status = cso->scsi_status; cto->ct_resid = cso->resid; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: CTIO[%x] scsi status %x resid %d tag_id %x\n", __func__, cto->ct_fwhandle, cso->scsi_status, cso->resid, cso->tag_id); } ccb->ccb_h.flags &= ~CAM_SEND_SENSE; cto->ct_timeout = 10; } if (isp_allocate_xs_tgt(isp, ccb, &handle)) { xpt_print(ccb->ccb_h.path, "No XFLIST pointers for %s\n", __func__); ccb->ccb_h.status = CAM_REQUEUE_REQ; goto out; } /* * Call the dma setup routines for this entry (and any subsequent * CTIOs) if there's data to move, and then tell the f/w it's got * new things to play with. As with isp_start's usage of DMA setup, * any swizzling is done in the machine dependent layer. Because * of this, we put the request onto the queue area first in native * format. */ if (IS_24XX(isp)) { ct7_entry_t *cto = (ct7_entry_t *) local; cto->ct_syshandle = handle; } else if (IS_FC(isp)) { ct2_entry_t *cto = (ct2_entry_t *) local; cto->ct_syshandle = handle; } else { ct_entry_t *cto = (ct_entry_t *) local; cto->ct_syshandle = handle; } dmaresult = ISP_DMASETUP(isp, cso, (ispreq_t *) local); if (dmaresult == CMD_QUEUED) { isp->isp_nactive++; ccb->ccb_h.status |= CAM_SIM_QUEUED; rls_lun_statep(isp, tptr); return; } if (dmaresult == CMD_EAGAIN) { ccb->ccb_h.status = CAM_REQUEUE_REQ; } else { ccb->ccb_h.status = CAM_REQ_CMP_ERR; } isp_destroy_tgt_handle(isp, handle); out: rls_lun_statep(isp, tptr); isp_free_pcmd(isp, ccb); xpt_done(ccb); } static void isp_refire_putback_atio(void *arg) { union ccb *ccb = arg; ispsoftc_t *isp = XS_ISP(ccb); ISP_LOCK(isp); isp_target_putback_atio(ccb); ISP_UNLOCK(isp); } static void isp_target_putback_atio(union ccb *ccb) { ispsoftc_t *isp; struct ccb_scsiio *cso; void *qe; isp = XS_ISP(ccb); qe = isp_getrqentry(isp); if (qe == NULL) { xpt_print(ccb->ccb_h.path, rqo, __func__); (void) timeout(isp_refire_putback_atio, ccb, 10); return; } memset(qe, 0, QENTRY_LEN); cso = &ccb->csio; if (IS_FC(isp)) { at2_entry_t local, *at = &local; ISP_MEMZERO(at, sizeof (at2_entry_t)); at->at_header.rqs_entry_type = RQSTYPE_ATIO2; at->at_header.rqs_entry_count = 1; if (ISP_CAP_SCCFW(isp)) { at->at_scclun = (uint16_t) ccb->ccb_h.target_lun; } else { at->at_lun = (uint8_t) ccb->ccb_h.target_lun; } at->at_status = CT_OK; at->at_rxid = cso->tag_id; at->at_iid = cso->ccb_h.target_id; isp_put_atio2(isp, at, qe); } else { at_entry_t local, *at = &local; ISP_MEMZERO(at, sizeof (at_entry_t)); at->at_header.rqs_entry_type = RQSTYPE_ATIO; at->at_header.rqs_entry_count = 1; at->at_iid = cso->init_id; at->at_iid |= XS_CHANNEL(ccb) << 7; at->at_tgt = cso->ccb_h.target_id; at->at_lun = cso->ccb_h.target_lun; at->at_status = CT_OK; at->at_tag_val = AT_GET_TAG(cso->tag_id); at->at_handle = AT_GET_HANDLE(cso->tag_id); isp_put_atio(isp, at, qe); } ISP_TDQE(isp, "isp_target_putback_atio", isp->isp_reqidx, qe); ISP_SYNC_REQUEST(isp); isp_complete_ctio(ccb); } static void isp_complete_ctio(union ccb *ccb) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { ccb->ccb_h.status |= CAM_REQ_CMP; } ccb->ccb_h.status &= ~CAM_SIM_QUEUED; isp_free_pcmd(XS_ISP(ccb), ccb); xpt_done(ccb); } /* * Handle ATIO stuff that the generic code can't. * This means handling CDBs. */ static void isp_handle_platform_atio(ispsoftc_t *isp, at_entry_t *aep) { tstate_t *tptr; int status, bus; struct ccb_accept_tio *atiop; atio_private_data_t *atp; /* * The firmware status (except for the QLTM_SVALID bit) * indicates why this ATIO was sent to us. * * If QLTM_SVALID is set, the firware has recommended Sense Data. * * If the DISCONNECTS DISABLED bit is set in the flags field, * we're still connected on the SCSI bus. */ status = aep->at_status; if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) { /* * Bus Phase Sequence error. We should have sense data * suggested by the f/w. I'm not sure quite yet what * to do about this for CAM. */ isp_prt(isp, ISP_LOGWARN, "PHASE ERROR"); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return; } if ((status & ~QLTM_SVALID) != AT_CDB) { isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform", status); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return; } bus = GET_BUS_VAL(aep->at_iid); tptr = get_lun_statep(isp, bus, aep->at_lun); if (tptr == NULL) { tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); if (tptr == NULL) { /* * Because we can't autofeed sense data back with * a command for parallel SCSI, we can't give back * a CHECK CONDITION. We'll give back a BUSY status * instead. This works out okay because the only * time we should, in fact, get this, is in the * case that somebody configured us without the * blackhole driver, so they get what they deserve. */ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return; } } atp = isp_get_atpd(isp, tptr, 0); atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); if (atiop == NULL || atp == NULL) { /* * Because we can't autofeed sense data back with * a command for parallel SCSI, we can't give back * a CHECK CONDITION. We'll give back a QUEUE FULL status * instead. This works out okay because the only time we * should, in fact, get this, is in the case that we've * run out of ATIOS. */ xpt_print(tptr->owner, "no %s for lun %d from initiator %d\n", (atp == NULL && atiop == NULL)? "ATIOs *or* ATPS" : ((atp == NULL)? "ATPs" : "ATIOs"), aep->at_lun, aep->at_iid); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); if (atp) { isp_put_atpd(isp, tptr, atp); } rls_lun_statep(isp, tptr); return; } atp->tag = aep->at_tag_val; if (atp->tag == 0) { atp->tag = ~0; } atp->state = ATPD_STATE_ATIO; SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); tptr->atio_count--; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count); atiop->ccb_h.target_id = aep->at_tgt; atiop->ccb_h.target_lun = aep->at_lun; if (aep->at_flags & AT_NODISC) { atiop->ccb_h.flags = CAM_DIS_DISCONNECT; } else { atiop->ccb_h.flags = 0; } if (status & QLTM_SVALID) { size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data)); atiop->sense_len = amt; ISP_MEMCPY(&atiop->sense_data, aep->at_sense, amt); } else { atiop->sense_len = 0; } atiop->init_id = GET_IID_VAL(aep->at_iid); atiop->cdb_len = aep->at_cdblen; ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen); atiop->ccb_h.status = CAM_CDB_RECVD; /* * Construct a tag 'id' based upon tag value (which may be 0..255) * and the handle (which we have to preserve). */ atiop->tag_id = atp->tag; if (aep->at_flags & AT_TQAE) { atiop->tag_action = aep->at_tag_type; atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; } atp->orig_datalen = 0; atp->bytes_xfered = 0; atp->last_xframt = 0; atp->lun = aep->at_lun; atp->nphdl = aep->at_iid; atp->portid = PORT_NONE; atp->oxid = 0; atp->cdb0 = atiop->cdb_io.cdb_bytes[0]; atp->tattr = aep->at_tag_type; atp->state = ATPD_STATE_CAM; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO[%x] CDB=0x%x lun %d\n", aep->at_tag_val, atp->cdb0, atp->lun); rls_lun_statep(isp, tptr); } static void isp_handle_platform_atio2(ispsoftc_t *isp, at2_entry_t *aep) { lun_id_t lun; fcportdb_t *lp; tstate_t *tptr; struct ccb_accept_tio *atiop; uint16_t nphdl; atio_private_data_t *atp = NULL; inot_private_data_t *ntp; /* * The firmware status (except for the QLTM_SVALID bit) * indicates why this ATIO was sent to us. * * If QLTM_SVALID is set, the firware has recommended Sense Data. */ if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) { isp_prt(isp, ISP_LOGWARN, "bogus atio (0x%x) leaked to platform", aep->at_status); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return; } if (ISP_CAP_SCCFW(isp)) { lun = aep->at_scclun; } else { lun = aep->at_lun; } if (ISP_CAP_2KLOGIN(isp)) { nphdl = ((at2e_entry_t *)aep)->at_iid; } else { nphdl = aep->at_iid; } tptr = get_lun_statep(isp, 0, lun); if (tptr == NULL) { tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); if (tptr == NULL) { isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] no state pointer for lun %d", aep->at_rxid, lun); isp_endcmd(isp, aep, SCSI_STATUS_CHECK_COND | ECMD_SVALID | (0x5 << 12) | (0x25 << 16), 0); return; } } /* * Start any commands pending resources first. */ if (tptr->restart_queue) { inot_private_data_t *restart_queue = tptr->restart_queue; tptr->restart_queue = NULL; while (restart_queue) { ntp = restart_queue; restart_queue = ntp->rd.nt.nt_hba; isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at2_entry_t *)ntp->rd.data)->at_rxid); isp_handle_platform_atio2(isp, (at2_entry_t *) ntp->rd.data); isp_put_ntpd(isp, tptr, ntp); /* * If a recursion caused the restart queue to start to fill again, * stop and splice the new list on top of the old list and restore * it and go to noresrc. */ if (tptr->restart_queue) { ntp = tptr->restart_queue; tptr->restart_queue = restart_queue; while (restart_queue->rd.nt.nt_hba) { restart_queue = restart_queue->rd.nt.nt_hba; } restart_queue->rd.nt.nt_hba = ntp; goto noresrc; } } } atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); if (atiop == NULL) { goto noresrc; } atp = isp_get_atpd(isp, tptr, 0); if (atp == NULL) { goto noresrc; } atp->tag = aep->at_rxid; atp->state = ATPD_STATE_ATIO; SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); tptr->atio_count--; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count); atiop->ccb_h.target_id = FCPARAM(isp, 0)->isp_loopid; atiop->ccb_h.target_lun = lun; /* * We don't get 'suggested' sense data as we do with SCSI cards. */ atiop->sense_len = 0; if (ISP_CAP_2KLOGIN(isp)) { /* * NB: We could not possibly have 2K logins if we * NB: also did not have SCC FW. */ atiop->init_id = ((at2e_entry_t *)aep)->at_iid; } else { atiop->init_id = aep->at_iid; } /* * If we're not in the port database, add ourselves. */ if (!IS_2100(isp) && isp_find_pdb_by_loopid(isp, 0, atiop->init_id, &lp) == 0) { uint64_t iid = (((uint64_t) aep->at_wwpn[0]) << 48) | (((uint64_t) aep->at_wwpn[1]) << 32) | (((uint64_t) aep->at_wwpn[2]) << 16) | (((uint64_t) aep->at_wwpn[3]) << 0); /* * However, make sure we delete ourselves if otherwise * we were there but at a different loop id. */ if (isp_find_pdb_by_wwn(isp, 0, iid, &lp)) { isp_del_wwn_entry(isp, 0, iid, lp->handle, lp->portid); } isp_add_wwn_entry(isp, 0, iid, atiop->init_id, PORT_ANY); } atiop->cdb_len = ATIO2_CDBLEN; ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN); atiop->ccb_h.status = CAM_CDB_RECVD; atiop->tag_id = atp->tag; switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) { case ATIO2_TC_ATTR_SIMPLEQ: atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atiop->tag_action = MSG_SIMPLE_Q_TAG; break; case ATIO2_TC_ATTR_HEADOFQ: atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atiop->tag_action = MSG_HEAD_OF_Q_TAG; break; case ATIO2_TC_ATTR_ORDERED: atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atiop->tag_action = MSG_ORDERED_Q_TAG; break; case ATIO2_TC_ATTR_ACAQ: /* ?? */ case ATIO2_TC_ATTR_UNTAGGED: default: atiop->tag_action = 0; break; } atp->orig_datalen = aep->at_datalen; atp->bytes_xfered = 0; atp->last_xframt = 0; atp->lun = lun; atp->nphdl = atiop->init_id; atp->sid = PORT_ANY; atp->oxid = aep->at_oxid; atp->cdb0 = aep->at_cdb[0]; atp->tattr = aep->at_taskflags & ATIO2_TC_ATTR_MASK; atp->state = ATPD_STATE_CAM; xpt_done((union ccb *)atiop); ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO2[%x] CDB=0x%x lun %d datalen %u\n", aep->at_rxid, atp->cdb0, lun, atp->orig_datalen); rls_lun_statep(isp, tptr); return; noresrc: if (atp) { isp_put_atpd(isp, tptr, atp); } ntp = isp_get_ntpd(isp, tptr); if (ntp == NULL) { rls_lun_statep(isp, tptr); isp_endcmd(isp, aep, nphdl, 0, SCSI_STATUS_BUSY, 0); return; } memcpy(ntp->rd.data, aep, QENTRY_LEN); ntp->rd.nt.nt_hba = tptr->restart_queue; tptr->restart_queue = ntp; rls_lun_statep(isp, tptr); } static void isp_handle_platform_atio7(ispsoftc_t *isp, at7_entry_t *aep) { int cdbxlen; uint16_t lun, chan, nphdl = NIL_HANDLE; uint32_t did, sid; uint64_t wwn = INI_NONE; fcportdb_t *lp; tstate_t *tptr; struct ccb_accept_tio *atiop; atio_private_data_t *atp = NULL; inot_private_data_t *ntp; did = (aep->at_hdr.d_id[0] << 16) | (aep->at_hdr.d_id[1] << 8) | aep->at_hdr.d_id[2]; sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2]; lun = (aep->at_cmnd.fcp_cmnd_lun[0] << 8) | aep->at_cmnd.fcp_cmnd_lun[1]; /* * Find the N-port handle, and Virtual Port Index for this command. * * If we can't, we're somewhat in trouble because we can't actually respond w/o that information. * We also, as a matter of course, need to know the WWN of the initiator too. */ if (ISP_CAP_MULTI_ID(isp)) { /* * Find the right channel based upon D_ID */ isp_find_chan_by_did(isp, did, &chan); if (chan == ISP_NOCHAN) { NANOTIME_T now; /* * If we don't recognizer our own D_DID, terminate the exchange, unless we're within 2 seconds of startup * It's a bit tricky here as we need to stash this command *somewhere*. */ GET_NANOTIME(&now); if (NANOTIME_SUB(&isp->isp_init_time, &now) > 2000000000ULL) { isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- dropping", __func__, aep->at_rxid, did); isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN, ECMD_TERMINATE, 0); return; } tptr = get_lun_statep(isp, 0, 0); if (tptr == NULL) { tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); if (tptr == NULL) { isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel and no tptr- dropping", __func__, aep->at_rxid, did); isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN, ECMD_TERMINATE, 0); return; } } isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- deferring", __func__, aep->at_rxid, did); goto noresrc; } isp_prt(isp, ISP_LOGTDEBUG0, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x", __func__, aep->at_rxid, did, chan, sid); } else { chan = 0; } /* * Find the PDB entry for this initiator */ if (isp_find_pdb_by_sid(isp, chan, sid, &lp) == 0) { /* * If we're not in the port database terminate the exchange. */ isp_prt(isp, ISP_LOGTINFO, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x wasn't in PDB already", __func__, aep->at_rxid, did, chan, sid); isp_endcmd(isp, aep, NIL_HANDLE, chan, ECMD_TERMINATE, 0); return; } nphdl = lp->handle; wwn = lp->port_wwn; /* * Get the tstate pointer */ tptr = get_lun_statep(isp, chan, lun); if (tptr == NULL) { tptr = get_lun_statep(isp, chan, CAM_LUN_WILDCARD); if (tptr == NULL) { isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] no state pointer for lun %d or wildcard", aep->at_rxid, lun); isp_endcmd(isp, aep, nphdl, chan, SCSI_STATUS_CHECK_COND | ECMD_SVALID | (0x5 << 12) | (0x25 << 16), 0); return; } } /* * Start any commands pending resources first. */ if (tptr->restart_queue) { inot_private_data_t *restart_queue = tptr->restart_queue; tptr->restart_queue = NULL; while (restart_queue) { ntp = restart_queue; restart_queue = ntp->rd.nt.nt_hba; isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at7_entry_t *)ntp->rd.data)->at_rxid); isp_handle_platform_atio7(isp, (at7_entry_t *) ntp->rd.data); isp_put_ntpd(isp, tptr, ntp); /* * If a recursion caused the restart queue to start to fill again, * stop and splice the new list on top of the old list and restore * it and go to noresrc. */ if (tptr->restart_queue) { if (restart_queue) { ntp = tptr->restart_queue; tptr->restart_queue = restart_queue; while (restart_queue->rd.nt.nt_hba) { restart_queue = restart_queue->rd.nt.nt_hba; } restart_queue->rd.nt.nt_hba = ntp; } goto noresrc; } } } /* * If the f/w is out of resources, just send a BUSY status back. */ if (aep->at_rxid == AT7_NORESRC_RXID) { rls_lun_statep(isp, tptr); isp_endcmd(isp, aep, nphdl, chan, SCSI_BUSY, 0); return; } /* * If we're out of resources, just send a BUSY status back. */ atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); if (atiop == NULL) { isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] out of atios", aep->at_rxid); goto noresrc; } atp = isp_get_atpd(isp, tptr, 0); if (atp == NULL) { isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] out of atps", aep->at_rxid); goto noresrc; } if (isp_get_atpd(isp, tptr, aep->at_rxid)) { isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] tag wraparound in isp_handle_platforms_atio7 (N-Port Handle 0x%04x S_ID 0x%04x OX_ID 0x%04x)\n", aep->at_rxid, nphdl, sid, aep->at_hdr.ox_id); /* * It's not a "no resource" condition- but we can treat it like one */ goto noresrc; } atp->tag = aep->at_rxid; atp->state = ATPD_STATE_ATIO; SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); tptr->atio_count--; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count); atiop->init_id = nphdl; atiop->ccb_h.target_id = FCPARAM(isp, chan)->isp_loopid; atiop->ccb_h.target_lun = lun; atiop->sense_len = 0; cdbxlen = aep->at_cmnd.fcp_cmnd_alen_datadir >> FCP_CMND_ADDTL_CDBLEN_SHIFT; if (cdbxlen) { isp_prt(isp, ISP_LOGWARN, "additional CDBLEN ignored"); } cdbxlen = sizeof (aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb); ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb, cdbxlen); atiop->cdb_len = cdbxlen; atiop->ccb_h.status = CAM_CDB_RECVD; atiop->tag_id = atp->tag; switch (aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK) { case FCP_CMND_TASK_ATTR_SIMPLE: atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atiop->tag_action = MSG_SIMPLE_Q_TAG; break; case FCP_CMND_TASK_ATTR_HEAD: atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atiop->tag_action = MSG_HEAD_OF_Q_TAG; break; case FCP_CMND_TASK_ATTR_ORDERED: atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atiop->tag_action = MSG_ORDERED_Q_TAG; break; default: /* FALLTHROUGH */ case FCP_CMND_TASK_ATTR_ACA: case FCP_CMND_TASK_ATTR_UNTAGGED: atiop->tag_action = 0; break; } atp->orig_datalen = aep->at_cmnd.cdb_dl.sf.fcp_cmnd_dl; atp->bytes_xfered = 0; atp->last_xframt = 0; atp->lun = lun; atp->nphdl = nphdl; atp->portid = sid; atp->oxid = aep->at_hdr.ox_id; atp->cdb0 = atiop->cdb_io.cdb_bytes[0]; atp->tattr = aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK; atp->state = ATPD_STATE_CAM; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO7[%x] CDB=0x%x lun %d datalen %u\n", aep->at_rxid, atp->cdb0, lun, atp->orig_datalen); xpt_done((union ccb *)atiop); rls_lun_statep(isp, tptr); return; noresrc: if (atp) { isp_put_atpd(isp, tptr, atp); } ntp = isp_get_ntpd(isp, tptr); if (ntp == NULL) { rls_lun_statep(isp, tptr); isp_endcmd(isp, aep, nphdl, chan, SCSI_STATUS_BUSY, 0); return; } memcpy(ntp->rd.data, aep, QENTRY_LEN); ntp->rd.nt.nt_hba = tptr->restart_queue; tptr->restart_queue = ntp; rls_lun_statep(isp, tptr); } static void isp_handle_platform_ctio(ispsoftc_t *isp, void *arg) { union ccb *ccb; int sentstatus, ok, notify_cam, resid = 0; tstate_t *tptr = NULL; atio_private_data_t *atp = NULL; int bus; uint32_t tval, handle; /* * CTIO, CTIO2 and CTIO7 are close enough.... */ if (IS_SCSI(isp)) { handle = ((ct_entry_t *)arg)->ct_syshandle; } else { handle = ((ct2_entry_t *)arg)->ct_syshandle; } ccb = isp_find_xs_tgt(isp, handle); if (ccb == NULL) { isp_print_bytes(isp, "null ccb in isp_handle_platform_ctio", QENTRY_LEN, arg); return; } isp_destroy_tgt_handle(isp, handle); bus = XS_CHANNEL(ccb); tptr = get_lun_statep(isp, bus, XS_LUN(ccb)); if (tptr == NULL) { tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); } KASSERT((tptr != NULL), ("cannot get state pointer")); if (isp->isp_nactive) { isp->isp_nactive++; } if (IS_24XX(isp)) { ct7_entry_t *ct = arg; atp = isp_get_atpd(isp, tptr, ct->ct_rxid); if (atp == NULL) { rls_lun_statep(isp, tptr); isp_prt(isp, ISP_LOGERR, "%s: cannot find adjunct for %x after I/O", __func__, ct->ct_rxid); return; } sentstatus = ct->ct_flags & CT7_SENDSTATUS; ok = (ct->ct_nphdl == CT7_OK); if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { ccb->ccb_h.status |= CAM_SENT_SENSE; } notify_cam = ct->ct_header.rqs_seqno & 0x1; if ((ct->ct_flags & CT7_DATAMASK) != CT7_NO_DATA) { resid = ct->ct_resid; atp->bytes_xfered += (atp->last_xframt - resid); atp->last_xframt = 0; } if (ct->ct_nphdl == CT_HBA_RESET) { ok = 0; notify_cam = 1; sentstatus = 1; ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; } else if (!ok) { ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } tval = atp->tag; isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "%s: CTIO7[%x] sts 0x%x flg 0x%x sns %d resid %d %s", __func__, ct->ct_rxid, ct->ct_nphdl, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID"); atp->state = ATPD_STATE_PDON; /* XXX: should really come after isp_complete_ctio */ } else if (IS_FC(isp)) { ct2_entry_t *ct = arg; atp = isp_get_atpd(isp, tptr, ct->ct_rxid); if (atp == NULL) { rls_lun_statep(isp, tptr); isp_prt(isp, ISP_LOGERR, "%s: cannot find adjunct for %x after I/O", __func__, ct->ct_rxid); return; } sentstatus = ct->ct_flags & CT2_SENDSTATUS; ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { ccb->ccb_h.status |= CAM_SENT_SENSE; } notify_cam = ct->ct_header.rqs_seqno & 0x1; if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) { resid = ct->ct_resid; atp->bytes_xfered += (atp->last_xframt - resid); atp->last_xframt = 0; } if (ct->ct_status == CT_HBA_RESET) { ok = 0; notify_cam = 1; sentstatus = 1; ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; } else if (!ok) { ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "%s: CTIO2[%x] sts 0x%x flg 0x%x sns %d resid %d %s", __func__, ct->ct_rxid, ct->ct_status, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID"); tval = atp->tag; atp->state = ATPD_STATE_PDON; /* XXX: should really come after isp_complete_ctio */ } else { ct_entry_t *ct = arg; sentstatus = ct->ct_flags & CT_SENDSTATUS; ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; /* * We *ought* to be able to get back to the original ATIO * here, but for some reason this gets lost. It's just as * well because it's squirrelled away as part of periph * private data. * * We can live without it as long as we continue to use * the auto-replenish feature for CTIOs. */ notify_cam = ct->ct_header.rqs_seqno & 0x1; if (ct->ct_status == (CT_HBA_RESET & 0xff)) { ok = 0; notify_cam = 1; sentstatus = 1; ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; } else if (!ok) { ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } else if (ct->ct_status & QLTM_SVALID) { char *sp = (char *)ct; sp += CTIO_SENSE_OFFSET; ccb->csio.sense_len = min(sizeof (ccb->csio.sense_data), QLTM_SENSELEN); ISP_MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; } if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) { resid = ct->ct_resid; } isp_prt(isp, ISP_LOGTDEBUG0, "%s: CTIO[%x] tag %x S_ID 0x%x lun %d sts %x flg %x resid %d %s", __func__, ct->ct_fwhandle, ct->ct_tag_val, ct->ct_iid, ct->ct_lun, ct->ct_status, ct->ct_flags, resid, sentstatus? "FIN" : "MID"); tval = ct->ct_fwhandle; } ccb->csio.resid += resid; /* * We're here either because intermediate data transfers are done * and/or the final status CTIO (which may have joined with a * Data Transfer) is done. * * In any case, for this platform, the upper layers figure out * what to do next, so all we do here is collect status and * pass information along. Any DMA handles have already been * freed. */ if (notify_cam == 0) { isp_prt(isp, ISP_LOGTDEBUG0, " INTER CTIO[0x%x] done", tval); return; } if (tptr) { rls_lun_statep(isp, tptr); } isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO[0x%x] done", (sentstatus)? " FINAL " : "MIDTERM ", tval); if (!ok && !IS_24XX(isp)) { isp_target_putback_atio(ccb); } else { isp_complete_ctio(ccb); } } static void isp_handle_platform_notify_scsi(ispsoftc_t *isp, in_entry_t *inot) { (void) isp_notify_ack(isp, inot); } static void isp_handle_platform_notify_fc(ispsoftc_t *isp, in_fcentry_t *inp) { int needack = 1; switch (inp->in_status) { case IN_PORT_LOGOUT: /* * XXX: Need to delete this initiator's WWN from the database * XXX: Need to send this LOGOUT upstream */ isp_prt(isp, ISP_LOGWARN, "port logout of S_ID 0x%x", inp->in_iid); break; case IN_PORT_CHANGED: isp_prt(isp, ISP_LOGWARN, "port changed for S_ID 0x%x", inp->in_iid); break; case IN_GLOBAL_LOGO: isp_del_all_wwn_entries(isp, 0); isp_prt(isp, ISP_LOGINFO, "all ports logged out"); break; case IN_ABORT_TASK: { tstate_t *tptr; uint16_t lun; uint32_t loopid; uint64_t wwn; atio_private_data_t *atp; fcportdb_t *lp; struct ccb_immediate_notify *inot = NULL; if (ISP_CAP_SCCFW(isp)) { lun = inp->in_scclun; } else { lun = inp->in_lun; } if (ISP_CAP_2KLOGIN(isp)) { loopid = ((in_fcentry_e_t *)inot)->in_iid; } else { loopid = inp->in_iid; } if (isp_find_pdb_by_loopid(isp, 0, loopid, &lp)) { wwn = lp->port_wwn; } else { wwn = INI_ANY; } tptr = get_lun_statep(isp, 0, lun); if (tptr == NULL) { tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); if (tptr == NULL) { isp_prt(isp, ISP_LOGWARN, "ABORT TASK for lun %u- but no tstate", lun); return; } } atp = isp_get_atpd(isp, tptr, inp->in_seqid); if (atp) { inot = (struct ccb_immediate_notify *) SLIST_FIRST(&tptr->inots); isp_prt(isp, ISP_LOGTDEBUG0, "ABORT TASK RX_ID %x WWN 0x%016llx state %d", inp->in_seqid, (unsigned long long) wwn, atp->state); if (inot) { tptr->inot_count--; SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle); ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: Take FREE INOT count now %d\n", __func__, tptr->inot_count); } else { ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "out of INOT structures\n"); } } else { ISP_PATH_PRT(isp, ISP_LOGWARN, tptr->owner, "abort task RX_ID %x from wwn 0x%016llx, state unknown\n", inp->in_seqid, wwn); } if (inot) { isp_notify_t tmp, *nt = &tmp; ISP_MEMZERO(nt, sizeof (isp_notify_t)); nt->nt_hba = isp; nt->nt_tgt = FCPARAM(isp, 0)->isp_wwpn; nt->nt_wwn = wwn; nt->nt_nphdl = loopid; nt->nt_sid = PORT_ANY; nt->nt_did = PORT_ANY; nt->nt_lun = lun; nt->nt_need_ack = 1; nt->nt_channel = 0; nt->nt_ncode = NT_ABORT_TASK; nt->nt_lreserved = inot; isp_handle_platform_target_tmf(isp, nt); needack = 0; } rls_lun_statep(isp, tptr); break; } default: break; } if (needack) { (void) isp_notify_ack(isp, inp); } } static void isp_handle_platform_notify_24xx(ispsoftc_t *isp, in_fcentry_24xx_t *inot) { uint16_t nphdl; uint32_t portid; fcportdb_t *lp; uint8_t *ptr = NULL; uint64_t wwn; nphdl = inot->in_nphdl; if (nphdl != NIL_HANDLE) { portid = inot->in_portid_hi << 16 | inot->in_portid_lo; } else { portid = PORT_ANY; } switch (inot->in_status) { case IN24XX_ELS_RCVD: { char buf[16], *msg; int chan = ISP_GET_VPIDX(isp, inot->in_vpidx); /* * Note that we're just getting notification that an ELS was received * (possibly with some associcated information sent upstream). This is * *not* the same as being given the ELS frame to accept or reject. */ switch (inot->in_status_subcode) { case LOGO: msg = "LOGO"; if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) { ptr = (uint8_t *)inot; /* point to unswizzled entry! */ wwn = (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF]) << 56) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+1]) << 48) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+2]) << 40) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+3]) << 32) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+4]) << 24) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+5]) << 16) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+6]) << 8) | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+7])); } else { wwn = INI_ANY; } isp_del_wwn_entry(isp, chan, wwn, nphdl, portid); break; case PRLO: msg = "PRLO"; break; case PLOGI: msg = "PLOGI"; if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) { ptr = (uint8_t *)inot; /* point to unswizzled entry! */ wwn = (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF]) << 56) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+1]) << 48) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+2]) << 40) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+3]) << 32) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+4]) << 24) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+5]) << 16) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+6]) << 8) | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+7])); } else { wwn = INI_NONE; } isp_add_wwn_entry(isp, chan, wwn, nphdl, portid); break; case PRLI: msg = "PRLI"; break; case PDISC: msg = "PDISC"; break; case ADISC: msg = "ADISC"; break; default: ISP_SNPRINTF(buf, sizeof (buf), "ELS 0x%x", inot->in_status_subcode); msg = buf; break; } if (inot->in_flags & IN24XX_FLAG_PUREX_IOCB) { isp_prt(isp, ISP_LOGERR, "%s Chan %d ELS N-port handle %x PortID 0x%06x marked as needing a PUREX response", msg, chan, nphdl, portid); break; } isp_prt(isp, ISP_LOGTDEBUG0, "%s Chan %d ELS N-port handle %x PortID 0x%06x RX_ID 0x%x OX_ID 0x%x", msg, chan, nphdl, portid, inot->in_rxid, inot->in_oxid); (void) isp_notify_ack(isp, inot); break; } case IN24XX_PORT_LOGOUT: ptr = "PORT LOGOUT"; if (isp_find_pdb_by_loopid(isp, ISP_GET_VPIDX(isp, inot->in_vpidx), nphdl, &lp)) { isp_del_wwn_entry(isp, ISP_GET_VPIDX(isp, inot->in_vpidx), lp->port_wwn, nphdl, lp->portid); } /* FALLTHROUGH */ case IN24XX_PORT_CHANGED: if (ptr == NULL) { ptr = "PORT CHANGED"; } /* FALLTHROUGH */ case IN24XX_LIP_RESET: if (ptr == NULL) { ptr = "LIP RESET"; } isp_prt(isp, ISP_LOGINFO, "Chan %d %s (sub-status 0x%x) for N-port handle 0x%x", ISP_GET_VPIDX(isp, inot->in_vpidx), ptr, inot->in_status_subcode, nphdl); /* * All subcodes here are irrelevant. What is relevant * is that we need to terminate all active commands from * this initiator (known by N-port handle). */ /* XXX IMPLEMENT XXX */ (void) isp_notify_ack(isp, inot); break; case IN24XX_LINK_RESET: case IN24XX_LINK_FAILED: case IN24XX_SRR_RCVD: default: (void) isp_notify_ack(isp, inot); break; } } static int isp_handle_platform_target_notify_ack(ispsoftc_t *isp, isp_notify_t *mp) { if (isp->isp_state != ISP_RUNSTATE) { isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x (qevalid=%d) acked- h/w not ready (dropping)", mp->nt_ncode, mp->nt_lreserved != NULL); return (0); } /* * This case is for a Task Management Function, which shows up as an ATIO7 entry. */ if (IS_24XX(isp) && mp->nt_lreserved && ((isphdr_t *)mp->nt_lreserved)->rqs_entry_type == RQSTYPE_ATIO) { ct7_entry_t local, *cto = &local; at7_entry_t *aep = (at7_entry_t *)mp->nt_lreserved; fcportdb_t *lp; uint32_t sid; uint16_t nphdl; sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2]; if (isp_find_pdb_by_sid(isp, mp->nt_channel, sid, &lp)) { nphdl = lp->handle; } else { nphdl = NIL_HANDLE; } ISP_MEMZERO(&local, sizeof (local)); cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7; cto->ct_header.rqs_entry_count = 1; cto->ct_nphdl = nphdl; cto->ct_rxid = aep->at_rxid; cto->ct_vpidx = mp->nt_channel; cto->ct_iid_lo = sid; cto->ct_iid_hi = sid >> 16; cto->ct_oxid = aep->at_hdr.ox_id; cto->ct_flags = CT7_SENDSTATUS|CT7_NOACK|CT7_NO_DATA|CT7_FLAG_MODE1; cto->ct_flags |= (aep->at_ta_len >> 12) << CT7_TASK_ATTR_SHIFT; return (isp_target_put_entry(isp, &local)); } /* * This case is for a responding to an ABTS frame */ if (IS_24XX(isp) && mp->nt_lreserved && ((isphdr_t *)mp->nt_lreserved)->rqs_entry_type == RQSTYPE_ABTS_RCVD) { /* * Overload nt_need_ack here to mark whether we've terminated the associated command. */ if (mp->nt_need_ack) { uint8_t storage[QENTRY_LEN]; ct7_entry_t *cto = (ct7_entry_t *) storage; abts_t *abts = (abts_t *)mp->nt_lreserved; ISP_MEMZERO(cto, sizeof (ct7_entry_t)); isp_prt(isp, ISP_LOGTDEBUG0, "%s: [%x] terminating after ABTS received", __func__, abts->abts_rxid_task); cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7; cto->ct_header.rqs_entry_count = 1; cto->ct_nphdl = mp->nt_nphdl; cto->ct_rxid = abts->abts_rxid_task; cto->ct_iid_lo = mp->nt_sid; cto->ct_iid_hi = mp->nt_sid >> 16; cto->ct_oxid = abts->abts_ox_id; cto->ct_vpidx = mp->nt_channel; cto->ct_flags = CT7_NOACK|CT7_TERMINATE; if (isp_target_put_entry(isp, cto)) { return (ENOMEM); } mp->nt_need_ack = 0; } if (isp_acknak_abts(isp, mp->nt_lreserved, 0) == ENOMEM) { return (ENOMEM); } else { return (0); } } /* * Handle logout cases here */ if (mp->nt_ncode == NT_GLOBAL_LOGOUT) { isp_del_all_wwn_entries(isp, mp->nt_channel); } if (mp->nt_ncode == NT_LOGOUT) { if (!IS_2100(isp) && IS_FC(isp)) { isp_del_wwn_entries(isp, mp); } } /* * General purpose acknowledgement */ if (mp->nt_need_ack) { isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x (qevalid=%d) being acked", mp->nt_ncode, mp->nt_lreserved != NULL); return (isp_notify_ack(isp, mp->nt_lreserved)); } return (0); } /* * Handle task managment functions. * * We show up here with a notify structure filled out. * * The nt_lreserved tag points to the original queue entry */ static void isp_handle_platform_target_tmf(ispsoftc_t *isp, isp_notify_t *notify) { tstate_t *tptr; fcportdb_t *lp; struct ccb_immediate_notify *inot; inot_private_data_t *ntp = NULL; lun_id_t lun; isp_prt(isp, ISP_LOGTDEBUG0, "%s: code 0x%x sid 0x%x tagval 0x%016llx chan %d lun 0x%x", __func__, notify->nt_ncode, notify->nt_sid, (unsigned long long) notify->nt_tagval, notify->nt_channel, notify->nt_lun); /* * NB: This assignment is necessary because of tricky type conversion. * XXX: This is tricky and I need to check this. If the lun isn't known * XXX: for the task management function, it does not of necessity follow * XXX: that it should go up stream to the wildcard listener. */ if (notify->nt_lun == LUN_ANY) { lun = CAM_LUN_WILDCARD; } else { lun = notify->nt_lun; } tptr = get_lun_statep(isp, notify->nt_channel, lun); if (tptr == NULL) { tptr = get_lun_statep(isp, notify->nt_channel, CAM_LUN_WILDCARD); if (tptr == NULL) { isp_prt(isp, ISP_LOGWARN, "%s: no state pointer found for chan %d lun 0x%x", __func__, notify->nt_channel, lun); goto bad; } } inot = (struct ccb_immediate_notify *) SLIST_FIRST(&tptr->inots); if (inot == NULL) { isp_prt(isp, ISP_LOGWARN, "%s: out of immediate notify structures for chan %d lun 0x%x", __func__, notify->nt_channel, lun); goto bad; } if (isp_find_pdb_by_sid(isp, notify->nt_channel, notify->nt_sid, &lp) == 0) { inot->initiator_id = CAM_TARGET_WILDCARD; } else { inot->initiator_id = lp->handle; } inot->seq_id = notify->nt_tagval; inot->tag_id = notify->nt_tagval >> 32; switch (notify->nt_ncode) { case NT_ABORT_TASK: isp_target_mark_aborted_early(isp, tptr, inot->tag_id); inot->arg = MSG_ABORT_TASK; break; case NT_ABORT_TASK_SET: isp_target_mark_aborted_early(isp, tptr, TAG_ANY); inot->arg = MSG_ABORT_TASK_SET; break; case NT_CLEAR_ACA: inot->arg = MSG_CLEAR_ACA; break; case NT_CLEAR_TASK_SET: inot->arg = MSG_CLEAR_TASK_SET; break; case NT_LUN_RESET: inot->arg = MSG_LOGICAL_UNIT_RESET; break; case NT_TARGET_RESET: inot->arg = MSG_TARGET_RESET; break; default: isp_prt(isp, ISP_LOGWARN, "%s: unknown TMF code 0x%x for chan %d lun 0x%x", __func__, notify->nt_ncode, notify->nt_channel, lun); goto bad; } ntp = isp_get_ntpd(isp, tptr); if (ntp == NULL) { isp_prt(isp, ISP_LOGWARN, "%s: out of inotify private structures", __func__); goto bad; } ISP_MEMCPY(&ntp->rd.nt, notify, sizeof (isp_notify_t)); if (notify->nt_lreserved) { ISP_MEMCPY(&ntp->rd.data, notify->nt_lreserved, QENTRY_LEN); ntp->rd.nt.nt_lreserved = &ntp->rd.data; } ntp->rd.seq_id = notify->nt_tagval; ntp->rd.tag_id = notify->nt_tagval >> 32; tptr->inot_count--; SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle); rls_lun_statep(isp, tptr); ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: Take FREE INOT count now %d\n", __func__, tptr->inot_count); inot->ccb_h.status = CAM_MESSAGE_RECV; xpt_done((union ccb *)inot); return; bad: if (tptr) { rls_lun_statep(isp, tptr); } if (notify->nt_need_ack && notify->nt_lreserved) { if (((isphdr_t *)notify->nt_lreserved)->rqs_entry_type == RQSTYPE_ABTS_RCVD) { (void) isp_acknak_abts(isp, notify->nt_lreserved, ENOMEM); } else { (void) isp_notify_ack(isp, notify->nt_lreserved); } } } /* * Find the associated private data and makr it as dead so * we don't try to work on it any further. */ static void isp_target_mark_aborted(ispsoftc_t *isp, union ccb *ccb) { tstate_t *tptr; atio_private_data_t *atp; tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb)); if (tptr == NULL) { tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD); if (tptr == NULL) { ccb->ccb_h.status = CAM_REQ_INVALID; return; } } atp = isp_get_atpd(isp, tptr, ccb->atio.tag_id); if (atp == NULL) { ccb->ccb_h.status = CAM_REQ_INVALID; return; } atp->dead = 1; ccb->ccb_h.status = CAM_REQ_CMP; } static void isp_target_mark_aborted_early(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag_id) { atio_private_data_t *atp; inot_private_data_t *restart_queue = tptr->restart_queue; /* * First, clean any commands pending restart */ tptr->restart_queue = NULL; while (restart_queue) { uint32_t this_tag_id; inot_private_data_t *ntp = restart_queue; restart_queue = ntp->rd.nt.nt_hba; if (IS_24XX(isp)) { this_tag_id = ((at7_entry_t *)ntp->rd.data)->at_rxid; } else { this_tag_id = ((at2_entry_t *)ntp->rd.data)->at_rxid; } if ((uint64_t)tag_id == TAG_ANY || tag_id == this_tag_id) { isp_put_ntpd(isp, tptr, ntp); } else { ntp->rd.nt.nt_hba = tptr->restart_queue; tptr->restart_queue = ntp; } } /* * Now mark other ones dead as well. */ for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) { if ((uint64_t)tag_id == TAG_ANY || atp->tag == tag_id) { atp->dead = 1; } } } #ifdef ISP_INTERNAL_TARGET // #define ISP_FORCE_TIMEOUT 1 // #define ISP_TEST_WWNS 1 // #define ISP_TEST_SEPARATE_STATUS 1 #define ccb_data_offset ppriv_field0 #define ccb_atio ppriv_ptr1 #define ccb_inot ppriv_ptr1 #define MAX_ISP_TARG_TRANSFER (2 << 20) #define NISP_TARG_CMDS 1024 #define NISP_TARG_NOTIFIES 1024 #define DISK_SHIFT 9 #define JUNK_SIZE 256 #ifndef VERIFY_10 #define VERIFY_10 0x2f #endif TAILQ_HEAD(ccb_queue, ccb_hdr); extern u_int vm_kmem_size; static int ca; static uint32_t disk_size; static uint8_t *disk_data = NULL; static uint8_t *junk_data; static MALLOC_DEFINE(M_ISPTARG, "ISPTARG", "ISP TARGET data"); struct isptarg_softc { /* CCBs (CTIOs, ATIOs, INOTs) pending on the controller */ struct ccb_queue work_queue; struct ccb_queue rework_queue; struct ccb_queue running_queue; struct ccb_queue inot_queue; struct cam_periph *periph; struct cam_path *path; ispsoftc_t *isp; }; static periph_ctor_t isptargctor; static periph_dtor_t isptargdtor; static periph_start_t isptargstart; static periph_init_t isptarginit; static void isptarg_done(struct cam_periph *, union ccb *); static void isptargasync(void *, u_int32_t, struct cam_path *, void *); static int isptarg_rwparm(uint8_t *, uint8_t *, uint64_t, uint32_t, uint8_t **, uint32_t *, int *); static struct periph_driver isptargdriver = { isptarginit, "isptarg", TAILQ_HEAD_INITIALIZER(isptargdriver.units), /* generation */ 0 }; static void isptarginit(void) { } static void isptargnotify(ispsoftc_t *isp, union ccb *iccb, struct ccb_immediate_notify *inot) { struct ccb_notify_acknowledge *ack = &iccb->cna2; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: [0x%x] immediate notify for 0x%x from 0x%x status 0x%x arg 0x%x\n", __func__, inot->tag_id, inot->initiator_id, inot->seq_id, inot->ccb_h.status, inot->arg); ack->ccb_h.func_code = XPT_NOTIFY_ACKNOWLEDGE; ack->ccb_h.flags = 0; ack->ccb_h.retry_count = 0; ack->ccb_h.cbfcnp = isptarg_done; ack->ccb_h.timeout = 0; ack->ccb_h.ccb_inot = inot; ack->tag_id = inot->tag_id; ack->seq_id = inot->seq_id; ack->initiator_id = inot->initiator_id; xpt_action(iccb); } static void isptargstart(struct cam_periph *periph, union ccb *iccb) { const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = { 0x7f }; const uint8_t iqd[SHORT_INQUIRY_LENGTH] = { 0, 0x0, 0x2, 0x2, 32, 0, 0, 0x32, 'F', 'R', 'E', 'E', 'B', 'S', 'D', ' ', 'S', 'C', 'S', 'I', ' ', 'M', 'E', 'M', 'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K', '0', '0', '0', '1' }; int i, more = 0, last; struct isptarg_softc *softc = periph->softc; struct ccb_scsiio *csio; lun_id_t return_lun; struct ccb_accept_tio *atio; uint8_t *cdb, *ptr, status; uint8_t *data_ptr; uint32_t data_len, flags; struct ccb_hdr *ccbh; mtx_assert(periph->sim->mtx, MA_OWNED); ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, iccb->ccb_h.path, "%s: function code 0x%x INOTQ=%c WORKQ=%c REWORKQ=%c\n", __func__, iccb->ccb_h.func_code, TAILQ_FIRST(&softc->inot_queue)? 'y' : 'n', TAILQ_FIRST(&softc->work_queue)? 'y' : 'n', TAILQ_FIRST(&softc->rework_queue)? 'y' : 'n'); /* * Check for immediate notifies first */ ccbh = TAILQ_FIRST(&softc->inot_queue); if (ccbh) { TAILQ_REMOVE(&softc->inot_queue, ccbh, periph_links.tqe); if (TAILQ_FIRST(&softc->inot_queue) || TAILQ_FIRST(&softc->work_queue) || TAILQ_FIRST(&softc->rework_queue)) { xpt_schedule(periph, 1); } isptargnotify(softc->isp, iccb, (struct ccb_immediate_notify *)ccbh); return; } /* * Check the rework (continuation) work queue first. */ ccbh = TAILQ_FIRST(&softc->rework_queue); if (ccbh) { atio = (struct ccb_accept_tio *)ccbh; TAILQ_REMOVE(&softc->rework_queue, ccbh, periph_links.tqe); more = TAILQ_FIRST(&softc->work_queue) || TAILQ_FIRST(&softc->rework_queue); } else { ccbh = TAILQ_FIRST(&softc->work_queue); if (ccbh == NULL) { ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, iccb->ccb_h.path, "%s: woken up but no work?\n", __func__); xpt_release_ccb(iccb); return; } atio = (struct ccb_accept_tio *)ccbh; TAILQ_REMOVE(&softc->work_queue, ccbh, periph_links.tqe); more = TAILQ_FIRST(&softc->work_queue) != NULL; atio->ccb_h.ccb_data_offset = 0; } if (atio->tag_id == 0xffffffff || atio->ccb_h.func_code != XPT_ACCEPT_TARGET_IO) { panic("BAD ATIO"); } data_ptr = NULL; data_len = 0; csio = &iccb->csio; status = SCSI_STATUS_OK; flags = CAM_SEND_STATUS; memset(&atio->sense_data, 0, sizeof (atio->sense_data)); cdb = atio->cdb_io.cdb_bytes; ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, ccbh->path, "%s: [0x%x] processing ATIO from 0x%x CDB=0x%x data_offset=%u\n", __func__, atio->tag_id, atio->init_id, cdb[0], atio->ccb_h.ccb_data_offset); return_lun = XS_LUN(atio); if (return_lun != 0) { xpt_print(atio->ccb_h.path, "[0x%x] Non-Zero Lun %d: cdb0=0x%x\n", atio->tag_id, return_lun, cdb[0]); if (cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_ILLEGAL_REQUEST; atio->sense_data.add_sense_code = 0x25; atio->sense_data.add_sense_code_qual = 0x0; atio->sense_len = sizeof (atio->sense_data); } return_lun = CAM_LUN_WILDCARD; } switch (cdb[0]) { case REQUEST_SENSE: flags |= CAM_DIR_IN; data_len = sizeof (atio->sense_data); junk_data[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_NO_SENSE; memset(junk_data+1, 0, data_len-1); if (data_len > cdb[4]) { data_len = cdb[4]; } if (data_len) { data_ptr = junk_data; } break; case READ_6: case READ_10: case READ_12: case READ_16: if (isptarg_rwparm(cdb, disk_data, disk_size, atio->ccb_h.ccb_data_offset, &data_ptr, &data_len, &last)) { status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; atio->sense_data.add_sense_code = 0x5; atio->sense_data.add_sense_code_qual = 0x24; atio->sense_len = sizeof (atio->sense_data); } else { #ifdef ISP_FORCE_TIMEOUT { static int foo; if (foo++ == 500) { if (more) { xpt_schedule(periph, 1); } foo = 0; return; } } #endif #ifdef ISP_TEST_SEPARATE_STATUS if (last && data_len) { last = 0; } #endif if (last == 0) { flags &= ~CAM_SEND_STATUS; } if (data_len) { atio->ccb_h.ccb_data_offset += data_len; flags |= CAM_DIR_IN; } else { flags |= CAM_DIR_NONE; } } break; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: if (isptarg_rwparm(cdb, disk_data, disk_size, atio->ccb_h.ccb_data_offset, &data_ptr, &data_len, &last)) { status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; atio->sense_data.add_sense_code = 0x5; atio->sense_data.add_sense_code_qual = 0x24; atio->sense_len = sizeof (atio->sense_data); } else { #ifdef ISP_FORCE_TIMEOUT { static int foo; if (foo++ == 500) { if (more) { xpt_schedule(periph, 1); } foo = 0; return; } } #endif #ifdef ISP_TEST_SEPARATE_STATUS if (last && data_len) { last = 0; } #endif if (last == 0) { flags &= ~CAM_SEND_STATUS; } if (data_len) { atio->ccb_h.ccb_data_offset += data_len; flags |= CAM_DIR_OUT; } else { flags |= CAM_DIR_NONE; } } break; case INQUIRY: flags |= CAM_DIR_IN; if (cdb[1] || cdb[2] || cdb[3]) { status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; atio->sense_data.add_sense_code = 0x5; atio->sense_data.add_sense_code_qual = 0x20; atio->sense_len = sizeof (atio->sense_data); break; } data_len = sizeof (iqd); if (data_len > cdb[4]) { data_len = cdb[4]; } if (data_len) { if (XS_LUN(iccb) != 0) { memcpy(junk_data, niliqd, sizeof (iqd)); } else { memcpy(junk_data, iqd, sizeof (iqd)); } data_ptr = junk_data; } break; case TEST_UNIT_READY: flags |= CAM_DIR_NONE; if (ca) { ca = 0; status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; atio->sense_data.add_sense_code = 0x28; atio->sense_data.add_sense_code_qual = 0x0; atio->sense_len = sizeof (atio->sense_data); } break; case SYNCHRONIZE_CACHE: case START_STOP: case RESERVE: case RELEASE: case VERIFY_10: flags |= CAM_DIR_NONE; break; case READ_CAPACITY: flags |= CAM_DIR_IN; if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; atio->sense_data.add_sense_code = 0x5; atio->sense_data.add_sense_code_qual = 0x24; atio->sense_len = sizeof (atio->sense_data); break; } if (cdb[8] & 0x1) { /* PMI */ junk_data[0] = 0xff; junk_data[1] = 0xff; junk_data[2] = 0xff; junk_data[3] = 0xff; } else { uint64_t last_blk = (disk_size >> DISK_SHIFT) - 1; if (last_blk < 0xffffffffULL) { junk_data[0] = (last_blk >> 24) & 0xff; junk_data[1] = (last_blk >> 16) & 0xff; junk_data[2] = (last_blk >> 8) & 0xff; junk_data[3] = (last_blk) & 0xff; } else { junk_data[0] = 0xff; junk_data[1] = 0xff; junk_data[2] = 0xff; junk_data[3] = 0xff; } } junk_data[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; junk_data[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; junk_data[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; junk_data[7] = ((1 << DISK_SHIFT)) & 0xff; data_ptr = junk_data; data_len = 8; break; case REPORT_LUNS: flags |= CAM_DIR_IN; memset(junk_data, 0, JUNK_SIZE); junk_data[0] = (1 << 3) >> 24; junk_data[1] = (1 << 3) >> 16; junk_data[2] = (1 << 3) >> 8; junk_data[3] = (1 << 3); ptr = NULL; for (i = 0; i < 1; i++) { ptr = &junk_data[8 + (1 << 3)]; if (i >= 256) { ptr[0] = 0x40 | ((i >> 8) & 0x3f); } ptr[1] = i; } data_ptr = junk_data; data_len = (ptr + 8) - junk_data; break; default: flags |= CAM_DIR_NONE; status = SCSI_STATUS_CHECK_COND; atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; atio->sense_data.add_sense_code = 0x5; atio->sense_data.add_sense_code_qual = 0x20; atio->sense_len = sizeof (atio->sense_data); break; } /* * If we are done with the transaction, tell the * controller to send status and perform a CMD_CMPLT. * If we have associated sense data, see if we can * send that too. */ if (status == SCSI_STATUS_CHECK_COND) { flags |= CAM_SEND_SENSE; csio->sense_len = atio->sense_len; csio->sense_data = atio->sense_data; flags &= ~CAM_DIR_MASK; data_len = 0; data_ptr = NULL; } cam_fill_ctio(csio, 0, isptarg_done, flags, MSG_SIMPLE_Q_TAG, atio->tag_id, atio->init_id, status, data_ptr, data_len, 0); iccb->ccb_h.target_id = atio->ccb_h.target_id; iccb->ccb_h.target_lun = return_lun; iccb->ccb_h.ccb_atio = atio; xpt_action(iccb); if ((atio->ccb_h.status & CAM_DEV_QFRZN) != 0) { cam_release_devq(periph->path, 0, 0, 0, 0); atio->ccb_h.status &= ~CAM_DEV_QFRZN; } if (more) { xpt_schedule(periph, 1); } } static cam_status isptargctor(struct cam_periph *periph, void *arg) { struct isptarg_softc *softc; softc = (struct isptarg_softc *)arg; periph->softc = softc; softc->periph = periph; softc->path = periph->path; ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, periph->path, "%s called\n", __func__); return (CAM_REQ_CMP); } static void isptargdtor(struct cam_periph *periph) { struct isptarg_softc *softc; softc = (struct isptarg_softc *)periph->softc; ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, periph->path, "%s called\n", __func__); softc->periph = NULL; softc->path = NULL; periph->softc = NULL; } static void isptarg_done(struct cam_periph *periph, union ccb *ccb) { struct isptarg_softc *softc; ispsoftc_t *isp; struct ccb_accept_tio *atio; struct ccb_immediate_notify *inot; cam_status status; softc = (struct isptarg_softc *)periph->softc; isp = softc->isp; status = ccb->ccb_h.status & CAM_STATUS_MASK; switch (ccb->ccb_h.func_code) { case XPT_ACCEPT_TARGET_IO: atio = (struct ccb_accept_tio *) ccb; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] ATIO seen in %s\n", atio->tag_id, __func__); TAILQ_INSERT_TAIL(&softc->work_queue, &ccb->ccb_h, periph_links.tqe); xpt_schedule(periph, 1); break; case XPT_IMMEDIATE_NOTIFY: inot = (struct ccb_immediate_notify *) ccb; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] INOT for 0x%x seen in %s\n", inot->tag_id, inot->seq_id, __func__); TAILQ_INSERT_TAIL(&softc->inot_queue, &ccb->ccb_h, periph_links.tqe); xpt_schedule(periph, 1); break; case XPT_CONT_TARGET_IO: if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0); ccb->ccb_h.status &= ~CAM_DEV_QFRZN; } atio = ccb->ccb_h.ccb_atio; if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(ccb, CAM_ESF_ALL, CAM_EPF_ALL); xpt_action((union ccb *)atio); } else if ((ccb->ccb_h.flags & CAM_SEND_STATUS) == 0) { ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] MID CTIO seen in %s\n", atio->tag_id, __func__); TAILQ_INSERT_TAIL(&softc->rework_queue, &atio->ccb_h, periph_links.tqe); xpt_schedule(periph, 1); } else { ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] FINAL CTIO seen in %s\n", atio->tag_id, __func__); xpt_action((union ccb *)atio); } xpt_release_ccb(ccb); break; case XPT_NOTIFY_ACKNOWLEDGE: if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0); ccb->ccb_h.status &= ~CAM_DEV_QFRZN; } inot = ccb->ccb_h.ccb_inot; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "[0x%x] recycle notify for tag 0x%x\n", inot->tag_id, inot->seq_id); xpt_release_ccb(ccb); xpt_action((union ccb *)inot); break; default: xpt_print(ccb->ccb_h.path, "unexpected code 0x%x\n", ccb->ccb_h.func_code); break; } } static void isptargasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct ac_contract *acp = arg; struct ac_device_changed *fc = (struct ac_device_changed *) acp->contract_data; if (code != AC_CONTRACT) { return; } xpt_print(path, "0x%016llx Port ID 0x%06x %s\n", (unsigned long long) fc->wwpn, fc->port, fc->arrived? "arrived" : "departed"); } static void isp_target_thread(ispsoftc_t *isp, int chan) { union ccb *ccb = NULL; int i; void *wchan; cam_status status; struct isptarg_softc *softc = NULL; struct cam_periph *periph = NULL, *wperiph = NULL; struct cam_path *path, *wpath; struct cam_sim *sim; if (disk_data == NULL) { disk_size = roundup2(vm_kmem_size >> 1, (1ULL << 20)); if (disk_size < (50 << 20)) { disk_size = 50 << 20; } disk_data = malloc(disk_size, M_ISPTARG, M_WAITOK | M_ZERO); if (disk_data == NULL) { isp_prt(isp, ISP_LOGERR, "%s: could not allocate disk data", __func__); goto out; } isp_prt(isp, ISP_LOGINFO, "allocated a %ju MiB disk", (uintmax_t) (disk_size >> 20)); } junk_data = malloc(JUNK_SIZE, M_ISPTARG, M_WAITOK | M_ZERO); if (junk_data == NULL) { isp_prt(isp, ISP_LOGERR, "%s: could not allocate junk", __func__); goto out; } softc = malloc(sizeof (*softc), M_ISPTARG, M_WAITOK | M_ZERO); if (softc == NULL) { isp_prt(isp, ISP_LOGERR, "%s: could not allocate softc", __func__); goto out; } TAILQ_INIT(&softc->work_queue); TAILQ_INIT(&softc->rework_queue); TAILQ_INIT(&softc->running_queue); TAILQ_INIT(&softc->inot_queue); softc->isp = isp; periphdriver_register(&isptargdriver); ISP_GET_PC(isp, chan, sim, sim); ISP_GET_PC(isp, chan, path, path); status = xpt_create_path_unlocked(&wpath, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) { isp_prt(isp, ISP_LOGERR, "%s: could not allocate wildcard path", __func__); return; } status = xpt_create_path_unlocked(&path, NULL, cam_sim_path(sim), 0, 0); if (status != CAM_REQ_CMP) { xpt_free_path(wpath); isp_prt(isp, ISP_LOGERR, "%s: could not allocate path", __func__); return; } ccb = xpt_alloc_ccb(); ISP_LOCK(isp); status = cam_periph_alloc(isptargctor, NULL, isptargdtor, isptargstart, "isptarg", CAM_PERIPH_BIO, wpath, NULL, 0, softc); if (status != CAM_REQ_CMP) { ISP_UNLOCK(isp); isp_prt(isp, ISP_LOGERR, "%s: cam_periph_alloc for wildcard failed", __func__); goto out; } wperiph = cam_periph_find(wpath, "isptarg"); if (wperiph == NULL) { ISP_UNLOCK(isp); isp_prt(isp, ISP_LOGERR, "%s: wildcard periph already allocated but doesn't exist", __func__); goto out; } status = cam_periph_alloc(isptargctor, NULL, isptargdtor, isptargstart, "isptarg", CAM_PERIPH_BIO, path, NULL, 0, softc); if (status != CAM_REQ_CMP) { ISP_UNLOCK(isp); isp_prt(isp, ISP_LOGERR, "%s: cam_periph_alloc failed", __func__); goto out; } periph = cam_periph_find(path, "isptarg"); if (periph == NULL) { ISP_UNLOCK(isp); isp_prt(isp, ISP_LOGERR, "%s: periph already allocated but doesn't exist", __func__); goto out; } status = xpt_register_async(AC_CONTRACT, isptargasync, isp, wpath); if (status != CAM_REQ_CMP) { ISP_UNLOCK(isp); isp_prt(isp, ISP_LOGERR, "%s: xpt_register_async failed", __func__); goto out; } ISP_UNLOCK(isp); ccb = xpt_alloc_ccb(); /* * Make sure role is none. */ xpt_setup_ccb(&ccb->ccb_h, periph->path, 10); ccb->ccb_h.func_code = XPT_SET_SIM_KNOB; ccb->knob.xport_specific.fc.role = KNOB_ROLE_NONE; #ifdef ISP_TEST_WWNS ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE | KNOB_VALID_ADDRESS; ccb->knob.xport_specific.fc.wwnn = 0x508004d000000000ULL | (device_get_unit(isp->isp_osinfo.dev) << 8) | (chan << 16); ccb->knob.xport_specific.fc.wwpn = 0x508004d000000001ULL | (device_get_unit(isp->isp_osinfo.dev) << 8) | (chan << 16); #else ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE; #endif ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); /* * Now enable luns */ xpt_setup_ccb(&ccb->ccb_h, periph->path, 10); ccb->ccb_h.func_code = XPT_EN_LUN; ccb->cel.enable = 1; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); if (ccb->ccb_h.status != CAM_REQ_CMP) { xpt_free_ccb(ccb); xpt_print(periph->path, "failed to enable lun (0x%x)\n", ccb->ccb_h.status); goto out; } xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 10); ccb->ccb_h.func_code = XPT_EN_LUN; ccb->cel.enable = 1; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); if (ccb->ccb_h.status != CAM_REQ_CMP) { xpt_free_ccb(ccb); xpt_print(wperiph->path, "failed to enable lun (0x%x)\n", ccb->ccb_h.status); goto out; } xpt_free_ccb(ccb); /* * Add resources */ ISP_GET_PC_ADDR(isp, chan, target_proc, wchan); for (i = 0; i < 4; i++) { ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 1); ccb->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; ccb->ccb_h.cbfcnp = isptarg_done; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); } for (i = 0; i < NISP_TARG_CMDS; i++) { ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); xpt_setup_ccb(&ccb->ccb_h, periph->path, 1); ccb->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; ccb->ccb_h.cbfcnp = isptarg_done; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); } for (i = 0; i < 4; i++) { ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 1); ccb->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY; ccb->ccb_h.cbfcnp = isptarg_done; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); } for (i = 0; i < NISP_TARG_NOTIFIES; i++) { ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); xpt_setup_ccb(&ccb->ccb_h, periph->path, 1); ccb->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY; ccb->ccb_h.cbfcnp = isptarg_done; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); } /* * Now turn it all back on */ xpt_setup_ccb(&ccb->ccb_h, periph->path, 10); ccb->ccb_h.func_code = XPT_SET_SIM_KNOB; ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE; ccb->knob.xport_specific.fc.role = KNOB_ROLE_TARGET; ISP_LOCK(isp); xpt_action(ccb); ISP_UNLOCK(isp); /* * Okay, while things are still active, sleep... */ ISP_LOCK(isp); for (;;) { ISP_GET_PC(isp, chan, proc_active, i); if (i == 0) { break; } msleep(wchan, &isp->isp_lock, PUSER, "tsnooze", 0); } ISP_UNLOCK(isp); out: if (wperiph) { cam_periph_invalidate(wperiph); } if (periph) { cam_periph_invalidate(periph); } if (junk_data) { free(junk_data, M_ISPTARG); } if (disk_data) { free(disk_data, M_ISPTARG); } if (softc) { free(softc, M_ISPTARG); } xpt_free_path(path); xpt_free_path(wpath); } static void isp_target_thread_pi(void *arg) { struct isp_spi *pi = arg; isp_target_thread(cam_sim_softc(pi->sim), cam_sim_bus(pi->sim)); } static void isp_target_thread_fc(void *arg) { struct isp_fc *fc = arg; isp_target_thread(cam_sim_softc(fc->sim), cam_sim_bus(fc->sim)); } static int isptarg_rwparm(uint8_t *cdb, uint8_t *dp, uint64_t dl, uint32_t offset, uint8_t **kp, uint32_t *tl, int *lp) { uint32_t cnt, curcnt; uint64_t lba; switch (cdb[0]) { case WRITE_16: case READ_16: cnt = (((uint32_t)cdb[10]) << 24) | (((uint32_t)cdb[11]) << 16) | (((uint32_t)cdb[12]) << 8) | ((uint32_t)cdb[13]); lba = (((uint64_t)cdb[2]) << 56) | (((uint64_t)cdb[3]) << 48) | (((uint64_t)cdb[4]) << 40) | (((uint64_t)cdb[5]) << 32) | (((uint64_t)cdb[6]) << 24) | (((uint64_t)cdb[7]) << 16) | (((uint64_t)cdb[8]) << 8) | ((uint64_t)cdb[9]); break; case WRITE_12: case READ_12: cnt = (((uint32_t)cdb[6]) << 16) | (((uint32_t)cdb[7]) << 8) | ((u_int32_t)cdb[8]); lba = (((uint32_t)cdb[2]) << 24) | (((uint32_t)cdb[3]) << 16) | (((uint32_t)cdb[4]) << 8) | ((uint32_t)cdb[5]); break; case WRITE_10: case READ_10: cnt = (((uint32_t)cdb[7]) << 8) | ((u_int32_t)cdb[8]); lba = (((uint32_t)cdb[2]) << 24) | (((uint32_t)cdb[3]) << 16) | (((uint32_t)cdb[4]) << 8) | ((uint32_t)cdb[5]); break; case WRITE_6: case READ_6: cnt = cdb[4]; if (cnt == 0) { cnt = 256; } lba = (((uint32_t)cdb[1] & 0x1f) << 16) | (((uint32_t)cdb[2]) << 8) | ((uint32_t)cdb[3]); break; default: return (-1); } cnt <<= DISK_SHIFT; lba <<= DISK_SHIFT; if (offset == cnt) { *lp = 1; return (0); } if (lba + cnt > dl) { return (-1); } curcnt = MAX_ISP_TARG_TRANSFER; if (offset + curcnt >= cnt) { curcnt = cnt - offset; *lp = 1; } else { *lp = 0; } *tl = curcnt; *kp = &dp[lba + offset]; return (0); } #endif #endif static void isp_cam_async(void *cbarg, uint32_t code, struct cam_path *path, void *arg) { struct cam_sim *sim; ispsoftc_t *isp; sim = (struct cam_sim *)cbarg; isp = (ispsoftc_t *) cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: if (IS_SCSI(isp)) { uint16_t oflags, nflags; int bus = cam_sim_bus(sim); sdparam *sdp = SDPARAM(isp, bus); int tgt; tgt = xpt_path_target_id(path); if (tgt >= 0) { nflags = sdp->isp_devparam[tgt].nvrm_flags; #ifndef ISP_TARGET_MODE nflags &= DPARM_SAFE_DFLT; if (isp->isp_loaded_fw) { nflags |= DPARM_NARROW | DPARM_ASYNC; } #else nflags = DPARM_DEFAULT; #endif oflags = sdp->isp_devparam[tgt].goal_flags; sdp->isp_devparam[tgt].goal_flags = nflags; sdp->isp_devparam[tgt].dev_update = 1; sdp->update = 1; (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, bus); sdp->isp_devparam[tgt].goal_flags = oflags; } } break; default: isp_prt(isp, ISP_LOGWARN, "isp_cam_async: Code 0x%x", code); break; } } static void isp_poll(struct cam_sim *sim) { ispsoftc_t *isp = cam_sim_softc(sim); uint32_t isr; uint16_t sema, mbox; if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { isp_intr(isp, isr, sema, mbox); } } static void isp_watchdog(void *arg) { struct ccb_scsiio *xs = arg; ispsoftc_t *isp; uint32_t handle; isp = XS_ISP(xs); handle = isp_find_handle(isp, xs); if (handle != ISP_HANDLE_FREE) { /* * Try and make sure the command is really dead before * we release the handle (and DMA resources) for reuse. * * If we are successful in aborting the command then * we're done here because we'll get the command returned * back separately. */ if (isp_control(isp, ISPCTL_ABORT_CMD, xs) == 0) { return; } /* * Note that after calling the above, the command may in * fact have been completed. */ xs = isp_find_xs(isp, handle); /* * If the command no longer exists, then we won't * be able to find the xs again with this handle. */ if (xs == NULL) { return; } /* * After this point, the command is really dead. */ if (XS_XFRLEN(xs)) { ISP_DMAFREE(isp, xs, handle); } isp_destroy_handle(isp, handle); isp_prt(isp, ISP_LOGERR, "%s: timeout for handle 0x%x", __func__, handle); XS_SETERR(xs, CAM_CMD_TIMEOUT); isp_done(xs); } } static void isp_make_here(ispsoftc_t *isp, int chan, int tgt) { union ccb *ccb; struct isp_fc *fc = ISP_FC_PC(isp, chan); if (isp_autoconfig == 0) { return; } /* * Allocate a CCB, create a wildcard path for this bus/target and schedule a rescan. */ ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { isp_prt(isp, ISP_LOGWARN, "Chan %d unable to alloc CCB for rescan", chan); return; } - if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(fc->sim), tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + /* + * xpt_rescan only honors wildcard in the target field. + * Scan the whole bus instead of target, which will then + * force a scan of all luns. + */ + if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(fc->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { isp_prt(isp, ISP_LOGWARN, "unable to create path for rescan"); xpt_free_ccb(ccb); return; } xpt_rescan(ccb); } static void isp_make_gone(ispsoftc_t *isp, int chan, int tgt) { struct cam_path *tp; struct isp_fc *fc = ISP_FC_PC(isp, chan); if (isp_autoconfig == 0) { return; } if (xpt_create_path(&tp, NULL, cam_sim_path(fc->sim), tgt, CAM_LUN_WILDCARD) == CAM_REQ_CMP) { xpt_async(AC_LOST_DEVICE, tp, NULL); xpt_free_path(tp); } } /* * Gone Device Timer Function- when we have decided that a device has gone * away, we wait a specific period of time prior to telling the OS it has * gone away. * * This timer function fires once a second and then scans the port database * for devices that are marked dead but still have a virtual target assigned. * We decrement a counter for that port database entry, and when it hits zero, * we tell the OS the device has gone away. */ static void isp_gdt(void *arg) { struct isp_fc *fc = arg; ispsoftc_t *isp = fc->isp; int chan = fc - isp->isp_osinfo.pc.fc; fcportdb_t *lp; int dbidx, tgt, more_to_do = 0; isp_prt(isp, ISP_LOGDEBUG0, "Chan %d GDT timer expired", chan); for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { lp = &FCPARAM(isp, chan)->portdb[dbidx]; if (lp->state != FC_PORTDB_STATE_ZOMBIE) { continue; } if (lp->dev_map_idx == 0 || lp->target_mode) { continue; } if (lp->new_reserved == 0) { continue; } lp->new_reserved -= 1; if (lp->new_reserved != 0) { more_to_do++; continue; } tgt = lp->dev_map_idx - 1; FCPARAM(isp, chan)->isp_dev_map[tgt] = 0; lp->dev_map_idx = 0; lp->state = FC_PORTDB_STATE_NIL; isp_prt(isp, ISP_LOGCONFIG, prom3, chan, lp->portid, tgt, "Gone Device Timeout"); isp_make_gone(isp, chan, tgt); } if (fc->ready) { if (more_to_do) { callout_reset(&fc->gdt, hz, isp_gdt, fc); } else { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d stopping Gone Device Timer", chan); } } } /* * Loop Down Timer Function- when loop goes down, a timer is started and * and after it expires we come here and take all probational devices that * the OS knows about and the tell the OS that they've gone away. * * We don't clear the devices out of our port database because, when loop * come back up, we have to do some actual cleanup with the chip at that * point (implicit PLOGO, e.g., to get the chip's port database state right). */ static void isp_ldt(void *arg) { struct isp_fc *fc = arg; ispsoftc_t *isp = fc->isp; int chan = fc - isp->isp_osinfo.pc.fc; fcportdb_t *lp; int dbidx, tgt; isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d Loop Down Timer expired @ %lu", chan, (unsigned long) time_uptime); /* * Notify to the OS all targets who we now consider have departed. */ for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { lp = &FCPARAM(isp, chan)->portdb[dbidx]; if (lp->state != FC_PORTDB_STATE_PROBATIONAL) { continue; } if (lp->dev_map_idx == 0 || lp->target_mode) { continue; } /* * XXX: CLEAN UP AND COMPLETE ANY PENDING COMMANDS FIRST! */ /* * Mark that we've announced that this device is gone.... */ lp->reserved = 1; /* * but *don't* change the state of the entry. Just clear * any target id stuff and announce to CAM that the * device is gone. This way any necessary PLOGO stuff * will happen when loop comes back up. */ tgt = lp->dev_map_idx - 1; FCPARAM(isp, chan)->isp_dev_map[tgt] = 0; lp->dev_map_idx = 0; lp->state = FC_PORTDB_STATE_NIL; isp_prt(isp, ISP_LOGCONFIG, prom3, chan, lp->portid, tgt, "Loop Down Timeout"); isp_make_gone(isp, chan, tgt); } /* * The loop down timer has expired. Wake up the kthread * to notice that fact (or make it false). */ fc->loop_dead = 1; fc->loop_down_time = fc->loop_down_limit+1; wakeup(fc); } static void isp_kthread(void *arg) { struct isp_fc *fc = arg; ispsoftc_t *isp = fc->isp; int chan = fc - isp->isp_osinfo.pc.fc; int slp = 0; mtx_lock(&isp->isp_osinfo.lock); for (;;) { int wasfrozen, lb, lim; isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d checking FC state", __func__, chan); lb = isp_fc_runstate(isp, chan, 250000); /* * Our action is different based upon whether we're supporting * Initiator mode or not. If we are, we might freeze the simq * when loop is down and set all sorts of different delays to * check again. * * If not, we simply just wait for loop to come up. */ if (lb && (fc->role & ISP_ROLE_INITIATOR)) { /* * Increment loop down time by the last sleep interval */ fc->loop_down_time += slp; if (lb < 0) { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC loop not up (down count %d)", __func__, chan, fc->loop_down_time); } else { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC got to %d (down count %d)", __func__, chan, lb, fc->loop_down_time); } /* * If we've never seen loop up and we've waited longer * than quickboot time, or we've seen loop up but we've * waited longer than loop_down_limit, give up and go * to sleep until loop comes up. */ if (FCPARAM(isp, chan)->loop_seen_once == 0) { lim = isp_quickboot_time; } else { lim = fc->loop_down_limit; } if (fc->loop_down_time >= lim) { isp_freeze_loopdown(isp, chan, "loop limit hit"); slp = 0; } else if (fc->loop_down_time < 10) { slp = 1; } else if (fc->loop_down_time < 30) { slp = 5; } else if (fc->loop_down_time < 60) { slp = 10; } else if (fc->loop_down_time < 120) { slp = 20; } else { slp = 30; } } else if (lb) { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC Loop Down", __func__, chan); fc->loop_down_time += slp; slp = 60; } else { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC state OK", __func__, chan); fc->loop_down_time = 0; slp = 0; } /* * If this is past the first loop up or the loop is dead and if we'd frozen the simq, unfreeze it * now so that CAM can start sending us commands. * * If the FC state isn't okay yet, they'll hit that in isp_start which will freeze the queue again * or kill the commands, as appropriate. */ if (FCPARAM(isp, chan)->loop_seen_once || fc->loop_dead) { wasfrozen = fc->simqfrozen & SIMQFRZ_LOOPDOWN; fc->simqfrozen &= ~SIMQFRZ_LOOPDOWN; if (wasfrozen && fc->simqfrozen == 0) { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d releasing simq", __func__, chan); xpt_release_simq(fc->sim, 1); } } isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d sleep time %d", __func__, chan, slp); msleep(fc, &isp->isp_osinfo.lock, PRIBIO, "ispf", slp * hz); /* * If slp is zero, we're waking up for the first time after * things have been okay. In this case, we set a deferral state * for all commands and delay hysteresis seconds before starting * the FC state evaluation. This gives the loop/fabric a chance * to settle. */ if (slp == 0 && fc->hysteresis) { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d sleep hysteresis ticks %d", __func__, chan, fc->hysteresis * hz); (void) msleep(&isp_fabric_hysteresis, &isp->isp_osinfo.lock, PRIBIO, "ispT", (fc->hysteresis * hz)); } } mtx_unlock(&isp->isp_osinfo.lock); } static void isp_action(struct cam_sim *sim, union ccb *ccb) { int bus, tgt, ts, error, lim; ispsoftc_t *isp; struct ccb_trans_settings *cts; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n")); isp = (ispsoftc_t *)cam_sim_softc(sim); mtx_assert(&isp->isp_lock, MA_OWNED); if (isp->isp_state != ISP_RUNSTATE && ccb->ccb_h.func_code == XPT_SCSI_IO) { isp_init(isp); if (isp->isp_state != ISP_INITSTATE) { /* * Lie. Say it was a selection timeout. */ ccb->ccb_h.status = CAM_SEL_TIMEOUT | CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, 1); xpt_done(ccb); return; } isp->isp_state = ISP_RUNSTATE; } isp_prt(isp, ISP_LOGDEBUG2, "isp_action code %x", ccb->ccb_h.func_code); ISP_PCMD(ccb) = NULL; switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ bus = XS_CHANNEL(ccb); /* * Do a couple of preliminary checks... */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } #ifdef DIAGNOSTIC if (ccb->ccb_h.target_id > (ISP_MAX_TARGETS(isp) - 1)) { xpt_print(ccb->ccb_h.path, "invalid target\n"); ccb->ccb_h.status = CAM_PATH_INVALID; } else if (ccb->ccb_h.target_lun > (ISP_MAX_LUNS(isp) - 1)) { xpt_print(ccb->ccb_h.path, "invalid lun\n"); ccb->ccb_h.status = CAM_PATH_INVALID; } if (ccb->ccb_h.status == CAM_PATH_INVALID) { xpt_done(ccb); break; } #endif ccb->csio.scsi_status = SCSI_STATUS_OK; if (isp_get_pcmd(isp, ccb)) { isp_prt(isp, ISP_LOGWARN, "out of PCMDs"); cam_freeze_devq(ccb->ccb_h.path); cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0); xpt_done(ccb); break; } error = isp_start((XS_T *) ccb); switch (error) { case CMD_QUEUED: XS_CMD_S_CLEAR(ccb); ccb->ccb_h.status |= CAM_SIM_QUEUED; if (ccb->ccb_h.timeout == CAM_TIME_INFINITY) { break; } ts = ccb->ccb_h.timeout; if (ts == CAM_TIME_DEFAULT) { ts = 60*1000; } ts = isp_mstohz(ts); callout_reset(&PISP_PCMD(ccb)->wdog, ts, isp_watchdog, ccb); break; case CMD_RQLATER: /* * We get this result for FC devices if the loop state isn't ready yet * or if the device in question has gone zombie on us. * * If we've never seen Loop UP at all, we requeue this request and wait * for the initial loop up delay to expire. */ lim = ISP_FC_PC(isp, bus)->loop_down_limit; if (FCPARAM(isp, bus)->loop_seen_once == 0 || ISP_FC_PC(isp, bus)->loop_down_time >= lim) { if (FCPARAM(isp, bus)->loop_seen_once == 0) { isp_prt(isp, ISP_LOGDEBUG0, "%d.%d loop not seen yet @ %lu", XS_TGT(ccb), XS_LUN(ccb), (unsigned long) time_uptime); } else { isp_prt(isp, ISP_LOGDEBUG0, "%d.%d downtime (%d) > lim (%d)", XS_TGT(ccb), XS_LUN(ccb), ISP_FC_PC(isp, bus)->loop_down_time, lim); } ccb->ccb_h.status = CAM_SEL_TIMEOUT|CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, 1); isp_free_pcmd(isp, ccb); xpt_done(ccb); break; } isp_prt(isp, ISP_LOGDEBUG0, "%d.%d retry later", XS_TGT(ccb), XS_LUN(ccb)); cam_freeze_devq(ccb->ccb_h.path); cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 1000, 0); XS_SETERR(ccb, CAM_REQUEUE_REQ); isp_free_pcmd(isp, ccb); xpt_done(ccb); break; case CMD_EAGAIN: isp_free_pcmd(isp, ccb); cam_freeze_devq(ccb->ccb_h.path); cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 100, 0); XS_SETERR(ccb, CAM_REQUEUE_REQ); xpt_done(ccb); break; case CMD_COMPLETE: isp_done((struct ccb_scsiio *) ccb); break; default: isp_prt(isp, ISP_LOGERR, "What's this? 0x%x at %d in file %s", error, __LINE__, __FILE__); XS_SETERR(ccb, CAM_REQ_CMP_ERR); isp_free_pcmd(isp, ccb); xpt_done(ccb); } break; #ifdef ISP_TARGET_MODE case XPT_EN_LUN: /* Enable/Disable LUN as a target */ if (ccb->cel.enable) { isp_enable_lun(isp, ccb); } else { isp_disable_lun(isp, ccb); } break; case XPT_IMMED_NOTIFY: case XPT_IMMEDIATE_NOTIFY: /* Add Immediate Notify Resource */ case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */ { tstate_t *tptr = get_lun_statep(isp, XS_CHANNEL(ccb), ccb->ccb_h.target_lun); if (tptr == NULL) { tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD); } if (tptr == NULL) { const char *str; uint32_t tag; if (ccb->ccb_h.func_code == XPT_IMMEDIATE_NOTIFY) { str = "XPT_IMMEDIATE_NOTIFY"; tag = ccb->cin1.seq_id; } else { tag = ccb->atio.tag_id; str = "XPT_ACCEPT_TARGET_IO"; } ISP_PATH_PRT(isp, ISP_LOGWARN, ccb->ccb_h.path, "%s: [0x%x] no state pointer found for %s\n", __func__, tag, str); dump_tstates(isp, XS_CHANNEL(ccb)); ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; } ccb->ccb_h.sim_priv.entries[0].field = 0; ccb->ccb_h.sim_priv.entries[1].ptr = isp; ccb->ccb_h.flags = 0; if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { if (ccb->atio.tag_id) { atio_private_data_t *atp = isp_get_atpd(isp, tptr, ccb->atio.tag_id); if (atp) { isp_put_atpd(isp, tptr, atp); } } tptr->atio_count++; SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h, sim_links.sle); ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "Put FREE ATIO (tag id 0x%x), count now %d\n", ((struct ccb_accept_tio *)ccb)->tag_id, tptr->atio_count); } else if (ccb->ccb_h.func_code == XPT_IMMEDIATE_NOTIFY) { if (ccb->cin1.tag_id) { inot_private_data_t *ntp = isp_find_ntpd(isp, tptr, ccb->cin1.tag_id, ccb->cin1.seq_id); if (ntp) { isp_put_ntpd(isp, tptr, ntp); } } tptr->inot_count++; SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, sim_links.sle); ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "Put FREE INOT, (seq id 0x%x) count now %d\n", ((struct ccb_immediate_notify *)ccb)->seq_id, tptr->inot_count); } else if (ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) { tptr->inot_count++; SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, sim_links.sle); ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "Put FREE INOT, (seq id 0x%x) count now %d\n", ((struct ccb_immediate_notify *)ccb)->seq_id, tptr->inot_count); } rls_lun_statep(isp, tptr); ccb->ccb_h.status = CAM_REQ_INPROG; break; } case XPT_NOTIFY_ACK: ccb->ccb_h.status = CAM_REQ_CMP_ERR; break; case XPT_NOTIFY_ACKNOWLEDGE: /* notify ack */ { tstate_t *tptr; inot_private_data_t *ntp; /* * XXX: Because we cannot guarantee that the path information in the notify acknowledge ccb * XXX: matches that for the immediate notify, we have to *search* for the notify structure */ /* * All the relevant path information is in the associated immediate notify */ ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: [0x%x] NOTIFY ACKNOWLEDGE for 0x%x seen\n", __func__, ccb->cna2.tag_id, ccb->cna2.seq_id); ntp = get_ntp_from_tagdata(isp, ccb->cna2.tag_id, ccb->cna2.seq_id, &tptr); if (ntp == NULL) { ISP_PATH_PRT(isp, ISP_LOGWARN, ccb->ccb_h.path, "%s: [0x%x] XPT_NOTIFY_ACKNOWLEDGE of 0x%x cannot find ntp private data\n", __func__, ccb->cna2.tag_id, ccb->cna2.seq_id); ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); break; } if (isp_handle_platform_target_notify_ack(isp, &ntp->rd.nt)) { rls_lun_statep(isp, tptr); cam_freeze_devq(ccb->ccb_h.path); cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 1000, 0); XS_SETERR(ccb, CAM_REQUEUE_REQ); break; } isp_put_ntpd(isp, tptr, ntp); rls_lun_statep(isp, tptr); ccb->ccb_h.status = CAM_REQ_CMP; ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: [0x%x] calling xpt_done for tag 0x%x\n", __func__, ccb->cna2.tag_id, ccb->cna2.seq_id); xpt_done(ccb); break; } case XPT_CONT_TARGET_IO: isp_target_start_ctio(isp, ccb); break; #endif case XPT_RESET_DEV: /* BDR the specified SCSI device */ bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); tgt = ccb->ccb_h.target_id; tgt |= (bus << 16); error = isp_control(isp, ISPCTL_RESET_DEV, bus, tgt); if (error) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; } else { ccb->ccb_h.status = CAM_REQ_CMP; } xpt_done(ccb); break; case XPT_ABORT: /* Abort the specified CCB */ { union ccb *accb = ccb->cab.abort_ccb; switch (accb->ccb_h.func_code) { #ifdef ISP_TARGET_MODE case XPT_ACCEPT_TARGET_IO: isp_target_mark_aborted(isp, accb); break; #endif case XPT_SCSI_IO: error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); if (error) { ccb->ccb_h.status = CAM_UA_ABORT; } else { ccb->ccb_h.status = CAM_REQ_CMP; } break; default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); break; } #define IS_CURRENT_SETTINGS(c) (c->type == CTS_TYPE_CURRENT_SETTINGS) case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */ cts = &ccb->cts; if (!IS_CURRENT_SETTINGS(cts)) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } tgt = cts->ccb_h.target_id; bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); if (IS_SCSI(isp)) { struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; sdparam *sdp = SDPARAM(isp, bus); uint16_t *dptr; if (spi->valid == 0 && scsi->valid == 0) { ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } /* * We always update (internally) from goal_flags * so any request to change settings just gets * vectored to that location. */ dptr = &sdp->isp_devparam[tgt].goal_flags; if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) *dptr |= DPARM_DISC; else *dptr &= ~DPARM_DISC; } if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) *dptr |= DPARM_TQING; else *dptr &= ~DPARM_TQING; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { if (spi->bus_width == MSG_EXT_WDTR_BUS_16_BIT) *dptr |= DPARM_WIDE; else *dptr &= ~DPARM_WIDE; } /* * XXX: FIX ME */ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) && (spi->valid & CTS_SPI_VALID_SYNC_RATE) && (spi->sync_period && spi->sync_offset)) { *dptr |= DPARM_SYNC; /* * XXX: CHECK FOR LEGALITY */ sdp->isp_devparam[tgt].goal_period = spi->sync_period; sdp->isp_devparam[tgt].goal_offset = spi->sync_offset; } else { *dptr &= ~DPARM_SYNC; } isp_prt(isp, ISP_LOGDEBUG0, "SET (%d.%d.%d) to flags %x off %x per %x", bus, tgt, cts->ccb_h.target_lun, sdp->isp_devparam[tgt].goal_flags, sdp->isp_devparam[tgt].goal_offset, sdp->isp_devparam[tgt].goal_period); sdp->isp_devparam[tgt].dev_update = 1; sdp->update = 1; } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: cts = &ccb->cts; tgt = cts->ccb_h.target_id; bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); if (IS_FC(isp)) { fcparam *fcp = FCPARAM(isp, bus); struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_fc *fc = &cts->xport_specific.fc; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_FC; cts->transport_version = 0; scsi->valid = CTS_SCSI_VALID_TQ; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; fc->valid = CTS_FC_VALID_SPEED; fc->bitrate = 100000; fc->bitrate *= fcp->isp_gbspeed; if (tgt > 0 && tgt < MAX_FC_TARG) { fcportdb_t *lp = &fcp->portdb[tgt]; fc->wwnn = lp->node_wwn; fc->wwpn = lp->port_wwn; fc->port = lp->portid; fc->valid |= CTS_FC_VALID_WWNN | CTS_FC_VALID_WWPN | CTS_FC_VALID_PORT; } } else { struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; sdparam *sdp = SDPARAM(isp, bus); uint16_t dval, pval, oval; if (IS_CURRENT_SETTINGS(cts)) { sdp->isp_devparam[tgt].dev_refresh = 1; sdp->update = 1; (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, bus); dval = sdp->isp_devparam[tgt].actv_flags; oval = sdp->isp_devparam[tgt].actv_offset; pval = sdp->isp_devparam[tgt].actv_period; } else { dval = sdp->isp_devparam[tgt].nvrm_flags; oval = sdp->isp_devparam[tgt].nvrm_offset; pval = sdp->isp_devparam[tgt].nvrm_period; } cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; spi->valid = 0; scsi->valid = 0; spi->flags = 0; scsi->flags = 0; if (dval & DPARM_DISC) { spi->flags |= CTS_SPI_FLAGS_DISC_ENB; } if ((dval & DPARM_SYNC) && oval && pval) { spi->sync_offset = oval; spi->sync_period = pval; } else { spi->sync_offset = 0; spi->sync_period = 0; } spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; spi->valid |= CTS_SPI_VALID_SYNC_RATE; spi->valid |= CTS_SPI_VALID_BUS_WIDTH; if (dval & DPARM_WIDE) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; if (dval & DPARM_TQING) { scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } spi->valid |= CTS_SPI_VALID_DISC; } isp_prt(isp, ISP_LOGDEBUG0, "GET %s (%d.%d.%d) to flags %x off %x per %x", IS_CURRENT_SETTINGS(cts)? "ACTIVE" : "NVRAM", bus, tgt, cts->ccb_h.target_lun, dval, oval, pval); } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: cam_calc_geometry(&ccb->ccg, 1); xpt_done(ccb); break; case XPT_RESET_BUS: /* Reset the specified bus */ bus = cam_sim_bus(sim); error = isp_control(isp, ISPCTL_RESET_BUS, bus); if (error) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); break; } if (bootverbose) { xpt_print(ccb->ccb_h.path, "reset bus on channel %d\n", bus); } if (IS_FC(isp)) { xpt_async(AC_BUS_RESET, ISP_FC_PC(isp, bus)->path, 0); } else { xpt_async(AC_BUS_RESET, ISP_SPI_PC(isp, bus)->path, 0); } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_TERM_IO: /* Terminate the I/O process */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_SIM_KNOB: /* Set SIM knobs */ { struct ccb_sim_knob *kp = &ccb->knob; fcparam *fcp; if (!IS_FC(isp)) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } bus = cam_sim_bus(xpt_path_sim(kp->ccb_h.path)); fcp = FCPARAM(isp, bus); if (kp->xport_specific.fc.valid & KNOB_VALID_ADDRESS) { fcp->isp_wwnn = ISP_FC_PC(isp, bus)->def_wwnn = kp->xport_specific.fc.wwnn; fcp->isp_wwpn = ISP_FC_PC(isp, bus)->def_wwpn = kp->xport_specific.fc.wwpn; isp_prt(isp, ISP_LOGALL, "Setting Channel %d wwns to 0x%jx 0x%jx", bus, fcp->isp_wwnn, fcp->isp_wwpn); } ccb->ccb_h.status = CAM_REQ_CMP; if (kp->xport_specific.fc.valid & KNOB_VALID_ROLE) { int rchange = 0; int newrole = 0; switch (kp->xport_specific.fc.role) { case KNOB_ROLE_NONE: if (fcp->role != ISP_ROLE_NONE) { rchange = 1; newrole = ISP_ROLE_NONE; } break; case KNOB_ROLE_TARGET: if (fcp->role != ISP_ROLE_TARGET) { rchange = 1; newrole = ISP_ROLE_TARGET; } break; case KNOB_ROLE_INITIATOR: if (fcp->role != ISP_ROLE_INITIATOR) { rchange = 1; newrole = ISP_ROLE_INITIATOR; } break; case KNOB_ROLE_BOTH: #if 0 if (fcp->role != ISP_ROLE_BOTH) { rchange = 1; newrole = ISP_ROLE_BOTH; } #else /* * We don't really support dual role at present on FC cards. * * We should, but a bunch of things are currently broken, * so don't allow it. */ isp_prt(isp, ISP_LOGERR, "cannot support dual role at present"); ccb->ccb_h.status = CAM_REQ_INVALID; #endif break; } if (rchange) { if (isp_fc_change_role(isp, bus, newrole) != 0) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; #ifdef ISP_TARGET_MODE } else if (newrole == ISP_ROLE_TARGET || newrole == ISP_ROLE_BOTH) { isp_enable_deferred_luns(isp, bus); #endif } } } xpt_done(ccb); break; } case XPT_GET_SIM_KNOB: /* Set SIM knobs */ { struct ccb_sim_knob *kp = &ccb->knob; if (IS_FC(isp)) { fcparam *fcp; bus = cam_sim_bus(xpt_path_sim(kp->ccb_h.path)); fcp = FCPARAM(isp, bus); kp->xport_specific.fc.wwnn = fcp->isp_wwnn; kp->xport_specific.fc.wwpn = fcp->isp_wwpn; switch (fcp->role) { case ISP_ROLE_NONE: kp->xport_specific.fc.role = KNOB_ROLE_NONE; break; case ISP_ROLE_TARGET: kp->xport_specific.fc.role = KNOB_ROLE_TARGET; break; case ISP_ROLE_INITIATOR: kp->xport_specific.fc.role = KNOB_ROLE_INITIATOR; break; case ISP_ROLE_BOTH: kp->xport_specific.fc.role = KNOB_ROLE_BOTH; break; } kp->xport_specific.fc.valid = KNOB_VALID_ADDRESS | KNOB_VALID_ROLE; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->ccb_h.status = CAM_REQ_INVALID; } xpt_done(ccb); break; } case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; #ifdef ISP_TARGET_MODE cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; #else cpi->target_sprt = 0; #endif cpi->hba_eng_cnt = 0; cpi->max_target = ISP_MAX_TARGETS(isp) - 1; cpi->max_lun = ISP_MAX_LUNS(isp) - 1; cpi->bus_id = cam_sim_bus(sim); bus = cam_sim_bus(xpt_path_sim(cpi->ccb_h.path)); if (IS_FC(isp)) { fcparam *fcp = FCPARAM(isp, bus); cpi->hba_misc = PIM_NOBUSRESET; /* * Because our loop ID can shift from time to time, * make our initiator ID out of range of our bus. */ cpi->initiator_id = cpi->max_target + 1; /* * Set base transfer capabilities for Fibre Channel, for this HBA. */ if (IS_24XX(isp)) { cpi->base_transfer_speed = 4000000; } else if (IS_23XX(isp)) { cpi->base_transfer_speed = 2000000; } else { cpi->base_transfer_speed = 1000000; } cpi->hba_inquiry = PI_TAG_ABLE; cpi->transport = XPORT_FC; cpi->transport_version = 0; cpi->xport_specific.fc.wwnn = fcp->isp_wwnn; cpi->xport_specific.fc.wwpn = fcp->isp_wwpn; cpi->xport_specific.fc.port = fcp->isp_portid; cpi->xport_specific.fc.bitrate = fcp->isp_gbspeed * 1000; } else { sdparam *sdp = SDPARAM(isp, bus); cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->hba_misc = 0; cpi->initiator_id = sdp->isp_initiator_id; cpi->base_transfer_speed = 3300; cpi->transport = XPORT_SPI; cpi->transport_version = 2; } cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } #define ISPDDB (CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB) void isp_done(XS_T *sccb) { ispsoftc_t *isp = XS_ISP(sccb); if (XS_NOERR(sccb)) XS_SETERR(sccb, CAM_REQ_CMP); if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (sccb->scsi_status != SCSI_STATUS_OK)) { sccb->ccb_h.status &= ~CAM_STATUS_MASK; if ((sccb->scsi_status == SCSI_STATUS_CHECK_COND) && (sccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0) { sccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; } else { sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; } } sccb->ccb_h.status &= ~CAM_SIM_QUEUED; if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { isp_prt(isp, ISP_LOGDEBUG0, "target %d lun %d CAM status 0x%x SCSI status 0x%x", XS_TGT(sccb), XS_LUN(sccb), sccb->ccb_h.status, sccb->scsi_status); if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { sccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(sccb->ccb_h.path, 1); } } if ((CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB)) && (sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { xpt_print(sccb->ccb_h.path, "cam completion status 0x%x\n", sccb->ccb_h.status); } XS_CMD_S_DONE(sccb); callout_stop(&PISP_PCMD(sccb)->wdog); XS_CMD_S_CLEAR(sccb); isp_free_pcmd(isp, (union ccb *) sccb); xpt_done((union ccb *) sccb); } void isp_async(ispsoftc_t *isp, ispasync_t cmd, ...) { int bus; static const char prom[] = "Chan %d PortID 0x%06x handle 0x%x role %s %s WWPN 0x%08x%08x"; static const char prom2[] = "Chan %d PortID 0x%06x handle 0x%x role %s %s tgt %u WWPN 0x%08x%08x"; char *msg = NULL; target_id_t tgt; fcportdb_t *lp; struct isp_fc *fc; struct cam_path *tmppath; va_list ap; switch (cmd) { case ISPASYNC_NEW_TGT_PARAMS: { struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; int flags, tgt; sdparam *sdp; struct ccb_trans_settings cts; memset(&cts, 0, sizeof (struct ccb_trans_settings)); va_start(ap, cmd); bus = va_arg(ap, int); tgt = va_arg(ap, int); va_end(ap); sdp = SDPARAM(isp, bus); if (xpt_create_path(&tmppath, NULL, cam_sim_path(ISP_SPI_PC(isp, bus)->sim), tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { isp_prt(isp, ISP_LOGWARN, "isp_async cannot make temp path for %d.%d", tgt, bus); break; } flags = sdp->isp_devparam[tgt].actv_flags; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.protocol = PROTO_SCSI; cts.transport = XPORT_SPI; scsi = &cts.proto_specific.scsi; spi = &cts.xport_specific.spi; if (flags & DPARM_TQING) { scsi->valid |= CTS_SCSI_VALID_TQ; scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } if (flags & DPARM_DISC) { spi->valid |= CTS_SPI_VALID_DISC; spi->flags |= CTS_SPI_FLAGS_DISC_ENB; } spi->flags |= CTS_SPI_VALID_BUS_WIDTH; if (flags & DPARM_WIDE) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } if (flags & DPARM_SYNC) { spi->valid |= CTS_SPI_VALID_SYNC_RATE; spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; spi->sync_period = sdp->isp_devparam[tgt].actv_period; spi->sync_offset = sdp->isp_devparam[tgt].actv_offset; } isp_prt(isp, ISP_LOGDEBUG2, "NEW_TGT_PARAMS bus %d tgt %d period %x offset %x flags %x", bus, tgt, sdp->isp_devparam[tgt].actv_period, sdp->isp_devparam[tgt].actv_offset, flags); xpt_setup_ccb(&cts.ccb_h, tmppath, 1); xpt_async(AC_TRANSFER_NEG, tmppath, &cts); xpt_free_path(tmppath); break; } case ISPASYNC_BUS_RESET: { va_start(ap, cmd); bus = va_arg(ap, int); va_end(ap); isp_prt(isp, ISP_LOGINFO, "SCSI bus reset on bus %d detected", bus); if (IS_FC(isp)) { xpt_async(AC_BUS_RESET, ISP_FC_PC(isp, bus)->path, NULL); } else { xpt_async(AC_BUS_RESET, ISP_SPI_PC(isp, bus)->path, NULL); } break; } case ISPASYNC_LIP: if (msg == NULL) { msg = "LIP Received"; } /* FALLTHROUGH */ case ISPASYNC_LOOP_RESET: if (msg == NULL) { msg = "LOOP Reset"; } /* FALLTHROUGH */ case ISPASYNC_LOOP_DOWN: { if (msg == NULL) { msg = "LOOP Down"; } va_start(ap, cmd); bus = va_arg(ap, int); va_end(ap); FCPARAM(isp, bus)->link_active = 0; fc = ISP_FC_PC(isp, bus); if (cmd == ISPASYNC_LOOP_DOWN && fc->ready) { /* * We don't do any simq freezing if we are only in target mode */ if (fc->role & ISP_ROLE_INITIATOR) { if (fc->path) { isp_freeze_loopdown(isp, bus, msg); } if (!callout_active(&fc->ldt)) { callout_reset(&fc->ldt, fc->loop_down_limit * hz, isp_ldt, fc); isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "starting Loop Down Timer @ %lu", (unsigned long) time_uptime); } } } isp_prt(isp, ISP_LOGINFO, "Chan %d: %s", bus, msg); break; } case ISPASYNC_LOOP_UP: va_start(ap, cmd); bus = va_arg(ap, int); va_end(ap); fc = ISP_FC_PC(isp, bus); /* * Now we just note that Loop has come up. We don't * actually do anything because we're waiting for a * Change Notify before activating the FC cleanup * thread to look at the state of the loop again. */ FCPARAM(isp, bus)->link_active = 1; fc->loop_dead = 0; fc->loop_down_time = 0; isp_prt(isp, ISP_LOGINFO, "Chan %d Loop UP", bus); break; case ISPASYNC_DEV_ARRIVED: va_start(ap, cmd); bus = va_arg(ap, int); lp = va_arg(ap, fcportdb_t *); va_end(ap); fc = ISP_FC_PC(isp, bus); lp->reserved = 0; if ((fc->role & ISP_ROLE_INITIATOR) && (lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT))) { int dbidx = lp - FCPARAM(isp, bus)->portdb; int i; for (i = 0; i < MAX_FC_TARG; i++) { if (i >= FL_ID && i <= SNS_ID) { continue; } if (FCPARAM(isp, bus)->isp_dev_map[i] == 0) { break; } } if (i < MAX_FC_TARG) { FCPARAM(isp, bus)->isp_dev_map[i] = dbidx + 1; lp->dev_map_idx = i + 1; } else { isp_prt(isp, ISP_LOGWARN, "out of target ids"); isp_dump_portdb(isp, bus); } } if (lp->dev_map_idx) { tgt = lp->dev_map_idx - 1; isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "arrived at", tgt, (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); isp_make_here(isp, bus, tgt); } else { isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "arrived", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } break; case ISPASYNC_DEV_CHANGED: va_start(ap, cmd); bus = va_arg(ap, int); lp = va_arg(ap, fcportdb_t *); va_end(ap); fc = ISP_FC_PC(isp, bus); lp->reserved = 0; if (isp_change_is_bad) { lp->state = FC_PORTDB_STATE_NIL; if (lp->dev_map_idx) { tgt = lp->dev_map_idx - 1; FCPARAM(isp, bus)->isp_dev_map[tgt] = 0; lp->dev_map_idx = 0; isp_prt(isp, ISP_LOGCONFIG, prom3, bus, lp->portid, tgt, "change is bad"); isp_make_gone(isp, bus, tgt); } else { isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "changed and departed", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } } else { lp->portid = lp->new_portid; lp->roles = lp->new_roles; if (lp->dev_map_idx) { int t = lp->dev_map_idx - 1; FCPARAM(isp, bus)->isp_dev_map[t] = (lp - FCPARAM(isp, bus)->portdb) + 1; tgt = lp->dev_map_idx - 1; isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "changed at", tgt, (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } else { isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "changed", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } } break; case ISPASYNC_DEV_STAYED: va_start(ap, cmd); bus = va_arg(ap, int); lp = va_arg(ap, fcportdb_t *); va_end(ap); if (lp->dev_map_idx) { tgt = lp->dev_map_idx - 1; isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "stayed at", tgt, (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } else { isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "stayed", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } break; case ISPASYNC_DEV_GONE: va_start(ap, cmd); bus = va_arg(ap, int); lp = va_arg(ap, fcportdb_t *); va_end(ap); fc = ISP_FC_PC(isp, bus); /* * If this has a virtual target and we haven't marked it * that we're going to have isp_gdt tell the OS it's gone, * set the isp_gdt timer running on it. * * If it isn't marked that isp_gdt is going to get rid of it, * announce that it's gone. */ if (lp->dev_map_idx && lp->reserved == 0) { lp->reserved = 1; lp->new_reserved = ISP_FC_PC(isp, bus)->gone_device_time; lp->state = FC_PORTDB_STATE_ZOMBIE; if (fc->ready && !callout_active(&fc->gdt)) { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d starting Gone Device Timer", bus); callout_reset(&fc->gdt, hz, isp_gdt, fc); } tgt = lp->dev_map_idx - 1; isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "gone zombie at", tgt, (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } else if (lp->reserved == 0) { isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "departed", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn); } break; case ISPASYNC_CHANGE_NOTIFY: { char *msg; int evt, nphdl, nlstate, reason; va_start(ap, cmd); bus = va_arg(ap, int); evt = va_arg(ap, int); if (IS_24XX(isp) && evt == ISPASYNC_CHANGE_PDB) { nphdl = va_arg(ap, int); nlstate = va_arg(ap, int); reason = va_arg(ap, int); } else { nphdl = NIL_HANDLE; nlstate = reason = 0; } va_end(ap); fc = ISP_FC_PC(isp, bus); if (evt == ISPASYNC_CHANGE_PDB) { msg = "Chan %d Port Database Changed"; } else if (evt == ISPASYNC_CHANGE_SNS) { msg = "Chan %d Name Server Database Changed"; } else { msg = "Chan %d Other Change Notify"; } /* * If the loop down timer is running, cancel it. */ if (fc->ready && callout_active(&fc->ldt)) { isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Stopping Loop Down Timer @ %lu", (unsigned long) time_uptime); callout_stop(&fc->ldt); } isp_prt(isp, ISP_LOGINFO, msg, bus); if (fc->role & ISP_ROLE_INITIATOR) { isp_freeze_loopdown(isp, bus, msg); } wakeup(fc); break; } #ifdef ISP_TARGET_MODE case ISPASYNC_TARGET_NOTIFY: { isp_notify_t *notify; va_start(ap, cmd); notify = va_arg(ap, isp_notify_t *); va_end(ap); switch (notify->nt_ncode) { case NT_ABORT_TASK: case NT_ABORT_TASK_SET: case NT_CLEAR_ACA: case NT_CLEAR_TASK_SET: case NT_LUN_RESET: case NT_TARGET_RESET: /* * These are task management functions. */ isp_handle_platform_target_tmf(isp, notify); break; case NT_BUS_RESET: case NT_LIP_RESET: case NT_LINK_UP: case NT_LINK_DOWN: /* * No action need be taken here. */ break; case NT_HBA_RESET: isp_del_all_wwn_entries(isp, ISP_NOCHAN); break; case NT_LOGOUT: /* * This is device arrival/departure notification */ isp_handle_platform_target_notify_ack(isp, notify); break; case NT_ARRIVED: { struct ac_contract ac; struct ac_device_changed *fc; ac.contract_number = AC_CONTRACT_DEV_CHG; fc = (struct ac_device_changed *) ac.contract_data; fc->wwpn = notify->nt_wwn; fc->port = notify->nt_sid; fc->target = notify->nt_nphdl; fc->arrived = 1; xpt_async(AC_CONTRACT, ISP_FC_PC(isp, notify->nt_channel)->path, &ac); break; } case NT_DEPARTED: { struct ac_contract ac; struct ac_device_changed *fc; ac.contract_number = AC_CONTRACT_DEV_CHG; fc = (struct ac_device_changed *) ac.contract_data; fc->wwpn = notify->nt_wwn; fc->port = notify->nt_sid; fc->target = notify->nt_nphdl; fc->arrived = 0; xpt_async(AC_CONTRACT, ISP_FC_PC(isp, notify->nt_channel)->path, &ac); break; } default: isp_prt(isp, ISP_LOGALL, "target notify code 0x%x", notify->nt_ncode); isp_handle_platform_target_notify_ack(isp, notify); break; } break; } case ISPASYNC_TARGET_ACTION: { isphdr_t *hp; va_start(ap, cmd); hp = va_arg(ap, isphdr_t *); va_end(ap); switch (hp->rqs_entry_type) { default: isp_prt(isp, ISP_LOGWARN, "%s: unhandled target action 0x%x", __func__, hp->rqs_entry_type); break; case RQSTYPE_NOTIFY: if (IS_SCSI(isp)) { isp_handle_platform_notify_scsi(isp, (in_entry_t *) hp); } else if (IS_24XX(isp)) { isp_handle_platform_notify_24xx(isp, (in_fcentry_24xx_t *) hp); } else { isp_handle_platform_notify_fc(isp, (in_fcentry_t *) hp); } break; case RQSTYPE_ATIO: if (IS_24XX(isp)) { isp_handle_platform_atio7(isp, (at7_entry_t *) hp); } else { isp_handle_platform_atio(isp, (at_entry_t *) hp); } break; case RQSTYPE_ATIO2: isp_handle_platform_atio2(isp, (at2_entry_t *) hp); break; case RQSTYPE_CTIO7: case RQSTYPE_CTIO3: case RQSTYPE_CTIO2: case RQSTYPE_CTIO: isp_handle_platform_ctio(isp, hp); break; case RQSTYPE_ABTS_RCVD: { abts_t *abts = (abts_t *)hp; isp_notify_t notify, *nt = ¬ify; tstate_t *tptr; fcportdb_t *lp; uint16_t chan; uint32_t sid, did; did = (abts->abts_did_hi << 16) | abts->abts_did_lo; sid = (abts->abts_sid_hi << 16) | abts->abts_sid_lo; ISP_MEMZERO(nt, sizeof (isp_notify_t)); nt->nt_hba = isp; nt->nt_did = did; nt->nt_nphdl = abts->abts_nphdl; nt->nt_sid = sid; isp_find_chan_by_did(isp, did, &chan); if (chan == ISP_NOCHAN) { nt->nt_tgt = TGT_ANY; } else { nt->nt_tgt = FCPARAM(isp, chan)->isp_wwpn; if (isp_find_pdb_by_loopid(isp, chan, abts->abts_nphdl, &lp)) { nt->nt_wwn = lp->port_wwn; } else { nt->nt_wwn = INI_ANY; } } /* * Try hard to find the lun for this command. */ tptr = get_lun_statep_from_tag(isp, chan, abts->abts_rxid_task); if (tptr) { nt->nt_lun = xpt_path_lun_id(tptr->owner); rls_lun_statep(isp, tptr); } else { nt->nt_lun = LUN_ANY; } nt->nt_need_ack = 1; nt->nt_tagval = abts->abts_rxid_task; nt->nt_tagval |= (((uint64_t) abts->abts_rxid_abts) << 32); if (abts->abts_rxid_task == ISP24XX_NO_TASK) { isp_prt(isp, ISP_LOGTINFO, "[0x%x] ABTS from N-Port handle 0x%x Port 0x%06x has no task id (rx_id 0x%04x ox_id 0x%04x)", abts->abts_rxid_abts, abts->abts_nphdl, sid, abts->abts_rx_id, abts->abts_ox_id); } else { isp_prt(isp, ISP_LOGTINFO, "[0x%x] ABTS from N-Port handle 0x%x Port 0x%06x for task 0x%x (rx_id 0x%04x ox_id 0x%04x)", abts->abts_rxid_abts, abts->abts_nphdl, sid, abts->abts_rxid_task, abts->abts_rx_id, abts->abts_ox_id); } nt->nt_channel = chan; nt->nt_ncode = NT_ABORT_TASK; nt->nt_lreserved = hp; isp_handle_platform_target_tmf(isp, nt); break; } case RQSTYPE_ENABLE_LUN: case RQSTYPE_MODIFY_LUN: isp_ledone(isp, (lun_entry_t *) hp); break; } break; } #endif case ISPASYNC_FW_CRASH: { uint16_t mbox1, mbox6; mbox1 = ISP_READ(isp, OUTMAILBOX1); if (IS_DUALBUS(isp)) { mbox6 = ISP_READ(isp, OUTMAILBOX6); } else { mbox6 = 0; } isp_prt(isp, ISP_LOGERR, "Internal Firmware Error on bus %d @ RISC Address 0x%x", mbox6, mbox1); mbox1 = isp->isp_osinfo.mbox_sleep_ok; isp->isp_osinfo.mbox_sleep_ok = 0; isp_reinit(isp, 1); isp->isp_osinfo.mbox_sleep_ok = mbox1; isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); break; } default: isp_prt(isp, ISP_LOGERR, "unknown isp_async event %d", cmd); break; } } /* * Locks are held before coming here. */ void isp_uninit(ispsoftc_t *isp) { if (IS_24XX(isp)) { ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_RESET); } else { ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); } ISP_DISABLE_INTS(isp); } /* * When we want to get the 'default' WWNs (when lacking NVRAM), we pick them * up from our platform default (defww{p|n}n) and morph them based upon * channel. * * When we want to get the 'active' WWNs, we get NVRAM WWNs and then morph them * based upon channel. */ uint64_t isp_default_wwn(ispsoftc_t * isp, int chan, int isactive, int iswwnn) { uint64_t seed; struct isp_fc *fc = ISP_FC_PC(isp, chan); /* * If we're asking for a active WWN, the default overrides get * returned, otherwise the NVRAM value is picked. * * If we're asking for a default WWN, we just pick the default override. */ if (isactive) { seed = iswwnn ? fc->def_wwnn : fc->def_wwpn; if (seed) { return (seed); } seed = iswwnn ? FCPARAM(isp, chan)->isp_wwnn_nvram : FCPARAM(isp, chan)->isp_wwpn_nvram; if (seed) { return (seed); } return (0x400000007F000009ull); } else { seed = iswwnn ? fc->def_wwnn : fc->def_wwpn; } /* * For channel zero just return what we have. For either ACIIVE or * DEFAULT cases, we depend on default override of NVRAM values for * channel zero. */ if (chan == 0) { return (seed); } /* * For other channels, we are doing one of three things: * * 1. If what we have now is non-zero, return it. Otherwise we morph * values from channel 0. 2. If we're here for a WWPN we synthesize * it if Channel 0's wwpn has a type 2 NAA. 3. If we're here for a * WWNN we synthesize it if Channel 0's wwnn has a type 2 NAA. */ if (seed) { return (seed); } if (isactive) { seed = iswwnn ? FCPARAM(isp, 0)->isp_wwnn_nvram : FCPARAM(isp, 0)->isp_wwpn_nvram; } else { seed = iswwnn ? ISP_FC_PC(isp, 0)->def_wwnn : ISP_FC_PC(isp, 0)->def_wwpn; } if (((seed >> 60) & 0xf) == 2) { /* * The type 2 NAA fields for QLogic cards appear be laid out * thusly: * * bits 63..60 NAA == 2 bits 59..57 unused/zero bit 56 * port (1) or node (0) WWN distinguishor bit 48 * physical port on dual-port chips (23XX/24XX) * * This is somewhat nutty, particularly since bit 48 is * irrelevant as they assign seperate serial numbers to * different physical ports anyway. * * We'll stick our channel number plus one first into bits * 57..59 and thence into bits 52..55 which allows for 8 bits * of channel which is comfortably more than our maximum * (126) now. */ seed &= ~0x0FF0000000000000ULL; if (iswwnn == 0) { seed |= ((uint64_t) (chan + 1) & 0xf) << 56; seed |= ((uint64_t) ((chan + 1) >> 4) & 0xf) << 52; } } else { seed = 0; } return (seed); } void isp_prt(ispsoftc_t *isp, int level, const char *fmt, ...) { va_list ap; if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) { return; } printf("%s: ", device_get_nameunit(isp->isp_dev)); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } uint64_t isp_nanotime_sub(struct timespec *b, struct timespec *a) { uint64_t elapsed; struct timespec x = *b; timespecsub(&x, a); elapsed = GET_NANOSEC(&x); if (elapsed == 0) elapsed++; return (elapsed); } int isp_mbox_acquire(ispsoftc_t *isp) { if (isp->isp_osinfo.mboxbsy) { return (1); } else { isp->isp_osinfo.mboxcmd_done = 0; isp->isp_osinfo.mboxbsy = 1; return (0); } } void isp_mbox_wait_complete(ispsoftc_t *isp, mbreg_t *mbp) { unsigned int usecs = mbp->timeout; unsigned int max, olim, ilim; if (usecs == 0) { usecs = MBCMD_DEFAULT_TIMEOUT; } max = isp->isp_mbxwrk0 + 1; if (isp->isp_osinfo.mbox_sleep_ok) { unsigned int ms = (usecs + 999) / 1000; isp->isp_osinfo.mbox_sleep_ok = 0; isp->isp_osinfo.mbox_sleeping = 1; for (olim = 0; olim < max; olim++) { msleep(&isp->isp_mbxworkp, &isp->isp_osinfo.lock, PRIBIO, "ispmbx_sleep", isp_mstohz(ms)); if (isp->isp_osinfo.mboxcmd_done) { break; } } isp->isp_osinfo.mbox_sleep_ok = 1; isp->isp_osinfo.mbox_sleeping = 0; } else { for (olim = 0; olim < max; olim++) { for (ilim = 0; ilim < usecs; ilim += 100) { uint32_t isr; uint16_t sema, mbox; if (isp->isp_osinfo.mboxcmd_done) { break; } if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { isp_intr(isp, isr, sema, mbox); if (isp->isp_osinfo.mboxcmd_done) { break; } } ISP_DELAY(100); } if (isp->isp_osinfo.mboxcmd_done) { break; } } } if (isp->isp_osinfo.mboxcmd_done == 0) { isp_prt(isp, ISP_LOGWARN, "%s Mailbox Command (0x%x) Timeout (%uus) (started @ %s:%d)", isp->isp_osinfo.mbox_sleep_ok? "Interrupting" : "Polled", isp->isp_lastmbxcmd, usecs, mbp->func, mbp->lineno); mbp->param[0] = MBOX_TIMEOUT; isp->isp_osinfo.mboxcmd_done = 1; } } void isp_mbox_notify_done(ispsoftc_t *isp) { if (isp->isp_osinfo.mbox_sleeping) { wakeup(&isp->isp_mbxworkp); } isp->isp_osinfo.mboxcmd_done = 1; } void isp_mbox_release(ispsoftc_t *isp) { isp->isp_osinfo.mboxbsy = 0; } int isp_fc_scratch_acquire(ispsoftc_t *isp, int chan) { int ret = 0; if (isp->isp_osinfo.pc.fc[chan].fcbsy) { ret = -1; } else { isp->isp_osinfo.pc.fc[chan].fcbsy = 1; } return (ret); } int isp_mstohz(int ms) { int hz; struct timeval t; t.tv_sec = ms / 1000; t.tv_usec = (ms % 1000) * 1000; hz = tvtohz(&t); if (hz < 0) { hz = 0x7fffffff; } if (hz == 0) { hz = 1; } return (hz); } void isp_platform_intr(void *arg) { ispsoftc_t *isp = arg; uint32_t isr; uint16_t sema, mbox; ISP_LOCK(isp); isp->isp_intcnt++; if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) { isp->isp_intbogus++; } else { isp_intr(isp, isr, sema, mbox); } ISP_UNLOCK(isp); } void isp_common_dmateardown(ispsoftc_t *isp, struct ccb_scsiio *csio, uint32_t hdl) { if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap, BUS_DMASYNC_POSTREAD); } else { bus_dmamap_sync(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap, BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap); } void isp_timer(void *arg) { ispsoftc_t *isp = arg; #ifdef ISP_TARGET_MODE isp_tmcmd_restart(isp); #endif callout_reset(&isp->isp_osinfo.tmo, hz, isp_timer, isp); } Index: projects/ppc64/sys/dev/jme/if_jme.c =================================================================== --- projects/ppc64/sys/dev/jme/if_jme.c (revision 204271) +++ projects/ppc64/sys/dev/jme/if_jme.c (revision 204272) @@ -1,3270 +1,3273 @@ /*- * Copyright (c) 2008, Pyun YongHyeon * 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 unmodified, 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* Define the following to disable printing Rx errors. */ #undef JME_SHOW_ERRORS #define JME_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) MODULE_DEPEND(jme, pci, 1, 1, 1); MODULE_DEPEND(jme, ether, 1, 1, 1); MODULE_DEPEND(jme, miibus, 1, 1, 1); /* Tunables. */ static int msi_disable = 0; static int msix_disable = 0; TUNABLE_INT("hw.jme.msi_disable", &msi_disable); TUNABLE_INT("hw.jme.msix_disable", &msix_disable); /* * Devices supported by this driver. */ static struct jme_dev { uint16_t jme_vendorid; uint16_t jme_deviceid; const char *jme_name; } jme_devs[] = { { VENDORID_JMICRON, DEVICEID_JMC250, "JMicron Inc, JMC250 Gigabit Ethernet" }, { VENDORID_JMICRON, DEVICEID_JMC260, "JMicron Inc, JMC260 Fast Ethernet" }, }; static int jme_miibus_readreg(device_t, int, int); static int jme_miibus_writereg(device_t, int, int, int); static void jme_miibus_statchg(device_t); static void jme_mediastatus(struct ifnet *, struct ifmediareq *); static int jme_mediachange(struct ifnet *); static int jme_probe(device_t); static int jme_eeprom_read_byte(struct jme_softc *, uint8_t, uint8_t *); static int jme_eeprom_macaddr(struct jme_softc *); static void jme_reg_macaddr(struct jme_softc *); static void jme_map_intr_vector(struct jme_softc *); static int jme_attach(device_t); static int jme_detach(device_t); static void jme_sysctl_node(struct jme_softc *); static void jme_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int jme_dma_alloc(struct jme_softc *); static void jme_dma_free(struct jme_softc *); static int jme_shutdown(device_t); static void jme_setlinkspeed(struct jme_softc *); static void jme_setwol(struct jme_softc *); static int jme_suspend(device_t); static int jme_resume(device_t); static int jme_encap(struct jme_softc *, struct mbuf **); static void jme_tx_task(void *, int); static void jme_start(struct ifnet *); static void jme_watchdog(struct jme_softc *); static int jme_ioctl(struct ifnet *, u_long, caddr_t); static void jme_mac_config(struct jme_softc *); static void jme_link_task(void *, int); static int jme_intr(void *); static void jme_int_task(void *, int); static void jme_txeof(struct jme_softc *); static __inline void jme_discard_rxbuf(struct jme_softc *, int); static void jme_rxeof(struct jme_softc *); static int jme_rxintr(struct jme_softc *, int); static void jme_tick(void *); static void jme_reset(struct jme_softc *); static void jme_init(void *); static void jme_init_locked(struct jme_softc *); static void jme_stop(struct jme_softc *); static void jme_stop_tx(struct jme_softc *); static void jme_stop_rx(struct jme_softc *); static int jme_init_rx_ring(struct jme_softc *); static void jme_init_tx_ring(struct jme_softc *); static void jme_init_ssb(struct jme_softc *); static int jme_newbuf(struct jme_softc *, struct jme_rxdesc *); static void jme_set_vlan(struct jme_softc *); static void jme_set_filter(struct jme_softc *); static void jme_stats_clear(struct jme_softc *); static void jme_stats_save(struct jme_softc *); static void jme_stats_update(struct jme_softc *); static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); static int sysctl_hw_jme_tx_coal_to(SYSCTL_HANDLER_ARGS); static int sysctl_hw_jme_tx_coal_pkt(SYSCTL_HANDLER_ARGS); static int sysctl_hw_jme_rx_coal_to(SYSCTL_HANDLER_ARGS); static int sysctl_hw_jme_rx_coal_pkt(SYSCTL_HANDLER_ARGS); static int sysctl_hw_jme_proc_limit(SYSCTL_HANDLER_ARGS); static device_method_t jme_methods[] = { /* Device interface. */ DEVMETHOD(device_probe, jme_probe), DEVMETHOD(device_attach, jme_attach), DEVMETHOD(device_detach, jme_detach), DEVMETHOD(device_shutdown, jme_shutdown), DEVMETHOD(device_suspend, jme_suspend), DEVMETHOD(device_resume, jme_resume), /* MII interface. */ DEVMETHOD(miibus_readreg, jme_miibus_readreg), DEVMETHOD(miibus_writereg, jme_miibus_writereg), DEVMETHOD(miibus_statchg, jme_miibus_statchg), { NULL, NULL } }; static driver_t jme_driver = { "jme", jme_methods, sizeof(struct jme_softc) }; static devclass_t jme_devclass; DRIVER_MODULE(jme, pci, jme_driver, jme_devclass, 0, 0); DRIVER_MODULE(miibus, jme, miibus_driver, miibus_devclass, 0, 0); static struct resource_spec jme_res_spec_mem[] = { { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec jme_irq_spec_legacy[] = { { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0, 0 } }; static struct resource_spec jme_irq_spec_msi[] = { { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { SYS_RES_IRQ, 8, RF_ACTIVE }, { -1, 0, 0 } }; /* * Read a PHY register on the MII of the JMC250. */ static int jme_miibus_readreg(device_t dev, int phy, int reg) { struct jme_softc *sc; uint32_t val; int i; sc = device_get_softc(dev); /* For FPGA version, PHY address 0 should be ignored. */ if ((sc->jme_flags & JME_FLAG_FPGA) != 0) { if (phy == 0) return (0); } else { if (sc->jme_phyaddr != phy) return (0); } CSR_WRITE_4(sc, JME_SMI, SMI_OP_READ | SMI_OP_EXECUTE | SMI_PHY_ADDR(phy) | SMI_REG_ADDR(reg)); for (i = JME_PHY_TIMEOUT; i > 0; i--) { DELAY(1); if (((val = CSR_READ_4(sc, JME_SMI)) & SMI_OP_EXECUTE) == 0) break; } if (i == 0) { device_printf(sc->jme_dev, "phy read timeout : %d\n", reg); return (0); } return ((val & SMI_DATA_MASK) >> SMI_DATA_SHIFT); } /* * Write a PHY register on the MII of the JMC250. */ static int jme_miibus_writereg(device_t dev, int phy, int reg, int val) { struct jme_softc *sc; int i; sc = device_get_softc(dev); /* For FPGA version, PHY address 0 should be ignored. */ if ((sc->jme_flags & JME_FLAG_FPGA) != 0) { if (phy == 0) return (0); } else { if (sc->jme_phyaddr != phy) return (0); } CSR_WRITE_4(sc, JME_SMI, SMI_OP_WRITE | SMI_OP_EXECUTE | ((val << SMI_DATA_SHIFT) & SMI_DATA_MASK) | SMI_PHY_ADDR(phy) | SMI_REG_ADDR(reg)); for (i = JME_PHY_TIMEOUT; i > 0; i--) { DELAY(1); if (((val = CSR_READ_4(sc, JME_SMI)) & SMI_OP_EXECUTE) == 0) break; } if (i == 0) device_printf(sc->jme_dev, "phy write timeout : %d\n", reg); return (0); } /* * Callback from MII layer when media changes. */ static void jme_miibus_statchg(device_t dev) { struct jme_softc *sc; sc = device_get_softc(dev); taskqueue_enqueue(taskqueue_swi, &sc->jme_link_task); } /* * Get the current interface media status. */ static void jme_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { struct jme_softc *sc; struct mii_data *mii; sc = ifp->if_softc; JME_LOCK(sc); if ((ifp->if_flags & IFF_UP) == 0) { JME_UNLOCK(sc); return; } mii = device_get_softc(sc->jme_miibus); mii_pollstat(mii); ifmr->ifm_status = mii->mii_media_status; ifmr->ifm_active = mii->mii_media_active; JME_UNLOCK(sc); } /* * Set hardware to newly-selected media. */ static int jme_mediachange(struct ifnet *ifp) { struct jme_softc *sc; struct mii_data *mii; struct mii_softc *miisc; int error; sc = ifp->if_softc; JME_LOCK(sc); mii = device_get_softc(sc->jme_miibus); if (mii->mii_instance != 0) { LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } error = mii_mediachg(mii); JME_UNLOCK(sc); return (error); } static int jme_probe(device_t dev) { struct jme_dev *sp; int i; uint16_t vendor, devid; vendor = pci_get_vendor(dev); devid = pci_get_device(dev); sp = jme_devs; for (i = 0; i < sizeof(jme_devs) / sizeof(jme_devs[0]); i++, sp++) { if (vendor == sp->jme_vendorid && devid == sp->jme_deviceid) { device_set_desc(dev, sp->jme_name); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } static int jme_eeprom_read_byte(struct jme_softc *sc, uint8_t addr, uint8_t *val) { uint32_t reg; int i; *val = 0; for (i = JME_TIMEOUT; i > 0; i--) { reg = CSR_READ_4(sc, JME_SMBCSR); if ((reg & SMBCSR_HW_BUSY_MASK) == SMBCSR_HW_IDLE) break; DELAY(1); } if (i == 0) { device_printf(sc->jme_dev, "EEPROM idle timeout!\n"); return (ETIMEDOUT); } reg = ((uint32_t)addr << SMBINTF_ADDR_SHIFT) & SMBINTF_ADDR_MASK; CSR_WRITE_4(sc, JME_SMBINTF, reg | SMBINTF_RD | SMBINTF_CMD_TRIGGER); for (i = JME_TIMEOUT; i > 0; i--) { DELAY(1); reg = CSR_READ_4(sc, JME_SMBINTF); if ((reg & SMBINTF_CMD_TRIGGER) == 0) break; } if (i == 0) { device_printf(sc->jme_dev, "EEPROM read timeout!\n"); return (ETIMEDOUT); } reg = CSR_READ_4(sc, JME_SMBINTF); *val = (reg & SMBINTF_RD_DATA_MASK) >> SMBINTF_RD_DATA_SHIFT; return (0); } static int jme_eeprom_macaddr(struct jme_softc *sc) { uint8_t eaddr[ETHER_ADDR_LEN]; uint8_t fup, reg, val; uint32_t offset; int match; offset = 0; if (jme_eeprom_read_byte(sc, offset++, &fup) != 0 || fup != JME_EEPROM_SIG0) return (ENOENT); if (jme_eeprom_read_byte(sc, offset++, &fup) != 0 || fup != JME_EEPROM_SIG1) return (ENOENT); match = 0; do { if (jme_eeprom_read_byte(sc, offset, &fup) != 0) break; if (JME_EEPROM_MKDESC(JME_EEPROM_FUNC0, JME_EEPROM_PAGE_BAR1) == (fup & (JME_EEPROM_FUNC_MASK | JME_EEPROM_PAGE_MASK))) { if (jme_eeprom_read_byte(sc, offset + 1, ®) != 0) break; if (reg >= JME_PAR0 && reg < JME_PAR0 + ETHER_ADDR_LEN) { if (jme_eeprom_read_byte(sc, offset + 2, &val) != 0) break; eaddr[reg - JME_PAR0] = val; match++; } } /* Check for the end of EEPROM descriptor. */ if ((fup & JME_EEPROM_DESC_END) == JME_EEPROM_DESC_END) break; /* Try next eeprom descriptor. */ offset += JME_EEPROM_DESC_BYTES; } while (match != ETHER_ADDR_LEN && offset < JME_EEPROM_END); if (match == ETHER_ADDR_LEN) { bcopy(eaddr, sc->jme_eaddr, ETHER_ADDR_LEN); return (0); } return (ENOENT); } static void jme_reg_macaddr(struct jme_softc *sc) { uint32_t par0, par1; /* Read station address. */ par0 = CSR_READ_4(sc, JME_PAR0); par1 = CSR_READ_4(sc, JME_PAR1); par1 &= 0xFFFF; if ((par0 == 0 && par1 == 0) || (par0 == 0xFFFFFFFF && par1 == 0xFFFF)) { device_printf(sc->jme_dev, "Failed to retrieve Ethernet address.\n"); } else { sc->jme_eaddr[0] = (par0 >> 0) & 0xFF; sc->jme_eaddr[1] = (par0 >> 8) & 0xFF; sc->jme_eaddr[2] = (par0 >> 16) & 0xFF; sc->jme_eaddr[3] = (par0 >> 24) & 0xFF; sc->jme_eaddr[4] = (par1 >> 0) & 0xFF; sc->jme_eaddr[5] = (par1 >> 8) & 0xFF; } } static void jme_map_intr_vector(struct jme_softc *sc) { uint32_t map[MSINUM_NUM_INTR_SOURCE / JME_MSI_MESSAGES]; bzero(map, sizeof(map)); /* Map Tx interrupts source to MSI/MSIX vector 2. */ map[MSINUM_REG_INDEX(N_INTR_TXQ0_COMP)] = MSINUM_INTR_SOURCE(2, N_INTR_TXQ0_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ1_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ1_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ2_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ2_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ3_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ3_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ4_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ4_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ4_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ5_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ6_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ6_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ7_COMP)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ7_COMP); map[MSINUM_REG_INDEX(N_INTR_TXQ_COAL)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ_COAL); map[MSINUM_REG_INDEX(N_INTR_TXQ_COAL_TO)] |= MSINUM_INTR_SOURCE(2, N_INTR_TXQ_COAL_TO); /* Map Rx interrupts source to MSI/MSIX vector 1. */ map[MSINUM_REG_INDEX(N_INTR_RXQ0_COMP)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ0_COMP); map[MSINUM_REG_INDEX(N_INTR_RXQ1_COMP)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ1_COMP); map[MSINUM_REG_INDEX(N_INTR_RXQ2_COMP)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ2_COMP); map[MSINUM_REG_INDEX(N_INTR_RXQ3_COMP)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ3_COMP); map[MSINUM_REG_INDEX(N_INTR_RXQ0_DESC_EMPTY)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ0_DESC_EMPTY); map[MSINUM_REG_INDEX(N_INTR_RXQ1_DESC_EMPTY)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ1_DESC_EMPTY); map[MSINUM_REG_INDEX(N_INTR_RXQ2_DESC_EMPTY)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ2_DESC_EMPTY); map[MSINUM_REG_INDEX(N_INTR_RXQ3_DESC_EMPTY)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ3_DESC_EMPTY); map[MSINUM_REG_INDEX(N_INTR_RXQ0_COAL)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ0_COAL); map[MSINUM_REG_INDEX(N_INTR_RXQ1_COAL)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ1_COAL); map[MSINUM_REG_INDEX(N_INTR_RXQ2_COAL)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ2_COAL); map[MSINUM_REG_INDEX(N_INTR_RXQ3_COAL)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ3_COAL); map[MSINUM_REG_INDEX(N_INTR_RXQ0_COAL_TO)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ0_COAL_TO); map[MSINUM_REG_INDEX(N_INTR_RXQ1_COAL_TO)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ1_COAL_TO); map[MSINUM_REG_INDEX(N_INTR_RXQ2_COAL_TO)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ2_COAL_TO); map[MSINUM_REG_INDEX(N_INTR_RXQ3_COAL_TO)] = MSINUM_INTR_SOURCE(1, N_INTR_RXQ3_COAL_TO); /* Map all other interrupts source to MSI/MSIX vector 0. */ CSR_WRITE_4(sc, JME_MSINUM_BASE + sizeof(uint32_t) * 0, map[0]); CSR_WRITE_4(sc, JME_MSINUM_BASE + sizeof(uint32_t) * 1, map[1]); CSR_WRITE_4(sc, JME_MSINUM_BASE + sizeof(uint32_t) * 2, map[2]); CSR_WRITE_4(sc, JME_MSINUM_BASE + sizeof(uint32_t) * 3, map[3]); } static int jme_attach(device_t dev) { struct jme_softc *sc; struct ifnet *ifp; struct mii_softc *miisc; struct mii_data *mii; uint32_t reg; uint16_t burst; int error, i, msic, msixc, pmc; error = 0; sc = device_get_softc(dev); sc->jme_dev = dev; mtx_init(&sc->jme_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->jme_tick_ch, &sc->jme_mtx, 0); TASK_INIT(&sc->jme_int_task, 0, jme_int_task, sc); TASK_INIT(&sc->jme_link_task, 0, jme_link_task, sc); /* * Map the device. JMC250 supports both memory mapped and I/O * register space access. Because I/O register access should * use different BARs to access registers it's waste of time * to use I/O register spce access. JMC250 uses 16K to map * entire memory space. */ pci_enable_busmaster(dev); sc->jme_res_spec = jme_res_spec_mem; sc->jme_irq_spec = jme_irq_spec_legacy; error = bus_alloc_resources(dev, sc->jme_res_spec, sc->jme_res); if (error != 0) { device_printf(dev, "cannot allocate memory resources.\n"); goto fail; } /* Allocate IRQ resources. */ msixc = pci_msix_count(dev); msic = pci_msi_count(dev); if (bootverbose) { device_printf(dev, "MSIX count : %d\n", msixc); device_printf(dev, "MSI count : %d\n", msic); } /* Prefer MSIX over MSI. */ if (msix_disable == 0 || msi_disable == 0) { if (msix_disable == 0 && msixc == JME_MSIX_MESSAGES && pci_alloc_msix(dev, &msixc) == 0) { if (msic == JME_MSIX_MESSAGES) { device_printf(dev, "Using %d MSIX messages.\n", msixc); sc->jme_flags |= JME_FLAG_MSIX; sc->jme_irq_spec = jme_irq_spec_msi; } else pci_release_msi(dev); } if (msi_disable == 0 && (sc->jme_flags & JME_FLAG_MSIX) == 0 && msic == JME_MSI_MESSAGES && pci_alloc_msi(dev, &msic) == 0) { if (msic == JME_MSI_MESSAGES) { device_printf(dev, "Using %d MSI messages.\n", msic); sc->jme_flags |= JME_FLAG_MSI; sc->jme_irq_spec = jme_irq_spec_msi; } else pci_release_msi(dev); } /* Map interrupt vector 0, 1 and 2. */ if ((sc->jme_flags & JME_FLAG_MSI) != 0 || (sc->jme_flags & JME_FLAG_MSIX) != 0) jme_map_intr_vector(sc); } error = bus_alloc_resources(dev, sc->jme_irq_spec, sc->jme_irq); if (error != 0) { device_printf(dev, "cannot allocate IRQ resources.\n"); goto fail; } sc->jme_rev = pci_get_device(dev); if ((sc->jme_rev & DEVICEID_JMC2XX_MASK) == DEVICEID_JMC260) { sc->jme_flags |= JME_FLAG_FASTETH; sc->jme_flags |= JME_FLAG_NOJUMBO; } reg = CSR_READ_4(sc, JME_CHIPMODE); sc->jme_chip_rev = (reg & CHIPMODE_REV_MASK) >> CHIPMODE_REV_SHIFT; if (((reg & CHIPMODE_FPGA_REV_MASK) >> CHIPMODE_FPGA_REV_SHIFT) != CHIPMODE_NOT_FPGA) sc->jme_flags |= JME_FLAG_FPGA; if (bootverbose) { device_printf(dev, "PCI device revision : 0x%04x\n", sc->jme_rev); device_printf(dev, "Chip revision : 0x%02x\n", sc->jme_chip_rev); if ((sc->jme_flags & JME_FLAG_FPGA) != 0) device_printf(dev, "FPGA revision : 0x%04x\n", (reg & CHIPMODE_FPGA_REV_MASK) >> CHIPMODE_FPGA_REV_SHIFT); } if (sc->jme_chip_rev == 0xFF) { device_printf(dev, "Unknown chip revision : 0x%02x\n", sc->jme_rev); error = ENXIO; goto fail; } if (CHIPMODE_REVFM(sc->jme_chip_rev) >= 2) { if ((sc->jme_rev & DEVICEID_JMC2XX_MASK) == DEVICEID_JMC260 && CHIPMODE_REVFM(sc->jme_chip_rev) == 2) sc->jme_flags |= JME_FLAG_DMA32BIT; sc->jme_flags |= JME_FLAG_TXCLK; sc->jme_flags |= JME_FLAG_HWMIB; } /* Reset the ethernet controller. */ jme_reset(sc); /* Get station address. */ reg = CSR_READ_4(sc, JME_SMBCSR); if ((reg & SMBCSR_EEPROM_PRESENT) != 0) error = jme_eeprom_macaddr(sc); if (error != 0 || (reg & SMBCSR_EEPROM_PRESENT) == 0) { if (error != 0 && (bootverbose)) device_printf(sc->jme_dev, "ethernet hardware address not found in EEPROM.\n"); jme_reg_macaddr(sc); } /* * Save PHY address. * Integrated JR0211 has fixed PHY address whereas FPGA version * requires PHY probing to get correct PHY address. */ if ((sc->jme_flags & JME_FLAG_FPGA) == 0) { sc->jme_phyaddr = CSR_READ_4(sc, JME_GPREG0) & GPREG0_PHY_ADDR_MASK; if (bootverbose) device_printf(dev, "PHY is at address %d.\n", sc->jme_phyaddr); } else sc->jme_phyaddr = 0; /* Set max allowable DMA size. */ if (pci_find_extcap(dev, PCIY_EXPRESS, &i) == 0) { sc->jme_flags |= JME_FLAG_PCIE; burst = pci_read_config(dev, i + 0x08, 2); if (bootverbose) { device_printf(dev, "Read request size : %d bytes.\n", 128 << ((burst >> 12) & 0x07)); device_printf(dev, "TLP payload size : %d bytes.\n", 128 << ((burst >> 5) & 0x07)); } switch ((burst >> 12) & 0x07) { case 0: sc->jme_tx_dma_size = TXCSR_DMA_SIZE_128; break; case 1: sc->jme_tx_dma_size = TXCSR_DMA_SIZE_256; break; default: sc->jme_tx_dma_size = TXCSR_DMA_SIZE_512; break; } sc->jme_rx_dma_size = RXCSR_DMA_SIZE_128; } else { sc->jme_tx_dma_size = TXCSR_DMA_SIZE_512; sc->jme_rx_dma_size = RXCSR_DMA_SIZE_128; } /* Create coalescing sysctl node. */ jme_sysctl_node(sc); if ((error = jme_dma_alloc(sc) != 0)) goto fail; ifp = sc->jme_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "cannot allocate ifnet structure.\n"); error = ENXIO; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = jme_ioctl; ifp->if_start = jme_start; ifp->if_init = jme_init; ifp->if_snd.ifq_drv_maxlen = JME_TX_RING_CNT - 1; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); /* JMC250 supports Tx/Rx checksum offload as well as TSO. */ ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_TSO4; ifp->if_hwassist = JME_CSUM_FEATURES | CSUM_TSO; if (pci_find_extcap(dev, PCIY_PMG, &pmc) == 0) { sc->jme_flags |= JME_FLAG_PMCAP; ifp->if_capabilities |= IFCAP_WOL_MAGIC; } ifp->if_capenable = ifp->if_capabilities; /* Set up MII bus. */ if ((error = mii_phy_probe(dev, &sc->jme_miibus, jme_mediachange, jme_mediastatus)) != 0) { device_printf(dev, "no PHY found!\n"); goto fail; } /* * Force PHY to FPGA mode. */ if ((sc->jme_flags & JME_FLAG_FPGA) != 0) { mii = device_get_softc(sc->jme_miibus); if (mii->mii_instance != 0) { LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { if (miisc->mii_phy != 0) { sc->jme_phyaddr = miisc->mii_phy; break; } } if (sc->jme_phyaddr != 0) { device_printf(sc->jme_dev, "FPGA PHY is at %d\n", sc->jme_phyaddr); /* vendor magic. */ jme_miibus_writereg(dev, sc->jme_phyaddr, 27, 0x0004); } } } ether_ifattach(ifp, sc->jme_eaddr); /* VLAN capability setup */ ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | - IFCAP_VLAN_HWCSUM; + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO; ifp->if_capenable = ifp->if_capabilities; /* Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); /* Create local taskq. */ TASK_INIT(&sc->jme_tx_task, 1, jme_tx_task, ifp); sc->jme_tq = taskqueue_create_fast("jme_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->jme_tq); if (sc->jme_tq == NULL) { device_printf(dev, "could not create taskqueue.\n"); ether_ifdetach(ifp); error = ENXIO; goto fail; } taskqueue_start_threads(&sc->jme_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->jme_dev)); if ((sc->jme_flags & JME_FLAG_MSIX) != 0) msic = JME_MSIX_MESSAGES; else if ((sc->jme_flags & JME_FLAG_MSI) != 0) msic = JME_MSI_MESSAGES; else msic = 1; for (i = 0; i < msic; i++) { error = bus_setup_intr(dev, sc->jme_irq[i], INTR_TYPE_NET | INTR_MPSAFE, jme_intr, NULL, sc, &sc->jme_intrhand[i]); if (error != 0) break; } if (error != 0) { device_printf(dev, "could not set up interrupt handler.\n"); taskqueue_free(sc->jme_tq); sc->jme_tq = NULL; ether_ifdetach(ifp); goto fail; } fail: if (error != 0) jme_detach(dev); return (error); } static int jme_detach(device_t dev) { struct jme_softc *sc; struct ifnet *ifp; int i, msic; sc = device_get_softc(dev); ifp = sc->jme_ifp; if (device_is_attached(dev)) { JME_LOCK(sc); sc->jme_flags |= JME_FLAG_DETACH; jme_stop(sc); JME_UNLOCK(sc); callout_drain(&sc->jme_tick_ch); taskqueue_drain(sc->jme_tq, &sc->jme_int_task); taskqueue_drain(sc->jme_tq, &sc->jme_tx_task); taskqueue_drain(taskqueue_swi, &sc->jme_link_task); ether_ifdetach(ifp); } if (sc->jme_tq != NULL) { taskqueue_drain(sc->jme_tq, &sc->jme_int_task); taskqueue_free(sc->jme_tq); sc->jme_tq = NULL; } if (sc->jme_miibus != NULL) { device_delete_child(dev, sc->jme_miibus); sc->jme_miibus = NULL; } bus_generic_detach(dev); jme_dma_free(sc); if (ifp != NULL) { if_free(ifp); sc->jme_ifp = NULL; } msic = 1; if ((sc->jme_flags & JME_FLAG_MSIX) != 0) msic = JME_MSIX_MESSAGES; else if ((sc->jme_flags & JME_FLAG_MSI) != 0) msic = JME_MSI_MESSAGES; else msic = 1; for (i = 0; i < msic; i++) { if (sc->jme_intrhand[i] != NULL) { bus_teardown_intr(dev, sc->jme_irq[i], sc->jme_intrhand[i]); sc->jme_intrhand[i] = NULL; } } bus_release_resources(dev, sc->jme_irq_spec, sc->jme_irq); if ((sc->jme_flags & (JME_FLAG_MSIX | JME_FLAG_MSI)) != 0) pci_release_msi(dev); bus_release_resources(dev, sc->jme_res_spec, sc->jme_res); mtx_destroy(&sc->jme_mtx); return (0); } #define JME_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) static void jme_sysctl_node(struct jme_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child, *parent; struct sysctl_oid *tree; struct jme_hw_stats *stats; int error; stats = &sc->jme_stats; ctx = device_get_sysctl_ctx(sc->jme_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->jme_dev)); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_coal_to", CTLTYPE_INT | CTLFLAG_RW, &sc->jme_tx_coal_to, 0, sysctl_hw_jme_tx_coal_to, "I", "jme tx coalescing timeout"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_coal_pkt", CTLTYPE_INT | CTLFLAG_RW, &sc->jme_tx_coal_pkt, 0, sysctl_hw_jme_tx_coal_pkt, "I", "jme tx coalescing packet"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_coal_to", CTLTYPE_INT | CTLFLAG_RW, &sc->jme_rx_coal_to, 0, sysctl_hw_jme_rx_coal_to, "I", "jme rx coalescing timeout"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_coal_pkt", CTLTYPE_INT | CTLFLAG_RW, &sc->jme_rx_coal_pkt, 0, sysctl_hw_jme_rx_coal_pkt, "I", "jme rx coalescing packet"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "process_limit", CTLTYPE_INT | CTLFLAG_RW, &sc->jme_process_limit, 0, sysctl_hw_jme_proc_limit, "I", "max number of Rx events to process"); /* Pull in device tunables. */ sc->jme_process_limit = JME_PROC_DEFAULT; error = resource_int_value(device_get_name(sc->jme_dev), device_get_unit(sc->jme_dev), "process_limit", &sc->jme_process_limit); if (error == 0) { if (sc->jme_process_limit < JME_PROC_MIN || sc->jme_process_limit > JME_PROC_MAX) { device_printf(sc->jme_dev, "process_limit value out of range; " "using default: %d\n", JME_PROC_DEFAULT); sc->jme_process_limit = JME_PROC_DEFAULT; } } sc->jme_tx_coal_to = PCCTX_COAL_TO_DEFAULT; error = resource_int_value(device_get_name(sc->jme_dev), device_get_unit(sc->jme_dev), "tx_coal_to", &sc->jme_tx_coal_to); if (error == 0) { if (sc->jme_tx_coal_to < PCCTX_COAL_TO_MIN || sc->jme_tx_coal_to > PCCTX_COAL_TO_MAX) { device_printf(sc->jme_dev, "tx_coal_to value out of range; " "using default: %d\n", PCCTX_COAL_TO_DEFAULT); sc->jme_tx_coal_to = PCCTX_COAL_TO_DEFAULT; } } sc->jme_tx_coal_pkt = PCCTX_COAL_PKT_DEFAULT; error = resource_int_value(device_get_name(sc->jme_dev), device_get_unit(sc->jme_dev), "tx_coal_pkt", &sc->jme_tx_coal_to); if (error == 0) { if (sc->jme_tx_coal_pkt < PCCTX_COAL_PKT_MIN || sc->jme_tx_coal_pkt > PCCTX_COAL_PKT_MAX) { device_printf(sc->jme_dev, "tx_coal_pkt value out of range; " "using default: %d\n", PCCTX_COAL_PKT_DEFAULT); sc->jme_tx_coal_pkt = PCCTX_COAL_PKT_DEFAULT; } } sc->jme_rx_coal_to = PCCRX_COAL_TO_DEFAULT; error = resource_int_value(device_get_name(sc->jme_dev), device_get_unit(sc->jme_dev), "rx_coal_to", &sc->jme_rx_coal_to); if (error == 0) { if (sc->jme_rx_coal_to < PCCRX_COAL_TO_MIN || sc->jme_rx_coal_to > PCCRX_COAL_TO_MAX) { device_printf(sc->jme_dev, "rx_coal_to value out of range; " "using default: %d\n", PCCRX_COAL_TO_DEFAULT); sc->jme_rx_coal_to = PCCRX_COAL_TO_DEFAULT; } } sc->jme_rx_coal_pkt = PCCRX_COAL_PKT_DEFAULT; error = resource_int_value(device_get_name(sc->jme_dev), device_get_unit(sc->jme_dev), "rx_coal_pkt", &sc->jme_rx_coal_to); if (error == 0) { if (sc->jme_rx_coal_pkt < PCCRX_COAL_PKT_MIN || sc->jme_rx_coal_pkt > PCCRX_COAL_PKT_MAX) { device_printf(sc->jme_dev, "tx_coal_pkt value out of range; " "using default: %d\n", PCCRX_COAL_PKT_DEFAULT); sc->jme_rx_coal_pkt = PCCRX_COAL_PKT_DEFAULT; } } if ((sc->jme_flags & JME_FLAG_HWMIB) == 0) return; tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, NULL, "JME statistics"); parent = SYSCTL_CHILDREN(tree); /* Rx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, NULL, "Rx MAC statistics"); child = SYSCTL_CHILDREN(tree); JME_SYSCTL_STAT_ADD32(ctx, child, "good_frames", &stats->rx_good_frames, "Good frames"); JME_SYSCTL_STAT_ADD32(ctx, child, "crc_errs", &stats->rx_crc_errs, "CRC errors"); JME_SYSCTL_STAT_ADD32(ctx, child, "mii_errs", &stats->rx_mii_errs, "MII errors"); JME_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows", &stats->rx_fifo_oflows, "FIFO overflows"); JME_SYSCTL_STAT_ADD32(ctx, child, "desc_empty", &stats->rx_desc_empty, "Descriptor empty"); JME_SYSCTL_STAT_ADD32(ctx, child, "bad_frames", &stats->rx_bad_frames, "Bad frames"); /* Tx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "Tx MAC statistics"); child = SYSCTL_CHILDREN(tree); JME_SYSCTL_STAT_ADD32(ctx, child, "good_frames", &stats->tx_good_frames, "Good frames"); JME_SYSCTL_STAT_ADD32(ctx, child, "bad_frames", &stats->tx_bad_frames, "Bad frames"); } #undef JME_SYSCTL_STAT_ADD32 struct jme_dmamap_arg { bus_addr_t jme_busaddr; }; static void jme_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct jme_dmamap_arg *ctx; if (error != 0) return; KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); ctx = (struct jme_dmamap_arg *)arg; ctx->jme_busaddr = segs[0].ds_addr; } static int jme_dma_alloc(struct jme_softc *sc) { struct jme_dmamap_arg ctx; struct jme_txdesc *txd; struct jme_rxdesc *rxd; bus_addr_t lowaddr, rx_ring_end, tx_ring_end; int error, i; lowaddr = BUS_SPACE_MAXADDR; if ((sc->jme_flags & JME_FLAG_DMA32BIT) != 0) lowaddr = BUS_SPACE_MAXADDR_32BIT; again: /* Create parent ring tag. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->jme_dev),/* parent */ 1, 0, /* algnmnt, boundary */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 0, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_ring_tag); if (error != 0) { device_printf(sc->jme_dev, "could not create parent ring DMA tag.\n"); goto fail; } /* Create tag for Tx ring. */ error = bus_dma_tag_create(sc->jme_cdata.jme_ring_tag,/* parent */ JME_TX_RING_ALIGN, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ JME_TX_RING_SIZE, /* maxsize */ 1, /* nsegments */ JME_TX_RING_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_tx_ring_tag); if (error != 0) { device_printf(sc->jme_dev, "could not allocate Tx ring DMA tag.\n"); goto fail; } /* Create tag for Rx ring. */ error = bus_dma_tag_create(sc->jme_cdata.jme_ring_tag,/* parent */ JME_RX_RING_ALIGN, 0, /* algnmnt, boundary */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ JME_RX_RING_SIZE, /* maxsize */ 1, /* nsegments */ JME_RX_RING_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_rx_ring_tag); if (error != 0) { device_printf(sc->jme_dev, "could not allocate Rx ring DMA tag.\n"); goto fail; } /* Allocate DMA'able memory and load the DMA map for Tx ring. */ error = bus_dmamem_alloc(sc->jme_cdata.jme_tx_ring_tag, (void **)&sc->jme_rdata.jme_tx_ring, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->jme_cdata.jme_tx_ring_map); if (error != 0) { device_printf(sc->jme_dev, "could not allocate DMA'able memory for Tx ring.\n"); goto fail; } ctx.jme_busaddr = 0; error = bus_dmamap_load(sc->jme_cdata.jme_tx_ring_tag, sc->jme_cdata.jme_tx_ring_map, sc->jme_rdata.jme_tx_ring, JME_TX_RING_SIZE, jme_dmamap_cb, &ctx, BUS_DMA_NOWAIT); if (error != 0 || ctx.jme_busaddr == 0) { device_printf(sc->jme_dev, "could not load DMA'able memory for Tx ring.\n"); goto fail; } sc->jme_rdata.jme_tx_ring_paddr = ctx.jme_busaddr; /* Allocate DMA'able memory and load the DMA map for Rx ring. */ error = bus_dmamem_alloc(sc->jme_cdata.jme_rx_ring_tag, (void **)&sc->jme_rdata.jme_rx_ring, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->jme_cdata.jme_rx_ring_map); if (error != 0) { device_printf(sc->jme_dev, "could not allocate DMA'able memory for Rx ring.\n"); goto fail; } ctx.jme_busaddr = 0; error = bus_dmamap_load(sc->jme_cdata.jme_rx_ring_tag, sc->jme_cdata.jme_rx_ring_map, sc->jme_rdata.jme_rx_ring, JME_RX_RING_SIZE, jme_dmamap_cb, &ctx, BUS_DMA_NOWAIT); if (error != 0 || ctx.jme_busaddr == 0) { device_printf(sc->jme_dev, "could not load DMA'able memory for Rx ring.\n"); goto fail; } sc->jme_rdata.jme_rx_ring_paddr = ctx.jme_busaddr; if (lowaddr != BUS_SPACE_MAXADDR_32BIT) { /* Tx/Rx descriptor queue should reside within 4GB boundary. */ tx_ring_end = sc->jme_rdata.jme_tx_ring_paddr + JME_TX_RING_SIZE; rx_ring_end = sc->jme_rdata.jme_rx_ring_paddr + JME_RX_RING_SIZE; if ((JME_ADDR_HI(tx_ring_end) != JME_ADDR_HI(sc->jme_rdata.jme_tx_ring_paddr)) || (JME_ADDR_HI(rx_ring_end) != JME_ADDR_HI(sc->jme_rdata.jme_rx_ring_paddr))) { device_printf(sc->jme_dev, "4GB boundary crossed, " "switching to 32bit DMA address mode.\n"); jme_dma_free(sc); /* Limit DMA address space to 32bit and try again. */ lowaddr = BUS_SPACE_MAXADDR_32BIT; goto again; } } lowaddr = BUS_SPACE_MAXADDR; if ((sc->jme_flags & JME_FLAG_DMA32BIT) != 0) lowaddr = BUS_SPACE_MAXADDR_32BIT; /* Create parent buffer tag. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->jme_dev),/* parent */ 1, 0, /* algnmnt, boundary */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 0, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_buffer_tag); if (error != 0) { device_printf(sc->jme_dev, "could not create parent buffer DMA tag.\n"); goto fail; } /* Create shadow status block tag. */ error = bus_dma_tag_create(sc->jme_cdata.jme_buffer_tag,/* parent */ JME_SSB_ALIGN, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ JME_SSB_SIZE, /* maxsize */ 1, /* nsegments */ JME_SSB_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_ssb_tag); if (error != 0) { device_printf(sc->jme_dev, "could not create shared status block DMA tag.\n"); goto fail; } /* Create tag for Tx buffers. */ error = bus_dma_tag_create(sc->jme_cdata.jme_buffer_tag,/* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ JME_TSO_MAXSIZE, /* maxsize */ JME_MAXTXSEGS, /* nsegments */ JME_TSO_MAXSEGSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_tx_tag); if (error != 0) { device_printf(sc->jme_dev, "could not create Tx DMA tag.\n"); goto fail; } /* Create tag for Rx buffers. */ error = bus_dma_tag_create(sc->jme_cdata.jme_buffer_tag,/* parent */ JME_RX_BUF_ALIGN, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, /* maxsize */ 1, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->jme_cdata.jme_rx_tag); if (error != 0) { device_printf(sc->jme_dev, "could not create Rx DMA tag.\n"); goto fail; } /* * Allocate DMA'able memory and load the DMA map for shared * status block. */ error = bus_dmamem_alloc(sc->jme_cdata.jme_ssb_tag, (void **)&sc->jme_rdata.jme_ssb_block, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->jme_cdata.jme_ssb_map); if (error != 0) { device_printf(sc->jme_dev, "could not allocate DMA'able " "memory for shared status block.\n"); goto fail; } ctx.jme_busaddr = 0; error = bus_dmamap_load(sc->jme_cdata.jme_ssb_tag, sc->jme_cdata.jme_ssb_map, sc->jme_rdata.jme_ssb_block, JME_SSB_SIZE, jme_dmamap_cb, &ctx, BUS_DMA_NOWAIT); if (error != 0 || ctx.jme_busaddr == 0) { device_printf(sc->jme_dev, "could not load DMA'able memory " "for shared status block.\n"); goto fail; } sc->jme_rdata.jme_ssb_block_paddr = ctx.jme_busaddr; /* Create DMA maps for Tx buffers. */ for (i = 0; i < JME_TX_RING_CNT; i++) { txd = &sc->jme_cdata.jme_txdesc[i]; txd->tx_m = NULL; txd->tx_dmamap = NULL; error = bus_dmamap_create(sc->jme_cdata.jme_tx_tag, 0, &txd->tx_dmamap); if (error != 0) { device_printf(sc->jme_dev, "could not create Tx dmamap.\n"); goto fail; } } /* Create DMA maps for Rx buffers. */ if ((error = bus_dmamap_create(sc->jme_cdata.jme_rx_tag, 0, &sc->jme_cdata.jme_rx_sparemap)) != 0) { device_printf(sc->jme_dev, "could not create spare Rx dmamap.\n"); goto fail; } for (i = 0; i < JME_RX_RING_CNT; i++) { rxd = &sc->jme_cdata.jme_rxdesc[i]; rxd->rx_m = NULL; rxd->rx_dmamap = NULL; error = bus_dmamap_create(sc->jme_cdata.jme_rx_tag, 0, &rxd->rx_dmamap); if (error != 0) { device_printf(sc->jme_dev, "could not create Rx dmamap.\n"); goto fail; } } fail: return (error); } static void jme_dma_free(struct jme_softc *sc) { struct jme_txdesc *txd; struct jme_rxdesc *rxd; int i; /* Tx ring */ if (sc->jme_cdata.jme_tx_ring_tag != NULL) { if (sc->jme_cdata.jme_tx_ring_map) bus_dmamap_unload(sc->jme_cdata.jme_tx_ring_tag, sc->jme_cdata.jme_tx_ring_map); if (sc->jme_cdata.jme_tx_ring_map && sc->jme_rdata.jme_tx_ring) bus_dmamem_free(sc->jme_cdata.jme_tx_ring_tag, sc->jme_rdata.jme_tx_ring, sc->jme_cdata.jme_tx_ring_map); sc->jme_rdata.jme_tx_ring = NULL; sc->jme_cdata.jme_tx_ring_map = NULL; bus_dma_tag_destroy(sc->jme_cdata.jme_tx_ring_tag); sc->jme_cdata.jme_tx_ring_tag = NULL; } /* Rx ring */ if (sc->jme_cdata.jme_rx_ring_tag != NULL) { if (sc->jme_cdata.jme_rx_ring_map) bus_dmamap_unload(sc->jme_cdata.jme_rx_ring_tag, sc->jme_cdata.jme_rx_ring_map); if (sc->jme_cdata.jme_rx_ring_map && sc->jme_rdata.jme_rx_ring) bus_dmamem_free(sc->jme_cdata.jme_rx_ring_tag, sc->jme_rdata.jme_rx_ring, sc->jme_cdata.jme_rx_ring_map); sc->jme_rdata.jme_rx_ring = NULL; sc->jme_cdata.jme_rx_ring_map = NULL; bus_dma_tag_destroy(sc->jme_cdata.jme_rx_ring_tag); sc->jme_cdata.jme_rx_ring_tag = NULL; } /* Tx buffers */ if (sc->jme_cdata.jme_tx_tag != NULL) { for (i = 0; i < JME_TX_RING_CNT; i++) { txd = &sc->jme_cdata.jme_txdesc[i]; if (txd->tx_dmamap != NULL) { bus_dmamap_destroy(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap); txd->tx_dmamap = NULL; } } bus_dma_tag_destroy(sc->jme_cdata.jme_tx_tag); sc->jme_cdata.jme_tx_tag = NULL; } /* Rx buffers */ if (sc->jme_cdata.jme_rx_tag != NULL) { for (i = 0; i < JME_RX_RING_CNT; i++) { rxd = &sc->jme_cdata.jme_rxdesc[i]; if (rxd->rx_dmamap != NULL) { bus_dmamap_destroy(sc->jme_cdata.jme_rx_tag, rxd->rx_dmamap); rxd->rx_dmamap = NULL; } } if (sc->jme_cdata.jme_rx_sparemap != NULL) { bus_dmamap_destroy(sc->jme_cdata.jme_rx_tag, sc->jme_cdata.jme_rx_sparemap); sc->jme_cdata.jme_rx_sparemap = NULL; } bus_dma_tag_destroy(sc->jme_cdata.jme_rx_tag); sc->jme_cdata.jme_rx_tag = NULL; } /* Shared status block. */ if (sc->jme_cdata.jme_ssb_tag != NULL) { if (sc->jme_cdata.jme_ssb_map) bus_dmamap_unload(sc->jme_cdata.jme_ssb_tag, sc->jme_cdata.jme_ssb_map); if (sc->jme_cdata.jme_ssb_map && sc->jme_rdata.jme_ssb_block) bus_dmamem_free(sc->jme_cdata.jme_ssb_tag, sc->jme_rdata.jme_ssb_block, sc->jme_cdata.jme_ssb_map); sc->jme_rdata.jme_ssb_block = NULL; sc->jme_cdata.jme_ssb_map = NULL; bus_dma_tag_destroy(sc->jme_cdata.jme_ssb_tag); sc->jme_cdata.jme_ssb_tag = NULL; } if (sc->jme_cdata.jme_buffer_tag != NULL) { bus_dma_tag_destroy(sc->jme_cdata.jme_buffer_tag); sc->jme_cdata.jme_buffer_tag = NULL; } if (sc->jme_cdata.jme_ring_tag != NULL) { bus_dma_tag_destroy(sc->jme_cdata.jme_ring_tag); sc->jme_cdata.jme_ring_tag = NULL; } } /* * Make sure the interface is stopped at reboot time. */ static int jme_shutdown(device_t dev) { return (jme_suspend(dev)); } /* * Unlike other ethernet controllers, JMC250 requires * explicit resetting link speed to 10/100Mbps as gigabit * link will cunsume more power than 375mA. * Note, we reset the link speed to 10/100Mbps with * auto-negotiation but we don't know whether that operation * would succeed or not as we have no control after powering * off. If the renegotiation fail WOL may not work. Running * at 1Gbps draws more power than 375mA at 3.3V which is * specified in PCI specification and that would result in * complete shutdowning power to ethernet controller. * * TODO * Save current negotiated media speed/duplex/flow-control * to softc and restore the same link again after resuming. * PHY handling such as power down/resetting to 100Mbps * may be better handled in suspend method in phy driver. */ static void jme_setlinkspeed(struct jme_softc *sc) { struct mii_data *mii; int aneg, i; JME_LOCK_ASSERT(sc); mii = device_get_softc(sc->jme_miibus); mii_pollstat(mii); aneg = 0; if ((mii->mii_media_status & IFM_AVALID) != 0) { switch IFM_SUBTYPE(mii->mii_media_active) { case IFM_10_T: case IFM_100_TX: return; case IFM_1000_T: aneg++; default: break; } } jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, MII_100T2CR, 0); jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA); jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); DELAY(1000); if (aneg != 0) { /* Poll link state until jme(4) get a 10/100 link. */ for (i = 0; i < MII_ANEGTICKS_GIGE; i++) { mii_pollstat(mii); if ((mii->mii_media_status & IFM_AVALID) != 0) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: jme_mac_config(sc); return; default: break; } } JME_UNLOCK(sc); pause("jmelnk", hz); JME_LOCK(sc); } if (i == MII_ANEGTICKS_GIGE) device_printf(sc->jme_dev, "establishing link failed, " "WOL may not work!"); } /* * No link, force MAC to have 100Mbps, full-duplex link. * This is the last resort and may/may not work. */ mii->mii_media_status = IFM_AVALID | IFM_ACTIVE; mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX; jme_mac_config(sc); } static void jme_setwol(struct jme_softc *sc) { struct ifnet *ifp; uint32_t gpr, pmcs; uint16_t pmstat; int pmc; JME_LOCK_ASSERT(sc); if (pci_find_extcap(sc->jme_dev, PCIY_PMG, &pmc) != 0) { /* Remove Tx MAC/offload clock to save more power. */ if ((sc->jme_flags & JME_FLAG_TXCLK) != 0) CSR_WRITE_4(sc, JME_GHC, CSR_READ_4(sc, JME_GHC) & ~(GHC_TX_OFFLD_CLK_100 | GHC_TX_MAC_CLK_100 | GHC_TX_OFFLD_CLK_1000 | GHC_TX_MAC_CLK_1000)); /* No PME capability, PHY power down. */ jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, MII_BMCR, BMCR_PDOWN); return; } ifp = sc->jme_ifp; gpr = CSR_READ_4(sc, JME_GPREG0) & ~GPREG0_PME_ENB; pmcs = CSR_READ_4(sc, JME_PMCS); pmcs &= ~PMCS_WOL_ENB_MASK; if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) { pmcs |= PMCS_MAGIC_FRAME | PMCS_MAGIC_FRAME_ENB; /* Enable PME message. */ gpr |= GPREG0_PME_ENB; /* For gigabit controllers, reset link speed to 10/100. */ if ((sc->jme_flags & JME_FLAG_FASTETH) == 0) jme_setlinkspeed(sc); } CSR_WRITE_4(sc, JME_PMCS, pmcs); CSR_WRITE_4(sc, JME_GPREG0, gpr); /* Remove Tx MAC/offload clock to save more power. */ if ((sc->jme_flags & JME_FLAG_TXCLK) != 0) CSR_WRITE_4(sc, JME_GHC, CSR_READ_4(sc, JME_GHC) & ~(GHC_TX_OFFLD_CLK_100 | GHC_TX_MAC_CLK_100 | GHC_TX_OFFLD_CLK_1000 | GHC_TX_MAC_CLK_1000)); /* Request PME. */ pmstat = pci_read_config(sc->jme_dev, pmc + PCIR_POWER_STATUS, 2); pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); if ((ifp->if_capenable & IFCAP_WOL) != 0) pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; pci_write_config(sc->jme_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); if ((ifp->if_capenable & IFCAP_WOL) == 0) { /* No WOL, PHY power down. */ jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, MII_BMCR, BMCR_PDOWN); } } static int jme_suspend(device_t dev) { struct jme_softc *sc; sc = device_get_softc(dev); JME_LOCK(sc); jme_stop(sc); jme_setwol(sc); JME_UNLOCK(sc); return (0); } static int jme_resume(device_t dev) { struct jme_softc *sc; struct ifnet *ifp; uint16_t pmstat; int pmc; sc = device_get_softc(dev); JME_LOCK(sc); if (pci_find_extcap(sc->jme_dev, PCIY_PMG, &pmc) != 0) { pmstat = pci_read_config(sc->jme_dev, pmc + PCIR_POWER_STATUS, 2); /* Disable PME clear PME status. */ pmstat &= ~PCIM_PSTAT_PMEENABLE; pci_write_config(sc->jme_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); } ifp = sc->jme_ifp; if ((ifp->if_flags & IFF_UP) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); } JME_UNLOCK(sc); return (0); } static int jme_encap(struct jme_softc *sc, struct mbuf **m_head) { struct jme_txdesc *txd; struct jme_desc *desc; struct mbuf *m; bus_dma_segment_t txsegs[JME_MAXTXSEGS]; int error, i, nsegs, prod; uint32_t cflags, tso_segsz; JME_LOCK_ASSERT(sc); M_ASSERTPKTHDR((*m_head)); if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0) { /* * Due to the adherence to NDIS specification JMC250 * assumes upper stack computed TCP pseudo checksum * without including payload length. This breaks * checksum offload for TSO case so recompute TCP * pseudo checksum for JMC250. Hopefully this wouldn't * be much burden on modern CPUs. */ struct ether_header *eh; struct ip *ip; struct tcphdr *tcp; uint32_t ip_off, poff; if (M_WRITABLE(*m_head) == 0) { /* Get a writable copy. */ m = m_dup(*m_head, M_DONTWAIT); m_freem(*m_head); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m; } ip_off = sizeof(struct ether_header); m = m_pullup(*m_head, ip_off); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } eh = mtod(m, struct ether_header *); /* Check the existence of VLAN tag. */ if (eh->ether_type == htons(ETHERTYPE_VLAN)) { ip_off = sizeof(struct ether_vlan_header); m = m_pullup(m, ip_off); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } } m = m_pullup(m, ip_off + sizeof(struct ip)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } ip = (struct ip *)(mtod(m, char *) + ip_off); poff = ip_off + (ip->ip_hl << 2); m = m_pullup(m, poff + sizeof(struct tcphdr)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } tcp = (struct tcphdr *)(mtod(m, char *) + poff); /* * Reset IP checksum and recompute TCP pseudo * checksum that NDIS specification requires. */ ip->ip_sum = 0; if (poff + (tcp->th_off << 2) == m->m_pkthdr.len) { tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons((tcp->th_off << 2) + IPPROTO_TCP)); /* No need to TSO, force IP checksum offload. */ (*m_head)->m_pkthdr.csum_flags &= ~CSUM_TSO; (*m_head)->m_pkthdr.csum_flags |= CSUM_IP; } else tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(IPPROTO_TCP)); *m_head = m; } prod = sc->jme_cdata.jme_tx_prod; txd = &sc->jme_cdata.jme_txdesc[prod]; error = bus_dmamap_load_mbuf_sg(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap, *m_head, txsegs, &nsegs, 0); if (error == EFBIG) { m = m_collapse(*m_head, M_DONTWAIT, JME_MAXTXSEGS); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOMEM); } *m_head = m; error = bus_dmamap_load_mbuf_sg(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap, *m_head, txsegs, &nsegs, 0); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (error); } } else if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* * Check descriptor overrun. Leave one free descriptor. * Since we always use 64bit address mode for transmitting, * each Tx request requires one more dummy descriptor. */ if (sc->jme_cdata.jme_tx_cnt + nsegs + 1 > JME_TX_RING_CNT - 1) { bus_dmamap_unload(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap); return (ENOBUFS); } m = *m_head; cflags = 0; tso_segsz = 0; /* Configure checksum offload and TSO. */ if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { tso_segsz = (uint32_t)m->m_pkthdr.tso_segsz << JME_TD_MSS_SHIFT; cflags |= JME_TD_TSO; } else { if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) cflags |= JME_TD_IPCSUM; if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0) cflags |= JME_TD_TCPCSUM; if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0) cflags |= JME_TD_UDPCSUM; } /* Configure VLAN. */ if ((m->m_flags & M_VLANTAG) != 0) { cflags |= (m->m_pkthdr.ether_vtag & JME_TD_VLAN_MASK); cflags |= JME_TD_VLAN_TAG; } desc = &sc->jme_rdata.jme_tx_ring[prod]; desc->flags = htole32(cflags); desc->buflen = htole32(tso_segsz); desc->addr_hi = htole32(m->m_pkthdr.len); desc->addr_lo = 0; sc->jme_cdata.jme_tx_cnt++; JME_DESC_INC(prod, JME_TX_RING_CNT); for (i = 0; i < nsegs; i++) { desc = &sc->jme_rdata.jme_tx_ring[prod]; desc->flags = htole32(JME_TD_OWN | JME_TD_64BIT); desc->buflen = htole32(txsegs[i].ds_len); desc->addr_hi = htole32(JME_ADDR_HI(txsegs[i].ds_addr)); desc->addr_lo = htole32(JME_ADDR_LO(txsegs[i].ds_addr)); sc->jme_cdata.jme_tx_cnt++; JME_DESC_INC(prod, JME_TX_RING_CNT); } /* Update producer index. */ sc->jme_cdata.jme_tx_prod = prod; /* * Finally request interrupt and give the first descriptor * owenership to hardware. */ desc = txd->tx_desc; desc->flags |= htole32(JME_TD_OWN | JME_TD_INTR); txd->tx_m = m; txd->tx_ndesc = nsegs + 1; /* Sync descriptors. */ bus_dmamap_sync(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->jme_cdata.jme_tx_ring_tag, sc->jme_cdata.jme_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static void jme_tx_task(void *arg, int pending) { struct ifnet *ifp; ifp = (struct ifnet *)arg; jme_start(ifp); } static void jme_start(struct ifnet *ifp) { struct jme_softc *sc; struct mbuf *m_head; int enq; sc = ifp->if_softc; JME_LOCK(sc); if (sc->jme_cdata.jme_tx_cnt >= JME_TX_DESC_HIWAT) jme_txeof(sc); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->jme_flags & JME_FLAG_LINK) == 0) { JME_UNLOCK(sc); return; } for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * Pack the data into the transmit ring. If we * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ if (jme_encap(sc, &m_head)) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ ETHER_BPF_MTAP(ifp, m_head); } if (enq > 0) { /* * Reading TXCSR takes very long time under heavy load * so cache TXCSR value and writes the ORed value with * the kick command to the TXCSR. This saves one register * access cycle. */ CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr | TXCSR_TX_ENB | TXCSR_TXQ_N_START(TXCSR_TXQ0)); /* Set a timeout in case the chip goes out to lunch. */ sc->jme_watchdog_timer = JME_TX_TIMEOUT; } JME_UNLOCK(sc); } static void jme_watchdog(struct jme_softc *sc) { struct ifnet *ifp; JME_LOCK_ASSERT(sc); if (sc->jme_watchdog_timer == 0 || --sc->jme_watchdog_timer) return; ifp = sc->jme_ifp; if ((sc->jme_flags & JME_FLAG_LINK) == 0) { if_printf(sc->jme_ifp, "watchdog timeout (missed link)\n"); ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); return; } jme_txeof(sc); if (sc->jme_cdata.jme_tx_cnt == 0) { if_printf(sc->jme_ifp, "watchdog timeout (missed Tx interrupts) -- recovering\n"); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue(sc->jme_tq, &sc->jme_tx_task); return; } if_printf(sc->jme_ifp, "watchdog timeout\n"); ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue(sc->jme_tq, &sc->jme_tx_task); } static int jme_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct jme_softc *sc; struct ifreq *ifr; struct mii_data *mii; uint32_t reg; int error, mask; sc = ifp->if_softc; ifr = (struct ifreq *)data; error = 0; switch (cmd) { case SIOCSIFMTU: if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > JME_JUMBO_MTU || ((sc->jme_flags & JME_FLAG_NOJUMBO) != 0 && ifr->ifr_mtu > JME_MAX_MTU)) { error = EINVAL; break; } if (ifp->if_mtu != ifr->ifr_mtu) { /* * No special configuration is required when interface * MTU is changed but availability of TSO/Tx checksum * offload should be chcked against new MTU size as * FIFO size is just 2K. */ JME_LOCK(sc); if (ifr->ifr_mtu >= JME_TX_FIFO_SIZE) { ifp->if_capenable &= ~(IFCAP_TXCSUM | IFCAP_TSO4); ifp->if_hwassist &= ~(JME_CSUM_FEATURES | CSUM_TSO); VLAN_CAPABILITIES(ifp); } ifp->if_mtu = ifr->ifr_mtu; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); } JME_UNLOCK(sc); } break; case SIOCSIFFLAGS: JME_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (((ifp->if_flags ^ sc->jme_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) jme_set_filter(sc); } else { if ((sc->jme_flags & JME_FLAG_DETACH) == 0) jme_init_locked(sc); } } else { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) jme_stop(sc); } sc->jme_if_flags = ifp->if_flags; JME_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: JME_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) jme_set_filter(sc); JME_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: mii = device_get_softc(sc->jme_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: JME_LOCK(sc); mask = ifr->ifr_reqcap ^ ifp->if_capenable; if ((mask & IFCAP_TXCSUM) != 0 && ifp->if_mtu < JME_TX_FIFO_SIZE) { if ((IFCAP_TXCSUM & ifp->if_capabilities) != 0) { ifp->if_capenable ^= IFCAP_TXCSUM; if ((IFCAP_TXCSUM & ifp->if_capenable) != 0) ifp->if_hwassist |= JME_CSUM_FEATURES; else ifp->if_hwassist &= ~JME_CSUM_FEATURES; } } if ((mask & IFCAP_RXCSUM) != 0 && (IFCAP_RXCSUM & ifp->if_capabilities) != 0) { ifp->if_capenable ^= IFCAP_RXCSUM; reg = CSR_READ_4(sc, JME_RXMAC); reg &= ~RXMAC_CSUM_ENB; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) reg |= RXMAC_CSUM_ENB; CSR_WRITE_4(sc, JME_RXMAC, reg); } if ((mask & IFCAP_TSO4) != 0 && ifp->if_mtu < JME_TX_FIFO_SIZE) { if ((IFCAP_TSO4 & ifp->if_capabilities) != 0) { ifp->if_capenable ^= IFCAP_TSO4; if ((IFCAP_TSO4 & ifp->if_capenable) != 0) ifp->if_hwassist |= CSUM_TSO; else ifp->if_hwassist &= ~CSUM_TSO; } } if ((mask & IFCAP_WOL_MAGIC) != 0 && (IFCAP_WOL_MAGIC & ifp->if_capabilities) != 0) ifp->if_capenable ^= IFCAP_WOL_MAGIC; if ((mask & IFCAP_VLAN_HWCSUM) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; + if ((mask & IFCAP_VLAN_HWTSO) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) + ifp->if_capenable ^= IFCAP_VLAN_HWTSO; if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && (IFCAP_VLAN_HWTAGGING & ifp->if_capabilities) != 0) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; jme_set_vlan(sc); } JME_UNLOCK(sc); VLAN_CAPABILITIES(ifp); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void jme_mac_config(struct jme_softc *sc) { struct mii_data *mii; uint32_t ghc, gpreg, rxmac, txmac, txpause; uint32_t txclk; JME_LOCK_ASSERT(sc); mii = device_get_softc(sc->jme_miibus); CSR_WRITE_4(sc, JME_GHC, GHC_RESET); DELAY(10); CSR_WRITE_4(sc, JME_GHC, 0); ghc = 0; txclk = 0; rxmac = CSR_READ_4(sc, JME_RXMAC); rxmac &= ~RXMAC_FC_ENB; txmac = CSR_READ_4(sc, JME_TXMAC); txmac &= ~(TXMAC_CARRIER_EXT | TXMAC_FRAME_BURST); txpause = CSR_READ_4(sc, JME_TXPFC); txpause &= ~TXPFC_PAUSE_ENB; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { ghc |= GHC_FULL_DUPLEX; rxmac &= ~RXMAC_COLL_DET_ENB; txmac &= ~(TXMAC_COLL_ENB | TXMAC_CARRIER_SENSE | TXMAC_BACKOFF | TXMAC_CARRIER_EXT | TXMAC_FRAME_BURST); #ifdef notyet if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) txpause |= TXPFC_PAUSE_ENB; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) rxmac |= RXMAC_FC_ENB; #endif /* Disable retry transmit timer/retry limit. */ CSR_WRITE_4(sc, JME_TXTRHD, CSR_READ_4(sc, JME_TXTRHD) & ~(TXTRHD_RT_PERIOD_ENB | TXTRHD_RT_LIMIT_ENB)); } else { rxmac |= RXMAC_COLL_DET_ENB; txmac |= TXMAC_COLL_ENB | TXMAC_CARRIER_SENSE | TXMAC_BACKOFF; /* Enable retry transmit timer/retry limit. */ CSR_WRITE_4(sc, JME_TXTRHD, CSR_READ_4(sc, JME_TXTRHD) | TXTRHD_RT_PERIOD_ENB | TXTRHD_RT_LIMIT_ENB); } /* Reprogram Tx/Rx MACs with resolved speed/duplex. */ switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: ghc |= GHC_SPEED_10; txclk |= GHC_TX_OFFLD_CLK_100 | GHC_TX_MAC_CLK_100; break; case IFM_100_TX: ghc |= GHC_SPEED_100; txclk |= GHC_TX_OFFLD_CLK_100 | GHC_TX_MAC_CLK_100; break; case IFM_1000_T: if ((sc->jme_flags & JME_FLAG_FASTETH) != 0) break; ghc |= GHC_SPEED_1000; txclk |= GHC_TX_OFFLD_CLK_1000 | GHC_TX_MAC_CLK_1000; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) == 0) txmac |= TXMAC_CARRIER_EXT | TXMAC_FRAME_BURST; break; default: break; } if (sc->jme_rev == DEVICEID_JMC250 && sc->jme_chip_rev == DEVICEREVID_JMC250_A2) { /* * Workaround occasional packet loss issue of JMC250 A2 * when it runs on half-duplex media. */ gpreg = CSR_READ_4(sc, JME_GPREG1); if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) gpreg &= ~GPREG1_HDPX_FIX; else gpreg |= GPREG1_HDPX_FIX; CSR_WRITE_4(sc, JME_GPREG1, gpreg); /* Workaround CRC errors at 100Mbps on JMC250 A2. */ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { /* Extend interface FIFO depth. */ jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, 0x1B, 0x0000); } else { /* Select default interface FIFO depth. */ jme_miibus_writereg(sc->jme_dev, sc->jme_phyaddr, 0x1B, 0x0004); } } if ((sc->jme_flags & JME_FLAG_TXCLK) != 0) ghc |= txclk; CSR_WRITE_4(sc, JME_GHC, ghc); CSR_WRITE_4(sc, JME_RXMAC, rxmac); CSR_WRITE_4(sc, JME_TXMAC, txmac); CSR_WRITE_4(sc, JME_TXPFC, txpause); } static void jme_link_task(void *arg, int pending) { struct jme_softc *sc; struct mii_data *mii; struct ifnet *ifp; struct jme_txdesc *txd; bus_addr_t paddr; int i; sc = (struct jme_softc *)arg; JME_LOCK(sc); mii = device_get_softc(sc->jme_miibus); ifp = sc->jme_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { JME_UNLOCK(sc); return; } sc->jme_flags &= ~JME_FLAG_LINK; if ((mii->mii_media_status & IFM_AVALID) != 0) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->jme_flags |= JME_FLAG_LINK; break; case IFM_1000_T: if ((sc->jme_flags & JME_FLAG_FASTETH) != 0) break; sc->jme_flags |= JME_FLAG_LINK; break; default: break; } } /* * Disabling Rx/Tx MACs have a side-effect of resetting * JME_TXNDA/JME_RXNDA register to the first address of * Tx/Rx descriptor address. So driver should reset its * internal procucer/consumer pointer and reclaim any * allocated resources. Note, just saving the value of * JME_TXNDA and JME_RXNDA registers before stopping MAC * and restoring JME_TXNDA/JME_RXNDA register is not * sufficient to make sure correct MAC state because * stopping MAC operation can take a while and hardware * might have updated JME_TXNDA/JME_RXNDA registers * during the stop operation. */ /* Block execution of task. */ taskqueue_block(sc->jme_tq); /* Disable interrupts and stop driver. */ CSR_WRITE_4(sc, JME_INTR_MASK_CLR, JME_INTRS); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); callout_stop(&sc->jme_tick_ch); sc->jme_watchdog_timer = 0; /* Stop receiver/transmitter. */ jme_stop_rx(sc); jme_stop_tx(sc); /* XXX Drain all queued tasks. */ JME_UNLOCK(sc); taskqueue_drain(sc->jme_tq, &sc->jme_int_task); taskqueue_drain(sc->jme_tq, &sc->jme_tx_task); JME_LOCK(sc); jme_rxintr(sc, JME_RX_RING_CNT); if (sc->jme_cdata.jme_rxhead != NULL) m_freem(sc->jme_cdata.jme_rxhead); JME_RXCHAIN_RESET(sc); jme_txeof(sc); if (sc->jme_cdata.jme_tx_cnt != 0) { /* Remove queued packets for transmit. */ for (i = 0; i < JME_TX_RING_CNT; i++) { txd = &sc->jme_cdata.jme_txdesc[i]; if (txd->tx_m != NULL) { bus_dmamap_sync( sc->jme_cdata.jme_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload( sc->jme_cdata.jme_tx_tag, txd->tx_dmamap); m_freem(txd->tx_m); txd->tx_m = NULL; txd->tx_ndesc = 0; ifp->if_oerrors++; } } } /* * Reuse configured Rx descriptors and reset * procuder/consumer index. */ sc->jme_cdata.jme_rx_cons = 0; atomic_set_int(&sc->jme_morework, 0); jme_init_tx_ring(sc); /* Initialize shadow status block. */ jme_init_ssb(sc); /* Program MAC with resolved speed/duplex/flow-control. */ if ((sc->jme_flags & JME_FLAG_LINK) != 0) { jme_mac_config(sc); jme_stats_clear(sc); CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr); CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr); /* Set Tx ring address to the hardware. */ paddr = JME_TX_RING_ADDR(sc, 0); CSR_WRITE_4(sc, JME_TXDBA_HI, JME_ADDR_HI(paddr)); CSR_WRITE_4(sc, JME_TXDBA_LO, JME_ADDR_LO(paddr)); /* Set Rx ring address to the hardware. */ paddr = JME_RX_RING_ADDR(sc, 0); CSR_WRITE_4(sc, JME_RXDBA_HI, JME_ADDR_HI(paddr)); CSR_WRITE_4(sc, JME_RXDBA_LO, JME_ADDR_LO(paddr)); /* Restart receiver/transmitter. */ CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr | RXCSR_RX_ENB | RXCSR_RXQ_START); CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr | TXCSR_TX_ENB); } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; callout_reset(&sc->jme_tick_ch, hz, jme_tick, sc); /* Unblock execution of task. */ taskqueue_unblock(sc->jme_tq); /* Reenable interrupts. */ CSR_WRITE_4(sc, JME_INTR_MASK_SET, JME_INTRS); JME_UNLOCK(sc); } static int jme_intr(void *arg) { struct jme_softc *sc; uint32_t status; sc = (struct jme_softc *)arg; status = CSR_READ_4(sc, JME_INTR_REQ_STATUS); if (status == 0 || status == 0xFFFFFFFF) return (FILTER_STRAY); /* Disable interrupts. */ CSR_WRITE_4(sc, JME_INTR_MASK_CLR, JME_INTRS); taskqueue_enqueue(sc->jme_tq, &sc->jme_int_task); return (FILTER_HANDLED); } static void jme_int_task(void *arg, int pending) { struct jme_softc *sc; struct ifnet *ifp; uint32_t status; int more; sc = (struct jme_softc *)arg; ifp = sc->jme_ifp; status = CSR_READ_4(sc, JME_INTR_STATUS); more = atomic_readandclear_int(&sc->jme_morework); if (more != 0) { status |= INTR_RXQ_COAL | INTR_RXQ_COAL_TO; more = 0; } if ((status & JME_INTRS) == 0 || status == 0xFFFFFFFF) goto done; /* Reset PCC counter/timer and Ack interrupts. */ status &= ~(INTR_TXQ_COMP | INTR_RXQ_COMP); if ((status & (INTR_TXQ_COAL | INTR_TXQ_COAL_TO)) != 0) status |= INTR_TXQ_COAL | INTR_TXQ_COAL_TO | INTR_TXQ_COMP; if ((status & (INTR_RXQ_COAL | INTR_RXQ_COAL_TO)) != 0) status |= INTR_RXQ_COAL | INTR_RXQ_COAL_TO | INTR_RXQ_COMP; CSR_WRITE_4(sc, JME_INTR_STATUS, status); more = 0; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if ((status & (INTR_RXQ_COAL | INTR_RXQ_COAL_TO)) != 0) { more = jme_rxintr(sc, sc->jme_process_limit); if (more != 0) atomic_set_int(&sc->jme_morework, 1); } if ((status & INTR_RXQ_DESC_EMPTY) != 0) { /* * Notify hardware availability of new Rx * buffers. * Reading RXCSR takes very long time under * heavy load so cache RXCSR value and writes * the ORed value with the kick command to * the RXCSR. This saves one register access * cycle. */ CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr | RXCSR_RX_ENB | RXCSR_RXQ_START); } /* * Reclaiming Tx buffers are deferred to make jme(4) run * without locks held. */ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue(sc->jme_tq, &sc->jme_tx_task); } if (more != 0 || (CSR_READ_4(sc, JME_INTR_STATUS) & JME_INTRS) != 0) { taskqueue_enqueue(sc->jme_tq, &sc->jme_int_task); return; } done: /* Reenable interrupts. */ CSR_WRITE_4(sc, JME_INTR_MASK_SET, JME_INTRS); } static void jme_txeof(struct jme_softc *sc) { struct ifnet *ifp; struct jme_txdesc *txd; uint32_t status; int cons, nsegs; JME_LOCK_ASSERT(sc); ifp = sc->jme_ifp; cons = sc->jme_cdata.jme_tx_cons; if (cons == sc->jme_cdata.jme_tx_prod) return; bus_dmamap_sync(sc->jme_cdata.jme_tx_ring_tag, sc->jme_cdata.jme_tx_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); /* * Go through our Tx list and free mbufs for those * frames which have been transmitted. */ for (; cons != sc->jme_cdata.jme_tx_prod;) { txd = &sc->jme_cdata.jme_txdesc[cons]; status = le32toh(txd->tx_desc->flags); if ((status & JME_TD_OWN) == JME_TD_OWN) break; if ((status & (JME_TD_TMOUT | JME_TD_RETRY_EXP)) != 0) ifp->if_oerrors++; else { ifp->if_opackets++; if ((status & JME_TD_COLLISION) != 0) ifp->if_collisions += le32toh(txd->tx_desc->buflen) & JME_TD_BUF_LEN_MASK; } /* * Only the first descriptor of multi-descriptor * transmission is updated so driver have to skip entire * chained buffers for the transmiited frame. In other * words, JME_TD_OWN bit is valid only at the first * descriptor of a multi-descriptor transmission. */ for (nsegs = 0; nsegs < txd->tx_ndesc; nsegs++) { sc->jme_rdata.jme_tx_ring[cons].flags = 0; JME_DESC_INC(cons, JME_TX_RING_CNT); } /* Reclaim transferred mbufs. */ bus_dmamap_sync(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap); KASSERT(txd->tx_m != NULL, ("%s: freeing NULL mbuf!\n", __func__)); m_freem(txd->tx_m); txd->tx_m = NULL; sc->jme_cdata.jme_tx_cnt -= txd->tx_ndesc; KASSERT(sc->jme_cdata.jme_tx_cnt >= 0, ("%s: Active Tx desc counter was garbled\n", __func__)); txd->tx_ndesc = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } sc->jme_cdata.jme_tx_cons = cons; /* Unarm watchog timer when there is no pending descriptors in queue. */ if (sc->jme_cdata.jme_tx_cnt == 0) sc->jme_watchdog_timer = 0; bus_dmamap_sync(sc->jme_cdata.jme_tx_ring_tag, sc->jme_cdata.jme_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static __inline void jme_discard_rxbuf(struct jme_softc *sc, int cons) { struct jme_desc *desc; desc = &sc->jme_rdata.jme_rx_ring[cons]; desc->flags = htole32(JME_RD_OWN | JME_RD_INTR | JME_RD_64BIT); desc->buflen = htole32(MCLBYTES); } /* Receive a frame. */ static void jme_rxeof(struct jme_softc *sc) { struct ifnet *ifp; struct jme_desc *desc; struct jme_rxdesc *rxd; struct mbuf *mp, *m; uint32_t flags, status; int cons, count, nsegs; ifp = sc->jme_ifp; cons = sc->jme_cdata.jme_rx_cons; desc = &sc->jme_rdata.jme_rx_ring[cons]; flags = le32toh(desc->flags); status = le32toh(desc->buflen); nsegs = JME_RX_NSEGS(status); sc->jme_cdata.jme_rxlen = JME_RX_BYTES(status) - JME_RX_PAD_BYTES; if ((status & JME_RX_ERR_STAT) != 0) { ifp->if_ierrors++; jme_discard_rxbuf(sc, sc->jme_cdata.jme_rx_cons); #ifdef JME_SHOW_ERRORS device_printf(sc->jme_dev, "%s : receive error = 0x%b\n", __func__, JME_RX_ERR(status), JME_RX_ERR_BITS); #endif sc->jme_cdata.jme_rx_cons += nsegs; sc->jme_cdata.jme_rx_cons %= JME_RX_RING_CNT; return; } for (count = 0; count < nsegs; count++, JME_DESC_INC(cons, JME_RX_RING_CNT)) { rxd = &sc->jme_cdata.jme_rxdesc[cons]; mp = rxd->rx_m; /* Add a new receive buffer to the ring. */ if (jme_newbuf(sc, rxd) != 0) { ifp->if_iqdrops++; /* Reuse buffer. */ for (; count < nsegs; count++) { jme_discard_rxbuf(sc, cons); JME_DESC_INC(cons, JME_RX_RING_CNT); } if (sc->jme_cdata.jme_rxhead != NULL) { m_freem(sc->jme_cdata.jme_rxhead); JME_RXCHAIN_RESET(sc); } break; } /* * Assume we've received a full sized frame. * Actual size is fixed when we encounter the end of * multi-segmented frame. */ mp->m_len = MCLBYTES; /* Chain received mbufs. */ if (sc->jme_cdata.jme_rxhead == NULL) { sc->jme_cdata.jme_rxhead = mp; sc->jme_cdata.jme_rxtail = mp; } else { /* * Receive processor can receive a maximum frame * size of 65535 bytes. */ mp->m_flags &= ~M_PKTHDR; sc->jme_cdata.jme_rxtail->m_next = mp; sc->jme_cdata.jme_rxtail = mp; } if (count == nsegs - 1) { /* Last desc. for this frame. */ m = sc->jme_cdata.jme_rxhead; m->m_flags |= M_PKTHDR; m->m_pkthdr.len = sc->jme_cdata.jme_rxlen; if (nsegs > 1) { /* Set first mbuf size. */ m->m_len = MCLBYTES - JME_RX_PAD_BYTES; /* Set last mbuf size. */ mp->m_len = sc->jme_cdata.jme_rxlen - ((MCLBYTES - JME_RX_PAD_BYTES) + (MCLBYTES * (nsegs - 2))); } else m->m_len = sc->jme_cdata.jme_rxlen; m->m_pkthdr.rcvif = ifp; /* * Account for 10bytes auto padding which is used * to align IP header on 32bit boundary. Also note, * CRC bytes is automatically removed by the * hardware. */ m->m_data += JME_RX_PAD_BYTES; /* Set checksum information. */ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0 && (flags & JME_RD_IPV4) != 0) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if ((flags & JME_RD_IPCSUM) != 0) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if (((flags & JME_RD_MORE_FRAG) == 0) && ((flags & (JME_RD_TCP | JME_RD_TCPCSUM)) == (JME_RD_TCP | JME_RD_TCPCSUM) || (flags & (JME_RD_UDP | JME_RD_UDPCSUM)) == (JME_RD_UDP | JME_RD_UDPCSUM))) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } /* Check for VLAN tagged packets. */ if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && (flags & JME_RD_VLAN_TAG) != 0) { m->m_pkthdr.ether_vtag = flags & JME_RD_VLAN_MASK; m->m_flags |= M_VLANTAG; } ifp->if_ipackets++; /* Pass it on. */ (*ifp->if_input)(ifp, m); /* Reset mbuf chains. */ JME_RXCHAIN_RESET(sc); } } sc->jme_cdata.jme_rx_cons += nsegs; sc->jme_cdata.jme_rx_cons %= JME_RX_RING_CNT; } static int jme_rxintr(struct jme_softc *sc, int count) { struct jme_desc *desc; int nsegs, prog, pktlen; bus_dmamap_sync(sc->jme_cdata.jme_rx_ring_tag, sc->jme_cdata.jme_rx_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (prog = 0; count > 0; prog++) { desc = &sc->jme_rdata.jme_rx_ring[sc->jme_cdata.jme_rx_cons]; if ((le32toh(desc->flags) & JME_RD_OWN) == JME_RD_OWN) break; if ((le32toh(desc->buflen) & JME_RD_VALID) == 0) break; nsegs = JME_RX_NSEGS(le32toh(desc->buflen)); /* * Check number of segments against received bytes. * Non-matching value would indicate that hardware * is still trying to update Rx descriptors. I'm not * sure whether this check is needed. */ pktlen = JME_RX_BYTES(le32toh(desc->buflen)); if (nsegs != ((pktlen + (MCLBYTES - 1)) / MCLBYTES)) break; prog++; /* Received a frame. */ jme_rxeof(sc); count -= nsegs; } if (prog > 0) bus_dmamap_sync(sc->jme_cdata.jme_rx_ring_tag, sc->jme_cdata.jme_rx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (count > 0 ? 0 : EAGAIN); } static void jme_tick(void *arg) { struct jme_softc *sc; struct mii_data *mii; sc = (struct jme_softc *)arg; JME_LOCK_ASSERT(sc); mii = device_get_softc(sc->jme_miibus); mii_tick(mii); /* * Reclaim Tx buffers that have been completed. It's not * needed here but it would release allocated mbuf chains * faster and limit the maximum delay to a hz. */ jme_txeof(sc); jme_stats_update(sc); jme_watchdog(sc); callout_reset(&sc->jme_tick_ch, hz, jme_tick, sc); } static void jme_reset(struct jme_softc *sc) { /* Stop receiver, transmitter. */ jme_stop_rx(sc); jme_stop_tx(sc); CSR_WRITE_4(sc, JME_GHC, GHC_RESET); DELAY(10); CSR_WRITE_4(sc, JME_GHC, 0); } static void jme_init(void *xsc) { struct jme_softc *sc; sc = (struct jme_softc *)xsc; JME_LOCK(sc); jme_init_locked(sc); JME_UNLOCK(sc); } static void jme_init_locked(struct jme_softc *sc) { struct ifnet *ifp; struct mii_data *mii; uint8_t eaddr[ETHER_ADDR_LEN]; bus_addr_t paddr; uint32_t reg; int error; JME_LOCK_ASSERT(sc); ifp = sc->jme_ifp; mii = device_get_softc(sc->jme_miibus); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* * Cancel any pending I/O. */ jme_stop(sc); /* * Reset the chip to a known state. */ jme_reset(sc); /* Init descriptors. */ error = jme_init_rx_ring(sc); if (error != 0) { device_printf(sc->jme_dev, "%s: initialization failed: no memory for Rx buffers.\n", __func__); jme_stop(sc); return; } jme_init_tx_ring(sc); /* Initialize shadow status block. */ jme_init_ssb(sc); /* Reprogram the station address. */ bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); CSR_WRITE_4(sc, JME_PAR0, eaddr[3] << 24 | eaddr[2] << 16 | eaddr[1] << 8 | eaddr[0]); CSR_WRITE_4(sc, JME_PAR1, eaddr[5] << 8 | eaddr[4]); /* * Configure Tx queue. * Tx priority queue weight value : 0 * Tx FIFO threshold for processing next packet : 16QW * Maximum Tx DMA length : 512 * Allow Tx DMA burst. */ sc->jme_txcsr = TXCSR_TXQ_N_SEL(TXCSR_TXQ0); sc->jme_txcsr |= TXCSR_TXQ_WEIGHT(TXCSR_TXQ_WEIGHT_MIN); sc->jme_txcsr |= TXCSR_FIFO_THRESH_16QW; sc->jme_txcsr |= sc->jme_tx_dma_size; sc->jme_txcsr |= TXCSR_DMA_BURST; CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr); /* Set Tx descriptor counter. */ CSR_WRITE_4(sc, JME_TXQDC, JME_TX_RING_CNT); /* Set Tx ring address to the hardware. */ paddr = JME_TX_RING_ADDR(sc, 0); CSR_WRITE_4(sc, JME_TXDBA_HI, JME_ADDR_HI(paddr)); CSR_WRITE_4(sc, JME_TXDBA_LO, JME_ADDR_LO(paddr)); /* Configure TxMAC parameters. */ reg = TXMAC_IFG1_DEFAULT | TXMAC_IFG2_DEFAULT | TXMAC_IFG_ENB; reg |= TXMAC_THRESH_1_PKT; reg |= TXMAC_CRC_ENB | TXMAC_PAD_ENB; CSR_WRITE_4(sc, JME_TXMAC, reg); /* * Configure Rx queue. * FIFO full threshold for transmitting Tx pause packet : 128T * FIFO threshold for processing next packet : 128QW * Rx queue 0 select * Max Rx DMA length : 128 * Rx descriptor retry : 32 * Rx descriptor retry time gap : 256ns * Don't receive runt/bad frame. */ sc->jme_rxcsr = RXCSR_FIFO_FTHRESH_128T; /* * Since Rx FIFO size is 4K bytes, receiving frames larger * than 4K bytes will suffer from Rx FIFO overruns. So * decrease FIFO threshold to reduce the FIFO overruns for * frames larger than 4000 bytes. * For best performance of standard MTU sized frames use * maximum allowable FIFO threshold, 128QW. Note these do * not hold on chip full mask verion >=2. For these * controllers 64QW and 128QW are not valid value. */ if (CHIPMODE_REVFM(sc->jme_chip_rev) >= 2) sc->jme_rxcsr |= RXCSR_FIFO_THRESH_16QW; else { if ((ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + ETHER_CRC_LEN) > JME_RX_FIFO_SIZE) sc->jme_rxcsr |= RXCSR_FIFO_THRESH_16QW; else sc->jme_rxcsr |= RXCSR_FIFO_THRESH_128QW; } sc->jme_rxcsr |= sc->jme_rx_dma_size | RXCSR_RXQ_N_SEL(RXCSR_RXQ0); sc->jme_rxcsr |= RXCSR_DESC_RT_CNT(RXCSR_DESC_RT_CNT_DEFAULT); sc->jme_rxcsr |= RXCSR_DESC_RT_GAP_256 & RXCSR_DESC_RT_GAP_MASK; CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr); /* Set Rx descriptor counter. */ CSR_WRITE_4(sc, JME_RXQDC, JME_RX_RING_CNT); /* Set Rx ring address to the hardware. */ paddr = JME_RX_RING_ADDR(sc, 0); CSR_WRITE_4(sc, JME_RXDBA_HI, JME_ADDR_HI(paddr)); CSR_WRITE_4(sc, JME_RXDBA_LO, JME_ADDR_LO(paddr)); /* Clear receive filter. */ CSR_WRITE_4(sc, JME_RXMAC, 0); /* Set up the receive filter. */ jme_set_filter(sc); jme_set_vlan(sc); /* * Disable all WOL bits as WOL can interfere normal Rx * operation. Also clear WOL detection status bits. */ reg = CSR_READ_4(sc, JME_PMCS); reg &= ~PMCS_WOL_ENB_MASK; CSR_WRITE_4(sc, JME_PMCS, reg); reg = CSR_READ_4(sc, JME_RXMAC); /* * Pad 10bytes right before received frame. This will greatly * help Rx performance on strict-alignment architectures as * it does not need to copy the frame to align the payload. */ reg |= RXMAC_PAD_10BYTES; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) reg |= RXMAC_CSUM_ENB; CSR_WRITE_4(sc, JME_RXMAC, reg); /* Configure general purpose reg0 */ reg = CSR_READ_4(sc, JME_GPREG0); reg &= ~GPREG0_PCC_UNIT_MASK; /* Set PCC timer resolution to micro-seconds unit. */ reg |= GPREG0_PCC_UNIT_US; /* * Disable all shadow register posting as we have to read * JME_INTR_STATUS register in jme_int_task. Also it seems * that it's hard to synchronize interrupt status between * hardware and software with shadow posting due to * requirements of bus_dmamap_sync(9). */ reg |= GPREG0_SH_POST_DW7_DIS | GPREG0_SH_POST_DW6_DIS | GPREG0_SH_POST_DW5_DIS | GPREG0_SH_POST_DW4_DIS | GPREG0_SH_POST_DW3_DIS | GPREG0_SH_POST_DW2_DIS | GPREG0_SH_POST_DW1_DIS | GPREG0_SH_POST_DW0_DIS; /* Disable posting of DW0. */ reg &= ~GPREG0_POST_DW0_ENB; /* Clear PME message. */ reg &= ~GPREG0_PME_ENB; /* Set PHY address. */ reg &= ~GPREG0_PHY_ADDR_MASK; reg |= sc->jme_phyaddr; CSR_WRITE_4(sc, JME_GPREG0, reg); /* Configure Tx queue 0 packet completion coalescing. */ reg = (sc->jme_tx_coal_to << PCCTX_COAL_TO_SHIFT) & PCCTX_COAL_TO_MASK; reg |= (sc->jme_tx_coal_pkt << PCCTX_COAL_PKT_SHIFT) & PCCTX_COAL_PKT_MASK; reg |= PCCTX_COAL_TXQ0; CSR_WRITE_4(sc, JME_PCCTX, reg); /* Configure Rx queue 0 packet completion coalescing. */ reg = (sc->jme_rx_coal_to << PCCRX_COAL_TO_SHIFT) & PCCRX_COAL_TO_MASK; reg |= (sc->jme_rx_coal_pkt << PCCRX_COAL_PKT_SHIFT) & PCCRX_COAL_PKT_MASK; CSR_WRITE_4(sc, JME_PCCRX0, reg); /* Configure shadow status block but don't enable posting. */ paddr = sc->jme_rdata.jme_ssb_block_paddr; CSR_WRITE_4(sc, JME_SHBASE_ADDR_HI, JME_ADDR_HI(paddr)); CSR_WRITE_4(sc, JME_SHBASE_ADDR_LO, JME_ADDR_LO(paddr)); /* Disable Timer 1 and Timer 2. */ CSR_WRITE_4(sc, JME_TIMER1, 0); CSR_WRITE_4(sc, JME_TIMER2, 0); /* Configure retry transmit period, retry limit value. */ CSR_WRITE_4(sc, JME_TXTRHD, ((TXTRHD_RT_PERIOD_DEFAULT << TXTRHD_RT_PERIOD_SHIFT) & TXTRHD_RT_PERIOD_MASK) | ((TXTRHD_RT_LIMIT_DEFAULT << TXTRHD_RT_LIMIT_SHIFT) & TXTRHD_RT_LIMIT_SHIFT)); /* Disable RSS. */ CSR_WRITE_4(sc, JME_RSSC, RSSC_DIS_RSS); /* Initialize the interrupt mask. */ CSR_WRITE_4(sc, JME_INTR_MASK_SET, JME_INTRS); CSR_WRITE_4(sc, JME_INTR_STATUS, 0xFFFFFFFF); /* * Enabling Tx/Rx DMA engines and Rx queue processing is * done after detection of valid link in jme_link_task. */ sc->jme_flags &= ~JME_FLAG_LINK; /* Set the current media. */ mii_mediachg(mii); callout_reset(&sc->jme_tick_ch, hz, jme_tick, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void jme_stop(struct jme_softc *sc) { struct ifnet *ifp; struct jme_txdesc *txd; struct jme_rxdesc *rxd; int i; JME_LOCK_ASSERT(sc); /* * Mark the interface down and cancel the watchdog timer. */ ifp = sc->jme_ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->jme_flags &= ~JME_FLAG_LINK; callout_stop(&sc->jme_tick_ch); sc->jme_watchdog_timer = 0; /* * Disable interrupts. */ CSR_WRITE_4(sc, JME_INTR_MASK_CLR, JME_INTRS); CSR_WRITE_4(sc, JME_INTR_STATUS, 0xFFFFFFFF); /* Disable updating shadow status block. */ CSR_WRITE_4(sc, JME_SHBASE_ADDR_LO, CSR_READ_4(sc, JME_SHBASE_ADDR_LO) & ~SHBASE_POST_ENB); /* Stop receiver, transmitter. */ jme_stop_rx(sc); jme_stop_tx(sc); /* Reclaim Rx/Tx buffers that have been completed. */ jme_rxintr(sc, JME_RX_RING_CNT); if (sc->jme_cdata.jme_rxhead != NULL) m_freem(sc->jme_cdata.jme_rxhead); JME_RXCHAIN_RESET(sc); jme_txeof(sc); /* * Free RX and TX mbufs still in the queues. */ for (i = 0; i < JME_RX_RING_CNT; i++) { rxd = &sc->jme_cdata.jme_rxdesc[i]; if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->jme_cdata.jme_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->jme_cdata.jme_rx_tag, rxd->rx_dmamap); m_freem(rxd->rx_m); rxd->rx_m = NULL; } } for (i = 0; i < JME_TX_RING_CNT; i++) { txd = &sc->jme_cdata.jme_txdesc[i]; if (txd->tx_m != NULL) { bus_dmamap_sync(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->jme_cdata.jme_tx_tag, txd->tx_dmamap); m_freem(txd->tx_m); txd->tx_m = NULL; txd->tx_ndesc = 0; } } jme_stats_update(sc); jme_stats_save(sc); } static void jme_stop_tx(struct jme_softc *sc) { uint32_t reg; int i; reg = CSR_READ_4(sc, JME_TXCSR); if ((reg & TXCSR_TX_ENB) == 0) return; reg &= ~TXCSR_TX_ENB; CSR_WRITE_4(sc, JME_TXCSR, reg); for (i = JME_TIMEOUT; i > 0; i--) { DELAY(1); if ((CSR_READ_4(sc, JME_TXCSR) & TXCSR_TX_ENB) == 0) break; } if (i == 0) device_printf(sc->jme_dev, "stopping transmitter timeout!\n"); } static void jme_stop_rx(struct jme_softc *sc) { uint32_t reg; int i; reg = CSR_READ_4(sc, JME_RXCSR); if ((reg & RXCSR_RX_ENB) == 0) return; reg &= ~RXCSR_RX_ENB; CSR_WRITE_4(sc, JME_RXCSR, reg); for (i = JME_TIMEOUT; i > 0; i--) { DELAY(1); if ((CSR_READ_4(sc, JME_RXCSR) & RXCSR_RX_ENB) == 0) break; } if (i == 0) device_printf(sc->jme_dev, "stopping recevier timeout!\n"); } static void jme_init_tx_ring(struct jme_softc *sc) { struct jme_ring_data *rd; struct jme_txdesc *txd; int i; sc->jme_cdata.jme_tx_prod = 0; sc->jme_cdata.jme_tx_cons = 0; sc->jme_cdata.jme_tx_cnt = 0; rd = &sc->jme_rdata; bzero(rd->jme_tx_ring, JME_TX_RING_SIZE); for (i = 0; i < JME_TX_RING_CNT; i++) { txd = &sc->jme_cdata.jme_txdesc[i]; txd->tx_m = NULL; txd->tx_desc = &rd->jme_tx_ring[i]; txd->tx_ndesc = 0; } bus_dmamap_sync(sc->jme_cdata.jme_tx_ring_tag, sc->jme_cdata.jme_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static void jme_init_ssb(struct jme_softc *sc) { struct jme_ring_data *rd; rd = &sc->jme_rdata; bzero(rd->jme_ssb_block, JME_SSB_SIZE); bus_dmamap_sync(sc->jme_cdata.jme_ssb_tag, sc->jme_cdata.jme_ssb_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static int jme_init_rx_ring(struct jme_softc *sc) { struct jme_ring_data *rd; struct jme_rxdesc *rxd; int i; sc->jme_cdata.jme_rx_cons = 0; JME_RXCHAIN_RESET(sc); atomic_set_int(&sc->jme_morework, 0); rd = &sc->jme_rdata; bzero(rd->jme_rx_ring, JME_RX_RING_SIZE); for (i = 0; i < JME_RX_RING_CNT; i++) { rxd = &sc->jme_cdata.jme_rxdesc[i]; rxd->rx_m = NULL; rxd->rx_desc = &rd->jme_rx_ring[i]; if (jme_newbuf(sc, rxd) != 0) return (ENOBUFS); } bus_dmamap_sync(sc->jme_cdata.jme_rx_ring_tag, sc->jme_cdata.jme_rx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static int jme_newbuf(struct jme_softc *sc, struct jme_rxdesc *rxd) { struct jme_desc *desc; struct mbuf *m; bus_dma_segment_t segs[1]; bus_dmamap_t map; int nsegs; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); /* * JMC250 has 64bit boundary alignment limitation so jme(4) * takes advantage of 10 bytes padding feature of hardware * in order not to copy entire frame to align IP header on * 32bit boundary. */ m->m_len = m->m_pkthdr.len = MCLBYTES; if (bus_dmamap_load_mbuf_sg(sc->jme_cdata.jme_rx_tag, sc->jme_cdata.jme_rx_sparemap, m, segs, &nsegs, 0) != 0) { m_freem(m); return (ENOBUFS); } KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->jme_cdata.jme_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->jme_cdata.jme_rx_tag, rxd->rx_dmamap); } map = rxd->rx_dmamap; rxd->rx_dmamap = sc->jme_cdata.jme_rx_sparemap; sc->jme_cdata.jme_rx_sparemap = map; bus_dmamap_sync(sc->jme_cdata.jme_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_PREREAD); rxd->rx_m = m; desc = rxd->rx_desc; desc->buflen = htole32(segs[0].ds_len); desc->addr_lo = htole32(JME_ADDR_LO(segs[0].ds_addr)); desc->addr_hi = htole32(JME_ADDR_HI(segs[0].ds_addr)); desc->flags = htole32(JME_RD_OWN | JME_RD_INTR | JME_RD_64BIT); return (0); } static void jme_set_vlan(struct jme_softc *sc) { struct ifnet *ifp; uint32_t reg; JME_LOCK_ASSERT(sc); ifp = sc->jme_ifp; reg = CSR_READ_4(sc, JME_RXMAC); reg &= ~RXMAC_VLAN_ENB; if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) reg |= RXMAC_VLAN_ENB; CSR_WRITE_4(sc, JME_RXMAC, reg); } static void jme_set_filter(struct jme_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; uint32_t crc; uint32_t mchash[2]; uint32_t rxcfg; JME_LOCK_ASSERT(sc); ifp = sc->jme_ifp; rxcfg = CSR_READ_4(sc, JME_RXMAC); rxcfg &= ~ (RXMAC_BROADCAST | RXMAC_PROMISC | RXMAC_MULTICAST | RXMAC_ALLMULTI); /* Always accept frames destined to our station address. */ rxcfg |= RXMAC_UNICAST; if ((ifp->if_flags & IFF_BROADCAST) != 0) rxcfg |= RXMAC_BROADCAST; if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { if ((ifp->if_flags & IFF_PROMISC) != 0) rxcfg |= RXMAC_PROMISC; if ((ifp->if_flags & IFF_ALLMULTI) != 0) rxcfg |= RXMAC_ALLMULTI; CSR_WRITE_4(sc, JME_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, JME_MAR1, 0xFFFFFFFF); CSR_WRITE_4(sc, JME_RXMAC, rxcfg); return; } /* * Set up the multicast address filter by passing all multicast * addresses through a CRC generator, and then using the low-order * 6 bits as an index into the 64 bit multicast hash table. The * high order bits select the register, while the rest of the bits * select the bit within the register. */ rxcfg |= RXMAC_MULTICAST; bzero(mchash, sizeof(mchash)); if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &sc->jme_ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; crc = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN); /* Just want the 6 least significant bits. */ crc &= 0x3f; /* Set the corresponding bit in the hash table. */ mchash[crc >> 5] |= 1 << (crc & 0x1f); } if_maddr_runlock(ifp); CSR_WRITE_4(sc, JME_MAR0, mchash[0]); CSR_WRITE_4(sc, JME_MAR1, mchash[1]); CSR_WRITE_4(sc, JME_RXMAC, rxcfg); } static void jme_stats_clear(struct jme_softc *sc) { JME_LOCK_ASSERT(sc); if ((sc->jme_flags & JME_FLAG_HWMIB) == 0) return; /* Disable and clear counters. */ CSR_WRITE_4(sc, JME_STATCSR, 0xFFFFFFFF); /* Activate hw counters. */ CSR_WRITE_4(sc, JME_STATCSR, 0); CSR_READ_4(sc, JME_STATCSR); bzero(&sc->jme_stats, sizeof(struct jme_hw_stats)); } static void jme_stats_save(struct jme_softc *sc) { JME_LOCK_ASSERT(sc); if ((sc->jme_flags & JME_FLAG_HWMIB) == 0) return; /* Save current counters. */ bcopy(&sc->jme_stats, &sc->jme_ostats, sizeof(struct jme_hw_stats)); /* Disable and clear counters. */ CSR_WRITE_4(sc, JME_STATCSR, 0xFFFFFFFF); } static void jme_stats_update(struct jme_softc *sc) { struct jme_hw_stats *stat, *ostat; uint32_t reg; JME_LOCK_ASSERT(sc); if ((sc->jme_flags & JME_FLAG_HWMIB) == 0) return; stat = &sc->jme_stats; ostat = &sc->jme_ostats; stat->tx_good_frames = CSR_READ_4(sc, JME_STAT_TXGOOD); stat->rx_good_frames = CSR_READ_4(sc, JME_STAT_RXGOOD); reg = CSR_READ_4(sc, JME_STAT_CRCMII); stat->rx_crc_errs = (reg & STAT_RX_CRC_ERR_MASK) >> STAT_RX_CRC_ERR_SHIFT; stat->rx_mii_errs = (reg & STAT_RX_MII_ERR_MASK) >> STAT_RX_MII_ERR_SHIFT; reg = CSR_READ_4(sc, JME_STAT_RXERR); stat->rx_fifo_oflows = (reg & STAT_RXERR_OFLOW_MASK) >> STAT_RXERR_OFLOW_SHIFT; stat->rx_desc_empty = (reg & STAT_RXERR_MPTY_MASK) >> STAT_RXERR_MPTY_SHIFT; reg = CSR_READ_4(sc, JME_STAT_FAIL); stat->rx_bad_frames = (reg & STAT_FAIL_RX_MASK) >> STAT_FAIL_RX_SHIFT; stat->tx_bad_frames = (reg & STAT_FAIL_TX_MASK) >> STAT_FAIL_TX_SHIFT; /* Account for previous counters. */ stat->rx_good_frames += ostat->rx_good_frames; stat->rx_crc_errs += ostat->rx_crc_errs; stat->rx_mii_errs += ostat->rx_mii_errs; stat->rx_fifo_oflows += ostat->rx_fifo_oflows; stat->rx_desc_empty += ostat->rx_desc_empty; stat->rx_bad_frames += ostat->rx_bad_frames; stat->tx_good_frames += ostat->tx_good_frames; stat->tx_bad_frames += ostat->tx_bad_frames; } static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) { int error, value; if (arg1 == NULL) return (EINVAL); value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if (error || req->newptr == NULL) return (error); if (value < low || value > high) return (EINVAL); *(int *)arg1 = value; return (0); } static int sysctl_hw_jme_tx_coal_to(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, PCCTX_COAL_TO_MIN, PCCTX_COAL_TO_MAX)); } static int sysctl_hw_jme_tx_coal_pkt(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, PCCTX_COAL_PKT_MIN, PCCTX_COAL_PKT_MAX)); } static int sysctl_hw_jme_rx_coal_to(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, PCCRX_COAL_TO_MIN, PCCRX_COAL_TO_MAX)); } static int sysctl_hw_jme_rx_coal_pkt(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, PCCRX_COAL_PKT_MIN, PCCRX_COAL_PKT_MAX)); } static int sysctl_hw_jme_proc_limit(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, JME_PROC_MIN, JME_PROC_MAX)); } Index: projects/ppc64/sys/dev/re/if_re.c =================================================================== --- projects/ppc64/sys/dev/re/if_re.c (revision 204271) +++ projects/ppc64/sys/dev/re/if_re.c (revision 204272) @@ -1,3169 +1,3177 @@ /*- * Copyright (c) 1997, 1998-2003 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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$"); /* * RealTek 8139C+/8169/8169S/8110S/8168/8111/8101E PCI NIC driver * * Written by Bill Paul * Senior Networking Software Engineer * Wind River Systems */ /* * This driver is designed to support RealTek's next generation of * 10/100 and 10/100/1000 PCI ethernet controllers. There are currently * seven devices in this family: the RTL8139C+, the RTL8169, the RTL8169S, * RTL8110S, the RTL8168, the RTL8111 and the RTL8101E. * * The 8139C+ is a 10/100 ethernet chip. It is backwards compatible * with the older 8139 family, however it also supports a special * C+ mode of operation that provides several new performance enhancing * features. These include: * * o Descriptor based DMA mechanism. Each descriptor represents * a single packet fragment. Data buffers may be aligned on * any byte boundary. * * o 64-bit DMA * * o TCP/IP checksum offload for both RX and TX * * o High and normal priority transmit DMA rings * * o VLAN tag insertion and extraction * * o TCP large send (segmentation offload) * * Like the 8139, the 8139C+ also has a built-in 10/100 PHY. The C+ * programming API is fairly straightforward. The RX filtering, EEPROM * access and PHY access is the same as it is on the older 8139 series * chips. * * The 8169 is a 64-bit 10/100/1000 gigabit ethernet MAC. It has almost the * same programming API and feature set as the 8139C+ with the following * differences and additions: * * o 1000Mbps mode * * o Jumbo frames * * o GMII and TBI ports/registers for interfacing with copper * or fiber PHYs * * o RX and TX DMA rings can have up to 1024 descriptors * (the 8139C+ allows a maximum of 64) * * o Slight differences in register layout from the 8139C+ * * The TX start and timer interrupt registers are at different locations * on the 8169 than they are on the 8139C+. Also, the status word in the * RX descriptor has a slightly different bit layout. The 8169 does not * have a built-in PHY. Most reference boards use a Marvell 88E1000 'Alaska' * copper gigE PHY. * * The 8169S/8110S 10/100/1000 devices have built-in copper gigE PHYs * (the 'S' stands for 'single-chip'). These devices have the same * programming API as the older 8169, but also have some vendor-specific * registers for the on-board PHY. The 8110S is a LAN-on-motherboard * part designed to be pin-compatible with the RealTek 8100 10/100 chip. * * This driver takes advantage of the RX and TX checksum offload and * VLAN tag insertion/extraction features. It also implements TX * interrupt moderation using the timer interrupt registers, which * significantly reduces TX interrupt load. There is also support * for jumbo frames, however the 8169/8169S/8110S can not transmit * jumbo frames larger than 7440, so the max MTU possible with this * driver is 7422 bytes. */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(re, pci, 1, 1, 1); MODULE_DEPEND(re, ether, 1, 1, 1); MODULE_DEPEND(re, miibus, 1, 1, 1); /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* Tunables. */ static int msi_disable = 0; TUNABLE_INT("hw.re.msi_disable", &msi_disable); static int prefer_iomap = 0; TUNABLE_INT("hw.re.prefer_iomap", &prefer_iomap); #define RE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) /* * Various supported device vendors/types and their names. */ static struct rl_type re_devs[] = { { DLINK_VENDORID, DLINK_DEVICEID_528T, 0, "D-Link DGE-528(T) Gigabit Ethernet Adapter" }, { RT_VENDORID, RT_DEVICEID_8139, 0, "RealTek 8139C+ 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8101E, 0, "RealTek 8101E/8102E/8102EL/8103E PCIe 10/100baseTX" }, { RT_VENDORID, RT_DEVICEID_8168, 0, "RealTek 8168/8168B/8168C/8168CP/8168D/8168DP/" "8111B/8111C/8111CP/8111DP PCIe Gigabit Ethernet" }, { RT_VENDORID, RT_DEVICEID_8169, 0, "RealTek 8169/8169S/8169SB(L)/8110S/8110SB(L) Gigabit Ethernet" }, { RT_VENDORID, RT_DEVICEID_8169SC, 0, "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" }, { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, 0, "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" }, { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, 0, "Linksys EG1032 (RTL8169S) Gigabit Ethernet" }, { USR_VENDORID, USR_DEVICEID_997902, 0, "US Robotics 997902 (RTL8169S) Gigabit Ethernet" } }; static struct rl_hwrev re_hwrevs[] = { { RL_HWREV_8139, RL_8139, "" }, { RL_HWREV_8139A, RL_8139, "A" }, { RL_HWREV_8139AG, RL_8139, "A-G" }, { RL_HWREV_8139B, RL_8139, "B" }, { RL_HWREV_8130, RL_8139, "8130" }, { RL_HWREV_8139C, RL_8139, "C" }, { RL_HWREV_8139D, RL_8139, "8139D/8100B/8100C" }, { RL_HWREV_8139CPLUS, RL_8139CPLUS, "C+"}, { RL_HWREV_8168_SPIN1, RL_8169, "8168"}, { RL_HWREV_8169, RL_8169, "8169"}, { RL_HWREV_8169S, RL_8169, "8169S"}, { RL_HWREV_8110S, RL_8169, "8110S"}, { RL_HWREV_8169_8110SB, RL_8169, "8169SB/8110SB"}, { RL_HWREV_8169_8110SC, RL_8169, "8169SC/8110SC"}, { RL_HWREV_8169_8110SBL, RL_8169, "8169SBL/8110SBL"}, { RL_HWREV_8169_8110SCE, RL_8169, "8169SC/8110SC"}, { RL_HWREV_8100, RL_8139, "8100"}, { RL_HWREV_8101, RL_8139, "8101"}, { RL_HWREV_8100E, RL_8169, "8100E"}, { RL_HWREV_8101E, RL_8169, "8101E"}, { RL_HWREV_8102E, RL_8169, "8102E"}, { RL_HWREV_8102EL, RL_8169, "8102EL"}, { RL_HWREV_8102EL_SPIN1, RL_8169, "8102EL"}, { RL_HWREV_8103E, RL_8169, "8103E"}, { RL_HWREV_8168_SPIN2, RL_8169, "8168"}, { RL_HWREV_8168_SPIN3, RL_8169, "8168"}, { RL_HWREV_8168C, RL_8169, "8168C/8111C"}, { RL_HWREV_8168C_SPIN2, RL_8169, "8168C/8111C"}, { RL_HWREV_8168CP, RL_8169, "8168CP/8111CP"}, { RL_HWREV_8168D, RL_8169, "8168D/8111D"}, { RL_HWREV_8168DP, RL_8169, "8168DP/8111DP"}, { 0, 0, NULL } }; static int re_probe (device_t); static int re_attach (device_t); static int re_detach (device_t); static int re_encap (struct rl_softc *, struct mbuf **); static void re_dma_map_addr (void *, bus_dma_segment_t *, int, int); static int re_allocmem (device_t, struct rl_softc *); static __inline void re_discard_rxbuf (struct rl_softc *, int); static int re_newbuf (struct rl_softc *, int); static int re_rx_list_init (struct rl_softc *); static int re_tx_list_init (struct rl_softc *); #ifdef RE_FIXUP_RX static __inline void re_fixup_rx (struct mbuf *); #endif static int re_rxeof (struct rl_softc *, int *); static void re_txeof (struct rl_softc *); #ifdef DEVICE_POLLING static int re_poll (struct ifnet *, enum poll_cmd, int); static int re_poll_locked (struct ifnet *, enum poll_cmd, int); #endif static int re_intr (void *); static void re_tick (void *); static void re_tx_task (void *, int); static void re_int_task (void *, int); static void re_start (struct ifnet *); static int re_ioctl (struct ifnet *, u_long, caddr_t); static void re_init (void *); static void re_init_locked (struct rl_softc *); static void re_stop (struct rl_softc *); static void re_watchdog (struct rl_softc *); static int re_suspend (device_t); static int re_resume (device_t); static int re_shutdown (device_t); static int re_ifmedia_upd (struct ifnet *); static void re_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void re_eeprom_putbyte (struct rl_softc *, int); static void re_eeprom_getword (struct rl_softc *, int, u_int16_t *); static void re_read_eeprom (struct rl_softc *, caddr_t, int, int); static int re_gmii_readreg (device_t, int, int); static int re_gmii_writereg (device_t, int, int, int); static int re_miibus_readreg (device_t, int, int); static int re_miibus_writereg (device_t, int, int, int); static void re_miibus_statchg (device_t); static void re_set_rxmode (struct rl_softc *); static void re_reset (struct rl_softc *); static void re_setwol (struct rl_softc *); static void re_clrwol (struct rl_softc *); #ifdef RE_DIAG static int re_diag (struct rl_softc *); #endif static device_method_t re_methods[] = { /* Device interface */ DEVMETHOD(device_probe, re_probe), DEVMETHOD(device_attach, re_attach), DEVMETHOD(device_detach, re_detach), DEVMETHOD(device_suspend, re_suspend), DEVMETHOD(device_resume, re_resume), DEVMETHOD(device_shutdown, re_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, re_miibus_readreg), DEVMETHOD(miibus_writereg, re_miibus_writereg), DEVMETHOD(miibus_statchg, re_miibus_statchg), { 0, 0 } }; static driver_t re_driver = { "re", re_methods, sizeof(struct rl_softc) }; static devclass_t re_devclass; DRIVER_MODULE(re, pci, re_driver, re_devclass, 0, 0); DRIVER_MODULE(miibus, re, miibus_driver, miibus_devclass, 0, 0); #define EE_SET(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) | x) #define EE_CLR(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) & ~x) /* * Send a read command and address to the EEPROM, check for ACK. */ static void re_eeprom_putbyte(struct rl_softc *sc, int addr) { int d, i; d = addr | (RL_9346_READ << sc->rl_eewidth); /* * Feed in each bit and strobe the clock. */ for (i = 1 << (sc->rl_eewidth + 3); i; i >>= 1) { if (d & i) { EE_SET(RL_EE_DATAIN); } else { EE_CLR(RL_EE_DATAIN); } DELAY(100); EE_SET(RL_EE_CLK); DELAY(150); EE_CLR(RL_EE_CLK); DELAY(100); } } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void re_eeprom_getword(struct rl_softc *sc, int addr, u_int16_t *dest) { int i; u_int16_t word = 0; /* * Send address of word we want to read. */ re_eeprom_putbyte(sc, addr); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { EE_SET(RL_EE_CLK); DELAY(100); if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) word |= i; EE_CLR(RL_EE_CLK); DELAY(100); } *dest = word; } /* * Read a sequence of words from the EEPROM. */ static void re_read_eeprom(struct rl_softc *sc, caddr_t dest, int off, int cnt) { int i; u_int16_t word = 0, *ptr; CSR_SETBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM); DELAY(100); for (i = 0; i < cnt; i++) { CSR_SETBIT_1(sc, RL_EECMD, RL_EE_SEL); re_eeprom_getword(sc, off + i, &word); CSR_CLRBIT_1(sc, RL_EECMD, RL_EE_SEL); ptr = (u_int16_t *)(dest + (i * 2)); *ptr = word; } CSR_CLRBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM); } static int re_gmii_readreg(device_t dev, int phy, int reg) { struct rl_softc *sc; u_int32_t rval; int i; if (phy != 1) return (0); sc = device_get_softc(dev); /* Let the rgephy driver read the GMEDIASTAT register */ if (reg == RL_GMEDIASTAT) { rval = CSR_READ_1(sc, RL_GMEDIASTAT); return (rval); } CSR_WRITE_4(sc, RL_PHYAR, reg << 16); DELAY(1000); for (i = 0; i < RL_PHY_TIMEOUT; i++) { rval = CSR_READ_4(sc, RL_PHYAR); if (rval & RL_PHYAR_BUSY) break; DELAY(100); } if (i == RL_PHY_TIMEOUT) { device_printf(sc->rl_dev, "PHY read failed\n"); return (0); } return (rval & RL_PHYAR_PHYDATA); } static int re_gmii_writereg(device_t dev, int phy, int reg, int data) { struct rl_softc *sc; u_int32_t rval; int i; sc = device_get_softc(dev); CSR_WRITE_4(sc, RL_PHYAR, (reg << 16) | (data & RL_PHYAR_PHYDATA) | RL_PHYAR_BUSY); DELAY(1000); for (i = 0; i < RL_PHY_TIMEOUT; i++) { rval = CSR_READ_4(sc, RL_PHYAR); if (!(rval & RL_PHYAR_BUSY)) break; DELAY(100); } if (i == RL_PHY_TIMEOUT) { device_printf(sc->rl_dev, "PHY write failed\n"); return (0); } return (0); } static int re_miibus_readreg(device_t dev, int phy, int reg) { struct rl_softc *sc; u_int16_t rval = 0; u_int16_t re8139_reg = 0; sc = device_get_softc(dev); if (sc->rl_type == RL_8169) { rval = re_gmii_readreg(dev, phy, reg); return (rval); } /* Pretend the internal PHY is only at address 0 */ if (phy) { return (0); } switch (reg) { case MII_BMCR: re8139_reg = RL_BMCR; break; case MII_BMSR: re8139_reg = RL_BMSR; break; case MII_ANAR: re8139_reg = RL_ANAR; break; case MII_ANER: re8139_reg = RL_ANER; break; case MII_ANLPAR: re8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); /* * Allow the rlphy driver to read the media status * register. If we have a link partner which does not * support NWAY, this is the register which will tell * us the results of parallel detection. */ case RL_MEDIASTAT: rval = CSR_READ_1(sc, RL_MEDIASTAT); return (rval); default: device_printf(sc->rl_dev, "bad phy register\n"); return (0); } rval = CSR_READ_2(sc, re8139_reg); if (sc->rl_type == RL_8139CPLUS && re8139_reg == RL_BMCR) { /* 8139C+ has different bit layout. */ rval &= ~(BMCR_LOOP | BMCR_ISO); } return (rval); } static int re_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rl_softc *sc; u_int16_t re8139_reg = 0; int rval = 0; sc = device_get_softc(dev); if (sc->rl_type == RL_8169) { rval = re_gmii_writereg(dev, phy, reg, data); return (rval); } /* Pretend the internal PHY is only at address 0 */ if (phy) return (0); switch (reg) { case MII_BMCR: re8139_reg = RL_BMCR; if (sc->rl_type == RL_8139CPLUS) { /* 8139C+ has different bit layout. */ data &= ~(BMCR_LOOP | BMCR_ISO); } break; case MII_BMSR: re8139_reg = RL_BMSR; break; case MII_ANAR: re8139_reg = RL_ANAR; break; case MII_ANER: re8139_reg = RL_ANER; break; case MII_ANLPAR: re8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); break; default: device_printf(sc->rl_dev, "bad phy register\n"); return (0); } CSR_WRITE_2(sc, re8139_reg, data); return (0); } static void re_miibus_statchg(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->rl_miibus); ifp = sc->rl_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; sc->rl_flags &= ~RL_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->rl_flags |= RL_FLAG_LINK; break; case IFM_1000_T: if ((sc->rl_flags & RL_FLAG_FASTETHER) != 0) break; sc->rl_flags |= RL_FLAG_LINK; break; default: break; } } /* * RealTek controllers does not provide any interface to * Tx/Rx MACs for resolved speed, duplex and flow-control * parameters. */ } /* * Set the RX configuration and 64-bit multicast hash filter. */ static void re_set_rxmode(struct rl_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; uint32_t hashes[2] = { 0, 0 }; uint32_t h, rxfilt; RL_LOCK_ASSERT(sc); ifp = sc->rl_ifp; rxfilt = RL_RXCFG_CONFIG | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_BROAD; if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { if (ifp->if_flags & IFF_PROMISC) rxfilt |= RL_RXCFG_RX_ALLPHYS; /* * Unlike other hardwares, we have to explicitly set * RL_RXCFG_RX_MULTI to receive multicast frames in * promiscuous mode. */ rxfilt |= RL_RXCFG_RX_MULTI; hashes[0] = hashes[1] = 0xffffffff; goto done; } if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } if_maddr_runlock(ifp); if (hashes[0] != 0 || hashes[1] != 0) { /* * For some unfathomable reason, RealTek decided to * reverse the order of the multicast hash registers * in the PCI Express parts. This means we have to * write the hash pattern in reverse order for those * devices. */ if ((sc->rl_flags & RL_FLAG_PCIE) != 0) { h = bswap32(hashes[0]); hashes[0] = bswap32(hashes[1]); hashes[1] = h; } rxfilt |= RL_RXCFG_RX_MULTI; } done: CSR_WRITE_4(sc, RL_MAR0, hashes[0]); CSR_WRITE_4(sc, RL_MAR4, hashes[1]); CSR_WRITE_4(sc, RL_RXCFG, rxfilt); } static void re_reset(struct rl_softc *sc) { int i; RL_LOCK_ASSERT(sc); CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); for (i = 0; i < RL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) break; } if (i == RL_TIMEOUT) device_printf(sc->rl_dev, "reset never completed!\n"); if ((sc->rl_flags & RL_FLAG_MACRESET) != 0) CSR_WRITE_1(sc, 0x82, 1); if (sc->rl_hwrev == RL_HWREV_8169S) re_gmii_writereg(sc->rl_dev, 1, 0x0b, 0); } #ifdef RE_DIAG /* * The following routine is designed to test for a defect on some * 32-bit 8169 cards. Some of these NICs have the REQ64# and ACK64# * lines connected to the bus, however for a 32-bit only card, they * should be pulled high. The result of this defect is that the * NIC will not work right if you plug it into a 64-bit slot: DMA * operations will be done with 64-bit transfers, which will fail * because the 64-bit data lines aren't connected. * * There's no way to work around this (short of talking a soldering * iron to the board), however we can detect it. The method we use * here is to put the NIC into digital loopback mode, set the receiver * to promiscuous mode, and then try to send a frame. We then compare * the frame data we sent to what was received. If the data matches, * then the NIC is working correctly, otherwise we know the user has * a defective NIC which has been mistakenly plugged into a 64-bit PCI * slot. In the latter case, there's no way the NIC can work correctly, * so we print out a message on the console and abort the device attach. */ static int re_diag(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; struct mbuf *m0; struct ether_header *eh; struct rl_desc *cur_rx; u_int16_t status; u_int32_t rxstat; int total_len, i, error = 0, phyaddr; u_int8_t dst[] = { 0x00, 'h', 'e', 'l', 'l', 'o' }; u_int8_t src[] = { 0x00, 'w', 'o', 'r', 'l', 'd' }; /* Allocate a single mbuf */ MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 == NULL) return (ENOBUFS); RL_LOCK(sc); /* * Initialize the NIC in test mode. This sets the chip up * so that it can send and receive frames, but performs the * following special functions: * - Puts receiver in promiscuous mode * - Enables digital loopback mode * - Leaves interrupts turned off */ ifp->if_flags |= IFF_PROMISC; sc->rl_testmode = 1; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; re_init_locked(sc); sc->rl_flags |= RL_FLAG_LINK; if (sc->rl_type == RL_8169) phyaddr = 1; else phyaddr = 0; re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_RESET); for (i = 0; i < RL_TIMEOUT; i++) { status = re_miibus_readreg(sc->rl_dev, phyaddr, MII_BMCR); if (!(status & BMCR_RESET)) break; } re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_LOOP); CSR_WRITE_2(sc, RL_ISR, RL_INTRS); DELAY(100000); /* Put some data in the mbuf */ eh = mtod(m0, struct ether_header *); bcopy ((char *)&dst, eh->ether_dhost, ETHER_ADDR_LEN); bcopy ((char *)&src, eh->ether_shost, ETHER_ADDR_LEN); eh->ether_type = htons(ETHERTYPE_IP); m0->m_pkthdr.len = m0->m_len = ETHER_MIN_LEN - ETHER_CRC_LEN; /* * Queue the packet, start transmission. * Note: IF_HANDOFF() ultimately calls re_start() for us. */ CSR_WRITE_2(sc, RL_ISR, 0xFFFF); RL_UNLOCK(sc); /* XXX: re_diag must not be called when in ALTQ mode */ IF_HANDOFF(&ifp->if_snd, m0, ifp); RL_LOCK(sc); m0 = NULL; /* Wait for it to propagate through the chip */ DELAY(100000); for (i = 0; i < RL_TIMEOUT; i++) { status = CSR_READ_2(sc, RL_ISR); CSR_WRITE_2(sc, RL_ISR, status); if ((status & (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) == (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) break; DELAY(10); } if (i == RL_TIMEOUT) { device_printf(sc->rl_dev, "diagnostic failed, failed to receive packet in" " loopback mode\n"); error = EIO; goto done; } /* * The packet should have been dumped into the first * entry in the RX DMA ring. Grab it from there. */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_POSTREAD); bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_desc[0].rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_desc[0].rx_dmamap); m0 = sc->rl_ldata.rl_rx_desc[0].rx_m; sc->rl_ldata.rl_rx_desc[0].rx_m = NULL; eh = mtod(m0, struct ether_header *); cur_rx = &sc->rl_ldata.rl_rx_list[0]; total_len = RL_RXBYTES(cur_rx); rxstat = le32toh(cur_rx->rl_cmdstat); if (total_len != ETHER_MIN_LEN) { device_printf(sc->rl_dev, "diagnostic failed, received short packet\n"); error = EIO; goto done; } /* Test that the received packet data matches what we sent. */ if (bcmp((char *)&eh->ether_dhost, (char *)&dst, ETHER_ADDR_LEN) || bcmp((char *)&eh->ether_shost, (char *)&src, ETHER_ADDR_LEN) || ntohs(eh->ether_type) != ETHERTYPE_IP) { device_printf(sc->rl_dev, "WARNING, DMA FAILURE!\n"); device_printf(sc->rl_dev, "expected TX data: %6D/%6D/0x%x\n", dst, ":", src, ":", ETHERTYPE_IP); device_printf(sc->rl_dev, "received RX data: %6D/%6D/0x%x\n", eh->ether_dhost, ":", eh->ether_shost, ":", ntohs(eh->ether_type)); device_printf(sc->rl_dev, "You may have a defective 32-bit " "NIC plugged into a 64-bit PCI slot.\n"); device_printf(sc->rl_dev, "Please re-install the NIC in a " "32-bit slot for proper operation.\n"); device_printf(sc->rl_dev, "Read the re(4) man page for more " "details.\n"); error = EIO; } done: /* Turn interface off, release resources */ sc->rl_testmode = 0; sc->rl_flags &= ~RL_FLAG_LINK; ifp->if_flags &= ~IFF_PROMISC; re_stop(sc); if (m0 != NULL) m_freem(m0); RL_UNLOCK(sc); return (error); } #endif /* * Probe for a RealTek 8139C+/8169/8110 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int re_probe(device_t dev) { struct rl_type *t; uint16_t devid, vendor; uint16_t revid, sdevid; int i; vendor = pci_get_vendor(dev); devid = pci_get_device(dev); revid = pci_get_revid(dev); sdevid = pci_get_subdevice(dev); if (vendor == LINKSYS_VENDORID && devid == LINKSYS_DEVICEID_EG1032) { if (sdevid != LINKSYS_SUBDEVICE_EG1032_REV3) { /* * Only attach to rev. 3 of the Linksys EG1032 adapter. * Rev. 2 is supported by sk(4). */ return (ENXIO); } } if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) { if (revid != 0x20) { /* 8139, let rl(4) take care of this device. */ return (ENXIO); } } t = re_devs; for (i = 0; i < sizeof(re_devs) / sizeof(re_devs[0]); i++, t++) { if (vendor == t->rl_vid && devid == t->rl_did) { device_set_desc(dev, t->rl_name); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } /* * Map a single buffer address. */ static void re_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *addr; if (error) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); addr = arg; *addr = segs->ds_addr; } static int re_allocmem(device_t dev, struct rl_softc *sc) { bus_size_t rx_list_size, tx_list_size; int error; int i; rx_list_size = sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc); tx_list_size = sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc); /* * Allocate the parent bus DMA tag appropriate for PCI. * In order to use DAC, RL_CPLUSCMD_PCI_DAC bit of RL_CPLUS_CMD * register should be set. However some RealTek chips are known * to be buggy on DAC handling, therefore disable DAC by limiting * DMA address space to 32bit. PCIe variants of RealTek chips * may not have the limitation but I took safer path. */ error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rl_parent_tag); if (error) { device_printf(dev, "could not allocate parent DMA tag\n"); return (error); } /* * Allocate map for TX mbufs. */ error = bus_dma_tag_create(sc->rl_parent_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * RL_NTXSEGS, RL_NTXSEGS, 4096, 0, NULL, NULL, &sc->rl_ldata.rl_tx_mtag); if (error) { device_printf(dev, "could not allocate TX DMA tag\n"); return (error); } /* * Allocate map for RX mbufs. */ error = bus_dma_tag_create(sc->rl_parent_tag, sizeof(uint64_t), 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->rl_ldata.rl_rx_mtag); if (error) { device_printf(dev, "could not allocate RX DMA tag\n"); return (error); } /* * Allocate map for TX descriptor list. */ error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, tx_list_size, 1, tx_list_size, 0, NULL, NULL, &sc->rl_ldata.rl_tx_list_tag); if (error) { device_printf(dev, "could not allocate TX DMA ring tag\n"); return (error); } /* Allocate DMA'able memory for the TX ring */ error = bus_dmamem_alloc(sc->rl_ldata.rl_tx_list_tag, (void **)&sc->rl_ldata.rl_tx_list, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_ldata.rl_tx_list_map); if (error) { device_printf(dev, "could not allocate TX DMA ring\n"); return (error); } /* Load the map for the TX ring. */ sc->rl_ldata.rl_tx_list_addr = 0; error = bus_dmamap_load(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, sc->rl_ldata.rl_tx_list, tx_list_size, re_dma_map_addr, &sc->rl_ldata.rl_tx_list_addr, BUS_DMA_NOWAIT); if (error != 0 || sc->rl_ldata.rl_tx_list_addr == 0) { device_printf(dev, "could not load TX DMA ring\n"); return (ENOMEM); } /* Create DMA maps for TX buffers */ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) { error = bus_dmamap_create(sc->rl_ldata.rl_tx_mtag, 0, &sc->rl_ldata.rl_tx_desc[i].tx_dmamap); if (error) { device_printf(dev, "could not create DMA map for TX\n"); return (error); } } /* * Allocate map for RX descriptor list. */ error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, rx_list_size, 1, rx_list_size, 0, NULL, NULL, &sc->rl_ldata.rl_rx_list_tag); if (error) { device_printf(dev, "could not create RX DMA ring tag\n"); return (error); } /* Allocate DMA'able memory for the RX ring */ error = bus_dmamem_alloc(sc->rl_ldata.rl_rx_list_tag, (void **)&sc->rl_ldata.rl_rx_list, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_ldata.rl_rx_list_map); if (error) { device_printf(dev, "could not allocate RX DMA ring\n"); return (error); } /* Load the map for the RX ring. */ sc->rl_ldata.rl_rx_list_addr = 0; error = bus_dmamap_load(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, sc->rl_ldata.rl_rx_list, rx_list_size, re_dma_map_addr, &sc->rl_ldata.rl_rx_list_addr, BUS_DMA_NOWAIT); if (error != 0 || sc->rl_ldata.rl_rx_list_addr == 0) { device_printf(dev, "could not load RX DMA ring\n"); return (ENOMEM); } /* Create DMA maps for RX buffers */ error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0, &sc->rl_ldata.rl_rx_sparemap); if (error) { device_printf(dev, "could not create spare DMA map for RX\n"); return (error); } for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) { error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0, &sc->rl_ldata.rl_rx_desc[i].rx_dmamap); if (error) { device_printf(dev, "could not create DMA map for RX\n"); return (error); } } return (0); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int re_attach(device_t dev) { u_char eaddr[ETHER_ADDR_LEN]; u_int16_t as[ETHER_ADDR_LEN / 2]; struct rl_softc *sc; struct ifnet *ifp; struct rl_hwrev *hw_rev; int hwrev; u_int16_t devid, re_did = 0; int error = 0, rid, i; int msic, reg; uint8_t cfg; sc = device_get_softc(dev); sc->rl_dev = dev; mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0); /* * Map control/status registers. */ pci_enable_busmaster(dev); devid = pci_get_device(dev); /* * Prefer memory space register mapping over IO space. * Because RTL8169SC does not seem to work when memory mapping * is used always activate io mapping. */ if (devid == RT_DEVICEID_8169SC) prefer_iomap = 1; if (prefer_iomap == 0) { sc->rl_res_id = PCIR_BAR(1); sc->rl_res_type = SYS_RES_MEMORY; /* RTL8168/8101E seems to use different BARs. */ if (devid == RT_DEVICEID_8168 || devid == RT_DEVICEID_8101E) sc->rl_res_id = PCIR_BAR(2); } else { sc->rl_res_id = PCIR_BAR(0); sc->rl_res_type = SYS_RES_IOPORT; } sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type, &sc->rl_res_id, RF_ACTIVE); if (sc->rl_res == NULL && prefer_iomap == 0) { sc->rl_res_id = PCIR_BAR(0); sc->rl_res_type = SYS_RES_IOPORT; sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type, &sc->rl_res_id, RF_ACTIVE); } if (sc->rl_res == NULL) { device_printf(dev, "couldn't map ports/memory\n"); error = ENXIO; goto fail; } sc->rl_btag = rman_get_bustag(sc->rl_res); sc->rl_bhandle = rman_get_bushandle(sc->rl_res); msic = 0; if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { sc->rl_flags |= RL_FLAG_PCIE; /* Set PCIe maximum read request size to 2048. */ if (pci_get_max_read_req(dev) < 2048) pci_set_max_read_req(dev, 2048); msic = pci_msi_count(dev); if (bootverbose) device_printf(dev, "MSI count : %d\n", msic); } if (msic > 0 && msi_disable == 0) { msic = 1; if (pci_alloc_msi(dev, &msic) == 0) { if (msic == RL_MSI_MESSAGES) { device_printf(dev, "Using %d MSI messages\n", msic); sc->rl_flags |= RL_FLAG_MSI; /* Explicitly set MSI enable bit. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); cfg = CSR_READ_1(sc, RL_CFG2); cfg |= RL_CFG2_MSI; CSR_WRITE_1(sc, RL_CFG2, cfg); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); } else pci_release_msi(dev); } } /* Allocate interrupt */ if ((sc->rl_flags & RL_FLAG_MSI) == 0) { rid = 0; sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->rl_irq[0] == NULL) { device_printf(dev, "couldn't allocate IRQ resources\n"); error = ENXIO; goto fail; } } else { for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) { sc->rl_irq[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->rl_irq[i] == NULL) { device_printf(dev, "couldn't llocate IRQ resources for " "message %d\n", rid); error = ENXIO; goto fail; } } } if ((sc->rl_flags & RL_FLAG_MSI) == 0) { CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); cfg = CSR_READ_1(sc, RL_CFG2); if ((cfg & RL_CFG2_MSI) != 0) { device_printf(dev, "turning off MSI enable bit.\n"); cfg &= ~RL_CFG2_MSI; CSR_WRITE_1(sc, RL_CFG2, cfg); } CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); } /* Reset the adapter. */ RL_LOCK(sc); re_reset(sc); RL_UNLOCK(sc); hw_rev = re_hwrevs; hwrev = CSR_READ_4(sc, RL_TXCFG); switch (hwrev & 0x70000000) { case 0x00000000: case 0x10000000: device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0xfc800000); hwrev &= (RL_TXCFG_HWREV | 0x80000000); break; default: device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0x7c800000); hwrev &= RL_TXCFG_HWREV; break; } device_printf(dev, "MAC rev. 0x%08x\n", hwrev & 0x00700000); while (hw_rev->rl_desc != NULL) { if (hw_rev->rl_rev == hwrev) { sc->rl_type = hw_rev->rl_type; sc->rl_hwrev = hw_rev->rl_rev; break; } hw_rev++; } if (hw_rev->rl_desc == NULL) { device_printf(dev, "Unknown H/W revision: 0x%08x\n", hwrev); error = ENXIO; goto fail; } switch (hw_rev->rl_rev) { case RL_HWREV_8139CPLUS: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_FASTETHER | RL_FLAG_AUTOPAD; break; case RL_HWREV_8100E: case RL_HWREV_8101E: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_PHYWAKE | RL_FLAG_FASTETHER; break; case RL_HWREV_8102E: case RL_HWREV_8102EL: case RL_HWREV_8102EL_SPIN1: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD; break; case RL_HWREV_8103E: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_MACSLEEP; break; case RL_HWREV_8168_SPIN1: case RL_HWREV_8168_SPIN2: sc->rl_flags |= RL_FLAG_WOLRXENB; /* FALLTHROUGH */ case RL_HWREV_8168_SPIN3: sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_MACSTAT; break; case RL_HWREV_8168C_SPIN2: sc->rl_flags |= RL_FLAG_MACSLEEP; /* FALLTHROUGH */ case RL_HWREV_8168C: if ((hwrev & 0x00700000) == 0x00200000) sc->rl_flags |= RL_FLAG_MACSLEEP; /* FALLTHROUGH */ case RL_HWREV_8168CP: case RL_HWREV_8168D: case RL_HWREV_8168DP: sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD; /* * These controllers support jumbo frame but it seems * that enabling it requires touching additional magic * registers. Depending on MAC revisions some * controllers need to disable checksum offload. So * disable jumbo frame until I have better idea what * it really requires to make it support. * RTL8168C/CP : supports up to 6KB jumbo frame. * RTL8111C/CP : supports up to 9KB jumbo frame. */ sc->rl_flags |= RL_FLAG_NOJUMBO; break; case RL_HWREV_8169_8110SB: case RL_HWREV_8169_8110SBL: case RL_HWREV_8169_8110SC: case RL_HWREV_8169_8110SCE: sc->rl_flags |= RL_FLAG_PHYWAKE; /* FALLTHROUGH */ case RL_HWREV_8169: case RL_HWREV_8169S: case RL_HWREV_8110S: sc->rl_flags |= RL_FLAG_MACRESET; break; default: break; } /* Enable PME. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); cfg = CSR_READ_1(sc, RL_CFG1); cfg |= RL_CFG1_PME; CSR_WRITE_1(sc, RL_CFG1, cfg); cfg = CSR_READ_1(sc, RL_CFG5); cfg &= RL_CFG5_PME_STS; CSR_WRITE_1(sc, RL_CFG5, cfg); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); if ((sc->rl_flags & RL_FLAG_PAR) != 0) { /* * XXX Should have a better way to extract station * address from EEPROM. */ for (i = 0; i < ETHER_ADDR_LEN; i++) eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i); } else { sc->rl_eewidth = RL_9356_ADDR_LEN; re_read_eeprom(sc, (caddr_t)&re_did, 0, 1); if (re_did != 0x8129) sc->rl_eewidth = RL_9346_ADDR_LEN; /* * Get station address from the EEPROM. */ re_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3); for (i = 0; i < ETHER_ADDR_LEN / 2; i++) as[i] = le16toh(as[i]); bcopy(as, eaddr, sizeof(eaddr)); } if (sc->rl_type == RL_8169) { /* Set RX length mask and number of descriptors. */ sc->rl_rxlenmask = RL_RDESC_STAT_GFRAGLEN; sc->rl_txstart = RL_GTXSTART; sc->rl_ldata.rl_tx_desc_cnt = RL_8169_TX_DESC_CNT; sc->rl_ldata.rl_rx_desc_cnt = RL_8169_RX_DESC_CNT; } else { /* Set RX length mask and number of descriptors. */ sc->rl_rxlenmask = RL_RDESC_STAT_FRAGLEN; sc->rl_txstart = RL_TXSTART; sc->rl_ldata.rl_tx_desc_cnt = RL_8139_TX_DESC_CNT; sc->rl_ldata.rl_rx_desc_cnt = RL_8139_RX_DESC_CNT; } error = re_allocmem(dev, sc); if (error) goto fail; ifp = sc->rl_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } /* Take controller out of deep sleep mode. */ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80) CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) | 0x01); else CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) & ~0x01); } /* Take PHY out of power down mode. */ if ((sc->rl_flags & RL_FLAG_PHYWAKE) != 0) { re_gmii_writereg(dev, 1, 0x1f, 0); re_gmii_writereg(dev, 1, 0x0e, 0); } /* Do MII setup */ if (mii_phy_probe(dev, &sc->rl_miibus, re_ifmedia_upd, re_ifmedia_sts)) { device_printf(dev, "MII without any phy!\n"); error = ENXIO; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = re_ioctl; ifp->if_start = re_start; ifp->if_hwassist = RE_CSUM_FEATURES; ifp->if_capabilities = IFCAP_HWCSUM; ifp->if_capenable = ifp->if_capabilities; ifp->if_init = re_init; IFQ_SET_MAXLEN(&ifp->if_snd, RL_IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = RL_IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); TASK_INIT(&sc->rl_txtask, 1, re_tx_task, ifp); TASK_INIT(&sc->rl_inttask, 0, re_int_task, sc); /* * XXX * Still have no idea how to make TSO work on 8168C, 8168CP, * 8111C and 8111CP. */ if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) { ifp->if_hwassist |= CSUM_TSO; - ifp->if_capabilities |= IFCAP_TSO4; + ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_VLAN_HWTSO; } /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* VLAN capability setup */ ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; if (ifp->if_capabilities & IFCAP_HWCSUM) ifp->if_capabilities |= IFCAP_VLAN_HWCSUM; /* Enable WOL if PM is supported. */ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, ®) == 0) ifp->if_capabilities |= IFCAP_WOL; ifp->if_capenable = ifp->if_capabilities; /* * Don't enable TSO by default. Under certain * circumtances the controller generated corrupted * packets in TSO size. */ ifp->if_hwassist &= ~CSUM_TSO; - ifp->if_capenable &= ~IFCAP_TSO4; + ifp->if_capenable &= ~(IFCAP_TSO4 | IFCAP_VLAN_HWTSO); #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif /* * Tell the upper layer(s) we support long frames. * Must appear after the call to ether_ifattach() because * ether_ifattach() sets ifi_hdrlen to the default value. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); #ifdef RE_DIAG /* * Perform hardware diagnostic on the original RTL8169. * Some 32-bit cards were incorrectly wired and would * malfunction if plugged into a 64-bit slot. */ if (hwrev == RL_HWREV_8169) { error = re_diag(sc); if (error) { device_printf(dev, "attach aborted due to hardware diag failure\n"); ether_ifdetach(ifp); goto fail; } } #endif /* Hook interrupt last to avoid having to lock softc */ if ((sc->rl_flags & RL_FLAG_MSI) == 0) error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, &sc->rl_intrhand[0]); else { for (i = 0; i < RL_MSI_MESSAGES; i++) { error = bus_setup_intr(dev, sc->rl_irq[i], INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, &sc->rl_intrhand[i]); if (error != 0) break; } } if (error) { device_printf(dev, "couldn't set up irq\n"); ether_ifdetach(ifp); } fail: if (error) re_detach(dev); return (error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int re_detach(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; int i, rid; sc = device_get_softc(dev); ifp = sc->rl_ifp; KASSERT(mtx_initialized(&sc->rl_mtx), ("re mutex not initialized")); /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif RL_LOCK(sc); #if 0 sc->suspended = 1; #endif re_stop(sc); RL_UNLOCK(sc); callout_drain(&sc->rl_stat_callout); taskqueue_drain(taskqueue_fast, &sc->rl_inttask); taskqueue_drain(taskqueue_fast, &sc->rl_txtask); /* * Force off the IFF_UP flag here, in case someone * still had a BPF descriptor attached to this * interface. If they do, ether_ifdetach() will cause * the BPF code to try and clear the promisc mode * flag, which will bubble down to re_ioctl(), * which will try to call re_init() again. This will * turn the NIC back on and restart the MII ticker, * which will panic the system when the kernel tries * to invoke the re_tick() function that isn't there * anymore. */ ifp->if_flags &= ~IFF_UP; ether_ifdetach(ifp); } if (sc->rl_miibus) device_delete_child(dev, sc->rl_miibus); bus_generic_detach(dev); /* * The rest is resource deallocation, so we should already be * stopped here. */ for (i = 0; i < RL_MSI_MESSAGES; i++) { if (sc->rl_intrhand[i] != NULL) { bus_teardown_intr(dev, sc->rl_irq[i], sc->rl_intrhand[i]); sc->rl_intrhand[i] = NULL; } } if (ifp != NULL) if_free(ifp); if ((sc->rl_flags & RL_FLAG_MSI) == 0) { if (sc->rl_irq[0] != NULL) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq[0]); sc->rl_irq[0] = NULL; } } else { for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) { if (sc->rl_irq[i] != NULL) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->rl_irq[i]); sc->rl_irq[i] = NULL; } } pci_release_msi(dev); } if (sc->rl_res) bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id, sc->rl_res); /* Unload and free the RX DMA ring memory and map */ if (sc->rl_ldata.rl_rx_list_tag) { bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map); bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list, sc->rl_ldata.rl_rx_list_map); bus_dma_tag_destroy(sc->rl_ldata.rl_rx_list_tag); } /* Unload and free the TX DMA ring memory and map */ if (sc->rl_ldata.rl_tx_list_tag) { bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map); bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list, sc->rl_ldata.rl_tx_list_map); bus_dma_tag_destroy(sc->rl_ldata.rl_tx_list_tag); } /* Destroy all the RX and TX buffer maps */ if (sc->rl_ldata.rl_tx_mtag) { for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) bus_dmamap_destroy(sc->rl_ldata.rl_tx_mtag, sc->rl_ldata.rl_tx_desc[i].tx_dmamap); bus_dma_tag_destroy(sc->rl_ldata.rl_tx_mtag); } if (sc->rl_ldata.rl_rx_mtag) { for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_desc[i].rx_dmamap); if (sc->rl_ldata.rl_rx_sparemap) bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_sparemap); bus_dma_tag_destroy(sc->rl_ldata.rl_rx_mtag); } /* Unload and free the stats buffer and map */ if (sc->rl_ldata.rl_stag) { bus_dmamap_unload(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_rx_list_map); bus_dmamem_free(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_stats, sc->rl_ldata.rl_smap); bus_dma_tag_destroy(sc->rl_ldata.rl_stag); } if (sc->rl_parent_tag) bus_dma_tag_destroy(sc->rl_parent_tag); mtx_destroy(&sc->rl_mtx); return (0); } static __inline void re_discard_rxbuf(struct rl_softc *sc, int idx) { struct rl_desc *desc; struct rl_rxdesc *rxd; uint32_t cmdstat; rxd = &sc->rl_ldata.rl_rx_desc[idx]; desc = &sc->rl_ldata.rl_rx_list[idx]; desc->rl_vlanctl = 0; cmdstat = rxd->rx_size; if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1) cmdstat |= RL_RDESC_CMD_EOR; desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN); } static int re_newbuf(struct rl_softc *sc, int idx) { struct mbuf *m; struct rl_rxdesc *rxd; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct rl_desc *desc; uint32_t cmdstat; int error, nsegs; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; #ifdef RE_FIXUP_RX /* * This is part of an evil trick to deal with non-x86 platforms. * The RealTek chip requires RX buffers to be aligned on 64-bit * boundaries, but that will hose non-x86 machines. To get around * this, we leave some empty space at the start of each buffer * and for non-x86 hosts, we copy the buffer back six bytes * to achieve word alignment. This is slightly more efficient * than allocating a new buffer, copying the contents, and * discarding the old buffer. */ m_adj(m, RE_ETHER_ALIGN); #endif error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { m_freem(m); return (ENOBUFS); } KASSERT(nsegs == 1, ("%s: %d segment returned!", __func__, nsegs)); rxd = &sc->rl_ldata.rl_rx_desc[idx]; if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap); } rxd->rx_m = m; map = rxd->rx_dmamap; rxd->rx_dmamap = sc->rl_ldata.rl_rx_sparemap; rxd->rx_size = segs[0].ds_len; sc->rl_ldata.rl_rx_sparemap = map; bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap, BUS_DMASYNC_PREREAD); desc = &sc->rl_ldata.rl_rx_list[idx]; desc->rl_vlanctl = 0; desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[0].ds_addr)); desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[0].ds_addr)); cmdstat = segs[0].ds_len; if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1) cmdstat |= RL_RDESC_CMD_EOR; desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN); return (0); } #ifdef RE_FIXUP_RX static __inline void re_fixup_rx(struct mbuf *m) { int i; uint16_t *src, *dst; src = mtod(m, uint16_t *); dst = src - (RE_ETHER_ALIGN - ETHER_ALIGN) / sizeof *src; for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) *dst++ = *src++; m->m_data -= RE_ETHER_ALIGN - ETHER_ALIGN; } #endif static int re_tx_list_init(struct rl_softc *sc) { struct rl_desc *desc; int i; RL_LOCK_ASSERT(sc); bzero(sc->rl_ldata.rl_tx_list, sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc)); for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) sc->rl_ldata.rl_tx_desc[i].tx_m = NULL; /* Set EOR. */ desc = &sc->rl_ldata.rl_tx_list[sc->rl_ldata.rl_tx_desc_cnt - 1]; desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOR); bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->rl_ldata.rl_tx_prodidx = 0; sc->rl_ldata.rl_tx_considx = 0; sc->rl_ldata.rl_tx_free = sc->rl_ldata.rl_tx_desc_cnt; return (0); } static int re_rx_list_init(struct rl_softc *sc) { int error, i; bzero(sc->rl_ldata.rl_rx_list, sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc)); for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) { sc->rl_ldata.rl_rx_desc[i].rx_m = NULL; if ((error = re_newbuf(sc, i)) != 0) return (error); } /* Flush the RX descriptors */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); sc->rl_ldata.rl_rx_prodidx = 0; sc->rl_head = sc->rl_tail = NULL; return (0); } /* * RX handler for C+ and 8169. For the gigE chips, we support * the reception of jumbo frames that have been fragmented * across multiple 2K mbuf cluster buffers. */ static int re_rxeof(struct rl_softc *sc, int *rx_npktsp) { struct mbuf *m; struct ifnet *ifp; int i, total_len; struct rl_desc *cur_rx; u_int32_t rxstat, rxvlan; int maxpkt = 16, rx_npkts = 0; RL_LOCK_ASSERT(sc); ifp = sc->rl_ifp; /* Invalidate the descriptor memory */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (i = sc->rl_ldata.rl_rx_prodidx; maxpkt > 0; i = RL_RX_DESC_NXT(sc, i)) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) break; cur_rx = &sc->rl_ldata.rl_rx_list[i]; rxstat = le32toh(cur_rx->rl_cmdstat); if ((rxstat & RL_RDESC_STAT_OWN) != 0) break; total_len = rxstat & sc->rl_rxlenmask; rxvlan = le32toh(cur_rx->rl_vlanctl); m = sc->rl_ldata.rl_rx_desc[i].rx_m; if (!(rxstat & RL_RDESC_STAT_EOF)) { if (re_newbuf(sc, i) != 0) { /* * If this is part of a multi-fragment packet, * discard all the pieces. */ if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } re_discard_rxbuf(sc, i); continue; } m->m_len = RE_RX_DESC_BUFLEN; if (sc->rl_head == NULL) sc->rl_head = sc->rl_tail = m; else { m->m_flags &= ~M_PKTHDR; sc->rl_tail->m_next = m; sc->rl_tail = m; } continue; } /* * NOTE: for the 8139C+, the frame length field * is always 12 bits in size, but for the gigE chips, * it is 13 bits (since the max RX frame length is 16K). * Unfortunately, all 32 bits in the status word * were already used, so to make room for the extra * length bit, RealTek took out the 'frame alignment * error' bit and shifted the other status bits * over one slot. The OWN, EOR, FS and LS bits are * still in the same places. We have already extracted * the frame length and checked the OWN bit, so rather * than using an alternate bit mapping, we shift the * status bits one space to the right so we can evaluate * them using the 8169 status as though it was in the * same format as that of the 8139C+. */ if (sc->rl_type == RL_8169) rxstat >>= 1; /* * if total_len > 2^13-1, both _RXERRSUM and _GIANT will be * set, but if CRC is clear, it will still be a valid frame. */ if (rxstat & RL_RDESC_STAT_RXERRSUM && !(total_len > 8191 && (rxstat & RL_RDESC_STAT_ERRS) == RL_RDESC_STAT_GIANT)) { ifp->if_ierrors++; /* * If this is part of a multi-fragment packet, * discard all the pieces. */ if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } re_discard_rxbuf(sc, i); continue; } /* * If allocating a replacement mbuf fails, * reload the current one. */ if (re_newbuf(sc, i) != 0) { ifp->if_iqdrops++; if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } re_discard_rxbuf(sc, i); continue; } if (sc->rl_head != NULL) { m->m_len = total_len % RE_RX_DESC_BUFLEN; if (m->m_len == 0) m->m_len = RE_RX_DESC_BUFLEN; /* * Special case: if there's 4 bytes or less * in this buffer, the mbuf can be discarded: * the last 4 bytes is the CRC, which we don't * care about anyway. */ if (m->m_len <= ETHER_CRC_LEN) { sc->rl_tail->m_len -= (ETHER_CRC_LEN - m->m_len); m_freem(m); } else { m->m_len -= ETHER_CRC_LEN; m->m_flags &= ~M_PKTHDR; sc->rl_tail->m_next = m; } m = sc->rl_head; sc->rl_head = sc->rl_tail = NULL; m->m_pkthdr.len = total_len - ETHER_CRC_LEN; } else m->m_pkthdr.len = m->m_len = (total_len - ETHER_CRC_LEN); #ifdef RE_FIXUP_RX re_fixup_rx(m); #endif ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; /* Do RX checksumming if enabled */ if (ifp->if_capenable & IFCAP_RXCSUM) { if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) { /* Check IP header checksum */ if (rxstat & RL_RDESC_STAT_PROTOID) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (!(rxstat & RL_RDESC_STAT_IPSUMBAD)) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; /* Check TCP/UDP checksum */ if ((RL_TCPPKT(rxstat) && !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) || (RL_UDPPKT(rxstat) && !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID|CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } else { /* * RTL8168C/RTL816CP/RTL8111C/RTL8111CP */ if ((rxstat & RL_RDESC_STAT_PROTOID) && (rxvlan & RL_RDESC_IPV4)) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (!(rxstat & RL_RDESC_STAT_IPSUMBAD) && (rxvlan & RL_RDESC_IPV4)) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if (((rxstat & RL_RDESC_STAT_TCP) && !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) || ((rxstat & RL_RDESC_STAT_UDP) && !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID|CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } } maxpkt--; if (rxvlan & RL_RDESC_VLANCTL_TAG) { m->m_pkthdr.ether_vtag = bswap16((rxvlan & RL_RDESC_VLANCTL_DATA)); m->m_flags |= M_VLANTAG; } RL_UNLOCK(sc); (*ifp->if_input)(ifp, m); RL_LOCK(sc); rx_npkts++; } /* Flush the RX DMA ring */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); sc->rl_ldata.rl_rx_prodidx = i; if (rx_npktsp != NULL) *rx_npktsp = rx_npkts; if (maxpkt) return(EAGAIN); return(0); } static void re_txeof(struct rl_softc *sc) { struct ifnet *ifp; struct rl_txdesc *txd; u_int32_t txstat; int cons; cons = sc->rl_ldata.rl_tx_considx; if (cons == sc->rl_ldata.rl_tx_prodidx) return; ifp = sc->rl_ifp; /* Invalidate the TX descriptor list */ bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (; cons != sc->rl_ldata.rl_tx_prodidx; cons = RL_TX_DESC_NXT(sc, cons)) { txstat = le32toh(sc->rl_ldata.rl_tx_list[cons].rl_cmdstat); if (txstat & RL_TDESC_STAT_OWN) break; /* * We only stash mbufs in the last descriptor * in a fragment chain, which also happens to * be the only place where the TX status bits * are valid. */ if (txstat & RL_TDESC_CMD_EOF) { txd = &sc->rl_ldata.rl_tx_desc[cons]; bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap); KASSERT(txd->tx_m != NULL, ("%s: freeing NULL mbufs!", __func__)); m_freem(txd->tx_m); txd->tx_m = NULL; if (txstat & (RL_TDESC_STAT_EXCESSCOL| RL_TDESC_STAT_COLCNT)) ifp->if_collisions++; if (txstat & RL_TDESC_STAT_TXERRSUM) ifp->if_oerrors++; else ifp->if_opackets++; } sc->rl_ldata.rl_tx_free++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } sc->rl_ldata.rl_tx_considx = cons; /* No changes made to the TX ring, so no flush needed */ if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) { #ifdef RE_TX_MODERATION /* * If not all descriptors have been reaped yet, reload * the timer so that we will eventually get another * interrupt that will cause us to re-enter this routine. * This is done in case the transmitter has gone idle. */ CSR_WRITE_4(sc, RL_TIMERCNT, 1); #endif } else sc->rl_watchdog_timer = 0; } static void re_tick(void *xsc) { struct rl_softc *sc; struct mii_data *mii; sc = xsc; RL_LOCK_ASSERT(sc); mii = device_get_softc(sc->rl_miibus); mii_tick(mii); if ((sc->rl_flags & RL_FLAG_LINK) == 0) re_miibus_statchg(sc->rl_dev); /* * Reclaim transmitted frames here. Technically it is not * necessary to do here but it ensures periodic reclamation * regardless of Tx completion interrupt which seems to be * lost on PCIe based controllers under certain situations. */ re_txeof(sc); re_watchdog(sc); callout_reset(&sc->rl_stat_callout, hz, re_tick, sc); } #ifdef DEVICE_POLLING static int re_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; int rx_npkts = 0; RL_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) rx_npkts = re_poll_locked(ifp, cmd, count); RL_UNLOCK(sc); return (rx_npkts); } static int re_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; int rx_npkts; RL_LOCK_ASSERT(sc); sc->rxcycles = count; re_rxeof(sc, &rx_npkts); re_txeof(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int16_t status; status = CSR_READ_2(sc, RL_ISR); if (status == 0xffff) return (rx_npkts); if (status) CSR_WRITE_2(sc, RL_ISR, status); if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) && (sc->rl_flags & RL_FLAG_PCIE)) CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); /* * XXX check behaviour on receiver stalls. */ if (status & RL_ISR_SYSTEM_ERR) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; re_init_locked(sc); } } return (rx_npkts); } #endif /* DEVICE_POLLING */ static int re_intr(void *arg) { struct rl_softc *sc; uint16_t status; sc = arg; status = CSR_READ_2(sc, RL_ISR); if (status == 0xFFFF || (status & RL_INTRS_CPLUS) == 0) return (FILTER_STRAY); CSR_WRITE_2(sc, RL_IMR, 0); taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); return (FILTER_HANDLED); } static void re_int_task(void *arg, int npending) { struct rl_softc *sc; struct ifnet *ifp; u_int16_t status; int rval = 0; sc = arg; ifp = sc->rl_ifp; RL_LOCK(sc); status = CSR_READ_2(sc, RL_ISR); CSR_WRITE_2(sc, RL_ISR, status); if (sc->suspended || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { RL_UNLOCK(sc); return; } #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { RL_UNLOCK(sc); return; } #endif if (status & (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_FIFO_OFLOW)) rval = re_rxeof(sc, NULL); /* * Some chips will ignore a second TX request issued * while an existing transmission is in progress. If * the transmitter goes idle but there are still * packets waiting to be sent, we need to restart the * channel here to flush them out. This only seems to * be required with the PCIe devices. */ if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) && (sc->rl_flags & RL_FLAG_PCIE)) CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); if (status & ( #ifdef RE_TX_MODERATION RL_ISR_TIMEOUT_EXPIRED| #else RL_ISR_TX_OK| #endif RL_ISR_TX_ERR|RL_ISR_TX_DESC_UNAVAIL)) re_txeof(sc); if (status & RL_ISR_SYSTEM_ERR) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; re_init_locked(sc); } if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); RL_UNLOCK(sc); if ((CSR_READ_2(sc, RL_ISR) & RL_INTRS_CPLUS) || rval) { taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); return; } CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); } static int re_encap(struct rl_softc *sc, struct mbuf **m_head) { struct rl_txdesc *txd, *txd_last; bus_dma_segment_t segs[RL_NTXSEGS]; bus_dmamap_t map; struct mbuf *m_new; struct rl_desc *desc; int nsegs, prod; int i, error, ei, si; int padlen; uint32_t cmdstat, csum_flags, vlanctl; RL_LOCK_ASSERT(sc); M_ASSERTPKTHDR((*m_head)); /* * With some of the RealTek chips, using the checksum offload * support in conjunction with the autopadding feature results * in the transmission of corrupt frames. For example, if we * need to send a really small IP fragment that's less than 60 * bytes in size, and IP header checksumming is enabled, the * resulting ethernet frame that appears on the wire will * have garbled payload. To work around this, if TX IP checksum * offload is enabled, we always manually pad short frames out * to the minimum ethernet frame size. */ if ((sc->rl_flags & RL_FLAG_AUTOPAD) == 0 && (*m_head)->m_pkthdr.len < RL_IP4CSUMTX_PADLEN && ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) != 0) { padlen = RL_MIN_FRAMELEN - (*m_head)->m_pkthdr.len; if (M_WRITABLE(*m_head) == 0) { /* Get a writable copy. */ m_new = m_dup(*m_head, M_DONTWAIT); m_freem(*m_head); if (m_new == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m_new; } if ((*m_head)->m_next != NULL || M_TRAILINGSPACE(*m_head) < padlen) { m_new = m_defrag(*m_head, M_DONTWAIT); if (m_new == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } } else m_new = *m_head; /* * Manually pad short frames, and zero the pad space * to avoid leaking data. */ bzero(mtod(m_new, char *) + m_new->m_pkthdr.len, padlen); m_new->m_pkthdr.len += padlen; m_new->m_len = m_new->m_pkthdr.len; *m_head = m_new; } prod = sc->rl_ldata.rl_tx_prodidx; txd = &sc->rl_ldata.rl_tx_desc[prod]; error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { m_new = m_collapse(*m_head, M_DONTWAIT, RL_NTXSEGS); if (m_new == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } *m_head = m_new; error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (error); } } else if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* Check for number of available descriptors. */ if (sc->rl_ldata.rl_tx_free - nsegs <= 1) { bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap); return (ENOBUFS); } bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, BUS_DMASYNC_PREWRITE); /* * Set up checksum offload. Note: checksum offload bits must * appear in all descriptors of a multi-descriptor transmit * attempt. This is according to testing done with an 8169 * chip. This is a requirement. */ vlanctl = 0; csum_flags = 0; if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0) csum_flags = RL_TDESC_CMD_LGSEND | ((uint32_t)(*m_head)->m_pkthdr.tso_segsz << RL_TDESC_CMD_MSSVAL_SHIFT); else { /* * Unconditionally enable IP checksum if TCP or UDP * checksum is required. Otherwise, TCP/UDP checksum * does't make effects. */ if (((*m_head)->m_pkthdr.csum_flags & RE_CSUM_FEATURES) != 0) { if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) { csum_flags |= RL_TDESC_CMD_IPCSUM; if (((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) != 0) csum_flags |= RL_TDESC_CMD_TCPCSUM; if (((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) != 0) csum_flags |= RL_TDESC_CMD_UDPCSUM; } else { vlanctl |= RL_TDESC_CMD_IPCSUMV2; if (((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) != 0) vlanctl |= RL_TDESC_CMD_TCPCSUMV2; if (((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) != 0) vlanctl |= RL_TDESC_CMD_UDPCSUMV2; } } } /* * Set up hardware VLAN tagging. Note: vlan tag info must * appear in all descriptors of a multi-descriptor * transmission attempt. */ if ((*m_head)->m_flags & M_VLANTAG) vlanctl |= bswap16((*m_head)->m_pkthdr.ether_vtag) | RL_TDESC_VLANCTL_TAG; si = prod; for (i = 0; i < nsegs; i++, prod = RL_TX_DESC_NXT(sc, prod)) { desc = &sc->rl_ldata.rl_tx_list[prod]; desc->rl_vlanctl = htole32(vlanctl); desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[i].ds_addr)); desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[i].ds_addr)); cmdstat = segs[i].ds_len; if (i != 0) cmdstat |= RL_TDESC_CMD_OWN; if (prod == sc->rl_ldata.rl_tx_desc_cnt - 1) cmdstat |= RL_TDESC_CMD_EOR; desc->rl_cmdstat = htole32(cmdstat | csum_flags); sc->rl_ldata.rl_tx_free--; } /* Update producer index. */ sc->rl_ldata.rl_tx_prodidx = prod; /* Set EOF on the last descriptor. */ ei = RL_TX_DESC_PRV(sc, prod); desc = &sc->rl_ldata.rl_tx_list[ei]; desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOF); desc = &sc->rl_ldata.rl_tx_list[si]; /* Set SOF and transfer ownership of packet to the chip. */ desc->rl_cmdstat |= htole32(RL_TDESC_CMD_OWN | RL_TDESC_CMD_SOF); /* * Insure that the map for this transmission * is placed at the array index of the last descriptor * in this chain. (Swap last and first dmamaps.) */ txd_last = &sc->rl_ldata.rl_tx_desc[ei]; map = txd->tx_dmamap; txd->tx_dmamap = txd_last->tx_dmamap; txd_last->tx_dmamap = map; txd_last->tx_m = *m_head; return (0); } static void re_tx_task(void *arg, int npending) { struct ifnet *ifp; ifp = arg; re_start(ifp); } /* * Main transmit routine for C+ and gigE NICs. */ static void re_start(struct ifnet *ifp) { struct rl_softc *sc; struct mbuf *m_head; int queued; sc = ifp->if_softc; RL_LOCK(sc); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) { RL_UNLOCK(sc); return; } for (queued = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->rl_ldata.rl_tx_free > 1;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (re_encap(sc, &m_head) != 0) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ ETHER_BPF_MTAP(ifp, m_head); queued++; } if (queued == 0) { #ifdef RE_TX_MODERATION if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) CSR_WRITE_4(sc, RL_TIMERCNT, 1); #endif RL_UNLOCK(sc); return; } /* Flush the TX descriptors */ bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); #ifdef RE_TX_MODERATION /* * Use the countdown timer for interrupt moderation. * 'TX done' interrupts are disabled. Instead, we reset the * countdown timer, which will begin counting until it hits * the value in the TIMERINT register, and then trigger an * interrupt. Each time we write to the TIMERCNT register, * the timer count is reset to 0. */ CSR_WRITE_4(sc, RL_TIMERCNT, 1); #endif /* * Set a timeout in case the chip goes out to lunch. */ sc->rl_watchdog_timer = 5; RL_UNLOCK(sc); } static void re_init(void *xsc) { struct rl_softc *sc = xsc; RL_LOCK(sc); re_init_locked(sc); RL_UNLOCK(sc); } static void re_init_locked(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; struct mii_data *mii; uint32_t reg; uint16_t cfg; union { uint32_t align_dummy; u_char eaddr[ETHER_ADDR_LEN]; } eaddr; RL_LOCK_ASSERT(sc); mii = device_get_softc(sc->rl_miibus); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* * Cancel pending I/O and free all RX/TX buffers. */ re_stop(sc); /* Put controller into known state. */ re_reset(sc); /* * Enable C+ RX and TX mode, as well as VLAN stripping and * RX checksum offload. We must configure the C+ register * before all others. */ cfg = RL_CPLUSCMD_PCI_MRW; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) cfg |= RL_CPLUSCMD_RXCSUM_ENB; if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) cfg |= RL_CPLUSCMD_VLANSTRIP; if ((sc->rl_flags & RL_FLAG_MACSTAT) != 0) { cfg |= RL_CPLUSCMD_MACSTAT_DIS; /* XXX magic. */ cfg |= 0x0001; } else cfg |= RL_CPLUSCMD_RXENB | RL_CPLUSCMD_TXENB; CSR_WRITE_2(sc, RL_CPLUS_CMD, cfg); if (sc->rl_hwrev == RL_HWREV_8169_8110SC || sc->rl_hwrev == RL_HWREV_8169_8110SCE) { reg = 0x000fff00; if ((CSR_READ_1(sc, RL_CFG2) & RL_CFG2_PCI66MHZ) != 0) reg |= 0x000000ff; if (sc->rl_hwrev == RL_HWREV_8169_8110SCE) reg |= 0x00f00000; CSR_WRITE_4(sc, 0x7c, reg); /* Disable interrupt mitigation. */ CSR_WRITE_2(sc, 0xe2, 0); } /* * Disable TSO if interface MTU size is greater than MSS * allowed in controller. */ if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; } /* * Init our MAC address. Even though the chipset * documentation doesn't mention it, we need to enter "Config * register write enable" mode to modify the ID registers. */ /* Copy MAC address on stack to align. */ bcopy(IF_LLADDR(ifp), eaddr.eaddr, ETHER_ADDR_LEN); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG); CSR_WRITE_4(sc, RL_IDR0, htole32(*(u_int32_t *)(&eaddr.eaddr[0]))); CSR_WRITE_4(sc, RL_IDR4, htole32(*(u_int32_t *)(&eaddr.eaddr[4]))); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); /* * For C+ mode, initialize the RX descriptors and mbufs. */ re_rx_list_init(sc); re_tx_list_init(sc); /* * Load the addresses of the RX and TX lists into the chip. */ CSR_WRITE_4(sc, RL_RXLIST_ADDR_HI, RL_ADDR_HI(sc->rl_ldata.rl_rx_list_addr)); CSR_WRITE_4(sc, RL_RXLIST_ADDR_LO, RL_ADDR_LO(sc->rl_ldata.rl_rx_list_addr)); CSR_WRITE_4(sc, RL_TXLIST_ADDR_HI, RL_ADDR_HI(sc->rl_ldata.rl_tx_list_addr)); CSR_WRITE_4(sc, RL_TXLIST_ADDR_LO, RL_ADDR_LO(sc->rl_ldata.rl_tx_list_addr)); /* * Enable transmit and receive. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); /* * Set the initial TX configuration. */ if (sc->rl_testmode) { if (sc->rl_type == RL_8169) CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG|RL_LOOPTEST_ON); else CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG|RL_LOOPTEST_ON_CPLUS); } else CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); CSR_WRITE_1(sc, RL_EARLY_TX_THRESH, 16); /* * Set the initial RX configuration. */ re_set_rxmode(sc); #ifdef DEVICE_POLLING /* * Disable interrupts if we are polling. */ if (ifp->if_capenable & IFCAP_POLLING) CSR_WRITE_2(sc, RL_IMR, 0); else /* otherwise ... */ #endif /* * Enable interrupts. */ if (sc->rl_testmode) CSR_WRITE_2(sc, RL_IMR, 0); else CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); CSR_WRITE_2(sc, RL_ISR, RL_INTRS_CPLUS); /* Set initial TX threshold */ sc->rl_txthresh = RL_TX_THRESH_INIT; /* Start RX/TX process. */ CSR_WRITE_4(sc, RL_MISSEDPKT, 0); #ifdef notdef /* Enable receiver and transmitter. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); #endif #ifdef RE_TX_MODERATION /* * Initialize the timer interrupt register so that * a timer interrupt will be generated once the timer * reaches a certain number of ticks. The timer is * reloaded on each transmit. This gives us TX interrupt * moderation, which dramatically improves TX frame rate. */ if (sc->rl_type == RL_8169) CSR_WRITE_4(sc, RL_TIMERINT_8169, 0x800); else CSR_WRITE_4(sc, RL_TIMERINT, 0x400); #endif /* * For 8169 gigE NICs, set the max allowed RX packet * size so we can receive jumbo frames. */ if (sc->rl_type == RL_8169) CSR_WRITE_2(sc, RL_MAXRXPKTLEN, 16383); if (sc->rl_testmode) return; mii_mediachg(mii); CSR_WRITE_1(sc, RL_CFG1, CSR_READ_1(sc, RL_CFG1) | RL_CFG1_DRVLOAD); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->rl_flags &= ~RL_FLAG_LINK; sc->rl_watchdog_timer = 0; callout_reset(&sc->rl_stat_callout, hz, re_tick, sc); } /* * Set media options. */ static int re_ifmedia_upd(struct ifnet *ifp) { struct rl_softc *sc; struct mii_data *mii; int error; sc = ifp->if_softc; mii = device_get_softc(sc->rl_miibus); RL_LOCK(sc); error = mii_mediachg(mii); RL_UNLOCK(sc); return (error); } /* * Report current media status. */ static void re_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rl_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->rl_miibus); RL_LOCK(sc); mii_pollstat(mii); RL_UNLOCK(sc); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int re_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct rl_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; switch (command) { case SIOCSIFMTU: if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > RL_JUMBO_MTU) { error = EINVAL; break; } if ((sc->rl_flags & RL_FLAG_NOJUMBO) != 0 && ifr->ifr_mtu > RL_MAX_FRAMELEN) { error = EINVAL; break; } RL_LOCK(sc); if (ifp->if_mtu != ifr->ifr_mtu) ifp->if_mtu = ifr->ifr_mtu; if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; + VLAN_CAPABILITIES(ifp); } RL_UNLOCK(sc); break; case SIOCSIFFLAGS: RL_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (((ifp->if_flags ^ sc->rl_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) re_set_rxmode(sc); } else re_init_locked(sc); } else { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) re_stop(sc); } sc->rl_if_flags = ifp->if_flags; RL_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: RL_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) re_set_rxmode(sc); RL_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->rl_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: { int mask, reinit; mask = ifr->ifr_reqcap ^ ifp->if_capenable; reinit = 0; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(re_poll, ifp); if (error) return(error); RL_LOCK(sc); /* Disable interrupts */ CSR_WRITE_2(sc, RL_IMR, 0x0000); ifp->if_capenable |= IFCAP_POLLING; RL_UNLOCK(sc); } else { error = ether_poll_deregister(ifp); /* Enable interrupts. */ RL_LOCK(sc); CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); ifp->if_capenable &= ~IFCAP_POLLING; RL_UNLOCK(sc); } } #endif /* DEVICE_POLLING */ if (mask & IFCAP_HWCSUM) { ifp->if_capenable ^= IFCAP_HWCSUM; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist |= RE_CSUM_FEATURES; else ifp->if_hwassist &= ~RE_CSUM_FEATURES; reinit = 1; } - if (mask & IFCAP_VLAN_HWTAGGING) { - ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; - reinit = 1; - } - if (mask & IFCAP_TSO4) { + if ((mask & IFCAP_TSO4) != 0 && + (ifp->if_capabilities & IFCAP_TSO) != 0) { ifp->if_capenable ^= IFCAP_TSO4; - if ((IFCAP_TSO4 & ifp->if_capenable) && - (IFCAP_TSO4 & ifp->if_capabilities)) + if ((IFCAP_TSO4 & ifp->if_capenable) != 0) ifp->if_hwassist |= CSUM_TSO; else ifp->if_hwassist &= ~CSUM_TSO; if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; } + } + if ((mask & IFCAP_VLAN_HWTSO) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) + ifp->if_capenable ^= IFCAP_VLAN_HWTSO; + if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + /* TSO over VLAN requires VLAN hardware tagging. */ + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) + ifp->if_capenable &= ~IFCAP_VLAN_HWTSO; + reinit = 1; } if ((mask & IFCAP_WOL) != 0 && (ifp->if_capabilities & IFCAP_WOL) != 0) { if ((mask & IFCAP_WOL_UCAST) != 0) ifp->if_capenable ^= IFCAP_WOL_UCAST; if ((mask & IFCAP_WOL_MCAST) != 0) ifp->if_capenable ^= IFCAP_WOL_MCAST; if ((mask & IFCAP_WOL_MAGIC) != 0) ifp->if_capenable ^= IFCAP_WOL_MAGIC; } if (reinit && ifp->if_drv_flags & IFF_DRV_RUNNING) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; re_init(sc); } VLAN_CAPABILITIES(ifp); } break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void re_watchdog(struct rl_softc *sc) { struct ifnet *ifp; RL_LOCK_ASSERT(sc); if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer != 0) return; ifp = sc->rl_ifp; re_txeof(sc); if (sc->rl_ldata.rl_tx_free == sc->rl_ldata.rl_tx_desc_cnt) { if_printf(ifp, "watchdog timeout (missed Tx interrupts) " "-- recovering\n"); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); return; } if_printf(ifp, "watchdog timeout\n"); ifp->if_oerrors++; re_rxeof(sc, NULL); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; re_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void re_stop(struct rl_softc *sc) { int i; struct ifnet *ifp; struct rl_txdesc *txd; struct rl_rxdesc *rxd; RL_LOCK_ASSERT(sc); ifp = sc->rl_ifp; sc->rl_watchdog_timer = 0; callout_stop(&sc->rl_stat_callout); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); if ((sc->rl_flags & RL_FLAG_CMDSTOP) != 0) CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_STOPREQ | RL_CMD_TX_ENB | RL_CMD_RX_ENB); else CSR_WRITE_1(sc, RL_COMMAND, 0x00); DELAY(1000); CSR_WRITE_2(sc, RL_IMR, 0x0000); CSR_WRITE_2(sc, RL_ISR, 0xFFFF); if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } /* Free the TX list buffers. */ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) { txd = &sc->rl_ldata.rl_tx_desc[i]; if (txd->tx_m != NULL) { bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap); m_freem(txd->tx_m); txd->tx_m = NULL; } } /* Free the RX list buffers. */ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) { rxd = &sc->rl_ldata.rl_rx_desc[i]; if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap); m_freem(rxd->rx_m); rxd->rx_m = NULL; } } } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int re_suspend(device_t dev) { struct rl_softc *sc; sc = device_get_softc(dev); RL_LOCK(sc); re_stop(sc); re_setwol(sc); sc->suspended = 1; RL_UNLOCK(sc); return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int re_resume(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); RL_LOCK(sc); ifp = sc->rl_ifp; /* Take controller out of sleep mode. */ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80) CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) | 0x01); } /* * Clear WOL matching such that normal Rx filtering * wouldn't interfere with WOL patterns. */ re_clrwol(sc); /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) re_init_locked(sc); sc->suspended = 0; RL_UNLOCK(sc); return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int re_shutdown(device_t dev) { struct rl_softc *sc; sc = device_get_softc(dev); RL_LOCK(sc); re_stop(sc); /* * Mark interface as down since otherwise we will panic if * interrupt comes in later on, which can happen in some * cases. */ sc->rl_ifp->if_flags &= ~IFF_UP; re_setwol(sc); RL_UNLOCK(sc); return (0); } static void re_setwol(struct rl_softc *sc) { struct ifnet *ifp; int pmc; uint16_t pmstat; uint8_t v; RL_LOCK_ASSERT(sc); if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0) return; ifp = sc->rl_ifp; /* Put controller into sleep mode. */ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80) CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) & ~0x01); } if ((ifp->if_capenable & IFCAP_WOL) != 0 && (sc->rl_flags & RL_FLAG_WOLRXENB) != 0) CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RX_ENB); /* Enable config register write. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); /* Enable PME. */ v = CSR_READ_1(sc, RL_CFG1); v &= ~RL_CFG1_PME; if ((ifp->if_capenable & IFCAP_WOL) != 0) v |= RL_CFG1_PME; CSR_WRITE_1(sc, RL_CFG1, v); v = CSR_READ_1(sc, RL_CFG3); v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC); if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) v |= RL_CFG3_WOL_MAGIC; CSR_WRITE_1(sc, RL_CFG3, v); /* Config register write done. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); v = CSR_READ_1(sc, RL_CFG5); v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST); v &= ~RL_CFG5_WOL_LANWAKE; if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0) v |= RL_CFG5_WOL_UCAST; if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST; if ((ifp->if_capenable & IFCAP_WOL) != 0) v |= RL_CFG5_WOL_LANWAKE; CSR_WRITE_1(sc, RL_CFG5, v); /* * It seems that hardware resets its link speed to 100Mbps in * power down mode so switching to 100Mbps in driver is not * needed. */ /* Request PME if WOL is requested. */ pmstat = pci_read_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, 2); pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); if ((ifp->if_capenable & IFCAP_WOL) != 0) pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; pci_write_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); } static void re_clrwol(struct rl_softc *sc) { int pmc; uint8_t v; RL_LOCK_ASSERT(sc); if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0) return; /* Enable config register write. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); v = CSR_READ_1(sc, RL_CFG3); v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC); CSR_WRITE_1(sc, RL_CFG3, v); /* Config register write done. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); v = CSR_READ_1(sc, RL_CFG5); v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST); v &= ~RL_CFG5_WOL_LANWAKE; CSR_WRITE_1(sc, RL_CFG5, v); } Index: projects/ppc64/sys/dev/syscons/scvidctl.c =================================================================== --- projects/ppc64/sys/dev/syscons/scvidctl.c (revision 204271) +++ projects/ppc64/sys/dev/syscons/scvidctl.c (revision 204272) @@ -1,892 +1,900 @@ /*- * Copyright (c) 1998 Kazutaka YOKOTA * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sascha Wildner * * 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 as * the first lines of this file unmodified. * 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 "opt_compat.h" #include "opt_syscons.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SET_DECLARE(scrndr_set, const sc_renderer_t); /* for compatibility with previous versions */ /* 3.0-RELEASE used the following structure */ typedef struct old_video_adapter { int va_index; int va_type; int va_flags; /* flag bits are the same as the -CURRENT #define V_ADP_COLOR (1<<0) #define V_ADP_MODECHANGE (1<<1) #define V_ADP_STATESAVE (1<<2) #define V_ADP_STATELOAD (1<<3) #define V_ADP_FONT (1<<4) #define V_ADP_PALETTE (1<<5) #define V_ADP_BORDER (1<<6) #define V_ADP_VESA (1<<7) */ int va_crtc_addr; u_int va_window; /* virtual address */ size_t va_window_size; size_t va_window_gran; u_int va_buffer; /* virtual address */ size_t va_buffer_size; int va_initial_mode; int va_initial_bios_mode; int va_mode; } old_video_adapter_t; #define OLD_CONS_ADPINFO _IOWR('c', 101, old_video_adapter_t) /* 3.1-RELEASE used the following structure */ typedef struct old_video_adapter_info { int va_index; int va_type; char va_name[16]; int va_unit; int va_flags; int va_io_base; int va_io_size; int va_crtc_addr; int va_mem_base; int va_mem_size; u_int va_window; /* virtual address */ size_t va_window_size; size_t va_window_gran; u_int va_buffer; size_t va_buffer_size; int va_initial_mode; int va_initial_bios_mode; int va_mode; int va_line_width; } old_video_adapter_info_t; #define OLD_CONS_ADPINFO2 _IOWR('c', 101, old_video_adapter_info_t) /* 3.0-RELEASE and 3.1-RELEASE used the following structure */ typedef struct old_video_info { int vi_mode; int vi_flags; /* flag bits are the same as the -CURRENT #define V_INFO_COLOR (1<<0) #define V_INFO_GRAPHICS (1<<1) #define V_INFO_LINEAR (1<<2) #define V_INFO_VESA (1<<3) */ int vi_width; int vi_height; int vi_cwidth; int vi_cheight; int vi_depth; int vi_planes; u_int vi_window; /* physical address */ size_t vi_window_size; size_t vi_window_gran; u_int vi_buffer; /* physical address */ size_t vi_buffer_size; } old_video_info_t; #define OLD_CONS_MODEINFO _IOWR('c', 102, old_video_info_t) #define OLD_CONS_FINDMODE _IOWR('c', 103, old_video_info_t) int sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, int fontsize, int fontwidth) { video_info_t info; u_char *font; int prev_ysize; int error; int s; if (vidd_get_info(scp->sc->adp, mode, &info)) return ENODEV; /* adjust argument values */ if (fontwidth <= 0) fontwidth = info.vi_cwidth; if (fontsize <= 0) fontsize = info.vi_cheight; if (fontsize < 14) { fontsize = 8; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_8)) return EINVAL; font = scp->sc->font_8; #else font = NULL; #endif } else if (fontsize >= 16) { fontsize = 16; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_16)) return EINVAL; font = scp->sc->font_16; #else font = NULL; #endif } else { fontsize = 14; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_14)) return EINVAL; font = scp->sc->font_14; #else font = NULL; #endif } if ((xsize <= 0) || (xsize > info.vi_width)) xsize = info.vi_width; if ((ysize <= 0) || (ysize > info.vi_height)) ysize = info.vi_height; /* stop screen saver, etc */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } if (sc_render_match(scp, scp->sc->adp->va_name, 0) == NULL) { splx(s); return ENODEV; } /* set up scp */ #ifndef SC_NO_HISTORY if (scp->history != NULL) sc_hist_save(scp); #endif prev_ysize = scp->ysize; /* * This is a kludge to fend off scrn_update() while we * muck around with scp. XXX */ scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE); scp->mode = mode; scp->xsize = xsize; scp->ysize = ysize; scp->xoff = 0; scp->yoff = 0; scp->xpixel = scp->xsize*8; scp->ypixel = scp->ysize*fontsize; scp->font = font; scp->font_size = fontsize; scp->font_width = fontwidth; /* allocate buffers */ sc_alloc_scr_buffer(scp, TRUE, TRUE); sc_init_emulator(scp, NULL); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, FALSE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, prev_ysize, FALSE); #endif splx(s); if (scp == scp->sc->cur_scp) set_mode(scp); scp->status &= ~UNKNOWN_MODE; if (tp == NULL) return 0; DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n", tp->t_winsize.ws_col, tp->t_winsize.ws_row, scp->xsize, scp->ysize)); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; tty_signal_pgrp(tp, SIGWINCH); } return 0; } int sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode) { #ifdef SC_NO_MODE_CHANGE return ENODEV; #else video_info_t info; int error; int s; if (vidd_get_info(scp->sc->adp, mode, &info)) return ENODEV; /* stop screen saver, etc */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } if (sc_render_match(scp, scp->sc->adp->va_name, GRAPHICS_MODE) == NULL) { splx(s); return ENODEV; } /* set up scp */ scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE | MOUSE_HIDDEN); scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE); scp->mode = mode; /* * Don't change xsize and ysize; preserve the previous vty * and history buffers. */ scp->xoff = 0; scp->yoff = 0; scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->font = NULL; scp->font_size = 0; #ifndef SC_NO_SYSMOUSE /* move the mouse cursor at the center of the screen */ sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); #endif sc_init_emulator(scp, NULL); splx(s); if (scp == scp->sc->cur_scp) set_mode(scp); /* clear_graphics();*/ scp->status &= ~UNKNOWN_MODE; if (tp == NULL) return 0; if (tp->t_winsize.ws_xpixel != scp->xpixel || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; tty_signal_pgrp(tp, SIGWINCH); } return 0; #endif /* SC_NO_MODE_CHANGE */ } int sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, int fontsize, int fontwidth) { #ifndef SC_PIXEL_MODE return ENODEV; #else video_info_t info; ksiginfo_t ksi; u_char *font; int prev_ysize; int error; int s; if (vidd_get_info(scp->sc->adp, scp->mode, &info)) return ENODEV; /* this shouldn't happen */ /* adjust argument values */ if (fontsize <= 0) fontsize = info.vi_cheight; if (fontsize < 14) { fontsize = 8; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_8)) return EINVAL; font = scp->sc->font_8; #else font = NULL; #endif } else if (fontsize >= 16) { fontsize = 16; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_16)) return EINVAL; font = scp->sc->font_16; #else font = NULL; #endif } else { fontsize = 14; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_14)) return EINVAL; font = scp->sc->font_14; #else font = NULL; #endif } if (xsize <= 0) xsize = info.vi_width/8; if (ysize <= 0) ysize = info.vi_height/fontsize; if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize)) return EINVAL; /* * We currently support the following graphic modes: * * - 4 bpp planar modes whose memory size does not exceed 64K * - 15, 16, 24 and 32 bpp linear modes */ if (info.vi_mem_model == V_INFO_MM_PLANAR) { if (info.vi_planes != 4) return ENODEV; /* * A memory size >64K requires bank switching to access the entire * screen. XXX */ if (info.vi_width * info.vi_height / 8 > info.vi_window_size) return ENODEV; } else if (info.vi_mem_model == V_INFO_MM_DIRECT) { if (!(info.vi_flags & V_INFO_LINEAR) && (info.vi_depth != 15) && (info.vi_depth != 16) && (info.vi_depth != 24) && (info.vi_depth != 32)) return ENODEV; } else if (info.vi_mem_model == V_INFO_MM_PACKED) { if (!(info.vi_flags & V_INFO_LINEAR) && (info.vi_depth != 8)) return ENODEV; } else return ENODEV; /* stop screen saver, etc */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } if (sc_render_match(scp, scp->sc->adp->va_name, PIXEL_MODE) == NULL) { splx(s); return ENODEV; } #if 0 if (scp->tsw) (*scp->tsw->te_term)(scp, scp->ts); scp->tsw = NULL; scp->ts = NULL; #endif /* set up scp */ #ifndef SC_NO_HISTORY if (scp->history != NULL) sc_hist_save(scp); #endif prev_ysize = scp->ysize; scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE); scp->xsize = xsize; scp->ysize = ysize; scp->xoff = (scp->xpixel/8 - xsize)/2; scp->yoff = (scp->ypixel/fontsize - ysize)/2; scp->font = font; scp->font_size = fontsize; scp->font_width = fontwidth; /* allocate buffers */ sc_alloc_scr_buffer(scp, TRUE, TRUE); sc_init_emulator(scp, NULL); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, FALSE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, prev_ysize, FALSE); #endif splx(s); if (scp == scp->sc->cur_scp) { sc_set_border(scp, scp->border); sc_set_cursor_image(scp); } scp->status &= ~UNKNOWN_MODE; if (tp == NULL) return 0; if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; if (tp->t_pgrp != NULL) { ksiginfo_init(&ksi); ksi.ksi_signo = SIGWINCH; ksi.ksi_code = SI_KERNEL; PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, SIGWINCH, 1, &ksi); PGRP_UNLOCK(tp->t_pgrp); } } return 0; #endif /* SC_PIXEL_MODE */ } #define fb_ioctl(a, c, d) \ (((a) == NULL) ? ENODEV : \ vidd_ioctl((a), (c), (caddr_t)(d))) int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; video_adapter_t *adp; video_info_t info; video_adapter_info_t adp_info; int error; int s; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif scp = SC_STAT(tp); if (scp == NULL) /* tp == SC_MOUSE */ return ENOIOCTL; adp = scp->sc->adp; if (adp == NULL) /* shouldn't happen??? */ return ENODEV; switch (cmd) { case CONS_CURRENTADP: /* get current adapter index */ case FBIO_ADAPTER: return fb_ioctl(adp, FBIO_ADAPTER, data); case CONS_CURRENT: /* get current adapter type */ case FBIO_ADPTYPE: return fb_ioctl(adp, FBIO_ADPTYPE, data); case OLD_CONS_ADPINFO: /* adapter information (old interface) */ if (((old_video_adapter_t *)data)->va_index >= 0) { adp = vid_get_adapter(((old_video_adapter_t *)data)->va_index); if (adp == NULL) return ENODEV; } ((old_video_adapter_t *)data)->va_index = adp->va_index; ((old_video_adapter_t *)data)->va_type = adp->va_type; ((old_video_adapter_t *)data)->va_flags = adp->va_flags; ((old_video_adapter_t *)data)->va_crtc_addr = adp->va_crtc_addr; ((old_video_adapter_t *)data)->va_window = adp->va_window; ((old_video_adapter_t *)data)->va_window_size = adp->va_window_size; ((old_video_adapter_t *)data)->va_window_gran = adp->va_window_gran; ((old_video_adapter_t *)data)->va_buffer = adp->va_buffer; ((old_video_adapter_t *)data)->va_buffer_size = adp->va_buffer_size; ((old_video_adapter_t *)data)->va_mode = adp->va_mode; ((old_video_adapter_t *)data)->va_initial_mode = adp->va_initial_mode; ((old_video_adapter_t *)data)->va_initial_bios_mode = adp->va_initial_bios_mode; return 0; case OLD_CONS_ADPINFO2: /* adapter information (yet another old I/F) */ adp_info.va_index = ((old_video_adapter_info_t *)data)->va_index; if (adp_info.va_index >= 0) { adp = vid_get_adapter(adp_info.va_index); if (adp == NULL) return ENODEV; } error = fb_ioctl(adp, FBIO_ADPINFO, &adp_info); if (error == 0) bcopy(&adp_info, data, sizeof(old_video_adapter_info_t)); return error; case CONS_ADPINFO: /* adapter information */ case FBIO_ADPINFO: if (((video_adapter_info_t *)data)->va_index >= 0) { adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index); if (adp == NULL) return ENODEV; } return fb_ioctl(adp, FBIO_ADPINFO, data); case CONS_GET: /* get current video mode */ case FBIO_GETMODE: *(int *)data = scp->mode; return 0; #ifndef SC_NO_MODE_CHANGE case FBIO_SETMODE: /* set video mode */ if (!(adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; info.vi_mode = *(int *)data; error = fb_ioctl(adp, FBIO_MODEINFO, &info); if (error) return error; if (info.vi_flags & V_INFO_GRAPHICS) return sc_set_graphics_mode(scp, tp, *(int *)data); else return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0, 0); #endif /* SC_NO_MODE_CHANGE */ case OLD_CONS_MODEINFO: /* get mode information (old infterface) */ info.vi_mode = ((old_video_info_t *)data)->vi_mode; error = fb_ioctl(adp, FBIO_MODEINFO, &info); if (error == 0) bcopy(&info, (old_video_info_t *)data, sizeof(old_video_info_t)); return error; case CONS_MODEINFO: /* get mode information */ case FBIO_MODEINFO: return fb_ioctl(adp, FBIO_MODEINFO, data); case OLD_CONS_FINDMODE: /* find a matching video mode (old interface) */ bzero(&info, sizeof(info)); bcopy((old_video_info_t *)data, &info, sizeof(old_video_info_t)); error = fb_ioctl(adp, FBIO_FINDMODE, &info); if (error == 0) bcopy(&info, (old_video_info_t *)data, sizeof(old_video_info_t)); return error; case CONS_FINDMODE: /* find a matching video mode */ case FBIO_FINDMODE: return fb_ioctl(adp, FBIO_FINDMODE, data); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('c', 104): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case CONS_SETWINORG: /* set frame buffer window origin */ case FBIO_SETWINORG: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, FBIO_SETWINORG, data); case FBIO_GETWINORG: /* get frame buffer window origin */ if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, FBIO_GETWINORG, data); case FBIO_GETDISPSTART: case FBIO_SETDISPSTART: case FBIO_GETLINEWIDTH: case FBIO_SETLINEWIDTH: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, cmd, data); case FBIO_GETPALETTE: case FBIO_SETPALETTE: case FBIOPUTCMAP: case FBIOGETCMAP: case FBIOGTYPE: case FBIOGATTR: case FBIOSVIDEO: case FBIOGVIDEO: case FBIOSCURSOR: case FBIOGCURSOR: case FBIOSCURPOS: case FBIOGCURPOS: case FBIOGCURMAX: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, cmd, data); case FBIO_BLANK: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, cmd, data); #ifndef SC_NO_MODE_CHANGE /* generic text modes */ case SW_TEXT_80x25: case SW_TEXT_80x30: case SW_TEXT_80x43: case SW_TEXT_80x50: case SW_TEXT_80x60: /* FALLTHROUGH */ /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_VGA_C90x25: case SW_VGA_M90x25: case SW_VGA_C90x30: case SW_VGA_M90x30: case SW_VGA_C90x43: case SW_VGA_M90x43: case SW_VGA_C90x50: case SW_VGA_M90x50: case SW_VGA_C90x60: case SW_VGA_M90x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: case SW_EGAMONO80x25: #ifdef PC98 /* PC98 TEXT MODES */ case SW_PC98_80x25: case SW_PC98_80x30: #endif if (!(adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; return sc_set_text_mode(scp, tp, cmd & 0xff, 0, 0, 0, 0); /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: case SW_VGA_MODEX: #ifdef PC98 /* PC98 GRAPHICS MODES */ case SW_PC98_EGC640x400: case SW_PC98_PEGC640x400: case SW_PC98_PEGC640x480: #endif if (!(adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; return sc_set_graphics_mode(scp, tp, cmd & 0xff); #endif /* SC_NO_MODE_CHANGE */ #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 10): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETMODE: /* set current mode of this (virtual) console */ switch (*(int *)data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* * If scp->mode is of graphics modes, we don't know which * text mode to switch back to... */ if (scp->status & GRAPHICS_MODE) return EINVAL; /* restore fonts & palette ! */ #if 0 #ifndef SC_NO_FONT_LOADING if (ISFONTAVAIL(adp->va_flags) && !(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) /* * FONT KLUDGE * Don't load fonts for now... XXX */ if (scp->sc->fonts_loaded & FONT_8) sc_load_font(scp, 0, 8, 8, scp->sc->font_8, 0, 256); if (scp->sc->fonts_loaded & FONT_14) sc_load_font(scp, 0, 14, 8, scp->sc->font_14, 0, 256); if (scp->sc->fonts_loaded & FONT_16) sc_load_font(scp, 0, 16, 8, scp->sc->font_16, 0, 256); } #endif /* SC_NO_FONT_LOADING */ #endif #ifndef SC_NO_PALETTE_LOADING +#ifdef SC_PIXEL_MODE + if ((adp->va_flags & V_ADP_DAC8) != 0) + vidd_load_palette(adp, scp->sc->palette2); + else +#endif vidd_load_palette(adp, scp->sc->palette); #endif #ifndef PC98 /* move hardware cursor out of the way */ vidd_set_hw_cursor(adp, -1, -1); #endif /* FALLTHROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ /* * If scp->mode is of graphics modes, we don't know which * text/pixel mode to switch back to... */ if (scp->status & GRAPHICS_MODE) return EINVAL; s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } #ifndef PC98 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; splx(s); /* no restore fonts & palette */ if (scp == scp->sc->cur_scp) set_mode(scp); sc_clear_screen(scp); scp->status &= ~UNKNOWN_MODE; #else /* PC98 */ scp->status &= ~UNKNOWN_MODE; /* no restore fonts & palette */ if (scp == scp->sc->cur_scp) set_mode(scp); sc_clear_screen(scp); splx(s); #endif /* PC98 */ return 0; #ifdef SC_PIXEL_MODE case KD_PIXEL: /* pixel (raster) display */ if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) return EINVAL; if (scp->status & GRAPHICS_MODE) return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize, scp->font_size, scp->font_width); s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); splx(s); if (scp == scp->sc->cur_scp) { set_mode(scp); #ifndef SC_NO_PALETTE_LOADING - vidd_load_palette(adp, scp->sc->palette); + if ((adp->va_flags & V_ADP_DAC8) != 0) + vidd_load_palette(adp, scp->sc->palette2); + else + vidd_load_palette(adp, scp->sc->palette); #endif } sc_clear_screen(scp); scp->status &= ~UNKNOWN_MODE; return 0; #endif /* SC_PIXEL_MODE */ case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; splx(s); #ifdef PC98 if (scp == scp->sc->cur_scp) set_mode(scp); #endif return 0; default: return EINVAL; } /* NOT REACHED */ #ifdef SC_PIXEL_MODE case KDRASTER: /* set pixel (raster) display mode */ if (ISUNKNOWNSC(scp) || ISTEXTSC(scp)) return ENODEV; return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1], ((int *)data)[2], 8); #endif /* SC_PIXEL_MODE */ case KDGETMODE: /* get current mode of this (virtual) console */ /* * From the user program's point of view, KD_PIXEL is the same * as KD_TEXT... */ *data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT; return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 13): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSBORDER: /* set border color of this (virtual) console */ scp->border = *(int *)data; if (scp == scp->sc->cur_scp) sc_set_border(scp, scp->border); return 0; } return ENOIOCTL; } static LIST_HEAD(, sc_renderer) sc_rndr_list = LIST_HEAD_INITIALIZER(sc_rndr_list); int sc_render_add(sc_renderer_t *rndr) { LIST_INSERT_HEAD(&sc_rndr_list, rndr, link); return 0; } int sc_render_remove(sc_renderer_t *rndr) { /* LIST_REMOVE(rndr, link); */ return EBUSY; /* XXX */ } sc_rndr_sw_t *sc_render_match(scr_stat *scp, char *name, int mode) { const sc_renderer_t **list; const sc_renderer_t *p; if (!LIST_EMPTY(&sc_rndr_list)) { LIST_FOREACH(p, &sc_rndr_list, link) { if ((strcmp(p->name, name) == 0) && (mode == p->mode)) { scp->status &= ~(VR_CURSOR_ON | VR_CURSOR_BLINK); return p->rndrsw; } } } else { SET_FOREACH(list, scrndr_set) { p = *list; if ((strcmp(p->name, name) == 0) && (mode == p->mode)) { scp->status &= ~(VR_CURSOR_ON | VR_CURSOR_BLINK); return p->rndrsw; } } } return NULL; } Index: projects/ppc64/sys/dev/syscons/snake/snake_saver.c =================================================================== --- projects/ppc64/sys/dev/syscons/snake/snake_saver.c (revision 204271) +++ projects/ppc64/sys/dev/syscons/snake/snake_saver.c (revision 204272) @@ -1,133 +1,176 @@ /*- * Copyright (c) 1995-1998 Søren Schmidt * 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, immediately at the beginning of the file. * 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$ */ #include #include #include #include #include #include #include #include +#include +#include #include #include #include #include static u_char *message; static int *messagep; static int messagelen; static int blanked; +#define MSGBUF_LEN 70 + +static int nofancy = 0; +TUNABLE_INT("hw.syscons.saver_snake_nofancy", &nofancy); + +#define FANCY_SNAKE (!nofancy) +#define LOAD_HIGH(ld) (((ld * 100 + FSCALE / 2) >> FSHIFT) / 100) +#define LOAD_LOW(ld) (((ld * 100 + FSCALE / 2) >> FSHIFT) % 100) + +static inline void update_msg(void); + static int snake_saver(video_adapter_t *adp, int blank) { static int dirx, diry; - int f; + int f, color, load; sc_softc_t *sc; scr_stat *scp; /* XXX hack for minimal changes. */ #define save message #define savs messagep sc = sc_find_softc(adp, NULL); if (sc == NULL) return EAGAIN; scp = sc->cur_scp; if (blank) { if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return EAGAIN; if (blanked <= 0) { sc_vtb_clear(&scp->scr, sc->scr_map[0x20], (FG_LIGHTGREY | BG_BLACK) << 8); vidd_set_hw_cursor(adp, -1, -1); sc_set_border(scp, 0); dirx = (scp->xpos ? 1 : -1); diry = (scp->ypos ? scp->xsize : -scp->xsize); for (f=0; f< messagelen; f++) savs[f] = scp->xpos + scp->ypos*scp->xsize; sc_vtb_putc(&scp->scr, savs[0], sc->scr_map[*save], (FG_LIGHTGREY | BG_BLACK) << 8); blanked = 1; } if (blanked++ < 4) return 0; blanked = 1; sc_vtb_putc(&scp->scr, savs[messagelen - 1], sc->scr_map[0x20], (FG_LIGHTGREY | BG_BLACK) << 8); for (f=messagelen-1; f > 0; f--) savs[f] = savs[f-1]; f = savs[0]; if ((f % scp->xsize) == 0 || (f % scp->xsize) == scp->xsize - 1 || (random() % 50) == 0) dirx = -dirx; if ((f / scp->xsize) == 0 || (f / scp->xsize) == scp->ysize - 1 || (random() % 20) == 0) diry = -diry; savs[0] += dirx + diry; + if (FANCY_SNAKE) { + update_msg(); + load = ((averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT); + if (load == 0) + color = FG_LIGHTGREY | BG_BLACK; + else if (load / mp_ncpus <= 50) + color = FG_LIGHTGREEN | BG_BLACK; + else if (load / mp_ncpus <= 75) + color = FG_YELLOW | BG_BLACK; + else if (load / mp_ncpus <= 99) + color = FG_LIGHTRED | BG_BLACK; + else + color = FG_RED | FG_BLINK | BG_BLACK; + } else + color = FG_LIGHTGREY | BG_BLACK; + for (f=messagelen-1; f>=0; f--) sc_vtb_putc(&scp->scr, savs[f], sc->scr_map[save[f]], - (FG_LIGHTGREY | BG_BLACK) << 8); + color << 8); } else blanked = 0; return 0; } +static inline void +update_msg(void) +{ + if (!FANCY_SNAKE) { + messagelen = sprintf(message, "%s %s", ostype, osrelease); + return; + } + messagelen = snprintf(message, MSGBUF_LEN, + "%s %s (%d.%02d %d.%02d, %d.%02d)", + ostype, osrelease, + LOAD_HIGH(averunnable.ldavg[0]), LOAD_LOW(averunnable.ldavg[0]), + LOAD_HIGH(averunnable.ldavg[1]), LOAD_LOW(averunnable.ldavg[1]), + LOAD_HIGH(averunnable.ldavg[2]), LOAD_LOW(averunnable.ldavg[2])); +} + static int snake_init(video_adapter_t *adp) { - messagelen = strlen(ostype) + 1 + strlen(osrelease); - message = malloc(messagelen + 1, M_DEVBUF, M_WAITOK); - sprintf(message, "%s %s", ostype, osrelease); - messagep = malloc(messagelen * sizeof *messagep, M_DEVBUF, M_WAITOK); + message = malloc(MSGBUF_LEN, M_DEVBUF, M_WAITOK); + messagep = malloc(MSGBUF_LEN * sizeof *messagep, M_DEVBUF, M_WAITOK); + update_msg(); return 0; } static int snake_term(video_adapter_t *adp) { free(message, M_DEVBUF); free(messagep, M_DEVBUF); return 0; } static scrn_saver_t snake_module = { "snake_saver", snake_init, snake_term, snake_saver, NULL, }; SAVER_MODULE(snake_saver, snake_module); Index: projects/ppc64/sys/dev/syscons/syscons.c =================================================================== --- projects/ppc64/sys/dev/syscons/syscons.c (revision 204271) +++ projects/ppc64/sys/dev/syscons/syscons.c (revision 204272) @@ -1,3718 +1,3730 @@ /*- * Copyright (c) 1992-1998 Søren Schmidt * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sascha Wildner * * 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, immediately at the beginning of the file. * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_syscons.h" #include "opt_splash.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__sparc64__) || defined(__powerpc__) #include #else #include #endif #if defined( __i386__) || defined(__amd64__) #include #include #endif #include #include #include #include #include #define COLD 0 #define WARM 1 #define DEFAULT_BLANKTIME (5*60) /* 5 minutes */ #define MAX_BLANKTIME (7*24*60*60) /* 7 days!? */ #define KEYCODE_BS 0x0e /* "<-- Backspace" key, XXX */ typedef struct default_attr { int std_color; /* normal hardware color */ int rev_color; /* reverse hardware color */ } default_attr; static default_attr user_default = { SC_NORM_ATTR, SC_NORM_REV_ATTR, }; static int sc_console_unit = -1; static int sc_saver_keyb_only = 1; static scr_stat *sc_console; static struct consdev *sc_consptr; static scr_stat main_console; static struct tty *main_devs[MAXCONS]; static char init_done = COLD; static char shutdown_in_progress = FALSE; static char sc_malloc = FALSE; static int saver_mode = CONS_NO_SAVER; /* LKM/user saver */ static int run_scrn_saver = FALSE; /* should run the saver? */ static int enable_bell = TRUE; /* enable beeper */ #ifndef SC_DISABLE_REBOOT static int enable_reboot = TRUE; /* enable keyboard reboot */ #endif #ifndef SC_DISABLE_KDBKEY static int enable_kdbkey = TRUE; /* enable keyboard debug */ #endif static long scrn_blank_time = 0; /* screen saver timeout value */ #ifdef DEV_SPLASH static int scrn_blanked; /* # of blanked screen */ static int sticky_splash = FALSE; static void none_saver(sc_softc_t *sc, int blank) { } static void (*current_saver)(sc_softc_t *, int) = none_saver; #endif SYSCTL_NODE(_hw, OID_AUTO, syscons, CTLFLAG_RD, 0, "syscons"); SYSCTL_NODE(_hw_syscons, OID_AUTO, saver, CTLFLAG_RD, 0, "saver"); SYSCTL_INT(_hw_syscons_saver, OID_AUTO, keybonly, CTLFLAG_RW, &sc_saver_keyb_only, 0, "screen saver interrupted by input only"); SYSCTL_INT(_hw_syscons, OID_AUTO, bell, CTLFLAG_RW, &enable_bell, 0, "enable bell"); #ifndef SC_DISABLE_REBOOT SYSCTL_INT(_hw_syscons, OID_AUTO, kbd_reboot, CTLFLAG_RW|CTLFLAG_SECURE, &enable_reboot, 0, "enable keyboard reboot"); #endif #ifndef SC_DISABLE_KDBKEY SYSCTL_INT(_hw_syscons, OID_AUTO, kbd_debug, CTLFLAG_RW|CTLFLAG_SECURE, &enable_kdbkey, 0, "enable keyboard debug"); #endif #if !defined(SC_NO_FONT_LOADING) && defined(SC_DFLT_FONT) #include "font.h" #endif tsw_ioctl_t *sc_user_ioctl; static bios_values_t bios_value; static int enable_panic_key; SYSCTL_INT(_machdep, OID_AUTO, enable_panic_key, CTLFLAG_RW, &enable_panic_key, 0, "Enable panic via keypress specified in kbdmap(5)"); #define SC_CONSOLECTL 255 #define VTY_WCHAN(sc, vty) (&SC_DEV(sc, vty)) static int debugger; /* prototypes */ static int sc_allocate_keyboard(sc_softc_t *sc, int unit); static int scvidprobe(int unit, int flags, int cons); static int sckbdprobe(int unit, int flags, int cons); static void scmeminit(void *arg); static int scdevtounit(struct tty *tp); static kbd_callback_func_t sckbdevent; static void scinit(int unit, int flags); static scr_stat *sc_get_stat(struct tty *tp); static void scterm(int unit, int flags); static void scshutdown(void *arg, int howto); static u_int scgetc(sc_softc_t *sc, u_int flags); #define SCGETC_CN 1 #define SCGETC_NONBLOCK 2 static void sccnupdate(scr_stat *scp); static scr_stat *alloc_scp(sc_softc_t *sc, int vty); static void init_scp(sc_softc_t *sc, int vty, scr_stat *scp); static timeout_t scrn_timer; static int and_region(int *s1, int *e1, int s2, int e2); static void scrn_update(scr_stat *scp, int show_cursor); #ifdef DEV_SPLASH static int scsplash_callback(int event, void *arg); static void scsplash_saver(sc_softc_t *sc, int show); static int add_scrn_saver(void (*this_saver)(sc_softc_t *, int)); static int remove_scrn_saver(void (*this_saver)(sc_softc_t *, int)); static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border); static int restore_scrn_saver_mode(scr_stat *scp, int changemode); static void stop_scrn_saver(sc_softc_t *sc, void (*saver)(sc_softc_t *, int)); static int wait_scrn_saver_stop(sc_softc_t *sc); #define scsplash_stick(stick) (sticky_splash = (stick)) #else /* !DEV_SPLASH */ #define scsplash_stick(stick) #endif /* DEV_SPLASH */ static int do_switch_scr(sc_softc_t *sc, int s); static int vt_proc_alive(scr_stat *scp); static int signal_vt_rel(scr_stat *scp); static int signal_vt_acq(scr_stat *scp); static int finish_vt_rel(scr_stat *scp, int release, int *s); static int finish_vt_acq(scr_stat *scp); static void exchange_scr(sc_softc_t *sc); static void update_cursor_image(scr_stat *scp); static void change_cursor_shape(scr_stat *scp, int flags, int base, int height); static int save_kbd_state(scr_stat *scp); static int update_kbd_state(scr_stat *scp, int state, int mask); static int update_kbd_leds(scr_stat *scp, int which); static timeout_t blink_screen; static struct tty *sc_alloc_tty(int, int); static cn_probe_t sc_cnprobe; static cn_init_t sc_cninit; static cn_term_t sc_cnterm; static cn_getc_t sc_cngetc; static cn_putc_t sc_cnputc; CONSOLE_DRIVER(sc); static tsw_open_t sctty_open; static tsw_close_t sctty_close; static tsw_outwakeup_t sctty_outwakeup; static tsw_ioctl_t sctty_ioctl; static tsw_mmap_t sctty_mmap; static struct ttydevsw sc_ttydevsw = { .tsw_open = sctty_open, .tsw_close = sctty_close, .tsw_outwakeup = sctty_outwakeup, .tsw_ioctl = sctty_ioctl, .tsw_mmap = sctty_mmap, }; static d_ioctl_t consolectl_ioctl; static struct cdevsw consolectl_devsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_ioctl = consolectl_ioctl, .d_name = "consolectl", }; int sc_probe_unit(int unit, int flags) { if (!scvidprobe(unit, flags, FALSE)) { if (bootverbose) printf("%s%d: no video adapter found.\n", SC_DRIVER_NAME, unit); return ENXIO; } /* syscons will be attached even when there is no keyboard */ sckbdprobe(unit, flags, FALSE); return 0; } /* probe video adapters, return TRUE if found */ static int scvidprobe(int unit, int flags, int cons) { /* * Access the video adapter driver through the back door! * Video adapter drivers need to be configured before syscons. * However, when syscons is being probed as the low-level console, * they have not been initialized yet. We force them to initialize * themselves here. XXX */ vid_configure(cons ? VIO_PROBE_ONLY : 0); return (vid_find_adapter("*", unit) >= 0); } /* probe the keyboard, return TRUE if found */ static int sckbdprobe(int unit, int flags, int cons) { /* access the keyboard driver through the backdoor! */ kbd_configure(cons ? KB_CONF_PROBE_ONLY : 0); return (kbd_find_keyboard("*", unit) >= 0); } static char *adapter_name(video_adapter_t *adp) { static struct { int type; char *name[2]; } names[] = { { KD_MONO, { "MDA", "MDA" } }, { KD_HERCULES, { "Hercules", "Hercules" } }, { KD_CGA, { "CGA", "CGA" } }, { KD_EGA, { "EGA", "EGA (mono)" } }, { KD_VGA, { "VGA", "VGA (mono)" } }, { KD_PC98, { "PC-98x1", "PC-98x1" } }, { KD_TGA, { "TGA", "TGA" } }, { -1, { "Unknown", "Unknown" } }, }; int i; for (i = 0; names[i].type != -1; ++i) if (names[i].type == adp->va_type) break; return names[i].name[(adp->va_flags & V_ADP_COLOR) ? 0 : 1]; } static void sctty_outwakeup(struct tty *tp) { size_t len; u_char buf[PCBURST]; scr_stat *scp = sc_get_stat(tp); if (scp->status & SLKED || (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) return; for (;;) { len = ttydisc_getc(tp, buf, sizeof buf); if (len == 0) break; sc_puts(scp, buf, len, 0); } } static struct tty * sc_alloc_tty(int index, int devnum) { struct sc_ttysoftc *stc; struct tty *tp; /* Allocate TTY object and softc to store unit number. */ stc = malloc(sizeof(struct sc_ttysoftc), M_DEVBUF, M_WAITOK); stc->st_index = index; stc->st_stat = NULL; tp = tty_alloc_mutex(&sc_ttydevsw, stc, &Giant); /* Create device node. */ tty_makedev(tp, NULL, "v%r", devnum); return (tp); } #ifdef SC_PIXEL_MODE static int sc_initial_mode(video_adapter_t *adp, int unit) { video_info_t info; int depth, vmode; int i; vmode = 0; (void)resource_int_value("sc", unit, "vesa_mode", &vmode); if (vmode < M_VESA_BASE || vmode > M_VESA_MODE_MAX) vmode = 0; /* * If the default mode is not supported, search for an available * 800x600 graphics mode with the highest color depth. */ if (vmode == 0 || vidd_get_info(adp, vmode, &info) != 0) { depth = vmode = 0; for (i = M_VESA_BASE; i <= M_VESA_MODE_MAX; i++) if (vidd_get_info(adp, i, &info) == 0 && (info.vi_flags & V_INFO_GRAPHICS) != 0 && info.vi_width == 800 && info.vi_height == 600 && info.vi_depth > depth) { vmode = i; depth = info.vi_depth; } } return (vmode); } #endif int sc_attach_unit(int unit, int flags) { sc_softc_t *sc; scr_stat *scp; int vc; struct cdev *dev; flags &= ~SC_KERNEL_CONSOLE; if (sc_console_unit == unit) { /* * If this unit is being used as the system console, we need to * adjust some variables and buffers before and after scinit(). */ /* assert(sc_console != NULL) */ flags |= SC_KERNEL_CONSOLE; scmeminit(NULL); } scinit(unit, flags); sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); sc->config = flags; scp = sc_get_stat(sc->dev[0]); if (sc_console == NULL) /* sc_console_unit < 0 */ sc_console = scp; #ifdef SC_PIXEL_MODE if ((sc->config & SC_VESAMODE) != 0) { int vmode; vmode = sc_initial_mode(sc->adp, unit); if (vmode >= M_VESA_BASE) { #ifdef DEV_SPLASH if (sc->flags & SC_SPLASH_SCRN) splash_term(sc->adp); #endif sc_set_graphics_mode(scp, NULL, vmode); sc_set_pixel_mode(scp, NULL, 0, 0, 16, 8); -#ifndef SC_NO_PALETTE_LOADING - vidd_save_palette(sc->adp, sc->palette); -#endif sc->initial_mode = vmode; #ifdef DEV_SPLASH /* put up the splash again! */ if (sc->flags & SC_SPLASH_SCRN) splash_init(sc->adp, scsplash_callback, sc); #endif } } #endif /* SC_PIXEL_MODE */ /* initialize cursor */ if (!ISGRAPHSC(scp)) update_cursor_image(scp); /* get screen update going */ scrn_timer(sc); /* set up the keyboard */ kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); printf("%s%d: %s <%d virtual consoles, flags=0x%x>\n", SC_DRIVER_NAME, unit, adapter_name(sc->adp), sc->vtys, sc->config); if (bootverbose) { printf("%s%d:", SC_DRIVER_NAME, unit); if (sc->adapter >= 0) printf(" fb%d", sc->adapter); if (sc->keyboard >= 0) printf(", kbd%d", sc->keyboard); if (scp->tsw) printf(", terminal emulator: %s (%s)", scp->tsw->te_name, scp->tsw->te_desc); printf("\n"); } /* register a shutdown callback for the kernel console */ if (sc_console_unit == unit) EVENTHANDLER_REGISTER(shutdown_pre_sync, scshutdown, (void *)(uintptr_t)unit, SHUTDOWN_PRI_DEFAULT); for (vc = 0; vc < sc->vtys; vc++) { if (sc->dev[vc] == NULL) { sc->dev[vc] = sc_alloc_tty(vc, vc + unit * MAXCONS); if (vc == 0 && sc->dev == main_devs) SC_STAT(sc->dev[0]) = &main_console; } /* * The first vty already has struct tty and scr_stat initialized * in scinit(). The other vtys will have these structs when * first opened. */ } dev = make_dev(&consolectl_devsw, 0, UID_ROOT, GID_WHEEL, 0600, "consolectl"); dev->si_drv1 = sc->dev[0]; return 0; } static void scmeminit(void *arg) { if (sc_malloc) return; sc_malloc = TRUE; /* * As soon as malloc() becomes functional, we had better allocate * various buffers for the kernel console. */ if (sc_console_unit < 0) /* sc_console == NULL */ return; /* copy the temporary buffer to the final buffer */ sc_alloc_scr_buffer(sc_console, FALSE, FALSE); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(sc_console, FALSE); #endif #ifndef SC_NO_HISTORY /* initialize history buffer & pointers */ sc_alloc_history_buffer(sc_console, 0, 0, FALSE); #endif } /* XXX */ SYSINIT(sc_mem, SI_SUB_KMEM, SI_ORDER_ANY, scmeminit, NULL); static int scdevtounit(struct tty *tp) { int vty = SC_VTY(tp); if (vty == SC_CONSOLECTL) return ((sc_console != NULL) ? sc_console->sc->unit : -1); else if ((vty < 0) || (vty >= MAXCONS*sc_max_unit())) return -1; else return vty/MAXCONS; } static int sctty_open(struct tty *tp) { int unit = scdevtounit(tp); sc_softc_t *sc; scr_stat *scp; #ifndef __sparc64__ keyarg_t key; #endif DPRINTF(5, ("scopen: dev:%s, unit:%d, vty:%d\n", devtoname(tp->t_dev), unit, SC_VTY(tp))); sc = sc_get_softc(unit, (sc_console_unit == unit) ? SC_KERNEL_CONSOLE : 0); if (sc == NULL) return ENXIO; if (!tty_opened(tp)) { /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ #ifndef __sparc64__ if (sc->kbd != NULL) { key.keynum = KEYCODE_BS; kbdd_ioctl(sc->kbd, GIO_KEYMAPENT, (caddr_t)&key); tp->t_termios.c_cc[VERASE] = key.key.map[0]; } #endif } scp = sc_get_stat(tp); if (scp == NULL) { scp = SC_STAT(tp) = alloc_scp(sc, SC_VTY(tp)); if (ISGRAPHSC(scp)) sc_set_pixel_mode(scp, NULL, 0, 0, 16, 8); } if (!tp->t_winsize.ws_col && !tp->t_winsize.ws_row) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; } return (0); } static void sctty_close(struct tty *tp) { scr_stat *scp; int s; if (SC_VTY(tp) != SC_CONSOLECTL) { scp = sc_get_stat(tp); /* were we in the middle of the VT switching process? */ DPRINTF(5, ("sc%d: scclose(), ", scp->sc->unit)); s = spltty(); if ((scp == scp->sc->cur_scp) && (scp->sc->unit == sc_console_unit)) cnavailable(sc_consptr, TRUE); if (finish_vt_rel(scp, TRUE, &s) == 0) /* force release */ DPRINTF(5, ("reset WAIT_REL, ")); if (finish_vt_acq(scp) == 0) /* force acknowledge */ DPRINTF(5, ("reset WAIT_ACQ, ")); #ifdef not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { sc_vtb_destroy(&scp->vtb); #ifndef __sparc64__ sc_vtb_destroy(&scp->scr); #endif sc_free_history_buffer(scp, scp->ysize); SC_STAT(tp) = NULL; free(scp, M_DEVBUF); } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif scp->kbd_mode = K_XLATE; if (scp == scp->sc->cur_scp) kbdd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); DPRINTF(5, ("done.\n")); } } #if 0 /* XXX mpsafetty: fix screensaver. What about outwakeup? */ static int scread(struct cdev *dev, struct uio *uio, int flag) { if (!sc_saver_keyb_only) sc_touch_scrn_saver(); return ttyread(dev, uio, flag); } #endif static int sckbdevent(keyboard_t *thiskbd, int event, void *arg) { sc_softc_t *sc; struct tty *cur_tty; int c, error = 0; size_t len; const u_char *cp; sc = (sc_softc_t *)arg; /* assert(thiskbd == sc->kbd) */ mtx_lock(&Giant); switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: sc->kbd = NULL; sc->keyboard = -1; kbd_release(thiskbd, (void *)&sc->keyboard); goto done; default: error = EINVAL; goto done; } /* * Loop while there is still input to get from the keyboard. * I don't think this is nessesary, and it doesn't fix * the Xaccel-2.1 keyboard hang, but it can't hurt. XXX */ while ((c = scgetc(sc, SCGETC_NONBLOCK)) != NOKEY) { cur_tty = SC_DEV(sc, sc->cur_scp->index); if (!tty_opened(cur_tty)) continue; if ((*sc->cur_scp->tsw->te_input)(sc->cur_scp, c, cur_tty)) continue; switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case FKEY: /* function key, return string */ cp = (*sc->cur_scp->tsw->te_fkeystr)(sc->cur_scp, c); if (cp != NULL) { ttydisc_rint_simple(cur_tty, cp, strlen(cp)); break; } cp = kbdd_get_fkeystr(thiskbd, KEYCHAR(c), &len); if (cp != NULL) ttydisc_rint_simple(cur_tty, cp, len); break; case MKEY: /* meta is active, prepend ESC */ ttydisc_rint(cur_tty, 0x1b, 0); ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ ttydisc_rint_simple(cur_tty, "\x1B[Z", 3); break; } ttydisc_rint_done(cur_tty); } sc->cur_scp->status |= MOUSE_HIDDEN; done: mtx_unlock(&Giant); return (error); } static int sctty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { int error; int i; sc_softc_t *sc; scr_stat *scp; int s; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { error = (*sc_user_ioctl)(tp, cmd, data, td); if (error != ENOIOCTL) return error; } error = sc_vid_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #ifndef SC_NO_HISTORY error = sc_hist_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif #ifndef SC_NO_SYSMOUSE error = sc_mouse_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif scp = sc_get_stat(tp); /* assert(scp != NULL) */ /* scp is sc_console, if SC_VTY(dev) == SC_CONSOLECTL. */ sc = scp->sc; if (scp->tsw) { error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, td); if (error != ENOIOCTL) return error; } switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ /* this ioctl is not processed here, but in the terminal emulator */ return ENOTTY; case GIO_COLOR: /* is this a color console ? */ *(int *)data = (sc->adp->va_flags & V_ADP_COLOR) ? 1 : 0; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ if (*(int *)data < 0 || *(int *)data > MAX_BLANKTIME) return EINVAL; s = spltty(); scrn_blank_time = *(int *)data; run_scrn_saver = (scrn_blank_time != 0); splx(s); return 0; case CONS_CURSORTYPE: /* set cursor type (obsolete) */ s = spltty(); *(int *)data &= CONS_CURSOR_ATTRS; sc_change_cursor_shape(scp, *(int *)data, -1, -1); splx(s); return 0; case CONS_GETCURSORSHAPE: /* get cursor shape (new interface) */ if (((int *)data)[0] & CONS_LOCAL_CURSOR) { ((int *)data)[0] = scp->curr_curs_attr.flags; ((int *)data)[1] = scp->curr_curs_attr.base; ((int *)data)[2] = scp->curr_curs_attr.height; } else { ((int *)data)[0] = sc->curs_attr.flags; ((int *)data)[1] = sc->curs_attr.base; ((int *)data)[2] = sc->curs_attr.height; } return 0; case CONS_SETCURSORSHAPE: /* set cursor shape (new interface) */ s = spltty(); sc_change_cursor_shape(scp, ((int *)data)[0], ((int *)data)[1], ((int *)data)[2]); splx(s); return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if ((*(int *)data) & CONS_VISUAL_BELL) sc->flags |= SC_VISUAL_BELL; else sc->flags &= ~SC_VISUAL_BELL; if ((*(int *)data) & CONS_QUIET_BELL) sc->flags |= SC_QUIET_BELL; else sc->flags &= ~SC_QUIET_BELL; return 0; case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = sc->cur_scp->index; ptr->font_size = scp->font_size; ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_hsz = (scp->history != NULL) ? scp->history->vtb_rows : 0; /* * The following fields are filled by the terminal emulator. XXX * * ptr->mv_norm.fore * ptr->mv_norm.back * ptr->mv_rev.fore * ptr->mv_rev.back */ ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; if (scp == sc->cur_scp) save_kbd_state(scp); ptr->mk_keylock = scp->status & LOCK_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; case CONS_IDLE: /* see if the screen has been idle */ /* * When the screen is in the GRAPHICS_MODE or UNKNOWN_MODE, * the user process may have been writing something on the * screen and syscons is not aware of it. Declare the screen * is NOT idle if it is in one of these modes. But there is * an exception to it; if a screen saver is running in the * graphics mode in the current screen, we should say that the * screen has been idle. */ *(int *)data = (sc->flags & SC_SCRN_IDLE) && (!ISGRAPHSC(sc->cur_scp) || (sc->cur_scp->status & SAVER_RUNNING)); return 0; case CONS_SAVERMODE: /* set saver mode */ switch(*(int *)data) { case CONS_NO_SAVER: case CONS_USR_SAVER: /* if a LKM screen saver is running, stop it first. */ scsplash_stick(FALSE); saver_mode = *(int *)data; s = spltty(); #ifdef DEV_SPLASH if ((error = wait_scrn_saver_stop(NULL))) { splx(s); return error; } #endif run_scrn_saver = TRUE; if (saver_mode == CONS_USR_SAVER) scp->status |= SAVER_RUNNING; else scp->status &= ~SAVER_RUNNING; scsplash_stick(TRUE); splx(s); break; case CONS_LKM_SAVER: s = spltty(); if ((saver_mode == CONS_USR_SAVER) && (scp->status & SAVER_RUNNING)) scp->status &= ~SAVER_RUNNING; saver_mode = *(int *)data; splx(s); break; default: return EINVAL; } return 0; case CONS_SAVERSTART: /* immediately start/stop the screen saver */ /* * Note that this ioctl does not guarantee the screen saver * actually starts or stops. It merely attempts to do so... */ s = spltty(); run_scrn_saver = (*(int *)data != 0); if (run_scrn_saver) sc->scrn_time_stamp -= scrn_blank_time; splx(s); return 0; case CONS_SCRSHOT: /* get a screen shot */ { int retval, hist_rsz; size_t lsize, csize; vm_offset_t frbp, hstp; unsigned lnum; scrshot_t *ptr = (scrshot_t *)data; void *outp = ptr->buf; if (ptr->x < 0 || ptr->y < 0 || ptr->xsize < 0 || ptr->ysize < 0) return EINVAL; s = spltty(); if (ISGRAPHSC(scp)) { splx(s); return EOPNOTSUPP; } hist_rsz = (scp->history != NULL) ? scp->history->vtb_rows : 0; if (((u_int)ptr->x + ptr->xsize) > scp->xsize || ((u_int)ptr->y + ptr->ysize) > (scp->ysize + hist_rsz)) { splx(s); return EINVAL; } lsize = scp->xsize * sizeof(u_int16_t); csize = ptr->xsize * sizeof(u_int16_t); /* Pointer to the last line of framebuffer */ frbp = scp->vtb.vtb_buffer + scp->ysize * lsize + ptr->x * sizeof(u_int16_t); /* Pointer to the last line of target buffer */ outp = (char *)outp + ptr->ysize * csize; /* Pointer to the last line of history buffer */ if (scp->history != NULL) hstp = scp->history->vtb_buffer + sc_vtb_tail(scp->history) * sizeof(u_int16_t) + ptr->x * sizeof(u_int16_t); else hstp = 0; retval = 0; for (lnum = 0; lnum < (ptr->y + ptr->ysize); lnum++) { if (lnum < scp->ysize) { frbp -= lsize; } else { hstp -= lsize; if (hstp < scp->history->vtb_buffer) hstp += scp->history->vtb_rows * lsize; frbp = hstp; } if (lnum < ptr->y) continue; outp = (char *)outp - csize; retval = copyout((void *)frbp, outp, csize); if (retval != 0) break; } splx(s); return retval; } case VT_SETMODE: /* set screen switcher mode */ { struct vt_mode *mode; struct proc *p1; mode = (struct vt_mode *)data; DPRINTF(5, ("%s%d: VT_SETMODE ", SC_DRIVER_NAME, sc->unit)); if (scp->smode.mode == VT_PROCESS) { p1 = pfind(scp->pid); if (scp->proc == p1 && scp->proc != td->td_proc) { if (p1) PROC_UNLOCK(p1); DPRINTF(5, ("error EPERM\n")); return EPERM; } if (p1) PROC_UNLOCK(p1); } s = spltty(); if (mode->mode == VT_AUTO) { scp->smode.mode = VT_AUTO; scp->proc = NULL; scp->pid = 0; DPRINTF(5, ("VT_AUTO, ")); if ((scp == sc->cur_scp) && (sc->unit == sc_console_unit)) cnavailable(sc_consptr, TRUE); /* were we in the middle of the vty switching process? */ if (finish_vt_rel(scp, TRUE, &s) == 0) DPRINTF(5, ("reset WAIT_REL, ")); if (finish_vt_acq(scp) == 0) DPRINTF(5, ("reset WAIT_ACQ, ")); } else { if (!ISSIGVALID(mode->relsig) || !ISSIGVALID(mode->acqsig) || !ISSIGVALID(mode->frsig)) { splx(s); DPRINTF(5, ("error EINVAL\n")); return EINVAL; } DPRINTF(5, ("VT_PROCESS %d, ", td->td_proc->p_pid)); bcopy(data, &scp->smode, sizeof(struct vt_mode)); scp->proc = td->td_proc; scp->pid = scp->proc->p_pid; if ((scp == sc->cur_scp) && (sc->unit == sc_console_unit)) cnavailable(sc_consptr, FALSE); } splx(s); DPRINTF(5, ("\n")); return 0; } case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('v', 4): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case VT_RELDISP: /* screen switcher ioctl */ s = spltty(); /* * This must be the current vty which is in the VT_PROCESS * switching mode... */ if ((scp != sc->cur_scp) || (scp->smode.mode != VT_PROCESS)) { splx(s); return EINVAL; } /* ...and this process is controlling it. */ if (scp->proc != td->td_proc) { splx(s); return EPERM; } error = EINVAL; switch(*(int *)data) { case VT_FALSE: /* user refuses to release screen, abort */ if ((error = finish_vt_rel(scp, FALSE, &s)) == 0) DPRINTF(5, ("%s%d: VT_FALSE\n", SC_DRIVER_NAME, sc->unit)); break; case VT_TRUE: /* user has released screen, go on */ if ((error = finish_vt_rel(scp, TRUE, &s)) == 0) DPRINTF(5, ("%s%d: VT_TRUE\n", SC_DRIVER_NAME, sc->unit)); break; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if ((error = finish_vt_acq(scp)) == 0) DPRINTF(5, ("%s%d: VT_ACKACQ\n", SC_DRIVER_NAME, sc->unit)); break; default: break; } splx(s); return error; case VT_OPENQRY: /* return free virtual console */ for (i = sc->first_vty; i < sc->first_vty + sc->vtys; i++) { tp = SC_DEV(sc, i); if (!tty_opened(tp)) { *(int *)data = i + 1; return 0; } } return EINVAL; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('v', 5): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case VT_ACTIVATE: /* switch to screen *data */ i = (*(int *)data == 0) ? scp->index : (*(int *)data - 1); s = spltty(); error = sc_clean_up(sc->cur_scp); splx(s); if (error) return error; error = sc_switch_scr(sc, i); return (error); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('v', 6): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case VT_WAITACTIVE: /* wait for switch to occur */ i = (*(int *)data == 0) ? scp->index : (*(int *)data - 1); if ((i < sc->first_vty) || (i >= sc->first_vty + sc->vtys)) return EINVAL; if (i == sc->cur_scp->index) return 0; error = tsleep(VTY_WCHAN(sc, i), (PZERO + 1) | PCATCH, "waitvt", 0); return error; case VT_GETACTIVE: /* get active vty # */ *(int *)data = sc->cur_scp->index + 1; return 0; case VT_GETINDEX: /* get this vty # */ *(int *)data = scp->index + 1; return 0; case VT_LOCKSWITCH: /* prevent vty switching */ if ((*(int *)data) & 0x01) sc->flags |= SC_SCRN_VTYLOCK; else sc->flags &= ~SC_SCRN_VTYLOCK; return 0; case KDENABIO: /* allow io operations */ error = priv_check(td, PRIV_IO); if (error != 0) return error; error = securelevel_gt(td->td_ucred, 0); if (error != 0) return error; #ifdef __i386__ td->td_frame->tf_eflags |= PSL_IOPL; #elif defined(__amd64__) td->td_frame->tf_rflags |= PSL_IOPL; #endif return 0; case KDDISABIO: /* disallow io operations (default) */ #ifdef __i386__ td->td_frame->tf_eflags &= ~PSL_IOPL; #elif defined(__amd64__) td->td_frame->tf_rflags &= ~PSL_IOPL; #endif return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set keyboard state (locks) */ if (*(int *)data & ~LOCK_MASK) return EINVAL; scp->status &= ~LOCK_MASK; scp->status |= *(int *)data; if (scp == sc->cur_scp) update_kbd_state(scp, scp->status, LOCK_MASK); return 0; case KDGKBSTATE: /* get keyboard state (locks) */ if (scp == sc->cur_scp) save_kbd_state(scp); *(int *)data = scp->status & LOCK_MASK; return 0; case KDGETREPEAT: /* get keyboard repeat & delay rates */ case KDSETREPEAT: /* set keyboard repeat & delay rates (new) */ error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat & delay rates (old) */ if (*(int *)data & ~0x7f) return EINVAL; error = kbdd_ioctl(sc->kbd, KDSETRAD, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)data) { case K_XLATE: /* switch to XLT ascii mode */ case K_RAW: /* switch to RAW scancode mode */ case K_CODE: /* switch to CODE mode */ scp->kbd_mode = *(int *)data; if (scp == sc->cur_scp) kbdd_ioctl(sc->kbd, KDSKBMODE, data); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *(int *)data = scp->kbd_mode; return 0; case KDGKBINFO: error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 8): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDMKTONE: /* sound the bell */ if (*(int*)data) sc_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else sc_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 63): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KIOCSOUND: /* make tone (*data) hz */ if (scp == sc->cur_scp) { if (*(int *)data) return sc_tone(*(int *)data); else return sc_tone(0); } return 0; case KDGKBTYPE: /* get keyboard type */ error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) { /* always return something? XXX */ *(int *)data = 0; } return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED status */ if (*(int *)data & ~LED_MASK) /* FIXME: LOCK_MASK? */ return EINVAL; scp->status &= ~LED_MASK; scp->status |= *(int *)data; if (scp == sc->cur_scp) update_kbd_leds(scp, scp->status); return 0; case KDGETLED: /* get keyboard LED status */ if (scp == sc->cur_scp) save_kbd_state(scp); *(int *)data = scp->status & LED_MASK; return 0; case KBADDKBD: /* add/remove keyboard to/from mux */ case KBRELKBD: error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('c', 110): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ #endif case CONS_SETKBD: /* set the new keyboard */ { keyboard_t *newkbd; s = spltty(); newkbd = kbd_get_keyboard(*(int *)data); if (newkbd == NULL) { splx(s); return EINVAL; } error = 0; if (sc->kbd != newkbd) { i = kbd_allocate(newkbd->kb_name, newkbd->kb_unit, (void *)&sc->keyboard, sckbdevent, sc); /* i == newkbd->kb_index */ if (i >= 0) { if (sc->kbd != NULL) { save_kbd_state(sc->cur_scp); kbd_release(sc->kbd, (void *)&sc->keyboard); } sc->kbd = kbd_get_keyboard(i); /* sc->kbd == newkbd */ sc->keyboard = i; kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); update_kbd_state(sc->cur_scp, sc->cur_scp->status, LOCK_MASK); } else { error = EPERM; /* XXX */ } } splx(s); return error; } case CONS_RELKBD: /* release the current keyboard */ s = spltty(); error = 0; if (sc->kbd != NULL) { save_kbd_state(sc->cur_scp); error = kbd_release(sc->kbd, (void *)&sc->keyboard); if (error == 0) { sc->kbd = NULL; sc->keyboard = -1; } } splx(s); return error; case CONS_GETTERM: /* get the current terminal emulator info */ { sc_term_sw_t *sw; if (((term_info_t *)data)->ti_index == 0) { sw = scp->tsw; } else { sw = sc_term_match_by_number(((term_info_t *)data)->ti_index); } if (sw != NULL) { strncpy(((term_info_t *)data)->ti_name, sw->te_name, sizeof(((term_info_t *)data)->ti_name)); strncpy(((term_info_t *)data)->ti_desc, sw->te_desc, sizeof(((term_info_t *)data)->ti_desc)); ((term_info_t *)data)->ti_flags = 0; return 0; } else { ((term_info_t *)data)->ti_name[0] = '\0'; ((term_info_t *)data)->ti_desc[0] = '\0'; ((term_info_t *)data)->ti_flags = 0; return EINVAL; } } case CONS_SETTERM: /* set the current terminal emulator */ s = spltty(); error = sc_init_emulator(scp, ((term_info_t *)data)->ti_name); /* FIXME: what if scp == sc_console! XXX */ splx(s); return error; case GIO_SCRNMAP: /* get output translation table */ bcopy(&sc->scr_map, data, sizeof(sc->scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &sc->scr_map, sizeof(sc->scr_map)); for (i=0; iscr_map); i++) { sc->scr_rmap[sc->scr_map[i]] = i; } return 0; case GIO_KEYMAP: /* get keyboard translation table */ case PIO_KEYMAP: /* set keyboard translation table */ case GIO_DEADKEYMAP: /* get accent key translation table */ case PIO_DEADKEYMAP: /* set accent key translation table */ case GETFKEY: /* get function key string */ case SETFKEY: /* set function key string */ error = kbdd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #ifndef SC_NO_FONT_LOADING case PIO_FONT8x8: /* set 8x8 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_8, 8*256); sc->fonts_loaded |= FONT_8; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x8. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size < 14)) sc_load_font(sc->cur_scp, 0, 8, 8, sc->font_8, 0, 256); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_8) { bcopy(sc->font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_14, 14*256); sc->fonts_loaded |= FONT_14; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x14. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size >= 14) && (sc->cur_scp->font_size < 16)) sc_load_font(sc->cur_scp, 0, 14, 8, sc->font_14, 0, 256); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_14) { bcopy(sc->font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_16, 16*256); sc->fonts_loaded |= FONT_16; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x16. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size >= 16)) sc_load_font(sc->cur_scp, 0, 16, 8, sc->font_16, 0, 256); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_16) { bcopy(sc->font_16, data, 16*256); return 0; } else return ENXIO; #endif /* SC_NO_FONT_LOADING */ default: break; } return (ENOIOCTL); } static int consolectl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { return sctty_ioctl(dev->si_drv1, cmd, data, td); } static void sc_cnprobe(struct consdev *cp) { int unit; int flags; cp->cn_pri = sc_get_cons_priority(&unit, &flags); /* a video card is always required */ if (!scvidprobe(unit, flags, TRUE)) cp->cn_pri = CN_DEAD; /* syscons will become console even when there is no keyboard */ sckbdprobe(unit, flags, TRUE); if (cp->cn_pri == CN_DEAD) return; /* initialize required fields */ strcpy(cp->cn_name, "ttyv0"); } static void sc_cninit(struct consdev *cp) { int unit; int flags; sc_get_cons_priority(&unit, &flags); scinit(unit, flags | SC_KERNEL_CONSOLE); sc_console_unit = unit; sc_console = sc_get_stat(sc_get_softc(unit, SC_KERNEL_CONSOLE)->dev[0]); sc_consptr = cp; } static void sc_cnterm(struct consdev *cp) { /* we are not the kernel console any more, release everything */ if (sc_console_unit < 0) return; /* shouldn't happen */ #if 0 /* XXX */ sc_clear_screen(sc_console); sccnupdate(sc_console); #endif scterm(sc_console_unit, SC_KERNEL_CONSOLE); sc_console_unit = -1; sc_console = NULL; } static void sc_cnputc(struct consdev *cd, int c) { u_char buf[1]; scr_stat *scp = sc_console; #ifndef SC_NO_HISTORY #if 0 struct tty *tp; #endif #endif /* !SC_NO_HISTORY */ int s; /* assert(sc_console != NULL) */ #ifndef SC_NO_HISTORY if (scp == scp->sc->cur_scp && scp->status & SLKED) { scp->status &= ~SLKED; update_kbd_state(scp, scp->status, SLKED); if (scp->status & BUFFER_SAVED) { if (!sc_hist_restore(scp)) sc_remove_cutmarking(scp); scp->status &= ~BUFFER_SAVED; scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } #if 0 /* * XXX: Now that TTY's have their own locks, we cannot process * any data after disabling scroll lock. cnputs already holds a * spinlock. */ tp = SC_DEV(scp->sc, scp->index); tty_lock(tp); if (tty_opened(tp)) sctty_outwakeup(tp); tty_unlock(tp); #endif } #endif /* !SC_NO_HISTORY */ buf[0] = c; sc_puts(scp, buf, 1, 1); s = spltty(); /* block sckbdevent and scrn_timer */ sccnupdate(scp); splx(s); } static int sc_cngetc(struct consdev *cd) { static struct fkeytab fkey; static int fkeycp; scr_stat *scp; const u_char *p; int cur_mode; int s = spltty(); /* block sckbdevent and scrn_timer while we poll */ int c; /* assert(sc_console != NULL) */ /* * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ sc_touch_scrn_saver(); scp = sc_console->sc->cur_scp; /* XXX */ sccnupdate(scp); if (fkeycp < fkey.len) { splx(s); return fkey.str[fkeycp++]; } if (scp->sc->kbd == NULL) { splx(s); return -1; } /* * Make sure the keyboard is accessible even when the kbd device * driver is disabled. */ kbdd_enable(scp->sc->kbd); /* we shall always use the keyboard in the XLATE mode here */ cur_mode = scp->kbd_mode; scp->kbd_mode = K_XLATE; kbdd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); kbdd_poll(scp->sc->kbd, TRUE); c = scgetc(scp->sc, SCGETC_CN | SCGETC_NONBLOCK); kbdd_poll(scp->sc->kbd, FALSE); scp->kbd_mode = cur_mode; kbdd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); kbdd_disable(scp->sc->kbd); splx(s); switch (KEYFLAGS(c)) { case 0: /* normal char */ return KEYCHAR(c); case FKEY: /* function key */ p = (*scp->tsw->te_fkeystr)(scp, c); if (p != NULL) { fkey.len = strlen(p); bcopy(p, fkey.str, fkey.len); fkeycp = 1; return fkey.str[0]; } p = kbdd_get_fkeystr(scp->sc->kbd, KEYCHAR(c), (size_t *)&fkeycp); fkey.len = fkeycp; if ((p != NULL) && (fkey.len > 0)) { bcopy(p, fkey.str, fkey.len); fkeycp = 1; return fkey.str[0]; } return c; /* XXX */ case NOKEY: case ERRKEY: default: return -1; } /* NOT REACHED */ } static void sccnupdate(scr_stat *scp) { /* this is a cut-down version of scrn_timer()... */ if (scp->sc->font_loading_in_progress) return; if (debugger > 0 || panicstr || shutdown_in_progress) { sc_touch_scrn_saver(); } else if (scp != scp->sc->cur_scp) { return; } if (!run_scrn_saver) scp->sc->flags &= ~SC_SCRN_IDLE; #ifdef DEV_SPLASH if ((saver_mode != CONS_LKM_SAVER) || !(scp->sc->flags & SC_SCRN_IDLE)) if (scp->sc->flags & SC_SCRN_BLANKED) stop_scrn_saver(scp->sc, current_saver); #endif if (scp != scp->sc->cur_scp || scp->sc->blink_in_progress || scp->sc->switch_in_progress) return; /* * FIXME: unlike scrn_timer(), we call scrn_update() from here even * when write_in_progress is non-zero. XXX */ if (!ISGRAPHSC(scp) && !(scp->sc->flags & SC_SCRN_BLANKED)) scrn_update(scp, TRUE); } static void scrn_timer(void *arg) { #ifndef PC98 static int kbd_interval = 0; #endif struct timeval tv; sc_softc_t *sc; scr_stat *scp; int again; int s; again = (arg != NULL); if (arg != NULL) sc = (sc_softc_t *)arg; else if (sc_console != NULL) sc = sc_console->sc; else return; /* don't do anything when we are performing some I/O operations */ if (sc->font_loading_in_progress) { if (again) timeout(scrn_timer, sc, hz / 10); return; } s = spltty(); #ifndef PC98 if ((sc->kbd == NULL) && (sc->config & SC_AUTODETECT_KBD)) { /* try to allocate a keyboard automatically */ if (++kbd_interval >= 25) { sc->keyboard = sc_allocate_keyboard(sc, -1); if (sc->keyboard >= 0) { sc->kbd = kbd_get_keyboard(sc->keyboard); kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); update_kbd_state(sc->cur_scp, sc->cur_scp->status, LOCK_MASK); } kbd_interval = 0; } } #endif /* PC98 */ /* find the vty to update */ scp = sc->cur_scp; /* should we stop the screen saver? */ getmicrouptime(&tv); if (debugger > 0 || panicstr || shutdown_in_progress) sc_touch_scrn_saver(); if (run_scrn_saver) { if (tv.tv_sec > sc->scrn_time_stamp + scrn_blank_time) sc->flags |= SC_SCRN_IDLE; else sc->flags &= ~SC_SCRN_IDLE; } else { sc->scrn_time_stamp = tv.tv_sec; sc->flags &= ~SC_SCRN_IDLE; if (scrn_blank_time > 0) run_scrn_saver = TRUE; } #ifdef DEV_SPLASH if ((saver_mode != CONS_LKM_SAVER) || !(sc->flags & SC_SCRN_IDLE)) if (sc->flags & SC_SCRN_BLANKED) stop_scrn_saver(sc, current_saver); #endif /* should we just return ? */ if (sc->blink_in_progress || sc->switch_in_progress || sc->write_in_progress) { if (again) timeout(scrn_timer, sc, hz / 10); splx(s); return; } /* Update the screen */ scp = sc->cur_scp; /* cur_scp may have changed... */ if (!ISGRAPHSC(scp) && !(sc->flags & SC_SCRN_BLANKED)) scrn_update(scp, TRUE); #ifdef DEV_SPLASH /* should we activate the screen saver? */ if ((saver_mode == CONS_LKM_SAVER) && (sc->flags & SC_SCRN_IDLE)) if (!ISGRAPHSC(scp) || (sc->flags & SC_SCRN_BLANKED)) (*current_saver)(sc, TRUE); #endif if (again) timeout(scrn_timer, sc, hz / 25); splx(s); } static int and_region(int *s1, int *e1, int s2, int e2) { if (*e1 < s2 || e2 < *s1) return FALSE; *s1 = imax(*s1, s2); *e1 = imin(*e1, e2); return TRUE; } static void scrn_update(scr_stat *scp, int show_cursor) { int start; int end; int s; int e; /* assert(scp == scp->sc->cur_scp) */ SC_VIDEO_LOCK(scp->sc); #ifndef SC_NO_CUTPASTE /* remove the previous mouse pointer image if necessary */ if (scp->status & MOUSE_VISIBLE) { s = scp->mouse_pos; e = scp->mouse_pos + scp->xsize + 1; if ((scp->status & (MOUSE_MOVED | MOUSE_HIDDEN)) || and_region(&s, &e, scp->start, scp->end) || ((scp->status & CURSOR_ENABLED) && (scp->cursor_pos != scp->cursor_oldpos) && (and_region(&s, &e, scp->cursor_pos, scp->cursor_pos) || and_region(&s, &e, scp->cursor_oldpos, scp->cursor_oldpos)))) { sc_remove_mouse_image(scp); if (scp->end >= scp->xsize*scp->ysize) scp->end = scp->xsize*scp->ysize - 1; } } #endif /* !SC_NO_CUTPASTE */ #if 1 /* debug: XXX */ if (scp->end >= scp->xsize*scp->ysize) { printf("scrn_update(): scp->end %d > size_of_screen!!\n", scp->end); scp->end = scp->xsize*scp->ysize - 1; } if (scp->start < 0) { printf("scrn_update(): scp->start %d < 0\n", scp->start); scp->start = 0; } #endif /* update screen image */ if (scp->start <= scp->end) { if (scp->mouse_cut_end >= 0) { /* there is a marked region for cut & paste */ if (scp->mouse_cut_start <= scp->mouse_cut_end) { start = scp->mouse_cut_start; end = scp->mouse_cut_end; } else { start = scp->mouse_cut_end; end = scp->mouse_cut_start - 1; } s = start; e = end; /* does the cut-mark region overlap with the update region? */ if (and_region(&s, &e, scp->start, scp->end)) { (*scp->rndr->draw)(scp, s, e - s + 1, TRUE); s = 0; e = start - 1; if (and_region(&s, &e, scp->start, scp->end)) (*scp->rndr->draw)(scp, s, e - s + 1, FALSE); s = end + 1; e = scp->xsize*scp->ysize - 1; if (and_region(&s, &e, scp->start, scp->end)) (*scp->rndr->draw)(scp, s, e - s + 1, FALSE); } else { (*scp->rndr->draw)(scp, scp->start, scp->end - scp->start + 1, FALSE); } } else { (*scp->rndr->draw)(scp, scp->start, scp->end - scp->start + 1, FALSE); } } /* we are not to show the cursor and the mouse pointer... */ if (!show_cursor) { scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; SC_VIDEO_UNLOCK(scp->sc); return; } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { s = scp->start; e = scp->end; /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if (!and_region(&s, &e, scp->cursor_oldpos, scp->cursor_oldpos)) sc_remove_cursor_image(scp); sc_draw_cursor_image(scp); } else { if (and_region(&s, &e, scp->cursor_pos, scp->cursor_pos)) /* cursor didn't move, but has been overwritten */ sc_draw_cursor_image(scp); else if (scp->curs_attr.flags & CONS_BLINK_CURSOR) /* if it's a blinking cursor, update it */ (*scp->rndr->blink_cursor)(scp, scp->cursor_pos, sc_inside_cutmark(scp, scp->cursor_pos)); } } #ifndef SC_NO_CUTPASTE /* update "pseudo" mouse pointer image */ if (scp->sc->flags & SC_MOUSE_ENABLED) { if (!(scp->status & (MOUSE_VISIBLE | MOUSE_HIDDEN))) { scp->status &= ~MOUSE_MOVED; sc_draw_mouse_image(scp); } } #endif /* SC_NO_CUTPASTE */ scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; SC_VIDEO_UNLOCK(scp->sc); } #ifdef DEV_SPLASH static int scsplash_callback(int event, void *arg) { sc_softc_t *sc; int error; sc = (sc_softc_t *)arg; switch (event) { case SPLASH_INIT: if (add_scrn_saver(scsplash_saver) == 0) { sc->flags &= ~SC_SAVER_FAILED; run_scrn_saver = TRUE; if (cold && !(boothowto & RB_VERBOSE)) { scsplash_stick(TRUE); (*current_saver)(sc, TRUE); } } return 0; case SPLASH_TERM: if (current_saver == scsplash_saver) { scsplash_stick(FALSE); error = remove_scrn_saver(scsplash_saver); if (error) return error; } return 0; default: return EINVAL; } } static void scsplash_saver(sc_softc_t *sc, int show) { static int busy = FALSE; scr_stat *scp; if (busy) return; busy = TRUE; scp = sc->cur_scp; if (show) { if (!(sc->flags & SC_SAVER_FAILED)) { if (!(sc->flags & SC_SCRN_BLANKED)) set_scrn_saver_mode(scp, -1, NULL, 0); switch (splash(sc->adp, TRUE)) { case 0: /* succeeded */ break; case EAGAIN: /* try later */ restore_scrn_saver_mode(scp, FALSE); sc_touch_scrn_saver(); /* XXX */ break; default: sc->flags |= SC_SAVER_FAILED; scsplash_stick(FALSE); restore_scrn_saver_mode(scp, TRUE); printf("scsplash_saver(): failed to put up the image\n"); break; } } } else if (!sticky_splash) { if ((sc->flags & SC_SCRN_BLANKED) && (splash(sc->adp, FALSE) == 0)) restore_scrn_saver_mode(scp, TRUE); } busy = FALSE; } static int add_scrn_saver(void (*this_saver)(sc_softc_t *, int)) { #if 0 int error; if (current_saver != none_saver) { error = remove_scrn_saver(current_saver); if (error) return error; } #endif if (current_saver != none_saver) return EBUSY; run_scrn_saver = FALSE; saver_mode = CONS_LKM_SAVER; current_saver = this_saver; return 0; } static int remove_scrn_saver(void (*this_saver)(sc_softc_t *, int)) { if (current_saver != this_saver) return EINVAL; #if 0 /* * In order to prevent `current_saver' from being called by * the timeout routine `scrn_timer()' while we manipulate * the saver list, we shall set `current_saver' to `none_saver' * before stopping the current saver, rather than blocking by `splXX()'. */ current_saver = none_saver; if (scrn_blanked) stop_scrn_saver(this_saver); #endif /* unblank all blanked screens */ wait_scrn_saver_stop(NULL); if (scrn_blanked) return EBUSY; current_saver = none_saver; return 0; } static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border) { int s; /* assert(scp == scp->sc->cur_scp) */ s = spltty(); if (!ISGRAPHSC(scp)) sc_remove_cursor_image(scp); scp->splash_save_mode = scp->mode; scp->splash_save_status = scp->status & (GRAPHICS_MODE | PIXEL_MODE); scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE); scp->status |= (UNKNOWN_MODE | SAVER_RUNNING); scp->sc->flags |= SC_SCRN_BLANKED; ++scrn_blanked; splx(s); if (mode < 0) return 0; scp->mode = mode; if (set_mode(scp) == 0) { if (scp->sc->adp->va_info.vi_flags & V_INFO_GRAPHICS) scp->status |= GRAPHICS_MODE; #ifndef SC_NO_PALETTE_LOADING if (pal != NULL) vidd_load_palette(scp->sc->adp, pal); #endif sc_set_border(scp, border); return 0; } else { s = spltty(); scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; splx(s); return 1; } } static int restore_scrn_saver_mode(scr_stat *scp, int changemode) { int mode; int status; int s; /* assert(scp == scp->sc->cur_scp) */ s = spltty(); mode = scp->mode; status = scp->status; scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; scp->sc->flags &= ~SC_SCRN_BLANKED; if (!changemode) { if (!ISGRAPHSC(scp)) sc_draw_cursor_image(scp); --scrn_blanked; splx(s); return 0; } if (set_mode(scp) == 0) { #ifndef SC_NO_PALETTE_LOADING +#ifdef SC_PIXEL_MODE + if ((scp->sc->adp->va_flags & V_ADP_DAC8) != 0) + vidd_load_palette(scp->sc->adp, scp->sc->palette2); + else +#endif vidd_load_palette(scp->sc->adp, scp->sc->palette); #endif --scrn_blanked; splx(s); return 0; } else { scp->mode = mode; scp->status = status; splx(s); return 1; } } static void stop_scrn_saver(sc_softc_t *sc, void (*saver)(sc_softc_t *, int)) { (*saver)(sc, FALSE); run_scrn_saver = FALSE; /* the screen saver may have chosen not to stop after all... */ if (sc->flags & SC_SCRN_BLANKED) return; mark_all(sc->cur_scp); if (sc->delayed_next_scr) sc_switch_scr(sc, sc->delayed_next_scr - 1); if (debugger == 0) wakeup(&scrn_blanked); } static int wait_scrn_saver_stop(sc_softc_t *sc) { int error = 0; while (scrn_blanked > 0) { run_scrn_saver = FALSE; if (sc && !(sc->flags & SC_SCRN_BLANKED)) { error = 0; break; } error = tsleep(&scrn_blanked, PZERO | PCATCH, "scrsav", 0); if ((error != 0) && (error != ERESTART)) break; } run_scrn_saver = FALSE; return error; } #endif /* DEV_SPLASH */ void sc_touch_scrn_saver(void) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } int sc_switch_scr(sc_softc_t *sc, u_int next_scr) { scr_stat *cur_scp; struct tty *tp; struct proc *p; int s; DPRINTF(5, ("sc0: sc_switch_scr() %d ", next_scr + 1)); if (sc->cur_scp == NULL) return (0); /* prevent switch if previously requested */ if (sc->flags & SC_SCRN_VTYLOCK) { sc_bell(sc->cur_scp, sc->cur_scp->bell_pitch, sc->cur_scp->bell_duration); return EPERM; } /* delay switch if the screen is blanked or being updated */ if ((sc->flags & SC_SCRN_BLANKED) || sc->write_in_progress || sc->blink_in_progress) { sc->delayed_next_scr = next_scr + 1; sc_touch_scrn_saver(); DPRINTF(5, ("switch delayed\n")); return 0; } sc->delayed_next_scr = 0; s = spltty(); cur_scp = sc->cur_scp; /* we are in the middle of the vty switching process... */ if (sc->switch_in_progress && (cur_scp->smode.mode == VT_PROCESS) && cur_scp->proc) { p = pfind(cur_scp->pid); if (cur_scp->proc != p) { if (p) PROC_UNLOCK(p); /* * The controlling process has died!!. Do some clean up. * NOTE:`cur_scp->proc' and `cur_scp->smode.mode' * are not reset here yet; they will be cleared later. */ DPRINTF(5, ("cur_scp controlling process %d died, ", cur_scp->pid)); if (cur_scp->status & SWITCH_WAIT_REL) { /* * Force the previous switch to finish, but return now * with error. */ DPRINTF(5, ("reset WAIT_REL, ")); finish_vt_rel(cur_scp, TRUE, &s); splx(s); DPRINTF(5, ("finishing previous switch\n")); return EINVAL; } else if (cur_scp->status & SWITCH_WAIT_ACQ) { /* let's assume screen switch has been completed. */ DPRINTF(5, ("reset WAIT_ACQ, ")); finish_vt_acq(cur_scp); } else { /* * We are in between screen release and acquisition, and * reached here via scgetc() or scrn_timer() which has * interrupted exchange_scr(). Don't do anything stupid. */ DPRINTF(5, ("waiting nothing, ")); } } else { if (p) PROC_UNLOCK(p); /* * The controlling process is alive, but not responding... * It is either buggy or it may be just taking time. * The following code is a gross kludge to cope with this * problem for which there is no clean solution. XXX */ if (cur_scp->status & SWITCH_WAIT_REL) { switch (sc->switch_in_progress++) { case 1: break; case 2: DPRINTF(5, ("sending relsig again, ")); signal_vt_rel(cur_scp); break; case 3: break; case 4: default: /* * Act as if the controlling program returned * VT_FALSE. */ DPRINTF(5, ("force reset WAIT_REL, ")); finish_vt_rel(cur_scp, FALSE, &s); splx(s); DPRINTF(5, ("act as if VT_FALSE was seen\n")); return EINVAL; } } else if (cur_scp->status & SWITCH_WAIT_ACQ) { switch (sc->switch_in_progress++) { case 1: break; case 2: DPRINTF(5, ("sending acqsig again, ")); signal_vt_acq(cur_scp); break; case 3: break; case 4: default: /* clear the flag and finish the previous switch */ DPRINTF(5, ("force reset WAIT_ACQ, ")); finish_vt_acq(cur_scp); break; } } } } /* * Return error if an invalid argument is given, or vty switch * is still in progress. */ if ((next_scr < sc->first_vty) || (next_scr >= sc->first_vty + sc->vtys) || sc->switch_in_progress) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 1\n")); return EINVAL; } /* * Don't allow switching away from the graphics mode vty * if the switch mode is VT_AUTO, unless the next vty is the same * as the current or the current vty has been closed (but showing). */ tp = SC_DEV(sc, cur_scp->index); if ((cur_scp->index != next_scr) && tty_opened(tp) && (cur_scp->smode.mode == VT_AUTO) && ISGRAPHSC(cur_scp)) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error, graphics mode\n")); return EINVAL; } /* * Is the wanted vty open? Don't allow switching to a closed vty. * If we are in DDB, don't switch to a vty in the VT_PROCESS mode. * Note that we always allow the user to switch to the kernel * console even if it is closed. */ if ((sc_console == NULL) || (next_scr != sc_console->index)) { tp = SC_DEV(sc, next_scr); if (!tty_opened(tp)) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 2, requested vty isn't open!\n")); return EINVAL; } if ((debugger > 0) && (SC_STAT(tp)->smode.mode == VT_PROCESS)) { splx(s); DPRINTF(5, ("error 3, requested vty is in the VT_PROCESS mode\n")); return EINVAL; } } /* this is the start of vty switching process... */ ++sc->switch_in_progress; sc->old_scp = cur_scp; sc->new_scp = sc_get_stat(SC_DEV(sc, next_scr)); if (sc->new_scp == sc->old_scp) { sc->switch_in_progress = 0; /* * XXX wakeup() locks the scheduler lock which will hang if * the lock is in an in-between state, e.g., when we stop at * a breakpoint at fork_exit. It has always been wrong to call * wakeup() when the debugger is active. In RELENG_4, wakeup() * is supposed to be locked by splhigh(), but the debugger may * be invoked at splhigh(). */ if (debugger == 0) wakeup(VTY_WCHAN(sc,next_scr)); splx(s); DPRINTF(5, ("switch done (new == old)\n")); return 0; } /* has controlling process died? */ vt_proc_alive(sc->old_scp); vt_proc_alive(sc->new_scp); /* wait for the controlling process to release the screen, if necessary */ if (signal_vt_rel(sc->old_scp)) { splx(s); return 0; } /* go set up the new vty screen */ splx(s); exchange_scr(sc); s = spltty(); /* wake up processes waiting for this vty */ if (debugger == 0) wakeup(VTY_WCHAN(sc,next_scr)); /* wait for the controlling process to acknowledge, if necessary */ if (signal_vt_acq(sc->cur_scp)) { splx(s); return 0; } sc->switch_in_progress = 0; if (sc->unit == sc_console_unit) cnavailable(sc_consptr, TRUE); splx(s); DPRINTF(5, ("switch done\n")); return 0; } static int do_switch_scr(sc_softc_t *sc, int s) { vt_proc_alive(sc->new_scp); splx(s); exchange_scr(sc); s = spltty(); /* sc->cur_scp == sc->new_scp */ wakeup(VTY_WCHAN(sc,sc->cur_scp->index)); /* wait for the controlling process to acknowledge, if necessary */ if (!signal_vt_acq(sc->cur_scp)) { sc->switch_in_progress = 0; if (sc->unit == sc_console_unit) cnavailable(sc_consptr, TRUE); } return s; } static int vt_proc_alive(scr_stat *scp) { struct proc *p; if (scp->proc) { if ((p = pfind(scp->pid)) != NULL) PROC_UNLOCK(p); if (scp->proc == p) return TRUE; scp->proc = NULL; scp->smode.mode = VT_AUTO; DPRINTF(5, ("vt controlling process %d died\n", scp->pid)); } return FALSE; } static int signal_vt_rel(scr_stat *scp) { if (scp->smode.mode != VT_PROCESS) return FALSE; scp->status |= SWITCH_WAIT_REL; PROC_LOCK(scp->proc); psignal(scp->proc, scp->smode.relsig); PROC_UNLOCK(scp->proc); DPRINTF(5, ("sending relsig to %d\n", scp->pid)); return TRUE; } static int signal_vt_acq(scr_stat *scp) { if (scp->smode.mode != VT_PROCESS) return FALSE; if (scp->sc->unit == sc_console_unit) cnavailable(sc_consptr, FALSE); scp->status |= SWITCH_WAIT_ACQ; PROC_LOCK(scp->proc); psignal(scp->proc, scp->smode.acqsig); PROC_UNLOCK(scp->proc); DPRINTF(5, ("sending acqsig to %d\n", scp->pid)); return TRUE; } static int finish_vt_rel(scr_stat *scp, int release, int *s) { if (scp == scp->sc->old_scp && scp->status & SWITCH_WAIT_REL) { scp->status &= ~SWITCH_WAIT_REL; if (release) *s = do_switch_scr(scp->sc, *s); else scp->sc->switch_in_progress = 0; return 0; } return EINVAL; } static int finish_vt_acq(scr_stat *scp) { if (scp == scp->sc->new_scp && scp->status & SWITCH_WAIT_ACQ) { scp->status &= ~SWITCH_WAIT_ACQ; scp->sc->switch_in_progress = 0; return 0; } return EINVAL; } static void exchange_scr(sc_softc_t *sc) { scr_stat *scp; /* save the current state of video and keyboard */ sc_move_cursor(sc->old_scp, sc->old_scp->xpos, sc->old_scp->ypos); if (!ISGRAPHSC(sc->old_scp)) sc_remove_cursor_image(sc->old_scp); if (sc->old_scp->kbd_mode == K_XLATE) save_kbd_state(sc->old_scp); /* set up the video for the new screen */ scp = sc->cur_scp = sc->new_scp; #ifdef PC98 if (sc->old_scp->mode != scp->mode || ISUNKNOWNSC(sc->old_scp) || ISUNKNOWNSC(sc->new_scp)) #else if (sc->old_scp->mode != scp->mode || ISUNKNOWNSC(sc->old_scp)) #endif set_mode(scp); #ifndef __sparc64__ else sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)sc->adp->va_window, FALSE); #endif scp->status |= MOUSE_HIDDEN; sc_move_cursor(scp, scp->xpos, scp->ypos); if (!ISGRAPHSC(scp)) sc_set_cursor_image(scp); #ifndef SC_NO_PALETTE_LOADING - if (ISGRAPHSC(sc->old_scp)) + if (ISGRAPHSC(sc->old_scp)) { +#ifdef SC_PIXEL_MODE + if ((sc->adp->va_flags & V_ADP_DAC8) != 0) + vidd_load_palette(sc->adp, sc->palette2); + else +#endif vidd_load_palette(sc->adp, sc->palette); + } #endif sc_set_border(scp, scp->border); /* set up the keyboard for the new screen */ if (sc->old_scp->kbd_mode != scp->kbd_mode) kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); mark_all(scp); } void sc_puts(scr_stat *scp, u_char *buf, int len, int kernel) { int need_unlock = 0; #ifdef DEV_SPLASH /* make screensaver happy */ if (!sticky_splash && scp == scp->sc->cur_scp && !sc_saver_keyb_only) run_scrn_saver = FALSE; #endif if (scp->tsw) { if (!kdb_active && !mtx_owned(&scp->scr_lock)) { need_unlock = 1; mtx_lock_spin(&scp->scr_lock); } (*scp->tsw->te_puts)(scp, buf, len, kernel); if (need_unlock) mtx_unlock_spin(&scp->scr_lock); } if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } void sc_draw_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ SC_VIDEO_LOCK(scp->sc); (*scp->rndr->draw_cursor)(scp, scp->cursor_pos, scp->curs_attr.flags & CONS_BLINK_CURSOR, TRUE, sc_inside_cutmark(scp, scp->cursor_pos)); scp->cursor_oldpos = scp->cursor_pos; SC_VIDEO_UNLOCK(scp->sc); } void sc_remove_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ SC_VIDEO_LOCK(scp->sc); (*scp->rndr->draw_cursor)(scp, scp->cursor_oldpos, scp->curs_attr.flags & CONS_BLINK_CURSOR, FALSE, sc_inside_cutmark(scp, scp->cursor_oldpos)); SC_VIDEO_UNLOCK(scp->sc); } static void update_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ sc_remove_cursor_image(scp); sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } void sc_set_cursor_image(scr_stat *scp) { scp->curs_attr.flags = scp->curr_curs_attr.flags; if (scp->curs_attr.flags & CONS_HIDDEN_CURSOR) { /* hidden cursor is internally represented as zero-height underline */ scp->curs_attr.flags = CONS_CHAR_CURSOR; scp->curs_attr.base = scp->curs_attr.height = 0; } else if (scp->curs_attr.flags & CONS_CHAR_CURSOR) { scp->curs_attr.base = imin(scp->curr_curs_attr.base, scp->font_size - 1); scp->curs_attr.height = imin(scp->curr_curs_attr.height, scp->font_size - scp->curs_attr.base); } else { /* block cursor */ scp->curs_attr.base = 0; scp->curs_attr.height = scp->font_size; } /* assert(scp == scp->sc->cur_scp); */ SC_VIDEO_LOCK(scp->sc); (*scp->rndr->set_cursor)(scp, scp->curs_attr.base, scp->curs_attr.height, scp->curs_attr.flags & CONS_BLINK_CURSOR); SC_VIDEO_UNLOCK(scp->sc); } static void change_cursor_shape(scr_stat *scp, int flags, int base, int height) { if ((scp == scp->sc->cur_scp) && !ISGRAPHSC(scp)) sc_remove_cursor_image(scp); if (base >= 0) scp->curr_curs_attr.base = base; if (height >= 0) scp->curr_curs_attr.height = height; if (flags & CONS_RESET_CURSOR) scp->curr_curs_attr = scp->dflt_curs_attr; else scp->curr_curs_attr.flags = flags & CONS_CURSOR_ATTRS; if ((scp == scp->sc->cur_scp) && !ISGRAPHSC(scp)) { sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } } void sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height) { sc_softc_t *sc; struct tty *tp; int s; int i; s = spltty(); if ((flags != -1) && (flags & CONS_LOCAL_CURSOR)) { /* local (per vty) change */ change_cursor_shape(scp, flags, base, height); splx(s); return; } /* global change */ sc = scp->sc; if (base >= 0) sc->curs_attr.base = base; if (height >= 0) sc->curs_attr.height = height; if (flags != -1) { if (flags & CONS_RESET_CURSOR) sc->curs_attr = sc->dflt_curs_attr; else sc->curs_attr.flags = flags & CONS_CURSOR_ATTRS; } for (i = sc->first_vty; i < sc->first_vty + sc->vtys; ++i) { if ((tp = SC_DEV(sc, i)) == NULL) continue; if ((scp = sc_get_stat(tp)) == NULL) continue; scp->dflt_curs_attr = sc->curs_attr; change_cursor_shape(scp, CONS_RESET_CURSOR, -1, -1); } splx(s); } static void scinit(int unit, int flags) { /* * When syscons is being initialized as the kernel console, malloc() * is not yet functional, because various kernel structures has not been * fully initialized yet. Therefore, we need to declare the following * static buffers for the console. This is less than ideal, * but is necessry evil for the time being. XXX */ #ifdef PC98 static u_short sc_buffer[ROW*COL*2];/* XXX */ #else static u_short sc_buffer[ROW*COL]; /* XXX */ #endif #ifndef SC_NO_FONT_LOADING static u_char font_8[256*8]; static u_char font_14[256*14]; static u_char font_16[256*16]; #endif sc_softc_t *sc; scr_stat *scp; video_adapter_t *adp; int col; int row; int i; /* one time initialization */ if (init_done == COLD) sc_get_bios_values(&bios_value); init_done = WARM; /* * Allocate resources. Even if we are being called for the second * time, we must allocate them again, because they might have * disappeared... */ sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); if ((sc->flags & SC_INIT_DONE) == 0) SC_VIDEO_LOCKINIT(sc); adp = NULL; if (sc->adapter >= 0) { vid_release(sc->adp, (void *)&sc->adapter); adp = sc->adp; sc->adp = NULL; } if (sc->keyboard >= 0) { DPRINTF(5, ("sc%d: releasing kbd%d\n", unit, sc->keyboard)); i = kbd_release(sc->kbd, (void *)&sc->keyboard); DPRINTF(5, ("sc%d: kbd_release returned %d\n", unit, i)); if (sc->kbd != NULL) { DPRINTF(5, ("sc%d: kbd != NULL!, index:%d, unit:%d, flags:0x%x\n", unit, sc->kbd->kb_index, sc->kbd->kb_unit, sc->kbd->kb_flags)); } sc->kbd = NULL; } sc->adapter = vid_allocate("*", unit, (void *)&sc->adapter); sc->adp = vid_get_adapter(sc->adapter); /* assert((sc->adapter >= 0) && (sc->adp != NULL)) */ sc->keyboard = sc_allocate_keyboard(sc, unit); DPRINTF(1, ("sc%d: keyboard %d\n", unit, sc->keyboard)); sc->kbd = kbd_get_keyboard(sc->keyboard); if (sc->kbd != NULL) { DPRINTF(1, ("sc%d: kbd index:%d, unit:%d, flags:0x%x\n", unit, sc->kbd->kb_index, sc->kbd->kb_unit, sc->kbd->kb_flags)); } if (!(sc->flags & SC_INIT_DONE) || (adp != sc->adp)) { sc->initial_mode = sc->adp->va_initial_mode; #ifndef SC_NO_FONT_LOADING if (flags & SC_KERNEL_CONSOLE) { sc->font_8 = font_8; sc->font_14 = font_14; sc->font_16 = font_16; } else if (sc->font_8 == NULL) { /* assert(sc_malloc) */ sc->font_8 = malloc(sizeof(font_8), M_DEVBUF, M_WAITOK); sc->font_14 = malloc(sizeof(font_14), M_DEVBUF, M_WAITOK); sc->font_16 = malloc(sizeof(font_16), M_DEVBUF, M_WAITOK); } #endif /* extract the hardware cursor location and hide the cursor for now */ vidd_read_hw_cursor(sc->adp, &col, &row); vidd_set_hw_cursor(sc->adp, -1, -1); /* set up the first console */ sc->first_vty = unit*MAXCONS; sc->vtys = MAXCONS; /* XXX: should be configurable */ if (flags & SC_KERNEL_CONSOLE) { /* * Set up devs structure but don't use it yet, calling make_dev() * might panic kernel. Wait for sc_attach_unit() to actually * create the devices. */ sc->dev = main_devs; scp = &main_console; init_scp(sc, sc->first_vty, scp); sc_vtb_init(&scp->vtb, VTB_MEMORY, scp->xsize, scp->ysize, (void *)sc_buffer, FALSE); /* move cursors to the initial positions */ if (col >= scp->xsize) col = 0; if (row >= scp->ysize) row = scp->ysize - 1; scp->xpos = col; scp->ypos = row; scp->cursor_pos = scp->cursor_oldpos = row*scp->xsize + col; if (sc_init_emulator(scp, SC_DFLT_TERM)) sc_init_emulator(scp, "*"); (*scp->tsw->te_default_attr)(scp, user_default.std_color, user_default.rev_color); } else { /* assert(sc_malloc) */ sc->dev = malloc(sizeof(struct tty *)*sc->vtys, M_DEVBUF, M_WAITOK|M_ZERO); sc->dev[0] = sc_alloc_tty(0, unit * MAXCONS); scp = alloc_scp(sc, sc->first_vty); SC_STAT(sc->dev[0]) = scp; } sc->cur_scp = scp; #ifndef __sparc64__ /* copy screen to temporary buffer */ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)scp->sc->adp->va_window, FALSE); if (ISTEXTSC(scp)) sc_vtb_copy(&scp->scr, 0, &scp->vtb, 0, scp->xsize*scp->ysize); #endif if (bios_value.cursor_end < scp->font_size) sc->dflt_curs_attr.base = scp->font_size - bios_value.cursor_end - 1; else sc->dflt_curs_attr.base = 0; i = bios_value.cursor_end - bios_value.cursor_start + 1; sc->dflt_curs_attr.height = imin(i, scp->font_size); sc->dflt_curs_attr.flags = 0; sc->curs_attr = sc->dflt_curs_attr; scp->curr_curs_attr = scp->dflt_curs_attr = sc->curs_attr; #ifndef SC_NO_SYSMOUSE sc_mouse_move(scp, scp->xpixel/2, scp->ypixel/2); #endif if (!ISGRAPHSC(scp)) { sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } /* save font and palette */ #ifndef SC_NO_FONT_LOADING sc->fonts_loaded = 0; if (ISFONTAVAIL(sc->adp->va_flags)) { #ifdef SC_DFLT_FONT bcopy(dflt_font_8, sc->font_8, sizeof(dflt_font_8)); bcopy(dflt_font_14, sc->font_14, sizeof(dflt_font_14)); bcopy(dflt_font_16, sc->font_16, sizeof(dflt_font_16)); sc->fonts_loaded = FONT_16 | FONT_14 | FONT_8; if (scp->font_size < 14) { sc_load_font(scp, 0, 8, 8, sc->font_8, 0, 256); } else if (scp->font_size >= 16) { sc_load_font(scp, 0, 16, 8, sc->font_16, 0, 256); } else { sc_load_font(scp, 0, 14, 8, sc->font_14, 0, 256); } #else /* !SC_DFLT_FONT */ if (scp->font_size < 14) { sc_save_font(scp, 0, 8, 8, sc->font_8, 0, 256); sc->fonts_loaded = FONT_8; } else if (scp->font_size >= 16) { sc_save_font(scp, 0, 16, 8, sc->font_16, 0, 256); sc->fonts_loaded = FONT_16; } else { sc_save_font(scp, 0, 14, 8, sc->font_14, 0, 256); sc->fonts_loaded = FONT_14; } #endif /* SC_DFLT_FONT */ /* FONT KLUDGE: always use the font page #0. XXX */ sc_show_font(scp, 0); } #endif /* !SC_NO_FONT_LOADING */ #ifndef SC_NO_PALETTE_LOADING vidd_save_palette(sc->adp, sc->palette); +#ifdef SC_PIXEL_MODE + for (i = 0; i < sizeof(sc->palette2); i++) + sc->palette2[i] = i / 3; +#endif #endif #ifdef DEV_SPLASH if (!(sc->flags & SC_SPLASH_SCRN)) { /* we are ready to put up the splash image! */ splash_init(sc->adp, scsplash_callback, sc); sc->flags |= SC_SPLASH_SCRN; } #endif } /* the rest is not necessary, if we have done it once */ if (sc->flags & SC_INIT_DONE) return; /* initialize mapscrn arrays to a one to one map */ for (i = 0; i < sizeof(sc->scr_map); i++) sc->scr_map[i] = sc->scr_rmap[i] = i; #ifdef PC98 sc->scr_map[0x5c] = (u_char)0xfc; /* for backslash */ #endif sc->flags |= SC_INIT_DONE; } static void scterm(int unit, int flags) { sc_softc_t *sc; scr_stat *scp; sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); if (sc == NULL) return; /* shouldn't happen */ #ifdef DEV_SPLASH /* this console is no longer available for the splash screen */ if (sc->flags & SC_SPLASH_SCRN) { splash_term(sc->adp); sc->flags &= ~SC_SPLASH_SCRN; } #endif #if 0 /* XXX */ /* move the hardware cursor to the upper-left corner */ vidd_set_hw_cursor(sc->adp, 0, 0); #endif /* release the keyboard and the video card */ if (sc->keyboard >= 0) kbd_release(sc->kbd, &sc->keyboard); if (sc->adapter >= 0) vid_release(sc->adp, &sc->adapter); /* stop the terminal emulator, if any */ scp = sc_get_stat(sc->dev[0]); if (scp->tsw) (*scp->tsw->te_term)(scp, &scp->ts); if (scp->ts != NULL) free(scp->ts, M_DEVBUF); mtx_destroy(&scp->scr_lock); /* clear the structure */ if (!(flags & SC_KERNEL_CONSOLE)) { /* XXX: We need delete_dev() for this */ free(sc->dev, M_DEVBUF); #if 0 /* XXX: We need a ttyunregister for this */ free(sc->tty, M_DEVBUF); #endif #ifndef SC_NO_FONT_LOADING free(sc->font_8, M_DEVBUF); free(sc->font_14, M_DEVBUF); free(sc->font_16, M_DEVBUF); #endif /* XXX vtb, history */ } bzero(sc, sizeof(*sc)); sc->keyboard = -1; sc->adapter = -1; } static void scshutdown(void *arg, int howto) { /* assert(sc_console != NULL) */ sc_touch_scrn_saver(); if (!cold && sc_console && sc_console->sc->cur_scp->smode.mode == VT_AUTO && sc_console->smode.mode == VT_AUTO) sc_switch_scr(sc_console->sc, sc_console->index); shutdown_in_progress = TRUE; } int sc_clean_up(scr_stat *scp) { #ifdef DEV_SPLASH int error; #endif if (scp->sc->flags & SC_SCRN_BLANKED) { sc_touch_scrn_saver(); #ifdef DEV_SPLASH if ((error = wait_scrn_saver_stop(scp->sc))) return error; #endif } scp->status |= MOUSE_HIDDEN; sc_remove_mouse_image(scp); sc_remove_cutmarking(scp); return 0; } void sc_alloc_scr_buffer(scr_stat *scp, int wait, int discard) { sc_vtb_t new; sc_vtb_t old; old = scp->vtb; sc_vtb_init(&new, VTB_MEMORY, scp->xsize, scp->ysize, NULL, wait); if (!discard && (old.vtb_flags & VTB_VALID)) { /* retain the current cursor position and buffer contants */ scp->cursor_oldpos = scp->cursor_pos; /* * This works only if the old buffer has the same size as or larger * than the new one. XXX */ sc_vtb_copy(&old, 0, &new, 0, scp->xsize*scp->ysize); scp->vtb = new; } else { scp->vtb = new; sc_vtb_destroy(&old); } #ifndef SC_NO_SYSMOUSE /* move the mouse cursor at the center of the screen */ sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); #endif } static scr_stat *alloc_scp(sc_softc_t *sc, int vty) { scr_stat *scp; /* assert(sc_malloc) */ scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK); init_scp(sc, vty, scp); sc_alloc_scr_buffer(scp, TRUE, TRUE); if (sc_init_emulator(scp, SC_DFLT_TERM)) sc_init_emulator(scp, "*"); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, TRUE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, 0, TRUE); #endif return scp; } static void init_scp(sc_softc_t *sc, int vty, scr_stat *scp) { video_info_t info; bzero(scp, sizeof(*scp)); scp->index = vty; scp->sc = sc; scp->status = 0; scp->mode = sc->initial_mode; vidd_get_info(sc->adp, scp->mode, &info); if (info.vi_flags & V_INFO_GRAPHICS) { scp->status |= GRAPHICS_MODE; scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->xsize = info.vi_width/info.vi_cwidth; scp->ysize = info.vi_height/info.vi_cheight; scp->font_size = 0; scp->font = NULL; } else { scp->xsize = info.vi_width; scp->ysize = info.vi_height; scp->xpixel = scp->xsize*info.vi_cwidth; scp->ypixel = scp->ysize*info.vi_cheight; } scp->font_size = info.vi_cheight; scp->font_width = info.vi_cwidth; if (info.vi_cheight < 14) { #ifndef SC_NO_FONT_LOADING scp->font = sc->font_8; #else scp->font = NULL; #endif } else if (info.vi_cheight >= 16) { #ifndef SC_NO_FONT_LOADING scp->font = sc->font_16; #else scp->font = NULL; #endif } else { #ifndef SC_NO_FONT_LOADING scp->font = sc->font_14; #else scp->font = NULL; #endif } sc_vtb_init(&scp->vtb, VTB_MEMORY, 0, 0, NULL, FALSE); #ifndef __sparc64__ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, 0, 0, NULL, FALSE); #endif scp->xoff = scp->yoff = 0; scp->xpos = scp->ypos = 0; scp->start = scp->xsize * scp->ysize - 1; scp->end = 0; scp->tsw = NULL; scp->ts = NULL; scp->rndr = NULL; scp->border = (SC_NORM_ATTR >> 4) & 0x0f; scp->curr_curs_attr = scp->dflt_curs_attr = sc->curs_attr; scp->mouse_cut_start = scp->xsize*scp->ysize; scp->mouse_cut_end = -1; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->kbd_mode = K_XLATE; scp->bell_pitch = bios_value.bell_pitch; scp->bell_duration = BELL_DURATION; scp->status |= (bios_value.shift_state & NLKED); scp->status |= CURSOR_ENABLED | MOUSE_HIDDEN; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history = NULL; scp->history_pos = 0; scp->history_size = 0; mtx_init(&scp->scr_lock, "scrlock", NULL, MTX_SPIN); } int sc_init_emulator(scr_stat *scp, char *name) { sc_term_sw_t *sw; sc_rndr_sw_t *rndr; void *p; int error; if (name == NULL) /* if no name is given, use the current emulator */ sw = scp->tsw; else /* ...otherwise find the named emulator */ sw = sc_term_match(name); if (sw == NULL) return EINVAL; rndr = NULL; if (strcmp(sw->te_renderer, "*") != 0) { rndr = sc_render_match(scp, sw->te_renderer, scp->status & (GRAPHICS_MODE | PIXEL_MODE)); } if (rndr == NULL) { rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->status & (GRAPHICS_MODE | PIXEL_MODE)); if (rndr == NULL) return ENODEV; } if (sw == scp->tsw) { error = (*sw->te_init)(scp, &scp->ts, SC_TE_WARM_INIT); scp->rndr = rndr; scp->rndr->init(scp); sc_clear_screen(scp); /* assert(error == 0); */ return error; } if (sc_malloc && (sw->te_size > 0)) p = malloc(sw->te_size, M_DEVBUF, M_NOWAIT); else p = NULL; error = (*sw->te_init)(scp, &p, SC_TE_COLD_INIT); if (error) return error; if (scp->tsw) (*scp->tsw->te_term)(scp, &scp->ts); if (scp->ts != NULL) free(scp->ts, M_DEVBUF); scp->tsw = sw; scp->ts = p; scp->rndr = rndr; scp->rndr->init(scp); /* XXX */ (*sw->te_default_attr)(scp, user_default.std_color, user_default.rev_color); sc_clear_screen(scp); return 0; } /* * scgetc(flags) - get character from keyboard. * If flags & SCGETC_CN, then avoid harmful side effects. * If flags & SCGETC_NONBLOCK, then wait until a key is pressed, else * return NOKEY if there is nothing there. */ static u_int scgetc(sc_softc_t *sc, u_int flags) { scr_stat *scp; #ifndef SC_NO_HISTORY struct tty *tp; #endif u_int c; int this_scr; int f; int i; if (sc->kbd == NULL) return NOKEY; next_code: #if 1 /* I don't like this, but... XXX */ if (flags & SCGETC_CN) sccnupdate(sc->cur_scp); #endif scp = sc->cur_scp; /* first see if there is something in the keyboard port */ for (;;) { c = kbdd_read_char(sc->kbd, !(flags & SCGETC_NONBLOCK)); if (c == ERRKEY) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); } else if (c == NOKEY) return c; else break; } /* make screensaver happy */ if (!(c & RELKEY)) sc_touch_scrn_saver(); if (!(flags & SCGETC_CN)) random_harvest(&c, sizeof(c), 1, 0, RANDOM_KEYBOARD); if (scp->kbd_mode != K_XLATE) return KEYCHAR(c); /* if scroll-lock pressed allow history browsing */ if (!ISGRAPHSC(scp) && scp->history && scp->status & SLKED) { scp->status &= ~CURSOR_ENABLED; sc_remove_cursor_image(scp); #ifndef SC_NO_HISTORY if (!(scp->status & BUFFER_SAVED)) { scp->status |= BUFFER_SAVED; sc_hist_save(scp); } switch (c) { /* FIXME: key codes */ case SPCLKEY | FKEY | F(49): /* home key */ sc_remove_cutmarking(scp); sc_hist_home(scp); goto next_code; case SPCLKEY | FKEY | F(57): /* end key */ sc_remove_cutmarking(scp); sc_hist_end(scp); goto next_code; case SPCLKEY | FKEY | F(50): /* up arrow key */ sc_remove_cutmarking(scp); if (sc_hist_up_line(scp)) if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(58): /* down arrow key */ sc_remove_cutmarking(scp); if (sc_hist_down_line(scp)) if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(51): /* page up key */ sc_remove_cutmarking(scp); for (i=0; iysize; i++) if (sc_hist_up_line(scp)) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); break; } goto next_code; case SPCLKEY | FKEY | F(59): /* page down key */ sc_remove_cutmarking(scp); for (i=0; iysize; i++) if (sc_hist_down_line(scp)) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); break; } goto next_code; } #endif /* SC_NO_HISTORY */ } /* * Process and consume special keys here. Return a plain char code * or a char code with the META flag or a function key code. */ if (c & RELKEY) { /* key released */ /* goto next_code */ } else { /* key pressed */ if (c & SPCLKEY) { c &= ~SPCLKEY; switch (KEYCHAR(c)) { /* LOCKING KEYS */ case NLK: case CLK: case ALK: break; case SLK: kbdd_ioctl(sc->kbd, KDGKBSTATE, (caddr_t)&f); if (f & SLKED) { scp->status |= SLKED; } else { if (scp->status & SLKED) { scp->status &= ~SLKED; #ifndef SC_NO_HISTORY if (scp->status & BUFFER_SAVED) { if (!sc_hist_restore(scp)) sc_remove_cutmarking(scp); scp->status &= ~BUFFER_SAVED; scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } tp = SC_DEV(sc, scp->index); if (!kdb_active && tty_opened(tp)) sctty_outwakeup(tp); #endif } } break; case PASTE: #ifndef SC_NO_CUTPASTE sc_mouse_paste(scp); #endif break; /* NON-LOCKING KEYS */ case NOP: case LSH: case RSH: case LCTR: case RCTR: case LALT: case RALT: case ASH: case META: break; case BTAB: if (!(sc->flags & SC_SCRN_BLANKED)) return c; break; case SPSC: #ifdef DEV_SPLASH /* force activatation/deactivation of the screen saver */ if (!(sc->flags & SC_SCRN_BLANKED)) { run_scrn_saver = TRUE; sc->scrn_time_stamp -= scrn_blank_time; } if (cold) { /* * While devices are being probed, the screen saver need * to be invoked explictly. XXX */ if (sc->flags & SC_SCRN_BLANKED) { scsplash_stick(FALSE); stop_scrn_saver(sc, current_saver); } else { if (!ISGRAPHSC(scp)) { scsplash_stick(TRUE); (*current_saver)(sc, TRUE); } } } #endif /* DEV_SPLASH */ break; case RBT: #ifndef SC_DISABLE_REBOOT if (enable_reboot) shutdown_nice(0); #endif break; case HALT: #ifndef SC_DISABLE_REBOOT if (enable_reboot) shutdown_nice(RB_HALT); #endif break; case PDWN: #ifndef SC_DISABLE_REBOOT if (enable_reboot) shutdown_nice(RB_HALT|RB_POWEROFF); #endif break; case SUSP: power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); break; case STBY: power_pm_suspend(POWER_SLEEP_STATE_STANDBY); break; case DBG: #ifndef SC_DISABLE_KDBKEY if (enable_kdbkey) kdb_enter(KDB_WHY_BREAK, "manual escape to debugger"); #endif break; case PNC: if (enable_panic_key) panic("Forced by the panic key"); break; case NEXT: this_scr = scp->index; for (i = (this_scr - sc->first_vty + 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + 1)%sc->vtys) { struct tty *tp = SC_DEV(sc, sc->first_vty + i); if (tty_opened(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } } break; case PREV: this_scr = scp->index; for (i = (this_scr - sc->first_vty + sc->vtys - 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + sc->vtys - 1)%sc->vtys) { struct tty *tp = SC_DEV(sc, sc->first_vty + i); if (tty_opened(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } } break; default: if (KEYCHAR(c) >= F_SCR && KEYCHAR(c) <= L_SCR) { sc_switch_scr(scp->sc, sc->first_vty + KEYCHAR(c) - F_SCR); break; } /* assert(c & FKEY) */ if (!(sc->flags & SC_SCRN_BLANKED)) return c; break; } /* goto next_code */ } else { /* regular keys (maybe MKEY is set) */ if (!(sc->flags & SC_SCRN_BLANKED)) return c; } } goto next_code; } static int sctty_mmap(struct tty *tp, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { scr_stat *scp; scp = sc_get_stat(tp); if (scp != scp->sc->cur_scp) return -1; return vidd_mmap(scp->sc->adp, offset, paddr, nprot, memattr); } static int save_kbd_state(scr_stat *scp) { int state; int error; error = kbdd_ioctl(scp->sc->kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error == 0) { scp->status &= ~LOCK_MASK; scp->status |= state; } return error; } static int update_kbd_state(scr_stat *scp, int new_bits, int mask) { int state; int error; if (mask != LOCK_MASK) { error = kbdd_ioctl(scp->sc->kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error) return error; state &= ~mask; state |= new_bits & mask; } else { state = new_bits & LOCK_MASK; } error = kbdd_ioctl(scp->sc->kbd, KDSKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; return error; } static int update_kbd_leds(scr_stat *scp, int which) { int error; which &= LOCK_MASK; error = kbdd_ioctl(scp->sc->kbd, KDSETLED, (caddr_t)&which); if (error == ENOIOCTL) error = ENODEV; return error; } int set_mode(scr_stat *scp) { video_info_t info; /* reject unsupported mode */ if (vidd_get_info(scp->sc->adp, scp->mode, &info)) return 1; /* if this vty is not currently showing, do nothing */ if (scp != scp->sc->cur_scp) return 0; /* setup video hardware for the given mode */ vidd_set_mode(scp->sc->adp, scp->mode); scp->rndr->init(scp); #ifndef __sparc64__ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)scp->sc->adp->va_window, FALSE); #endif #ifndef SC_NO_FONT_LOADING /* load appropriate font */ if (!(scp->status & GRAPHICS_MODE)) { if (!(scp->status & PIXEL_MODE) && ISFONTAVAIL(scp->sc->adp->va_flags)) { if (scp->font_size < 14) { if (scp->sc->fonts_loaded & FONT_8) sc_load_font(scp, 0, 8, 8, scp->sc->font_8, 0, 256); } else if (scp->font_size >= 16) { if (scp->sc->fonts_loaded & FONT_16) sc_load_font(scp, 0, 16, 8, scp->sc->font_16, 0, 256); } else { if (scp->sc->fonts_loaded & FONT_14) sc_load_font(scp, 0, 14, 8, scp->sc->font_14, 0, 256); } /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ sc_show_font(scp, 0); } mark_all(scp); } #endif /* !SC_NO_FONT_LOADING */ sc_set_border(scp, scp->border); sc_set_cursor_image(scp); return 0; } void sc_set_border(scr_stat *scp, int color) { SC_VIDEO_LOCK(scp->sc); (*scp->rndr->draw_border)(scp, color); SC_VIDEO_UNLOCK(scp->sc); } #ifndef SC_NO_FONT_LOADING void sc_load_font(scr_stat *scp, int page, int size, int width, u_char *buf, int base, int count) { sc_softc_t *sc; sc = scp->sc; sc->font_loading_in_progress = TRUE; vidd_load_font(sc->adp, page, size, width, buf, base, count); sc->font_loading_in_progress = FALSE; } void sc_save_font(scr_stat *scp, int page, int size, int width, u_char *buf, int base, int count) { sc_softc_t *sc; sc = scp->sc; sc->font_loading_in_progress = TRUE; vidd_save_font(sc->adp, page, size, width, buf, base, count); sc->font_loading_in_progress = FALSE; } void sc_show_font(scr_stat *scp, int page) { vidd_show_font(scp->sc->adp, page); } #endif /* !SC_NO_FONT_LOADING */ void sc_paste(scr_stat *scp, const u_char *p, int count) { struct tty *tp; u_char *rmap; tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); if (!tty_opened(tp)) return; rmap = scp->sc->scr_rmap; for (; count > 0; --count) ttydisc_rint(tp, rmap[*p++], 0); ttydisc_rint_done(tp); } void sc_respond(scr_stat *scp, const u_char *p, int count, int wakeup) { struct tty *tp; tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); if (!tty_opened(tp)) return; ttydisc_rint_simple(tp, p, count); if (wakeup) { /* XXX: we can't always call ttydisc_rint_done() here! */ ttydisc_rint_done(tp); } } void sc_bell(scr_stat *scp, int pitch, int duration) { if (cold || shutdown_in_progress || !enable_bell) return; if (scp != scp->sc->cur_scp && (scp->sc->flags & SC_QUIET_BELL)) return; if (scp->sc->flags & SC_VISUAL_BELL) { if (scp->sc->blink_in_progress) return; scp->sc->blink_in_progress = 3; if (scp != scp->sc->cur_scp) scp->sc->blink_in_progress += 2; blink_screen(scp->sc->cur_scp); } else if (duration != 0 && pitch != 0) { if (scp != scp->sc->cur_scp) pitch *= 2; sysbeep(1193182 / pitch, duration); } } static void blink_screen(void *arg) { scr_stat *scp = arg; struct tty *tp; if (ISGRAPHSC(scp) || (scp->sc->blink_in_progress <= 1)) { scp->sc->blink_in_progress = 0; mark_all(scp); tp = SC_DEV(scp->sc, scp->index); if (tty_opened(tp)) sctty_outwakeup(tp); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } else { (*scp->rndr->draw)(scp, 0, scp->xsize*scp->ysize, scp->sc->blink_in_progress & 1); scp->sc->blink_in_progress--; timeout(blink_screen, scp, hz / 10); } } /* * Until sc_attach_unit() gets called no dev structures will be available * to store the per-screen current status. This is the case when the * kernel is initially booting and needs access to its console. During * this early phase of booting the console's current status is kept in * one statically defined scr_stat structure, and any pointers to the * dev structures will be NULL. */ static scr_stat * sc_get_stat(struct tty *tp) { if (tp == NULL) return (&main_console); return (SC_STAT(tp)); } /* * Allocate active keyboard. Try to allocate "kbdmux" keyboard first, and, * if found, add all non-busy keyboards to "kbdmux". Otherwise look for * any keyboard. */ static int sc_allocate_keyboard(sc_softc_t *sc, int unit) { int idx0, idx; keyboard_t *k0, *k; keyboard_info_t ki; idx0 = kbd_allocate("kbdmux", -1, (void *)&sc->keyboard, sckbdevent, sc); if (idx0 != -1) { k0 = kbd_get_keyboard(idx0); for (idx = kbd_find_keyboard2("*", -1, 0); idx != -1; idx = kbd_find_keyboard2("*", -1, idx + 1)) { k = kbd_get_keyboard(idx); if (idx == idx0 || KBD_IS_BUSY(k)) continue; bzero(&ki, sizeof(ki)); strcpy(ki.kb_name, k->kb_name); ki.kb_unit = k->kb_unit; kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki); } } else idx0 = kbd_allocate("*", unit, (void *)&sc->keyboard, sckbdevent, sc); return (idx0); } Index: projects/ppc64/sys/dev/syscons/syscons.h =================================================================== --- projects/ppc64/sys/dev/syscons/syscons.h (revision 204271) +++ projects/ppc64/sys/dev/syscons/syscons.h (revision 204272) @@ -1,677 +1,680 @@ /*- * Copyright (c) 1995-1998 Søren Schmidt * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sascha Wildner * * 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, immediately at the beginning of the file. * 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$ */ #ifndef _DEV_SYSCONS_SYSCONS_H_ #define _DEV_SYSCONS_SYSCONS_H_ #include #include /* machine-dependent part of the header */ #ifdef PC98 #include #elif defined(__i386__) /* nothing for the moment */ #endif /* default values for configuration options */ #ifndef MAXCONS #define MAXCONS 16 #endif #ifdef SC_NO_SYSMOUSE #undef SC_NO_CUTPASTE #define SC_NO_CUTPASTE 1 #endif #ifdef SC_NO_MODE_CHANGE #undef SC_PIXEL_MODE #endif /* Always load font data if the pixel (raster text) mode is to be used. */ #ifdef SC_PIXEL_MODE #undef SC_NO_FONT_LOADING #endif /* * If font data is not available, the `arrow'-shaped mouse cursor cannot * be drawn. Use the alternative drawing method. */ #ifdef SC_NO_FONT_LOADING #undef SC_ALT_MOUSE_IMAGE #define SC_ALT_MOUSE_IMAGE 1 #endif #ifndef SC_CURSOR_CHAR #define SC_CURSOR_CHAR (0x07) #endif #ifndef SC_MOUSE_CHAR #define SC_MOUSE_CHAR (0xd0) #endif #if SC_MOUSE_CHAR <= SC_CURSOR_CHAR && SC_CURSOR_CHAR < (SC_MOUSE_CHAR + 4) #undef SC_CURSOR_CHAR #define SC_CURSOR_CHAR (SC_MOUSE_CHAR + 4) #endif #ifndef SC_DEBUG_LEVEL #define SC_DEBUG_LEVEL 0 #endif #define DPRINTF(l, p) if (SC_DEBUG_LEVEL >= (l)) printf p #ifndef __sparc64__ #define SC_DRIVER_NAME "sc" #else /* * Use a different driver name on sparc64 so it does not get confused * with the system controller devices which are also termed 'sc' in OFW. */ #define SC_DRIVER_NAME "syscons" #endif #define SC_VTY(dev) (((sc_ttysoftc *)tty_softc(tp))->st_index) #define SC_DEV(sc, vty) ((sc)->dev[(vty) - (sc)->first_vty]) #define SC_STAT(tp) (*((scr_stat **)&((sc_ttysoftc *)tty_softc(tp))->st_stat)) /* printable chars */ #ifndef PRINTABLE #define PRINTABLE(ch) ((ch) > 0x1b || ((ch) > 0x0d && (ch) < 0x1b) \ || (ch) < 0x07) #endif /* macros for "intelligent" screen update */ #define mark_for_update(scp, x) {\ if ((x) < scp->start) scp->start = (x);\ else if ((x) > scp->end) scp->end = (x);\ } #define mark_all(scp) {\ scp->start = 0;\ scp->end = scp->xsize * scp->ysize - 1;\ } /* vty status flags (scp->status) */ #define UNKNOWN_MODE 0x00010 /* unknown video mode */ #define SWITCH_WAIT_REL 0x00080 /* waiting for vty release */ #define SWITCH_WAIT_ACQ 0x00100 /* waiting for vty ack */ #define BUFFER_SAVED 0x00200 /* vty buffer is saved */ #define CURSOR_ENABLED 0x00400 /* text cursor is enabled */ #define MOUSE_MOVED 0x01000 /* mouse cursor has moved */ #define MOUSE_CUTTING 0x02000 /* mouse cursor is cutting text */ #define MOUSE_VISIBLE 0x04000 /* mouse cursor is showing */ #define GRAPHICS_MODE 0x08000 /* vty is in a graphics mode */ #define PIXEL_MODE 0x10000 /* vty is in a raster text mode */ #define SAVER_RUNNING 0x20000 /* screen saver is running */ #define VR_CURSOR_BLINK 0x40000 /* blinking text cursor */ #define VR_CURSOR_ON 0x80000 /* text cursor is on */ #define MOUSE_HIDDEN 0x100000 /* mouse cursor is temporarily hidden */ /* misc defines */ #define FALSE 0 #define TRUE 1 /* The following #defines are hard-coded for a maximum text resolution corresponding to a maximum framebuffer resolution of 1600x1200 with an 8x8 font... */ #define COL 200 #define ROW 150 #define PCBURST 128 #ifndef BELL_DURATION #define BELL_DURATION ((5 * hz + 99) / 100) #define BELL_PITCH 800 #endif /* virtual terminal buffer */ typedef struct sc_vtb { int vtb_flags; #define VTB_VALID (1 << 0) #define VTB_ALLOCED (1 << 1) int vtb_type; #define VTB_INVALID 0 #define VTB_MEMORY 1 #define VTB_FRAMEBUFFER 2 #define VTB_RINGBUFFER 3 int vtb_cols; int vtb_rows; int vtb_size; vm_offset_t vtb_buffer; int vtb_tail; /* valid for VTB_RINGBUFFER only */ } sc_vtb_t; /* text cursor attributes */ struct cursor_attr { int flags; int base; int height; }; /* softc */ struct keyboard; struct video_adapter; struct scr_stat; struct tty; typedef struct sc_softc { int unit; /* unit # */ int config; /* configuration flags */ #define SC_VESAMODE (1 << 7) #define SC_AUTODETECT_KBD (1 << 8) #define SC_KERNEL_CONSOLE (1 << 9) int flags; /* status flags */ #define SC_VISUAL_BELL (1 << 0) #define SC_QUIET_BELL (1 << 1) #if 0 /* not used anymore */ #define SC_BLINK_CURSOR (1 << 2) #define SC_CHAR_CURSOR (1 << 3) #endif #define SC_MOUSE_ENABLED (1 << 4) #define SC_SCRN_IDLE (1 << 5) #define SC_SCRN_BLANKED (1 << 6) #define SC_SAVER_FAILED (1 << 7) #define SC_SCRN_VTYLOCK (1 << 8) #define SC_INIT_DONE (1 << 16) #define SC_SPLASH_SCRN (1 << 17) int keyboard; /* -1 if unavailable */ struct keyboard *kbd; int adapter; struct video_adapter *adp; int initial_mode; /* initial video mode */ int first_vty; int vtys; struct tty **dev; struct scr_stat *cur_scp; struct scr_stat *new_scp; struct scr_stat *old_scp; int delayed_next_scr; char font_loading_in_progress; char switch_in_progress; char write_in_progress; char blink_in_progress; struct mtx video_mtx; long scrn_time_stamp; struct cursor_attr dflt_curs_attr; struct cursor_attr curs_attr; u_char scr_map[256]; u_char scr_rmap[256]; #ifdef _SC_MD_SOFTC_DECLARED_ sc_md_softc_t md; /* machine dependent vars */ #endif #ifndef SC_NO_PALETTE_LOADING - u_char palette[256*3]; + u_char palette[256 * 3]; +#ifdef SC_PIXEL_MODE + u_char palette2[256 * 3]; +#endif #endif #ifndef SC_NO_FONT_LOADING int fonts_loaded; #define FONT_8 2 #define FONT_14 4 #define FONT_16 8 #define FONT_22 8 u_char *font_8; u_char *font_14; u_char *font_16; u_char *font_22; #endif u_char cursor_char; u_char mouse_char; } sc_softc_t; /* virtual screen */ typedef struct scr_stat { int index; /* index of this vty */ struct sc_softc *sc; /* pointer to softc */ struct sc_rndr_sw *rndr; /* renderer */ #ifndef __sparc64__ sc_vtb_t scr; #endif sc_vtb_t vtb; int xpos; /* current X position */ int ypos; /* current Y position */ int xsize; /* X text size */ int ysize; /* Y text size */ int xpixel; /* X graphics size */ int ypixel; /* Y graphics size */ int xoff; /* X offset in pixel mode */ int yoff; /* Y offset in pixel mode */ u_char *font; /* current font */ int font_size; /* fontsize in Y direction */ int font_width; /* fontsize in X direction */ int start; /* modified area start */ int end; /* modified area end */ struct sc_term_sw *tsw; void *ts; int status; /* status (bitfield) */ int kbd_mode; /* keyboard I/O mode */ int cursor_pos; /* cursor buffer position */ int cursor_oldpos; /* cursor old buffer position */ u_short cursor_saveunder_char; /* saved char under cursor */ u_short cursor_saveunder_attr; /* saved attr under cursor */ struct cursor_attr dflt_curs_attr; struct cursor_attr curr_curs_attr; struct cursor_attr curs_attr; int mouse_pos; /* mouse buffer position */ int mouse_oldpos; /* mouse old buffer position */ short mouse_xpos; /* mouse x coordinate */ short mouse_ypos; /* mouse y coordinate */ short mouse_oldxpos; /* mouse previous x coordinate */ short mouse_oldypos; /* mouse previous y coordinate */ short mouse_buttons; /* mouse buttons */ int mouse_cut_start; /* mouse cut start pos */ int mouse_cut_end; /* mouse cut end pos */ int mouse_level; /* xterm mouse protocol */ struct proc *mouse_proc; /* proc* of controlling proc */ pid_t mouse_pid; /* pid of controlling proc */ int mouse_signal; /* signal # to report with */ u_short bell_duration; u_short bell_pitch; u_char border; /* border color */ int mode; /* mode */ pid_t pid; /* pid of controlling proc */ struct proc *proc; /* proc* of controlling proc */ struct vt_mode smode; /* switch mode */ sc_vtb_t *history; /* circular history buffer */ int history_pos; /* position shown on screen */ int history_size; /* size of history buffer */ int splash_save_mode; /* saved mode for splash screen */ int splash_save_status; /* saved status for splash screen */ struct mtx scr_lock; /* mutex for sc_puts() */ #ifdef _SCR_MD_STAT_DECLARED_ scr_md_stat_t md; /* machine dependent vars */ #endif } scr_stat; /* TTY softc. */ typedef struct sc_ttysoftc { int st_index; scr_stat *st_stat; } sc_ttysoftc; #ifndef SC_NORM_ATTR #define SC_NORM_ATTR (FG_LIGHTGREY | BG_BLACK) #endif #ifndef SC_NORM_REV_ATTR #define SC_NORM_REV_ATTR (FG_BLACK | BG_LIGHTGREY) #endif #ifndef SC_KERNEL_CONS_ATTR #define SC_KERNEL_CONS_ATTR (FG_WHITE | BG_BLACK) #endif #ifndef SC_KERNEL_CONS_REV_ATTR #define SC_KERNEL_CONS_REV_ATTR (FG_BLACK | BG_LIGHTGREY) #endif /* terminal emulator */ #ifndef SC_DFLT_TERM #define SC_DFLT_TERM "*" /* any */ #endif typedef int sc_term_init_t(scr_stat *scp, void **tcp, int code); #define SC_TE_COLD_INIT 0 #define SC_TE_WARM_INIT 1 typedef int sc_term_term_t(scr_stat *scp, void **tcp); typedef void sc_term_puts_t(scr_stat *scp, u_char *buf, int len, int kernel); typedef int sc_term_ioctl_t(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, struct thread *td); typedef int sc_term_reset_t(scr_stat *scp, int code); #define SC_TE_HARD_RESET 0 #define SC_TE_SOFT_RESET 1 typedef void sc_term_default_attr_t(scr_stat *scp, int norm, int rev); typedef void sc_term_clear_t(scr_stat *scp); typedef void sc_term_notify_t(scr_stat *scp, int event); #define SC_TE_NOTIFY_VTSWITCH_IN 0 #define SC_TE_NOTIFY_VTSWITCH_OUT 1 typedef int sc_term_input_t(scr_stat *scp, int c, struct tty *tp); typedef const char *sc_term_fkeystr_t(scr_stat *scp, int c); typedef struct sc_term_sw { LIST_ENTRY(sc_term_sw) link; char *te_name; /* name of the emulator */ char *te_desc; /* description */ char *te_renderer; /* matching renderer */ size_t te_size; /* size of internal buffer */ int te_refcount; /* reference counter */ sc_term_init_t *te_init; sc_term_term_t *te_term; sc_term_puts_t *te_puts; sc_term_ioctl_t *te_ioctl; sc_term_reset_t *te_reset; sc_term_default_attr_t *te_default_attr; sc_term_clear_t *te_clear; sc_term_notify_t *te_notify; sc_term_input_t *te_input; sc_term_fkeystr_t *te_fkeystr; } sc_term_sw_t; #define SCTERM_MODULE(name, sw) \ DATA_SET(scterm_set, sw); \ static int \ scterm_##name##_event(module_t mod, int type, void *data) \ { \ switch (type) { \ case MOD_LOAD: \ return sc_term_add(&sw); \ case MOD_UNLOAD: \ if (sw.te_refcount > 0) \ return EBUSY; \ return sc_term_remove(&sw); \ default: \ return EOPNOTSUPP; \ break; \ } \ return 0; \ } \ static moduledata_t scterm_##name##_mod = { \ "scterm-" #name, \ scterm_##name##_event, \ NULL, \ }; \ DECLARE_MODULE(scterm_##name, scterm_##name##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) /* renderer function table */ typedef void vr_init_t(scr_stat *scp); typedef void vr_clear_t(scr_stat *scp, int c, int attr); typedef void vr_draw_border_t(scr_stat *scp, int color); typedef void vr_draw_t(scr_stat *scp, int from, int count, int flip); typedef void vr_set_cursor_t(scr_stat *scp, int base, int height, int blink); typedef void vr_draw_cursor_t(scr_stat *scp, int at, int blink, int on, int flip); typedef void vr_blink_cursor_t(scr_stat *scp, int at, int flip); typedef void vr_set_mouse_t(scr_stat *scp); typedef void vr_draw_mouse_t(scr_stat *scp, int x, int y, int on); typedef struct sc_rndr_sw { vr_init_t *init; vr_clear_t *clear; vr_draw_border_t *draw_border; vr_draw_t *draw; vr_set_cursor_t *set_cursor; vr_draw_cursor_t *draw_cursor; vr_blink_cursor_t *blink_cursor; vr_set_mouse_t *set_mouse; vr_draw_mouse_t *draw_mouse; } sc_rndr_sw_t; typedef struct sc_renderer { char *name; int mode; sc_rndr_sw_t *rndrsw; LIST_ENTRY(sc_renderer) link; } sc_renderer_t; #define RENDERER(name, mode, sw, set) \ static struct sc_renderer scrndr_##name##_##mode = { \ #name, mode, &sw \ }; \ DATA_SET(scrndr_set, scrndr_##name##_##mode); \ DATA_SET(set, scrndr_##name##_##mode) #define RENDERER_MODULE(name, set) \ SET_DECLARE(set, sc_renderer_t); \ static int \ scrndr_##name##_event(module_t mod, int type, void *data) \ { \ sc_renderer_t **list; \ int error = 0; \ switch (type) { \ case MOD_LOAD: \ SET_FOREACH(list, set) { \ error = sc_render_add(*list); \ if (error) \ break; \ } \ break; \ case MOD_UNLOAD: \ SET_FOREACH(list, set) { \ error = sc_render_remove(*list);\ if (error) \ break; \ } \ break; \ default: \ return EOPNOTSUPP; \ break; \ } \ return error; \ } \ static moduledata_t scrndr_##name##_mod = { \ "scrndr-" #name, \ scrndr_##name##_event, \ NULL, \ }; \ DECLARE_MODULE(scrndr_##name, scrndr_##name##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) typedef struct { int cursor_start; int cursor_end; int shift_state; int bell_pitch; } bios_values_t; /* other macros */ #define ISTEXTSC(scp) (!((scp)->status \ & (UNKNOWN_MODE | GRAPHICS_MODE | PIXEL_MODE))) #define ISGRAPHSC(scp) (((scp)->status \ & (UNKNOWN_MODE | GRAPHICS_MODE))) #define ISPIXELSC(scp) (((scp)->status \ & (UNKNOWN_MODE | GRAPHICS_MODE | PIXEL_MODE))\ == PIXEL_MODE) #define ISUNKNOWNSC(scp) ((scp)->status & UNKNOWN_MODE) #define ISMOUSEAVAIL(af) ((af) & V_ADP_FONT) #define ISFONTAVAIL(af) ((af) & V_ADP_FONT) #define ISPALAVAIL(af) ((af) & V_ADP_PALETTE) #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) #define SC_VIDEO_LOCKINIT(sc) \ mtx_init(&(sc)->video_mtx, "syscons video lock", NULL, \ MTX_SPIN | MTX_RECURSE); #define SC_VIDEO_LOCK(sc) \ do { \ if (!cold) \ mtx_lock_spin(&(sc)->video_mtx); \ } while(0) #define SC_VIDEO_UNLOCK(sc) \ do { \ if (!cold) \ mtx_unlock_spin(&(sc)->video_mtx); \ } while(0) /* syscons.c */ extern int (*sc_user_ioctl)(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); int sc_probe_unit(int unit, int flags); int sc_attach_unit(int unit, int flags); int set_mode(scr_stat *scp); void sc_set_border(scr_stat *scp, int color); void sc_load_font(scr_stat *scp, int page, int size, int width, u_char *font, int base, int count); void sc_save_font(scr_stat *scp, int page, int size, int width, u_char *font, int base, int count); void sc_show_font(scr_stat *scp, int page); void sc_touch_scrn_saver(void); void sc_puts(scr_stat *scp, u_char *buf, int len, int kernel); void sc_draw_cursor_image(scr_stat *scp); void sc_remove_cursor_image(scr_stat *scp); void sc_set_cursor_image(scr_stat *scp); void sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height); int sc_clean_up(scr_stat *scp); int sc_switch_scr(sc_softc_t *sc, u_int next_scr); void sc_alloc_scr_buffer(scr_stat *scp, int wait, int discard); int sc_init_emulator(scr_stat *scp, char *name); void sc_paste(scr_stat *scp, const u_char *p, int count); void sc_respond(scr_stat *scp, const u_char *p, int count, int wakeup); void sc_bell(scr_stat *scp, int pitch, int duration); /* schistory.c */ #ifndef SC_NO_HISTORY int sc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait); void sc_free_history_buffer(scr_stat *scp, int prev_ysize); void sc_hist_save(scr_stat *scp); #define sc_hist_save_one_line(scp, from) \ sc_vtb_append(&(scp)->vtb, (from), (scp)->history, (scp)->xsize) int sc_hist_restore(scr_stat *scp); void sc_hist_home(scr_stat *scp); void sc_hist_end(scr_stat *scp); int sc_hist_up_line(scr_stat *scp); int sc_hist_down_line(scr_stat *scp); int sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); #endif /* SC_NO_HISTORY */ /* scmouse.c */ #ifndef SC_NO_CUTPASTE void sc_alloc_cut_buffer(scr_stat *scp, int wait); void sc_draw_mouse_image(scr_stat *scp); void sc_remove_mouse_image(scr_stat *scp); int sc_inside_cutmark(scr_stat *scp, int pos); void sc_remove_cutmarking(scr_stat *scp); void sc_remove_all_cutmarkings(sc_softc_t *scp); void sc_remove_all_mouse(sc_softc_t *scp); void sc_mouse_paste(scr_stat *scp); #else #define sc_draw_mouse_image(scp) #define sc_remove_mouse_image(scp) #define sc_inside_cutmark(scp, pos) FALSE #define sc_remove_cutmarking(scp) #define sc_remove_all_cutmarkings(scp) #define sc_remove_all_mouse(scp) #define sc_mouse_paste(scp) #endif /* SC_NO_CUTPASTE */ #ifndef SC_NO_SYSMOUSE void sc_mouse_move(scr_stat *scp, int x, int y); int sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); #endif /* SC_NO_SYSMOUSE */ /* scvidctl.c */ int sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, int fontsize, int font_width); int sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode); int sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, int fontsize, int font_width); int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); int sc_render_add(sc_renderer_t *rndr); int sc_render_remove(sc_renderer_t *rndr); sc_rndr_sw_t *sc_render_match(scr_stat *scp, char *name, int mode); /* scvtb.c */ void sc_vtb_init(sc_vtb_t *vtb, int type, int cols, int rows, void *buffer, int wait); void sc_vtb_destroy(sc_vtb_t *vtb); size_t sc_vtb_size(int cols, int rows); void sc_vtb_clear(sc_vtb_t *vtb, int c, int attr); int sc_vtb_getc(sc_vtb_t *vtb, int at); int sc_vtb_geta(sc_vtb_t *vtb, int at); void sc_vtb_putc(sc_vtb_t *vtb, int at, int c, int a); vm_offset_t sc_vtb_putchar(sc_vtb_t *vtb, vm_offset_t p, int c, int a); vm_offset_t sc_vtb_pointer(sc_vtb_t *vtb, int at); int sc_vtb_pos(sc_vtb_t *vtb, int pos, int offset); #define sc_vtb_tail(vtb) ((vtb)->vtb_tail) #define sc_vtb_rows(vtb) ((vtb)->vtb_rows) #define sc_vtb_cols(vtb) ((vtb)->vtb_cols) void sc_vtb_copy(sc_vtb_t *vtb1, int from, sc_vtb_t *vtb2, int to, int count); void sc_vtb_append(sc_vtb_t *vtb1, int from, sc_vtb_t *vtb2, int count); void sc_vtb_seek(sc_vtb_t *vtb, int pos); void sc_vtb_erase(sc_vtb_t *vtb, int at, int count, int c, int attr); void sc_vtb_move(sc_vtb_t *vtb, int from, int to, int count); void sc_vtb_delete(sc_vtb_t *vtb, int at, int count, int c, int attr); void sc_vtb_ins(sc_vtb_t *vtb, int at, int count, int c, int attr); /* sysmouse.c */ int sysmouse_event(mouse_info_t *info); /* scterm.c */ void sc_move_cursor(scr_stat *scp, int x, int y); void sc_clear_screen(scr_stat *scp); int sc_term_add(sc_term_sw_t *sw); int sc_term_remove(sc_term_sw_t *sw); sc_term_sw_t *sc_term_match(char *name); sc_term_sw_t *sc_term_match_by_number(int index); /* machine dependent functions */ int sc_max_unit(void); sc_softc_t *sc_get_softc(int unit, int flags); sc_softc_t *sc_find_softc(struct video_adapter *adp, struct keyboard *kbd); int sc_get_cons_priority(int *unit, int *flags); void sc_get_bios_values(bios_values_t *values); int sc_tone(int herz); #endif /* !_DEV_SYSCONS_SYSCONS_H_ */ Index: projects/ppc64/sys/dev/xen/xenpci =================================================================== --- projects/ppc64/sys/dev/xen/xenpci (revision 204271) +++ projects/ppc64/sys/dev/xen/xenpci (revision 204272) Property changes on: projects/ppc64/sys/dev/xen/xenpci ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/dev/xen/xenpci:r204217-204271 Index: projects/ppc64/sys/powerpc/aim/mmu_oea64.c =================================================================== --- projects/ppc64/sys/powerpc/aim/mmu_oea64.c (revision 204271) +++ projects/ppc64/sys/powerpc/aim/mmu_oea64.c (revision 204272) @@ -1,2620 +1,2636 @@ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Matt Thomas of Allegro Networks, Inc. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: pmap.c,v 1.28 2000/03/26 20:42:36 kleink Exp $ */ /*- * Copyright (C) 2001 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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$"); /* * Manages physical address maps. * * In addition to hardware address maps, this module is called upon to * provide software-use-only maps which may or may not be stored in the * same form as hardware maps. These pseudo-maps are used to store * intermediate results from copy operations to and from address spaces. * * Since the information managed by this module is also stored by the * logical address mapping module, this module may throw away valid virtual * to physical mappings at almost any time. However, invalidations of * mappings must be done as requested. * * In order to cope with hardware architectures which make virtual to * physical map invalidates expensive, this module may delay invalidate * reduced protection operations until such time as they are actually * necessary. This module is given full information as to which processors * are currently using which maps, and to when physical maps must be made * correct. */ #include "opt_kstack_pages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" #define MOEA_DEBUG #define TODO panic("%s: not implemented", __func__); uintptr_t moea64_get_unique_vsid(void); static __inline register_t cntlzd(volatile register_t a) { register_t b; __asm ("cntlzd %0, %1" : "=r"(b) : "r"(a)); return b; } #define PTESYNC() __asm __volatile("ptesync"); #define TLBSYNC() __asm __volatile("tlbsync; ptesync"); #define SYNC() __asm __volatile("sync"); #define EIEIO() __asm __volatile("eieio"); /* * The tlbie instruction must be executed in 64-bit mode * so we have to twiddle MSR[SF] around every invocation. * Just to add to the fun, exceptions must be off as well * so that we can't trap in 64-bit mode. What a pain. */ struct mtx tlbie_mutex; static __inline void TLBIE(pmap_t pmap, vm_offset_t va) { #ifndef __powerpc64__ register_t vpn_hi, vpn_lo; register_t msr; register_t scratch; #endif uint64_t vpn; /* * Compute the virtual page number we wish to invalidate. */ vpn = (uint64_t)(va & ADDR_PIDX); if (pmap != NULL) vpn |= (va_to_vsid(pmap,va) << 28); vpn &= ~(0xffffULL << 48); mtx_lock_spin(&tlbie_mutex); #ifdef __powerpc64__ __asm __volatile("\ ptesync; \ tlbie %0; \ eieio; \ tlbsync; \ ptesync;" :: "r"(vpn) : "memory"); #else vpn_hi = (uint32_t)(vpn >> 32); vpn_lo = (uint32_t)vpn; __asm __volatile("\ mfmsr %0; \ mr %1, %0; \ insrdi %1,%5,1,0; \ mtmsrd %1; \ ptesync; \ \ sld %1,%2,%4; \ or %1,%1,%3; \ tlbie %1; \ \ mtmsrd %0; \ eieio; \ tlbsync; \ ptesync;" : "=r"(msr), "=r"(scratch) : "r"(vpn_hi), "r"(vpn_lo), "r"(32), "r"(1) : "memory"); #endif mtx_unlock_spin(&tlbie_mutex); } #define DISABLE_TRANS(msr) msr = mfmsr(); mtmsr(msr & ~PSL_DR); isync() #define ENABLE_TRANS(msr) mtmsr(msr); isync() #define VSID_MAKE(sr, hash) ((sr) | (((hash) & 0xfffff) << 4)) #define VSID_TO_HASH(vsid) (((vsid) >> 4) & 0xfffff) +#define VSID_HASH_MASK 0x0000007fffffffffULL #define PVO_PTEGIDX_MASK 0x007UL /* which PTEG slot */ #define PVO_PTEGIDX_VALID 0x008UL /* slot is valid */ #define PVO_WIRED 0x010UL /* PVO entry is wired */ #define PVO_MANAGED 0x020UL /* PVO entry is managed */ #define PVO_BOOTSTRAP 0x080UL /* PVO entry allocated during bootstrap */ #define PVO_FAKE 0x100UL /* fictitious phys page */ #define PVO_VADDR(pvo) ((pvo)->pvo_vaddr & ~ADDR_POFF) #define PVO_ISFAKE(pvo) ((pvo)->pvo_vaddr & PVO_FAKE) #define PVO_PTEGIDX_GET(pvo) ((pvo)->pvo_vaddr & PVO_PTEGIDX_MASK) #define PVO_PTEGIDX_ISSET(pvo) ((pvo)->pvo_vaddr & PVO_PTEGIDX_VALID) #define PVO_PTEGIDX_CLR(pvo) \ ((void)((pvo)->pvo_vaddr &= ~(PVO_PTEGIDX_VALID|PVO_PTEGIDX_MASK))) #define PVO_PTEGIDX_SET(pvo, i) \ ((void)((pvo)->pvo_vaddr |= (i)|PVO_PTEGIDX_VALID)) #define MOEA_PVO_CHECK(pvo) #define LOCK_TABLE() mtx_lock(&moea64_table_mutex) #define UNLOCK_TABLE() mtx_unlock(&moea64_table_mutex); #define ASSERT_TABLE_LOCK() mtx_assert(&moea64_table_mutex, MA_OWNED) struct ofw_map { cell_t om_va; cell_t om_len; cell_t om_pa_hi; cell_t om_pa_lo; cell_t om_mode; }; /* * Map of physical memory regions. */ static struct mem_region *regions; static struct mem_region *pregions; static u_int phys_avail_count; static int regions_sz, pregions_sz; extern int ofw_real_mode; extern struct pmap ofw_pmap; extern void bs_remap_earlyboot(void); /* * Lock for the pteg and pvo tables. */ struct mtx moea64_table_mutex; /* * PTEG data. */ static struct lpteg *moea64_pteg_table; u_int moea64_pteg_count; u_int moea64_pteg_mask; /* * PVO data. */ struct pvo_head *moea64_pvo_table; /* pvo entries by pteg index */ /* lists of unmanaged pages */ struct pvo_head moea64_pvo_kunmanaged = LIST_HEAD_INITIALIZER(moea64_pvo_kunmanaged); struct pvo_head moea64_pvo_unmanaged = LIST_HEAD_INITIALIZER(moea64_pvo_unmanaged); uma_zone_t moea64_upvo_zone; /* zone for pvo entries for unmanaged pages */ uma_zone_t moea64_mpvo_zone; /* zone for pvo entries for managed pages */ #define BPVO_POOL_SIZE 327680 static struct pvo_entry *moea64_bpvo_pool; static int moea64_bpvo_pool_index = 0; #define VSID_NBPW (sizeof(u_int32_t) * 8) #ifdef __powerpc64__ #define NVSIDS (NPMAPS * 16) #define VSID_HASHMASK 0xffffffffUL #else #define NVSIDS NPMAPS #define VSID_HASHMASK 0xfffffUL #endif static u_int moea64_vsid_bitmap[NVSIDS / VSID_NBPW]; static boolean_t moea64_initialized = FALSE; /* * Statistics. */ u_int moea64_pte_valid = 0; u_int moea64_pte_overflow = 0; u_int moea64_pvo_entries = 0; u_int moea64_pvo_enter_calls = 0; u_int moea64_pvo_remove_calls = 0; SYSCTL_INT(_machdep, OID_AUTO, moea64_pte_valid, CTLFLAG_RD, &moea64_pte_valid, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pte_overflow, CTLFLAG_RD, &moea64_pte_overflow, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_entries, CTLFLAG_RD, &moea64_pvo_entries, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_enter_calls, CTLFLAG_RD, &moea64_pvo_enter_calls, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_remove_calls, CTLFLAG_RD, &moea64_pvo_remove_calls, 0, ""); vm_offset_t moea64_scratchpage_va[2]; struct pvo_entry *moea64_scratchpage_pvo[2]; struct lpte *moea64_scratchpage_pte[2]; struct mtx moea64_scratchpage_mtx; /* * Allocate physical memory for use in moea64_bootstrap. */ static vm_offset_t moea64_bootstrap_alloc(vm_size_t, u_int); /* * PTE calls. */ static int moea64_pte_insert(u_int, struct lpte *); /* * PVO calls. */ static int moea64_pvo_enter(pmap_t, uma_zone_t, struct pvo_head *, vm_offset_t, vm_offset_t, uint64_t, int); static void moea64_pvo_remove(struct pvo_entry *, int); static struct pvo_entry *moea64_pvo_find_va(pmap_t, vm_offset_t, int *); static struct lpte *moea64_pvo_to_pte(const struct pvo_entry *, int); /* * Utility routines. */ static void moea64_bridge_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); static void moea64_bridge_cpu_bootstrap(mmu_t, int ap); static void moea64_enter_locked(pmap_t, vm_offset_t, vm_page_t, vm_prot_t, boolean_t); static boolean_t moea64_query_bit(vm_page_t, u_int64_t); static u_int moea64_clear_bit(vm_page_t, u_int64_t, u_int64_t *); static void moea64_kremove(mmu_t, vm_offset_t); static void moea64_syncicache(pmap_t pmap, vm_offset_t va, vm_offset_t pa, vm_size_t sz); static void tlbia(void); #ifdef __powerpc64__ static void slbia(void); #endif /* * Kernel MMU interface */ void moea64_change_wiring(mmu_t, pmap_t, vm_offset_t, boolean_t); void moea64_clear_modify(mmu_t, vm_page_t); void moea64_clear_reference(mmu_t, vm_page_t); void moea64_copy_page(mmu_t, vm_page_t, vm_page_t); void moea64_enter(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t, boolean_t); void moea64_enter_object(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_page_t, vm_prot_t); void moea64_enter_quick(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t); vm_paddr_t moea64_extract(mmu_t, pmap_t, vm_offset_t); vm_page_t moea64_extract_and_hold(mmu_t, pmap_t, vm_offset_t, vm_prot_t); void moea64_init(mmu_t); boolean_t moea64_is_modified(mmu_t, vm_page_t); boolean_t moea64_ts_referenced(mmu_t, vm_page_t); vm_offset_t moea64_map(mmu_t, vm_offset_t *, vm_offset_t, vm_offset_t, int); boolean_t moea64_page_exists_quick(mmu_t, pmap_t, vm_page_t); int moea64_page_wired_mappings(mmu_t, vm_page_t); void moea64_pinit(mmu_t, pmap_t); void moea64_pinit0(mmu_t, pmap_t); void moea64_protect(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); void moea64_qenter(mmu_t, vm_offset_t, vm_page_t *, int); void moea64_qremove(mmu_t, vm_offset_t, int); void moea64_release(mmu_t, pmap_t); void moea64_remove(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea64_remove_all(mmu_t, vm_page_t); void moea64_remove_write(mmu_t, vm_page_t); void moea64_zero_page(mmu_t, vm_page_t); void moea64_zero_page_area(mmu_t, vm_page_t, int, int); void moea64_zero_page_idle(mmu_t, vm_page_t); void moea64_activate(mmu_t, struct thread *); void moea64_deactivate(mmu_t, struct thread *); void *moea64_mapdev(mmu_t, vm_offset_t, vm_size_t); void moea64_unmapdev(mmu_t, vm_offset_t, vm_size_t); vm_offset_t moea64_kextract(mmu_t, vm_offset_t); void moea64_kenter(mmu_t, vm_offset_t, vm_offset_t); boolean_t moea64_dev_direct_mapped(mmu_t, vm_offset_t, vm_size_t); static void moea64_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); static mmu_method_t moea64_bridge_methods[] = { MMUMETHOD(mmu_change_wiring, moea64_change_wiring), MMUMETHOD(mmu_clear_modify, moea64_clear_modify), MMUMETHOD(mmu_clear_reference, moea64_clear_reference), MMUMETHOD(mmu_copy_page, moea64_copy_page), MMUMETHOD(mmu_enter, moea64_enter), MMUMETHOD(mmu_enter_object, moea64_enter_object), MMUMETHOD(mmu_enter_quick, moea64_enter_quick), MMUMETHOD(mmu_extract, moea64_extract), MMUMETHOD(mmu_extract_and_hold, moea64_extract_and_hold), MMUMETHOD(mmu_init, moea64_init), MMUMETHOD(mmu_is_modified, moea64_is_modified), MMUMETHOD(mmu_ts_referenced, moea64_ts_referenced), MMUMETHOD(mmu_map, moea64_map), MMUMETHOD(mmu_page_exists_quick,moea64_page_exists_quick), MMUMETHOD(mmu_page_wired_mappings,moea64_page_wired_mappings), MMUMETHOD(mmu_pinit, moea64_pinit), MMUMETHOD(mmu_pinit0, moea64_pinit0), MMUMETHOD(mmu_protect, moea64_protect), MMUMETHOD(mmu_qenter, moea64_qenter), MMUMETHOD(mmu_qremove, moea64_qremove), MMUMETHOD(mmu_release, moea64_release), MMUMETHOD(mmu_remove, moea64_remove), MMUMETHOD(mmu_remove_all, moea64_remove_all), MMUMETHOD(mmu_remove_write, moea64_remove_write), MMUMETHOD(mmu_sync_icache, moea64_sync_icache), MMUMETHOD(mmu_zero_page, moea64_zero_page), MMUMETHOD(mmu_zero_page_area, moea64_zero_page_area), MMUMETHOD(mmu_zero_page_idle, moea64_zero_page_idle), MMUMETHOD(mmu_activate, moea64_activate), MMUMETHOD(mmu_deactivate, moea64_deactivate), /* Internal interfaces */ MMUMETHOD(mmu_bootstrap, moea64_bridge_bootstrap), MMUMETHOD(mmu_cpu_bootstrap, moea64_bridge_cpu_bootstrap), MMUMETHOD(mmu_mapdev, moea64_mapdev), MMUMETHOD(mmu_unmapdev, moea64_unmapdev), MMUMETHOD(mmu_kextract, moea64_kextract), MMUMETHOD(mmu_kenter, moea64_kenter), MMUMETHOD(mmu_dev_direct_mapped,moea64_dev_direct_mapped), { 0, 0 } }; static mmu_def_t oea64_bridge_mmu = { MMU_TYPE_G5, moea64_bridge_methods, 0 }; MMU_DEF(oea64_bridge_mmu); static __inline u_int va_to_pteg(uint64_t vsid, vm_offset_t addr) { - u_int hash; + uint64_t hash; - hash = vsid ^ (((uint64_t)addr & ADDR_PIDX) >> + hash = (vsid & VSID_HASH_MASK) ^ (((uint64_t)addr & ADDR_PIDX) >> ADDR_PIDX_SHFT); return (hash & moea64_pteg_mask); } static __inline struct pvo_head * pa_to_pvoh(vm_offset_t pa, vm_page_t *pg_p) { struct vm_page *pg; pg = PHYS_TO_VM_PAGE(pa); if (pg_p != NULL) *pg_p = pg; if (pg == NULL) return (&moea64_pvo_unmanaged); return (&pg->md.mdpg_pvoh); } static __inline struct pvo_head * vm_page_to_pvoh(vm_page_t m) { return (&m->md.mdpg_pvoh); } static __inline void moea64_attr_clear(vm_page_t m, u_int64_t ptebit) { mtx_assert(&vm_page_queue_mtx, MA_OWNED); m->md.mdpg_attrs &= ~ptebit; } static __inline u_int64_t moea64_attr_fetch(vm_page_t m) { return (m->md.mdpg_attrs); } static __inline void moea64_attr_save(vm_page_t m, u_int64_t ptebit) { mtx_assert(&vm_page_queue_mtx, MA_OWNED); m->md.mdpg_attrs |= ptebit; } static __inline void moea64_pte_create(struct lpte *pt, uint64_t vsid, vm_offset_t va, uint64_t pte_lo) { ASSERT_TABLE_LOCK(); /* * Construct a PTE. Default to IMB initially. Valid bit only gets * set when the real pte is set in memory. * * Note: Don't set the valid bit for correct operation of tlb update. */ pt->pte_hi = (vsid << LPTE_VSID_SHIFT) | (((uint64_t)(va & ADDR_PIDX) >> ADDR_API_SHFT64) & LPTE_API); pt->pte_lo = pte_lo; } static __inline void moea64_pte_synch(struct lpte *pt, struct lpte *pvo_pt) { ASSERT_TABLE_LOCK(); pvo_pt->pte_lo |= pt->pte_lo & (LPTE_REF | LPTE_CHG); } static __inline void moea64_pte_clear(struct lpte *pt, pmap_t pmap, vm_offset_t va, u_int64_t ptebit) { ASSERT_TABLE_LOCK(); /* * As shown in Section 7.6.3.2.3 */ pt->pte_lo &= ~ptebit; TLBIE(pmap,va); } static __inline void moea64_pte_set(struct lpte *pt, struct lpte *pvo_pt) { ASSERT_TABLE_LOCK(); pvo_pt->pte_hi |= LPTE_VALID; /* * Update the PTE as defined in section 7.6.3.1. * Note that the REF/CHG bits are from pvo_pt and thus should have * been saved so this routine can restore them (if desired). */ pt->pte_lo = pvo_pt->pte_lo; EIEIO(); pt->pte_hi = pvo_pt->pte_hi; PTESYNC(); moea64_pte_valid++; } static __inline void moea64_pte_unset(struct lpte *pt, struct lpte *pvo_pt, pmap_t pmap, vm_offset_t va) { ASSERT_TABLE_LOCK(); pvo_pt->pte_hi &= ~LPTE_VALID; /* * Force the reg & chg bits back into the PTEs. */ SYNC(); /* * Invalidate the pte. */ pt->pte_hi &= ~LPTE_VALID; TLBIE(pmap,va); /* * Save the reg & chg bits. */ moea64_pte_synch(pt, pvo_pt); moea64_pte_valid--; } static __inline void moea64_pte_change(struct lpte *pt, struct lpte *pvo_pt, pmap_t pmap, vm_offset_t va) { /* * Invalidate the PTE */ moea64_pte_unset(pt, pvo_pt, pmap, va); moea64_pte_set(pt, pvo_pt); if (pmap == kernel_pmap) isync(); } static __inline uint64_t moea64_calc_wimg(vm_offset_t pa) { uint64_t pte_lo; int i; /* * Assume the page is cache inhibited and access is guarded unless * it's in our available memory array. */ pte_lo = LPTE_I | LPTE_G; for (i = 0; i < pregions_sz; i++) { if ((pa >= pregions[i].mr_start) && (pa < (pregions[i].mr_start + pregions[i].mr_size))) { pte_lo &= ~(LPTE_I | LPTE_G); pte_lo |= LPTE_M; break; } } return pte_lo; } /* * Quick sort callout for comparing memory regions. */ static int mr_cmp(const void *a, const void *b); static int om_cmp(const void *a, const void *b); static int mr_cmp(const void *a, const void *b) { const struct mem_region *regiona; const struct mem_region *regionb; regiona = a; regionb = b; if (regiona->mr_start < regionb->mr_start) return (-1); else if (regiona->mr_start > regionb->mr_start) return (1); else return (0); } static int om_cmp(const void *a, const void *b) { const struct ofw_map *mapa; const struct ofw_map *mapb; mapa = a; mapb = b; if (mapa->om_pa_hi < mapb->om_pa_hi) return (-1); else if (mapa->om_pa_hi > mapb->om_pa_hi) return (1); else if (mapa->om_pa_lo < mapb->om_pa_lo) return (-1); else if (mapa->om_pa_lo > mapb->om_pa_lo) return (1); else return (0); } static void moea64_bridge_cpu_bootstrap(mmu_t mmup, int ap) { int i = 0; /* * Initialize segment registers and MMU */ mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR); isync(); /* * Install kernel SLB entries */ #ifdef __powerpc64__ slbia(); for (i = 0; i < 64; i++) { if (!(kernel_pmap->pm_slb[i].slbe & SLBE_VALID)) continue; __asm __volatile ("slbmte %0, %1" :: "r"(kernel_pmap->pm_slb[i].slbv), "r"(kernel_pmap->pm_slb[i].slbe)); } #else for (i = 0; i < 16; i++) mtsrin(i << ADDR_SR_SHFT, kernel_pmap->pm_sr[i]); #endif /* * Install page table */ __asm __volatile ("ptesync; mtsdr1 %0; isync" :: "r"((uintptr_t)moea64_pteg_table | (64 - cntlzd(moea64_pteg_mask >> 11)))); tlbia(); } static void moea64_add_ofw_mappings(mmu_t mmup, phandle_t mmu, size_t sz) { struct ofw_map translations[sz/sizeof(struct ofw_map)]; register_t msr; vm_offset_t off; vm_paddr_t pa_base; int i, ofw_mappings; bzero(translations, sz); if (OF_getprop(mmu, "translations", translations, sz) == -1) panic("moea64_bootstrap: can't get ofw translations"); CTR0(KTR_PMAP, "moea64_add_ofw_mappings: translations"); sz /= sizeof(*translations); qsort(translations, sz, sizeof (*translations), om_cmp); for (i = 0, ofw_mappings = 0; i < sz; i++) { CTR3(KTR_PMAP, "translation: pa=%#x va=%#x len=%#x", (uint32_t)(translations[i].om_pa_lo), translations[i].om_va, translations[i].om_len); if (translations[i].om_pa_lo % PAGE_SIZE) panic("OFW translation not page-aligned!"); pa_base = translations[i].om_pa_lo; #ifdef __powerpc64__ pa_base += (vm_offset_t)translations[i].om_pa_hi << 32; #else if (translations[i].om_pa_hi) panic("OFW translations above 32-bit boundary!"); #endif /* Now enter the pages for this mapping */ DISABLE_TRANS(msr); for (off = 0; off < translations[i].om_len; off += PAGE_SIZE) { moea64_kenter(mmup, translations[i].om_va + off, pa_base + off); ofw_mappings++; } ENABLE_TRANS(msr); } } static void moea64_bridge_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { ihandle_t mmui; phandle_t chosen; phandle_t mmu; size_t sz; int i, j; vm_size_t size, physsz, hwphyssz; vm_offset_t pa, va, off; register_t msr; void *dpcpu; /* We don't have a direct map since there is no BAT */ hw_direct_map = 0; #ifndef __powerpc64__ /* Make sure battable is zero, since we have no BAT */ for (i = 0; i < 16; i++) { battable[i].batu = 0; battable[i].batl = 0; } #endif /* Get physical memory regions from firmware */ mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); CTR0(KTR_PMAP, "moea64_bootstrap: physical memory"); qsort(pregions, pregions_sz, sizeof(*pregions), mr_cmp); if (sizeof(phys_avail)/sizeof(phys_avail[0]) < regions_sz) panic("moea64_bootstrap: phys_avail too small"); qsort(regions, regions_sz, sizeof(*regions), mr_cmp); phys_avail_count = 0; physsz = 0; hwphyssz = 0; TUNABLE_ULONG_FETCH("hw.physmem", (u_long *) &hwphyssz); for (i = 0, j = 0; i < regions_sz; i++, j += 2) { CTR3(KTR_PMAP, "region: %#x - %#x (%#x)", regions[i].mr_start, regions[i].mr_start + regions[i].mr_size, regions[i].mr_size); if (hwphyssz != 0 && (physsz + regions[i].mr_size) >= hwphyssz) { if (physsz < hwphyssz) { phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + hwphyssz - physsz; physsz = hwphyssz; phys_avail_count++; } break; } phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + regions[i].mr_size; phys_avail_count++; physsz += regions[i].mr_size; } /* Check for overlap with the kernel and exception vectors */ for (j = 0; j < 2*phys_avail_count; j+=2) { if (phys_avail[j] < EXC_LAST) phys_avail[j] += EXC_LAST; if (kernelstart >= phys_avail[j] && kernelstart < phys_avail[j+1]) { if (kernelend < phys_avail[j+1]) { phys_avail[2*phys_avail_count] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; phys_avail[2*phys_avail_count + 1] = phys_avail[j+1]; phys_avail_count++; } phys_avail[j+1] = kernelstart & ~PAGE_MASK; } if (kernelend >= phys_avail[j] && kernelend < phys_avail[j+1]) { if (kernelstart > phys_avail[j]) { phys_avail[2*phys_avail_count] = phys_avail[j]; phys_avail[2*phys_avail_count + 1] = kernelstart & ~PAGE_MASK; phys_avail_count++; } phys_avail[j] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; } } physmem = btoc(physsz); /* * Allocate PTEG table. */ #ifdef PTEGCOUNT moea64_pteg_count = PTEGCOUNT; #else moea64_pteg_count = 0x1000; while (moea64_pteg_count < physmem) moea64_pteg_count <<= 1; #endif /* PTEGCOUNT */ size = moea64_pteg_count * sizeof(struct lpteg); CTR2(KTR_PMAP, "moea64_bootstrap: %d PTEGs, %d bytes", moea64_pteg_count, size); /* * We now need to allocate memory. This memory, to be allocated, * has to reside in a page table. The page table we are about to * allocate. We don't have BAT. So drop to data real mode for a minute * as a measure of last resort. We do this a couple times. */ moea64_pteg_table = (struct lpteg *)moea64_bootstrap_alloc(size, size); DISABLE_TRANS(msr); bzero((void *)moea64_pteg_table, moea64_pteg_count * sizeof(struct lpteg)); ENABLE_TRANS(msr); moea64_pteg_mask = moea64_pteg_count - 1; CTR1(KTR_PMAP, "moea64_bootstrap: PTEG table at %p", moea64_pteg_table); /* * Allocate pv/overflow lists. */ size = sizeof(struct pvo_head) * moea64_pteg_count; moea64_pvo_table = (struct pvo_head *)moea64_bootstrap_alloc(size, PAGE_SIZE); CTR1(KTR_PMAP, "moea64_bootstrap: PVO table at %p", moea64_pvo_table); DISABLE_TRANS(msr); for (i = 0; i < moea64_pteg_count; i++) LIST_INIT(&moea64_pvo_table[i]); ENABLE_TRANS(msr); /* * Initialize the lock that synchronizes access to the pteg and pvo * tables. */ mtx_init(&moea64_table_mutex, "pmap table", NULL, MTX_DEF | MTX_RECURSE); /* * Initialize the TLBIE lock. TLBIE can only be executed by one CPU. */ mtx_init(&tlbie_mutex, "tlbie mutex", NULL, MTX_SPIN); /* * Initialise the unmanaged pvo pool. */ moea64_bpvo_pool = (struct pvo_entry *)moea64_bootstrap_alloc( BPVO_POOL_SIZE*sizeof(struct pvo_entry), 0); moea64_bpvo_pool_index = 0; /* * Make sure kernel vsid is allocated as well as VSID 0. */ moea64_vsid_bitmap[(KERNEL_VSIDBITS & (NVSIDS - 1)) / VSID_NBPW] |= 1 << (KERNEL_VSIDBITS % VSID_NBPW); moea64_vsid_bitmap[0] |= 1; /* * Initialize the kernel pmap (which is statically allocated). */ #ifdef __powerpc64__ for (i = 0; i < 16; i++) { kernel_pmap->pm_slb[i].slbv = ((KERNEL_VSIDBITS << 17) | i) << SLBV_VSID_SHIFT; kernel_pmap->pm_slb[i].slbe = ((uint64_t)i << SLBE_ESID_SHIFT) | SLBE_VALID | i; } kernel_pmap->pm_slb[USER_SR].slbe = 0; #else for (i = 0; i < 16; i++) kernel_pmap->pm_sr[i] = EMPTY_SEGMENT + i; #endif kernel_pmap->pmap_phys = kernel_pmap; kernel_pmap->pm_active = ~0; PMAP_LOCK_INIT(kernel_pmap); /* * Now map in all the other buffers we allocated earlier */ DISABLE_TRANS(msr); size = moea64_pteg_count * sizeof(struct lpteg); off = (vm_offset_t)(moea64_pteg_table); for (pa = off; pa < off + size; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); size = sizeof(struct pvo_head) * moea64_pteg_count; off = (vm_offset_t)(moea64_pvo_table); for (pa = off; pa < off + size; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); size = BPVO_POOL_SIZE*sizeof(struct pvo_entry); off = (vm_offset_t)(moea64_bpvo_pool); for (pa = off; pa < off + size; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); /* * Map certain important things, like ourselves. * * NOTE: We do not map the exception vector space. That code is * used only in real mode, and leaving it unmapped allows us to * catch NULL pointer deferences, instead of making NULL a valid * address. */ for (pa = kernelstart & ~PAGE_MASK; pa < kernelend; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); ENABLE_TRANS(msr); if (!ofw_real_mode) { /* * Set up the Open Firmware pmap and add its mappings. */ moea64_pinit(mmup, &ofw_pmap); #ifndef __powerpc64__ for (i = 0; i < 16; i++) ofw_pmap.pm_sr[i] = kernel_pmap->pm_sr[i]; #endif if ((chosen = OF_finddevice("/chosen")) == -1) panic("moea64_bootstrap: can't find /chosen"); OF_getprop(chosen, "mmu", &mmui, 4); if ((mmu = OF_instance_to_package(mmui)) == -1) panic("moea64_bootstrap: can't get mmu package"); if ((sz = OF_getproplen(mmu, "translations")) == -1) panic("moea64_bootstrap: can't get ofw translation count"); if (sz > 6144 /* tmpstksz - 2 KB headroom */) panic("moea64_bootstrap: too many ofw translations"); moea64_add_ofw_mappings(mmup, mmu, sz); } #ifdef SMP TLBSYNC(); #endif /* * Calculate the last available physical address. */ for (i = 0; phys_avail[i + 2] != 0; i += 2) ; Maxmem = powerpc_btop(phys_avail[i + 1]); /* * Initialize MMU and remap early physical mappings */ moea64_bridge_cpu_bootstrap(mmup,0); mtmsr(mfmsr() | PSL_DR | PSL_IR); isync(); pmap_bootstrapped++; bs_remap_earlyboot(); /* * Set the start and end of kva. */ virtual_avail = VM_MIN_KERNEL_ADDRESS; virtual_end = VM_MAX_SAFE_KERNEL_ADDRESS; /* * Figure out how far we can extend virtual_end into segment 16 * without running into existing mappings. Segment 16 is guaranteed * to contain neither RAM nor devices (at least on Apple hardware), * but will generally contain some OFW mappings we should not * step on. */ PMAP_LOCK(kernel_pmap); while (moea64_pvo_find_va(kernel_pmap, virtual_end+1, NULL) == NULL) virtual_end += PAGE_SIZE; PMAP_UNLOCK(kernel_pmap); /* * Allocate some things for page zeroing */ mtx_init(&moea64_scratchpage_mtx, "pvo zero page", NULL, MTX_DEF); for (i = 0; i < 2; i++) { moea64_scratchpage_va[i] = virtual_avail; virtual_avail += PAGE_SIZE; moea64_kenter(mmup,moea64_scratchpage_va[i],kernelstart); LOCK_TABLE(); moea64_scratchpage_pvo[i] = moea64_pvo_find_va(kernel_pmap, moea64_scratchpage_va[i],&j); moea64_scratchpage_pte[i] = moea64_pvo_to_pte( moea64_scratchpage_pvo[i],j); + moea64_scratchpage_pte[i]->pte_hi |= LPTE_LOCKED; UNLOCK_TABLE(); } /* * Allocate a kernel stack with a guard page for thread0 and map it * into the kernel page map. */ pa = moea64_bootstrap_alloc(KSTACK_PAGES * PAGE_SIZE, PAGE_SIZE); va = virtual_avail + KSTACK_GUARD_PAGES * PAGE_SIZE; virtual_avail = va + KSTACK_PAGES * PAGE_SIZE; CTR2(KTR_PMAP, "moea_bootstrap: kstack0 at %#x (%#x)", pa, va); thread0.td_kstack = va; thread0.td_kstack_pages = KSTACK_PAGES; for (i = 0; i < KSTACK_PAGES; i++) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the message buffer. */ pa = msgbuf_phys = moea64_bootstrap_alloc(MSGBUF_SIZE, PAGE_SIZE); msgbufp = (struct msgbuf *)msgbuf_phys; while (pa - msgbuf_phys < MSGBUF_SIZE) { moea64_kenter(mmup, pa, pa); pa += PAGE_SIZE; } /* * Allocate virtual address space for the dynamic percpu area. */ pa = moea64_bootstrap_alloc(DPCPU_SIZE, PAGE_SIZE); dpcpu = (void *)pa; while (pa - (vm_offset_t)dpcpu < DPCPU_SIZE) { moea64_kenter(mmup, pa, pa); pa += PAGE_SIZE; } dpcpu_init(dpcpu, 0); } /* * Activate a user pmap. The pmap must be activated before it's address * space can be accessed in any way. */ void moea64_activate(mmu_t mmu, struct thread *td) { pmap_t pm, pmr; /* * Load all the data we need up front to encourage the compiler to * not issue any loads while we have interrupts disabled below. */ pm = &td->td_proc->p_vmspace->vm_pmap; pmr = pm->pmap_phys; pm->pm_active |= PCPU_GET(cpumask); PCPU_SET(curpmap, pmr); } void moea64_deactivate(mmu_t mmu, struct thread *td) { pmap_t pm; pm = &td->td_proc->p_vmspace->vm_pmap; pm->pm_active &= ~(PCPU_GET(cpumask)); PCPU_SET(curpmap, NULL); } void moea64_change_wiring(mmu_t mmu, pmap_t pm, vm_offset_t va, boolean_t wired) { struct pvo_entry *pvo; PMAP_LOCK(pm); pvo = moea64_pvo_find_va(pm, va & ~ADDR_POFF, NULL); if (pvo != NULL) { if (wired) { if ((pvo->pvo_vaddr & PVO_WIRED) == 0) pm->pm_stats.wired_count++; pvo->pvo_vaddr |= PVO_WIRED; } else { if ((pvo->pvo_vaddr & PVO_WIRED) != 0) pm->pm_stats.wired_count--; pvo->pvo_vaddr &= ~PVO_WIRED; } } PMAP_UNLOCK(pm); } /* - * Zero a page of physical memory by temporarily mapping it into the tlb. - */ -void -moea64_zero_page(mmu_t mmu, vm_page_t m) -{ - moea64_zero_page_area(mmu,m,0,PAGE_SIZE); -} - -/* * This goes through and sets the physical address of our * special scratch PTE to the PA we want to zero or copy. Because * of locking issues (this can get called in pvo_enter() by * the UMA allocator), we can't use most other utility functions here */ static __inline void moea64_set_scratchpage_pa(int which, vm_offset_t pa) { + mtx_assert(&moea64_scratchpage_mtx, MA_OWNED); + moea64_scratchpage_pvo[which]->pvo_pte.lpte.pte_lo &= - (~LPTE_WIMG & ~LPTE_RPGN); + ~(LPTE_WIMG | LPTE_RPGN); moea64_scratchpage_pvo[which]->pvo_pte.lpte.pte_lo |= moea64_calc_wimg(pa) | (uint64_t)pa; moea64_scratchpage_pte[which]->pte_hi &= ~LPTE_VALID; TLBIE(kernel_pmap, moea64_scratchpage_va[which]); moea64_scratchpage_pte[which]->pte_lo = moea64_scratchpage_pvo[which]->pvo_pte.lpte.pte_lo; EIEIO(); moea64_scratchpage_pte[which]->pte_hi |= LPTE_VALID; PTESYNC(); isync(); } void moea64_copy_page(mmu_t mmu, vm_page_t msrc, vm_page_t mdst) { vm_offset_t dst; vm_offset_t src; dst = VM_PAGE_TO_PHYS(mdst); src = VM_PAGE_TO_PHYS(msrc); mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(0,src); moea64_set_scratchpage_pa(1,dst); kcopy((void *)moea64_scratchpage_va[0], (void *)moea64_scratchpage_va[1], PAGE_SIZE); mtx_unlock(&moea64_scratchpage_mtx); } void moea64_zero_page_area(mmu_t mmu, vm_page_t m, int off, int size) { vm_offset_t pa = VM_PAGE_TO_PHYS(m); if (!moea64_initialized) panic("moea64_zero_page: can't zero pa %#" PRIxPTR, pa); if (size + off > PAGE_SIZE) panic("moea64_zero_page: size + off > PAGE_SIZE"); mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(0,pa); bzero((caddr_t)moea64_scratchpage_va[0] + off, size); mtx_unlock(&moea64_scratchpage_mtx); } +/* + * Zero a page of physical memory by temporarily mapping it + */ void +moea64_zero_page(mmu_t mmu, vm_page_t m) +{ + vm_offset_t pa = VM_PAGE_TO_PHYS(m); + vm_offset_t off; + + if (!moea64_initialized) + panic("moea64_zero_page: can't zero pa %#x", pa); + + mtx_lock(&moea64_scratchpage_mtx); + + moea64_set_scratchpage_pa(0,pa); + for (off = 0; off < PAGE_SIZE; off += cacheline_size) + __asm __volatile("dcbz 0,%0" :: + "r"(moea64_scratchpage_va[0] + off)); + mtx_unlock(&moea64_scratchpage_mtx); +} + +void moea64_zero_page_idle(mmu_t mmu, vm_page_t m) { moea64_zero_page(mmu, m); } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. */ void moea64_enter(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, boolean_t wired) { vm_page_lock_queues(); PMAP_LOCK(pmap); moea64_enter_locked(pmap, va, m, prot, wired); vm_page_unlock_queues(); PMAP_UNLOCK(pmap); } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. * * The page queues and pmap must be locked. */ static void moea64_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, boolean_t wired) { struct pvo_head *pvo_head; uma_zone_t zone; vm_page_t pg; uint64_t pte_lo; u_int pvo_flags; int error; if (!moea64_initialized) { pvo_head = &moea64_pvo_kunmanaged; pg = NULL; zone = moea64_upvo_zone; pvo_flags = 0; } else { pvo_head = vm_page_to_pvoh(m); pg = m; zone = moea64_mpvo_zone; pvo_flags = PVO_MANAGED; } if (pmap_bootstrapped) mtx_assert(&vm_page_queue_mtx, MA_OWNED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); /* XXX change the pvo head for fake pages */ if ((m->flags & PG_FICTITIOUS) == PG_FICTITIOUS) { pvo_flags &= ~PVO_MANAGED; pvo_head = &moea64_pvo_kunmanaged; zone = moea64_upvo_zone; } pte_lo = moea64_calc_wimg(VM_PAGE_TO_PHYS(m)); if (prot & VM_PROT_WRITE) { pte_lo |= LPTE_BW; if (pmap_bootstrapped) vm_page_flag_set(m, PG_WRITEABLE); } else pte_lo |= LPTE_BR; if (prot & VM_PROT_EXECUTE) pvo_flags |= VM_PROT_EXECUTE; if (wired) pvo_flags |= PVO_WIRED; if ((m->flags & PG_FICTITIOUS) != 0) pvo_flags |= PVO_FAKE; error = moea64_pvo_enter(pmap, zone, pvo_head, va, VM_PAGE_TO_PHYS(m), pte_lo, pvo_flags); /* * Flush the page from the instruction cache if this page is * mapped executable and cacheable. */ if ((pte_lo & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { moea64_syncicache(pmap, va, VM_PAGE_TO_PHYS(m), PAGE_SIZE); } } static void moea64_syncicache(pmap_t pmap, vm_offset_t va, vm_offset_t pa, vm_size_t sz) { /* * This is much trickier than on older systems because * we can't sync the icache on physical addresses directly * without a direct map. Instead we check a couple of cases * where the memory is already mapped in and, failing that, * use the same trick we use for page zeroing to create * a temporary mapping for this physical address. */ if (!pmap_bootstrapped) { /* * If PMAP is not bootstrapped, we are likely to be * in real mode. */ __syncicache((void *)pa, sz); } else if (pmap == kernel_pmap) { __syncicache((void *)va, sz); } else { /* Use the scratch page to set up a temp mapping */ mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(1,pa & ~ADDR_POFF); __syncicache((void *)(moea64_scratchpage_va[1] + (va & ADDR_POFF)), sz); mtx_unlock(&moea64_scratchpage_mtx); } } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void moea64_enter_object(mmu_t mmu, pmap_t pm, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { vm_page_t m; vm_pindex_t diff, psize; psize = atop(end - start); m = m_start; PMAP_LOCK(pm); while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { moea64_enter_locked(pm, start + ptoa(diff), m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), FALSE); m = TAILQ_NEXT(m, listq); } PMAP_UNLOCK(pm); } void moea64_enter_quick(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot) { PMAP_LOCK(pm); moea64_enter_locked(pm, va, m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), FALSE); PMAP_UNLOCK(pm); } vm_paddr_t moea64_extract(mmu_t mmu, pmap_t pm, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; PMAP_LOCK(pm); pvo = moea64_pvo_find_va(pm, va & ~ADDR_POFF, NULL); if (pvo == NULL) pa = 0; else pa = (pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN) | (va & ADDR_POFF); PMAP_UNLOCK(pm); return (pa); } /* * Atomically extract and hold the physical page with the given * pmap and virtual address pair if that mapping permits the given * protection. */ vm_page_t moea64_extract_and_hold(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_prot_t prot) { struct pvo_entry *pvo; vm_page_t m; m = NULL; vm_page_lock_queues(); PMAP_LOCK(pmap); pvo = moea64_pvo_find_va(pmap, va & ~ADDR_POFF, NULL); if (pvo != NULL && (pvo->pvo_pte.lpte.pte_hi & LPTE_VALID) && ((pvo->pvo_pte.lpte.pte_lo & LPTE_PP) == LPTE_RW || (prot & VM_PROT_WRITE) == 0)) { m = PHYS_TO_VM_PAGE(pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN); vm_page_hold(m); } vm_page_unlock_queues(); PMAP_UNLOCK(pmap); return (m); } static void * moea64_uma_page_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int wait) { /* * This entire routine is a horrible hack to avoid bothering kmem * for new KVA addresses. Because this can get called from inside * kmem allocation routines, calling kmem for a new address here * can lead to multiply locking non-recursive mutexes. */ static vm_pindex_t color; vm_offset_t va; vm_page_t m; int pflags, needed_lock; *flags = UMA_SLAB_PRIV; needed_lock = !PMAP_LOCKED(kernel_pmap); if (needed_lock) PMAP_LOCK(kernel_pmap); if ((wait & (M_NOWAIT|M_USE_RESERVE)) == M_NOWAIT) pflags = VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED; else pflags = VM_ALLOC_SYSTEM | VM_ALLOC_WIRED; if (wait & M_ZERO) pflags |= VM_ALLOC_ZERO; for (;;) { m = vm_page_alloc(NULL, color++, pflags | VM_ALLOC_NOOBJ); if (m == NULL) { if (wait & M_NOWAIT) return (NULL); VM_WAIT; } else break; } va = VM_PAGE_TO_PHYS(m); moea64_pvo_enter(kernel_pmap, moea64_upvo_zone, &moea64_pvo_kunmanaged, va, VM_PAGE_TO_PHYS(m), LPTE_M, PVO_WIRED | PVO_BOOTSTRAP); if (needed_lock) PMAP_UNLOCK(kernel_pmap); if ((wait & M_ZERO) && (m->flags & PG_ZERO) == 0) bzero((void *)va, PAGE_SIZE); return (void *)va; } void moea64_init(mmu_t mmu) { CTR0(KTR_PMAP, "moea64_init"); moea64_upvo_zone = uma_zcreate("UPVO entry", sizeof (struct pvo_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); moea64_mpvo_zone = uma_zcreate("MPVO entry", sizeof(struct pvo_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); if (!hw_direct_map) { uma_zone_set_allocf(moea64_upvo_zone,moea64_uma_page_alloc); uma_zone_set_allocf(moea64_mpvo_zone,moea64_uma_page_alloc); } moea64_initialized = TRUE; } boolean_t moea64_is_modified(mmu_t mmu, vm_page_t m) { if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) != 0) return (FALSE); return (moea64_query_bit(m, LPTE_CHG)); } void moea64_clear_reference(mmu_t mmu, vm_page_t m) { if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) != 0) return; moea64_clear_bit(m, LPTE_REF, NULL); } void moea64_clear_modify(mmu_t mmu, vm_page_t m) { if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) != 0) return; moea64_clear_bit(m, LPTE_CHG, NULL); } /* * Clear the write and modified bits in each of the given page's mappings. */ void moea64_remove_write(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; struct lpte *pt; pmap_t pmap; uint64_t lo; mtx_assert(&vm_page_queue_mtx, MA_OWNED); if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) != 0 || (m->flags & PG_WRITEABLE) == 0) return; lo = moea64_attr_fetch(m); SYNC(); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); if ((pvo->pvo_pte.lpte.pte_lo & LPTE_PP) != LPTE_BR) { LOCK_TABLE(); pt = moea64_pvo_to_pte(pvo, -1); pvo->pvo_pte.lpte.pte_lo &= ~LPTE_PP; pvo->pvo_pte.lpte.pte_lo |= LPTE_BR; if (pt != NULL) { moea64_pte_synch(pt, &pvo->pvo_pte.lpte); lo |= pvo->pvo_pte.lpte.pte_lo; pvo->pvo_pte.lpte.pte_lo &= ~LPTE_CHG; moea64_pte_change(pt, &pvo->pvo_pte.lpte, pvo->pvo_pmap, PVO_VADDR(pvo)); } UNLOCK_TABLE(); } PMAP_UNLOCK(pmap); } if ((lo & LPTE_CHG) != 0) { moea64_attr_clear(m, LPTE_CHG); vm_page_dirty(m); } vm_page_flag_clear(m, PG_WRITEABLE); } /* * moea64_ts_referenced: * * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * XXX: The exact number of bits to check and clear is a matter that * should be tested and standardized at some point in the future for * optimal aging of shared pages. */ boolean_t moea64_ts_referenced(mmu_t mmu, vm_page_t m) { int count; if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) != 0) return (0); count = moea64_clear_bit(m, LPTE_REF, NULL); return (count); } /* * Map a wired page into kernel virtual address space. */ void moea64_kenter(mmu_t mmu, vm_offset_t va, vm_offset_t pa) { uint64_t pte_lo; int error; #if 0 if (!pmap_bootstrapped) { if (va >= VM_MIN_KERNEL_ADDRESS && va < virtual_end) panic("Trying to enter an address in KVA -- %#" PRIxPTR "!\n",pa); } #endif pte_lo = moea64_calc_wimg(pa); PMAP_LOCK(kernel_pmap); error = moea64_pvo_enter(kernel_pmap, moea64_upvo_zone, &moea64_pvo_kunmanaged, va, pa, pte_lo, PVO_WIRED | VM_PROT_EXECUTE); if (error != 0 && error != ENOENT) panic("moea64_kenter: failed to enter va %#zx pa %#zx: %d", va, pa, error); /* * Flush the memory from the instruction cache. */ if ((pte_lo & (LPTE_I | LPTE_G)) == 0) { __syncicache((void *)va, PAGE_SIZE); } PMAP_UNLOCK(kernel_pmap); } /* * Extract the physical page address associated with the given kernel virtual * address. */ vm_offset_t moea64_kextract(mmu_t mmu, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; PMAP_LOCK(kernel_pmap); pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF, NULL); KASSERT(pvo != NULL, ("moea64_kextract: no addr found")); pa = (pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN) | (va & ADDR_POFF); PMAP_UNLOCK(kernel_pmap); return (pa); } /* * Remove a wired page from kernel virtual address space. */ void moea64_kremove(mmu_t mmu, vm_offset_t va) { moea64_remove(mmu, kernel_pmap, va, va + PAGE_SIZE); } /* * Map a range of physical addresses into kernel virtual address space. * * The value passed in *virt is a suggested virtual address for the mapping. * Architectures which can support a direct-mapped physical to virtual region * can return the appropriate address within that region, leaving '*virt' * unchanged. We cannot and therefore do not; *virt is updated with the * first usable address after the mapped region. */ vm_offset_t moea64_map(mmu_t mmu, vm_offset_t *virt, vm_offset_t pa_start, vm_offset_t pa_end, int prot) { vm_offset_t sva, va; sva = *virt; va = sva; for (; pa_start < pa_end; pa_start += PAGE_SIZE, va += PAGE_SIZE) moea64_kenter(mmu, va, pa_start); *virt = va; return (sva); } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t moea64_page_exists_quick(mmu_t mmu, pmap_t pmap, vm_page_t m) { int loops; struct pvo_entry *pvo; if (!moea64_initialized || (m->flags & PG_FICTITIOUS)) return FALSE; loops = 0; LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { if (pvo->pvo_pmap == pmap) return (TRUE); if (++loops >= 16) break; } return (FALSE); } /* * Return the number of managed mappings to the given physical page * that are wired. */ int moea64_page_wired_mappings(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; int count; count = 0; if (!moea64_initialized || (m->flags & PG_FICTITIOUS) != 0) return (count); mtx_assert(&vm_page_queue_mtx, MA_OWNED); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) if ((pvo->pvo_vaddr & PVO_WIRED) != 0) count++; return (count); } static uintptr_t moea64_vsidcontext; uintptr_t moea64_get_unique_vsid(void) { u_int entropy; register_t hash; uint32_t mask; int i; entropy = 0; __asm __volatile("mftb %0" : "=r"(entropy)); for (i = 0; i < NVSIDS; i += VSID_NBPW) { u_int n; /* * Create a new value by mutiplying by a prime and adding in * entropy from the timebase register. This is to make the * VSID more random so that the PT hash function collides * less often. (Note that the prime casues gcc to do shifts * instead of a multiply.) */ moea64_vsidcontext = (moea64_vsidcontext * 0x1105) + entropy; hash = moea64_vsidcontext & (NVSIDS - 1); if (hash == 0) /* 0 is special, avoid it */ continue; n = hash >> 5; mask = 1 << (hash & (VSID_NBPW - 1)); hash = (moea64_vsidcontext & VSID_HASHMASK); if (moea64_vsid_bitmap[n] & mask) { /* collision? */ /* anything free in this bucket? */ if (moea64_vsid_bitmap[n] == 0xffffffff) { entropy = (moea64_vsidcontext >> 20); continue; } i = ffs(~moea64_vsid_bitmap[i]) - 1; mask = 1 << i; hash &= VSID_HASHMASK & ~(VSID_NBPW - 1); hash |= i; } moea64_vsid_bitmap[n] |= mask; return (hash); } panic("%s: out of segments",__func__); } void moea64_pinit(mmu_t mmu, pmap_t pmap) { int i; #ifndef __powerpc64__ register_t hash; #endif PMAP_LOCK_INIT(pmap); if (pmap_bootstrapped) pmap->pmap_phys = (pmap_t)moea64_kextract(mmu, (vm_offset_t)pmap); else pmap->pmap_phys = pmap; #ifdef __powerpc64__ /* * 64-bit PowerPC uses lazy segment allocation, so NULL * all the segment entries for now. */ for (i = 0; i < sizeof(pmap->pm_slb)/sizeof(pmap->pm_slb[0]); i++) { pmap->pm_slb[i].slbv = 0; pmap->pm_slb[i].slbe = 0; } #else /* * Allocate some segment registers for this pmap. */ hash = moea64_get_unique_vsid(); for (i = 0; i < 16; i++) pmap->pm_sr[i] = VSID_MAKE(i, hash); #endif } /* * Initialize the pmap associated with process 0. */ void moea64_pinit0(mmu_t mmu, pmap_t pm) { moea64_pinit(mmu, pm); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } /* * Set the physical protection on the specified range of this map as requested. */ void moea64_protect(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { struct pvo_entry *pvo; struct lpte *pt; int pteidx; CTR4(KTR_PMAP, "moea64_protect: pm=%p sva=%#x eva=%#x prot=%#x", pm, sva, eva, prot); KASSERT(pm == &curproc->p_vmspace->vm_pmap || pm == kernel_pmap, ("moea64_protect: non current pmap")); if ((prot & VM_PROT_READ) == VM_PROT_NONE) { moea64_remove(mmu, pm, sva, eva); return; } vm_page_lock_queues(); PMAP_LOCK(pm); for (; sva < eva; sva += PAGE_SIZE) { pvo = moea64_pvo_find_va(pm, sva, &pteidx); if (pvo == NULL) continue; /* * Grab the PTE pointer before we diddle with the cached PTE * copy. */ LOCK_TABLE(); pt = moea64_pvo_to_pte(pvo, pteidx); /* * Change the protection of the page. */ pvo->pvo_pte.lpte.pte_lo &= ~LPTE_PP; pvo->pvo_pte.lpte.pte_lo |= LPTE_BR; pvo->pvo_pte.lpte.pte_lo &= ~LPTE_NOEXEC; if ((prot & VM_PROT_EXECUTE) == 0) pvo->pvo_pte.lpte.pte_lo |= LPTE_NOEXEC; /* * If the PVO is in the page table, update that pte as well. */ if (pt != NULL) { moea64_pte_change(pt, &pvo->pvo_pte.lpte, pvo->pvo_pmap, PVO_VADDR(pvo)); if ((pvo->pvo_pte.lpte.pte_lo & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { moea64_syncicache(pm, sva, pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN, PAGE_SIZE); } } UNLOCK_TABLE(); } vm_page_unlock_queues(); PMAP_UNLOCK(pm); } /* * Map a list of wired pages into kernel virtual address space. This is * intended for temporary mappings which do not need page modification or * references recorded. Existing mappings in the region are overwritten. */ void moea64_qenter(mmu_t mmu, vm_offset_t va, vm_page_t *m, int count) { while (count-- > 0) { moea64_kenter(mmu, va, VM_PAGE_TO_PHYS(*m)); va += PAGE_SIZE; m++; } } /* * Remove page mappings from kernel virtual address space. Intended for * temporary mappings entered by moea64_qenter. */ void moea64_qremove(mmu_t mmu, vm_offset_t va, int count) { while (count-- > 0) { moea64_kremove(mmu, va); va += PAGE_SIZE; } } static __inline void moea64_release_vsid(uint64_t vsid) { int idx, mask; idx = vsid & (NVSIDS-1); mask = 1 << (idx % VSID_NBPW); idx /= VSID_NBPW; moea64_vsid_bitmap[idx] &= ~mask; } void moea64_release(mmu_t mmu, pmap_t pmap) { /* * Free segment registers' VSIDs */ #ifdef __powerpc64__ int i; for (i = 0; i < sizeof(pmap->pm_slb)/sizeof(pmap->pm_slb[0]); i++) moea64_release_vsid(pmap->pm_slb[i].slbv); #else if (pmap->pm_sr[0] == 0) panic("moea64_release"); moea64_release_vsid(pmap->pm_sr[0]); #endif PMAP_LOCK_DESTROY(pmap); } /* * Remove the given range of addresses from the specified map. */ void moea64_remove(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry *pvo; int pteidx; vm_page_lock_queues(); PMAP_LOCK(pm); for (; sva < eva; sva += PAGE_SIZE) { pvo = moea64_pvo_find_va(pm, sva, &pteidx); if (pvo != NULL) { moea64_pvo_remove(pvo, pteidx); } } vm_page_unlock_queues(); PMAP_UNLOCK(pm); } /* * Remove physical page from all pmaps in which it resides. moea64_pvo_remove() * will reflect changes in pte's back to the vm_page. */ void moea64_remove_all(mmu_t mmu, vm_page_t m) { struct pvo_head *pvo_head; struct pvo_entry *pvo, *next_pvo; pmap_t pmap; mtx_assert(&vm_page_queue_mtx, MA_OWNED); pvo_head = vm_page_to_pvoh(m); for (pvo = LIST_FIRST(pvo_head); pvo != NULL; pvo = next_pvo) { next_pvo = LIST_NEXT(pvo, pvo_vlink); MOEA_PVO_CHECK(pvo); /* sanity check */ pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); moea64_pvo_remove(pvo, -1); PMAP_UNLOCK(pmap); } if ((m->flags & PG_WRITEABLE) && moea64_is_modified(mmu, m)) { moea64_attr_clear(m, LPTE_CHG); vm_page_dirty(m); } vm_page_flag_clear(m, PG_WRITEABLE); } /* * Allocate a physical page of memory directly from the phys_avail map. * Can only be called from moea64_bootstrap before avail start and end are * calculated. */ static vm_offset_t moea64_bootstrap_alloc(vm_size_t size, u_int align) { vm_offset_t s, e; int i, j; size = round_page(size); for (i = 0; phys_avail[i + 1] != 0; i += 2) { if (align != 0) s = (phys_avail[i] + align - 1) & ~(align - 1); else s = phys_avail[i]; e = s + size; if (s < phys_avail[i] || e > phys_avail[i + 1]) continue; if (s == phys_avail[i]) { phys_avail[i] += size; } else if (e == phys_avail[i + 1]) { phys_avail[i + 1] -= size; } else { for (j = phys_avail_count * 2; j > i; j -= 2) { phys_avail[j] = phys_avail[j - 2]; phys_avail[j + 1] = phys_avail[j - 1]; } phys_avail[i + 3] = phys_avail[i + 1]; phys_avail[i + 1] = s; phys_avail[i + 2] = e; phys_avail_count++; } return (s); } panic("moea64_bootstrap_alloc: could not allocate memory"); } static void tlbia(void) { vm_offset_t i; #ifndef __powerpc64__ register_t msr, scratch; #endif TLBSYNC(); for (i = 0; i < 0xFF000; i += 0x00001000) { #ifdef __powerpc64__ __asm __volatile("tlbiel %0" :: "r"(i)); #else __asm __volatile("\ mfmsr %0; \ mr %1, %0; \ insrdi %1,%3,1,0; \ mtmsrd %1; \ isync; \ \ tlbiel %2; \ \ mtmsrd %0; \ isync;" : "=r"(msr), "=r"(scratch) : "r"(i), "r"(1)); #endif } EIEIO(); TLBSYNC(); } #ifdef __powerpc64__ static void slbia(void) { __asm __volatile ("slbia"); } #endif static int moea64_pvo_enter(pmap_t pm, uma_zone_t zone, struct pvo_head *pvo_head, vm_offset_t va, vm_offset_t pa, uint64_t pte_lo, int flags) { struct pvo_entry *pvo; uint64_t vsid; int first; u_int ptegidx; int i; int bootstrap; /* * One nasty thing that can happen here is that the UMA calls to * allocate new PVOs need to map more memory, which calls pvo_enter(), * which calls UMA... * * We break the loop by detecting recursion and allocating out of * the bootstrap pool. */ moea64_pvo_enter_calls++; first = 0; bootstrap = (flags & PVO_BOOTSTRAP); if (!moea64_initialized) bootstrap = 1; /* * Compute the PTE Group index. */ va &= ~ADDR_POFF; vsid = va_to_vsid(pm, va); ptegidx = va_to_pteg(vsid, va); /* * Remove any existing mapping for this page. Reuse the pvo entry if * there is a mapping. */ LOCK_TABLE(); LIST_FOREACH(pvo, &moea64_pvo_table[ptegidx], pvo_olink) { if (pvo->pvo_pmap == pm && PVO_VADDR(pvo) == va) { if ((pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN) == pa && (pvo->pvo_pte.lpte.pte_lo & LPTE_PP) == (pte_lo & LPTE_PP)) { UNLOCK_TABLE(); return (0); } moea64_pvo_remove(pvo, -1); break; } } /* * If we aren't overwriting a mapping, try to allocate. */ if (bootstrap) { if (moea64_bpvo_pool_index >= BPVO_POOL_SIZE) { panic("moea64_enter: bpvo pool exhausted, %d, %d, %zd", moea64_bpvo_pool_index, BPVO_POOL_SIZE, BPVO_POOL_SIZE * sizeof(struct pvo_entry)); } pvo = &moea64_bpvo_pool[moea64_bpvo_pool_index]; moea64_bpvo_pool_index++; bootstrap = 1; } else { /* * Note: drop the table around the UMA allocation in * case the UMA allocator needs to manipulate the page * table. The mapping we are working with is already * protected by the PMAP lock. */ UNLOCK_TABLE(); pvo = uma_zalloc(zone, M_NOWAIT); LOCK_TABLE(); } if (pvo == NULL) { UNLOCK_TABLE(); return (ENOMEM); } moea64_pvo_entries++; pvo->pvo_vaddr = va; pvo->pvo_pmap = pm; LIST_INSERT_HEAD(&moea64_pvo_table[ptegidx], pvo, pvo_olink); pvo->pvo_vaddr &= ~ADDR_POFF; if (!(flags & VM_PROT_EXECUTE)) pte_lo |= LPTE_NOEXEC; if (flags & PVO_WIRED) pvo->pvo_vaddr |= PVO_WIRED; if (pvo_head != &moea64_pvo_kunmanaged) pvo->pvo_vaddr |= PVO_MANAGED; if (bootstrap) pvo->pvo_vaddr |= PVO_BOOTSTRAP; if (flags & PVO_FAKE) pvo->pvo_vaddr |= PVO_FAKE; moea64_pte_create(&pvo->pvo_pte.lpte, vsid, va, (uint64_t)(pa) | pte_lo); /* * Remember if the list was empty and therefore will be the first * item. */ if (LIST_FIRST(pvo_head) == NULL) first = 1; LIST_INSERT_HEAD(pvo_head, pvo, pvo_vlink); if (pvo->pvo_vaddr & PVO_WIRED) pm->pm_stats.wired_count++; pm->pm_stats.resident_count++; /* * We hope this succeeds but it isn't required. */ i = moea64_pte_insert(ptegidx, &pvo->pvo_pte.lpte); if (i >= 0) { PVO_PTEGIDX_SET(pvo, i); } else { panic("moea64_pvo_enter: overflow"); moea64_pte_overflow++; } if (pm == kernel_pmap) isync(); UNLOCK_TABLE(); return (first ? ENOENT : 0); } static void moea64_pvo_remove(struct pvo_entry *pvo, int pteidx) { struct lpte *pt; /* * If there is an active pte entry, we need to deactivate it (and * save the ref & cfg bits). */ LOCK_TABLE(); pt = moea64_pvo_to_pte(pvo, pteidx); if (pt != NULL) { moea64_pte_unset(pt, &pvo->pvo_pte.lpte, pvo->pvo_pmap, PVO_VADDR(pvo)); PVO_PTEGIDX_CLR(pvo); } else { moea64_pte_overflow--; } UNLOCK_TABLE(); /* * Update our statistics. */ pvo->pvo_pmap->pm_stats.resident_count--; if (pvo->pvo_vaddr & PVO_WIRED) pvo->pvo_pmap->pm_stats.wired_count--; /* * Save the REF/CHG bits into their cache if the page is managed. */ if ((pvo->pvo_vaddr & (PVO_MANAGED|PVO_FAKE)) == PVO_MANAGED) { struct vm_page *pg; pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN); if (pg != NULL) { moea64_attr_save(pg, pvo->pvo_pte.lpte.pte_lo & (LPTE_REF | LPTE_CHG)); } } /* * Remove this PVO from the PV list. */ LIST_REMOVE(pvo, pvo_vlink); /* * Remove this from the overflow list and return it to the pool * if we aren't going to reuse it. */ LIST_REMOVE(pvo, pvo_olink); if (!(pvo->pvo_vaddr & PVO_BOOTSTRAP)) uma_zfree((pvo->pvo_vaddr & PVO_MANAGED) ? moea64_mpvo_zone : moea64_upvo_zone, pvo); moea64_pvo_entries--; moea64_pvo_remove_calls++; } static __inline int moea64_pvo_pte_index(const struct pvo_entry *pvo, int ptegidx) { - int pteidx; /* * We can find the actual pte entry without searching by grabbing - * the PTEG index from 3 unused bits in pte_lo[11:9] and by + * the PTEG index from 3 unused bits in pvo_vaddr and by * noticing the HID bit. */ - pteidx = ptegidx * 8 + PVO_PTEGIDX_GET(pvo); if (pvo->pvo_pte.lpte.pte_hi & LPTE_HID) - pteidx ^= moea64_pteg_mask * 8; + ptegidx ^= moea64_pteg_mask; - return (pteidx); + return ((ptegidx << 3) | PVO_PTEGIDX_GET(pvo)); } static struct pvo_entry * moea64_pvo_find_va(pmap_t pm, vm_offset_t va, int *pteidx_p) { struct pvo_entry *pvo; int ptegidx; uint64_t vsid; va &= ~ADDR_POFF; vsid = va_to_vsid(pm, va); ptegidx = va_to_pteg(vsid, va); LOCK_TABLE(); LIST_FOREACH(pvo, &moea64_pvo_table[ptegidx], pvo_olink) { if (pvo->pvo_pmap == pm && PVO_VADDR(pvo) == va) { if (pteidx_p) *pteidx_p = moea64_pvo_pte_index(pvo, ptegidx); break; } } UNLOCK_TABLE(); return (pvo); } static struct lpte * moea64_pvo_to_pte(const struct pvo_entry *pvo, int pteidx) { struct lpte *pt; /* * If we haven't been supplied the ptegidx, calculate it. */ if (pteidx == -1) { int ptegidx; uint64_t vsid; vsid = va_to_vsid(pvo->pvo_pmap, PVO_VADDR(pvo)); ptegidx = va_to_pteg(vsid, PVO_VADDR(pvo)); pteidx = moea64_pvo_pte_index(pvo, ptegidx); } pt = &moea64_pteg_table[pteidx >> 3].pt[pteidx & 7]; if ((pvo->pvo_pte.lpte.pte_hi & LPTE_VALID) && !PVO_PTEGIDX_ISSET(pvo)) { panic("moea64_pvo_to_pte: pvo %p has valid pte in pvo but no " "valid pte index", pvo); } if ((pvo->pvo_pte.lpte.pte_hi & LPTE_VALID) == 0 && PVO_PTEGIDX_ISSET(pvo)) { panic("moea64_pvo_to_pte: pvo %p has valid pte index in pvo " "pvo but no valid pte", pvo); } if ((pt->pte_hi ^ (pvo->pvo_pte.lpte.pte_hi & ~LPTE_VALID)) == LPTE_VALID) { if ((pvo->pvo_pte.lpte.pte_hi & LPTE_VALID) == 0) { panic("moea64_pvo_to_pte: pvo %p has valid pte in " "moea64_pteg_table %p but invalid in pvo", pvo, pt); } if (((pt->pte_lo ^ pvo->pvo_pte.lpte.pte_lo) & ~(LPTE_CHG|LPTE_REF)) != 0) { panic("moea64_pvo_to_pte: pvo %p pte does not match " "pte %p in moea64_pteg_table difference is %#x", pvo, pt, (uint32_t)(pt->pte_lo ^ pvo->pvo_pte.lpte.pte_lo)); } ASSERT_TABLE_LOCK(); return (pt); } if (pvo->pvo_pte.lpte.pte_hi & LPTE_VALID) { panic("moea64_pvo_to_pte: pvo %p has invalid pte %p in " "moea64_pteg_table but valid in pvo", pvo, pt); } return (NULL); } static int moea64_pte_insert(u_int ptegidx, struct lpte *pvo_pt) { struct lpte *pt; int i; ASSERT_TABLE_LOCK(); /* * First try primary hash. */ for (pt = moea64_pteg_table[ptegidx].pt, i = 0; i < 8; i++, pt++) { - if ((pt->pte_hi & LPTE_VALID) == 0) { + if ((pt->pte_hi & LPTE_VALID) == 0 && + (pt->pte_hi & LPTE_LOCKED) == 0) { pvo_pt->pte_hi &= ~LPTE_HID; moea64_pte_set(pt, pvo_pt); return (i); } } /* * Now try secondary hash. */ ptegidx ^= moea64_pteg_mask; for (pt = moea64_pteg_table[ptegidx].pt, i = 0; i < 8; i++, pt++) { - if ((pt->pte_hi & LPTE_VALID) == 0) { + if ((pt->pte_hi & LPTE_VALID) == 0 && + (pt->pte_hi & LPTE_LOCKED) == 0) { pvo_pt->pte_hi |= LPTE_HID; moea64_pte_set(pt, pvo_pt); return (i); } } panic("moea64_pte_insert: overflow"); return (-1); } static boolean_t moea64_query_bit(vm_page_t m, u_int64_t ptebit) { struct pvo_entry *pvo; struct lpte *pt; if (moea64_attr_fetch(m) & ptebit) return (TRUE); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { MOEA_PVO_CHECK(pvo); /* sanity check */ /* * See if we saved the bit off. If so, cache it and return * success. */ if (pvo->pvo_pte.lpte.pte_lo & ptebit) { moea64_attr_save(m, ptebit); MOEA_PVO_CHECK(pvo); /* sanity check */ return (TRUE); } } /* * No luck, now go through the hard part of looking at the PTEs * themselves. Sync so that any pending REF/CHG bits are flushed to * the PTEs. */ SYNC(); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { MOEA_PVO_CHECK(pvo); /* sanity check */ /* * See if this pvo has a valid PTE. if so, fetch the * REF/CHG bits from the valid PTE. If the appropriate * ptebit is set, cache it and return success. */ LOCK_TABLE(); pt = moea64_pvo_to_pte(pvo, -1); if (pt != NULL) { moea64_pte_synch(pt, &pvo->pvo_pte.lpte); if (pvo->pvo_pte.lpte.pte_lo & ptebit) { UNLOCK_TABLE(); moea64_attr_save(m, ptebit); MOEA_PVO_CHECK(pvo); /* sanity check */ return (TRUE); } } UNLOCK_TABLE(); } return (FALSE); } static u_int moea64_clear_bit(vm_page_t m, u_int64_t ptebit, u_int64_t *origbit) { u_int count; struct pvo_entry *pvo; struct lpte *pt; uint64_t rv; /* * Clear the cached value. */ rv = moea64_attr_fetch(m); moea64_attr_clear(m, ptebit); /* * Sync so that any pending REF/CHG bits are flushed to the PTEs (so * we can reset the right ones). note that since the pvo entries and * list heads are accessed via BAT0 and are never placed in the page * table, we don't have to worry about further accesses setting the * REF/CHG bits. */ SYNC(); /* * For each pvo entry, clear the pvo's ptebit. If this pvo has a * valid pte clear the ptebit from the valid pte. */ count = 0; LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { MOEA_PVO_CHECK(pvo); /* sanity check */ LOCK_TABLE(); pt = moea64_pvo_to_pte(pvo, -1); if (pt != NULL) { moea64_pte_synch(pt, &pvo->pvo_pte.lpte); if (pvo->pvo_pte.lpte.pte_lo & ptebit) { count++; moea64_pte_clear(pt, pvo->pvo_pmap, PVO_VADDR(pvo), ptebit); } } UNLOCK_TABLE(); rv |= pvo->pvo_pte.lpte.pte_lo; pvo->pvo_pte.lpte.pte_lo &= ~ptebit; MOEA_PVO_CHECK(pvo); /* sanity check */ } if (origbit != NULL) { *origbit = rv; } return (count); } boolean_t moea64_dev_direct_mapped(mmu_t mmu, vm_offset_t pa, vm_size_t size) { return (EFAULT); } /* * Map a set of physical memory pages into the kernel virtual * address space. Return a pointer to where it is mapped. This * routine is intended to be used for mapping device memory, * NOT real memory. */ void * moea64_mapdev(mmu_t mmu, vm_offset_t pa, vm_size_t size) { vm_offset_t va, tmpva, ppa, offset; ppa = trunc_page(pa); offset = pa & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); va = kmem_alloc_nofault(kernel_map, size); if (!va) panic("moea64_mapdev: Couldn't alloc kernel virtual memory"); for (tmpva = va; size > 0;) { moea64_kenter(mmu, tmpva, ppa); size -= PAGE_SIZE; tmpva += PAGE_SIZE; ppa += PAGE_SIZE; } return ((void *)(va + offset)); } void moea64_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) { vm_offset_t base, offset; base = trunc_page(va); offset = va & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); kmem_free(kernel_map, base, size); } static void moea64_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) { struct pvo_entry *pvo; vm_offset_t lim; vm_paddr_t pa; vm_size_t len; PMAP_LOCK(pm); while (sz > 0) { lim = round_page(va); len = MIN(lim - va, sz); pvo = moea64_pvo_find_va(pm, va & ~ADDR_POFF, NULL); if (pvo != NULL) { pa = (pvo->pvo_pte.pte.pte_lo & PTE_RPGN) | (va & ADDR_POFF); moea64_syncicache(pm, va, pa, len); } va += len; sz -= len; } PMAP_UNLOCK(pm); } Index: projects/ppc64/sys/powerpc/include/pte.h =================================================================== --- projects/ppc64/sys/powerpc/include/pte.h (revision 204271) +++ projects/ppc64/sys/powerpc/include/pte.h (revision 204272) @@ -1,258 +1,259 @@ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: pte.h,v 1.2 1998/08/31 14:43:40 tsubai Exp $ * $FreeBSD$ */ #ifndef _MACHINE_PTE_H_ #define _MACHINE_PTE_H_ #if defined(AIM) /* * Page Table Entries */ #ifndef LOCORE /* 32-bit PTE */ struct pte { u_int32_t pte_hi; u_int32_t pte_lo; }; struct pteg { struct pte pt[8]; }; /* 64-bit (long) PTE */ struct lpte { u_int64_t pte_hi; u_int64_t pte_lo; }; struct lpteg { struct lpte pt[8]; }; #endif /* LOCORE */ /* 32-bit PTE definitions */ /* High word: */ #define PTE_VALID 0x80000000 #define PTE_VSID_SHFT 7 #define PTE_HID 0x00000040 #define PTE_API 0x0000003f /* Low word: */ #define PTE_RPGN 0xfffff000 #define PTE_REF 0x00000100 #define PTE_CHG 0x00000080 #define PTE_WIMG 0x00000078 #define PTE_W 0x00000040 #define PTE_I 0x00000020 #define PTE_M 0x00000010 #define PTE_G 0x00000008 #define PTE_PP 0x00000003 #define PTE_SO 0x00000000 /* Super. Only (U: XX, S: RW) */ #define PTE_SW 0x00000001 /* Super. Write-Only (U: RO, S: RW) */ #define PTE_BW 0x00000002 /* Supervisor (U: RW, S: RW) */ #define PTE_BR 0x00000003 /* Both Read Only (U: RO, S: RO) */ #define PTE_RW PTE_BW #define PTE_RO PTE_BR #define PTE_EXEC 0x00000200 /* pseudo bit in attrs; page is exec */ /* 64-bit PTE definitions */ /* High quadword: */ #define LPTE_VSID_SHIFT 12 #define LPTE_API 0x0000000000000F80ULL +#define LPTE_LOCKED 0x0000000000000008ULL #define LPTE_BIG 0x0000000000000004ULL /* 4kb/16Mb page */ #define LPTE_HID 0x0000000000000002ULL #define LPTE_VALID 0x0000000000000001ULL /* Low quadword: */ #define EXTEND_PTE(x) UINT64_C(x) /* make constants 64-bit */ #define LPTE_RPGN 0xfffffffffffff000ULL #define LPTE_REF EXTEND_PTE( PTE_REF ) #define LPTE_CHG EXTEND_PTE( PTE_CHG ) #define LPTE_WIMG EXTEND_PTE( PTE_WIMG ) #define LPTE_W EXTEND_PTE( PTE_W ) #define LPTE_I EXTEND_PTE( PTE_I ) #define LPTE_M EXTEND_PTE( PTE_M ) #define LPTE_G EXTEND_PTE( PTE_G ) #define LPTE_NOEXEC 0x0000000000000004ULL #define LPTE_PP EXTEND_PTE( PTE_PP ) #define LPTE_SO EXTEND_PTE( PTE_SO ) /* Super. Only */ #define LPTE_SW EXTEND_PTE( PTE_SW ) /* Super. Write-Only */ #define LPTE_BW EXTEND_PTE( PTE_BW ) /* Supervisor */ #define LPTE_BR EXTEND_PTE( PTE_BR ) /* Both Read Only */ #define LPTE_RW LPTE_BW #define LPTE_RO LPTE_BR #ifndef LOCORE typedef struct pte pte_t; typedef struct lpte lpte_t; #endif /* LOCORE */ /* * Extract bits from address */ #define ADDR_SR_SHFT 28 #define ADDR_PIDX 0x0ffff000UL #define ADDR_PIDX_SHFT 12 #define ADDR_API_SHFT 22 #define ADDR_API_SHFT64 16 #define ADDR_POFF 0x00000fffUL /* * Bits in DSISR: */ #define DSISR_DIRECT 0x80000000 #define DSISR_NOTFOUND 0x40000000 #define DSISR_PROTECT 0x08000000 #define DSISR_INVRX 0x04000000 #define DSISR_STORE 0x02000000 #define DSISR_DABR 0x00400000 #define DSISR_SEGMENT 0x00200000 #define DSISR_EAR 0x00100000 /* * Bits in SRR1 on ISI: */ #define ISSRR1_NOTFOUND 0x40000000 #define ISSRR1_DIRECT 0x10000000 #define ISSRR1_PROTECT 0x08000000 #define ISSRR1_SEGMENT 0x00200000 #ifdef _KERNEL #ifndef LOCORE extern u_int dsisr(void); #endif /* _KERNEL */ #endif /* LOCORE */ #else #include /* * 1st level - page table directory (pdir) * * pdir consists of 1024 entries, each being a pointer to * second level entity, i.e. the actual page table (ptbl). */ #define PDIR_SHIFT 22 #define PDIR_SIZE (1 << PDIR_SHIFT) /* va range mapped by pdir */ #define PDIR_MASK (~(PDIR_SIZE - 1)) #define PDIR_NENTRIES 1024 /* number of page tables in pdir */ /* Returns pdir entry number for given va */ #define PDIR_IDX(va) ((va) >> PDIR_SHIFT) #define PDIR_ENTRY_SHIFT 2 /* entry size is 2^2 = 4 bytes */ /* * 2nd level - page table (ptbl) * * Page table covers 1024 page table entries. Page * table entry (pte) is 32 bit wide and defines mapping * for a single page. */ #define PTBL_SHIFT PAGE_SHIFT #define PTBL_SIZE PAGE_SIZE /* va range mapped by ptbl entry */ #define PTBL_MASK ((PDIR_SIZE - 1) & ~PAGE_MASK) #define PTBL_NENTRIES 1024 /* number of pages mapped by ptbl */ /* Returns ptbl entry number for given va */ #define PTBL_IDX(va) (((va) & PTBL_MASK) >> PTBL_SHIFT) /* Size of ptbl in pages, 1024 entries, each sizeof(struct pte_entry). */ #define PTBL_PAGES 2 #define PTBL_ENTRY_SHIFT 3 /* entry size is 2^3 = 8 bytes */ /* * Flags for pte_remove() routine. */ #define PTBL_HOLD 0x00000001 /* do not unhold ptbl pages */ #define PTBL_UNHOLD 0x00000002 /* unhold and attempt to free ptbl pages */ #define PTBL_HOLD_FLAG(pmap) (((pmap) == kernel_pmap) ? PTBL_HOLD : PTBL_UNHOLD) /* * Page Table Entry definitions and macros. */ #ifndef LOCORE struct pte { vm_offset_t rpn; uint32_t flags; }; typedef struct pte pte_t; #endif /* RPN mask, TLB0 4K pages */ #define PTE_PA_MASK PAGE_MASK /* PTE bits assigned to MAS2, MAS3 flags */ #define PTE_W MAS2_W #define PTE_I MAS2_I #define PTE_M MAS2_M #define PTE_G MAS2_G #define PTE_MAS2_MASK (MAS2_G | MAS2_M | MAS2_I | MAS2_W) #define PTE_MAS3_SHIFT 8 #define PTE_UX (MAS3_UX << PTE_MAS3_SHIFT) #define PTE_SX (MAS3_SX << PTE_MAS3_SHIFT) #define PTE_UW (MAS3_UW << PTE_MAS3_SHIFT) #define PTE_SW (MAS3_SW << PTE_MAS3_SHIFT) #define PTE_UR (MAS3_UR << PTE_MAS3_SHIFT) #define PTE_SR (MAS3_SR << PTE_MAS3_SHIFT) #define PTE_MAS3_MASK ((MAS3_UX | MAS3_SX | MAS3_UW \ | MAS3_SW | MAS3_UR | MAS3_SR) << PTE_MAS3_SHIFT) /* Other PTE flags */ #define PTE_VALID 0x80000000 /* Valid */ #define PTE_MODIFIED 0x40000000 /* Modified */ #define PTE_WIRED 0x20000000 /* Wired */ #define PTE_MANAGED 0x10000000 /* Managed */ #define PTE_REFERENCED 0x04000000 /* Referenced */ /* Macro argument must of pte_t type. */ #define PTE_PA(pte) ((pte)->rpn & ~PTE_PA_MASK) #define PTE_ISVALID(pte) ((pte)->flags & PTE_VALID) #define PTE_ISWIRED(pte) ((pte)->flags & PTE_WIRED) #define PTE_ISMANAGED(pte) ((pte)->flags & PTE_MANAGED) #define PTE_ISMODIFIED(pte) ((pte)->flags & PTE_MODIFIED) #define PTE_ISREFERENCED(pte) ((pte)->flags & PTE_REFERENCED) #endif /* #elif defined(E500) */ #endif /* _MACHINE_PTE_H_ */ Index: projects/ppc64/sys/powerpc/powermac/smu.c =================================================================== --- projects/ppc64/sys/powerpc/powermac/smu.c (revision 204271) +++ projects/ppc64/sys/powerpc/powermac/smu.c (revision 204272) @@ -1,855 +1,937 @@ /*- * Copyright (c) 2009 Nathan Whitehorn * 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 struct smu_cmd { volatile uint8_t cmd; uint8_t len; uint8_t data[254]; }; struct smu_fan { cell_t reg; cell_t min_rpm; cell_t max_rpm; cell_t unmanaged_rpm; char location[32]; int old_style; int setpoint; }; struct smu_sensor { cell_t reg; char location[32]; enum { SMU_CURRENT_SENSOR, SMU_VOLTAGE_SENSOR, SMU_POWER_SENSOR, SMU_TEMP_SENSOR } type; }; struct smu_softc { device_t sc_dev; struct mtx sc_mtx; struct resource *sc_memr; int sc_memrid; bus_dma_tag_t sc_dmatag; bus_space_tag_t sc_bt; bus_space_handle_t sc_mailbox; struct smu_cmd *sc_cmd; bus_addr_t sc_cmd_phys; bus_dmamap_t sc_cmd_dmamap; struct smu_fan *sc_fans; int sc_nfans; struct smu_sensor *sc_sensors; int sc_nsensors; struct callout sc_fanmgt_callout; time_t sc_lastuserchange; /* Calibration data */ uint16_t sc_cpu_diode_scale; int16_t sc_cpu_diode_offset; uint16_t sc_cpu_volt_scale; int16_t sc_cpu_volt_offset; uint16_t sc_cpu_curr_scale; int16_t sc_cpu_curr_offset; uint16_t sc_slots_pow_scale; int16_t sc_slots_pow_offset; /* Thermal management parameters */ int sc_target_temp; /* Default 55 C */ int sc_critical_temp; /* Default 90 C */ + + struct cdev *sc_leddev; }; /* regular bus attachment functions */ static int smu_probe(device_t); static int smu_attach(device_t); /* cpufreq notification hooks */ static void smu_cpufreq_pre_change(device_t, const struct cf_level *level); static void smu_cpufreq_post_change(device_t, const struct cf_level *level); /* utility functions */ static int smu_run_cmd(device_t dev, struct smu_cmd *cmd); static int smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len); static void smu_attach_fans(device_t dev, phandle_t fanroot); static void smu_attach_sensors(device_t dev, phandle_t sensroot); static void smu_fanmgt_callout(void *xdev); +static void smu_set_sleepled(void *xdev, int onoff); +static int smu_server_mode(SYSCTL_HANDLER_ARGS); /* where to find the doorbell GPIO */ static device_t smu_doorbell = NULL; static device_method_t smu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, smu_probe), DEVMETHOD(device_attach, smu_attach), { 0, 0 }, }; static driver_t smu_driver = { "smu", smu_methods, sizeof(struct smu_softc) }; static devclass_t smu_devclass; DRIVER_MODULE(smu, nexus, smu_driver, smu_devclass, 0, 0); MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information"); #define SMU_MAILBOX 0x8000860c #define SMU_FANMGT_INTERVAL 500 /* ms */ /* Command types */ #define SMU_ADC 0xd8 #define SMU_FAN 0x4a #define SMU_I2C 0x9a #define SMU_I2C_SIMPLE 0x00 #define SMU_I2C_NORMAL 0x01 #define SMU_I2C_COMBINED 0x02 #define SMU_MISC 0xee #define SMU_MISC_GET_DATA 0x02 #define SMU_MISC_LED_CTRL 0x04 #define SMU_POWER 0xaa +#define SMU_POWER_EVENTS 0x8f +#define SMU_PWR_GET_POWERUP 0x00 +#define SMU_PWR_SET_POWERUP 0x01 +#define SMU_PWR_CLR_POWERUP 0x02 +/* Power event types */ +#define SMU_WAKEUP_KEYPRESS 0x01 +#define SMU_WAKEUP_AC_INSERT 0x02 +#define SMU_WAKEUP_AC_CHANGE 0x04 +#define SMU_WAKEUP_RING 0x10 + /* Data blocks */ #define SMU_CPUTEMP_CAL 0x18 #define SMU_CPUVOLT_CAL 0x21 #define SMU_SLOTPW_CAL 0x78 /* Partitions */ #define SMU_PARTITION 0x3e #define SMU_PARTITION_LATEST 0x01 #define SMU_PARTITION_BASE 0x02 #define SMU_PARTITION_UPDATE 0x03 static int smu_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (strcmp(name, "smu") != 0) return (ENXIO); device_set_desc(dev, "Apple System Management Unit"); return (0); } static void smu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct smu_softc *sc = xsc; sc->sc_cmd_phys = segs[0].ds_addr; } static int smu_attach(device_t dev) { struct smu_softc *sc; phandle_t node, child; uint8_t data[12]; sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF); /* * Map the mailbox area. This should be determined from firmware, * but I have not found a simple way to do that. */ bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, NULL, &(sc->sc_dmatag)); sc->sc_bt = &bs_le_tag; bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox); /* * Allocate the command buffer. This can be anywhere in the low 4 GB * of memory. */ bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->sc_cmd_dmamap); bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap, sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0); /* * Set up handlers to change CPU voltage when CPU frequency is changed. */ EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev, EVENTHANDLER_PRI_ANY); /* * Detect and attach child devices. */ node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(child, "name", name, sizeof(name)); if (strncmp(name, "rpm-fans", 9) == 0 || strncmp(name, "fans", 5) == 0) smu_attach_fans(dev, child); if (strncmp(name, "sensors", 8) == 0) smu_attach_sensors(dev, child); } /* * Collect calibration constants. */ smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data)); sc->sc_cpu_diode_scale = (data[4] << 8) + data[5]; sc->sc_cpu_diode_offset = (data[6] << 8) + data[7]; smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data)); sc->sc_cpu_volt_scale = (data[4] << 8) + data[5]; sc->sc_cpu_volt_offset = (data[6] << 8) + data[7]; sc->sc_cpu_curr_scale = (data[8] << 8) + data[9]; sc->sc_cpu_curr_offset = (data[10] << 8) + data[11]; smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data)); sc->sc_slots_pow_scale = (data[4] << 8) + data[5]; sc->sc_slots_pow_offset = (data[6] << 8) + data[7]; /* * Set up simple-minded thermal management. */ sc->sc_target_temp = 55; sc->sc_critical_temp = 90; SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "target_temp", CTLTYPE_INT | CTLFLAG_RW, &sc->sc_target_temp, sizeof(int), "Target temperature (C)"); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "critical_temp", CTLTYPE_INT | CTLFLAG_RW, &sc->sc_critical_temp, sizeof(int), "Critical temperature (C)"); callout_init(&sc->sc_fanmgt_callout, 1); smu_fanmgt_callout(dev); + /* + * Set up LED interface + */ + sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled"); + + /* + * Reset on power loss behavior + */ + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0, + smu_server_mode, "I", "Enable reboot after power failure"); + return (0); } static int smu_run_cmd(device_t dev, struct smu_cmd *cmd) { struct smu_softc *sc; int doorbell_ack, result, oldpow; sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); oldpow = powerpc_pow_enabled; powerpc_pow_enabled = 0; /* Copy the command to the mailbox */ memcpy(sc->sc_cmd, cmd, sizeof(*cmd)); bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE); bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys); /* Flush the cacheline it is in -- SMU bypasses the cache */ __asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory"); /* Ring SMU doorbell */ macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT); /* Wait for the doorbell GPIO to go high, signaling completion */ do { /* XXX: timeout */ DELAY(50); doorbell_ack = macgpio_read(smu_doorbell); } while (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA)); /* Check result. First invalidate the cache again... */ __asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory"); bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD); /* SMU acks the command by inverting the command bits */ if (sc->sc_cmd->cmd == ((~cmd->cmd) & 0xff)) result = 0; else result = EIO; powerpc_pow_enabled = oldpow; memcpy(cmd->data, sc->sc_cmd->data, sizeof(cmd->data)); cmd->len = sc->sc_cmd->len; mtx_unlock(&sc->sc_mtx); return (result); } static int smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len) { struct smu_cmd cmd; uint8_t addr[4]; cmd.cmd = SMU_PARTITION; cmd.len = 2; cmd.data[0] = SMU_PARTITION_LATEST; cmd.data[1] = id; smu_run_cmd(dev, &cmd); addr[0] = addr[1] = 0; addr[2] = cmd.data[0]; addr[3] = cmd.data[1]; cmd.cmd = SMU_MISC; cmd.len = 7; cmd.data[0] = SMU_MISC_GET_DATA; cmd.data[1] = sizeof(addr); memcpy(&cmd.data[2], addr, sizeof(addr)); cmd.data[6] = len; smu_run_cmd(dev, &cmd); memcpy(buf, cmd.data, len); return (0); } static void smu_slew_cpu_voltage(device_t dev, int to) { struct smu_cmd cmd; cmd.cmd = SMU_POWER; cmd.len = 8; cmd.data[0] = 'V'; cmd.data[1] = 'S'; cmd.data[2] = 'L'; cmd.data[3] = 'E'; cmd.data[4] = 'W'; cmd.data[5] = 0xff; cmd.data[6] = 1; cmd.data[7] = to; smu_run_cmd(dev, &cmd); } static void smu_cpufreq_pre_change(device_t dev, const struct cf_level *level) { /* * Make sure the CPU voltage is raised before we raise * the clock. */ if (level->rel_set[0].freq == 10000 /* max */) smu_slew_cpu_voltage(dev, 0); } static void smu_cpufreq_post_change(device_t dev, const struct cf_level *level) { /* We are safe to reduce CPU voltage after a downward transition */ if (level->rel_set[0].freq < 10000 /* max */) smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */ } /* Routines for probing the SMU doorbell GPIO */ static int doorbell_probe(device_t dev); static int doorbell_attach(device_t dev); static device_method_t doorbell_methods[] = { /* Device interface */ DEVMETHOD(device_probe, doorbell_probe), DEVMETHOD(device_attach, doorbell_attach), { 0, 0 }, }; static driver_t doorbell_driver = { "smudoorbell", doorbell_methods, 0 }; static devclass_t doorbell_devclass; DRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0); static int doorbell_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (strcmp(name, "smu-doorbell") != 0) return (ENXIO); device_set_desc(dev, "SMU Doorbell GPIO"); device_quiet(dev); return (0); } static int doorbell_attach(device_t dev) { smu_doorbell = dev; return (0); } /* * Sensor and fan management */ static int smu_fan_set_rpm(device_t smu, struct smu_fan *fan, int rpm) { struct smu_cmd cmd; int error; cmd.cmd = SMU_FAN; error = EIO; /* Clamp to allowed range */ rpm = max(fan->min_rpm, rpm); rpm = min(fan->max_rpm, rpm); /* * Apple has two fan control mechanisms. We can't distinguish * them except by seeing if the new one fails. If the new one * fails, use the old one. */ if (!fan->old_style) { cmd.len = 4; cmd.data[0] = 0x30; cmd.data[1] = fan->reg; cmd.data[2] = (rpm >> 8) & 0xff; cmd.data[3] = rpm & 0xff; error = smu_run_cmd(smu, &cmd); if (error) fan->old_style = 1; } if (fan->old_style) { cmd.len = 14; cmd.data[0] = 0; cmd.data[1] = 1 << fan->reg; cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff; cmd.data[3 + 2*fan->reg] = rpm & 0xff; error = smu_run_cmd(smu, &cmd); } if (error == 0) fan->setpoint = rpm; return (error); } static int smu_fan_read_rpm(device_t smu, struct smu_fan *fan) { struct smu_cmd cmd; cmd.cmd = SMU_FAN; cmd.len = 1; cmd.data[0] = 1; smu_run_cmd(smu, &cmd); return ((cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]); } static int smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) { device_t smu; struct smu_softc *sc; struct smu_fan *fan; int rpm, error; smu = arg1; sc = device_get_softc(smu); fan = &sc->sc_fans[arg2]; rpm = smu_fan_read_rpm(smu, fan); error = sysctl_handle_int(oidp, &rpm, 0, req); if (error || !req->newptr) return (error); sc->sc_lastuserchange = time_uptime; return (smu_fan_set_rpm(smu, fan, rpm)); } static void smu_attach_fans(device_t dev, phandle_t fanroot) { struct smu_fan *fan; struct smu_softc *sc; struct sysctl_oid *oid, *fanroot_oid; struct sysctl_ctx_list *ctx; phandle_t child; char type[32], sysctl_name[32]; int i; sc = device_get_softc(dev); sc->sc_nfans = 0; for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) sc->sc_nfans++; if (sc->sc_nfans == 0) { device_printf(dev, "WARNING: No fans detected!\n"); return; } sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU, M_WAITOK | M_ZERO); fan = sc->sc_fans; sc->sc_nfans = 0; ctx = device_get_sysctl_ctx(dev); fanroot_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans", CTLFLAG_RD, 0, "SMU Fan Information"); for (child = OF_child(fanroot); child != 0; child = OF_peer(child)) { OF_getprop(child, "device_type", type, sizeof(type)); if (strcmp(type, "fan-rpm-control") != 0) continue; fan->old_style = 0; OF_getprop(child, "reg", &fan->reg, sizeof(cell_t)); OF_getprop(child, "min-value", &fan->min_rpm, sizeof(cell_t)); OF_getprop(child, "max-value", &fan->max_rpm, sizeof(cell_t)); if (OF_getprop(child, "unmanaged-value", &fan->unmanaged_rpm, sizeof(cell_t)) != sizeof(cell_t)) fan->unmanaged_rpm = fan->max_rpm; fan->setpoint = smu_fan_read_rpm(dev, fan); OF_getprop(child, "location", fan->location, sizeof(fan->location)); /* Add sysctls */ for (i = 0; i < strlen(fan->location); i++) { sysctl_name[i] = tolower(fan->location[i]); if (isspace(sysctl_name[i])) sysctl_name[i] = '_'; } sysctl_name[i] = 0; oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm", CTLTYPE_INT | CTLFLAG_RD, &fan->min_rpm, sizeof(cell_t), "Minimum allowed RPM"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm", CTLTYPE_INT | CTLFLAG_RD, &fan->max_rpm, sizeof(cell_t), "Maximum allowed RPM"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm", CTLTYPE_INT | CTLFLAG_RW, dev, sc->sc_nfans, smu_fanrpm_sysctl, "I", "Fan RPM"); fan++; sc->sc_nfans++; } } static int smu_sensor_read(device_t smu, struct smu_sensor *sens) { struct smu_cmd cmd; struct smu_softc *sc; int64_t value; cmd.cmd = SMU_ADC; cmd.len = 1; cmd.data[0] = sens->reg; smu_run_cmd(smu, &cmd); sc = device_get_softc(smu); value = (cmd.data[0] << 8) | cmd.data[1]; switch (sens->type) { case SMU_TEMP_SENSOR: value *= sc->sc_cpu_diode_scale; value >>= 3; value += ((int64_t)sc->sc_cpu_diode_offset) << 9; value <<= 1; /* Convert from 16.16 fixed point degC into integer C. */ value *= 15625; value /= 1024; value /= 1000000; break; case SMU_VOLTAGE_SENSOR: value *= sc->sc_cpu_volt_scale; value += sc->sc_cpu_volt_offset; value <<= 4; /* Convert from 16.16 fixed point V into mV. */ value *= 15625; value /= 1024; value /= 1000; break; case SMU_CURRENT_SENSOR: value *= sc->sc_cpu_curr_scale; value += sc->sc_cpu_curr_offset; value <<= 4; /* Convert from 16.16 fixed point A into mA. */ value *= 15625; value /= 1024; value /= 1000; break; case SMU_POWER_SENSOR: value *= sc->sc_slots_pow_scale; value += sc->sc_slots_pow_offset; value <<= 4; /* Convert from 16.16 fixed point W into mW. */ value *= 15625; value /= 1024; value /= 1000; break; } return (value); } static int smu_sensor_sysctl(SYSCTL_HANDLER_ARGS) { device_t smu; struct smu_softc *sc; struct smu_sensor *sens; int value, error; smu = arg1; sc = device_get_softc(smu); sens = &sc->sc_sensors[arg2]; value = smu_sensor_read(smu, sens); error = sysctl_handle_int(oidp, &value, 0, req); return (error); } static void smu_attach_sensors(device_t dev, phandle_t sensroot) { struct smu_sensor *sens; struct smu_softc *sc; struct sysctl_oid *sensroot_oid; struct sysctl_ctx_list *ctx; phandle_t child; char type[32]; int i; sc = device_get_softc(dev); sc->sc_nsensors = 0; for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) sc->sc_nsensors++; if (sc->sc_nsensors == 0) { device_printf(dev, "WARNING: No sensors detected!\n"); return; } sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor), M_SMU, M_WAITOK | M_ZERO); sens = sc->sc_sensors; sc->sc_nsensors = 0; ctx = device_get_sysctl_ctx(dev); sensroot_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors", CTLFLAG_RD, 0, "SMU Sensor Information"); for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) { char sysctl_name[40], sysctl_desc[40]; const char *units; OF_getprop(child, "device_type", type, sizeof(type)); if (strcmp(type, "current-sensor") == 0) { sens->type = SMU_CURRENT_SENSOR; units = "mA"; } else if (strcmp(type, "temp-sensor") == 0) { sens->type = SMU_TEMP_SENSOR; units = "C"; } else if (strcmp(type, "voltage-sensor") == 0) { sens->type = SMU_VOLTAGE_SENSOR; units = "mV"; } else if (strcmp(type, "power-sensor") == 0) { sens->type = SMU_POWER_SENSOR; units = "mW"; } else { continue; } OF_getprop(child, "reg", &sens->reg, sizeof(cell_t)); OF_getprop(child, "location", sens->location, sizeof(sens->location)); for (i = 0; i < strlen(sens->location); i++) { sysctl_name[i] = tolower(sens->location[i]); if (isspace(sysctl_name[i])) sysctl_name[i] = '_'; } sysctl_name[i] = 0; sprintf(sysctl_desc,"%s (%s)", sens->location, units); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO, sysctl_name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sc_nsensors, smu_sensor_sysctl, "I", sysctl_desc); sens++; sc->sc_nsensors++; } } static int ms_to_ticks(int ms) { if (hz > 1000) return ms*(hz/1000); return ms/(1000/hz); } static void smu_fanmgt_callout(void *xdev) { device_t smu = xdev; struct smu_softc *sc; int i, maxtemp, temp, factor; sc = device_get_softc(smu); if (time_uptime - sc->sc_lastuserchange < 3) { /* * If we have heard from a user process in the last 3 seconds, * go away. */ callout_reset(&sc->sc_fanmgt_callout, ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu); return; } maxtemp = 0; for (i = 0; i < sc->sc_nsensors; i++) { if (sc->sc_sensors[i].type != SMU_TEMP_SENSOR) continue; temp = smu_sensor_read(smu, &sc->sc_sensors[i]); if (temp > maxtemp) maxtemp = temp; } if (maxtemp < 10) { /* Bail if no good sensors */ for (i = 0; i < sc->sc_nfans; i++) smu_fan_set_rpm(smu, &sc->sc_fans[i], sc->sc_fans[i].unmanaged_rpm); return; } if (maxtemp > sc->sc_critical_temp) { device_printf(smu, "WARNING: Current system temperature (%d C) " "exceeds critical temperature (%d C)! Shutting down!\n", maxtemp, sc->sc_critical_temp); shutdown_nice(RB_POWEROFF); } if (maxtemp - sc->sc_target_temp > 20) device_printf(smu, "WARNING: Current system temperature (%d C) " "more than 20 degrees over target temperature (%d C)!\n", maxtemp, sc->sc_target_temp); if (maxtemp > sc->sc_target_temp) factor = 110; else if (sc->sc_target_temp - maxtemp > 4) factor = 90; else if (sc->sc_target_temp - maxtemp > 1) factor = 95; else factor = 100; for (i = 0; i < sc->sc_nfans; i++) smu_fan_set_rpm(smu, &sc->sc_fans[i], (sc->sc_fans[i].setpoint * factor) / 100); callout_reset(&sc->sc_fanmgt_callout, ms_to_ticks(SMU_FANMGT_INTERVAL), smu_fanmgt_callout, smu); +} + +static void +smu_set_sleepled(void *xdev, int onoff) +{ + struct smu_cmd cmd; + device_t smu = xdev; + + cmd.cmd = SMU_MISC; + cmd.len = 3; + cmd.data[0] = SMU_MISC_LED_CTRL; + cmd.data[1] = 0; + cmd.data[2] = onoff; + + smu_run_cmd(smu, &cmd); +} + +static int +smu_server_mode(SYSCTL_HANDLER_ARGS) +{ + struct smu_cmd cmd; + u_int server_mode; + device_t smu = arg1; + int error; + + cmd.cmd = SMU_POWER_EVENTS; + cmd.len = 1; + cmd.data[0] = SMU_PWR_GET_POWERUP; + + error = smu_run_cmd(smu, &cmd); + + if (error) + return (error); + + server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0; + + error = sysctl_handle_int(oidp, &server_mode, 0, req); + + if (error || !req->newptr) + return (error); + + if (server_mode == 1) + cmd.data[0] = SMU_PWR_SET_POWERUP; + else if (server_mode == 0) + cmd.data[0] = SMU_PWR_CLR_POWERUP; + else + return (EINVAL); + + cmd.len = 3; + cmd.data[1] = 0; + cmd.data[2] = SMU_WAKEUP_AC_INSERT; + + return (smu_run_cmd(smu, &cmd)); } Index: projects/ppc64/sys/sys/fbio.h =================================================================== --- projects/ppc64/sys/sys/fbio.h (revision 204271) +++ projects/ppc64/sys/sys/fbio.h (revision 204272) @@ -1,556 +1,557 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software developed by the Computer Systems * Engineering group at Lawrence Berkeley Laboratory under DARPA * contract BG 91-66 and contributed to Berkeley. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)fbio.h 8.2 (Berkeley) 10/30/93 * * $FreeBSD$ */ #ifndef _SYS_FBIO_H_ #define _SYS_FBIO_H_ #ifndef _KERNEL #include #endif #include /* * Frame buffer ioctls (from Sprite, trimmed to essentials for X11). */ /* * Frame buffer type codes. */ #define FBTYPE_SUN1BW 0 /* multibus mono */ #define FBTYPE_SUN1COLOR 1 /* multibus color */ #define FBTYPE_SUN2BW 2 /* memory mono */ #define FBTYPE_SUN2COLOR 3 /* color w/rasterop chips */ #define FBTYPE_SUN2GP 4 /* GP1/GP2 */ #define FBTYPE_SUN5COLOR 5 /* RoadRunner accelerator */ #define FBTYPE_SUN3COLOR 6 /* memory color */ #define FBTYPE_MEMCOLOR 7 /* memory 24-bit */ #define FBTYPE_SUN4COLOR 8 /* memory color w/overlay */ #define FBTYPE_NOTSUN1 9 /* reserved for customer */ #define FBTYPE_NOTSUN2 10 /* reserved for customer */ #define FBTYPE_PCIMISC 11 /* (generic) PCI misc. disp. */ #define FBTYPE_SUNFAST_COLOR 12 /* accelerated 8bit */ #define FBTYPE_SUNROP_COLOR 13 /* MEMCOLOR with rop h/w */ #define FBTYPE_SUNFB_VIDEO 14 /* Simple video mixing */ #define FBTYPE_RESERVED5 15 /* reserved, do not use */ #define FBTYPE_RESERVED4 16 /* reserved, do not use */ #define FBTYPE_SUNGP3 17 #define FBTYPE_SUNGT 18 #define FBTYPE_SUNLEO 19 /* zx Leo */ #define FBTYPE_MDA 20 #define FBTYPE_HERCULES 21 #define FBTYPE_CGA 22 #define FBTYPE_EGA 23 #define FBTYPE_VGA 24 #define FBTYPE_PC98 25 #define FBTYPE_TGA 26 #define FBTYPE_TGA2 27 #define FBTYPE_MDICOLOR 28 /* cg14 */ #define FBTYPE_TCXCOLOR 29 /* SUNW,tcx */ #define FBTYPE_CREATOR 30 #define FBTYPE_LASTPLUSONE 31 /* max number of fbs (change as add) */ /* * Frame buffer descriptor as returned by FBIOGTYPE. */ struct fbtype { int fb_type; /* as defined above */ int fb_height; /* in pixels */ int fb_width; /* in pixels */ int fb_depth; /* bits per pixel */ int fb_cmsize; /* size of color map (entries) */ int fb_size; /* total size in bytes */ }; #define FBIOGTYPE _IOR('F', 0, struct fbtype) #ifdef notdef /* * General purpose structure for passing info in and out of frame buffers * (used for gp1) -- unsupported. */ struct fbinfo { int fb_physaddr; /* physical frame buffer address */ int fb_hwwidth; /* fb board width */ int fb_hwheight; /* fb board height */ int fb_addrdelta; /* phys addr diff between boards */ u_char *fb_ropaddr; /* fb virtual addr */ int fb_unit; /* minor devnum of fb */ }; #define FBIOGINFO _IOR('F', 2, struct fbinfo) #endif /* * Color map I/O. */ struct fbcmap { int index; /* first element (0 origin) */ int count; /* number of elements */ u_char *red; /* red color map elements */ u_char *green; /* green color map elements */ u_char *blue; /* blue color map elements */ }; #define FBIOPUTCMAP _IOW('F', 3, struct fbcmap) #define FBIOGETCMAP _IOW('F', 4, struct fbcmap) /* * Set/get attributes. */ #define FB_ATTR_NDEVSPECIFIC 8 /* no. of device specific values */ #define FB_ATTR_NEMUTYPES 4 /* no. of emulation types */ struct fbsattr { int flags; /* flags; see below */ int emu_type; /* emulation type (-1 if unused) */ int dev_specific[FB_ATTR_NDEVSPECIFIC]; /* catchall */ }; #define FB_ATTR_AUTOINIT 1 /* emulation auto init flag */ #define FB_ATTR_DEVSPECIFIC 2 /* dev. specific stuff valid flag */ struct fbgattr { int real_type; /* real device type */ int owner; /* PID of owner, 0 if myself */ struct fbtype fbtype; /* fbtype info for real device */ struct fbsattr sattr; /* see above */ int emu_types[FB_ATTR_NEMUTYPES]; /* possible emulations */ /* (-1 if unused) */ }; #define FBIOSATTR _IOW('F', 5, struct fbsattr) #define FBIOGATTR _IOR('F', 6, struct fbgattr) /* * Video control. */ #define FBVIDEO_OFF 0 #define FBVIDEO_ON 1 #define FBIOSVIDEO _IOW('F', 7, int) #define FBIOGVIDEO _IOR('F', 8, int) /* vertical retrace */ #define FBIOVERTICAL _IO('F', 9) /* * Hardware cursor control (for, e.g., CG6). A rather complex and icky * interface that smells like VMS, but there it is.... */ struct fbcurpos { short x; short y; }; struct fbcursor { short set; /* flags; see below */ short enable; /* nonzero => cursor on, 0 => cursor off */ struct fbcurpos pos; /* position on display */ struct fbcurpos hot; /* hot-spot within cursor */ struct fbcmap cmap; /* cursor color map */ struct fbcurpos size; /* number of valid bits in image & mask */ caddr_t image; /* cursor image bits */ caddr_t mask; /* cursor mask bits */ }; #define FB_CUR_SETCUR 0x01 /* set on/off (i.e., obey fbcursor.enable) */ #define FB_CUR_SETPOS 0x02 /* set position */ #define FB_CUR_SETHOT 0x04 /* set hot-spot */ #define FB_CUR_SETCMAP 0x08 /* set cursor color map */ #define FB_CUR_SETSHAPE 0x10 /* set size & bits */ #define FB_CUR_SETALL (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT | \ FB_CUR_SETCMAP | FB_CUR_SETSHAPE) /* controls for cursor attributes & shape (including position) */ #define FBIOSCURSOR _IOW('F', 24, struct fbcursor) #define FBIOGCURSOR _IOWR('F', 25, struct fbcursor) /* controls for cursor position only */ #define FBIOSCURPOS _IOW('F', 26, struct fbcurpos) #define FBIOGCURPOS _IOW('F', 27, struct fbcurpos) /* get maximum cursor size */ #define FBIOGCURMAX _IOR('F', 28, struct fbcurpos) /* * Video board information */ struct brd_info { u_short accessible_width; /* accessible bytes in scanline */ u_short accessible_height; /* number of accessible scanlines */ u_short line_bytes; /* number of bytes/scanline */ u_short hdb_capable; /* can this thing hardware db? */ u_short vmsize; /* video memory size */ u_char boardrev; /* board revision # */ u_char pad0; u_long pad1; }; #define FBIOGXINFO _IOR('F', 39, struct brd_info) /* * Monitor information */ struct mon_info { u_long mon_type; /* bit array */ #define MON_TYPE_STEREO 0x8 /* stereo display */ #define MON_TYPE_0_OFFSET 0x4 /* black level 0 ire instead of 7.5 */ #define MON_TYPE_OVERSCAN 0x2 /* overscan */ #define MON_TYPE_GRAY 0x1 /* greyscale monitor */ u_long pixfreq; /* pixel frequency in Hz */ u_long hfreq; /* horizontal freq in Hz */ u_long vfreq; /* vertical freq in Hz */ u_long vsync; /* vertical sync in scanlines */ u_long hsync; /* horizontal sync in pixels */ /* these are in pixel units */ u_short hfporch; /* horizontal front porch */ u_short hbporch; /* horizontal back porch */ u_short vfporch; /* vertical front porch */ u_short vbporch; /* vertical back porch */ }; #define FBIOMONINFO _IOR('F', 40, struct mon_info) /* * Color map I/O. */ struct fbcmap_i { unsigned int flags; #define FB_CMAP_BLOCK (1 << 0) /* wait for vertical refresh */ #define FB_CMAP_KERNEL (1 << 1) /* called within kernel */ int id; /* color map id */ int index; /* first element (0 origin) */ int count; /* number of elements */ u_char *red; /* red color map elements */ u_char *green; /* green color map elements */ u_char *blue; /* blue color map elements */ }; #define FBIOPUTCMAPI _IOW('F', 41, struct fbcmap_i) #define FBIOGETCMAPI _IOW('F', 42, struct fbcmap_i) /* The new style frame buffer ioctls. */ /* video mode information block */ struct video_info { int vi_mode; /* mode number, see below */ int vi_flags; #define V_INFO_COLOR (1 << 0) #define V_INFO_GRAPHICS (1 << 1) #define V_INFO_LINEAR (1 << 2) #define V_INFO_VESA (1 << 3) #define V_INFO_NONVGA (1 << 4) int vi_width; int vi_height; int vi_cwidth; int vi_cheight; int vi_depth; int vi_planes; vm_offset_t vi_window; /* physical address */ size_t vi_window_size; size_t vi_window_gran; vm_offset_t vi_buffer; /* physical address */ size_t vi_buffer_size; int vi_mem_model; #define V_INFO_MM_OTHER (-1) #define V_INFO_MM_TEXT 0 #define V_INFO_MM_PLANAR 1 #define V_INFO_MM_PACKED 2 #define V_INFO_MM_DIRECT 3 #define V_INFO_MM_CGA 100 #define V_INFO_MM_HGC 101 #define V_INFO_MM_VGAX 102 /* for MM_PACKED and MM_DIRECT only */ int vi_pixel_size; /* in bytes */ /* for MM_DIRECT only */ int vi_pixel_fields[4]; /* RGB and reserved fields */ int vi_pixel_fsizes[4]; /* reserved */ u_char vi_reserved[64]; vm_offset_t vi_registers; /* physical address */ vm_offset_t vi_registers_size; }; typedef struct video_info video_info_t; /* adapter infromation block */ struct video_adapter { int va_index; int va_type; #define KD_OTHER 0 /* unknown */ #define KD_MONO 1 /* monochrome adapter */ #define KD_HERCULES 2 /* hercules adapter */ #define KD_CGA 3 /* color graphics adapter */ #define KD_EGA 4 /* enhanced graphics adapter */ #define KD_VGA 5 /* video graphics adapter */ #define KD_PC98 6 /* PC-98 display */ #define KD_TGA 7 /* TGA */ #define KD_TGA2 8 /* TGA2 */ char *va_name; int va_unit; int va_minor; int va_flags; #define V_ADP_COLOR (1 << 0) #define V_ADP_MODECHANGE (1 << 1) #define V_ADP_STATESAVE (1 << 2) #define V_ADP_STATELOAD (1 << 3) #define V_ADP_FONT (1 << 4) #define V_ADP_PALETTE (1 << 5) #define V_ADP_BORDER (1 << 6) #define V_ADP_VESA (1 << 7) #define V_ADP_BOOTDISPLAY (1 << 8) #define V_ADP_PROBED (1 << 16) #define V_ADP_INITIALIZED (1 << 17) #define V_ADP_REGISTERED (1 << 18) #define V_ADP_ATTACHED (1 << 19) +#define V_ADP_DAC8 (1 << 20) vm_offset_t va_io_base; int va_io_size; vm_offset_t va_crtc_addr; vm_offset_t va_mem_base; int va_mem_size; vm_offset_t va_window; /* virtual address */ size_t va_window_size; size_t va_window_gran; u_int va_window_orig; vm_offset_t va_buffer; /* virtual address */ size_t va_buffer_size; int va_initial_mode; int va_initial_bios_mode; int va_mode; struct video_info va_info; int va_line_width; struct { int x; int y; } va_disp_start; void *va_token; int va_model; int va_little_bitian; int va_little_endian; int va_buffer_alias; vm_offset_t va_registers; /* virtual address */ vm_offset_t va_registers_size; }; typedef struct video_adapter video_adapter_t; struct video_adapter_info { int va_index; int va_type; char va_name[16]; int va_unit; int va_flags; vm_offset_t va_io_base; int va_io_size; vm_offset_t va_crtc_addr; vm_offset_t va_mem_base; int va_mem_size; vm_offset_t va_window; /* virtual address */ size_t va_window_size; size_t va_window_gran; vm_offset_t va_unused0; size_t va_buffer_size; int va_initial_mode; int va_initial_bios_mode; int va_mode; int va_line_width; struct { int x; int y; } va_disp_start; u_int va_window_orig; /* reserved */ u_char va_reserved[64]; }; typedef struct video_adapter_info video_adapter_info_t; /* some useful video adapter index */ #define V_ADP_PRIMARY 0 #define V_ADP_SECONDARY 1 /* video mode numbers */ #define M_B40x25 0 /* black & white 40 columns */ #define M_C40x25 1 /* color 40 columns */ #define M_B80x25 2 /* black & white 80 columns */ #define M_C80x25 3 /* color 80 columns */ #define M_BG320 4 /* black & white graphics 320x200 */ #define M_CG320 5 /* color graphics 320x200 */ #define M_BG640 6 /* black & white graphics 640x200 hi-res */ #define M_EGAMONO80x25 7 /* ega-mono 80x25 */ #define M_CG320_D 13 /* ega mode D */ #define M_CG640_E 14 /* ega mode E */ #define M_EGAMONOAPA 15 /* ega mode F */ #define M_CG640x350 16 /* ega mode 10 */ #define M_ENHMONOAPA2 17 /* ega mode F with extended memory */ #define M_ENH_CG640 18 /* ega mode 10* */ #define M_ENH_B40x25 19 /* ega enhanced black & white 40 columns */ #define M_ENH_C40x25 20 /* ega enhanced color 40 columns */ #define M_ENH_B80x25 21 /* ega enhanced black & white 80 columns */ #define M_ENH_C80x25 22 /* ega enhanced color 80 columns */ #define M_VGA_C40x25 23 /* vga 8x16 font on color */ #define M_VGA_C80x25 24 /* vga 8x16 font on color */ #define M_VGA_M80x25 25 /* vga 8x16 font on mono */ #define M_VGA11 26 /* vga 640x480 2 colors */ #define M_BG640x480 26 #define M_VGA12 27 /* vga 640x480 16 colors */ #define M_CG640x480 27 #define M_VGA13 28 /* vga 320x200 256 colors */ #define M_VGA_CG320 28 #define M_VGA_C80x50 30 /* vga 8x8 font on color */ #define M_VGA_M80x50 31 /* vga 8x8 font on color */ #define M_VGA_C80x30 32 /* vga 8x16 font on color */ #define M_VGA_M80x30 33 /* vga 8x16 font on color */ #define M_VGA_C80x60 34 /* vga 8x8 font on color */ #define M_VGA_M80x60 35 /* vga 8x8 font on color */ #define M_VGA_CG640 36 /* vga 640x400 256 color */ #define M_VGA_MODEX 37 /* vga 320x240 256 color */ #define M_VGA_C90x25 40 /* vga 8x16 font on color */ #define M_VGA_M90x25 41 /* vga 8x16 font on mono */ #define M_VGA_C90x30 42 /* vga 8x16 font on color */ #define M_VGA_M90x30 43 /* vga 8x16 font on mono */ #define M_VGA_C90x43 44 /* vga 8x8 font on color */ #define M_VGA_M90x43 45 /* vga 8x8 font on mono */ #define M_VGA_C90x50 46 /* vga 8x8 font on color */ #define M_VGA_M90x50 47 /* vga 8x8 font on mono */ #define M_VGA_C90x60 48 /* vga 8x8 font on color */ #define M_VGA_M90x60 49 /* vga 8x8 font on mono */ #define M_ENH_B80x43 0x70 /* ega black & white 80x43 */ #define M_ENH_C80x43 0x71 /* ega color 80x43 */ #define M_PC98_80x25 98 /* PC98 text 80x25 */ #define M_PC98_80x30 99 /* PC98 text 80x30 */ #define M_PC98_EGC640x400 100 /* PC98 graphic 640x400 16 colors */ #define M_PC98_PEGC640x400 101 /* PC98 graphic 640x400 256 colors */ #define M_PC98_PEGC640x480 102 /* PC98 graphic 640x480 256 colors */ #define M_HGC_P0 0xe0 /* hercules graphics - page 0 @ B0000 */ #define M_HGC_P1 0xe1 /* hercules graphics - page 1 @ B8000 */ #define M_MCA_MODE 0xff /* monochrome adapter mode */ #define M_TEXT_80x25 200 /* generic text modes */ #define M_TEXT_80x30 201 #define M_TEXT_80x43 202 #define M_TEXT_80x50 203 #define M_TEXT_80x60 204 #define M_TEXT_132x25 205 #define M_TEXT_132x30 206 #define M_TEXT_132x43 207 #define M_TEXT_132x50 208 #define M_TEXT_132x60 209 #define M_VESA_BASE 0x100 /* VESA mode number base */ #define M_VESA_CG640x400 0x100 /* 640x400, 256 color */ #define M_VESA_CG640x480 0x101 /* 640x480, 256 color */ #define M_VESA_800x600 0x102 /* 800x600, 16 color */ #define M_VESA_CG800x600 0x103 /* 800x600, 256 color */ #define M_VESA_1024x768 0x104 /* 1024x768, 16 color */ #define M_VESA_CG1024x768 0x105 /* 1024x768, 256 color */ #define M_VESA_1280x1024 0x106 /* 1280x1024, 16 color */ #define M_VESA_CG1280x1024 0x107 /* 1280x1024, 256 color */ #define M_VESA_C80x60 0x108 /* 8x8 font */ #define M_VESA_C132x25 0x109 /* 8x16 font */ #define M_VESA_C132x43 0x10a /* 8x14 font */ #define M_VESA_C132x50 0x10b /* 8x8 font */ #define M_VESA_C132x60 0x10c /* 8x8 font */ #define M_VESA_32K_320 0x10d /* 320x200, 5:5:5 */ #define M_VESA_64K_320 0x10e /* 320x200, 5:6:5 */ #define M_VESA_FULL_320 0x10f /* 320x200, 8:8:8 */ #define M_VESA_32K_640 0x110 /* 640x480, 5:5:5 */ #define M_VESA_64K_640 0x111 /* 640x480, 5:6:5 */ #define M_VESA_FULL_640 0x112 /* 640x480, 8:8:8 */ #define M_VESA_32K_800 0x113 /* 800x600, 5:5:5 */ #define M_VESA_64K_800 0x114 /* 800x600, 5:6:5 */ #define M_VESA_FULL_800 0x115 /* 800x600, 8:8:8 */ #define M_VESA_32K_1024 0x116 /* 1024x768, 5:5:5 */ #define M_VESA_64K_1024 0x117 /* 1024x768, 5:6:5 */ #define M_VESA_FULL_1024 0x118 /* 1024x768, 8:8:8 */ #define M_VESA_32K_1280 0x119 /* 1280x1024, 5:5:5 */ #define M_VESA_64K_1280 0x11a /* 1280x1024, 5:6:5 */ #define M_VESA_FULL_1280 0x11b /* 1280x1024, 8:8:8 */ #define M_VESA_MODE_MAX 0x1ff struct video_display_start { int x; int y; }; typedef struct video_display_start video_display_start_t; struct video_color_palette { int index; /* first element (zero-based) */ int count; /* number of elements */ u_char *red; /* red */ u_char *green; /* green */ u_char *blue; /* blue */ u_char *transparent; /* may be NULL */ }; typedef struct video_color_palette video_color_palette_t; /* adapter info. */ #define FBIO_ADAPTER _IOR('F', 100, int) #define FBIO_ADPTYPE _IOR('F', 101, int) #define FBIO_ADPINFO _IOR('F', 102, struct video_adapter_info) /* video mode control */ #define FBIO_MODEINFO _IOWR('F', 103, struct video_info) #define FBIO_FINDMODE _IOWR('F', 104, struct video_info) #define FBIO_GETMODE _IOR('F', 105, int) #define FBIO_SETMODE _IOW('F', 106, int) /* get/set frame buffer window origin */ #define FBIO_GETWINORG _IOR('F', 107, u_int) #define FBIO_SETWINORG _IOW('F', 108, u_int) /* get/set display start address */ #define FBIO_GETDISPSTART _IOR('F', 109, video_display_start_t) #define FBIO_SETDISPSTART _IOW('F', 110, video_display_start_t) /* get/set scan line width */ #define FBIO_GETLINEWIDTH _IOR('F', 111, u_int) #define FBIO_SETLINEWIDTH _IOW('F', 112, u_int) /* color palette control */ #define FBIO_GETPALETTE _IOW('F', 113, video_color_palette_t) #define FBIO_SETPALETTE _IOW('F', 114, video_color_palette_t) /* blank display */ #define V_DISPLAY_ON 0 #define V_DISPLAY_BLANK 1 #define V_DISPLAY_STAND_BY 2 #define V_DISPLAY_SUSPEND 3 #define FBIO_BLANK _IOW('F', 115, int) #endif /* !_SYS_FBIO_H_ */ Index: projects/ppc64/sys =================================================================== --- projects/ppc64/sys (revision 204271) +++ projects/ppc64/sys (revision 204272) Property changes on: projects/ppc64/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r204217-204271 Index: projects/ppc64/usr.sbin/cxgbtool/cxgbtool.c =================================================================== --- projects/ppc64/usr.sbin/cxgbtool/cxgbtool.c (revision 204271) +++ projects/ppc64/usr.sbin/cxgbtool/cxgbtool.c (revision 204272) @@ -1,1543 +1,1594 @@ /************************************************************************** Copyright (c) 2007-2009, Chelsio 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. 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. Neither the name of the Chelsio Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 #define NMTUS 16 #define TCB_SIZE 128 #define TCB_WORDS (TCB_SIZE / 4) #define PROTO_SRAM_LINES 128 #define PROTO_SRAM_LINE_BITS 132 #define PROTO_SRAM_LINE_NIBBLES (132 / 4) #define PROTO_SRAM_SIZE (PROTO_SRAM_LINE_NIBBLES * PROTO_SRAM_LINES / 2) #define PROTO_SRAM_EEPROM_ADDR 4096 #include #include #include "version.h" struct reg_info { const char *name; uint16_t addr; uint16_t len; }; #include "reg_defs.c" #if defined(CONFIG_T3_REGS) # include "reg_defs_t3.c" # include "reg_defs_t3b.c" # include "reg_defs_t3c.c" #endif static const char *progname; -static void __attribute__((noreturn)) usage(FILE *fp) +static void +usage(FILE *fp) { fprintf(fp, "Usage: %s [operation]\n", progname); fprintf(fp, "\tclearstats clear MAC statistics\n" "\tcontext show an SGE context\n" "\tdesc [] dump SGE descriptors\n" "\tioqs dump uP IOQs\n" "\tla dump uP logic analyzer info\n" "\tloadboot download boot image\n" "\tloadfw download firmware\n" "\tmdio \n" "\t [] read/write MDIO register\n" "\tmemdump cm|tx|rx dump a mem range\n" "\tmeminfo show memory info\n" "\tmtus [...] read/write MTU table\n" "\tpktsched port set TX port scheduler params\n" "\tpktsched tunnelq \n" "\t set TX tunnelq scheduler params\n" "\tpktsched tx \n" "\t [ ] ... set Tx HW scheduler\n" "\tpm [ ] read/write PM config\n" "\tproto read proto SRAM\n" "\tqset read qset parameters\n" "\tqsets read # of qsets\n" "\treg
[=] read/write register\n" "\tregdump [] dump registers\n" "\ttcamdump
show TCAM contents\n" "\ttcb read TCB\n" "\ttrace tx|rx|all on|off [not]\n" "\t [ [:]] ... write trace parameters\n" ); exit(fp == stderr ? 1 : 0); } static int doit(const char *iff_name, unsigned long cmd, void *data) { static int fd = 0; if (fd == 0) { char buf[64]; snprintf(buf, 64, "/dev/%s", iff_name); if ((fd = open(buf, O_RDWR)) < 0) return -1; } return ioctl(fd, cmd, data) < 0 ? -1 : 0; } -static int get_int_arg(const char *s, uint32_t *valp) +static int +get_int_arg(const char *s, uint32_t *valp) { char *p; *valp = strtoul(s, &p, 0); if (*p) { warnx("bad parameter \"%s\"", s); return -1; } return 0; } static uint32_t read_reg(const char *iff_name, uint32_t addr) { struct ch_reg reg; reg.addr = addr; if (doit(iff_name, CHELSIO_GETREG, ®) < 0) err(1, "register read"); return reg.val; } static void write_reg(const char *iff_name, uint32_t addr, uint32_t val) { struct ch_reg ch_reg; ch_reg.addr = addr; ch_reg.val = val; if (doit(iff_name, CHELSIO_SETREG, &ch_reg) < 0) err(1, "register write"); } -static int register_io(int argc, char *argv[], int start_arg, +static int +register_io(int argc, char *argv[], int start_arg, const char *iff_name) { char *p; - uint32_t addr, val = 0, write = 0; + uint32_t addr, val = 0, w = 0; if (argc != start_arg + 1) return -1; addr = strtoul(argv[start_arg], &p, 0); if (p == argv[start_arg]) return -1; if (*p == '=' && p[1]) { val = strtoul(p + 1, &p, 0); - write = 1; + w = 1; } if (*p) { warnx("bad parameter \"%s\"", argv[start_arg]); return -1; } - if (write) + if (w) write_reg(iff_name, addr, val); else { val = read_reg(iff_name, addr); printf("%#x [%u]\n", val, val); } return 0; } -static int mdio_io(int argc, char *argv[], int start_arg, const char *iff_name) +static int +mdio_io(int argc, char *argv[], int start_arg, const char *iff_name) { - struct ifreq ifr; struct ch_mii_data p; unsigned int cmd, phy_addr, reg, mmd, val; if (argc == start_arg + 3) cmd = CHELSIO_GET_MIIREG; else if (argc == start_arg + 4) cmd = CHELSIO_SET_MIIREG; else return -1; if (get_int_arg(argv[start_arg], &phy_addr) || get_int_arg(argv[start_arg + 1], &mmd) || get_int_arg(argv[start_arg + 2], ®) || (cmd == CHELSIO_SET_MIIREG && get_int_arg(argv[start_arg + 3], &val))) return -1; p.phy_id = phy_addr | (mmd << 8); p.reg_num = reg; p.val_in = val; if (doit(iff_name, cmd, &p) < 0) err(1, "MDIO %s", cmd == CHELSIO_GET_MIIREG ? "read" : "write"); if (cmd == CHELSIO_GET_MIIREG) printf("%#x [%u]\n", p.val_out, p.val_out); return 0; } -static inline uint32_t xtract(uint32_t val, int shift, int len) +static inline +uint32_t xtract(uint32_t val, int shift, int len) { return (val >> shift) & ((1 << len) - 1); } -static int dump_block_regs(const struct reg_info *reg_array, uint32_t *regs) +static int +dump_block_regs(const struct reg_info *reg_array, uint32_t *regs) { uint32_t reg_val = 0; // silence compiler warning for ( ; reg_array->name; ++reg_array) if (!reg_array->len) { reg_val = regs[reg_array->addr / 4]; printf("[%#5x] %-40s %#-10x [%u]\n", reg_array->addr, reg_array->name, reg_val, reg_val); } else { uint32_t v = xtract(reg_val, reg_array->addr, reg_array->len); printf(" %-40s %#-10x [%u]\n", reg_array->name, v, v); } return 1; } -static int dump_regs_t2(int argc, char *argv[], int start_arg, uint32_t *regs) +static int +dump_regs_t2(int argc, char *argv[], int start_arg, uint32_t *regs) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(sge_regs, regs); if (!block_name || !strcmp(block_name, "mc3")) match += dump_block_regs(mc3_regs, regs); if (!block_name || !strcmp(block_name, "mc4")) match += dump_block_regs(mc4_regs, regs); if (!block_name || !strcmp(block_name, "tpi")) match += dump_block_regs(tpi_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(tp_regs, regs); if (!block_name || !strcmp(block_name, "rat")) match += dump_block_regs(rat_regs, regs); if (!block_name || !strcmp(block_name, "cspi")) match += dump_block_regs(cspi_regs, regs); if (!block_name || !strcmp(block_name, "espi")) match += dump_block_regs(espi_regs, regs); if (!block_name || !strcmp(block_name, "ulp")) match += dump_block_regs(ulp_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(pl_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(mc5_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } #if defined(CONFIG_T3_REGS) -static int dump_regs_t3(int argc, char *argv[], int start_arg, uint32_t *regs, - int is_pcie) +static int +dump_regs_t3(int argc, char *argv[], int start_arg, uint32_t *regs, int is_pcie) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(sge3_regs, regs); if (!block_name || !strcmp(block_name, "pci")) match += dump_block_regs(is_pcie ? pcie0_regs : pcix1_regs, regs); if (!block_name || !strcmp(block_name, "t3dbg")) match += dump_block_regs(t3dbg_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(mc7_pmrx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(mc7_pmtx_regs, regs); if (!block_name || !strcmp(block_name, "cm")) match += dump_block_regs(mc7_cm_regs, regs); if (!block_name || !strcmp(block_name, "cim")) match += dump_block_regs(cim_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(tp1_regs, regs); if (!block_name || !strcmp(block_name, "ulp_rx")) match += dump_block_regs(ulp2_rx_regs, regs); if (!block_name || !strcmp(block_name, "ulp_tx")) match += dump_block_regs(ulp2_tx_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(pm1_rx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(pm1_tx_regs, regs); if (!block_name || !strcmp(block_name, "mps")) match += dump_block_regs(mps0_regs, regs); if (!block_name || !strcmp(block_name, "cplsw")) match += dump_block_regs(cpl_switch_regs, regs); if (!block_name || !strcmp(block_name, "smb")) match += dump_block_regs(smb0_regs, regs); if (!block_name || !strcmp(block_name, "i2c")) match += dump_block_regs(i2cm0_regs, regs); if (!block_name || !strcmp(block_name, "mi1")) match += dump_block_regs(mi1_regs, regs); if (!block_name || !strcmp(block_name, "sf")) match += dump_block_regs(sf1_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(pl3_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(mc5a_regs, regs); if (!block_name || !strcmp(block_name, "xgmac0")) match += dump_block_regs(xgmac0_0_regs, regs); if (!block_name || !strcmp(block_name, "xgmac1")) match += dump_block_regs(xgmac0_1_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } -static int dump_regs_t3b(int argc, char *argv[], int start_arg, uint32_t *regs, - int is_pcie) +static int +dump_regs_t3b(int argc, char *argv[], int start_arg, uint32_t *regs, + int is_pcie) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(t3b_sge3_regs, regs); if (!block_name || !strcmp(block_name, "pci")) match += dump_block_regs(is_pcie ? t3b_pcie0_regs : t3b_pcix1_regs, regs); if (!block_name || !strcmp(block_name, "t3dbg")) match += dump_block_regs(t3b_t3dbg_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3b_mc7_pmrx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3b_mc7_pmtx_regs, regs); if (!block_name || !strcmp(block_name, "cm")) match += dump_block_regs(t3b_mc7_cm_regs, regs); if (!block_name || !strcmp(block_name, "cim")) match += dump_block_regs(t3b_cim_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(t3b_tp1_regs, regs); if (!block_name || !strcmp(block_name, "ulp_rx")) match += dump_block_regs(t3b_ulp2_rx_regs, regs); if (!block_name || !strcmp(block_name, "ulp_tx")) match += dump_block_regs(t3b_ulp2_tx_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3b_pm1_rx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3b_pm1_tx_regs, regs); if (!block_name || !strcmp(block_name, "mps")) match += dump_block_regs(t3b_mps0_regs, regs); if (!block_name || !strcmp(block_name, "cplsw")) match += dump_block_regs(t3b_cpl_switch_regs, regs); if (!block_name || !strcmp(block_name, "smb")) match += dump_block_regs(t3b_smb0_regs, regs); if (!block_name || !strcmp(block_name, "i2c")) match += dump_block_regs(t3b_i2cm0_regs, regs); if (!block_name || !strcmp(block_name, "mi1")) match += dump_block_regs(t3b_mi1_regs, regs); if (!block_name || !strcmp(block_name, "sf")) match += dump_block_regs(t3b_sf1_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(t3b_pl3_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(t3b_mc5a_regs, regs); if (!block_name || !strcmp(block_name, "xgmac0")) match += dump_block_regs(t3b_xgmac0_0_regs, regs); if (!block_name || !strcmp(block_name, "xgmac1")) match += dump_block_regs(t3b_xgmac0_1_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } -static int dump_regs_t3c(int argc, char *argv[], int start_arg, uint32_t *regs, - int is_pcie) +static int +dump_regs_t3c(int argc, char *argv[], int start_arg, uint32_t *regs, + int is_pcie) { int match = 0; char *block_name = NULL; if (argc == start_arg + 1) block_name = argv[start_arg]; else if (argc != start_arg) return -1; if (!block_name || !strcmp(block_name, "sge")) match += dump_block_regs(t3c_sge3_regs, regs); if (!block_name || !strcmp(block_name, "pci")) match += dump_block_regs(is_pcie ? t3c_pcie0_regs : t3c_pcix1_regs, regs); if (!block_name || !strcmp(block_name, "t3dbg")) match += dump_block_regs(t3c_t3dbg_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3c_mc7_pmrx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3c_mc7_pmtx_regs, regs); if (!block_name || !strcmp(block_name, "cm")) match += dump_block_regs(t3c_mc7_cm_regs, regs); if (!block_name || !strcmp(block_name, "cim")) match += dump_block_regs(t3c_cim_regs, regs); if (!block_name || !strcmp(block_name, "tp")) match += dump_block_regs(t3c_tp1_regs, regs); if (!block_name || !strcmp(block_name, "ulp_rx")) match += dump_block_regs(t3c_ulp2_rx_regs, regs); if (!block_name || !strcmp(block_name, "ulp_tx")) match += dump_block_regs(t3c_ulp2_tx_regs, regs); if (!block_name || !strcmp(block_name, "pmrx")) match += dump_block_regs(t3c_pm1_rx_regs, regs); if (!block_name || !strcmp(block_name, "pmtx")) match += dump_block_regs(t3c_pm1_tx_regs, regs); if (!block_name || !strcmp(block_name, "mps")) match += dump_block_regs(t3c_mps0_regs, regs); if (!block_name || !strcmp(block_name, "cplsw")) match += dump_block_regs(t3c_cpl_switch_regs, regs); if (!block_name || !strcmp(block_name, "smb")) match += dump_block_regs(t3c_smb0_regs, regs); if (!block_name || !strcmp(block_name, "i2c")) match += dump_block_regs(t3c_i2cm0_regs, regs); if (!block_name || !strcmp(block_name, "mi1")) match += dump_block_regs(t3c_mi1_regs, regs); if (!block_name || !strcmp(block_name, "sf")) match += dump_block_regs(t3c_sf1_regs, regs); if (!block_name || !strcmp(block_name, "pl")) match += dump_block_regs(t3c_pl3_regs, regs); if (!block_name || !strcmp(block_name, "mc5")) match += dump_block_regs(t3c_mc5a_regs, regs); if (!block_name || !strcmp(block_name, "xgmac0")) match += dump_block_regs(t3c_xgmac0_0_regs, regs); if (!block_name || !strcmp(block_name, "xgmac1")) match += dump_block_regs(t3c_xgmac0_1_regs, regs); if (!match) errx(1, "unknown block \"%s\"", block_name); return 0; } #endif static int dump_regs(int argc, char *argv[], int start_arg, const char *iff_name) { - int i, vers, revision, is_pcie; + int vers, revision, is_pcie; struct ch_ifconf_regs regs; regs.len = REGDUMP_SIZE; /* XXX: This is never freed. Looks like we don't care. */ if ((regs.data = malloc(regs.len)) == NULL) err(1, "can't malloc"); if (doit(iff_name, CHELSIO_IFCONF_GETREGS, ®s)) err(1, "can't read registers"); vers = regs.version & 0x3ff; revision = (regs.version >> 10) & 0x3f; is_pcie = (regs.version & 0x80000000) != 0; if (vers <= 2) return dump_regs_t2(argc, argv, start_arg, (uint32_t *)regs.data); #if defined(CONFIG_T3_REGS) if (vers == 3) { if (revision == 0) return dump_regs_t3(argc, argv, start_arg, (uint32_t *)regs.data, is_pcie); if (revision == 2 || revision == 3) return dump_regs_t3b(argc, argv, start_arg, (uint32_t *)regs.data, is_pcie); if (revision == 4) return dump_regs_t3c(argc, argv, start_arg, (uint32_t *)regs.data, is_pcie); } #endif errx(1, "unknown card type %d.%d", vers, revision); return 0; } -static int t3_meminfo(const uint32_t *regs) +static int +t3_meminfo(const uint32_t *regs) { enum { SG_EGR_CNTX_BADDR = 0x58, SG_CQ_CONTEXT_BADDR = 0x6c, CIM_SDRAM_BASE_ADDR = 0x28c, CIM_SDRAM_ADDR_SIZE = 0x290, TP_CMM_MM_BASE = 0x314, TP_CMM_TIMER_BASE = 0x318, TP_CMM_MM_RX_FLST_BASE = 0x460, TP_CMM_MM_TX_FLST_BASE = 0x464, TP_CMM_MM_PS_FLST_BASE = 0x468, ULPRX_ISCSI_LLIMIT = 0x50c, ULPRX_ISCSI_ULIMIT = 0x510, ULPRX_TDDP_LLIMIT = 0x51c, ULPRX_TDDP_ULIMIT = 0x520, ULPRX_STAG_LLIMIT = 0x52c, ULPRX_STAG_ULIMIT = 0x530, ULPRX_RQ_LLIMIT = 0x534, ULPRX_RQ_ULIMIT = 0x538, ULPRX_PBL_LLIMIT = 0x53c, ULPRX_PBL_ULIMIT = 0x540, }; unsigned int egr_cntxt = regs[SG_EGR_CNTX_BADDR / 4], cq_cntxt = regs[SG_CQ_CONTEXT_BADDR / 4], timers = regs[TP_CMM_TIMER_BASE / 4] & 0xfffffff, pstructs = regs[TP_CMM_MM_BASE / 4], pstruct_fl = regs[TP_CMM_MM_PS_FLST_BASE / 4], rx_fl = regs[TP_CMM_MM_RX_FLST_BASE / 4], tx_fl = regs[TP_CMM_MM_TX_FLST_BASE / 4], cim_base = regs[CIM_SDRAM_BASE_ADDR / 4], cim_size = regs[CIM_SDRAM_ADDR_SIZE / 4]; unsigned int iscsi_ll = regs[ULPRX_ISCSI_LLIMIT / 4], iscsi_ul = regs[ULPRX_ISCSI_ULIMIT / 4], tddp_ll = regs[ULPRX_TDDP_LLIMIT / 4], tddp_ul = regs[ULPRX_TDDP_ULIMIT / 4], stag_ll = regs[ULPRX_STAG_LLIMIT / 4], stag_ul = regs[ULPRX_STAG_ULIMIT / 4], rq_ll = regs[ULPRX_RQ_LLIMIT / 4], rq_ul = regs[ULPRX_RQ_ULIMIT / 4], pbl_ll = regs[ULPRX_PBL_LLIMIT / 4], pbl_ul = regs[ULPRX_PBL_ULIMIT / 4]; printf("CM memory map:\n"); printf(" TCB region: 0x%08x - 0x%08x [%u]\n", 0, egr_cntxt - 1, egr_cntxt); printf(" Egress contexts: 0x%08x - 0x%08x [%u]\n", egr_cntxt, cq_cntxt - 1, cq_cntxt - egr_cntxt); printf(" CQ contexts: 0x%08x - 0x%08x [%u]\n", cq_cntxt, timers - 1, timers - cq_cntxt); printf(" Timers: 0x%08x - 0x%08x [%u]\n", timers, pstructs - 1, pstructs - timers); printf(" Pstructs: 0x%08x - 0x%08x [%u]\n", pstructs, pstruct_fl - 1, pstruct_fl - pstructs); printf(" Pstruct FL: 0x%08x - 0x%08x [%u]\n", pstruct_fl, rx_fl - 1, rx_fl - pstruct_fl); printf(" Rx FL: 0x%08x - 0x%08x [%u]\n", rx_fl, tx_fl - 1, tx_fl - rx_fl); printf(" Tx FL: 0x%08x - 0x%08x [%u]\n", tx_fl, cim_base - 1, cim_base - tx_fl); printf(" uP RAM: 0x%08x - 0x%08x [%u]\n", cim_base, cim_base + cim_size - 1, cim_size); printf("\nPMRX memory map:\n"); printf(" iSCSI region: 0x%08x - 0x%08x [%u]\n", iscsi_ll, iscsi_ul, iscsi_ul - iscsi_ll + 1); printf(" TCP DDP region: 0x%08x - 0x%08x [%u]\n", tddp_ll, tddp_ul, tddp_ul - tddp_ll + 1); printf(" TPT region: 0x%08x - 0x%08x [%u]\n", stag_ll, stag_ul, stag_ul - stag_ll + 1); printf(" RQ region: 0x%08x - 0x%08x [%u]\n", rq_ll, rq_ul, rq_ul - rq_ll + 1); printf(" PBL region: 0x%08x - 0x%08x [%u]\n", pbl_ll, pbl_ul, pbl_ul - pbl_ll + 1); return 0; } -static int meminfo(int argc, char *argv[], int start_arg, const char *iff_name) +static int +meminfo(int argc, char *argv[], int start_arg, const char *iff_name) { int vers; struct ch_ifconf_regs regs; + (void) argc; + (void) argv; + (void) start_arg; + regs.len = REGDUMP_SIZE; if ((regs.data = malloc(regs.len)) == NULL) err(1, "can't malloc"); if (doit(iff_name, CHELSIO_IFCONF_GETREGS, ®s)) err(1, "can't read registers"); vers = regs.version & 0x3ff; if (vers == 3) return t3_meminfo((uint32_t *)regs.data); errx(1, "unknown card type %d", vers); return 0; } -static int mtu_tab_op(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +mtu_tab_op(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_mtus m; - int i; + unsigned int i; if (argc == start_arg) { if (doit(iff_name, CHELSIO_GETMTUTAB, &m) < 0) err(1, "get MTU table"); for (i = 0; i < m.nmtus; ++i) printf("%u ", m.mtus[i]); printf("\n"); } else if (argc <= start_arg + NMTUS) { m.nmtus = argc - start_arg; for (i = 0; i < m.nmtus; ++i) { char *p; unsigned long mt = strtoul(argv[start_arg + i], &p, 0); if (*p || mt > 9600) { warnx("bad parameter \"%s\"", argv[start_arg + i]); return -1; } if (i && mt < m.mtus[i - 1]) errx(1, "MTUs must be in ascending order"); m.mtus[i] = mt; } if (doit(iff_name, CHELSIO_SETMTUTAB, &m) < 0) err(1, "set MTU table"); } else return -1; return 0; } #ifdef CHELSIO_INTERNAL -static void show_egress_cntxt(uint32_t data[]) +static void +show_egress_cntxt(uint32_t data[]) { printf("credits: %u\n", data[0] & 0x7fff); printf("GTS: %u\n", (data[0] >> 15) & 1); printf("index: %u\n", data[0] >> 16); printf("queue size: %u\n", data[1] & 0xffff); - printf("base address: 0x%llx\n", + printf("base address: 0x%" PRIx64 "\n", ((data[1] >> 16) | ((uint64_t)data[2] << 16) | (((uint64_t)data[3] & 0xf) << 48)) << 12); printf("rsp queue #: %u\n", (data[3] >> 4) & 7); printf("cmd queue #: %u\n", (data[3] >> 7) & 1); printf("TUN: %u\n", (data[3] >> 8) & 1); printf("TOE: %u\n", (data[3] >> 9) & 1); printf("generation: %u\n", (data[3] >> 10) & 1); printf("uP token: %u\n", (data[3] >> 11) & 0xfffff); printf("valid: %u\n", (data[3] >> 31) & 1); } -static void show_fl_cntxt(uint32_t data[]) +static void +show_fl_cntxt(uint32_t data[]) { - printf("base address: 0x%llx\n", + printf("base address: 0x%" PRIx64 "\n", ((uint64_t)data[0] | ((uint64_t)data[1] & 0xfffff) << 32) << 12); printf("index: %u\n", (data[1] >> 20) | ((data[2] & 0xf) << 12)); printf("queue size: %u\n", (data[2] >> 4) & 0xffff); printf("generation: %u\n", (data[2] >> 20) & 1); printf("entry size: %u\n", (data[2] >> 21) | (data[3] & 0x1fffff) << 11); printf("congest thr: %u\n", (data[3] >> 21) & 0x3ff); printf("GTS: %u\n", (data[3] >> 31) & 1); } -static void show_response_cntxt(uint32_t data[]) +static void +show_response_cntxt(uint32_t data[]) { printf("index: %u\n", data[0] & 0xffff); printf("size: %u\n", data[0] >> 16); - printf("base address: 0x%llx\n", + printf("base address: 0x%" PRIx64 "\n", ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12); printf("MSI-X/RspQ: %u\n", (data[2] >> 20) & 0x3f); printf("intr enable: %u\n", (data[2] >> 26) & 1); printf("intr armed: %u\n", (data[2] >> 27) & 1); printf("generation: %u\n", (data[2] >> 28) & 1); printf("CQ mode: %u\n", (data[2] >> 31) & 1); printf("FL threshold: %u\n", data[3]); } -static void show_cq_cntxt(uint32_t data[]) +static void +show_cq_cntxt(uint32_t data[]) { printf("index: %u\n", data[0] & 0xffff); printf("size: %u\n", data[0] >> 16); - printf("base address: 0x%llx\n", + printf("base address: 0x%" PRIx64 "\n", ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12); printf("rsp queue #: %u\n", (data[2] >> 20) & 0x3f); printf("AN: %u\n", (data[2] >> 26) & 1); printf("armed: %u\n", (data[2] >> 27) & 1); printf("ANS: %u\n", (data[2] >> 28) & 1); printf("generation: %u\n", (data[2] >> 29) & 1); printf("overflow mode: %u\n", (data[2] >> 31) & 1); printf("credits: %u\n", data[3] & 0xffff); printf("credit threshold: %u\n", data[3] >> 16); } -static int get_sge_context(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +get_sge_context(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_cntxt ctx; if (argc != start_arg + 2) return -1; if (!strcmp(argv[start_arg], "egress")) ctx.cntxt_type = CNTXT_TYPE_EGRESS; else if (!strcmp(argv[start_arg], "fl")) ctx.cntxt_type = CNTXT_TYPE_FL; else if (!strcmp(argv[start_arg], "response")) ctx.cntxt_type = CNTXT_TYPE_RSP; else if (!strcmp(argv[start_arg], "cq")) ctx.cntxt_type = CNTXT_TYPE_CQ; else { warnx("unknown context type \"%s\"; known types are egress, " "fl, cq, and response", argv[start_arg]); return -1; } if (get_int_arg(argv[start_arg + 1], &ctx.cntxt_id)) return -1; if (doit(iff_name, CHELSIO_GET_SGE_CONTEXT, &ctx) < 0) err(1, "get SGE context"); if (!strcmp(argv[start_arg], "egress")) show_egress_cntxt(ctx.data); else if (!strcmp(argv[start_arg], "fl")) show_fl_cntxt(ctx.data); else if (!strcmp(argv[start_arg], "response")) show_response_cntxt(ctx.data); else if (!strcmp(argv[start_arg], "cq")) show_cq_cntxt(ctx.data); return 0; } #define ntohll(x) be64toh((x)) -static int get_sge_desc(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +get_sge_desc(int argc, char *argv[], int start_arg, const char *iff_name) { uint64_t *p, wr_hdr; unsigned int n = 1, qset, qnum; struct ch_desc desc; if (argc != start_arg + 3 && argc != start_arg + 4) return -1; if (get_int_arg(argv[start_arg], &qset) || get_int_arg(argv[start_arg + 1], &qnum) || get_int_arg(argv[start_arg + 2], &desc.idx)) return -1; if (argc == start_arg + 4 && get_int_arg(argv[start_arg + 3], &n)) return -1; if (qnum > 5) errx(1, "invalid queue number %d, range is 0..5", qnum); desc.queue_num = qset * 6 + qnum; for (; n--; desc.idx++) { if (doit(iff_name, CHELSIO_GET_SGE_DESC, &desc) < 0) err(1, "get SGE descriptor"); p = (uint64_t *)desc.data; wr_hdr = ntohll(*p); printf("Descriptor %u: cmd %u, TID %u, %s%s%s%s%u flits\n", desc.idx, (unsigned int)(wr_hdr >> 56), ((unsigned int)wr_hdr >> 8) & 0xfffff, ((wr_hdr >> 55) & 1) ? "SOP, " : "", ((wr_hdr >> 54) & 1) ? "EOP, " : "", ((wr_hdr >> 53) & 1) ? "COMPL, " : "", ((wr_hdr >> 52) & 1) ? "SGL, " : "", (unsigned int)wr_hdr & 0xff); for (; desc.size; p++, desc.size -= sizeof(uint64_t)) printf("%016" PRIx64 "%c", ntohll(*p), desc.size % 32 == 8 ? '\n' : ' '); } return 0; } #endif -static int get_tcb2(int argc, char *argv[], int start_arg, const char *iff_name) +static int +get_tcb2(int argc, char *argv[], int start_arg, const char *iff_name) { uint64_t *d; unsigned int i; unsigned int tcb_idx; struct ch_mem_range mr; if (argc != start_arg + 1) return -1; if (get_int_arg(argv[start_arg], &tcb_idx)) return -1; mr.buf = calloc(1, TCB_SIZE); if (!mr.buf) err(1, "get TCB"); mr.mem_id = MEM_CM; mr.addr = tcb_idx * TCB_SIZE; mr.len = TCB_SIZE; if (doit(iff_name, CHELSIO_GET_MEM, &mr) < 0) err(1, "get TCB"); for (d = (uint64_t *)mr.buf, i = 0; i < TCB_SIZE / 32; i++) { printf("%2u:", i); printf(" %08x %08x %08x %08x", (uint32_t)d[1], (uint32_t)(d[1] >> 32), (uint32_t)d[0], (uint32_t)(d[0] >> 32)); d += 2; printf(" %08x %08x %08x %08x\n", (uint32_t)d[1], (uint32_t)(d[1] >> 32), (uint32_t)d[0], (uint32_t)(d[0] >> 32)); d += 2; } free(mr.buf); return 0; } -static int get_pm_page_spec(const char *s, unsigned int *page_size, - unsigned int *num_pages) +static int +get_pm_page_spec(const char *s, unsigned int *page_size, + unsigned int *num_pages) { char *p; unsigned long val; val = strtoul(s, &p, 0); if (p == s) return -1; if (*p == 'x' && p[1]) { *num_pages = val; *page_size = strtoul(p + 1, &p, 0); } else { *num_pages = -1; *page_size = val; } *page_size <<= 10; // KB -> bytes return *p; } -static int conf_pm(int argc, char *argv[], int start_arg, const char *iff_name) +static int +conf_pm(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_pm pm; if (argc == start_arg) { if (doit(iff_name, CHELSIO_GET_PM, &pm) < 0) err(1, "read pm config"); printf("%ux%uKB TX pages, %ux%uKB RX pages, %uKB total memory\n", pm.tx_num_pg, pm.tx_pg_sz >> 10, pm.rx_num_pg, pm.rx_pg_sz >> 10, pm.pm_total >> 10); return 0; } if (argc != start_arg + 2) return -1; if (get_pm_page_spec(argv[start_arg], &pm.tx_pg_sz, &pm.tx_num_pg)) { warnx("bad parameter \"%s\"", argv[start_arg]); return -1; } if (get_pm_page_spec(argv[start_arg + 1], &pm.rx_pg_sz, &pm.rx_num_pg)) { warnx("bad parameter \"%s\"", argv[start_arg + 1]); return -1; } if (doit(iff_name, CHELSIO_SET_PM, &pm) < 0) err(1, "pm config"); return 0; } #ifdef CHELSIO_INTERNAL -static int dump_tcam(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +dump_tcam(int argc, char *argv[], int start_arg, const char *iff_name) { unsigned int nwords; struct ch_tcam_word op; if (argc != start_arg + 2) return -1; if (get_int_arg(argv[start_arg], &op.addr) || get_int_arg(argv[start_arg + 1], &nwords)) return -1; while (nwords--) { if (doit(iff_name, CHELSIO_READ_TCAM_WORD, &op) < 0) err(1, "tcam dump"); printf("0x%08x: 0x%02x 0x%08x 0x%08x\n", op.addr, op.buf[0] & 0xff, op.buf[1], op.buf[2]); op.addr++; } return 0; } -static void hexdump_8b(unsigned int start, uint64_t *data, unsigned int len) +static void +hexdump_8b(unsigned int start, uint64_t *data, unsigned int len) { int i; while (len) { printf("0x%08x:", start); for (i = 0; i < 4 && len; ++i, --len) printf(" %016llx", (unsigned long long)*data++); printf("\n"); start += 32; } } -static int dump_mc7(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +dump_mc7(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_mem_range mem; unsigned int mem_id, addr, len; if (argc != start_arg + 3) return -1; if (!strcmp(argv[start_arg], "cm")) mem_id = MEM_CM; else if (!strcmp(argv[start_arg], "rx")) mem_id = MEM_PMRX; else if (!strcmp(argv[start_arg], "tx")) mem_id = MEM_PMTX; else errx(1, "unknown memory \"%s\"; must be one of \"cm\", \"tx\"," " or \"rx\"", argv[start_arg]); if (get_int_arg(argv[start_arg + 1], &addr) || get_int_arg(argv[start_arg + 2], &len)) return -1; mem.buf = malloc(len); if (!mem.buf) err(1, "memory dump"); mem.mem_id = mem_id; mem.addr = addr; mem.len = len; if (doit(iff_name, CHELSIO_GET_MEM, &mem) < 0) err(1, "memory dump"); hexdump_8b(mem.addr, (uint64_t *)mem.buf, mem.len >> 3); free(mem.buf); return 0; } #endif -/* Max FW size is 32K including version, +4 bytes for the checksum. */ +/* Max FW size is 64K including version, +4 bytes for the checksum. */ #define MAX_FW_IMAGE_SIZE (64 * 1024) -static int load_fw(int argc, char *argv[], int start_arg, const char *iff_name) +static int +load_fw(int argc, char *argv[], int start_arg, const char *iff_name) { int fd, len; struct ch_mem_range op; const char *fname = argv[start_arg]; if (argc != start_arg + 1) return -1; fd = open(fname, O_RDONLY); if (fd < 0) err(1, "load firmware"); bzero(&op, sizeof(op)); op.buf = malloc(MAX_FW_IMAGE_SIZE + 1); if (!op.buf) err(1, "load firmware"); - op.len = read(fd, op.buf, MAX_FW_IMAGE_SIZE + 1); - if (op.len < 0) + len = read(fd, op.buf, MAX_FW_IMAGE_SIZE + 1); + if (len < 0) err(1, "load firmware"); - if (op.len > MAX_FW_IMAGE_SIZE) + if (len > MAX_FW_IMAGE_SIZE) errx(1, "FW image too large"); + op.len = len; if (doit(iff_name, CHELSIO_LOAD_FW, &op) < 0) err(1, "load firmware"); return 0; } /* Max BOOT size is 255*512 bytes including the BIOS boot ROM basic header */ #define MAX_BOOT_IMAGE_SIZE (0xff * 512) -static int load_boot(int argc, char *argv[], - int start_arg, const char *iff_name) +static int +load_boot(int argc, char *argv[], int start_arg, const char *iff_name) { int fd, len; struct ch_mem_range op; const char *fname = argv[start_arg]; if (argc != start_arg + 1) return -1; fd = open(fname, O_RDONLY); if (fd < 0) err(1, "load boot image"); op.buf = malloc(MAX_BOOT_IMAGE_SIZE + 1); if (!op.buf) err(1, "load boot image"); len = read(fd, op.buf, MAX_BOOT_IMAGE_SIZE + 1); if (len < 0) err(1, "load boot image"); if (len > MAX_BOOT_IMAGE_SIZE) errx(1, "boot image too large"); op.len = len; if (doit(iff_name, CHELSIO_LOAD_BOOT, &op) < 0) err(1, "load boot image"); return 0; } -static int dump_proto_sram(const char *iff_name) +static int +dump_proto_sram(const char *iff_name) { int i, j; uint8_t buf[PROTO_SRAM_SIZE]; struct ch_eeprom ee; uint8_t *p = buf; bzero(buf, sizeof(buf)); ee.offset = PROTO_SRAM_EEPROM_ADDR; ee.data = p; ee.len = sizeof(buf); if (doit(iff_name, CHELSIO_GET_EEPROM, &ee)) err(1, "show protocol sram"); for (i = 0; i < PROTO_SRAM_LINES; i++) { for (j = PROTO_SRAM_LINE_NIBBLES - 1; j >= 0; j--) { int nibble_idx = i * PROTO_SRAM_LINE_NIBBLES + j; uint8_t nibble = p[nibble_idx / 2]; if (nibble_idx & 1) nibble >>= 4; else nibble &= 0xf; printf("%x", nibble); } putchar('\n'); } return 0; } -static int proto_sram_op(int argc, char *argv[], int start_arg, +static int +proto_sram_op(int argc, char *argv[], int start_arg, const char *iff_name) { + (void) argv; + (void) start_arg; + if (argc == start_arg) return dump_proto_sram(iff_name); return -1; } -static int dump_qset_params(const char *iff_name) +static int +dump_qset_params(const char *iff_name) { struct ch_qset_params qp; qp.qset_idx = 0; while (doit(iff_name, CHELSIO_GET_QSET_PARAMS, &qp) == 0) { if (!qp.qset_idx) printf("Qset TxQ0 TxQ1 TxQ2 RspQ RxQ0 RxQ1" " Cong Lat IRQ\n"); printf("%4u %6u %6u %6u %6u %6u %6u %5u %4u %5d\n", qp.qnum, qp.txq_size[0], qp.txq_size[1], qp.txq_size[2], qp.rspq_size, qp.fl_size[0], qp.fl_size[1], qp.cong_thres, qp.intr_lat, qp.vector); qp.qset_idx++; } if (!qp.qset_idx || (errno && errno != EINVAL)) err(1, "get qset parameters"); return 0; } -static int qset_config(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +qset_config(int argc, char *argv[], int start_arg, const char *iff_name) { - struct ch_qset_params qp; + (void) argv; if (argc == start_arg) return dump_qset_params(iff_name); return -1; } -static int qset_num_config(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +qset_num_config(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_reg reg; + (void) argv; + if (argc == start_arg) { if (doit(iff_name, CHELSIO_GET_QSET_NUM, ®) < 0) err(1, "get qsets"); printf("%u\n", reg.val); return 0; } return -1; } /* * Parse a string containing an IP address with an optional network prefix. */ -static int parse_ipaddr(const char *s, uint32_t *addr, uint32_t *mask) +static int +parse_ipaddr(const char *s, uint32_t *addr, uint32_t *mask) { char *p, *slash; struct in_addr ia; *mask = 0xffffffffU; slash = strchr(s, '/'); if (slash) *slash = 0; if (!inet_aton(s, &ia)) { if (slash) *slash = '/'; *addr = 0; return -1; } *addr = ntohl(ia.s_addr); if (slash) { unsigned int prefix = strtoul(slash + 1, &p, 10); *slash = '/'; if (p == slash + 1 || *p || prefix > 32) return -1; *mask <<= (32 - prefix); } return 0; } /* * Parse a string containing a value and an optional colon separated mask. */ -static int parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask) +static int +parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask) { char *p; *mask = 0xffffffffU; *val = strtoul(s, &p, 0); if (p == s) return -1; if (*p == ':' && p[1]) *mask = strtoul(p + 1, &p, 0); return *p ? -1 : 0; } -static int parse_trace_param(const char *s, uint32_t *val, uint32_t *mask) +static int +parse_trace_param(const char *s, uint32_t *val, uint32_t *mask) { return strchr(s, '.') ? parse_ipaddr(s, val, mask) : parse_val_mask_param(s, val, mask); } -static int trace_config(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +trace_config(int argc, char *argv[], int start_arg, const char *iff_name) { uint32_t val, mask; struct ch_trace trace; if (argc == start_arg) return -1; memset(&trace, 0, sizeof(trace)); if (!strcmp(argv[start_arg], "tx")) trace.config_tx = 1; else if (!strcmp(argv[start_arg], "rx")) trace.config_rx = 1; else if (!strcmp(argv[start_arg], "all")) trace.config_tx = trace.config_rx = 1; else errx(1, "bad trace filter \"%s\"; must be one of \"rx\", " "\"tx\" or \"all\"", argv[start_arg]); if (argc == ++start_arg) return -1; if (!strcmp(argv[start_arg], "on")) { trace.trace_tx = trace.config_tx; trace.trace_rx = trace.config_rx; } else if (strcmp(argv[start_arg], "off")) errx(1, "bad argument \"%s\"; must be \"on\" or \"off\"", argv[start_arg]); start_arg++; if (start_arg < argc && !strcmp(argv[start_arg], "not")) { trace.invert_match = 1; start_arg++; } while (start_arg + 2 <= argc) { int ret = parse_trace_param(argv[start_arg + 1], &val, &mask); if (!strcmp(argv[start_arg], "interface")) { trace.intf = val; trace.intf_mask = mask; } else if (!strcmp(argv[start_arg], "sip")) { trace.sip = val; trace.sip_mask = mask; } else if (!strcmp(argv[start_arg], "dip")) { trace.dip = val; trace.dip_mask = mask; } else if (!strcmp(argv[start_arg], "sport")) { trace.sport = val; trace.sport_mask = mask; } else if (!strcmp(argv[start_arg], "dport")) { trace.dport = val; trace.dport_mask = mask; } else if (!strcmp(argv[start_arg], "vlan")) { trace.vlan = val; trace.vlan_mask = mask; } else if (!strcmp(argv[start_arg], "proto")) { trace.proto = val; trace.proto_mask = mask; } else errx(1, "unknown trace parameter \"%s\"\n" "known parameters are \"interface\", \"sip\", " "\"dip\", \"sport\", \"dport\", \"vlan\", " "\"proto\"", argv[start_arg]); if (ret < 0) errx(1, "bad parameter \"%s\"", argv[start_arg + 1]); start_arg += 2; } if (start_arg != argc) errx(1, "unknown parameter \"%s\"", argv[start_arg]); if (doit(iff_name, CHELSIO_SET_TRACE_FILTER, &trace) < 0) err(1, "trace"); return 0; } -static int get_sched_param(int argc, char *argv[], int pos, unsigned int *valp) +static int +get_sched_param(int argc, char *argv[], int pos, unsigned int *valp) { if (pos + 1 >= argc) errx(1, "missing value for %s", argv[pos]); if (get_int_arg(argv[pos + 1], valp)) exit(1); return 0; } -static int tx_sched(int argc, char *argv[], int start_arg, const char *iff_name) +static int +tx_sched(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_hw_sched op; unsigned int idx, val; if (argc < 5 || get_int_arg(argv[start_arg++], &idx)) return -1; op.sched = idx; op.mode = op.channel = -1; op.kbps = op.class_ipg = op.flow_ipg = -1; while (argc > start_arg) { if (!strcmp(argv[start_arg], "mode")) { if (start_arg + 1 >= argc) errx(1, "missing value for mode"); if (!strcmp(argv[start_arg + 1], "class")) op.mode = 0; else if (!strcmp(argv[start_arg + 1], "flow")) op.mode = 1; else errx(1, "bad mode \"%s\"", argv[start_arg + 1]); } else if (!strcmp(argv[start_arg], "channel") && !get_sched_param(argc, argv, start_arg, &val)) op.channel = val; else if (!strcmp(argv[start_arg], "rate") && !get_sched_param(argc, argv, start_arg, &val)) op.kbps = val; else if (!strcmp(argv[start_arg], "ipg") && !get_sched_param(argc, argv, start_arg, &val)) op.class_ipg = val; else if (!strcmp(argv[start_arg], "flowipg") && !get_sched_param(argc, argv, start_arg, &val)) op.flow_ipg = val; else errx(1, "unknown scheduler parameter \"%s\"", argv[start_arg]); start_arg += 2; } if (doit(iff_name, CHELSIO_SET_HW_SCHED, &op) < 0) err(1, "pktsched"); return 0; } -static int pktsched(int argc, char *argv[], int start_arg, const char *iff_name) +static int +pktsched(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_pktsched_params op; unsigned int idx, min = -1, max, binding = -1; if (argc < 4) errx(1, "no scheduler specified"); if (!strcmp(argv[start_arg], "port")) { if (argc != start_arg + 4) return -1; if (get_int_arg(argv[start_arg + 1], &idx) || get_int_arg(argv[start_arg + 2], &min) || get_int_arg(argv[start_arg + 3], &max)) return -1; op.sched = 0; } else if (!strcmp(argv[start_arg], "tunnelq")) { if (argc != start_arg + 4) return -1; if (get_int_arg(argv[start_arg + 1], &idx) || get_int_arg(argv[start_arg + 2], &max) || get_int_arg(argv[start_arg + 3], &binding)) return -1; op.sched = 1; } else if (!strcmp(argv[start_arg], "tx")) return tx_sched(argc, argv, start_arg + 1, iff_name); else errx(1, "unknown scheduler \"%s\"; must be one of \"port\", " "\"tunnelq\" or \"tx\"", argv[start_arg]); op.idx = idx; op.min = min; op.max = max; op.binding = binding; if (doit(iff_name, CHELSIO_SET_PKTSCHED, &op) < 0) err(1, "pktsched"); return 0; } -static int clear_stats(int argc, char *argv[], int start_arg, - const char *iff_name) +static int +clear_stats(int argc, char *argv[], int start_arg, const char *iff_name) { + (void) argc; + (void) argv; + (void) start_arg; + if (doit(iff_name, CHELSIO_CLEAR_STATS, NULL) < 0) err(1, "clearstats"); return 0; } -static int get_up_la(int argc, char *argv[], int start_arg, const char *iff_name) +static int +get_up_la(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_up_la la; int i, idx, max_idx, entries; + (void) argc; + (void) argv; + (void) start_arg; + la.stopped = 0; la.idx = -1; la.bufsize = LA_BUFSIZE; la.data = malloc(la.bufsize); if (!la.data) err(1, "uP_LA malloc"); if (doit(iff_name, CHELSIO_GET_UP_LA, &la) < 0) err(1, "uP_LA"); if (la.stopped) printf("LA is not running\n"); entries = la.bufsize / 4; idx = (int)la.idx; max_idx = (entries / 4) - 1; for (i = 0; i < max_idx; i++) { printf("%04x %08x %08x\n", la.data[idx], la.data[idx+2], la.data[idx+1]); idx = (idx + 4) & (entries - 1); } return 0; } -static int get_up_ioqs(int argc, char *argv[], int start_arg, const char *iff_name) +static int +get_up_ioqs(int argc, char *argv[], int start_arg, const char *iff_name) { struct ch_up_ioqs ioqs; int i, entries; + (void) argc; + (void) argv; + (void) start_arg; + bzero(&ioqs, sizeof(ioqs)); ioqs.bufsize = IOQS_BUFSIZE; ioqs.data = malloc(IOQS_BUFSIZE); if (!ioqs.data) err(1, "uP_IOQs malloc"); if (doit(iff_name, CHELSIO_GET_UP_IOQS, &ioqs) < 0) err(1, "uP_IOQs"); printf("ioq_rx_enable : 0x%08x\n", ioqs.ioq_rx_enable); printf("ioq_tx_enable : 0x%08x\n", ioqs.ioq_tx_enable); printf("ioq_rx_status : 0x%08x\n", ioqs.ioq_rx_status); printf("ioq_tx_status : 0x%08x\n", ioqs.ioq_tx_status); entries = ioqs.bufsize / sizeof(struct t3_ioq_entry); for (i = 0; i < entries; i++) { printf("\nioq[%d].cp : 0x%08x\n", i, ioqs.data[i].ioq_cp); printf("ioq[%d].pp : 0x%08x\n", i, ioqs.data[i].ioq_pp); printf("ioq[%d].alen : 0x%08x\n", i, ioqs.data[i].ioq_alen); printf("ioq[%d].stats : 0x%08x\n", i, ioqs.data[i].ioq_stats); printf(" sop %u\n", ioqs.data[i].ioq_stats >> 16); printf(" eop %u\n", ioqs.data[i].ioq_stats & 0xFFFF); } return 0; } static int run_cmd(int argc, char *argv[], const char *iff_name) { int r = -1; if (!strcmp(argv[2], "reg")) r = register_io(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "mdio")) r = mdio_io(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "mtus")) r = mtu_tab_op(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "pm")) r = conf_pm(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "regdump")) r = dump_regs(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "tcamdump")) r = dump_tcam(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "memdump")) r = dump_mc7(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "meminfo")) r = meminfo(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "context")) r = get_sge_context(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "desc")) r = get_sge_desc(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "loadfw")) r = load_fw(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "loadboot")) r = load_boot(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "proto")) r = proto_sram_op(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "qset")) r = qset_config(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "qsets")) r = qset_num_config(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "trace")) r = trace_config(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "pktsched")) r = pktsched(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "tcb")) r = get_tcb2(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "clearstats")) r = clear_stats(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "la")) r = get_up_la(argc, argv, 3, iff_name); else if (!strcmp(argv[2], "ioqs")) r = get_up_ioqs(argc, argv, 3, iff_name); if (r == -1) usage(stderr); return (0); } static int run_cmd_loop(int argc, char *argv[], const char *iff_name) { - int n, i; + int n; + unsigned int i; char buf[64]; char *args[8], *s; + (void) argc; args[0] = argv[0]; args[1] = argv[1]; /* * Fairly simplistic loop. Displays a "> " prompt and processes any * input as a cxgbtool command. You're supposed to enter only the part * after "cxgbtool cxgbX". Use "quit" or "exit" to exit. Any error in * the command will also terminate cxgbtool. */ for (;;) { fprintf(stdout, "> "); fflush(stdout); - n = read(STDIN_FILENO, buf, sizeof(buf)); - if (n > sizeof(buf) - 1) { - fprintf(stdout, "too much input.\n"); - return (0); - } else if (n <= 0) + n = read(STDIN_FILENO, buf, sizeof(buf) - 1); + if (n <= 0) return (0); if (buf[--n] != '\n') continue; else buf[n] = 0; s = &buf[0]; for (i = 2; i < sizeof(args)/sizeof(args[0]) - 1; i++) { while (s && (*s == ' ' || *s == '\t')) s++; if ((args[i] = strsep(&s, " \t")) == NULL) break; } args[sizeof(args)/sizeof(args[0]) - 1] = 0; if (!strcmp(args[2], "quit") || !strcmp(args[2], "exit")) return (0); (void) run_cmd(i, args, iff_name); } /* Can't really get here */ return (0); } int main(int argc, char *argv[]) { int r = -1; const char *iff_name; progname = argv[0]; if (argc == 2) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) usage(stdout); if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) { printf("%s version %s\n", PROGNAME, VERSION); printf("%s\n", COPYRIGHT); exit(0); } } if (argc < 3) usage(stderr); iff_name = argv[1]; if (argc == 3 && !strcmp(argv[2], "stdio")) r = run_cmd_loop(argc, argv, iff_name); else r = run_cmd(argc, argv, iff_name); return (r); } Index: projects/ppc64/usr.sbin/cxgbtool/reg_defs.c =================================================================== --- projects/ppc64/usr.sbin/cxgbtool/reg_defs.c (revision 204271) +++ projects/ppc64/usr.sbin/cxgbtool/reg_defs.c (revision 204272) @@ -1,837 +1,837 @@ /* * $FreeBSD$ */ /* This file is automatically generated --- do not edit */ struct reg_info sge_regs[] = { { "SG_CONTROL", 0x0, 0 }, { "CmdQ0_Enable", 0, 1 }, { "CmdQ1_Enable", 1, 1 }, { "FL0_Enable", 2, 1 }, { "FL1_Enable", 3, 1 }, { "CPL_Enable", 4, 1 }, { "Response_Queue_Enable", 5, 1 }, { "CmdQ_Priority", 6, 2 }, { "Disable_CmdQ0_GTS", 8, 1 }, { "Disable_CmdQ1_GTS", 9, 1 }, { "Disable_FL0_GTS", 10, 1 }, { "Disable_FL1_GTS", 11, 1 }, { "Enable_Big_Endian", 12, 1 }, { "FL_Selection_Criteria", 13, 1 }, { "iSCSI_Coalesce", 14, 1 }, { "RX_Pkt_Offset", 15, 3 }, { "VLAN_Xtract", 18, 1 }, { "SG_DOORBELL", 0x4, 0 }, { "CmdQ0_Enable", 0, 1 }, { "CmdQ1_Enable", 1, 1 }, { "FL0_Enable", 2, 1 }, { "FL1_Enable", 3, 1 }, { "SG_CMD0BASELWR", 0x8, 0 }, { "SG_CMD0BASEUPR", 0xc, 0 }, { "SG_CMD1BASELWR", 0x10, 0 }, { "SG_CMD1BASEUPR", 0x14, 0 }, { "SG_FL0BASELWR", 0x18, 0 }, { "SG_FL0BASEUPR", 0x1c, 0 }, { "SG_FL1BASELWR", 0x20, 0 }, { "SG_FL1BASEUPR", 0x24, 0 }, { "SG_CMD0SIZE", 0x28, 0 }, { "CmdQ0_Size", 0, 17 }, { "SG_FL0SIZE", 0x2c, 0 }, { "FL0_Size", 0, 17 }, { "SG_RSPSIZE", 0x30, 0 }, { "RespQ_Size", 0, 17 }, { "SG_RSPBASELWR", 0x34, 0 }, { "SG_RSPBASEUPR", 0x38, 0 }, { "SG_FLTHRESHOLD", 0x3c, 0 }, { "FL_Threshold", 0, 16 }, { "SG_RSPQUEUECREDIT", 0x40, 0 }, { "RespQ_Credit", 0, 17 }, { "SG_DEBUGTXDATAL", 0x44, 0 }, { "SG_SLEEPING", 0x48, 0 }, { "Sleeping", 0, 16 }, { "SG_INTRTIMER", 0x4c, 0 }, { "Interrupt_Timer_Count", 0, 24 }, { "SG_CMD0PTR", 0x50, 0 }, { "CmdQ0_Pointer", 0, 16 }, { "Current_Generation_Bit", 16, 1 }, { "SG_CMD1PTR", 0x54, 0 }, { "CmdQ1_Pointer", 0, 16 }, { "Current_Generation_Bit", 16, 1 }, { "SG_FL0PTR", 0x58, 0 }, { "FL0_Pointer", 0, 16 }, { "Current_Generation_Bit", 16, 1 }, { "SG_FL1PTR", 0x5c, 0 }, { "FL1_Pointer", 0, 16 }, { "Current_Generation_Bit", 16, 1 }, { "SG_DEBUGTXDATAH", 0x60, 0 }, { "SG_DEBUGRXDATAL", 0x64, 0 }, { "SG_DEBUGRXDATAH", 0x68, 0 }, { "SG_VERSION", 0x6c, 0 }, { "Day", 0, 5 }, { "Month", 5, 4 }, { "SG_DEBUGRXSOP", 0x70, 0 }, { "SG_DEBUGTXSOP", 0x74, 0 }, { "SG_LA_RDPTR0", 0x78, 0 }, { "Logic_Analyzer0_Read_Pointer", 0, 9 }, { "SG_LA_RDDATA0", 0x7c, 0 }, { "SG_LA_WRPTR0", 0x80, 0 }, { "SG_DEBUGRXEOP", 0x84, 0 }, { "SG_DEBUGTXEOP", 0x88, 0 }, { "SG_DEBUGRXSIZE", 0x8c, 0 }, { "SG_DEBUGTXSIZE", 0x90, 0 }, { "SG_NUMBER_LA", 0x94, 0 }, { "SG_LA_RDPTR1", 0x98, 0 }, { "Logic_Analyzer1_Read_Pointer", 0, 9 }, { "SG_LA_RDDATA1", 0x9c, 0 }, { "SG_LA_WRPTR1", 0xa0, 0 }, { "SG_LA_RDPTR2", 0xa4, 0 }, { "Logic_Analyzer2_Read_Pointer", 0, 9 }, { "SG_LA_RDDATA2", 0xa8, 0 }, { "SG_LA_WRPTR2", 0xac, 0 }, { "SG_CMD1SIZE", 0xb0, 0 }, { "CmdQ1_Size", 0, 17 }, { "SG_FL1SIZE", 0xb4, 0 }, { "FL1_Size", 0, 17 }, { "SG_INT_ENABLE", 0xb8, 0 }, { "RespQ_Exhausted", 0, 1 }, { "RespQ_Overflow", 1, 1 }, { "FL_Exhausted", 2, 1 }, { "Packet_Too_Big", 3, 1 }, { "Packet_Mismatch", 4, 1 }, { "SG_INT_CAUSE", 0xbc, 0 }, { "RespQ_Exhausted", 0, 1 }, { "RespQ_Overflow", 1, 1 }, { "FL_Exhausted", 2, 1 }, { "Packet_Too_Big", 3, 1 }, { "Packet_Mismatch", 4, 1 }, { "SG_RESPACCUTIMER", 0xc0, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc3_regs[] = { { "MC3_CFG", 0x100, 0 }, { "Clk_Enable", 0, 1 }, { "Ready", 1, 1 }, { "Read_to_Write_Delay", 2, 3 }, { "Write_to_Read_Delay", 5, 3 }, { "MC3_Bank_Cycle", 8, 4 }, { "Refresh_Cycle", 12, 4 }, { "Precharge_Cycle", 16, 2 }, { "Active_to_Read_Write_Delay", 18, 1 }, { "Active_to_Precharge_Delay", 19, 3 }, { "Write_Recovery_Delay", 22, 2 }, { "Density", 24, 2 }, { "Organization", 26, 1 }, { "Banks", 27, 1 }, { "Unregistered", 28, 1 }, { "MC3_Width", 29, 2 }, { "MC3_Slow", 31, 1 }, { "MC3_MODE", 0x104, 0 }, { "MC3_Mode", 0, 14 }, { "Busy", 31, 1 }, { "MC3_EXT_MODE", 0x108, 0 }, { "MC3_Extended_Mode", 0, 14 }, { "Busy", 31, 1 }, { "MC3_PRECHARG", 0x10c, 0 }, { "Busy", 31, 1 }, { "MC3_REFRESH", 0x110, 0 }, { "Refresh_Enable", 0, 1 }, { "Refresh_Divisor", 1, 14 }, { "Busy", 31, 1 }, { "MC3_STROBE", 0x114, 0 }, { "Master_DLL_Reset", 0, 1 }, { "Master_DLL_Tap_Count", 1, 8 }, { "Master_DLL_Locked", 9, 1 }, { "Master_DLL_Max_Tap_Count", 10, 1 }, { "Master_DLL_Tap_Count_Offset", 11, 6 }, { "Slave_DLL_Reset", 11, 1 }, { "Slave_DLL_Delta", 12, 4 }, { "Slave_Delay_Line_Manual_Tap_Count", 17, 6 }, { "Slave_Delay_Line_Manual_Tap_Count_Enable", 23, 1 }, { "Slave_Delay_Line_Tap_Count", 24, 6 }, { "MC3_ECC_CNTL", 0x118, 0 }, { "ECC_Generation_Enable", 0, 1 }, { "ECC_Check_Enable", 1, 1 }, { "Correctable_Error_Count", 2, 8 }, { "Uncorrectable_Error_Count", 10, 8 }, { "MC3_CE_ADDR", 0x11c, 0 }, { "MC3_CE_Addr", 4, 28 }, { "MC3_CE_DATA0", 0x120, 0 }, { "MC3_CE_DATA1", 0x124, 0 }, { "MC3_CE_DATA2", 0x128, 0 }, { "MC3_CE_DATA3", 0x12c, 0 }, { "MC3_CE_DATA4", 0x130, 0 }, { "MC3_UE_ADDR", 0x134, 0 }, { "MC3_UE_Addr", 4, 28 }, { "MC3_UE_DATA0", 0x138, 0 }, { "MC3_UE_DATA1", 0x13c, 0 }, { "MC3_UE_DATA2", 0x140, 0 }, { "MC3_UE_DATA3", 0x144, 0 }, { "MC3_UE_DATA4", 0x148, 0 }, { "MC3_BD_ADDR", 0x14c, 0 }, { "MC3_BD_DATA0", 0x150, 0 }, { "MC3_BD_DATA1", 0x154, 0 }, { "MC3_BD_DATA2", 0x158, 0 }, { "MC3_BD_DATA3", 0x15c, 0 }, { "MC3_BD_DATA4", 0x160, 0 }, { "MC3_BD_OP", 0x164, 0 }, { "Back_Door_Operation", 0, 1 }, { "Busy", 31, 1 }, { "MC3_BIST_ADDR_BEG", 0x168, 0 }, { "MC3_BIST_ADDR_END", 0x16c, 0 }, { "MC3_BIST_DATA", 0x170, 0 }, { "MC3_BIST_OP", 0x174, 0 }, { "Op", 0, 1 }, { "Data_Pattern", 1, 2 }, { "Continuous", 3, 1 }, { "Busy", 31, 1 }, { "MC3_INT_ENABLE", 0x178, 0 }, { "MC3_Corr_Err", 0, 1 }, { "MC3_Uncorr_Err", 1, 1 }, { "MC3_Parity_Err", 2, 8 }, { "MC3_Addr_Err", 10, 1 }, { "MC3_INT_CAUSE", 0x17c, 0 }, { "MC3_Corr_Err", 0, 1 }, { "MC3_Uncorr_Err", 1, 1 }, { "MC3_Parity_Err", 2, 8 }, { "MC3_Addr_Err", 10, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc4_regs[] = { { "MC4_CFG", 0x180, 0 }, { "Power_Up", 0, 1 }, { "Ready", 1, 1 }, { "Read_to_Write_Delay", 2, 3 }, { "Write_to_Read_Delay", 5, 3 }, { "MC4_Bank_Cycle", 8, 3 }, { "MC4_Narrow", 24, 1 }, { "MC4_Slow", 25, 1 }, { "MC4A_Width", 24, 2 }, { "MC4A_Slow", 26, 1 }, { "MC4_MODE", 0x184, 0 }, { "MC4_Mode", 0, 15 }, { "Busy", 31, 1 }, { "MC4_EXT_MODE", 0x188, 0 }, { "MC4_Extended_Mode", 0, 15 }, { "Busy", 31, 1 }, { "MC4_REFRESH", 0x190, 0 }, { "Refresh_Enable", 0, 1 }, { "Refresh_Divisor", 1, 14 }, { "Busy", 31, 1 }, { "MC4_STROBE", 0x194, 0 }, { "Master_DLL_Reset", 0, 1 }, { "Master_DLL_Tap_Count", 1, 8 }, { "Master_DLL_Locked", 9, 1 }, { "Master_DLL_Max_Tap_Count", 10, 1 }, { "Master_DLL_Tap_Count_Offset", 11, 6 }, { "Slave_DLL_Reset", 11, 1 }, { "Slave_DLL_Delta", 12, 4 }, { "Slave_Delay_Line_Manual_Tap_Count", 17, 6 }, { "Slave_Delay_Line_Manual_Tap_Count_Enable", 23, 1 }, { "Slave_Delay_Line_Tap_Count", 24, 6 }, { "MC4_ECC_CNTL", 0x198, 0 }, { "ECC_Generation_Enable", 0, 1 }, { "ECC_Check_Enable", 1, 1 }, { "Correctable_Error_Count", 2, 8 }, { "Uncorrectable_Error_Count", 10, 8 }, { "MC4_CE_ADDR", 0x19c, 0 }, { "MC4_CE_Addr", 4, 24 }, { "MC4_CE_DATA0", 0x1a0, 0 }, { "MC4_CE_DATA1", 0x1a4, 0 }, { "MC4_CE_DATA2", 0x1a8, 0 }, { "MC4_CE_DATA3", 0x1ac, 0 }, { "MC4_CE_DATA4", 0x1b0, 0 }, { "MC4_UE_ADDR", 0x1b4, 0 }, { "MC4_UE_Addr", 4, 24 }, { "MC4_UE_DATA0", 0x1b8, 0 }, { "MC4_UE_DATA1", 0x1bc, 0 }, { "MC4_UE_DATA2", 0x1c0, 0 }, { "MC4_UE_DATA3", 0x1c4, 0 }, { "MC4_UE_DATA4", 0x1c8, 0 }, { "MC4_BD_ADDR", 0x1cc, 0 }, { "MC4_Back_Door_Addr", 0, 28 }, { "MC4_BD_DATA0", 0x1d0, 0 }, { "MC4_BD_DATA1", 0x1d4, 0 }, { "MC4_BD_DATA2", 0x1d8, 0 }, { "MC4_BD_DATA3", 0x1dc, 0 }, { "MC4_BD_DATA4", 0x1e0, 0 }, { "MC4_BD_OP", 0x1e4, 0 }, { "Operation", 0, 1 }, { "Busy", 31, 1 }, { "MC4_BIST_ADDR_BEG", 0x1e8, 0 }, { "MC4_BIST_ADDR_END", 0x1ec, 0 }, { "MC4_BIST_DATA", 0x1f0, 0 }, { "MC4_BIST_OP", 0x1f4, 0 }, { "Op", 0, 1 }, { "Data_Pattern", 1, 2 }, { "Continuous", 3, 1 }, { "Busy", 31, 1 }, { "MC4_INT_ENABLE", 0x1f8, 0 }, { "MC4_Corr_Err", 0, 1 }, { "MC4_Uncorr_Err", 1, 1 }, { "MC4_Addr_Err", 2, 1 }, { "MC4_INT_CAUSE", 0x1fc, 0 }, { "MC4_Corr_Err", 0, 1 }, { "MC4_Uncorr_Err", 1, 1 }, { "MC4_Addr_Err", 2, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info tpi_regs[] = { { "TPI_ADDR", 0x280, 0 }, { "TPI_ADDRESS", 0, 24 }, { "TPI_WR_DATA", 0x284, 0 }, { "TPI_RD_DATA", 0x288, 0 }, { "TPI_CSR", 0x28c, 0 }, { "TPIWR", 0, 1 }, { "TPIRDY", 1, 1 }, { "INT_DIR", 31, 1 }, { "TPI_PAR", 0x29c, 0 }, { "TPIPAR", 0, 7 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info tp_regs[] = { { "TP_IN_CONFIG", 0x300, 0 }, { "TP_IN_CSPI_Tunnel", 0, 1 }, { "TP_IN_CSPI_Ethernet", 1, 1 }, { "TP_IN_CSPI_CPL", 3, 1 }, { "TP_IN_CSPI_POS", 4, 1 }, { "TP_IN_CSPI_Check_IP_Csum", 5, 1 }, { "TP_IN_CSPI_Check_TCP_Csum", 6, 1 }, { "TP_IN_ESPI_Tunnel", 7, 1 }, { "TP_IN_ESPI_Ethernet", 8, 1 }, { "TP_IN_ESPI_CPL", 10, 1 }, { "TP_IN_ESPI_POS", 11, 1 }, { "TP_IN_ESPI_Check_IP_Csum", 12, 1 }, { "TP_IN_ESPI_Check_TCP_Csum", 13, 1 }, { "Offload_Disable", 14, 1 }, { "TP_OUT_CONFIG", 0x304, 0 }, { "TP_OUT_C_ETH", 0, 1 }, { "TP_OUT_CSPI_CPL", 2, 1 }, { "TP_OUT_CSPI_POS", 3, 1 }, { "TP_OUT_CSPI_Generate_IP_Csum", 4, 1 }, { "TP_OUT_CSPI_Generate_TCP_Csum", 5, 1 }, { "TP_OUT_ESPI_Ethernet", 6, 1 }, { "TP_OUT_ESPI_TAG_Ethernet", 7, 1 }, { "TP_OUT_ESPI_CPL", 8, 1 }, { "TP_OUT_ESPI_POS", 9, 1 }, { "TP_OUT_ESPI_Generate_IP_Csum", 10, 1 }, { "TP_OUT_ESPI_Generate_TCP_Csum", 11, 1 }, { "TP_GLOBAL_CONFIG", 0x308, 0 }, { "IP_TTL", 0, 8 }, { "TCAM_Server_Region_Usage", 8, 2 }, { "QOS_Mapping", 10, 1 }, { "TCP_Csum", 11, 1 }, { "UDP_Csum", 12, 1 }, { "IP_Csum", 13, 1 }, { "IP_ID_Split", 14, 1 }, { "Path_MTU", 15, 1 }, { "5Tuple_Lookup", 17, 2 }, { "IP_Fragment_Drop", 19, 1 }, { "Ping_Drop", 20, 1 }, { "Protect_Mode", 21, 1 }, { "SYN_Cookie_Algorithm", 22, 1 }, { "Attack_Filter", 23, 1 }, { "Interface_Type", 24, 1 }, { "Disable_RX_Flow_Control", 25, 1 }, { "SYN_Cookie_Parameter", 26, 6 }, { "TP_GLOBAL_RX_CREDITS", 0x30c, 0 }, { "TP_CM_SIZE", 0x310, 0 }, { "TP_CM_MM_BASE", 0x314, 0 }, { "CM_MemMgr_Base", 0, 28 }, { "TP_CM_TIMER_BASE", 0x318, 0 }, { "CM_Timer_Base", 0, 28 }, { "TP_PM_SIZE", 0x31c, 0 }, { "TP_PM_TX_BASE", 0x320, 0 }, { "TP_PM_DEFRAG_BASE", 0x324, 0 }, { "TP_PM_RX_BASE", 0x328, 0 }, { "TP_PM_RX_PG_SIZE", 0x32c, 0 }, { "TP_PM_RX_MAX_PGS", 0x330, 0 }, { "TP_PM_TX_PG_SIZE", 0x334, 0 }, { "TP_PM_TX_MAX_PGS", 0x338, 0 }, { "TP_TCP_OPTIONS", 0x340, 0 }, { "Timestamp", 0, 2 }, { "Window_Scale", 2, 2 }, { "SACK", 4, 2 }, { "ECN", 6, 2 }, { "SACK_Algorithm", 8, 2 }, { "MSS", 10, 1 }, { "Default_Peer_MSS", 16, 16 }, { "TP_DACK_CONFIG", 0x344, 0 }, { "DACK_Mode", 0, 1 }, { "DACK_Auto_Mgmt", 1, 1 }, { "DACK_Auto_Careful", 2, 1 }, { "DACK_MSS_Selector", 3, 2 }, { "DACK_Byte_Threshold", 5, 20 }, { "TP_PC_CONFIG", 0x348, 0 }, { "TP_Access_Latency", 0, 4 }, { "Held_FIN_Disable", 4, 1 }, { "DDP_FC_Enable", 5, 1 }, { "RDMA_Err_Enable", 6, 1 }, { "Fast_PDU_Delivery", 7, 1 }, { "Clear_FIN", 8, 1 }, { "TP_PC_Rev", 30, 2 }, { "TP_BACKOFF0", 0x350, 0 }, { "Element0", 0, 8 }, { "Element1", 8, 8 }, { "Element2", 16, 8 }, { "Element3", 24, 8 }, { "TP_BACKOFF1", 0x354, 0 }, { "Element0", 0, 8 }, { "Element1", 8, 8 }, { "Element2", 16, 8 }, { "Element3", 24, 8 }, { "TP_BACKOFF2", 0x358, 0 }, { "Element0", 0, 8 }, { "Element1", 8, 8 }, { "Element2", 16, 8 }, { "Element3", 24, 8 }, { "TP_BACKOFF3", 0x35c, 0 }, { "Element0", 0, 8 }, { "Element1", 8, 8 }, { "Element2", 16, 8 }, { "Element3", 24, 8 }, { "TP_PARA_REG0", 0x360, 0 }, { "Var_Mult", 0, 4 }, { "Var_Gain", 4, 4 }, { "SRTT_Gain", 8, 4 }, { "RTTVar_Init", 12, 4 }, { "Dup_Thresh", 20, 4 }, { "Init_Cong_Win", 24, 3 }, { "TP_PARA_REG1", 0x364, 0 }, { "Initial_Slow_Start_Threshold", 0, 16 }, { "Receive_Buffer_Size", 16, 16 }, { "TP_PARA_REG2", 0x368, 0 }, { "RX_Coalesce_Size", 0, 16 }, { "MAX_RX_Size", 16, 16 }, { "TP_PARA_REG3", 0x36c, 0 }, { "RX_Coalescing_PSH_Deliver", 0, 1 }, { "RX_Coalescing_Enable", 1, 1 }, { "Tahoe_Enable", 2, 1 }, { "MAX_Reorder_Fragments", 12, 3 }, { "TP_TIMER_RESOLUTION", 0x390, 0 }, { "Delayed_ACK_Timer_Resolution", 0, 6 }, { "Generic_Timer_Resolution", 16, 6 }, { "TP_2MSL", 0x394, 0 }, { "2MSL", 0, 30 }, { "TP_RXT_MIN", 0x398, 0 }, { "Retransmit_Timer_MIN", 0, 16 }, { "TP_RXT_MAX", 0x39c, 0 }, { "Retransmit_Timer_MAX", 0, 30 }, { "TP_PERS_MIN", 0x3a0, 0 }, { "Persist_Timer_MIN", 0, 16 }, { "TP_PERS_MAX", 0x3a4, 0 }, { "Persist_Timer_MAX", 0, 30 }, { "TP_KEEP_IDLE", 0x3ac, 0 }, { "Keep_Alive_Idle_Time", 0, 30 }, { "TP_KEEP_INTVL", 0x3b0, 0 }, { "Keep_Alive_Interval_Time", 0, 30 }, { "TP_INIT_SRTT", 0x3b4, 0 }, { "Initial_SRTT", 0, 16 }, { "TP_DACK_TIME", 0x3b8, 0 }, { "Delayed_ACK_Time", 0, 11 }, { "TP_FINWAIT2_TIME", 0x3bc, 0 }, { "FINWAIT2_TIME", 0, 30 }, { "TP_FAST_FINWAIT2_TIME", 0x3c0, 0 }, { "Fast_FINWAIT2_Time", 0, 30 }, { "TP_SHIFT_CNT", 0x3c4, 0 }, { "KeepAlive_MAX", 0, 8 }, { "WindowProbe_MAX", 8, 8 }, { "Retransmission_MAX", 16, 8 }, { "SYN_MAX", 24, 8 }, { "TP_QOS_REG0", 0x3e0, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG1", 0x3e4, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG2", 0x3e8, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG3", 0x3ec, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG4", 0x3f0, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG5", 0x3f4, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG6", 0x3f8, 0 }, { "L3_Value", 0, 6 }, { "TP_QOS_REG7", 0x3fc, 0 }, { "L3_Value", 0, 6 }, { "TP_MTU_REG0", 0x404, 0 }, { "TP_MTU_REG1", 0x408, 0 }, { "TP_MTU_REG2", 0x40c, 0 }, { "TP_MTU_REG3", 0x410, 0 }, { "TP_MTU_REG4", 0x414, 0 }, { "TP_MTU_REG5", 0x418, 0 }, { "TP_MTU_REG6", 0x41c, 0 }, { "TP_MTU_REG7", 0x420, 0 }, { "TP_RESET", 0x44c, 0 }, { "TP_Reset", 0, 1 }, { "CM_MemMgr_Init", 1, 1 }, { "TP_MIB_INDEX", 0x450, 0 }, { "TP_MIB_DATA", 0x454, 0 }, { "TP_SYNC_TIME_HI", 0x458, 0 }, { "TP_SYNC_TIME_LO", 0x45c, 0 }, { "TP_CM_MM_RX_FLST_BASE", 0x460, 0 }, { "CM_MemMgr_RX_Free_List_Base", 0, 28 }, { "TP_CM_MM_TX_FLST_BASE", 0x464, 0 }, { "CM_MemMgr_TX_Free_List_Base", 0, 28 }, { "TP_CM_MM_P_FLST_BASE", 0x468, 0 }, { "CM_MemMgr_PStruct_Free_List_Base", 0, 28 }, { "TP_CM_MM_MAX_P", 0x46c, 0 }, { "CM_MemMgr_MAX_PStruct", 0, 28 }, { "TP_INT_ENABLE", 0x470, 0 }, { "TX_Free_List_Empty", 0, 1 }, { "RX_Free_List_Empty", 1, 1 }, { "TP_INT_CAUSE", 0x474, 0 }, { "TX_Free_List_Empty", 0, 1 }, { "RX_Free_List_Empty", 1, 1 }, { "TP_FLM_FREE_PSTRUCT_CNT", 0x480, 0 }, { "TP_FLM_FREE_RX_PG_CNT", 0x484, 0 }, { "TP_FLM_FREE_TX_PG_CNT", 0x488, 0 }, { "TP_HEAP_PUSH_CNT", 0x48c, 0 }, { "TP_HEAP_POP_CNT", 0x490, 0 }, { "TP_DACK_PUSH_CNT", 0x494, 0 }, { "TP_DACK_POP_CNT", 0x498, 0 }, { "TP_MOD_PUSH_CNT", 0x49c, 0 }, { "TP_MOD_POP_CNT", 0x4a0, 0 }, { "TP_TIMER_SEPARATOR", 0x4a4, 0 }, { "Disable_Past_Timer_Insertion", 0, 1 }, { "Modulation_Timer_Separator", 1, 15 }, { "Global_Timer_Separator", 16, 16 }, { "TP_DEBUG_SEL", 0x4a8, 0 }, { "TP_CM_FC_MODE", 0x4b0, 0 }, { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 }, { "TP_TX_DROP_CONFIG", 0x4b8, 0 }, { "ENABLE_TX_DROP", 31, 1 }, { "ENABLE_TX_ERROR", 30, 1 }, { "DROP_TICKS_CNT", 4, 26 }, { "NUM_PKTS_DROPPED", 0, 4 }, { "TP_TX_DROP_COUNT", 0x4bc, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info rat_regs[] = { { "RAT_ROUTE_CONTROL", 0x580, 0 }, { "Use_Route_Table", 0, 1 }, { "Enable_CSPI", 1, 1 }, { "Enable_PCIX", 2, 1 }, { "RAT_ROUTE_TABLE_INDEX", 0x584, 0 }, { "Route_Table_Index", 0, 4 }, { "RAT_ROUTE_TABLE_DATA", 0x588, 0 }, { "RAT_NO_ROUTE", 0x58c, 0 }, { "CPL_Opcode", 0, 8 }, { "RAT_INTR_ENABLE", 0x590, 0 }, { "ZeroRouteError", 0, 1 }, { "CspiFramingError", 1, 1 }, { "SgeFramingError", 2, 1 }, { "TpFramingError", 3, 1 }, { "RAT_INTR_CAUSE", 0x594, 0 }, { "ZeroRouteError", 0, 1 }, { "CspiFramingError", 1, 1 }, { "SgeFramingError", 2, 1 }, { "TpFramingError", 3, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info cspi_regs[] = { { "CSPI_RX_AE_WM", 0x810, 0 }, { "CSPI_RX_AF_WM", 0x814, 0 }, { "CSPI_CALENDAR_LEN", 0x818, 0 }, { "CalendarLength", 0, 16 }, { "CSPI_FIFO_STATUS_ENABLE", 0x820, 0 }, { "FifoStatusEnable", 0, 1 }, { "CSPI_MAXBURST1_MAXBURST2", 0x828, 0 }, { "MaxBurst1", 0, 16 }, { "MaxBurst2", 16, 16 }, { "CSPI_TRAIN", 0x82c, 0 }, { "CSPI_TRAIN_ALPHA", 0, 16 }, { "CSPI_TRAIN_DATA_MAXT", 16, 16 }, { "CSPI_INTR_STATUS", 0x848, 0 }, { "DIP4Err", 0, 1 }, { "RXDrop", 1, 1 }, { "TXDrop", 2, 1 }, { "RXOverflow", 3, 1 }, { "RAMParityErr", 4, 1 }, { "CSPI_INTR_ENABLE", 0x84c, 0 }, { "DIP4Err", 0, 1 }, { "RXDrop", 1, 1 }, { "TXDrop", 2, 1 }, { "RXOverflow", 3, 1 }, { "RAMParityErr", 4, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info espi_regs[] = { { "ESPI_SCH_TOKEN0", 0x880, 0 }, { "SchToken0", 0, 16 }, { "ESPI_SCH_TOKEN1", 0x884, 0 }, { "SchToken1", 0, 16 }, { "ESPI_SCH_TOKEN2", 0x888, 0 }, { "SchToken2", 0, 16 }, { "ESPI_SCH_TOKEN3", 0x88c, 0 }, { "SchToken3", 0, 16 }, { "ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK", 0x890, 0 }, { "AlmostEmpty", 0, 16 }, { "ESPI_RX_FIFO_ALMOST_FULL_WATERMARK", 0x894, 0 }, { "AlmostFull", 0, 16 }, { "ESPI_CALENDAR_LENGTH", 0x898, 0 }, { "CalendarLength", 0, 16 }, { "PORT_CONFIG", 0x89c, 0 }, { "RX_NPorts", 0, 8 }, { "TX_NPorts", 8, 8 }, { "ESPI_FIFO_STATUS_ENABLE", 0x8a0, 0 }, { "RXStatusEnable", 0, 1 }, { "TXDropEnable", 1, 1 }, { "RXEndianMode", 2, 1 }, { "TXEndianMode", 3, 1 }, { "Intel1010Mode", 4, 1 }, { "ESPI_MAXBURST1_MAXBURST2", 0x8a8, 0 }, { "MaxBurst1", 0, 16 }, { "MaxBurst2", 16, 16 }, { "ESPI_TRAIN", 0x8ac, 0 }, { "MaxTrainAlpha", 0, 16 }, { "MaxTrainData", 16, 16 }, { "RAM_STATUS", 0x8b0, 0 }, { "RXFIFOParityError", 0, 10 }, { "TXFIFOParityError", 10, 10 }, { "RXFIFOOverflow", 20, 10 }, { "TX_DROP_COUNT0", 0x8b4, 0 }, { "TXPort0DropCnt", 0, 16 }, { "TxPort1DropCnt", 16, 16 }, { "TX_DROP_COUNT1", 0x8b8, 0 }, { "TXPort2DropCnt", 0, 16 }, { "TxPort3DropCnt", 16, 16 }, { "RX_DROP_COUNT0", 0x8bc, 0 }, { "RXPort0DropCnt", 0, 16 }, { "RxPort1DropCnt", 16, 16 }, { "RX_DROP_COUNT1", 0x8c0, 0 }, { "RXPort2DropCnt", 0, 16 }, { "RxPort3DropCnt", 16, 16 }, { "DIP4_ERROR_COUNT", 0x8c4, 0 }, { "Dip4ErrorCnt", 0, 12 }, { "Dip4ErrorCntShadow", 12, 12 }, { "TriCN_RX_Train_Err", 24, 1 }, { "TriCN_RX_Training", 25, 1 }, { "TriCN_RX_Train_OK", 26, 1 }, { "ESPI_INTR_STATUS", 0x8c8, 0 }, { "DIP4Err", 0, 1 }, { "RXDrop", 1, 1 }, { "TXDrop", 2, 1 }, { "RXOverflow", 3, 1 }, { "RAMParityErr", 4, 1 }, { "DIP2ParityErr", 5, 1 }, { "ESPI_INTR_ENABLE", 0x8cc, 0 }, { "DIP4Err", 0, 1 }, { "RXDrop", 1, 1 }, { "TXDrop", 2, 1 }, { "RXOverflow", 3, 1 }, { "RAMParityErr", 4, 1 }, { "DIP2ParityErr", 5, 1 }, { "RX_DROP_THRESHOLD", 0x8d0, 0 }, { "ESPI_RX_RESET", 0x8ec, 0 }, { "ESPI_RX_LNK_RST", 0, 1 }, { "ESPI_RX_CORE_RST", 1, 1 }, { "RX_CLK_STATUS", 2, 1 }, { "ESPI_MISC_CONTROL", 0x8f0, 0 }, { "Out_of_Sync_Count", 0, 4 }, { "DIP2_Count_Mode_Enable", 4, 1 }, { "DIP2_Parity_Err_Thres", 5, 4 }, { "DIP4_Thres", 9, 12 }, { "DIP4_Thres_Enable", 21, 1 }, { "Force_Disable_Status", 22, 1 }, { "Dynamic_Deskew", 23, 1 }, { "Monitored_Port_Num", 25, 2 }, { "Monitored_Direction", 27, 1 }, { "Monitored_Interface", 28, 1 }, { "ESPI_DIP2_ERR_COUNT", 0x8f4, 0 }, { "DIP2_Err_Cnt", 0, 4 }, { "ESPI_CMD_ADDR", 0x8f8, 0 }, { "Write_Data", 0, 8 }, { "Register_Offset", 8, 4 }, { "Channel_Addr", 12, 4 }, { "Module_Addr", 16, 2 }, { "Bundle_Addr", 20, 2 }, { "SPI4_Command", 24, 8 }, { "ESPI_GOSTAT", 0x8fc, 0 }, { "Read_Data", 0, 8 }, { "ESPI_Cmd_Busy", 8, 1 }, { "Error_Ack", 9, 1 }, { "Unmapped_Err", 10, 1 }, { "Transaction_Timer", 16, 8 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info ulp_regs[] = { { "ULP_ULIMIT", 0x980, 0 }, { "ULP_TAGMASK", 0x984, 0 }, { "ULP_HREG_INDEX", 0x988, 0 }, { "ULP_HREG_DATA", 0x98c, 0 }, { "ULP_INT_ENABLE", 0x990, 0 }, { "ULP_INT_CAUSE", 0x994, 0 }, { "Hreg_Par_Err", 0, 1 }, { "Egrs_Data_Par_Err", 1, 1 }, { "Ingrs_Data_Par_Err", 2, 1 }, { "Pm_Intr", 3, 1 }, { "Pm_E2C_Sync_Err", 4, 1 }, { "Pm_C2E_Sync_Err", 5, 1 }, { "Pm_E2C_Empty_Err", 6, 1 }, { "Pm_C2E_Empty_Err", 7, 1 }, { "Pm_Par_Err", 8, 16 }, { "Pm_E2C_Wrt_Full", 24, 1 }, { "Pm_C2E_Wrt_Full", 25, 1 }, { "ULP_PIO_CTRL", 0x998, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info pl_regs[] = { { "PL_ENABLE", 0xa00, 0 }, { "PL_Intr_SGE_Err", 0, 1 }, { "PL_Intr_SGE_Data", 1, 1 }, { "PL_Intr_MC3", 2, 1 }, { "PL_Intr_MC4", 3, 1 }, { "PL_Intr_MC5", 4, 1 }, { "PL_Intr_RAT", 5, 1 }, { "PL_Intr_TP", 6, 1 }, { "PL_Intr_ULP", 7, 1 }, { "PL_Intr_ESPI", 8, 1 }, { "PL_Intr_CSPI", 9, 1 }, { "PL_Intr_PCIX", 10, 1 }, { "PL_Intr_EXT", 11, 1 }, { "PL_CAUSE", 0xa04, 0 }, { "PL_Intr_SGE_Err", 0, 1 }, { "PL_Intr_SGE_Data", 1, 1 }, { "PL_Intr_MC3", 2, 1 }, { "PL_Intr_MC4", 3, 1 }, { "PL_Intr_MC5", 4, 1 }, { "PL_Intr_RAT", 5, 1 }, { "PL_Intr_TP", 6, 1 }, { "PL_Intr_ULP", 7, 1 }, { "PL_Intr_ESPI", 8, 1 }, { "PL_Intr_CSPI", 9, 1 }, { "PL_Intr_PCIX", 10, 1 }, { "PL_Intr_EXT", 11, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc5_regs[] = { { "MC5_CONFIG", 0xc04, 0 }, { "Mode", 0, 1 }, { "TCAM_Reset", 1, 1 }, { "TCAM_Ready", 2, 1 }, { "DBGI_Enable", 4, 1 }, { "M_Bus_Enable", 5, 1 }, { "Parity_Enable", 6, 1 }, { "SYN_Issue_Mode", 7, 2 }, { "Build", 16, 1 }, { "Compression_Enable", 17, 1 }, { "Num_LIP", 18, 6 }, { "TCAM_Part_Cnt", 24, 2 }, { "TCAM_Part_Type", 26, 2 }, { "TCAM_Part_Size", 28, 2 }, { "TCAM_Part_Type_HI", 30, 1 }, { "MC5_SIZE", 0xc08, 0 }, { "Size", 0, 22 }, { "MC5_ROUTING_TABLE_INDEX", 0xc0c, 0 }, { "Start_of_Routing_Table", 0, 22 }, { "MC5_SERVER_INDEX", 0xc14, 0 }, { "Start_of_Server_Index", 0, 22 }, { "MC5_LIP_RAM_ADDR", 0xc18, 0 }, { "Local_IP_RAM_Addr", 0, 6 }, { "RAM_Write_Enable", 8, 1 }, { "MC5_LIP_RAM_DATA", 0xc1c, 0 }, { "MC5_RSP_LATENCY", 0xc20, 0 }, { "Search_Response_Latency", 0, 5 }, { "Learn_Response_Latency", 8, 5 }, { "MC5_PARITY_LATENCY", 0xc24, 0 }, { "SRCHLAT", 0, 5 }, { "PARLAT", 8, 5 }, { "MC5_WR_LRN_VERIFY", 0xc28, 0 }, { "POVEREN", 0, 1 }, { "LRNVEREN", 1, 1 }, { "VWVEREN", 2, 1 }, { "MC5_PART_ID_INDEX", 0xc2c, 0 }, { "IDINDEX", 0, 4 }, { "MC5_RESET_MAX", 0xc30, 0 }, { "RSTMAX", 0, 9 }, { "MC5_INT_ENABLE", 0xc40, 0 }, { "MC5_Int_Hit_Out_Active_Region_Err", 0, 1 }, { "MC5_Int_Hit_In_Active_Region_Err", 1, 1 }, { "MC5_Int_Hit_In_RT_Region_Err", 2, 1 }, { "MC5_Int_Miss_Err", 3, 1 }, { "MC5_Int_LIP0_Err", 4, 1 }, { "MC5_Int_LIP_Miss_Err", 5, 1 }, { "MC5_Int_Parity_Err", 6, 1 }, { "MC5_Int_Active_Region_Full", 7, 1 }, { "MC5_Int_NFA_Srch_Err", 8, 1 }, { "MC5_Int_SYN_Cookie", 9, 1 }, { "MC5_Int_SYN_Cookie_Bad", 10, 1 }, { "MC5_Int_SYN_Cookie_Off", 11, 1 }, { "MC5_Int_Unknown_Cmd", 15, 1 }, { "MC5_Int_RequestQ_Parity_Err", 16, 1 }, { "MC5_Int_DispatchQ_Parity_Err", 17, 1 }, { "MC5_Int_Del_Act_Empty", 18, 1 }, { "MC5_INT_CAUSE", 0xc44, 0 }, { "MC5_Int_Hit_Out_Active_Region_Err", 0, 1 }, { "MC5_Int_Hit_In_Active_Region_Err", 1, 1 }, { "MC5_Int_Hit_In_RT_Region_Err", 2, 1 }, { "MC5_Int_Miss_Err", 3, 1 }, { "MC5_Int_LIP0_Err", 4, 1 }, { "MC5_Int_LIP_Miss_Err", 5, 1 }, { "MC5_Int_Parity_Err", 6, 1 }, { "MC5_Int_Active_Region_Full", 7, 1 }, { "MC5_Int_NFA_Srch_Err", 8, 1 }, { "MC5_Int_SYN_Cookie", 9, 1 }, { "MC5_Int_SYN_Cookie_Bad", 10, 1 }, { "MC5_Int_SYN_Cookie_Off", 11, 1 }, { "MC5_Int_Unknown_Cmd", 15, 1 }, { "MC5_Int_RequestQ_Parity_Err", 16, 1 }, { "MC5_Int_DispatchQ_Parity_Err", 17, 1 }, { "MC5_Int_Del_Act_Empty", 18, 1 }, { "MC5_INT_TID", 0xc48, 0 }, { "MC5_INT_PTID", 0xc4c, 0 }, { "MC5_DBGI_CONFIG", 0xc74, 0 }, { "MC5_DBGI_REQ_CMD", 0xc78, 0 }, { "CmdMode", 0, 3 }, { "SADRSEL", 4, 1 }, { "Write_Burst_Size", 22, 10 }, { "MC5_DBGI_REQ_ADDR0", 0xc7c, 0 }, { "MC5_DBGI_REQ_ADDR1", 0xc80, 0 }, { "MC5_DBGI_REQ_ADDR2", 0xc84, 0 }, { "MC5_DBGI_REQ_DATA0", 0xc88, 0 }, { "MC5_DBGI_REQ_DATA1", 0xc8c, 0 }, { "MC5_DBGI_REQ_DATA2", 0xc90, 0 }, { "MC5_DBGI_REQ_DATA3", 0xc94, 0 }, { "MC5_DBGI_REQ_DATA4", 0xc98, 0 }, { "MC5_DBGI_REQ_MASK0", 0xc9c, 0 }, { "MC5_DBGI_REQ_MASK1", 0xca0, 0 }, { "MC5_DBGI_REQ_MASK2", 0xca4, 0 }, { "MC5_DBGI_REQ_MASK3", 0xca8, 0 }, { "MC5_DBGI_REQ_MASK4", 0xcac, 0 }, { "MC5_DBGI_RSP_STATUS", 0xcb0, 0 }, { "DBGI_Rsp_Valid", 0, 1 }, { "DBGI_Rsp_Hit", 1, 1 }, { "DBGI_Rsp_Err", 2, 1 }, { "DBGI_Rsp_Err_Reason", 8, 3 }, { "MC5_DBGI_RSP_DATA0", 0xcb4, 0 }, { "MC5_DBGI_RSP_DATA1", 0xcb8, 0 }, { "MC5_DBGI_RSP_DATA2", 0xcbc, 0 }, { "MC5_DBGI_RSP_DATA3", 0xcc0, 0 }, { "MC5_DBGI_RSP_DATA4", 0xcc4, 0 }, { "MC5_DBGI_RSP_LAST_CMD", 0xcc8, 0 }, { "MC5_POPEN_DATA_WR_CMD", 0xccc, 0 }, { "MC5_POPEN_MASK_WR_CMD", 0xcd0, 0 }, { "MC5_AOPEN_SRCH_CMD", 0xcd4, 0 }, { "MC5_AOPEN_LRN_CMD", 0xcd8, 0 }, { "MC5_SYN_SRCH_CMD", 0xcdc, 0 }, { "MC5_SYN_LRN_CMD", 0xce0, 0 }, { "MC5_ACK_SRCH_CMD", 0xce4, 0 }, { "MC5_ACK_LRN_CMD", 0xce8, 0 }, { "MC5_ILOOKUP_CMD", 0xcec, 0 }, { "MC5_ELOOKUP_CMD", 0xcf0, 0 }, { "MC5_DATA_WRITE_CMD", 0xcf4, 0 }, { "MC5_DATA_READ_CMD", 0xcf8, 0 }, { "MC5_MASK_WRITE_CMD", 0xcfc, 0 }, - { NULL } + { NULL, 0, 0 } }; Index: projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3.c =================================================================== --- projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3.c (revision 204271) +++ projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3.c (revision 204272) @@ -1,2676 +1,2676 @@ /* * $FreeBSD$ */ /* This file is automatically generated --- do not edit */ struct reg_info sge3_regs[] = { { "SG_CONTROL", 0x0, 0 }, { "EgrEnUpBp", 21, 1 }, { "DropPkt", 20, 1 }, { "EgrGenCtrl", 19, 1 }, { "UserSpaceSize", 14, 5 }, { "HostPageSize", 11, 3 }, { "PCIRelax", 10, 1 }, { "FLMode", 9, 1 }, { "PktShift", 6, 3 }, { "OneIntMultQ", 5, 1 }, { "FLPickAvail", 4, 1 }, { "BigEndianEgress", 3, 1 }, { "BigEndianIngress", 2, 1 }, { "IscsiCoalescing", 1, 1 }, { "GlobalEnable", 0, 1 }, { "SG_KDOORBELL", 0x4, 0 }, { "SelEgrCntx", 31, 1 }, { "EgrCntx", 0, 16 }, { "SG_GTS", 0x8, 0 }, { "RspQ", 29, 3 }, { "NewTimer", 16, 13 }, { "NewIndex", 0, 16 }, { "SG_CONTEXT_CMD", 0xc, 0 }, { "Opcode", 28, 4 }, { "Busy", 27, 1 }, { "CQ_credit", 20, 7 }, { "CQ", 19, 1 }, { "RspQ", 18, 1 }, { "Egress", 17, 1 }, { "FreeList", 16, 1 }, { "Context", 0, 16 }, { "SG_CONTEXT_DATA0", 0x10, 0 }, { "SG_CONTEXT_DATA1", 0x14, 0 }, { "SG_CONTEXT_DATA2", 0x18, 0 }, { "SG_CONTEXT_DATA3", 0x1c, 0 }, { "SG_CONTEXT_MASK0", 0x20, 0 }, { "SG_CONTEXT_MASK1", 0x24, 0 }, { "SG_CONTEXT_MASK2", 0x28, 0 }, { "SG_CONTEXT_MASK3", 0x2c, 0 }, { "SG_RSPQ_CREDIT_RETURN", 0x30, 0 }, { "RspQ", 29, 3 }, { "Data", 0, 16 }, { "SG_HI_DRB_HI_THRSH", 0x38, 0 }, { "HiDrbHiThrsh", 0, 10 }, { "SG_HI_DRB_LO_THRSH", 0x3c, 0 }, { "HiDrbLoThrsh", 0, 10 }, { "SG_LO_DRB_HI_THRSH", 0x40, 0 }, { "LoDrbHiThrsh", 0, 10 }, { "SG_LO_DRB_LO_THRSH", 0x44, 0 }, { "LoDrbLoThrsh", 0, 10 }, { "SG_ONE_INT_MULT_Q_COALESCING_TIMER", 0x48, 0 }, { "SG_RSPQ_FL_STATUS", 0x4c, 0 }, { "RspQ0Starved", 0, 1 }, { "RspQ1Starved", 1, 1 }, { "RspQ2Starved", 2, 1 }, { "RspQ3Starved", 3, 1 }, { "RspQ4Starved", 4, 1 }, { "RspQ5Starved", 5, 1 }, { "RspQ6Starved", 6, 1 }, { "RspQ7Starved", 7, 1 }, { "RspQ0Disabled", 8, 1 }, { "RspQ1Disabled", 9, 1 }, { "RspQ2Disabled", 10, 1 }, { "RspQ3Disabled", 11, 1 }, { "RspQ4Disabled", 12, 1 }, { "RspQ5Disabled", 13, 1 }, { "RspQ6Disabled", 14, 1 }, { "RspQ7Disabled", 15, 1 }, { "FL0Empty", 16, 1 }, { "FL1Empty", 17, 1 }, { "FL2Empty", 18, 1 }, { "FL3Empty", 19, 1 }, { "FL4Empty", 20, 1 }, { "FL5Empty", 21, 1 }, { "FL6Empty", 22, 1 }, { "FL7Empty", 23, 1 }, { "FL8Empty", 24, 1 }, { "FL9Empty", 25, 1 }, { "FL10Empty", 26, 1 }, { "FL11Empty", 27, 1 }, { "FL12Empty", 28, 1 }, { "FL13Empty", 29, 1 }, { "FL14Empty", 30, 1 }, { "FL15Empty", 31, 1 }, { "SG_EGR_PRI_CNT", 0x50, 0 }, { "EgrPriCnt", 0, 5 }, { "SG_EGR_RCQ_DRB_THRSH", 0x54, 0 }, { "HiRcqDrbThrsh", 16, 11 }, { "LoRcqDrbThrsh", 0, 11 }, { "SG_EGR_CNTX_BADDR", 0x58, 0 }, { "EgrCntxBAddr", 5, 27 }, { "SG_INT_CAUSE", 0x5c, 0 }, { "HiCtlDrbDropErr", 13, 1 }, { "LoCtlDrbDropErr", 12, 1 }, { "HiPioDrbDropErr", 11, 1 }, { "LoPioDrbDropErr", 10, 1 }, { "HiCrdtUndFlowErr", 9, 1 }, { "LoCrdtUndFlowErr", 8, 1 }, { "HiPriorityDBFull", 7, 1 }, { "HiPriorityDBEmpty", 6, 1 }, { "LoPriorityDBFull", 5, 1 }, { "LoPriorityDBEmpty", 4, 1 }, { "RspQDisabled", 3, 1 }, { "RspQCreditOverfow", 2, 1 }, { "FlEmpty", 1, 1 }, { "RspQStarve", 0, 1 }, { "SG_INT_ENABLE", 0x60, 0 }, { "HiCtlDrbDropErr", 13, 1 }, { "LoCtlDrbDropErr", 12, 1 }, { "HiPioDrbDropErr", 11, 1 }, { "LoPioDrbDropErr", 10, 1 }, { "HiCrdtUndFlowErr", 9, 1 }, { "LoCrdtUndFlowErr", 8, 1 }, { "HiPriorityDBFull", 7, 1 }, { "HiPriorityDBEmpty", 6, 1 }, { "LoPriorityDBFull", 5, 1 }, { "LoPriorityDBEmpty", 4, 1 }, { "RspQDisabled", 3, 1 }, { "RspQCreditOverfow", 2, 1 }, { "FlEmpty", 1, 1 }, { "RspQStarve", 0, 1 }, { "SG_CMDQ_CREDIT_TH", 0x64, 0 }, { "Timeout", 8, 24 }, { "Threshold", 0, 8 }, { "SG_TIMER_TICK", 0x68, 0 }, { "SG_CQ_CONTEXT_BADDR", 0x6c, 0 }, { "baseAddr", 5, 27 }, { "SG_OCO_BASE", 0x70, 0 }, { "Base1", 16, 16 }, { "Base0", 0, 16 }, { "SG_DRB_PRI_THRESH", 0x74, 0 }, { "DrbPriThrsh", 0, 16 }, { "SG_DEBUG_INDEX", 0x78, 0 }, { "SG_DEBUG_DATA", 0x7c, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info pcix1_regs[] = { { "PCIX_INT_ENABLE", 0x80, 0 }, { "MSIXParErr", 22, 3 }, { "CFParErr", 18, 4 }, { "RFParErr", 14, 4 }, { "WFParErr", 12, 2 }, { "PIOParErr", 11, 1 }, { "DetUncECCErr", 10, 1 }, { "DetCorECCErr", 9, 1 }, { "RcvSplCmpErr", 8, 1 }, { "UnxSplCmp", 7, 1 }, { "SplCmpDis", 6, 1 }, { "DetParErr", 5, 1 }, { "SigSysErr", 4, 1 }, { "RcvMstAbt", 3, 1 }, { "RcvTarAbt", 2, 1 }, { "SigTarAbt", 1, 1 }, { "MstDetParErr", 0, 1 }, { "PCIX_INT_CAUSE", 0x84, 0 }, { "MSIXParErr", 22, 3 }, { "CFParErr", 18, 4 }, { "RFParErr", 14, 4 }, { "WFParErr", 12, 2 }, { "PIOParErr", 11, 1 }, { "DetUncECCErr", 10, 1 }, { "DetCorECCErr", 9, 1 }, { "RcvSplCmpErr", 8, 1 }, { "UnxSplCmp", 7, 1 }, { "SplCmpDis", 6, 1 }, { "DetParErr", 5, 1 }, { "SigSysErr", 4, 1 }, { "RcvMstAbt", 3, 1 }, { "RcvTarAbt", 2, 1 }, { "SigTarAbt", 1, 1 }, { "MstDetParErr", 0, 1 }, { "PCIX_CFG", 0x88, 0 }, { "CLIDecEn", 18, 1 }, { "LatTmrDis", 17, 1 }, { "LowPwrEn", 16, 1 }, { "AsyncIntVec", 11, 5 }, { "MaxSplTrnC", 8, 3 }, { "MaxSplTrnR", 5, 3 }, { "MaxWrByteCnt", 3, 2 }, { "WrReqAtomicEn", 2, 1 }, { "CRstWrmMode", 1, 1 }, { "PIOAck64En", 0, 1 }, { "PCIX_MODE", 0x8c, 0 }, { "PClkRange", 6, 2 }, { "PCIXInitPat", 2, 4 }, { "66MHz", 1, 1 }, { "64Bit", 0, 1 }, { "PCIX_CAL", 0x90, 0 }, { "Busy", 31, 1 }, { "PerCalDiv", 22, 8 }, { "PerCalEn", 21, 1 }, { "SglCalEn", 20, 1 }, { "ZInUpdMode", 19, 1 }, { "ZInSel", 18, 1 }, { "ZPDMan", 15, 3 }, { "ZPUMan", 12, 3 }, { "ZPDOut", 9, 3 }, { "ZPUOut", 6, 3 }, { "ZPDIn", 3, 3 }, { "ZPUIn", 0, 3 }, { "PCIX_WOL", 0x94, 0 }, { "WakeUp1", 3, 1 }, { "WakeUp0", 2, 1 }, { "SleepMode1", 1, 1 }, { "SleepMode0", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info pcie0_regs[] = { { "PCIE_INT_ENABLE", 0x80, 0 }, { "BISTErr", 15, 8 }, { "MSIXParErr", 12, 3 }, { "CFParErr", 11, 1 }, { "RFParErr", 10, 1 }, { "WFParErr", 9, 1 }, { "PIOParErr", 8, 1 }, { "UnxSplCplErrC", 7, 1 }, { "UnxSplCplErrR", 6, 1 }, { "VPDAddrChng", 5, 1 }, { "BusMstrEn", 4, 1 }, { "PMStChng", 3, 1 }, { "PEXMsg", 2, 1 }, { "ZeroLenRd", 1, 1 }, { "PEXErr", 0, 1 }, { "PCIE_INT_CAUSE", 0x84, 0 }, { "BISTErr", 15, 8 }, { "MSIXParErr", 12, 3 }, { "CFParErr", 11, 1 }, { "RFParErr", 10, 1 }, { "WFParErr", 9, 1 }, { "PIOParErr", 8, 1 }, { "UnxSplCplErrC", 7, 1 }, { "UnxSplCplErrR", 6, 1 }, { "VPDAddrChng", 5, 1 }, { "BusMstrEn", 4, 1 }, { "PMStChng", 3, 1 }, { "PEXMsg", 2, 1 }, { "ZeroLenRd", 1, 1 }, { "PEXErr", 0, 1 }, { "PCIE_CFG", 0x88, 0 }, { "EnableLinkDwnDRst", 21, 1 }, { "EnableLinkDownRst", 20, 1 }, { "EnableHotRst", 19, 1 }, { "IniWaitForGnt", 18, 1 }, { "IniBEDis", 17, 1 }, { "CLIDecEn", 16, 1 }, { "AsyncIntVec", 11, 5 }, { "MaxSplTrnC", 7, 4 }, { "MaxSplTrnR", 1, 6 }, { "CRstWrmMode", 0, 1 }, { "PCIE_MODE", 0x8c, 0 }, { "LnkCntlState", 2, 8 }, { "VC0Up", 1, 1 }, { "LnkInitial", 0, 1 }, { "PCIE_CAL", 0x90, 0 }, { "CalBusy", 31, 1 }, { "CalFault", 30, 1 }, { "ZInSel", 11, 1 }, { "ZMan", 8, 3 }, { "ZOut", 3, 5 }, { "ZIn", 0, 3 }, { "PCIE_WOL", 0x94, 0 }, { "WakeUp1", 3, 1 }, { "WakeUp0", 2, 1 }, { "SleepMode1", 1, 1 }, { "SleepMode0", 0, 1 }, { "PCIE_PEX_CTRL0", 0x98, 0 }, { "NumFstTrnSeq", 22, 8 }, { "ReplayLmt", 2, 20 }, { "TxPndChkEn", 1, 1 }, { "CplPndChkEn", 0, 1 }, { "PCIE_PEX_CTRL1", 0x9c, 0 }, { "DLLPTimeoutLmt", 11, 20 }, { "AckLat", 0, 11 }, { "PCIE_PEX_CTRL2", 0xa0, 0 }, { "PMExitL1Req", 29, 1 }, { "PMTxIdle", 28, 1 }, { "PCIModeLoop", 27, 1 }, { "L1ASPMTxRxL0sTime", 15, 12 }, { "L0sIdleTime", 4, 11 }, { "EnterL23", 3, 1 }, { "EnterL1ASPMEn", 2, 1 }, { "EnterL1En", 1, 1 }, { "EnterL0sEn", 0, 1 }, { "PCIE_PEX_ERR", 0xa4, 0 }, { "FlowCtlOFlowErr", 17, 1 }, { "ReplayTimeout", 16, 1 }, { "ReplayRollover", 15, 1 }, { "BadDLLP", 14, 1 }, { "DLLPErr", 13, 1 }, { "FlowCtlProtErr", 12, 1 }, { "CplTimeout", 11, 1 }, { "PHYRcvErr", 10, 1 }, { "DisTLP", 9, 1 }, { "BadECRC", 8, 1 }, { "BadTLP", 7, 1 }, { "MalTLP", 6, 1 }, { "UnxCpl", 5, 1 }, { "UnsReq", 4, 1 }, { "PsnReq", 3, 1 }, { "UnsCpl", 2, 1 }, { "CplAbt", 1, 1 }, { "PsnCpl", 0, 1 }, { "PCIE_PIPE_CTRL", 0xa8, 0 }, { "RecDetUsec", 19, 3 }, { "PLLLckCyc", 6, 13 }, { "ElecIdleDetCyc", 3, 3 }, { "UseCDRLOS", 2, 1 }, { "PClkReqInP1", 1, 1 }, { "PClkOffInP1", 0, 1 }, { "PCIE_SERDES_CTRL", 0xac, 0 }, { "ManMode", 31, 1 }, { "ManLpbkEn", 29, 2 }, { "ManTxRecDetEn", 28, 1 }, { "ManTxBeacon", 27, 1 }, { "ManTxEI", 26, 1 }, { "ManRxPolarity", 25, 1 }, { "ManTxRst", 24, 1 }, { "ManRxRst", 23, 1 }, { "ManTxEn", 22, 1 }, { "ManRxEn", 21, 1 }, { "ManEn", 20, 1 }, { "CMURange", 17, 3 }, { "BGEnb", 16, 1 }, { "EnSkpDrop", 15, 1 }, { "EnComma", 14, 1 }, { "En8B10B", 13, 1 }, { "EnElBuf", 12, 1 }, { "Gain", 7, 5 }, { "BandGap", 3, 4 }, { "RxComAdj", 2, 1 }, { "PreEmph", 0, 2 }, { "PCIE_SERDES_STATUS0", 0xb0, 0 }, { "RxErrLane7", 21, 3 }, { "RxErrLane6", 18, 3 }, { "RxErrLane5", 15, 3 }, { "RxErrLane4", 12, 3 }, { "RxErrLane3", 9, 3 }, { "RxErrLane2", 6, 3 }, { "RxErrLane1", 3, 3 }, { "RxErrLane0", 0, 3 }, { "PCIE_SERDES_STATUS1", 0xb4, 0 }, { "CMULock", 31, 1 }, { "RxKLockLane7", 23, 1 }, { "RxKLockLane6", 22, 1 }, { "RxKLockLane5", 21, 1 }, { "RxKLockLane4", 20, 1 }, { "RxKLockLane3", 19, 1 }, { "RxKLockLane2", 18, 1 }, { "RxKLockLane1", 17, 1 }, { "RxKLockLane0", 16, 1 }, { "RxUFlowLane7", 15, 1 }, { "RxUFlowLane6", 14, 1 }, { "RxUFlowLane5", 13, 1 }, { "RxUFlowLane4", 12, 1 }, { "RxUFlowLane3", 11, 1 }, { "RxUFlowLane2", 10, 1 }, { "RxUFlowLane1", 9, 1 }, { "RxUFlowLane0", 8, 1 }, { "RxOFlowLane7", 7, 1 }, { "RxOFlowLane6", 6, 1 }, { "RxOFlowLane5", 5, 1 }, { "RxOFlowLane4", 4, 1 }, { "RxOFlowLane3", 3, 1 }, { "RxOFlowLane2", 2, 1 }, { "RxOFlowLane1", 1, 1 }, { "RxOFlowLane0", 0, 1 }, { "PCIE_SERDES_STATUS2", 0xb8, 0 }, { "TxRecDetLane7", 31, 1 }, { "TxRecDetLane6", 30, 1 }, { "TxRecDetLane5", 29, 1 }, { "TxRecDetLane4", 28, 1 }, { "TxRecDetLane3", 27, 1 }, { "TxRecDetLane2", 26, 1 }, { "TxRecDetLane1", 25, 1 }, { "TxRecDetLane0", 24, 1 }, { "RxEIDLane7", 23, 1 }, { "RxEIDLane6", 22, 1 }, { "RxEIDLane5", 21, 1 }, { "RxEIDLane4", 20, 1 }, { "RxEIDLane3", 19, 1 }, { "RxEIDLane2", 18, 1 }, { "RxEIDLane1", 17, 1 }, { "RxEIDLane0", 16, 1 }, { "RxRemSkipLane7", 15, 1 }, { "RxRemSkipLane6", 14, 1 }, { "RxRemSkipLane5", 13, 1 }, { "RxRemSkipLane4", 12, 1 }, { "RxRemSkipLane3", 11, 1 }, { "RxRemSkipLane2", 10, 1 }, { "RxRemSkipLane1", 9, 1 }, { "RxRemSkipLane0", 8, 1 }, { "RxAddSkipLane7", 7, 1 }, { "RxAddSkipLane6", 6, 1 }, { "RxAddSkipLane5", 5, 1 }, { "RxAddSkipLane4", 4, 1 }, { "RxAddSkipLane3", 3, 1 }, { "RxAddSkipLane2", 2, 1 }, { "RxAddSkipLane1", 1, 1 }, { "RxAddSkipLane0", 0, 1 }, { "PCIE_SERDES_BIST", 0xbc, 0 }, { "BISTDone", 24, 8 }, { "BISTCycleThresh", 3, 16 }, { "BISTMode", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3dbg_regs[] = { { "T3DBG_DBG0_CFG", 0xc0, 0 }, { "RegSelect", 9, 8 }, { "ModuleSelect", 4, 5 }, { "ClkSelect", 0, 4 }, { "T3DBG_DBG0_EN", 0xc4, 0 }, { "SDRByte0", 8, 1 }, { "DDREn", 4, 1 }, { "PortEn", 0, 1 }, { "T3DBG_DBG1_CFG", 0xc8, 0 }, { "RegSelect", 9, 8 }, { "ModuleSelect", 4, 5 }, { "ClkSelect", 0, 4 }, { "T3DBG_DBG1_EN", 0xcc, 0 }, { "SDRByte0", 8, 1 }, { "DDREn", 4, 1 }, { "PortEn", 0, 1 }, { "T3DBG_GPIO_EN", 0xd0, 0 }, { "GPIO11_OEn", 27, 1 }, { "GPIO10_OEn", 26, 1 }, { "GPIO9_OEn", 25, 1 }, { "GPIO8_OEn", 24, 1 }, { "GPIO7_OEn", 23, 1 }, { "GPIO6_OEn", 22, 1 }, { "GPIO5_OEn", 21, 1 }, { "GPIO4_OEn", 20, 1 }, { "GPIO3_OEn", 19, 1 }, { "GPIO2_OEn", 18, 1 }, { "GPIO1_OEn", 17, 1 }, { "GPIO0_OEn", 16, 1 }, { "GPIO11_Out_Val", 11, 1 }, { "GPIO10_Out_Val", 10, 1 }, { "GPIO9_Out_Val", 9, 1 }, { "GPIO8_Out_Val", 8, 1 }, { "GPIO7_Out_Val", 7, 1 }, { "GPIO6_Out_Val", 6, 1 }, { "GPIO5_Out_Val", 5, 1 }, { "GPIO4_Out_Val", 4, 1 }, { "GPIO3_Out_Val", 3, 1 }, { "GPIO2_Out_Val", 2, 1 }, { "GPIO1_Out_Val", 1, 1 }, { "GPIO0_Out_Val", 0, 1 }, { "T3DBG_GPIO_IN", 0xd4, 0 }, { "GPIO11_IN", 11, 1 }, { "GPIO10_IN", 10, 1 }, { "GPIO9_IN", 9, 1 }, { "GPIO8_IN", 8, 1 }, { "GPIO7_IN", 7, 1 }, { "GPIO6_IN", 6, 1 }, { "GPIO5_IN", 5, 1 }, { "GPIO4_IN", 4, 1 }, { "GPIO3_IN", 3, 1 }, { "GPIO2_IN", 2, 1 }, { "GPIO1_IN", 1, 1 }, { "GPIO0_IN", 0, 1 }, { "T3DBG_INT_ENABLE", 0xd8, 0 }, { "C_LOCK", 21, 1 }, { "M_LOCK", 20, 1 }, { "U_LOCK", 19, 1 }, { "R_LOCK", 18, 1 }, { "PX_LOCK", 17, 1 }, { "PE_LOCK", 16, 1 }, { "GPIO11", 11, 1 }, { "GPIO10", 10, 1 }, { "GPIO9", 9, 1 }, { "GPIO8", 8, 1 }, { "GPIO7", 7, 1 }, { "GPIO6", 6, 1 }, { "GPIO5", 5, 1 }, { "GPIO4", 4, 1 }, { "GPIO3", 3, 1 }, { "GPIO2", 2, 1 }, { "GPIO1", 1, 1 }, { "GPIO0", 0, 1 }, { "T3DBG_INT_CAUSE", 0xdc, 0 }, { "C_LOCK", 21, 1 }, { "M_LOCK", 20, 1 }, { "U_LOCK", 19, 1 }, { "R_LOCK", 18, 1 }, { "PX_LOCK", 17, 1 }, { "PE_LOCK", 16, 1 }, { "GPIO11", 11, 1 }, { "GPIO10", 10, 1 }, { "GPIO9", 9, 1 }, { "GPIO8", 8, 1 }, { "GPIO7", 7, 1 }, { "GPIO6", 6, 1 }, { "GPIO5", 5, 1 }, { "GPIO4", 4, 1 }, { "GPIO3", 3, 1 }, { "GPIO2", 2, 1 }, { "GPIO1", 1, 1 }, { "GPIO0", 0, 1 }, { "T3DBG_DBG0_RST_VALUE", 0xe0, 0 }, { "DebugData", 0, 1 }, { "T3DBG_PLL_OCLK_PAD_EN", 0xe4, 0 }, { "PCIE_OCLK_En", 20, 1 }, { "PCIX_OCLK_En", 16, 1 }, { "U_OCLK_En", 12, 1 }, { "R_OCLK_En", 8, 1 }, { "M_OCLK_En", 4, 1 }, { "C_OCLK_En", 0, 1 }, { "T3DBG_PLL_LOCK", 0xe8, 0 }, { "PCIE_LOCK", 20, 1 }, { "PCIX_LOCK", 16, 1 }, { "U_LOCK", 12, 1 }, { "R_LOCK", 8, 1 }, { "M_LOCK", 4, 1 }, { "C_LOCK", 0, 1 }, { "T3DBG_SERDES_RBC_CFG", 0xec, 0 }, { "X_RBC_Lane_Sel", 16, 1 }, { "X_RBC_Dbg_En", 12, 1 }, { "X_Serdes_Sel", 8, 1 }, { "PE_RBC_Lane_Sel", 4, 1 }, { "PE_RBC_Dbg_En", 0, 1 }, { "T3DBG_GPIO_ACT_LOW", 0xf0, 0 }, { "C_LOCK_ACT_LOW", 21, 1 }, { "M_LOCK_ACT_LOW", 20, 1 }, { "U_LOCK_ACT_LOW", 19, 1 }, { "R_LOCK_ACT_LOW", 18, 1 }, { "PX_LOCK_ACT_LOW", 17, 1 }, { "PE_LOCK_ACT_LOW", 16, 1 }, { "GPIO11_ACT_LOW", 11, 1 }, { "GPIO10_ACT_LOW", 10, 1 }, { "GPIO9_ACT_LOW", 9, 1 }, { "GPIO8_ACT_LOW", 8, 1 }, { "GPIO7_ACT_LOW", 7, 1 }, { "GPIO6_ACT_LOW", 6, 1 }, { "GPIO5_ACT_LOW", 5, 1 }, { "GPIO4_ACT_LOW", 4, 1 }, { "GPIO3_ACT_LOW", 3, 1 }, { "GPIO2_ACT_LOW", 2, 1 }, { "GPIO1_ACT_LOW", 1, 1 }, { "GPIO0_ACT_LOW", 0, 1 }, { "T3DBG_PMON_CFG", 0xf4, 0 }, { "PMON_DONE", 29, 1 }, { "PMON_FAIL", 28, 1 }, { "PMON_FDEL_AUTO", 22, 1 }, { "PMON_CDEL_AUTO", 16, 1 }, { "PMON_FDEL_MANUAL", 10, 1 }, { "PMON_CDEL_MANUAL", 4, 1 }, { "PMON_MANUAL", 1, 1 }, { "PMON_AUTO", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc7_pmrx_regs[] = { { "MC7_CFG", 0x100, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x104, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x108, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x10c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x110, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x114, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x118, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x11c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x120, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x124, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x128, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ECC", 0x130, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x134, 0 }, { "MC7_CE_DATA0", 0x138, 0 }, { "MC7_CE_DATA1", 0x13c, 0 }, { "MC7_CE_DATA2", 0x140, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x144, 0 }, { "MC7_UE_DATA0", 0x148, 0 }, { "MC7_UE_DATA1", 0x14c, 0 }, { "MC7_UE_DATA2", 0x150, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x154, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x158, 0 }, { "MC7_BD_DATA1", 0x15c, 0 }, { "MC7_BD_DATA2", 0x160, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x164, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x168, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x16c, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x170, 0 }, { "MC7_BIST_OP", 0x174, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x178, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x17c, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc7_pmtx_regs[] = { { "MC7_CFG", 0x180, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x184, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x188, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x18c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x190, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x194, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x198, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x19c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x1a0, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x1a4, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x1a8, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ECC", 0x1b0, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x1b4, 0 }, { "MC7_CE_DATA0", 0x1b8, 0 }, { "MC7_CE_DATA1", 0x1bc, 0 }, { "MC7_CE_DATA2", 0x1c0, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x1c4, 0 }, { "MC7_UE_DATA0", 0x1c8, 0 }, { "MC7_UE_DATA1", 0x1cc, 0 }, { "MC7_UE_DATA2", 0x1d0, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x1d4, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x1d8, 0 }, { "MC7_BD_DATA1", 0x1dc, 0 }, { "MC7_BD_DATA2", 0x1e0, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x1e4, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x1e8, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x1ec, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x1f0, 0 }, { "MC7_BIST_OP", 0x1f4, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x1f8, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x1fc, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc7_cm_regs[] = { { "MC7_CFG", 0x200, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x204, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x208, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x20c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x210, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x214, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x218, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x21c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x220, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x224, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x228, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ECC", 0x230, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x234, 0 }, { "MC7_CE_DATA0", 0x238, 0 }, { "MC7_CE_DATA1", 0x23c, 0 }, { "MC7_CE_DATA2", 0x240, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x244, 0 }, { "MC7_UE_DATA0", 0x248, 0 }, { "MC7_UE_DATA1", 0x24c, 0 }, { "MC7_UE_DATA2", 0x250, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x254, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x258, 0 }, { "MC7_BD_DATA1", 0x25c, 0 }, { "MC7_BD_DATA2", 0x260, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x264, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x268, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x26c, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x270, 0 }, { "MC7_BIST_OP", 0x274, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x278, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x27c, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info cim_regs[] = { { "CIM_BOOT_CFG", 0x280, 0 }, { "BootAddr", 2, 30 }, { "BootSdram", 1, 1 }, { "uPCRst", 0, 1 }, { "CIM_FLASH_BASE_ADDR", 0x284, 0 }, { "FlashBaseAddr", 2, 22 }, { "CIM_FLASH_ADDR_SIZE", 0x288, 0 }, { "FlashAddrSize", 2, 22 }, { "CIM_SDRAM_BASE_ADDR", 0x28c, 0 }, { "SdramBaseAddr", 2, 30 }, { "CIM_SDRAM_ADDR_SIZE", 0x290, 0 }, { "SdramAddrSize", 2, 30 }, { "CIM_UP_SPARE_INT", 0x294, 0 }, { "uPSpareInt", 0, 3 }, { "CIM_HOST_INT_ENABLE", 0x298, 0 }, { "Timer1IntEn", 15, 1 }, { "Timer0IntEn", 14, 1 }, { "PrefDropIntEn", 13, 1 }, { "BlkWrPlIntEn", 12, 1 }, { "BlkRdPlIntEn", 11, 1 }, { "BlkWrCtlIntEn", 10, 1 }, { "BlkRdCtlIntEn", 9, 1 }, { "BlkWrFlashIntEn", 8, 1 }, { "BlkRdFlashIntEn", 7, 1 }, { "SglWrFlashIntEn", 6, 1 }, { "WrBlkFlashIntEn", 5, 1 }, { "BlkWrBootIntEn", 4, 1 }, { "BlkRdBootIntEn", 3, 1 }, { "FlashRangeIntEn", 2, 1 }, { "SdramRangeIntEn", 1, 1 }, { "RsvdSpaceIntEn", 0, 1 }, { "CIM_HOST_INT_CAUSE", 0x29c, 0 }, { "Timer1Int", 15, 1 }, { "Timer0Int", 14, 1 }, { "PrefDropInt", 13, 1 }, { "BlkWrPlInt", 12, 1 }, { "BlkRdPlInt", 11, 1 }, { "BlkWrCtlInt", 10, 1 }, { "BlkRdCtlInt", 9, 1 }, { "BlkWrFlashInt", 8, 1 }, { "BlkRdFlashInt", 7, 1 }, { "SglWrFlashInt", 6, 1 }, { "WrBlkFlashInt", 5, 1 }, { "BlkWrBootInt", 4, 1 }, { "BlkRdBootInt", 3, 1 }, { "FlashRangeInt", 2, 1 }, { "SdramRangeInt", 1, 1 }, { "RsvdSpaceInt", 0, 1 }, { "CIM_UP_INT_ENABLE", 0x2a0, 0 }, { "MstPlIntEn", 16, 1 }, { "Timer1IntEn", 15, 1 }, { "Timer0IntEn", 14, 1 }, { "PrefDropIntEn", 13, 1 }, { "BlkWrPlIntEn", 12, 1 }, { "BlkRdPlIntEn", 11, 1 }, { "BlkWrCtlIntEn", 10, 1 }, { "BlkRdCtlIntEn", 9, 1 }, { "BlkWrFlashIntEn", 8, 1 }, { "BlkRdFlashIntEn", 7, 1 }, { "SglWrFlashIntEn", 6, 1 }, { "WrBlkFlashIntEn", 5, 1 }, { "BlkWrBootIntEn", 4, 1 }, { "BlkRdBootIntEn", 3, 1 }, { "FlashRangeIntEn", 2, 1 }, { "SdramRangeIntEn", 1, 1 }, { "RsvdSpaceIntEn", 0, 1 }, { "CIM_UP_INT_CAUSE", 0x2a4, 0 }, { "MstPlInt", 16, 1 }, { "Timer1Int", 15, 1 }, { "Timer0Int", 14, 1 }, { "PrefDropInt", 13, 1 }, { "BlkWrPlInt", 12, 1 }, { "BlkRdPlInt", 11, 1 }, { "BlkWrCtlInt", 10, 1 }, { "BlkRdCtlInt", 9, 1 }, { "BlkWrFlashInt", 8, 1 }, { "BlkRdFlashInt", 7, 1 }, { "SglWrFlashInt", 6, 1 }, { "WrBlkFlashInt", 5, 1 }, { "BlkWrBootInt", 4, 1 }, { "BlkRdBootInt", 3, 1 }, { "FlashRangeInt", 2, 1 }, { "SdramRangeInt", 1, 1 }, { "RsvdSpaceInt", 0, 1 }, { "CIM_IBQ_FULLA_THRSH", 0x2a8, 0 }, { "Ibq0FullThrsh", 0, 9 }, { "Ibq1FullThrsh", 16, 9 }, { "CIM_IBQ_FULLB_THRSH", 0x2ac, 0 }, { "Ibq2FullThrsh", 0, 9 }, { "Ibq3FullThrsh", 16, 9 }, { "CIM_HOST_ACC_CTRL", 0x2b0, 0 }, { "HostBusy", 17, 1 }, { "HostWrite", 16, 1 }, { "HostAddr", 0, 16 }, { "CIM_HOST_ACC_DATA", 0x2b4, 0 }, { "CIM_IBQ_DBG_CFG", 0x2c0, 0 }, { "IbqDbgAddr", 16, 9 }, { "IbqDbgQID", 3, 2 }, { "IbqDbgWr", 2, 1 }, { "IbqDbgBusy", 1, 1 }, { "IbqDbgEn", 0, 1 }, { "CIM_OBQ_DBG_CFG", 0x2c4, 0 }, { "ObqDbgAddr", 16, 9 }, { "ObqDbgQID", 3, 2 }, { "ObqDbgWr", 2, 1 }, { "ObqDbgBusy", 1, 1 }, { "ObqDbgEn", 0, 1 }, { "CIM_IBQ_DBG_DATA", 0x2c8, 0 }, { "CIM_OBQ_DBG_DATA", 0x2cc, 0 }, { "CIM_CDEBUGDATA", 0x2d0, 0 }, { "CDebugDataH", 16, 16 }, { "CDebugDataL", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info tp1_regs[] = { { "TP_IN_CONFIG", 0x300, 0 }, { "RXFbArbPrio", 25, 1 }, { "TXFbArbPrio", 24, 1 }, { "DBMaxOpCnt", 16, 8 }, { "NICMode", 14, 1 }, { "EChecksumCheckTCP", 13, 1 }, { "EChecksumCheckIP", 12, 1 }, { "ECPL", 10, 1 }, { "EEthernet", 8, 1 }, { "ETunnel", 7, 1 }, { "CChecksumCheckTCP", 6, 1 }, { "CChecksumCheckIP", 5, 1 }, { "CCPL", 3, 1 }, { "CEthernet", 1, 1 }, { "CTunnel", 0, 1 }, { "TP_OUT_CONFIG", 0x304, 0 }, { "VLANExtractionEnable", 12, 1 }, { "EChecksumGenerateTCP", 11, 1 }, { "EChecksumGenerateIP", 10, 1 }, { "ECPL", 8, 1 }, { "EEthernet", 6, 1 }, { "CChecksumGenerateTCP", 5, 1 }, { "CChecksumGenerateIP", 4, 1 }, { "CCPL", 2, 1 }, { "CEthernet", 0, 1 }, { "TP_GLOBAL_CONFIG", 0x308, 0 }, { "RXFlowControlDisable", 25, 1 }, { "TXPacingEnable", 24, 1 }, { "AttackFilterEnable", 23, 1 }, { "SYNCookieNoOptions", 22, 1 }, { "ProtectedMode", 21, 1 }, { "PingDrop", 20, 1 }, { "FragmentDrop", 19, 1 }, { "FiveTupleLookup", 17, 2 }, { "PathMTU", 15, 1 }, { "IPIdentSplit", 14, 1 }, { "IPChecksumOffload", 13, 1 }, { "UDPChecksumOffload", 12, 1 }, { "TCPChecksumOffload", 11, 1 }, { "QOSMapping", 10, 1 }, { "TCAMServerUse", 8, 2 }, { "IPTTL", 0, 8 }, { "TP_GLOBAL_RX_CREDIT", 0x30c, 0 }, { "TP_CMM_SIZE", 0x310, 0 }, { "CMMemMgrSize", 0, 28 }, { "TP_CMM_MM_BASE", 0x314, 0 }, { "CMMemMgrBase", 0, 28 }, { "TP_CMM_TIMER_BASE", 0x318, 0 }, { "CMTimerBase", 0, 28 }, { "TP_PMM_SIZE", 0x31c, 0 }, { "PMSize", 0, 28 }, { "TP_PMM_TX_BASE", 0x320, 0 }, { "TP_PMM_DEFRAG_BASE", 0x324, 0 }, { "TP_PMM_RX_BASE", 0x328, 0 }, { "TP_PMM_RX_PAGE_SIZE", 0x32c, 0 }, { "TP_PMM_RX_MAX_PAGE", 0x330, 0 }, { "PMRxMaxPage", 0, 21 }, { "TP_PMM_TX_PAGE_SIZE", 0x334, 0 }, { "TP_PMM_TX_MAX_PAGE", 0x338, 0 }, { "PMTxMaxPage", 0, 21 }, { "TP_TCP_OPTIONS", 0x340, 0 }, { "MTUDefault", 16, 16 }, { "MTUEnable", 10, 1 }, { "SACKTx", 9, 1 }, { "SACKRx", 8, 1 }, { "SACKMode", 4, 2 }, { "WindowScaleMode", 2, 2 }, { "TimestampsMode", 0, 2 }, { "TP_DACK_CONFIG", 0x344, 0 }, { "AutoState3", 30, 2 }, { "AutoState2", 28, 2 }, { "AutoState1", 26, 2 }, { "ByteThreshold", 5, 20 }, { "MSSThreshold", 3, 2 }, { "AutoCareful", 2, 1 }, { "AutoEnable", 1, 1 }, { "Mode", 0, 1 }, { "TP_PC_CONFIG", 0x348, 0 }, { "TxTosQueueMapMode", 26, 1 }, { "RddpCongEn", 25, 1 }, { "EnableOnFlyPDU", 24, 1 }, { "EnableEPCMDAFull", 23, 1 }, { "ModulateUnionMode", 22, 1 }, { "TxDataAckRateEnable", 21, 1 }, { "TxDeferEnable", 20, 1 }, { "RxCongestionMode", 19, 1 }, { "HearbeatOnceDACK", 18, 1 }, { "HearbeatOnceHeap", 17, 1 }, { "HearbeatDACK", 16, 1 }, { "TxCongestionMode", 15, 1 }, { "AcceptLatestRcvAdv", 14, 1 }, { "DisableSYNData", 13, 1 }, { "DisableWindowPSH", 12, 1 }, { "DisableFINOldData", 11, 1 }, { "EnableFLMError", 10, 1 }, { "DisableFINOldDataFix", 9, 1 }, { "FilterPeerFIN", 8, 1 }, { "EnableFeedbackSend", 7, 1 }, { "EnableRDMAError", 6, 1 }, { "EnableDDPFlowControl", 5, 1 }, { "DisableHeldData", 4, 1 }, { "TableLatencyDelta", 0, 4 }, { "TP_TCP_BACKOFF_REG0", 0x350, 0 }, { "TimerBackoffIndex3", 24, 8 }, { "TimerBackoffIndex2", 16, 8 }, { "TimerBackoffIndex1", 8, 8 }, { "TimerBackoffIndex0", 0, 8 }, { "TP_TCP_BACKOFF_REG1", 0x354, 0 }, { "TimerBackoffIndex7", 24, 8 }, { "TimerBackoffIndex6", 16, 8 }, { "TimerBackoffIndex5", 8, 8 }, { "TimerBackoffIndex4", 0, 8 }, { "TP_TCP_BACKOFF_REG2", 0x358, 0 }, { "TimerBackoffIndex11", 24, 8 }, { "TimerBackoffIndex10", 16, 8 }, { "TimerBackoffIndex9", 8, 8 }, { "TimerBackoffIndex8", 0, 8 }, { "TP_TCP_BACKOFF_REG3", 0x35c, 0 }, { "TimerBackoffIndex15", 24, 8 }, { "TimerBackoffIndex14", 16, 8 }, { "TimerBackoffIndex13", 8, 8 }, { "TimerBackoffIndex12", 0, 8 }, { "TP_PARA_REG0", 0x360, 0 }, { "InitCwnd", 24, 3 }, { "DupAckThresh", 20, 4 }, { "TP_PARA_REG1", 0x364, 0 }, { "InitRwnd", 16, 16 }, { "InitialSSThresh", 0, 16 }, { "TP_PARA_REG2", 0x368, 0 }, { "MaxRxData", 16, 16 }, { "RxCoalesceSize", 0, 16 }, { "TP_PARA_REG3", 0x36c, 0 }, { "TunnelCngDrop1", 21, 1 }, { "TunnelCngDrop0", 20, 1 }, { "TxDataAckIdx", 16, 4 }, { "RxFragEnable", 12, 3 }, { "TxPaceFixedStrict", 11, 1 }, { "TxPaceAutoStrict", 10, 1 }, { "TxPaceFixed", 9, 1 }, { "TxPaceAuto", 8, 1 }, { "RxUrgMode", 5, 1 }, { "TxUrgMode", 4, 1 }, { "CngCtrlMode", 2, 2 }, { "RxCoalesceEnable", 1, 1 }, { "RxCoalescePshEn", 0, 1 }, { "TP_PARA_REG4", 0x370, 0 }, { "HighSpeedCfg", 24, 8 }, { "NewRenoCfg", 16, 8 }, { "TahoeCfg", 8, 8 }, { "RenoCfg", 0, 8 }, { "TP_PARA_REG5", 0x374, 0 }, { "IndicateSize", 16, 16 }, { "SchdEnable", 8, 1 }, { "OnFlyDDPEnable", 2, 1 }, { "DackTimerSpin", 1, 1 }, { "PushTimerEnable", 0, 1 }, { "TP_PARA_REG6", 0x378, 0 }, { "TxPDUSizeAdj", 16, 8 }, { "EnableEPDU", 14, 1 }, { "EnableESnd", 13, 1 }, { "EnableCSnd", 12, 1 }, { "EnableDeferACK", 9, 1 }, { "EnablePDUC", 8, 1 }, { "EnablePDUI", 7, 1 }, { "EnablePDUE", 6, 1 }, { "EnableDefer", 5, 1 }, { "EnableClearRxmtOos", 4, 1 }, { "DisablePDUCng", 3, 1 }, { "DisablePDUTimeout", 2, 1 }, { "DisablePDURxmt", 1, 1 }, { "DisablePDUxmt", 0, 1 }, { "TP_PARA_REG7", 0x37c, 0 }, { "PMMaxXferLen1", 16, 16 }, { "PMMaxXferLen0", 0, 16 }, { "TP_TIMER_RESOLUTION", 0x390, 0 }, { "TimerResolution", 16, 8 }, { "TimestampResolution", 8, 8 }, { "DelayedACKResolution", 0, 8 }, { "TP_MSL", 0x394, 0 }, { "MSL", 0, 30 }, { "TP_RXT_MIN", 0x398, 0 }, { "RxtMin", 0, 30 }, { "TP_RXT_MAX", 0x39c, 0 }, { "RxtMax", 0, 30 }, { "TP_PERS_MIN", 0x3a0, 0 }, { "PersMin", 0, 30 }, { "TP_PERS_MAX", 0x3a4, 0 }, { "PersMax", 0, 30 }, { "TP_KEEP_IDLE", 0x3a8, 0 }, { "KeepaliveIdle", 0, 30 }, { "TP_KEEP_INTVL", 0x3ac, 0 }, { "KeepaliveIntvl", 0, 30 }, { "TP_INIT_SRTT", 0x3b0, 0 }, { "InitSrtt", 0, 16 }, { "TP_DACK_TIMER", 0x3b4, 0 }, { "DackTime", 0, 12 }, { "TP_FINWAIT2_TIMER", 0x3b8, 0 }, { "Finwait2Time", 0, 30 }, { "TP_FAST_FINWAIT2_TIMER", 0x3bc, 0 }, { "FastFinwait2Time", 0, 30 }, { "TP_SHIFT_CNT", 0x3c0, 0 }, { "SynShiftMax", 24, 8 }, { "RxtShiftMaxR1", 20, 4 }, { "RxtShiftMaxR2", 16, 4 }, { "PerShiftBackoffMax", 12, 4 }, { "PerShiftMax", 8, 4 }, { "KeepaliveMax", 0, 8 }, { "TP_TIME_HI", 0x3c8, 0 }, { "TP_TIME_LO", 0x3cc, 0 }, { "TP_ULP_TABLE", 0x3d4, 0 }, { "ULPType7Field", 28, 4 }, { "ULPType6Field", 24, 4 }, { "ULPType5Field", 20, 4 }, { "ULPType4Field", 16, 4 }, { "ULPType3Field", 12, 4 }, { "ULPType2Field", 8, 4 }, { "ULPType1Field", 4, 4 }, { "ULPType0Field", 0, 4 }, { "TP_PACE_TABLE", 0x3d8, 0 }, { "TP_CCTRL_TABLE", 0x3dc, 0 }, { "TP_TOS_TABLE", 0x3e0, 0 }, { "TP_MTU_TABLE", 0x3e4, 0 }, { "TP_RSS_MAP_TABLE", 0x3e8, 0 }, { "TP_RSS_LKP_TABLE", 0x3ec, 0 }, { "TP_RSS_CONFIG", 0x3f0, 0 }, { "TNL4tupEn", 29, 1 }, { "TNL2tupEn", 28, 1 }, { "TNLprtEn", 26, 1 }, { "TNLMapEn", 25, 1 }, { "TNLLkpEn", 24, 1 }, { "OFD4tupEn", 21, 1 }, { "OFD2tupEn", 20, 1 }, { "OFDMapEn", 17, 1 }, { "OFDLkpEn", 16, 1 }, { "SYN4tupEn", 13, 1 }, { "SYN2tupEn", 12, 1 }, { "SYNMapEn", 9, 1 }, { "SYNLkpEn", 8, 1 }, { "RRCPLMapEn", 7, 1 }, { "RRCPLCPUSIZE", 4, 3 }, { "RQFeedbackEnable", 3, 1 }, { "HashToeplitz", 2, 1 }, { "HashSave", 1, 1 }, { "Disable", 0, 1 }, { "TP_RSS_CONFIG_TNL", 0x3f4, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_CONFIG_OFD", 0x3f8, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_CONFIG_SYN", 0x3fc, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_SECRET_KEY0", 0x400, 0 }, { "TP_RSS_SECRET_KEY1", 0x404, 0 }, { "TP_RSS_SECRET_KEY2", 0x408, 0 }, { "TP_RSS_SECRET_KEY3", 0x40c, 0 }, { "TP_TM_PIO_ADDR", 0x418, 0 }, { "TP_TM_PIO_DATA", 0x41c, 0 }, { "TP_TX_MOD_QUE_TABLE", 0x420, 0 }, { "TP_TX_RESOURCE_LIMIT", 0x424, 0 }, { "TX_RESOURCE_LIMIT_CH1_PC", 24, 8 }, { "TX_RESOURCE_LIMIT_CH1_NON_PC", 16, 8 }, { "TX_RESOURCE_LIMIT_CH0_PC", 8, 8 }, { "TX_RESOURCE_LIMIT_CH0_NON_PC", 0, 8 }, { "TP_TX_MOD_QUEUE_REQ_MAP", 0x428, 0 }, { "RX_MOD_WEIGHT", 24, 8 }, { "TX_MOD_WEIGHT", 16, 8 }, { "TX_MOD_TIMER_MODE", 8, 8 }, { "TX_MOD_QUEUE_REQ_MAP", 0, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT1", 0x42c, 0 }, { "TP_TX_MOD_QUEUE_WEIGHT7", 24, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT6", 16, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT5", 8, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT4", 0, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT0", 0x430, 0 }, { "TP_TX_MOD_QUEUE_WEIGHT3", 24, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT2", 16, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT1", 8, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT0", 0, 8 }, { "TP_MOD_CHANNEL_WEIGHT", 0x434, 0 }, { "RX_MOD_CHANNEL_WEIGHT1", 24, 8 }, { "RX_MOD_CHANNEL_WEIGHT0", 16, 8 }, { "TX_MOD_CHANNEL_WEIGHT1", 8, 8 }, { "TX_MOD_CHANNEL_WEIGHT0", 0, 8 }, { "TP_MOD_RATE_LIMIT", 0x438, 0 }, { "RX_MOD_RATE_LIMIT_INC", 24, 8 }, { "RX_MOD_RATE_LIMIT_TICK", 16, 8 }, { "TX_MOD_RATE_LIMIT_INC", 8, 8 }, { "TX_MOD_RATE_LIMIT_TICK", 0, 8 }, { "TP_PIO_ADDR", 0x440, 0 }, { "TP_PIO_DATA", 0x444, 0 }, { "TP_RESET", 0x44c, 0 }, { "FlstInitEnable", 1, 1 }, { "TPReset", 0, 1 }, { "TP_MIB_INDEX", 0x450, 0 }, { "TP_MIB_RDATA", 0x454, 0 }, { "TP_SYNC_TIME_HI", 0x458, 0 }, { "TP_SYNC_TIME_LO", 0x45c, 0 }, { "TP_CMM_MM_RX_FLST_BASE", 0x460, 0 }, { "CMRxFlstBase", 0, 28 }, { "TP_CMM_MM_TX_FLST_BASE", 0x464, 0 }, { "CMTxFlstBase", 0, 28 }, { "TP_CMM_MM_PS_FLST_BASE", 0x468, 0 }, { "CMPsFlstBase", 0, 28 }, { "TP_CMM_MM_MAX_PSTRUCT", 0x46c, 0 }, { "CMMaxPstruct", 0, 21 }, { "TP_INT_ENABLE", 0x470, 0 }, { "TP_INT_CAUSE", 0x474, 0 }, { "TP_FLM_FREE_PS_CNT", 0x480, 0 }, { "FreePstructCount", 0, 21 }, { "TP_FLM_FREE_RX_CNT", 0x484, 0 }, { "FreeRxPageCount", 0, 21 }, { "TP_FLM_FREE_TX_CNT", 0x488, 0 }, { "FreeTxPageCount", 0, 21 }, { "TP_TM_HEAP_PUSH_CNT", 0x48c, 0 }, { "TP_TM_HEAP_POP_CNT", 0x490, 0 }, { "TP_TM_DACK_PUSH_CNT", 0x494, 0 }, { "TP_TM_DACK_POP_CNT", 0x498, 0 }, { "TP_TM_MOD_PUSH_CNT", 0x49c, 0 }, { "TP_MOD_POP_CNT", 0x4a0, 0 }, { "TP_TIMER_SEPARATOR", 0x4a4, 0 }, { "TP_DEBUG_SEL", 0x4a8, 0 }, { "TP_DEBUG_FLAGS", 0x4ac, 0 }, { "RXDebugFlags", 16, 16 }, { "TXDebugFlags", 0, 16 }, { "TP_CM_FLOW_CNTL_MODE", 0x4b0, 0 }, { "CMFlowCacheDisable", 0, 1 }, { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 }, { "EDropTunnel", 19, 1 }, { "CDropTunnel", 18, 1 }, { "EThreshold", 12, 6 }, { "CThreshold", 6, 6 }, { "TxThreshold", 0, 6 }, { "TP_TX_DROP_COUNT", 0x4bc, 0 }, { "TP_CLEAR_DEBUG", 0x4c0, 0 }, { "ClrDebug", 0, 1 }, { "TP_DEBUG_VEC", 0x4c4, 0 }, { "TP_DEBUG_VEC2", 0x4c8, 0 }, { "TP_DEBUG_REG_SEL", 0x4cc, 0 }, { "TP_DEBUG", 0x4d0, 0 }, { "TP_DBG_LA_CONFIG", 0x4d4, 0 }, { "TP_DBG_LA_DATAH", 0x4d8, 0 }, { "TP_DBG_LA_DATAL", 0x4dc, 0 }, { "TP_EMBED_OP_FIELD0", 0x4e8, 0 }, { "TP_EMBED_OP_FIELD1", 0x4ec, 0 }, { "TP_EMBED_OP_FIELD2", 0x4f0, 0 }, { "TP_EMBED_OP_FIELD3", 0x4f4, 0 }, { "TP_EMBED_OP_FIELD4", 0x4f8, 0 }, { "TP_EMBED_OP_FIELD5", 0x4fc, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info ulp2_rx_regs[] = { { "ULPRX_CTL", 0x500, 0 }, { "PCMD1Threshold", 24, 8 }, { "PCMD0Threshold", 16, 8 }, { "round_robin", 4, 1 }, { "RDMA_permissive_mode", 3, 1 }, { "PagePodME", 2, 1 }, { "IscsiTagTcb", 1, 1 }, { "TddpTagTcb", 0, 1 }, { "ULPRX_INT_ENABLE", 0x504, 0 }, { "ParErr", 0, 1 }, { "ULPRX_INT_CAUSE", 0x508, 0 }, { "ParErr", 0, 1 }, { "ULPRX_ISCSI_LLIMIT", 0x50c, 0 }, { "IscsiLlimit", 6, 26 }, { "ULPRX_ISCSI_ULIMIT", 0x510, 0 }, { "IscsiUlimit", 6, 26 }, { "ULPRX_ISCSI_TAGMASK", 0x514, 0 }, { "IscsiTagMask", 6, 26 }, { "ULPRX_ISCSI_PSZ", 0x518, 0 }, { "Hpz3", 24, 4 }, { "Hpz2", 16, 4 }, { "Hpz1", 8, 4 }, { "Hpz0", 0, 4 }, { "ULPRX_TDDP_LLIMIT", 0x51c, 0 }, { "TddpLlimit", 6, 26 }, { "ULPRX_TDDP_ULIMIT", 0x520, 0 }, { "TddpUlimit", 6, 26 }, { "ULPRX_TDDP_TAGMASK", 0x524, 0 }, { "TddpTagMask", 6, 26 }, { "ULPRX_TDDP_PSZ", 0x528, 0 }, { "Hpz3", 24, 4 }, { "Hpz2", 16, 4 }, { "Hpz1", 8, 4 }, { "Hpz0", 0, 4 }, { "ULPRX_STAG_LLIMIT", 0x52c, 0 }, { "ULPRX_STAG_ULIMIT", 0x530, 0 }, { "ULPRX_RQ_LLIMIT", 0x534, 0 }, { "ULPRX_RQ_ULIMIT", 0x538, 0 }, { "ULPRX_PBL_LLIMIT", 0x53c, 0 }, { "ULPRX_PBL_ULIMIT", 0x540, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info ulp2_tx_regs[] = { { "ULPTX_CONFIG", 0x580, 0 }, { "CFG_RR_ARB", 0, 1 }, { "ULPTX_INT_ENABLE", 0x584, 0 }, { "Pbl_bound_err_ch1", 1, 1 }, { "Pbl_bound_err_ch0", 0, 1 }, { "ULPTX_INT_CAUSE", 0x588, 0 }, { "Pbl_bound_err_ch1", 1, 1 }, { "Pbl_bound_err_ch0", 0, 1 }, { "ULPTX_TPT_LLIMIT", 0x58c, 0 }, { "ULPTX_TPT_ULIMIT", 0x590, 0 }, { "ULPTX_PBL_LLIMIT", 0x594, 0 }, { "ULPTX_PBL_ULIMIT", 0x598, 0 }, { "ULPTX_CPL_ERR_OFFSET", 0x59c, 0 }, { "ULPTX_CPL_ERR_MASK", 0x5a0, 0 }, { "ULPTX_CPL_ERR_VALUE", 0x5a4, 0 }, { "ULPTX_CPL_PACK_SIZE", 0x5a8, 0 }, { "value", 24, 8 }, { "Ch1Size2", 24, 8 }, { "Ch1Size1", 16, 8 }, { "Ch0Size2", 8, 8 }, { "Ch0Size1", 0, 8 }, { "ULPTX_DMA_WEIGHT", 0x5ac, 0 }, { "D1_WEIGHT", 16, 16 }, { "D0_WEIGHT", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info pm1_rx_regs[] = { { "PM1_RX_CFG", 0x5c0, 0 }, { "PM1_RX_MODE", 0x5c4, 0 }, { "stat_channel", 1, 1 }, { "priority_ch", 0, 1 }, { "PM1_RX_STAT_CONFIG", 0x5c8, 0 }, { "PM1_RX_STAT_COUNT", 0x5cc, 0 }, { "PM1_RX_STAT_MSB", 0x5d0, 0 }, { "PM1_RX_STAT_LSB", 0x5d4, 0 }, { "PM1_RX_INT_ENABLE", 0x5d8, 0 }, { "zero_e_cmd_error", 18, 1 }, { "iespi0_fifo2x_Rx_framing_error", 17, 1 }, { "iespi1_fifo2x_Rx_framing_error", 16, 1 }, { "iespi0_Rx_framing_error", 15, 1 }, { "iespi1_Rx_framing_error", 14, 1 }, { "iespi0_Tx_framing_error", 13, 1 }, { "iespi1_Tx_framing_error", 12, 1 }, { "ocspi0_Rx_framing_error", 11, 1 }, { "ocspi1_Rx_framing_error", 10, 1 }, { "ocspi0_Tx_framing_error", 9, 1 }, { "ocspi1_Tx_framing_error", 8, 1 }, { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 }, { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 }, { "iespi_par_error", 3, 3 }, { "ocspi_par_error", 0, 3 }, { "PM1_RX_INT_CAUSE", 0x5dc, 0 }, { "zero_e_cmd_error", 18, 1 }, { "iespi0_fifo2x_Rx_framing_error", 17, 1 }, { "iespi1_fifo2x_Rx_framing_error", 16, 1 }, { "iespi0_Rx_framing_error", 15, 1 }, { "iespi1_Rx_framing_error", 14, 1 }, { "iespi0_Tx_framing_error", 13, 1 }, { "iespi1_Tx_framing_error", 12, 1 }, { "ocspi0_Rx_framing_error", 11, 1 }, { "ocspi1_Rx_framing_error", 10, 1 }, { "ocspi0_Tx_framing_error", 9, 1 }, { "ocspi1_Tx_framing_error", 8, 1 }, { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 }, { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 }, { "iespi_par_error", 3, 3 }, { "ocspi_par_error", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info pm1_tx_regs[] = { { "PM1_TX_CFG", 0x5e0, 0 }, { "PM1_TX_MODE", 0x5e4, 0 }, { "stat_channel", 1, 1 }, { "priority_ch", 0, 1 }, { "PM1_TX_STAT_CONFIG", 0x5e8, 0 }, { "PM1_TX_STAT_COUNT", 0x5ec, 0 }, { "PM1_TX_STAT_MSB", 0x5f0, 0 }, { "PM1_TX_STAT_LSB", 0x5f4, 0 }, { "PM1_TX_INT_ENABLE", 0x5f8, 0 }, { "zero_c_cmd_error", 18, 1 }, { "icspi0_fifo2x_Rx_framing_error", 17, 1 }, { "icspi1_fifo2x_Rx_framing_error", 16, 1 }, { "icspi0_Rx_framing_error", 15, 1 }, { "icspi1_Rx_framing_error", 14, 1 }, { "icspi0_Tx_framing_error", 13, 1 }, { "icspi1_Tx_framing_error", 12, 1 }, { "oespi0_Rx_framing_error", 11, 1 }, { "oespi1_Rx_framing_error", 10, 1 }, { "oespi0_Tx_framing_error", 9, 1 }, { "oespi1_Tx_framing_error", 8, 1 }, { "oespi0_ofifo2x_Tx_framing_error", 7, 1 }, { "oespi1_ofifo2x_Tx_framing_error", 6, 1 }, { "icspi_par_error", 3, 3 }, { "oespi_par_error", 0, 3 }, { "PM1_TX_INT_CAUSE", 0x5fc, 0 }, { "zero_c_cmd_error", 18, 1 }, { "icspi0_fifo2x_Rx_framing_error", 17, 1 }, { "icspi1_fifo2x_Rx_framing_error", 16, 1 }, { "icspi0_Rx_framing_error", 15, 1 }, { "icspi1_Rx_framing_error", 14, 1 }, { "icspi0_Tx_framing_error", 13, 1 }, { "icspi1_Tx_framing_error", 12, 1 }, { "oespi0_Rx_framing_error", 11, 1 }, { "oespi1_Rx_framing_error", 10, 1 }, { "oespi0_Tx_framing_error", 9, 1 }, { "oespi1_Tx_framing_error", 8, 1 }, { "oespi0_ofifo2x_Tx_framing_error", 7, 1 }, { "oespi1_ofifo2x_Tx_framing_error", 6, 1 }, { "icspi_par_error", 3, 3 }, { "oespi_par_error", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mps0_regs[] = { { "MPS_CFG", 0x600, 0 }, { "SGETPQid", 8, 3 }, { "TPRxPortSize", 7, 1 }, { "TPTxPort1Size", 6, 1 }, { "TPTxPort0Size", 5, 1 }, { "TPRxPortEn", 4, 1 }, { "TPTxPort1En", 3, 1 }, { "TPTxPort0En", 2, 1 }, { "Port1Active", 1, 1 }, { "Port0Active", 0, 1 }, { "MPS_DRR_CFG1", 0x604, 0 }, { "RldWtTPD1", 11, 11 }, { "RldWtTPD0", 0, 11 }, { "MPS_DRR_CFG2", 0x608, 0 }, { "RldWtTotal", 0, 12 }, { "MPS_MCA_STATUS", 0x60c, 0 }, { "MCAPktCnt", 12, 20 }, { "MCADepth", 0, 12 }, { "MPS_TX0_TP_CNT", 0x610, 0 }, { "TX0TPDisCnt", 24, 8 }, { "TX0TPCnt", 0, 24 }, { "MPS_TX1_TP_CNT", 0x614, 0 }, { "TX1TPDisCnt", 24, 8 }, { "TX1TPCnt", 0, 24 }, { "MPS_RX_TP_CNT", 0x618, 0 }, { "RXTPDisCnt", 24, 8 }, { "RXTPCnt", 0, 24 }, { "MPS_INT_ENABLE", 0x61c, 0 }, { "MCAParErrEnb", 6, 3 }, { "RXTpParErrEnb", 4, 2 }, { "TX1TpParErrEnb", 2, 2 }, { "TX0TpParErrEnb", 0, 2 }, { "MPS_INT_CAUSE", 0x620, 0 }, { "MCAParErr", 6, 3 }, { "RXTpParErr", 4, 2 }, { "TX1TpParErr", 2, 2 }, { "TX0TpParErr", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info cpl_switch_regs[] = { { "CPL_SWITCH_CNTRL", 0x640, 0 }, { "cpl_pkt_tid", 8, 24 }, { "cpu_no_3F_CIM_enable", 3, 1 }, { "switch_table_enable", 2, 1 }, { "sge_enable", 1, 1 }, { "cim_enable", 0, 1 }, { "CPL_SWITCH_TBL_IDX", 0x644, 0 }, { "switch_tbl_idx", 0, 4 }, { "CPL_SWITCH_TBL_DATA", 0x648, 0 }, { "CPL_SWITCH_ZERO_ERROR", 0x64c, 0 }, { "zero_cmd", 0, 8 }, { "CPL_INTR_ENABLE", 0x650, 0 }, { "cim_ovfl_error", 4, 1 }, { "tp_framing_error", 3, 1 }, { "sge_framing_error", 2, 1 }, { "cim_framing_error", 1, 1 }, { "zero_switch_error", 0, 1 }, { "CPL_INTR_CAUSE", 0x654, 0 }, { "cim_ovfl_error", 4, 1 }, { "tp_framing_error", 3, 1 }, { "sge_framing_error", 2, 1 }, { "cim_framing_error", 1, 1 }, { "zero_switch_error", 0, 1 }, { "CPL_MAP_TBL_IDX", 0x658, 0 }, { "cpl_map_tbl_idx", 0, 8 }, { "CPL_MAP_TBL_DATA", 0x65c, 0 }, { "cpl_map_tbl_data", 0, 8 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info smb0_regs[] = { { "SMB_GLOBAL_TIME_CFG", 0x660, 0 }, { "LADbgWrPtr", 24, 8 }, { "LADbgRdPtr", 16, 8 }, { "LADbgEn", 13, 1 }, { "MacroCntCfg", 8, 5 }, { "MicroCntCfg", 0, 8 }, { "SMB_MST_TIMEOUT_CFG", 0x664, 0 }, { "DebugSelH", 28, 4 }, { "DebugSelL", 24, 4 }, { "MstTimeOutCfg", 0, 24 }, { "SMB_MST_CTL_CFG", 0x668, 0 }, { "MstFifoDbg", 31, 1 }, { "MstFifoDbgClr", 30, 1 }, { "MstRxByteCfg", 12, 6 }, { "MstTxByteCfg", 6, 6 }, { "MstReset", 1, 1 }, { "MstCtlEn", 0, 1 }, { "SMB_MST_CTL_STS", 0x66c, 0 }, { "MstRxByteCnt", 12, 6 }, { "MstTxByteCnt", 6, 6 }, { "MstBusySts", 0, 1 }, { "SMB_MST_TX_FIFO_RDWR", 0x670, 0 }, { "SMB_MST_RX_FIFO_RDWR", 0x674, 0 }, { "SMB_SLV_TIMEOUT_CFG", 0x678, 0 }, { "SlvTimeOutCfg", 0, 24 }, { "SMB_SLV_CTL_CFG", 0x67c, 0 }, { "SlvFifoDbg", 31, 1 }, { "SlvFifoDbgClr", 30, 1 }, { "SlvAddrCfg", 4, 7 }, { "SlvAlrtSet", 2, 1 }, { "SlvReset", 1, 1 }, { "SlvCtlEn", 0, 1 }, { "SMB_SLV_CTL_STS", 0x680, 0 }, { "SlvFifoTxCnt", 12, 6 }, { "SlvFifoCnt", 6, 6 }, { "SlvAlrtSts", 2, 1 }, { "SlvBusySts", 0, 1 }, { "SMB_SLV_FIFO_RDWR", 0x684, 0 }, { "SMB_SLV_CMD_FIFO_RDWR", 0x688, 0 }, { "SMB_INT_ENABLE", 0x68c, 0 }, { "SlvTimeOutIntEn", 7, 1 }, { "SlvErrIntEn", 6, 1 }, { "SlvDoneIntEn", 5, 1 }, { "SlvRxRdyIntEn", 4, 1 }, { "MstTimeOutIntEn", 3, 1 }, { "MstNAckIntEn", 2, 1 }, { "MstLostArbIntEn", 1, 1 }, { "MstDoneIntEn", 0, 1 }, { "SMB_INT_CAUSE", 0x690, 0 }, { "SlvTimeOutInt", 7, 1 }, { "SlvErrInt", 6, 1 }, { "SlvDoneInt", 5, 1 }, { "SlvRxRdyInt", 4, 1 }, { "MstTimeOutInt", 3, 1 }, { "MstNAckInt", 2, 1 }, { "MstLostArbInt", 1, 1 }, { "MstDoneInt", 0, 1 }, { "SMB_DEBUG_DATA", 0x694, 0 }, { "DebugDataH", 16, 16 }, { "DebugDataL", 0, 16 }, { "SMB_DEBUG_LA", 0x69c, 0 }, { "DebugLAReqAddr", 0, 10 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info i2cm0_regs[] = { { "I2C_CFG", 0x6a0, 0 }, { "ClkDiv", 0, 12 }, { "I2C_DATA", 0x6a4, 0 }, { "Data", 0, 8 }, { "I2C_OP", 0x6a8, 0 }, { "Busy", 31, 1 }, { "Ack", 30, 1 }, { "Cont", 1, 1 }, { "Op", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mi1_regs[] = { { "MI1_CFG", 0x6b0, 0 }, { "ClkDiv", 5, 8 }, { "St", 3, 2 }, { "PreEn", 2, 1 }, { "MDIInv", 1, 1 }, { "MDIEn", 0, 1 }, { "MI1_ADDR", 0x6b4, 0 }, { "PhyAddr", 5, 5 }, { "RegAddr", 0, 5 }, { "MI1_DATA", 0x6b8, 0 }, { "Data", 0, 16 }, { "MI1_OP", 0x6bc, 0 }, { "Busy", 31, 1 }, { "Inc", 2, 1 }, { "Op", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info jm1_regs[] = { { "JM_CFG", 0x6c0, 0 }, { "ClkDiv", 2, 8 }, { "TRst", 1, 1 }, { "En", 0, 1 }, { "JM_MODE", 0x6c4, 0 }, { "JM_DATA", 0x6c8, 0 }, { "JM_OP", 0x6cc, 0 }, { "Busy", 31, 1 }, { "Cnt", 0, 5 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info sf1_regs[] = { { "SF_DATA", 0x6d8, 0 }, { "SF_OP", 0x6dc, 0 }, { "Busy", 31, 1 }, { "Cont", 3, 1 }, { "ByteCnt", 1, 2 }, { "Op", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info pl3_regs[] = { { "PL_INT_ENABLE0", 0x6e0, 0 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_CAUSE0", 0x6e4, 0 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_ENABLE1", 0x6e8, 0 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_CAUSE1", 0x6ec, 0 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_RST", 0x6f0, 0 }, { "CRstWrm", 1, 1 }, { "CRstWrmMode", 0, 1 }, { "PL_REV", 0x6f4, 0 }, { "Rev", 0, 4 }, { "PL_CLI", 0x6f8, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info mc5a_regs[] = { { "MC5_BUF_CONFIG", 0x700, 0 }, { "term300_240", 31, 1 }, { "term150", 30, 1 }, { "term60", 29, 1 }, { "gddriii", 28, 1 }, { "gddrii", 27, 1 }, { "gddri", 26, 1 }, { "read", 25, 1 }, { "cal_imp_upd", 23, 1 }, { "cal_busy", 22, 1 }, { "cal_error", 21, 1 }, { "sgl_cal_en", 20, 1 }, { "imp_upd_mode", 19, 1 }, { "imp_sel", 18, 1 }, { "man_pu", 15, 3 }, { "man_pd", 12, 3 }, { "cal_pu", 9, 3 }, { "cal_pd", 6, 3 }, { "set_pu", 3, 3 }, { "set_pd", 0, 3 }, { "MC5_DB_CONFIG", 0x704, 0 }, { "TMCfgWrLock", 31, 1 }, { "TMTypeHi", 30, 1 }, { "TMPartSize", 28, 2 }, { "TMType", 26, 2 }, { "TMPartCount", 24, 2 }, { "nLIP", 18, 6 }, { "COMPEN", 17, 1 }, { "BUILD", 16, 1 }, { "TM_IO_PDOWN", 9, 1 }, { "SYNMode", 7, 2 }, { "PRTYEN", 6, 1 }, { "MBUSEN", 5, 1 }, { "DBGIEN", 4, 1 }, { "TMRDY", 2, 1 }, { "TMRST", 1, 1 }, { "TMMode", 0, 1 }, { "MC5_DB_ROUTING_TABLE_INDEX", 0x70c, 0 }, { "RTINDX", 0, 22 }, { "MC5_DB_SERVER_INDEX", 0x714, 0 }, { "SRINDX", 0, 22 }, { "MC5_DB_LIP_RAM_ADDR", 0x718, 0 }, { "RAMWR", 8, 1 }, { "RAMADDR", 0, 6 }, { "MC5_DB_LIP_RAM_DATA", 0x71c, 0 }, { "MC5_DB_RSP_LATENCY", 0x720, 0 }, { "RDLAT", 16, 5 }, { "LRNLAT", 8, 5 }, { "SRCHLAT", 0, 5 }, { "MC5_DB_PARITY_LATENCY", 0x724, 0 }, { "PARLAT", 0, 4 }, { "MC5_DB_WR_LRN_VERIFY", 0x728, 0 }, { "VWVEREN", 2, 1 }, { "LRNVEREN", 1, 1 }, { "POVEREN", 0, 1 }, { "MC5_DB_PART_ID_INDEX", 0x72c, 0 }, { "IDINDEX", 0, 4 }, { "MC5_DB_RESET_MAX", 0x730, 0 }, { "RSTMAX", 0, 4 }, { "MC5_DB_ACT_CNT", 0x734, 0 }, { "ACTCNT", 0, 20 }, { "MC5_DB_INT_ENABLE", 0x740, 0 }, { "MsgSel", 28, 4 }, { "DelActEmpty", 18, 1 }, { "DispQParErr", 17, 1 }, { "ReqQParErr", 16, 1 }, { "UnknownCmd", 15, 1 }, { "SYNCookieOff", 11, 1 }, { "SYNCookieBad", 10, 1 }, { "SYNCookie", 9, 1 }, { "NFASrchFail", 8, 1 }, { "ActRgnFull", 7, 1 }, { "ParityErr", 6, 1 }, { "LIPMiss", 5, 1 }, { "LIP0", 4, 1 }, { "Miss", 3, 1 }, { "RoutingHit", 2, 1 }, { "ActiveHit", 1, 1 }, { "ActiveOutHit", 0, 1 }, { "MC5_DB_INT_CAUSE", 0x744, 0 }, { "DelActEmpty", 18, 1 }, { "DispQParErr", 17, 1 }, { "ReqQParErr", 16, 1 }, { "UnknownCmd", 15, 1 }, { "SYNCookieOff", 11, 1 }, { "SYNCookieBad", 10, 1 }, { "SYNCookie", 9, 1 }, { "NFASrchFail", 8, 1 }, { "ActRgnFull", 7, 1 }, { "ParityErr", 6, 1 }, { "LIPMiss", 5, 1 }, { "LIP0", 4, 1 }, { "Miss", 3, 1 }, { "RoutingHit", 2, 1 }, { "ActiveHit", 1, 1 }, { "ActiveOutHit", 0, 1 }, { "MC5_DB_INT_TID", 0x748, 0 }, { "INTTID", 0, 20 }, { "MC5_DB_INT_PTID", 0x74c, 0 }, { "INTPTID", 0, 20 }, { "MC5_DB_DBGI_CONFIG", 0x774, 0 }, { "WRReqSize", 22, 10 }, { "SADRSel", 4, 1 }, { "CMDMode", 0, 3 }, { "MC5_DB_DBGI_REQ_CMD", 0x778, 0 }, { "MBusCmd", 0, 4 }, { "IDTCmdHi", 11, 3 }, { "IDTCmdLo", 0, 4 }, { "IDTCmd", 0, 20 }, { "LCMDB", 16, 11 }, { "LCMDA", 0, 11 }, { "MC5_DB_DBGI_REQ_ADDR0", 0x77c, 0 }, { "MC5_DB_DBGI_REQ_ADDR1", 0x780, 0 }, { "MC5_DB_DBGI_REQ_ADDR2", 0x784, 0 }, { "DBGIReqAdrHi", 0, 8 }, { "MC5_DB_DBGI_REQ_DATA0", 0x788, 0 }, { "MC5_DB_DBGI_REQ_DATA1", 0x78c, 0 }, { "MC5_DB_DBGI_REQ_DATA2", 0x790, 0 }, { "MC5_DB_DBGI_REQ_DATA3", 0x794, 0 }, { "MC5_DB_DBGI_REQ_DATA4", 0x798, 0 }, { "DBGIReqData4", 0, 16 }, { "MC5_DB_DBGI_REQ_MASK0", 0x79c, 0 }, { "MC5_DB_DBGI_REQ_MASK1", 0x7a0, 0 }, { "MC5_DB_DBGI_REQ_MASK2", 0x7a4, 0 }, { "MC5_DB_DBGI_REQ_MASK3", 0x7a8, 0 }, { "MC5_DB_DBGI_REQ_MASK4", 0x7ac, 0 }, { "DBGIReqMsk4", 0, 16 }, { "MC5_DB_DBGI_RSP_STATUS", 0x7b0, 0 }, { "DBGIRspMsg", 8, 4 }, { "DBGIRspMsgVld", 2, 1 }, { "DBGIRspHit", 1, 1 }, { "DBGIRspValid", 0, 1 }, { "MC5_DB_DBGI_RSP_DATA0", 0x7b4, 0 }, { "MC5_DB_DBGI_RSP_DATA1", 0x7b8, 0 }, { "MC5_DB_DBGI_RSP_DATA2", 0x7bc, 0 }, { "MC5_DB_DBGI_RSP_DATA3", 0x7c0, 0 }, { "MC5_DB_DBGI_RSP_DATA4", 0x7c4, 0 }, { "DBGIRspData3", 0, 16 }, { "MC5_DB_DBGI_RSP_LAST_CMD", 0x7c8, 0 }, { "LastCmdB", 16, 11 }, { "LastCmdA", 0, 11 }, { "MC5_DB_POPEN_DATA_WR_CMD", 0x7cc, 0 }, { "PO_DWR", 0, 20 }, { "MC5_DB_POPEN_MASK_WR_CMD", 0x7d0, 0 }, { "PO_MWR", 0, 20 }, { "MC5_DB_AOPEN_SRCH_CMD", 0x7d4, 0 }, { "AO_SRCH", 0, 20 }, { "MC5_DB_AOPEN_LRN_CMD", 0x7d8, 0 }, { "AO_LRN", 0, 20 }, { "MC5_DB_SYN_SRCH_CMD", 0x7dc, 0 }, { "SYN_SRCH", 0, 20 }, { "MC5_DB_SYN_LRN_CMD", 0x7e0, 0 }, { "SYN_LRN", 0, 20 }, { "MC5_DB_ACK_SRCH_CMD", 0x7e4, 0 }, { "ACK_SRCH", 0, 20 }, { "MC5_DB_ACK_LRN_CMD", 0x7e8, 0 }, { "ACK_LRN", 0, 20 }, { "MC5_DB_ILOOKUP_CMD", 0x7ec, 0 }, { "I_SRCH", 0, 20 }, { "MC5_DB_ELOOKUP_CMD", 0x7f0, 0 }, { "E_SRCH", 0, 20 }, { "MC5_DB_DATA_WRITE_CMD", 0x7f4, 0 }, { "Write", 0, 20 }, { "MC5_DB_DATA_READ_CMD", 0x7f8, 0 }, { "ReadCmd", 0, 20 }, { "MC5_DB_MASK_WRITE_CMD", 0x7fc, 0 }, { "MaskWr", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info xgmac0_0_regs[] = { { "XGM_TX_CTRL", 0x800, 0 }, { "SendPause", 2, 1 }, { "SendZeroPause", 1, 1 }, { "TxEn", 0, 1 }, { "XGM_TX_CFG", 0x804, 0 }, { "CfgClkSpeed", 2, 3 }, { "StretchMode", 1, 1 }, { "TxPauseEn", 0, 1 }, { "XGM_TX_PAUSE_QUANTA", 0x808, 0 }, { "TxPauseQuanta", 0, 16 }, { "XGM_RX_CTRL", 0x80c, 0 }, { "RxEn", 0, 1 }, { "XGM_RX_CFG", 0x810, 0 }, { "Con802_3Preamble", 12, 1 }, { "EnNon802_3Preamble", 11, 1 }, { "CopyPreamble", 10, 1 }, { "DisPauseFrames", 9, 1 }, { "En1536BFrames", 8, 1 }, { "EnJumbo", 7, 1 }, { "RmFCS", 6, 1 }, { "DisNonVlan", 5, 1 }, { "EnExtMatch", 4, 1 }, { "EnHashUcast", 3, 1 }, { "EnHashMcast", 2, 1 }, { "DisBCast", 1, 1 }, { "CopyAllFrames", 0, 1 }, { "XGM_RX_HASH_LOW", 0x814, 0 }, { "XGM_RX_HASH_HIGH", 0x818, 0 }, { "XGM_RX_EXACT_MATCH_LOW_1", 0x81c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_1", 0x820, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_2", 0x824, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_2", 0x828, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_3", 0x82c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_3", 0x830, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_4", 0x834, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_4", 0x838, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_5", 0x83c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_5", 0x840, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_6", 0x844, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_6", 0x848, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_7", 0x84c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_7", 0x850, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_8", 0x854, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_8", 0x858, 0 }, { "address_high", 0, 16 }, { "XGM_RX_TYPE_MATCH_1", 0x85c, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_2", 0x860, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_3", 0x864, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_4", 0x868, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_INT_STATUS", 0x86c, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_MASK", 0x870, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_ENABLE", 0x874, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_DISABLE", 0x878, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_TX_PAUSE_TIMER", 0x87c, 0 }, { "CurPauseTimer", 0, 16 }, { "XGM_STAT_CTRL", 0x880, 0 }, { "ReadSnpShot", 4, 1 }, { "TakeSnpShot", 3, 1 }, { "ClrStats", 2, 1 }, { "IncrStats", 1, 1 }, { "EnTestModeWr", 0, 1 }, { "XGM_RXFIFO_CFG", 0x884, 0 }, { "RxFIFOPauseHWM", 17, 12 }, { "RxFIFOPauseLWM", 5, 12 }, { "ForcedPause", 4, 1 }, { "ExternLoopback", 3, 1 }, { "RxByteSwap", 2, 1 }, { "RxStrFrwrd", 1, 1 }, { "DisErrFrames", 0, 1 }, { "XGM_TXFIFO_CFG", 0x888, 0 }, { "TxIPG", 13, 8 }, { "TxFIFOThresh", 4, 9 }, { "InternLoopback", 3, 1 }, { "TxByteSwap", 2, 1 }, { "DisCRC", 1, 1 }, { "DisPreAmble", 0, 1 }, { "XGM_SLOW_TIMER", 0x88c, 0 }, { "PauseSlowTimerEn", 31, 1 }, { "PauseSlowTimer", 0, 20 }, { "XGM_SERDES_CTRL", 0x890, 0 }, { "SERDESEn", 25, 1 }, { "SERDESReset_", 24, 1 }, { "CMURange", 21, 3 }, { "BGEnb", 20, 1 }, { "EnSkpDrop", 19, 1 }, { "EnComma", 18, 1 }, { "En8B10B", 17, 1 }, { "EnElBuf", 16, 1 }, { "Gain", 11, 5 }, { "BandGap", 7, 4 }, { "LpbkEn", 5, 2 }, { "RxEn", 4, 1 }, { "TxEn", 3, 1 }, { "RxComAdj", 2, 1 }, { "PreEmph", 0, 2 }, { "XGM_XAUI_PCS_TEST", 0x894, 0 }, { "TestPattern", 1, 2 }, { "EnTest", 0, 1 }, { "XGM_RGMII_CTRL", 0x898, 0 }, { "PhAlignFIFOThresh", 1, 2 }, { "TxClk90Shift", 0, 1 }, { "XGM_RGMII_IMP", 0x89c, 0 }, { "ImpSetUpdate", 6, 1 }, { "RGMIIImpPD", 3, 3 }, { "RGMIIImpPU", 0, 3 }, { "XGM_XAUI_IMP", 0x8a0, 0 }, { "CalBusy", 31, 1 }, { "CalFault", 29, 1 }, { "CalImp", 24, 5 }, { "XAUIImp", 0, 3 }, { "XGM_SERDES_BIST", 0x8a4, 0 }, { "BISTDone", 28, 4 }, { "BISTCycleThresh", 3, 17 }, { "BISTMode", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE", 0x8a8, 0 }, { "RxMaxPktSize", 0, 14 }, { "XGM_RESET_CTRL", 0x8ac, 0 }, { "XG2G_Reset_", 3, 1 }, { "RGMII_Reset_", 2, 1 }, { "PCS_Reset_", 1, 1 }, { "MAC_Reset_", 0, 1 }, { "XGM_XAUI1G_CTRL", 0x8b0, 0 }, { "XAUI1GLinkId", 0, 2 }, { "XGM_SERDES_LANE_CTRL", 0x8b4, 0 }, { "LaneReversal", 8, 1 }, { "TxPolarity", 4, 4 }, { "RxPolarity", 0, 4 }, { "XGM_PORT_CFG", 0x8b8, 0 }, { "SafeSpeedChange", 4, 1 }, { "ClkDivReset_", 3, 1 }, { "PortSpeed", 1, 2 }, { "EnRGMII", 0, 1 }, { "XGM_EPIO_DATA0", 0x8c0, 0 }, { "XGM_EPIO_DATA1", 0x8c4, 0 }, { "XGM_EPIO_DATA2", 0x8c8, 0 }, { "XGM_EPIO_DATA3", 0x8cc, 0 }, { "XGM_EPIO_OP", 0x8d0, 0 }, { "PIO_Ready", 31, 1 }, { "PIO_WrRd", 24, 1 }, { "PIO_Address", 0, 8 }, { "XGM_INT_ENABLE", 0x8d4, 0 }, { "SERDESCMULock_loss", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBIST_err", 8, 4 }, { "SERDES_los", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_INT_CAUSE", 0x8d8, 0 }, { "SERDESCMULock_loss", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBIST_err", 8, 4 }, { "SERDES_los", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_STAT_TX_BYTE_LOW", 0x900, 0 }, { "XGM_STAT_TX_BYTE_HIGH", 0x904, 0 }, { "TxBytes_high", 0, 13 }, { "XGM_STAT_TX_FRAME_LOW", 0x908, 0 }, { "XGM_STAT_TX_FRAME_HIGH", 0x90c, 0 }, { "TxFrames_high", 0, 4 }, { "XGM_STAT_TX_BCAST", 0x910, 0 }, { "XGM_STAT_TX_MCAST", 0x914, 0 }, { "XGM_STAT_TX_PAUSE", 0x918, 0 }, { "XGM_STAT_TX_64B_FRAMES", 0x91c, 0 }, { "XGM_STAT_TX_65_127B_FRAMES", 0x920, 0 }, { "XGM_STAT_TX_128_255B_FRAMES", 0x924, 0 }, { "XGM_STAT_TX_256_511B_FRAMES", 0x928, 0 }, { "XGM_STAT_TX_512_1023B_FRAMES", 0x92c, 0 }, { "XGM_STAT_TX_1024_1518B_FRAMES", 0x930, 0 }, { "XGM_STAT_TX_1519_MAXB_FRAMES", 0x934, 0 }, { "XGM_STAT_TX_ERR_FRAMES", 0x938, 0 }, { "XGM_STAT_RX_BYTES_LOW", 0x93c, 0 }, { "XGM_STAT_RX_BYTES_HIGH", 0x940, 0 }, { "RxBytes_high", 0, 13 }, { "XGM_STAT_RX_FRAMES_LOW", 0x944, 0 }, { "XGM_STAT_RX_FRAMES_HIGH", 0x948, 0 }, { "RxFrames_high", 0, 4 }, { "XGM_STAT_RX_BCAST_FRAMES", 0x94c, 0 }, { "XGM_STAT_RX_MCAST_FRAMES", 0x950, 0 }, { "XGM_STAT_RX_PAUSE_FRAMES", 0x954, 0 }, { "RxPauseFrames", 0, 16 }, { "XGM_STAT_RX_64B_FRAMES", 0x958, 0 }, { "XGM_STAT_RX_65_127B_FRAMES", 0x95c, 0 }, { "XGM_STAT_RX_128_255B_FRAMES", 0x960, 0 }, { "XGM_STAT_RX_256_511B_FRAMES", 0x964, 0 }, { "XGM_STAT_RX_512_1023B_FRAMES", 0x968, 0 }, { "XGM_STAT_RX_1024_1518B_FRAMES", 0x96c, 0 }, { "XGM_STAT_RX_1519_MAXB_FRAMES", 0x970, 0 }, { "XGM_STAT_RX_SHORT_FRAMES", 0x974, 0 }, { "RxShortFrames", 0, 16 }, { "XGM_STAT_RX_OVERSIZE_FRAMES", 0x978, 0 }, { "RxOversizeFrames", 0, 16 }, { "XGM_STAT_RX_JABBER_FRAMES", 0x97c, 0 }, { "RxJabberFrames", 0, 16 }, { "XGM_STAT_RX_CRC_ERR_FRAMES", 0x980, 0 }, { "RxCRCErrFrames", 0, 16 }, { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0x984, 0 }, { "RxLengthErrFrames", 0, 16 }, { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0x988, 0 }, { "RxSymCodeErrFrames", 0, 16 }, { "XGM_SERDES_STATUS0", 0x98c, 0 }, { "RxErrLane3", 9, 3 }, { "RxErrLane2", 6, 3 }, { "RxErrLane1", 3, 3 }, { "RxErrLane0", 0, 3 }, { "XGM_SERDES_STATUS1", 0x990, 0 }, { "CMULock", 31, 1 }, { "RxKLockLane3", 11, 1 }, { "RxKLockLane2", 10, 1 }, { "RxKLockLane1", 9, 1 }, { "RxKLockLane0", 8, 1 }, { "RxUFlowLane3", 7, 1 }, { "RxUFlowLane2", 6, 1 }, { "RxUFlowLane1", 5, 1 }, { "RxUFlowLane0", 4, 1 }, { "RxOFlowLane3", 3, 1 }, { "RxOFlowLane2", 2, 1 }, { "RxOFlowLane1", 1, 1 }, { "RxOFlowLane0", 0, 1 }, { "XGM_SERDES_STATUS2", 0x994, 0 }, { "RxEIDLane3", 11, 1 }, { "RxEIDLane2", 10, 1 }, { "RxEIDLane1", 9, 1 }, { "RxEIDLane0", 8, 1 }, { "RxRemSkipLane3", 7, 1 }, { "RxRemSkipLane2", 6, 1 }, { "RxRemSkipLane1", 5, 1 }, { "RxRemSkipLane0", 4, 1 }, { "RxAddSkipLane3", 3, 1 }, { "RxAddSkipLane2", 2, 1 }, { "RxAddSkipLane1", 1, 1 }, { "RxAddSkipLane0", 0, 1 }, { "XGM_XAUI_PCS_ERR", 0x998, 0 }, { "PCS_SyncStatus", 5, 4 }, { "PCS_CTCFIFOErr", 1, 4 }, { "PCS_NotAligned", 0, 1 }, { "XGM_RGMII_STATUS", 0x99c, 0 }, { "GMIIDuplex", 3, 1 }, { "GMIISpeed", 1, 2 }, { "GMIILinkStatus", 0, 1 }, { "XGM_WOL_STATUS", 0x9a0, 0 }, { "PatDetected", 31, 1 }, { "MatchedFilter", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0x9a4, 0 }, { "XGM_TX_SPI4_SOP_EOP_CNT", 0x9a8, 0 }, { "TxSPI4SopCnt", 16, 16 }, { "TxSPI4EopCnt", 0, 16 }, { "XGM_RX_SPI4_SOP_EOP_CNT", 0x9ac, 0 }, { "RxSPI4SopCnt", 16, 16 }, { "RxSPI4EopCnt", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info xgmac0_1_regs[] = { { "XGM_TX_CTRL", 0xa00, 0 }, { "SendPause", 2, 1 }, { "SendZeroPause", 1, 1 }, { "TxEn", 0, 1 }, { "XGM_TX_CFG", 0xa04, 0 }, { "CfgClkSpeed", 2, 3 }, { "StretchMode", 1, 1 }, { "TxPauseEn", 0, 1 }, { "XGM_TX_PAUSE_QUANTA", 0xa08, 0 }, { "TxPauseQuanta", 0, 16 }, { "XGM_RX_CTRL", 0xa0c, 0 }, { "RxEn", 0, 1 }, { "XGM_RX_CFG", 0xa10, 0 }, { "Con802_3Preamble", 12, 1 }, { "EnNon802_3Preamble", 11, 1 }, { "CopyPreamble", 10, 1 }, { "DisPauseFrames", 9, 1 }, { "En1536BFrames", 8, 1 }, { "EnJumbo", 7, 1 }, { "RmFCS", 6, 1 }, { "DisNonVlan", 5, 1 }, { "EnExtMatch", 4, 1 }, { "EnHashUcast", 3, 1 }, { "EnHashMcast", 2, 1 }, { "DisBCast", 1, 1 }, { "CopyAllFrames", 0, 1 }, { "XGM_RX_HASH_LOW", 0xa14, 0 }, { "XGM_RX_HASH_HIGH", 0xa18, 0 }, { "XGM_RX_EXACT_MATCH_LOW_1", 0xa1c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_1", 0xa20, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_2", 0xa24, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_2", 0xa28, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_3", 0xa2c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_3", 0xa30, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_4", 0xa34, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_4", 0xa38, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_5", 0xa3c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_5", 0xa40, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_6", 0xa44, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_6", 0xa48, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_7", 0xa4c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_7", 0xa50, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_8", 0xa54, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_8", 0xa58, 0 }, { "address_high", 0, 16 }, { "XGM_RX_TYPE_MATCH_1", 0xa5c, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_2", 0xa60, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_3", 0xa64, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_4", 0xa68, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_INT_STATUS", 0xa6c, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_MASK", 0xa70, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_ENABLE", 0xa74, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_DISABLE", 0xa78, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_TX_PAUSE_TIMER", 0xa7c, 0 }, { "CurPauseTimer", 0, 16 }, { "XGM_STAT_CTRL", 0xa80, 0 }, { "ReadSnpShot", 4, 1 }, { "TakeSnpShot", 3, 1 }, { "ClrStats", 2, 1 }, { "IncrStats", 1, 1 }, { "EnTestModeWr", 0, 1 }, { "XGM_RXFIFO_CFG", 0xa84, 0 }, { "RxFIFOPauseHWM", 17, 12 }, { "RxFIFOPauseLWM", 5, 12 }, { "ForcedPause", 4, 1 }, { "ExternLoopback", 3, 1 }, { "RxByteSwap", 2, 1 }, { "RxStrFrwrd", 1, 1 }, { "DisErrFrames", 0, 1 }, { "XGM_TXFIFO_CFG", 0xa88, 0 }, { "TxIPG", 13, 8 }, { "TxFIFOThresh", 4, 9 }, { "InternLoopback", 3, 1 }, { "TxByteSwap", 2, 1 }, { "DisCRC", 1, 1 }, { "DisPreAmble", 0, 1 }, { "XGM_SLOW_TIMER", 0xa8c, 0 }, { "PauseSlowTimerEn", 31, 1 }, { "PauseSlowTimer", 0, 20 }, { "XGM_SERDES_CTRL", 0xa90, 0 }, { "SERDESEn", 25, 1 }, { "SERDESReset_", 24, 1 }, { "CMURange", 21, 3 }, { "BGEnb", 20, 1 }, { "EnSkpDrop", 19, 1 }, { "EnComma", 18, 1 }, { "En8B10B", 17, 1 }, { "EnElBuf", 16, 1 }, { "Gain", 11, 5 }, { "BandGap", 7, 4 }, { "LpbkEn", 5, 2 }, { "RxEn", 4, 1 }, { "TxEn", 3, 1 }, { "RxComAdj", 2, 1 }, { "PreEmph", 0, 2 }, { "XGM_XAUI_PCS_TEST", 0xa94, 0 }, { "TestPattern", 1, 2 }, { "EnTest", 0, 1 }, { "XGM_RGMII_CTRL", 0xa98, 0 }, { "PhAlignFIFOThresh", 1, 2 }, { "TxClk90Shift", 0, 1 }, { "XGM_RGMII_IMP", 0xa9c, 0 }, { "ImpSetUpdate", 6, 1 }, { "RGMIIImpPD", 3, 3 }, { "RGMIIImpPU", 0, 3 }, { "XGM_XAUI_IMP", 0xaa0, 0 }, { "CalBusy", 31, 1 }, { "CalFault", 29, 1 }, { "CalImp", 24, 5 }, { "XAUIImp", 0, 3 }, { "XGM_SERDES_BIST", 0xaa4, 0 }, { "BISTDone", 28, 4 }, { "BISTCycleThresh", 3, 17 }, { "BISTMode", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE", 0xaa8, 0 }, { "RxMaxPktSize", 0, 14 }, { "XGM_RESET_CTRL", 0xaac, 0 }, { "XG2G_Reset_", 3, 1 }, { "RGMII_Reset_", 2, 1 }, { "PCS_Reset_", 1, 1 }, { "MAC_Reset_", 0, 1 }, { "XGM_XAUI1G_CTRL", 0xab0, 0 }, { "XAUI1GLinkId", 0, 2 }, { "XGM_SERDES_LANE_CTRL", 0xab4, 0 }, { "LaneReversal", 8, 1 }, { "TxPolarity", 4, 4 }, { "RxPolarity", 0, 4 }, { "XGM_PORT_CFG", 0xab8, 0 }, { "SafeSpeedChange", 4, 1 }, { "ClkDivReset_", 3, 1 }, { "PortSpeed", 1, 2 }, { "EnRGMII", 0, 1 }, { "XGM_EPIO_DATA0", 0xac0, 0 }, { "XGM_EPIO_DATA1", 0xac4, 0 }, { "XGM_EPIO_DATA2", 0xac8, 0 }, { "XGM_EPIO_DATA3", 0xacc, 0 }, { "XGM_EPIO_OP", 0xad0, 0 }, { "PIO_Ready", 31, 1 }, { "PIO_WrRd", 24, 1 }, { "PIO_Address", 0, 8 }, { "XGM_INT_ENABLE", 0xad4, 0 }, { "SERDESCMULock_loss", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBIST_err", 8, 4 }, { "SERDES_los", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_INT_CAUSE", 0xad8, 0 }, { "SERDESCMULock_loss", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBIST_err", 8, 4 }, { "SERDES_los", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_STAT_TX_BYTE_LOW", 0xb00, 0 }, { "XGM_STAT_TX_BYTE_HIGH", 0xb04, 0 }, { "TxBytes_high", 0, 13 }, { "XGM_STAT_TX_FRAME_LOW", 0xb08, 0 }, { "XGM_STAT_TX_FRAME_HIGH", 0xb0c, 0 }, { "TxFrames_high", 0, 4 }, { "XGM_STAT_TX_BCAST", 0xb10, 0 }, { "XGM_STAT_TX_MCAST", 0xb14, 0 }, { "XGM_STAT_TX_PAUSE", 0xb18, 0 }, { "XGM_STAT_TX_64B_FRAMES", 0xb1c, 0 }, { "XGM_STAT_TX_65_127B_FRAMES", 0xb20, 0 }, { "XGM_STAT_TX_128_255B_FRAMES", 0xb24, 0 }, { "XGM_STAT_TX_256_511B_FRAMES", 0xb28, 0 }, { "XGM_STAT_TX_512_1023B_FRAMES", 0xb2c, 0 }, { "XGM_STAT_TX_1024_1518B_FRAMES", 0xb30, 0 }, { "XGM_STAT_TX_1519_MAXB_FRAMES", 0xb34, 0 }, { "XGM_STAT_TX_ERR_FRAMES", 0xb38, 0 }, { "XGM_STAT_RX_BYTES_LOW", 0xb3c, 0 }, { "XGM_STAT_RX_BYTES_HIGH", 0xb40, 0 }, { "RxBytes_high", 0, 13 }, { "XGM_STAT_RX_FRAMES_LOW", 0xb44, 0 }, { "XGM_STAT_RX_FRAMES_HIGH", 0xb48, 0 }, { "RxFrames_high", 0, 4 }, { "XGM_STAT_RX_BCAST_FRAMES", 0xb4c, 0 }, { "XGM_STAT_RX_MCAST_FRAMES", 0xb50, 0 }, { "XGM_STAT_RX_PAUSE_FRAMES", 0xb54, 0 }, { "RxPauseFrames", 0, 16 }, { "XGM_STAT_RX_64B_FRAMES", 0xb58, 0 }, { "XGM_STAT_RX_65_127B_FRAMES", 0xb5c, 0 }, { "XGM_STAT_RX_128_255B_FRAMES", 0xb60, 0 }, { "XGM_STAT_RX_256_511B_FRAMES", 0xb64, 0 }, { "XGM_STAT_RX_512_1023B_FRAMES", 0xb68, 0 }, { "XGM_STAT_RX_1024_1518B_FRAMES", 0xb6c, 0 }, { "XGM_STAT_RX_1519_MAXB_FRAMES", 0xb70, 0 }, { "XGM_STAT_RX_SHORT_FRAMES", 0xb74, 0 }, { "RxShortFrames", 0, 16 }, { "XGM_STAT_RX_OVERSIZE_FRAMES", 0xb78, 0 }, { "RxOversizeFrames", 0, 16 }, { "XGM_STAT_RX_JABBER_FRAMES", 0xb7c, 0 }, { "RxJabberFrames", 0, 16 }, { "XGM_STAT_RX_CRC_ERR_FRAMES", 0xb80, 0 }, { "RxCRCErrFrames", 0, 16 }, { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0xb84, 0 }, { "RxLengthErrFrames", 0, 16 }, { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0xb88, 0 }, { "RxSymCodeErrFrames", 0, 16 }, { "XGM_SERDES_STATUS0", 0xb8c, 0 }, { "RxErrLane3", 9, 3 }, { "RxErrLane2", 6, 3 }, { "RxErrLane1", 3, 3 }, { "RxErrLane0", 0, 3 }, { "XGM_SERDES_STATUS1", 0xb90, 0 }, { "CMULock", 31, 1 }, { "RxKLockLane3", 11, 1 }, { "RxKLockLane2", 10, 1 }, { "RxKLockLane1", 9, 1 }, { "RxKLockLane0", 8, 1 }, { "RxUFlowLane3", 7, 1 }, { "RxUFlowLane2", 6, 1 }, { "RxUFlowLane1", 5, 1 }, { "RxUFlowLane0", 4, 1 }, { "RxOFlowLane3", 3, 1 }, { "RxOFlowLane2", 2, 1 }, { "RxOFlowLane1", 1, 1 }, { "RxOFlowLane0", 0, 1 }, { "XGM_SERDES_STATUS2", 0xb94, 0 }, { "RxEIDLane3", 11, 1 }, { "RxEIDLane2", 10, 1 }, { "RxEIDLane1", 9, 1 }, { "RxEIDLane0", 8, 1 }, { "RxRemSkipLane3", 7, 1 }, { "RxRemSkipLane2", 6, 1 }, { "RxRemSkipLane1", 5, 1 }, { "RxRemSkipLane0", 4, 1 }, { "RxAddSkipLane3", 3, 1 }, { "RxAddSkipLane2", 2, 1 }, { "RxAddSkipLane1", 1, 1 }, { "RxAddSkipLane0", 0, 1 }, { "XGM_XAUI_PCS_ERR", 0xb98, 0 }, { "PCS_SyncStatus", 5, 4 }, { "PCS_CTCFIFOErr", 1, 4 }, { "PCS_NotAligned", 0, 1 }, { "XGM_RGMII_STATUS", 0xb9c, 0 }, { "GMIIDuplex", 3, 1 }, { "GMIISpeed", 1, 2 }, { "GMIILinkStatus", 0, 1 }, { "XGM_WOL_STATUS", 0xba0, 0 }, { "PatDetected", 31, 1 }, { "MatchedFilter", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0xba4, 0 }, { "XGM_TX_SPI4_SOP_EOP_CNT", 0xba8, 0 }, { "TxSPI4SopCnt", 16, 16 }, { "TxSPI4EopCnt", 0, 16 }, { "XGM_RX_SPI4_SOP_EOP_CNT", 0xbac, 0 }, { "RxSPI4SopCnt", 16, 16 }, { "RxSPI4EopCnt", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; Index: projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3b.c =================================================================== --- projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3b.c (revision 204271) +++ projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3b.c (revision 204272) @@ -1,2832 +1,2832 @@ /* * $FreeBSD$ */ /* This file is automatically generated --- do not edit */ struct reg_info t3b_sge3_regs[] = { { "SG_CONTROL", 0x0, 0 }, { "UrgTnl", 26, 1 }, { "NewNotify", 25, 1 }, { "AvoidCqOvfl", 24, 1 }, { "OptOneIntMultQ", 23, 1 }, { "CQCrdtCtrl", 22, 1 }, { "EgrEnUpBp", 21, 1 }, { "DropPkt", 20, 1 }, { "EgrGenCtrl", 19, 1 }, { "UserSpaceSize", 14, 5 }, { "HostPageSize", 11, 3 }, { "PCIRelax", 10, 1 }, { "FLMode", 9, 1 }, { "PktShift", 6, 3 }, { "OneIntMultQ", 5, 1 }, { "FLPickAvail", 4, 1 }, { "BigEndianEgress", 3, 1 }, { "BigEndianIngress", 2, 1 }, { "IscsiCoalescing", 1, 1 }, { "GlobalEnable", 0, 1 }, { "SG_KDOORBELL", 0x4, 0 }, { "SelEgrCntx", 31, 1 }, { "EgrCntx", 0, 16 }, { "SG_GTS", 0x8, 0 }, { "RspQ", 29, 3 }, { "NewTimer", 16, 13 }, { "NewIndex", 0, 16 }, { "SG_CONTEXT_CMD", 0xc, 0 }, { "Opcode", 28, 4 }, { "Busy", 27, 1 }, { "CQ_credit", 20, 7 }, { "CQ", 19, 1 }, { "RspQ", 18, 1 }, { "Egress", 17, 1 }, { "FreeList", 16, 1 }, { "Context", 0, 16 }, { "SG_CONTEXT_DATA0", 0x10, 0 }, { "SG_CONTEXT_DATA1", 0x14, 0 }, { "SG_CONTEXT_DATA2", 0x18, 0 }, { "SG_CONTEXT_DATA3", 0x1c, 0 }, { "SG_CONTEXT_MASK0", 0x20, 0 }, { "SG_CONTEXT_MASK1", 0x24, 0 }, { "SG_CONTEXT_MASK2", 0x28, 0 }, { "SG_CONTEXT_MASK3", 0x2c, 0 }, { "SG_RSPQ_CREDIT_RETURN", 0x30, 0 }, { "RspQ", 29, 3 }, { "Data", 0, 16 }, { "SG_DATA_INTR", 0x34, 0 }, { "ErrIntr", 31, 1 }, { "DataIntr", 0, 8 }, { "SG_HI_DRB_HI_THRSH", 0x38, 0 }, { "HiDrbHiThrsh", 0, 10 }, { "SG_HI_DRB_LO_THRSH", 0x3c, 0 }, { "HiDrbLoThrsh", 0, 10 }, { "SG_LO_DRB_HI_THRSH", 0x40, 0 }, { "LoDrbHiThrsh", 0, 10 }, { "SG_LO_DRB_LO_THRSH", 0x44, 0 }, { "LoDrbLoThrsh", 0, 10 }, { "SG_ONE_INT_MULT_Q_COALESCING_TIMER", 0x48, 0 }, { "SG_RSPQ_FL_STATUS", 0x4c, 0 }, { "RspQ0Starved", 0, 1 }, { "RspQ1Starved", 1, 1 }, { "RspQ2Starved", 2, 1 }, { "RspQ3Starved", 3, 1 }, { "RspQ4Starved", 4, 1 }, { "RspQ5Starved", 5, 1 }, { "RspQ6Starved", 6, 1 }, { "RspQ7Starved", 7, 1 }, { "RspQ0Disabled", 8, 1 }, { "RspQ1Disabled", 9, 1 }, { "RspQ2Disabled", 10, 1 }, { "RspQ3Disabled", 11, 1 }, { "RspQ4Disabled", 12, 1 }, { "RspQ5Disabled", 13, 1 }, { "RspQ6Disabled", 14, 1 }, { "RspQ7Disabled", 15, 1 }, { "FL0Empty", 16, 1 }, { "FL1Empty", 17, 1 }, { "FL2Empty", 18, 1 }, { "FL3Empty", 19, 1 }, { "FL4Empty", 20, 1 }, { "FL5Empty", 21, 1 }, { "FL6Empty", 22, 1 }, { "FL7Empty", 23, 1 }, { "FL8Empty", 24, 1 }, { "FL9Empty", 25, 1 }, { "FL10Empty", 26, 1 }, { "FL11Empty", 27, 1 }, { "FL12Empty", 28, 1 }, { "FL13Empty", 29, 1 }, { "FL14Empty", 30, 1 }, { "FL15Empty", 31, 1 }, { "SG_EGR_PRI_CNT", 0x50, 0 }, { "EgrErrOpCode", 24, 8 }, { "EgrHiOpCode", 16, 8 }, { "EgrLoOpCode", 8, 8 }, { "EgrPriCnt", 0, 5 }, { "SG_EGR_RCQ_DRB_THRSH", 0x54, 0 }, { "HiRcqDrbThrsh", 16, 11 }, { "LoRcqDrbThrsh", 0, 11 }, { "SG_EGR_CNTX_BADDR", 0x58, 0 }, { "EgrCntxBAddr", 5, 27 }, { "SG_INT_CAUSE", 0x5c, 0 }, { "HiCtlDrbDropErr", 13, 1 }, { "LoCtlDrbDropErr", 12, 1 }, { "HiPioDrbDropErr", 11, 1 }, { "LoPioDrbDropErr", 10, 1 }, { "HiCrdtUndFlowErr", 9, 1 }, { "LoCrdtUndFlowErr", 8, 1 }, { "HiPriorityDBFull", 7, 1 }, { "HiPriorityDBEmpty", 6, 1 }, { "LoPriorityDBFull", 5, 1 }, { "LoPriorityDBEmpty", 4, 1 }, { "RspQDisabled", 3, 1 }, { "RspQCreditOverfow", 2, 1 }, { "FlEmpty", 1, 1 }, { "RspQStarve", 0, 1 }, { "SG_INT_ENABLE", 0x60, 0 }, { "HiCtlDrbDropErr", 13, 1 }, { "LoCtlDrbDropErr", 12, 1 }, { "HiPioDrbDropErr", 11, 1 }, { "LoPioDrbDropErr", 10, 1 }, { "HiCrdtUndFlowErr", 9, 1 }, { "LoCrdtUndFlowErr", 8, 1 }, { "HiPriorityDBFull", 7, 1 }, { "HiPriorityDBEmpty", 6, 1 }, { "LoPriorityDBFull", 5, 1 }, { "LoPriorityDBEmpty", 4, 1 }, { "RspQDisabled", 3, 1 }, { "RspQCreditOverfow", 2, 1 }, { "FlEmpty", 1, 1 }, { "RspQStarve", 0, 1 }, { "SG_CMDQ_CREDIT_TH", 0x64, 0 }, { "Timeout", 8, 24 }, { "Threshold", 0, 8 }, { "SG_TIMER_TICK", 0x68, 0 }, { "SG_CQ_CONTEXT_BADDR", 0x6c, 0 }, { "baseAddr", 5, 27 }, { "SG_OCO_BASE", 0x70, 0 }, { "Base1", 16, 16 }, { "Base0", 0, 16 }, { "SG_DRB_PRI_THRESH", 0x74, 0 }, { "DrbPriThrsh", 0, 16 }, { "SG_DEBUG_INDEX", 0x78, 0 }, { "SG_DEBUG_DATA", 0x7c, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_pcix1_regs[] = { { "PCIX_INT_ENABLE", 0x80, 0 }, { "MSIXParErr", 22, 3 }, { "CFParErr", 18, 4 }, { "RFParErr", 14, 4 }, { "WFParErr", 12, 2 }, { "PIOParErr", 11, 1 }, { "DetUncECCErr", 10, 1 }, { "DetCorECCErr", 9, 1 }, { "RcvSplCmpErr", 8, 1 }, { "UnxSplCmp", 7, 1 }, { "SplCmpDis", 6, 1 }, { "DetParErr", 5, 1 }, { "SigSysErr", 4, 1 }, { "RcvMstAbt", 3, 1 }, { "RcvTarAbt", 2, 1 }, { "SigTarAbt", 1, 1 }, { "MstDetParErr", 0, 1 }, { "PCIX_INT_CAUSE", 0x84, 0 }, { "MSIXParErr", 22, 3 }, { "CFParErr", 18, 4 }, { "RFParErr", 14, 4 }, { "WFParErr", 12, 2 }, { "PIOParErr", 11, 1 }, { "DetUncECCErr", 10, 1 }, { "DetCorECCErr", 9, 1 }, { "RcvSplCmpErr", 8, 1 }, { "UnxSplCmp", 7, 1 }, { "SplCmpDis", 6, 1 }, { "DetParErr", 5, 1 }, { "SigSysErr", 4, 1 }, { "RcvMstAbt", 3, 1 }, { "RcvTarAbt", 2, 1 }, { "SigTarAbt", 1, 1 }, { "MstDetParErr", 0, 1 }, { "PCIX_CFG", 0x88, 0 }, { "CLIDecEn", 18, 1 }, { "LatTmrDis", 17, 1 }, { "LowPwrEn", 16, 1 }, { "AsyncIntVec", 11, 5 }, { "MaxSplTrnC", 8, 3 }, { "MaxSplTrnR", 5, 3 }, { "MaxWrByteCnt", 3, 2 }, { "WrReqAtomicEn", 2, 1 }, { "CRstWrmMode", 1, 1 }, { "PIOAck64En", 0, 1 }, { "PCIX_MODE", 0x8c, 0 }, { "PClkRange", 6, 2 }, { "PCIXInitPat", 2, 4 }, { "66MHz", 1, 1 }, { "64Bit", 0, 1 }, { "PCIX_CAL", 0x90, 0 }, { "Busy", 31, 1 }, { "PerCalDiv", 22, 8 }, { "PerCalEn", 21, 1 }, { "SglCalEn", 20, 1 }, { "ZInUpdMode", 19, 1 }, { "ZInSel", 18, 1 }, { "ZPDMan", 15, 3 }, { "ZPUMan", 12, 3 }, { "ZPDOut", 9, 3 }, { "ZPUOut", 6, 3 }, { "ZPDIn", 3, 3 }, { "ZPUIn", 0, 3 }, { "PCIX_WOL", 0x94, 0 }, { "WakeUp1", 3, 1 }, { "WakeUp0", 2, 1 }, { "SleepMode1", 1, 1 }, { "SleepMode0", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_pcie0_regs[] = { { "PCIE_INT_ENABLE", 0x80, 0 }, { "BISTErr", 15, 8 }, { "MSIXParErr", 12, 3 }, { "CFParErr", 11, 1 }, { "RFParErr", 10, 1 }, { "WFParErr", 9, 1 }, { "PIOParErr", 8, 1 }, { "UnxSplCplErrC", 7, 1 }, { "UnxSplCplErrR", 6, 1 }, { "VPDAddrChng", 5, 1 }, { "BusMstrEn", 4, 1 }, { "PMStChng", 3, 1 }, { "PEXMsg", 2, 1 }, { "ZeroLenRd", 1, 1 }, { "PEXErr", 0, 1 }, { "PCIE_INT_CAUSE", 0x84, 0 }, { "BISTErr", 15, 8 }, { "MSIXParErr", 12, 3 }, { "CFParErr", 11, 1 }, { "RFParErr", 10, 1 }, { "WFParErr", 9, 1 }, { "PIOParErr", 8, 1 }, { "UnxSplCplErrC", 7, 1 }, { "UnxSplCplErrR", 6, 1 }, { "VPDAddrChng", 5, 1 }, { "BusMstrEn", 4, 1 }, { "PMStChng", 3, 1 }, { "PEXMsg", 2, 1 }, { "ZeroLenRd", 1, 1 }, { "PEXErr", 0, 1 }, { "PCIE_CFG", 0x88, 0 }, { "PriorityINTA", 23, 1 }, { "IniFullPkt", 22, 1 }, { "EnableLinkDwnDRst", 21, 1 }, { "EnableLinkDownRst", 20, 1 }, { "EnableHotRst", 19, 1 }, { "IniWaitForGnt", 18, 1 }, { "IniBEDis", 17, 1 }, { "CLIDecEn", 16, 1 }, { "AsyncIntVec", 11, 5 }, { "MaxSplTrnC", 7, 4 }, { "MaxSplTrnR", 1, 6 }, { "CRstWrmMode", 0, 1 }, { "PCIE_MODE", 0x8c, 0 }, { "NumFstTrnSeqRx", 10, 8 }, { "LnkCntlState", 2, 8 }, { "VC0Up", 1, 1 }, { "LnkInitial", 0, 1 }, { "PCIE_WOL", 0x94, 0 }, { "WakeUp1", 3, 1 }, { "WakeUp0", 2, 1 }, { "SleepMode1", 1, 1 }, { "SleepMode0", 0, 1 }, { "PCIE_PEX_CTRL0", 0x98, 0 }, { "CplTimeoutRetry", 31, 1 }, { "StrictTSMN", 30, 1 }, { "NumFstTrnSeq", 22, 8 }, { "ReplayLmt", 2, 20 }, { "TxPndChkEn", 1, 1 }, { "CplPndChkEn", 0, 1 }, { "PCIE_PEX_CTRL1", 0x9c, 0 }, { "RxPhyErrEn", 31, 1 }, { "DLLPTimeoutLmt", 13, 18 }, { "AckLat", 0, 13 }, { "PCIE_PEX_CTRL2", 0xa0, 0 }, { "LnkCntlDetDir", 30, 1 }, { "EnterL1rEn", 29, 1 }, { "PMExitL1Req", 28, 1 }, { "PMTxIdle", 27, 1 }, { "PCIModeLoop", 26, 1 }, { "L1ASPMTxRxL0sTime", 14, 12 }, { "L0sIdleTime", 3, 11 }, { "EnterL1ASPMEn", 2, 1 }, { "EnterL1En", 1, 1 }, { "EnterL0sEn", 0, 1 }, { "PCIE_PEX_ERR", 0xa4, 0 }, { "CplTimeoutID", 18, 7 }, { "FlowCtlOFlowErr", 17, 1 }, { "ReplayTimeout", 16, 1 }, { "ReplayRollover", 15, 1 }, { "BadDLLP", 14, 1 }, { "DLLPErr", 13, 1 }, { "FlowCtlProtErr", 12, 1 }, { "CplTimeout", 11, 1 }, { "PHYRcvErr", 10, 1 }, { "DisTLP", 9, 1 }, { "BadECRC", 8, 1 }, { "BadTLP", 7, 1 }, { "MalTLP", 6, 1 }, { "UnxCpl", 5, 1 }, { "UnsReq", 4, 1 }, { "PsnReq", 3, 1 }, { "UnsCpl", 2, 1 }, { "CplAbt", 1, 1 }, { "PsnCpl", 0, 1 }, { "PCIE_SERDES_CTRL", 0xa8, 0 }, { "PMASel", 3, 1 }, { "Lane", 0, 3 }, { "PCIE_SERDES_QUAD_CTRL0", 0xac, 0 }, { "TestSig", 10, 19 }, { "Offset", 2, 8 }, { "OffsetEn", 1, 1 }, { "IDDQb", 0, 1 }, { "PCIE_SERDES_QUAD_CTRL1", 0xb0, 0 }, { "FastInit", 28, 1 }, { "CTCDisable", 27, 1 }, { "ManResetPLL", 26, 1 }, { "ManL2Pwrdn", 25, 1 }, { "ManQuadEn", 24, 1 }, { "RxEqCtl", 22, 2 }, { "HiVMode", 21, 1 }, { "RefSel", 19, 2 }, { "RxTermAdj", 17, 2 }, { "TxTermAdj", 15, 2 }, { "Deq", 11, 4 }, { "Dtx", 7, 4 }, { "LoDrv", 6, 1 }, { "HiDrv", 5, 1 }, { "IntParReset", 4, 1 }, { "IntParLPBK", 3, 1 }, { "IntSerLPBKwDrv", 2, 1 }, { "PW", 1, 1 }, { "PClkDetect", 0, 1 }, { "PCIE_SERDES_LANE_CTRL", 0xb4, 0 }, { "ExtBISTChkErrClr", 22, 1 }, { "ExtBISTChkEn", 21, 1 }, { "ExtBISTGenEn", 20, 1 }, { "ExtBISTPat", 17, 3 }, { "ExtParReset", 16, 1 }, { "ExtParLPBK", 15, 1 }, { "ManRxTermEn", 14, 1 }, { "ManBeaconTxEn", 13, 1 }, { "ManRxDetectEn", 12, 1 }, { "ManTxIdleEn", 11, 1 }, { "ManRxIdleEn", 10, 1 }, { "ManL1Pwrdn", 9, 1 }, { "ManReset", 8, 1 }, { "ManFmOffset", 3, 5 }, { "ManFmOffsetEn", 2, 1 }, { "ManLaneEn", 1, 1 }, { "IntSerLPBK", 0, 1 }, { "PCIE_SERDES_LANE_STAT", 0xb8, 0 }, { "ExtBISTChkErrCnt", 8, 24 }, { "ExtBISTChkFmd", 7, 1 }, { "BeaconDetectChg", 6, 1 }, { "RxDetectChg", 5, 1 }, { "TxIdleDetectChg", 4, 1 }, { "BeaconDetect", 2, 1 }, { "RxDetect", 1, 1 }, { "TxIdleDetect", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_t3dbg_regs[] = { { "T3DBG_DBG0_CFG", 0xc0, 0 }, { "RegSelect", 9, 8 }, { "ModuleSelect", 4, 5 }, { "ClkSelect", 0, 4 }, { "T3DBG_DBG0_EN", 0xc4, 0 }, { "SDRByte0", 8, 1 }, { "DDREn", 4, 1 }, { "PortEn", 0, 1 }, { "T3DBG_DBG1_CFG", 0xc8, 0 }, { "RegSelect", 9, 8 }, { "ModuleSelect", 4, 5 }, { "ClkSelect", 0, 4 }, { "T3DBG_DBG1_EN", 0xcc, 0 }, { "SDRByte0", 8, 1 }, { "DDREn", 4, 1 }, { "PortEn", 0, 1 }, { "T3DBG_GPIO_EN", 0xd0, 0 }, { "GPIO11_OEn", 27, 1 }, { "GPIO10_OEn", 26, 1 }, { "GPIO9_OEn", 25, 1 }, { "GPIO8_OEn", 24, 1 }, { "GPIO7_OEn", 23, 1 }, { "GPIO6_OEn", 22, 1 }, { "GPIO5_OEn", 21, 1 }, { "GPIO4_OEn", 20, 1 }, { "GPIO3_OEn", 19, 1 }, { "GPIO2_OEn", 18, 1 }, { "GPIO1_OEn", 17, 1 }, { "GPIO0_OEn", 16, 1 }, { "GPIO11_Out_Val", 11, 1 }, { "GPIO10_Out_Val", 10, 1 }, { "GPIO9_Out_Val", 9, 1 }, { "GPIO8_Out_Val", 8, 1 }, { "GPIO7_Out_Val", 7, 1 }, { "GPIO6_Out_Val", 6, 1 }, { "GPIO5_Out_Val", 5, 1 }, { "GPIO4_Out_Val", 4, 1 }, { "GPIO3_Out_Val", 3, 1 }, { "GPIO2_Out_Val", 2, 1 }, { "GPIO1_Out_Val", 1, 1 }, { "GPIO0_Out_Val", 0, 1 }, { "T3DBG_GPIO_IN", 0xd4, 0 }, { "GPIO11_CHG_DET", 27, 1 }, { "GPIO10_CHG_DET", 26, 1 }, { "GPIO9_CHG_DET", 25, 1 }, { "GPIO8_CHG_DET", 24, 1 }, { "GPIO7_CHG_DET", 23, 1 }, { "GPIO6_CHG_DET", 22, 1 }, { "GPIO5_CHG_DET", 21, 1 }, { "GPIO4_CHG_DET", 20, 1 }, { "GPIO3_CHG_DET", 19, 1 }, { "GPIO2_CHG_DET", 18, 1 }, { "GPIO1_CHG_DET", 17, 1 }, { "GPIO0_CHG_DET", 16, 1 }, { "GPIO11_IN", 11, 1 }, { "GPIO10_IN", 10, 1 }, { "GPIO9_IN", 9, 1 }, { "GPIO8_IN", 8, 1 }, { "GPIO7_IN", 7, 1 }, { "GPIO6_IN", 6, 1 }, { "GPIO5_IN", 5, 1 }, { "GPIO4_IN", 4, 1 }, { "GPIO3_IN", 3, 1 }, { "GPIO2_IN", 2, 1 }, { "GPIO1_IN", 1, 1 }, { "GPIO0_IN", 0, 1 }, { "T3DBG_INT_ENABLE", 0xd8, 0 }, { "C_LOCK", 21, 1 }, { "M_LOCK", 20, 1 }, { "U_LOCK", 19, 1 }, { "R_LOCK", 18, 1 }, { "PX_LOCK", 17, 1 }, { "GPIO11", 11, 1 }, { "GPIO10", 10, 1 }, { "GPIO9", 9, 1 }, { "GPIO8", 8, 1 }, { "GPIO7", 7, 1 }, { "GPIO6", 6, 1 }, { "GPIO5", 5, 1 }, { "GPIO4", 4, 1 }, { "GPIO3", 3, 1 }, { "GPIO2", 2, 1 }, { "GPIO1", 1, 1 }, { "GPIO0", 0, 1 }, { "T3DBG_INT_CAUSE", 0xdc, 0 }, { "C_LOCK", 21, 1 }, { "M_LOCK", 20, 1 }, { "U_LOCK", 19, 1 }, { "R_LOCK", 18, 1 }, { "PX_LOCK", 17, 1 }, { "GPIO11", 11, 1 }, { "GPIO10", 10, 1 }, { "GPIO9", 9, 1 }, { "GPIO8", 8, 1 }, { "GPIO7", 7, 1 }, { "GPIO6", 6, 1 }, { "GPIO5", 5, 1 }, { "GPIO4", 4, 1 }, { "GPIO3", 3, 1 }, { "GPIO2", 2, 1 }, { "GPIO1", 1, 1 }, { "GPIO0", 0, 1 }, { "T3DBG_DBG0_RST_VALUE", 0xe0, 0 }, { "DebugData", 0, 8 }, { "T3DBG_PLL_OCLK_PAD_EN", 0xe4, 0 }, { "PCIE_OCLK_En", 20, 1 }, { "PClkTree_DBG_En", 17, 1 }, { "PCIX_OCLK_En", 16, 1 }, { "U_OCLK_En", 12, 1 }, { "R_OCLK_En", 8, 1 }, { "M_OCLK_En", 4, 1 }, { "C_OCLK_En", 0, 1 }, { "T3DBG_PLL_LOCK", 0xe8, 0 }, { "PCIX_LOCK", 16, 1 }, { "U_LOCK", 12, 1 }, { "R_LOCK", 8, 1 }, { "M_LOCK", 4, 1 }, { "C_LOCK", 0, 1 }, { "T3DBG_SERDES_RBC_CFG", 0xec, 0 }, { "X_RBC_Lane_Sel", 16, 2 }, { "X_RBC_Dbg_En", 12, 1 }, { "X_Serdes_Sel", 8, 1 }, { "PE_RBC_Lane_Sel", 4, 3 }, { "PE_RBC_Dbg_En", 0, 1 }, { "T3DBG_GPIO_ACT_LOW", 0xf0, 0 }, { "C_LOCK_ACT_LOW", 21, 1 }, { "M_LOCK_ACT_LOW", 20, 1 }, { "U_LOCK_ACT_LOW", 19, 1 }, { "R_LOCK_ACT_LOW", 18, 1 }, { "PX_LOCK_ACT_LOW", 17, 1 }, { "GPIO11_ACT_LOW", 11, 1 }, { "GPIO10_ACT_LOW", 10, 1 }, { "GPIO9_ACT_LOW", 9, 1 }, { "GPIO8_ACT_LOW", 8, 1 }, { "GPIO7_ACT_LOW", 7, 1 }, { "GPIO6_ACT_LOW", 6, 1 }, { "GPIO5_ACT_LOW", 5, 1 }, { "GPIO4_ACT_LOW", 4, 1 }, { "GPIO3_ACT_LOW", 3, 1 }, { "GPIO2_ACT_LOW", 2, 1 }, { "GPIO1_ACT_LOW", 1, 1 }, { "GPIO0_ACT_LOW", 0, 1 }, { "T3DBG_PMON_CFG", 0xf4, 0 }, { "PMON_DONE", 29, 1 }, { "PMON_FAIL", 28, 1 }, { "PMON_FDEL_AUTO", 22, 6 }, { "PMON_CDEL_AUTO", 16, 6 }, { "PMON_FDEL_MANUAL", 10, 6 }, { "PMON_CDEL_MANUAL", 4, 6 }, { "PMON_MANUAL", 1, 1 }, { "PMON_AUTO", 0, 1 }, { "T3DBG_SERDES_REFCLK_CFG", 0xf8, 0 }, { "PE_REFCLK_DBG_EN", 12, 1 }, { "X_REFCLK_DBG_EN", 8, 1 }, { "PE_REFCLK_TERMADJ", 5, 2 }, { "PE_REFCLK_PD", 4, 1 }, { "X_REFCLK_TERMADJ", 1, 2 }, { "X_REFCLK_PD", 0, 1 }, { "T3DBG_PCIE_PMA_BSPIN_CFG", 0xfc, 0 }, { "BSModeQuad1", 31, 1 }, { "BSInSelLane7", 29, 2 }, { "BSEnLane7", 28, 1 }, { "BSInSelLane6", 25, 2 }, { "BSEnLane6", 24, 1 }, { "BSInSelLane5", 21, 2 }, { "BSEnLane5", 20, 1 }, { "BSInSelLane4", 17, 2 }, { "BSEnLane4", 16, 1 }, { "BSModeQuad0", 15, 1 }, { "BSInSelLane3", 13, 2 }, { "BSEnLane3", 12, 1 }, { "BSInSelLane2", 9, 2 }, { "BSEnLane2", 8, 1 }, { "BSInSelLane1", 5, 2 }, { "BSEnLane1", 4, 1 }, { "BSInSelLane0", 1, 2 }, { "BSEnLane0", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_mc7_pmrx_regs[] = { { "MC7_CFG", 0x100, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x104, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x108, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x10c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x110, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x114, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x118, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x11c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x120, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x124, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x128, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ERR_ADDR", 0x12c, 0 }, { "ErrAddress", 3, 29 }, { "ErrAgent", 1, 2 }, { "ErrOp", 0, 1 }, { "MC7_ECC", 0x130, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x134, 0 }, { "MC7_CE_DATA0", 0x138, 0 }, { "MC7_CE_DATA1", 0x13c, 0 }, { "MC7_CE_DATA2", 0x140, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x144, 0 }, { "MC7_UE_DATA0", 0x148, 0 }, { "MC7_UE_DATA1", 0x14c, 0 }, { "MC7_UE_DATA2", 0x150, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x154, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x158, 0 }, { "MC7_BD_DATA1", 0x15c, 0 }, { "MC7_BD_DATA2", 0x160, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x164, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x168, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x16c, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x170, 0 }, { "MC7_BIST_OP", 0x174, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x178, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x17c, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_mc7_pmtx_regs[] = { { "MC7_CFG", 0x180, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x184, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x188, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x18c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x190, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x194, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x198, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x19c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x1a0, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x1a4, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x1a8, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ERR_ADDR", 0x1ac, 0 }, { "ErrAddress", 3, 29 }, { "ErrAgent", 1, 2 }, { "ErrOp", 0, 1 }, { "MC7_ECC", 0x1b0, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x1b4, 0 }, { "MC7_CE_DATA0", 0x1b8, 0 }, { "MC7_CE_DATA1", 0x1bc, 0 }, { "MC7_CE_DATA2", 0x1c0, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x1c4, 0 }, { "MC7_UE_DATA0", 0x1c8, 0 }, { "MC7_UE_DATA1", 0x1cc, 0 }, { "MC7_UE_DATA2", 0x1d0, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x1d4, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x1d8, 0 }, { "MC7_BD_DATA1", 0x1dc, 0 }, { "MC7_BD_DATA2", 0x1e0, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x1e4, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x1e8, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x1ec, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x1f0, 0 }, { "MC7_BIST_OP", 0x1f4, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x1f8, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x1fc, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_mc7_cm_regs[] = { { "MC7_CFG", 0x200, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x204, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x208, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x20c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x210, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x214, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x218, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x21c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x220, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x224, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x228, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ERR_ADDR", 0x22c, 0 }, { "ErrAddress", 3, 29 }, { "ErrAgent", 1, 2 }, { "ErrOp", 0, 1 }, { "MC7_ECC", 0x230, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x234, 0 }, { "MC7_CE_DATA0", 0x238, 0 }, { "MC7_CE_DATA1", 0x23c, 0 }, { "MC7_CE_DATA2", 0x240, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x244, 0 }, { "MC7_UE_DATA0", 0x248, 0 }, { "MC7_UE_DATA1", 0x24c, 0 }, { "MC7_UE_DATA2", 0x250, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x254, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x258, 0 }, { "MC7_BD_DATA1", 0x25c, 0 }, { "MC7_BD_DATA2", 0x260, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x264, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x268, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x26c, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x270, 0 }, { "MC7_BIST_OP", 0x274, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x278, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x27c, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_cim_regs[] = { { "CIM_BOOT_CFG", 0x280, 0 }, { "BootAddr", 2, 30 }, { "BootSdram", 1, 1 }, { "uPCRst", 0, 1 }, { "CIM_FLASH_BASE_ADDR", 0x284, 0 }, { "FlashBaseAddr", 2, 22 }, { "CIM_FLASH_ADDR_SIZE", 0x288, 0 }, { "FlashAddrSize", 2, 22 }, { "CIM_SDRAM_BASE_ADDR", 0x28c, 0 }, { "SdramBaseAddr", 2, 30 }, { "CIM_SDRAM_ADDR_SIZE", 0x290, 0 }, { "SdramAddrSize", 2, 30 }, { "CIM_UP_SPARE_INT", 0x294, 0 }, { "uPSpareInt", 0, 3 }, { "CIM_HOST_INT_ENABLE", 0x298, 0 }, { "Timer1IntEn", 15, 1 }, { "Timer0IntEn", 14, 1 }, { "PrefDropIntEn", 13, 1 }, { "BlkWrPlIntEn", 12, 1 }, { "BlkRdPlIntEn", 11, 1 }, { "BlkWrCtlIntEn", 10, 1 }, { "BlkRdCtlIntEn", 9, 1 }, { "BlkWrFlashIntEn", 8, 1 }, { "BlkRdFlashIntEn", 7, 1 }, { "SglWrFlashIntEn", 6, 1 }, { "WrBlkFlashIntEn", 5, 1 }, { "BlkWrBootIntEn", 4, 1 }, { "BlkRdBootIntEn", 3, 1 }, { "FlashRangeIntEn", 2, 1 }, { "SdramRangeIntEn", 1, 1 }, { "RsvdSpaceIntEn", 0, 1 }, { "CIM_HOST_INT_CAUSE", 0x29c, 0 }, { "Timer1Int", 15, 1 }, { "Timer0Int", 14, 1 }, { "PrefDropInt", 13, 1 }, { "BlkWrPlInt", 12, 1 }, { "BlkRdPlInt", 11, 1 }, { "BlkWrCtlInt", 10, 1 }, { "BlkRdCtlInt", 9, 1 }, { "BlkWrFlashInt", 8, 1 }, { "BlkRdFlashInt", 7, 1 }, { "SglWrFlashInt", 6, 1 }, { "WrBlkFlashInt", 5, 1 }, { "BlkWrBootInt", 4, 1 }, { "BlkRdBootInt", 3, 1 }, { "FlashRangeInt", 2, 1 }, { "SdramRangeInt", 1, 1 }, { "RsvdSpaceInt", 0, 1 }, { "CIM_UP_INT_ENABLE", 0x2a0, 0 }, { "MstPlIntEn", 16, 1 }, { "Timer1IntEn", 15, 1 }, { "Timer0IntEn", 14, 1 }, { "PrefDropIntEn", 13, 1 }, { "BlkWrPlIntEn", 12, 1 }, { "BlkRdPlIntEn", 11, 1 }, { "BlkWrCtlIntEn", 10, 1 }, { "BlkRdCtlIntEn", 9, 1 }, { "BlkWrFlashIntEn", 8, 1 }, { "BlkRdFlashIntEn", 7, 1 }, { "SglWrFlashIntEn", 6, 1 }, { "WrBlkFlashIntEn", 5, 1 }, { "BlkWrBootIntEn", 4, 1 }, { "BlkRdBootIntEn", 3, 1 }, { "FlashRangeIntEn", 2, 1 }, { "SdramRangeIntEn", 1, 1 }, { "RsvdSpaceIntEn", 0, 1 }, { "CIM_UP_INT_CAUSE", 0x2a4, 0 }, { "MstPlInt", 16, 1 }, { "Timer1Int", 15, 1 }, { "Timer0Int", 14, 1 }, { "PrefDropInt", 13, 1 }, { "BlkWrPlInt", 12, 1 }, { "BlkRdPlInt", 11, 1 }, { "BlkWrCtlInt", 10, 1 }, { "BlkRdCtlInt", 9, 1 }, { "BlkWrFlashInt", 8, 1 }, { "BlkRdFlashInt", 7, 1 }, { "SglWrFlashInt", 6, 1 }, { "WrBlkFlashInt", 5, 1 }, { "BlkWrBootInt", 4, 1 }, { "BlkRdBootInt", 3, 1 }, { "FlashRangeInt", 2, 1 }, { "SdramRangeInt", 1, 1 }, { "RsvdSpaceInt", 0, 1 }, { "CIM_IBQ_FULLA_THRSH", 0x2a8, 0 }, { "Ibq0FullThrsh", 0, 9 }, { "Ibq1FullThrsh", 16, 9 }, { "CIM_IBQ_FULLB_THRSH", 0x2ac, 0 }, { "Ibq2FullThrsh", 0, 9 }, { "Ibq3FullThrsh", 16, 9 }, { "CIM_HOST_ACC_CTRL", 0x2b0, 0 }, { "HostBusy", 17, 1 }, { "HostWrite", 16, 1 }, { "HostAddr", 0, 16 }, { "CIM_HOST_ACC_DATA", 0x2b4, 0 }, { "CIM_IBQ_DBG_CFG", 0x2c0, 0 }, { "IbqDbgAddr", 16, 9 }, { "IbqDbgQID", 3, 2 }, { "IbqDbgWr", 2, 1 }, { "IbqDbgBusy", 1, 1 }, { "IbqDbgEn", 0, 1 }, { "CIM_OBQ_DBG_CFG", 0x2c4, 0 }, { "ObqDbgAddr", 16, 9 }, { "ObqDbgQID", 3, 2 }, { "ObqDbgWr", 2, 1 }, { "ObqDbgBusy", 1, 1 }, { "ObqDbgEn", 0, 1 }, { "CIM_IBQ_DBG_DATA", 0x2c8, 0 }, { "CIM_OBQ_DBG_DATA", 0x2cc, 0 }, { "CIM_CDEBUGDATA", 0x2d0, 0 }, { "CDebugDataH", 16, 16 }, { "CDebugDataL", 0, 16 }, { "CIM_DEBUGCFG", 0x2e0, 0 }, { "POLADbgRdPtr", 23, 9 }, { "PILADbgRdPtr", 14, 9 }, { "LADbgEn", 12, 1 }, { "DebugSelH", 5, 5 }, { "DebugSelL", 0, 5 }, { "CIM_DEBUGSTS", 0x2e4, 0 }, { "POLADbgWrPtr", 16, 9 }, { "PILADbgWrPtr", 0, 9 }, { "CIM_PO_LA_DEBUGDATA", 0x2e8, 0 }, { "CIM_PI_LA_DEBUGDATA", 0x2ec, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_tp1_regs[] = { { "TP_IN_CONFIG", 0x300, 0 }, { "RXFbArbPrio", 25, 1 }, { "TXFbArbPrio", 24, 1 }, { "DBMaxOpCnt", 16, 8 }, { "IPv6Enable", 15, 1 }, { "NICMode", 14, 1 }, { "EChecksumCheckTCP", 13, 1 }, { "EChecksumCheckIP", 12, 1 }, { "ECPL", 10, 1 }, { "EEthernet", 8, 1 }, { "ETunnel", 7, 1 }, { "CChecksumCheckTCP", 6, 1 }, { "CChecksumCheckIP", 5, 1 }, { "CCPL", 3, 1 }, { "CEthernet", 1, 1 }, { "CTunnel", 0, 1 }, { "TP_OUT_CONFIG", 0x304, 0 }, { "IPIDSplitMode", 16, 1 }, { "VLANExtractionEnable2ndPort", 13, 1 }, { "VLANExtractionEnable", 12, 1 }, { "EChecksumGenerateTCP", 11, 1 }, { "EChecksumGenerateIP", 10, 1 }, { "ECPL", 8, 1 }, { "EEthernet", 6, 1 }, { "CChecksumGenerateTCP", 5, 1 }, { "CChecksumGenerateIP", 4, 1 }, { "CCPL", 2, 1 }, { "CEthernet", 0, 1 }, { "TP_GLOBAL_CONFIG", 0x308, 0 }, { "SYNCookieParams", 26, 6 }, { "RXFlowControlDisable", 25, 1 }, { "TXPacingEnable", 24, 1 }, { "AttackFilterEnable", 23, 1 }, { "SYNCookieNoOptions", 22, 1 }, { "ProtectedMode", 21, 1 }, { "PingDrop", 20, 1 }, { "FragmentDrop", 19, 1 }, { "FiveTupleLookup", 17, 2 }, { "PathMTU", 15, 1 }, { "IPIdentSplit", 14, 1 }, { "IPChecksumOffload", 13, 1 }, { "UDPChecksumOffload", 12, 1 }, { "TCPChecksumOffload", 11, 1 }, { "QOSMapping", 10, 1 }, { "TCAMServerUse", 8, 2 }, { "IPTTL", 0, 8 }, { "TP_GLOBAL_RX_CREDIT", 0x30c, 0 }, { "TP_CMM_SIZE", 0x310, 0 }, { "CMMemMgrSize", 0, 28 }, { "TP_CMM_MM_BASE", 0x314, 0 }, { "CMMemMgrBase", 0, 28 }, { "TP_CMM_TIMER_BASE", 0x318, 0 }, { "CMTimerMaxNum", 28, 2 }, { "CMTimerBase", 0, 28 }, { "TP_PMM_SIZE", 0x31c, 0 }, { "PMSize", 0, 28 }, { "TP_PMM_TX_BASE", 0x320, 0 }, { "TP_PMM_DEFRAG_BASE", 0x324, 0 }, { "TP_PMM_RX_BASE", 0x328, 0 }, { "TP_PMM_RX_PAGE_SIZE", 0x32c, 0 }, { "TP_PMM_RX_MAX_PAGE", 0x330, 0 }, { "PMRxMaxPage", 0, 21 }, { "TP_PMM_TX_PAGE_SIZE", 0x334, 0 }, { "TP_PMM_TX_MAX_PAGE", 0x338, 0 }, { "PMTxMaxPage", 0, 21 }, { "TP_TCP_OPTIONS", 0x340, 0 }, { "MTUDefault", 16, 16 }, { "MTUEnable", 10, 1 }, { "SACKTx", 9, 1 }, { "SACKRx", 8, 1 }, { "SACKMode", 4, 2 }, { "WindowScaleMode", 2, 2 }, { "TimestampsMode", 0, 2 }, { "TP_DACK_CONFIG", 0x344, 0 }, { "AutoState3", 30, 2 }, { "AutoState2", 28, 2 }, { "AutoState1", 26, 2 }, { "ByteThreshold", 5, 20 }, { "MSSThreshold", 3, 2 }, { "AutoCareful", 2, 1 }, { "AutoEnable", 1, 1 }, { "Mode", 0, 1 }, { "TP_PC_CONFIG", 0x348, 0 }, { "CMCacheDisable", 31, 1 }, { "EnableOcspiFull", 30, 1 }, { "EnableFLMErrorDDP", 29, 1 }, { "LockTid", 28, 1 }, { "FixRcvWnd", 27, 1 }, { "TxTosQueueMapMode", 26, 1 }, { "RddpCongEn", 25, 1 }, { "EnableOnFlyPDU", 24, 1 }, { "EnableEPCMDAFull", 23, 1 }, { "ModulateUnionMode", 22, 1 }, { "TxDataAckRateEnable", 21, 1 }, { "TxDeferEnable", 20, 1 }, { "RxCongestionMode", 19, 1 }, { "HearbeatOnceDACK", 18, 1 }, { "HearbeatOnceHeap", 17, 1 }, { "HearbeatDACK", 16, 1 }, { "TxCongestionMode", 15, 1 }, { "AcceptLatestRcvAdv", 14, 1 }, { "DisableSYNData", 13, 1 }, { "DisableWindowPSH", 12, 1 }, { "DisableFINOldData", 11, 1 }, { "EnableFLMError", 10, 1 }, { "DisableNextMtu", 9, 1 }, { "FilterPeerFIN", 8, 1 }, { "EnableFeedbackSend", 7, 1 }, { "EnableRDMAError", 6, 1 }, { "EnableDDPFlowControl", 5, 1 }, { "DisableHeldFIN", 4, 1 }, { "TableLatencyDelta", 0, 4 }, { "TP_PC_CONFIG2", 0x34c, 0 }, { "EnableDropRQEmptyPkt", 10, 1 }, { "EnableTxPortfromDA2", 9, 1 }, { "EnableRxPktTmstpRss", 8, 1 }, { "EnableSndUnaInRxData", 7, 1 }, { "EnableRxPortFromAddr", 6, 1 }, { "EnableTxPortfromDA", 5, 1 }, { "CHdrAFull", 4, 1 }, { "EnableNonOfdScbBit", 3, 1 }, { "EnableNonOfdTidRss", 2, 1 }, { "EnableNonOfdTcbRss", 1, 1 }, { "EnableOldRxForward", 0, 1 }, { "TP_TCP_BACKOFF_REG0", 0x350, 0 }, { "TimerBackoffIndex3", 24, 8 }, { "TimerBackoffIndex2", 16, 8 }, { "TimerBackoffIndex1", 8, 8 }, { "TimerBackoffIndex0", 0, 8 }, { "TP_TCP_BACKOFF_REG1", 0x354, 0 }, { "TimerBackoffIndex7", 24, 8 }, { "TimerBackoffIndex6", 16, 8 }, { "TimerBackoffIndex5", 8, 8 }, { "TimerBackoffIndex4", 0, 8 }, { "TP_TCP_BACKOFF_REG2", 0x358, 0 }, { "TimerBackoffIndex11", 24, 8 }, { "TimerBackoffIndex10", 16, 8 }, { "TimerBackoffIndex9", 8, 8 }, { "TimerBackoffIndex8", 0, 8 }, { "TP_TCP_BACKOFF_REG3", 0x35c, 0 }, { "TimerBackoffIndex15", 24, 8 }, { "TimerBackoffIndex14", 16, 8 }, { "TimerBackoffIndex13", 8, 8 }, { "TimerBackoffIndex12", 0, 8 }, { "TP_PARA_REG0", 0x360, 0 }, { "InitCwnd", 24, 3 }, { "DupAckThresh", 20, 4 }, { "TP_PARA_REG1", 0x364, 0 }, { "InitRwnd", 16, 16 }, { "InitialSSThresh", 0, 16 }, { "TP_PARA_REG2", 0x368, 0 }, { "MaxRxData", 16, 16 }, { "RxCoalesceSize", 0, 16 }, { "TP_PARA_REG3", 0x36c, 0 }, { "TunnelCngDrop1", 21, 1 }, { "TunnelCngDrop0", 20, 1 }, { "TxDataAckIdx", 16, 4 }, { "RxFragEnable", 12, 3 }, { "TxPaceFixedStrict", 11, 1 }, { "TxPaceAutoStrict", 10, 1 }, { "TxPaceFixed", 9, 1 }, { "TxPaceAuto", 8, 1 }, { "RxUrgTunnel", 6, 1 }, { "RxUrgMode", 5, 1 }, { "TxUrgMode", 4, 1 }, { "CngCtrlMode", 2, 2 }, { "RxCoalesceEnable", 1, 1 }, { "RxCoalescePshEn", 0, 1 }, { "TP_PARA_REG4", 0x370, 0 }, { "HighSpeedCfg", 24, 8 }, { "NewRenoCfg", 16, 8 }, { "TahoeCfg", 8, 8 }, { "RenoCfg", 0, 8 }, { "TP_PARA_REG5", 0x374, 0 }, { "IndicateSize", 16, 16 }, { "SchdEnable", 8, 1 }, { "OnFlyDDPEnable", 2, 1 }, { "DackTimerSpin", 1, 1 }, { "PushTimerEnable", 0, 1 }, { "TP_PARA_REG6", 0x378, 0 }, { "TxPDUSizeAdj", 16, 8 }, { "EnableDeferACK", 12, 1 }, { "EnableESnd", 11, 1 }, { "EnableCSnd", 10, 1 }, { "EnablePDUE", 9, 1 }, { "EnablePDUC", 8, 1 }, { "EnableBUFI", 7, 1 }, { "EnableBUFE", 6, 1 }, { "EnableDefer", 5, 1 }, { "EnableClearRxmtOos", 4, 1 }, { "DisablePDUCng", 3, 1 }, { "DisablePDUTimeout", 2, 1 }, { "DisablePDURxmt", 1, 1 }, { "DisablePDUxmt", 0, 1 }, { "TP_PARA_REG7", 0x37c, 0 }, { "PMMaxXferLen1", 16, 16 }, { "PMMaxXferLen0", 0, 16 }, { "TP_TIMER_RESOLUTION", 0x390, 0 }, { "TimerResolution", 16, 8 }, { "TimestampResolution", 8, 8 }, { "DelayedACKResolution", 0, 8 }, { "TP_MSL", 0x394, 0 }, { "MSL", 0, 30 }, { "TP_RXT_MIN", 0x398, 0 }, { "RxtMin", 0, 30 }, { "TP_RXT_MAX", 0x39c, 0 }, { "RxtMax", 0, 30 }, { "TP_PERS_MIN", 0x3a0, 0 }, { "PersMin", 0, 30 }, { "TP_PERS_MAX", 0x3a4, 0 }, { "PersMax", 0, 30 }, { "TP_KEEP_IDLE", 0x3a8, 0 }, { "KeepaliveIdle", 0, 30 }, { "TP_KEEP_INTVL", 0x3ac, 0 }, { "KeepaliveIntvl", 0, 30 }, { "TP_INIT_SRTT", 0x3b0, 0 }, { "InitSrtt", 0, 16 }, { "TP_DACK_TIMER", 0x3b4, 0 }, { "DackTime", 0, 12 }, { "TP_FINWAIT2_TIMER", 0x3b8, 0 }, { "Finwait2Time", 0, 30 }, { "TP_FAST_FINWAIT2_TIMER", 0x3bc, 0 }, { "FastFinwait2Time", 0, 30 }, { "TP_SHIFT_CNT", 0x3c0, 0 }, { "SynShiftMax", 24, 8 }, { "RxtShiftMaxR1", 20, 4 }, { "RxtShiftMaxR2", 16, 4 }, { "PerShiftBackoffMax", 12, 4 }, { "PerShiftMax", 8, 4 }, { "KeepaliveMax", 0, 8 }, { "TP_TIME_HI", 0x3c8, 0 }, { "TP_TIME_LO", 0x3cc, 0 }, { "TP_MTU_PORT_TABLE", 0x3d0, 0 }, { "Port1MTUValue", 16, 16 }, { "Port0MTUValue", 0, 16 }, { "TP_ULP_TABLE", 0x3d4, 0 }, { "ULPType7Field", 28, 4 }, { "ULPType6Field", 24, 4 }, { "ULPType5Field", 20, 4 }, { "ULPType4Field", 16, 4 }, { "ULPType3Field", 12, 4 }, { "ULPType2Field", 8, 4 }, { "ULPType1Field", 4, 4 }, { "ULPType0Field", 0, 4 }, { "TP_PACE_TABLE", 0x3d8, 0 }, { "TP_CCTRL_TABLE", 0x3dc, 0 }, { "TP_TOS_TABLE", 0x3e0, 0 }, { "TP_MTU_TABLE", 0x3e4, 0 }, { "TP_RSS_MAP_TABLE", 0x3e8, 0 }, { "TP_RSS_LKP_TABLE", 0x3ec, 0 }, { "TP_RSS_CONFIG", 0x3f0, 0 }, { "TNL4tupEn", 29, 1 }, { "TNL2tupEn", 28, 1 }, { "TNLprtEn", 26, 1 }, { "TNLMapEn", 25, 1 }, { "TNLLkpEn", 24, 1 }, { "OFD4tupEn", 21, 1 }, { "OFD2tupEn", 20, 1 }, { "OFDMapEn", 17, 1 }, { "OFDLkpEn", 16, 1 }, { "SYN4tupEn", 13, 1 }, { "SYN2tupEn", 12, 1 }, { "SYNMapEn", 9, 1 }, { "SYNLkpEn", 8, 1 }, { "RRCPLMapEn", 7, 1 }, { "RRCPLCPUSIZE", 4, 3 }, { "RQFeedbackEnable", 3, 1 }, { "HashToeplitz", 2, 1 }, { "HashSave", 1, 1 }, { "Disable", 0, 1 }, { "TP_RSS_CONFIG_TNL", 0x3f4, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_CONFIG_OFD", 0x3f8, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_CONFIG_SYN", 0x3fc, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_SECRET_KEY0", 0x400, 0 }, { "TP_RSS_SECRET_KEY1", 0x404, 0 }, { "TP_RSS_SECRET_KEY2", 0x408, 0 }, { "TP_RSS_SECRET_KEY3", 0x40c, 0 }, { "TP_TM_PIO_ADDR", 0x418, 0 }, { "TP_TM_PIO_DATA", 0x41c, 0 }, { "TP_TX_MOD_QUE_TABLE", 0x420, 0 }, { "TP_TX_RESOURCE_LIMIT", 0x424, 0 }, { "TX_RESOURCE_LIMIT_CH1_PC", 24, 8 }, { "TX_RESOURCE_LIMIT_CH1_NON_PC", 16, 8 }, { "TX_RESOURCE_LIMIT_CH0_PC", 8, 8 }, { "TX_RESOURCE_LIMIT_CH0_NON_PC", 0, 8 }, { "TP_TX_MOD_QUEUE_REQ_MAP", 0x428, 0 }, { "RX_MOD_WEIGHT", 24, 8 }, { "TX_MOD_WEIGHT", 16, 8 }, { "TX_MOD_TIMER_MODE", 8, 8 }, { "TX_MOD_QUEUE_REQ_MAP", 0, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT1", 0x42c, 0 }, { "TP_TX_MOD_QUEUE_WEIGHT7", 24, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT6", 16, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT5", 8, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT4", 0, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT0", 0x430, 0 }, { "TP_TX_MOD_QUEUE_WEIGHT3", 24, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT2", 16, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT1", 8, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT0", 0, 8 }, { "TP_MOD_CHANNEL_WEIGHT", 0x434, 0 }, { "RX_MOD_CHANNEL_WEIGHT1", 24, 8 }, { "RX_MOD_CHANNEL_WEIGHT0", 16, 8 }, { "TX_MOD_CHANNEL_WEIGHT1", 8, 8 }, { "TX_MOD_CHANNEL_WEIGHT0", 0, 8 }, { "TP_MOD_RATE_LIMIT", 0x438, 0 }, { "RX_MOD_RATE_LIMIT_INC", 24, 8 }, { "RX_MOD_RATE_LIMIT_TICK", 16, 8 }, { "TX_MOD_RATE_LIMIT_INC", 8, 8 }, { "TX_MOD_RATE_LIMIT_TICK", 0, 8 }, { "TP_PIO_ADDR", 0x440, 0 }, { "TP_PIO_DATA", 0x444, 0 }, { "TP_RESET", 0x44c, 0 }, { "FlstInitEnable", 1, 1 }, { "TPReset", 0, 1 }, { "TP_MIB_INDEX", 0x450, 0 }, { "TP_MIB_RDATA", 0x454, 0 }, { "TP_SYNC_TIME_HI", 0x458, 0 }, { "TP_SYNC_TIME_LO", 0x45c, 0 }, { "TP_CMM_MM_RX_FLST_BASE", 0x460, 0 }, { "CMRxFlstBase", 0, 28 }, { "TP_CMM_MM_TX_FLST_BASE", 0x464, 0 }, { "CMTxFlstBase", 0, 28 }, { "TP_CMM_MM_PS_FLST_BASE", 0x468, 0 }, { "CMPsFlstBase", 0, 28 }, { "TP_CMM_MM_MAX_PSTRUCT", 0x46c, 0 }, { "CMMaxPstruct", 0, 21 }, { "TP_INT_ENABLE", 0x470, 0 }, { "TP_INT_CAUSE", 0x474, 0 }, { "TP_FLM_FREE_PS_CNT", 0x480, 0 }, { "FreePstructCount", 0, 21 }, { "TP_FLM_FREE_RX_CNT", 0x484, 0 }, { "FreeRxPageCount", 0, 21 }, { "TP_FLM_FREE_TX_CNT", 0x488, 0 }, { "FreeTxPageCount", 0, 21 }, { "TP_TM_HEAP_PUSH_CNT", 0x48c, 0 }, { "TP_TM_HEAP_POP_CNT", 0x490, 0 }, { "TP_TM_DACK_PUSH_CNT", 0x494, 0 }, { "TP_TM_DACK_POP_CNT", 0x498, 0 }, { "TP_TM_MOD_PUSH_CNT", 0x49c, 0 }, { "TP_MOD_POP_CNT", 0x4a0, 0 }, { "TP_TIMER_SEPARATOR", 0x4a4, 0 }, { "TP_DEBUG_SEL", 0x4a8, 0 }, { "TP_DEBUG_FLAGS", 0x4ac, 0 }, { "RxTimerDackFirst", 26, 1 }, { "RxTimerDack", 25, 1 }, { "RxTimerHeartbeat", 24, 1 }, { "RxPawsDrop", 23, 1 }, { "RxUrgDataDrop", 22, 1 }, { "RxFutureData", 21, 1 }, { "RxRcvRxmData", 20, 1 }, { "RxRcvOooDataFin", 19, 1 }, { "RxRcvOooData", 18, 1 }, { "RxRcvWndZero", 17, 1 }, { "RxRcvWndLtMss", 16, 1 }, { "TxDupAckInc", 11, 1 }, { "TxRxmUrg", 10, 1 }, { "TxRxmFin", 9, 1 }, { "TxRxmSyn", 8, 1 }, { "TxRxmNewReno", 7, 1 }, { "TxRxmFast", 6, 1 }, { "TxRxmTimer", 5, 1 }, { "TxRxmTimerKeepalive", 4, 1 }, { "TxRxmTimerPersist", 3, 1 }, { "TxRcvAdvShrunk", 2, 1 }, { "TxRcvAdvZero", 1, 1 }, { "TxRcvAdvLtMss", 0, 1 }, { "TP_PROXY_FLOW_CNTL", 0x4b0, 0 }, { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 }, { "EDropTunnel", 19, 1 }, { "CDropTunnel", 18, 1 }, { "EThreshold", 12, 6 }, { "CThreshold", 6, 6 }, { "TxThreshold", 0, 6 }, { "TP_TX_DROP_COUNT", 0x4bc, 0 }, { "TP_CLEAR_DEBUG", 0x4c0, 0 }, { "ClrDebug", 0, 1 }, { "TP_DEBUG_VEC", 0x4c4, 0 }, { "TP_DEBUG_VEC2", 0x4c8, 0 }, { "TP_DEBUG_REG_SEL", 0x4cc, 0 }, { "TP_DEBUG", 0x4d0, 0 }, { "TP_DBG_LA_CONFIG", 0x4d4, 0 }, { "TP_DBG_LA_DATAH", 0x4d8, 0 }, { "TP_DBG_LA_DATAL", 0x4dc, 0 }, { "TP_EMBED_OP_FIELD0", 0x4e8, 0 }, { "TP_EMBED_OP_FIELD1", 0x4ec, 0 }, { "TP_EMBED_OP_FIELD2", 0x4f0, 0 }, { "TP_EMBED_OP_FIELD3", 0x4f4, 0 }, { "TP_EMBED_OP_FIELD4", 0x4f8, 0 }, { "TP_EMBED_OP_FIELD5", 0x4fc, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_ulp2_rx_regs[] = { { "ULPRX_CTL", 0x500, 0 }, { "PCMD1Threshold", 24, 8 }, { "PCMD0Threshold", 16, 8 }, { "round_robin", 4, 1 }, { "RDMA_permissive_mode", 3, 1 }, { "PagePodME", 2, 1 }, { "IscsiTagTcb", 1, 1 }, { "TddpTagTcb", 0, 1 }, { "ULPRX_INT_ENABLE", 0x504, 0 }, { "ParErr", 0, 1 }, { "ULPRX_INT_CAUSE", 0x508, 0 }, { "ParErr", 0, 1 }, { "ULPRX_ISCSI_LLIMIT", 0x50c, 0 }, { "IscsiLlimit", 6, 26 }, { "ULPRX_ISCSI_ULIMIT", 0x510, 0 }, { "IscsiUlimit", 6, 26 }, { "ULPRX_ISCSI_TAGMASK", 0x514, 0 }, { "IscsiTagMask", 6, 26 }, { "ULPRX_ISCSI_PSZ", 0x518, 0 }, { "Hpz3", 24, 4 }, { "Hpz2", 16, 4 }, { "Hpz1", 8, 4 }, { "Hpz0", 0, 4 }, { "ULPRX_TDDP_LLIMIT", 0x51c, 0 }, { "TddpLlimit", 6, 26 }, { "ULPRX_TDDP_ULIMIT", 0x520, 0 }, { "TddpUlimit", 6, 26 }, { "ULPRX_TDDP_TAGMASK", 0x524, 0 }, { "TddpTagMask", 6, 26 }, { "ULPRX_TDDP_PSZ", 0x528, 0 }, { "Hpz3", 24, 4 }, { "Hpz2", 16, 4 }, { "Hpz1", 8, 4 }, { "Hpz0", 0, 4 }, { "ULPRX_STAG_LLIMIT", 0x52c, 0 }, { "ULPRX_STAG_ULIMIT", 0x530, 0 }, { "ULPRX_RQ_LLIMIT", 0x534, 0 }, { "ULPRX_RQ_ULIMIT", 0x538, 0 }, { "ULPRX_PBL_LLIMIT", 0x53c, 0 }, { "ULPRX_PBL_ULIMIT", 0x540, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_ulp2_tx_regs[] = { { "ULPTX_CONFIG", 0x580, 0 }, { "CFG_RR_ARB", 0, 1 }, { "ULPTX_INT_ENABLE", 0x584, 0 }, { "Pbl_bound_err_ch1", 1, 1 }, { "Pbl_bound_err_ch0", 0, 1 }, { "ULPTX_INT_CAUSE", 0x588, 0 }, { "Pbl_bound_err_ch1", 1, 1 }, { "Pbl_bound_err_ch0", 0, 1 }, { "ULPTX_TPT_LLIMIT", 0x58c, 0 }, { "ULPTX_TPT_ULIMIT", 0x590, 0 }, { "ULPTX_PBL_LLIMIT", 0x594, 0 }, { "ULPTX_PBL_ULIMIT", 0x598, 0 }, { "ULPTX_CPL_ERR_OFFSET", 0x59c, 0 }, { "ULPTX_CPL_ERR_MASK", 0x5a0, 0 }, { "ULPTX_CPL_ERR_VALUE", 0x5a4, 0 }, { "ULPTX_CPL_PACK_SIZE", 0x5a8, 0 }, { "value", 24, 8 }, { "Ch1Size2", 24, 8 }, { "Ch1Size1", 16, 8 }, { "Ch0Size2", 8, 8 }, { "Ch0Size1", 0, 8 }, { "ULPTX_DMA_WEIGHT", 0x5ac, 0 }, { "D1_WEIGHT", 16, 16 }, { "D0_WEIGHT", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_pm1_rx_regs[] = { { "PM1_RX_CFG", 0x5c0, 0 }, { "PM1_RX_MODE", 0x5c4, 0 }, { "stat_channel", 1, 1 }, { "priority_ch", 0, 1 }, { "PM1_RX_STAT_CONFIG", 0x5c8, 0 }, { "PM1_RX_STAT_COUNT", 0x5cc, 0 }, { "PM1_RX_STAT_MSB", 0x5d0, 0 }, { "PM1_RX_STAT_LSB", 0x5d4, 0 }, { "PM1_RX_INT_ENABLE", 0x5d8, 0 }, { "zero_e_cmd_error", 18, 1 }, { "iespi0_fifo2x_Rx_framing_error", 17, 1 }, { "iespi1_fifo2x_Rx_framing_error", 16, 1 }, { "iespi0_Rx_framing_error", 15, 1 }, { "iespi1_Rx_framing_error", 14, 1 }, { "iespi0_Tx_framing_error", 13, 1 }, { "iespi1_Tx_framing_error", 12, 1 }, { "ocspi0_Rx_framing_error", 11, 1 }, { "ocspi1_Rx_framing_error", 10, 1 }, { "ocspi0_Tx_framing_error", 9, 1 }, { "ocspi1_Tx_framing_error", 8, 1 }, { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 }, { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 }, { "iespi_par_error", 3, 3 }, { "ocspi_par_error", 0, 3 }, { "PM1_RX_INT_CAUSE", 0x5dc, 0 }, { "zero_e_cmd_error", 18, 1 }, { "iespi0_fifo2x_Rx_framing_error", 17, 1 }, { "iespi1_fifo2x_Rx_framing_error", 16, 1 }, { "iespi0_Rx_framing_error", 15, 1 }, { "iespi1_Rx_framing_error", 14, 1 }, { "iespi0_Tx_framing_error", 13, 1 }, { "iespi1_Tx_framing_error", 12, 1 }, { "ocspi0_Rx_framing_error", 11, 1 }, { "ocspi1_Rx_framing_error", 10, 1 }, { "ocspi0_Tx_framing_error", 9, 1 }, { "ocspi1_Tx_framing_error", 8, 1 }, { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 }, { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 }, { "iespi_par_error", 3, 3 }, { "ocspi_par_error", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_pm1_tx_regs[] = { { "PM1_TX_CFG", 0x5e0, 0 }, { "PM1_TX_MODE", 0x5e4, 0 }, { "stat_channel", 1, 1 }, { "priority_ch", 0, 1 }, { "PM1_TX_STAT_CONFIG", 0x5e8, 0 }, { "PM1_TX_STAT_COUNT", 0x5ec, 0 }, { "PM1_TX_STAT_MSB", 0x5f0, 0 }, { "PM1_TX_STAT_LSB", 0x5f4, 0 }, { "PM1_TX_INT_ENABLE", 0x5f8, 0 }, { "zero_c_cmd_error", 18, 1 }, { "icspi0_fifo2x_Rx_framing_error", 17, 1 }, { "icspi1_fifo2x_Rx_framing_error", 16, 1 }, { "icspi0_Rx_framing_error", 15, 1 }, { "icspi1_Rx_framing_error", 14, 1 }, { "icspi0_Tx_framing_error", 13, 1 }, { "icspi1_Tx_framing_error", 12, 1 }, { "oespi0_Rx_framing_error", 11, 1 }, { "oespi1_Rx_framing_error", 10, 1 }, { "oespi0_Tx_framing_error", 9, 1 }, { "oespi1_Tx_framing_error", 8, 1 }, { "oespi0_ofifo2x_Tx_framing_error", 7, 1 }, { "oespi1_ofifo2x_Tx_framing_error", 6, 1 }, { "icspi_par_error", 3, 3 }, { "oespi_par_error", 0, 3 }, { "PM1_TX_INT_CAUSE", 0x5fc, 0 }, { "zero_c_cmd_error", 18, 1 }, { "icspi0_fifo2x_Rx_framing_error", 17, 1 }, { "icspi1_fifo2x_Rx_framing_error", 16, 1 }, { "icspi0_Rx_framing_error", 15, 1 }, { "icspi1_Rx_framing_error", 14, 1 }, { "icspi0_Tx_framing_error", 13, 1 }, { "icspi1_Tx_framing_error", 12, 1 }, { "oespi0_Rx_framing_error", 11, 1 }, { "oespi1_Rx_framing_error", 10, 1 }, { "oespi0_Tx_framing_error", 9, 1 }, { "oespi1_Tx_framing_error", 8, 1 }, { "oespi0_ofifo2x_Tx_framing_error", 7, 1 }, { "oespi1_ofifo2x_Tx_framing_error", 6, 1 }, { "icspi_par_error", 3, 3 }, { "oespi_par_error", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_mps0_regs[] = { { "MPS_CFG", 0x600, 0 }, { "EnForcePkt", 11, 1 }, { "SGETPQid", 8, 3 }, { "TPRxPortSize", 7, 1 }, { "TPTxPort1Size", 6, 1 }, { "TPTxPort0Size", 5, 1 }, { "TPRxPortEn", 4, 1 }, { "TPTxPort1En", 3, 1 }, { "TPTxPort0En", 2, 1 }, { "Port1Active", 1, 1 }, { "Port0Active", 0, 1 }, { "MPS_DRR_CFG1", 0x604, 0 }, { "RldWtTPD1", 11, 11 }, { "RldWtTPD0", 0, 11 }, { "MPS_DRR_CFG2", 0x608, 0 }, { "RldWtTotal", 0, 12 }, { "MPS_MCA_STATUS", 0x60c, 0 }, { "MCAPktCnt", 12, 20 }, { "MCADepth", 0, 12 }, { "MPS_TX0_TP_CNT", 0x610, 0 }, { "TX0TPDisCnt", 24, 8 }, { "TX0TPCnt", 0, 24 }, { "MPS_TX1_TP_CNT", 0x614, 0 }, { "TX1TPDisCnt", 24, 8 }, { "TX1TPCnt", 0, 24 }, { "MPS_RX_TP_CNT", 0x618, 0 }, { "RXTPDisCnt", 24, 8 }, { "RXTPCnt", 0, 24 }, { "MPS_INT_ENABLE", 0x61c, 0 }, { "MCAParErrEnb", 6, 3 }, { "RXTpParErrEnb", 4, 2 }, { "TX1TpParErrEnb", 2, 2 }, { "TX0TpParErrEnb", 0, 2 }, { "MPS_INT_CAUSE", 0x620, 0 }, { "MCAParErr", 6, 3 }, { "RXTpParErr", 4, 2 }, { "TX1TpParErr", 2, 2 }, { "TX0TpParErr", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_cpl_switch_regs[] = { { "CPL_SWITCH_CNTRL", 0x640, 0 }, { "cpl_pkt_tid", 8, 24 }, { "cpu_no_3F_CIM_enable", 3, 1 }, { "switch_table_enable", 2, 1 }, { "sge_enable", 1, 1 }, { "cim_enable", 0, 1 }, { "CPL_SWITCH_TBL_IDX", 0x644, 0 }, { "switch_tbl_idx", 0, 4 }, { "CPL_SWITCH_TBL_DATA", 0x648, 0 }, { "CPL_SWITCH_ZERO_ERROR", 0x64c, 0 }, { "zero_cmd", 0, 8 }, { "CPL_INTR_ENABLE", 0x650, 0 }, { "cim_ovfl_error", 4, 1 }, { "tp_framing_error", 3, 1 }, { "sge_framing_error", 2, 1 }, { "cim_framing_error", 1, 1 }, { "zero_switch_error", 0, 1 }, { "CPL_INTR_CAUSE", 0x654, 0 }, { "cim_ovfl_error", 4, 1 }, { "tp_framing_error", 3, 1 }, { "sge_framing_error", 2, 1 }, { "cim_framing_error", 1, 1 }, { "zero_switch_error", 0, 1 }, { "CPL_MAP_TBL_IDX", 0x658, 0 }, { "cpl_map_tbl_idx", 0, 8 }, { "CPL_MAP_TBL_DATA", 0x65c, 0 }, { "cpl_map_tbl_data", 0, 8 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_smb0_regs[] = { { "SMB_GLOBAL_TIME_CFG", 0x660, 0 }, { "LADbgWrPtr", 24, 8 }, { "LADbgRdPtr", 16, 8 }, { "LADbgEn", 13, 1 }, { "MacroCntCfg", 8, 5 }, { "MicroCntCfg", 0, 8 }, { "SMB_MST_TIMEOUT_CFG", 0x664, 0 }, { "DebugSelH", 28, 4 }, { "DebugSelL", 24, 4 }, { "MstTimeOutCfg", 0, 24 }, { "SMB_MST_CTL_CFG", 0x668, 0 }, { "MstFifoDbg", 31, 1 }, { "MstFifoDbgClr", 30, 1 }, { "MstRxByteCfg", 12, 6 }, { "MstTxByteCfg", 6, 6 }, { "MstReset", 1, 1 }, { "MstCtlEn", 0, 1 }, { "SMB_MST_CTL_STS", 0x66c, 0 }, { "MstRxByteCnt", 12, 6 }, { "MstTxByteCnt", 6, 6 }, { "MstBusySts", 0, 1 }, { "SMB_MST_TX_FIFO_RDWR", 0x670, 0 }, { "SMB_MST_RX_FIFO_RDWR", 0x674, 0 }, { "SMB_SLV_TIMEOUT_CFG", 0x678, 0 }, { "SlvTimeOutCfg", 0, 24 }, { "SMB_SLV_CTL_CFG", 0x67c, 0 }, { "SlvFifoDbg", 31, 1 }, { "SlvFifoDbgClr", 30, 1 }, { "SlvAddrCfg", 4, 7 }, { "SlvAlrtSet", 2, 1 }, { "SlvReset", 1, 1 }, { "SlvCtlEn", 0, 1 }, { "SMB_SLV_CTL_STS", 0x680, 0 }, { "SlvFifoTxCnt", 12, 6 }, { "SlvFifoCnt", 6, 6 }, { "SlvAlrtSts", 2, 1 }, { "SlvBusySts", 0, 1 }, { "SMB_SLV_FIFO_RDWR", 0x684, 0 }, { "SMB_SLV_CMD_FIFO_RDWR", 0x688, 0 }, { "SMB_INT_ENABLE", 0x68c, 0 }, { "SlvTimeOutIntEn", 7, 1 }, { "SlvErrIntEn", 6, 1 }, { "SlvDoneIntEn", 5, 1 }, { "SlvRxRdyIntEn", 4, 1 }, { "MstTimeOutIntEn", 3, 1 }, { "MstNAckIntEn", 2, 1 }, { "MstLostArbIntEn", 1, 1 }, { "MstDoneIntEn", 0, 1 }, { "SMB_INT_CAUSE", 0x690, 0 }, { "SlvTimeOutInt", 7, 1 }, { "SlvErrInt", 6, 1 }, { "SlvDoneInt", 5, 1 }, { "SlvRxRdyInt", 4, 1 }, { "MstTimeOutInt", 3, 1 }, { "MstNAckInt", 2, 1 }, { "MstLostArbInt", 1, 1 }, { "MstDoneInt", 0, 1 }, { "SMB_DEBUG_DATA", 0x694, 0 }, { "DebugDataH", 16, 16 }, { "DebugDataL", 0, 16 }, { "SMB_DEBUG_LA", 0x69c, 0 }, { "DebugLAReqAddr", 0, 10 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_i2cm0_regs[] = { { "I2C_CFG", 0x6a0, 0 }, { "ClkDiv", 0, 12 }, { "I2C_DATA", 0x6a4, 0 }, { "Data", 0, 8 }, { "I2C_OP", 0x6a8, 0 }, { "Busy", 31, 1 }, { "Ack", 30, 1 }, { "Cont", 1, 1 }, { "Op", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_mi1_regs[] = { { "MI1_CFG", 0x6b0, 0 }, { "ClkDiv", 5, 8 }, { "St", 3, 2 }, { "PreEn", 2, 1 }, { "MDIInv", 1, 1 }, { "MDIEn", 0, 1 }, { "MI1_ADDR", 0x6b4, 0 }, { "PhyAddr", 5, 5 }, { "RegAddr", 0, 5 }, { "MI1_DATA", 0x6b8, 0 }, { "Data", 0, 16 }, { "MI1_OP", 0x6bc, 0 }, { "Busy", 31, 1 }, { "Inc", 2, 1 }, { "Op", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_jm1_regs[] = { { "JM_CFG", 0x6c0, 0 }, { "ClkDiv", 2, 8 }, { "TRst", 1, 1 }, { "En", 0, 1 }, { "JM_MODE", 0x6c4, 0 }, { "JM_DATA", 0x6c8, 0 }, { "JM_OP", 0x6cc, 0 }, { "Busy", 31, 1 }, { "Cnt", 0, 5 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_sf1_regs[] = { { "SF_DATA", 0x6d8, 0 }, { "SF_OP", 0x6dc, 0 }, { "Busy", 31, 1 }, { "Cont", 3, 1 }, { "ByteCnt", 1, 2 }, { "Op", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_pl3_regs[] = { { "PL_INT_ENABLE0", 0x6e0, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_CAUSE0", 0x6e4, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_ENABLE1", 0x6e8, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_CAUSE1", 0x6ec, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_RST", 0x6f0, 0 }, { "SWInt1", 3, 1 }, { "SWInt0", 2, 1 }, { "CRstWrm", 1, 1 }, { "CRstWrmMode", 0, 1 }, { "PL_REV", 0x6f4, 0 }, { "Rev", 0, 4 }, { "PL_CLI", 0x6f8, 0 }, { "PL_LCK", 0x6fc, 0 }, { "Lck", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_mc5a_regs[] = { { "MC5_BUF_CONFIG", 0x700, 0 }, { "term300_240", 31, 1 }, { "term150", 30, 1 }, { "term60", 29, 1 }, { "gddriii", 28, 1 }, { "gddrii", 27, 1 }, { "gddri", 26, 1 }, { "read", 25, 1 }, { "imp_set_update", 24, 1 }, { "cal_update", 23, 1 }, { "cal_busy", 22, 1 }, { "cal_error", 21, 1 }, { "sgl_cal_en", 20, 1 }, { "imp_upd_mode", 19, 1 }, { "imp_sel", 18, 1 }, { "man_pu", 15, 3 }, { "man_pd", 12, 3 }, { "cal_pu", 9, 3 }, { "cal_pd", 6, 3 }, { "set_pu", 3, 3 }, { "set_pd", 0, 3 }, { "MC5_DB_CONFIG", 0x704, 0 }, { "TMCfgWrLock", 31, 1 }, { "TMTypeHi", 30, 1 }, { "TMPartSize", 28, 2 }, { "TMType", 26, 2 }, { "TMPartCount", 24, 2 }, { "nLIP", 18, 6 }, { "COMPEN", 17, 1 }, { "BUILD", 16, 1 }, { "FilterEn", 11, 1 }, { "CLIPUpdate", 10, 1 }, { "TM_IO_PDOWN", 9, 1 }, { "SYNMode", 7, 2 }, { "PRTYEN", 6, 1 }, { "MBUSEN", 5, 1 }, { "DBGIEN", 4, 1 }, { "TcmCfgOvr", 3, 1 }, { "TMRDY", 2, 1 }, { "TMRST", 1, 1 }, { "TMMode", 0, 1 }, { "MC5_MISC", 0x708, 0 }, { "LIP_Cmp_Unavailable", 0, 4 }, { "MC5_DB_ROUTING_TABLE_INDEX", 0x70c, 0 }, { "RTINDX", 0, 22 }, { "MC5_DB_FILTER_TABLE", 0x710, 0 }, { "SRINDX", 0, 22 }, { "MC5_DB_SERVER_INDEX", 0x714, 0 }, { "SRINDX", 0, 22 }, { "MC5_DB_LIP_RAM_ADDR", 0x718, 0 }, { "RAMWR", 8, 1 }, { "RAMADDR", 0, 6 }, { "MC5_DB_LIP_RAM_DATA", 0x71c, 0 }, { "MC5_DB_RSP_LATENCY", 0x720, 0 }, { "RDLAT", 16, 5 }, { "LRNLAT", 8, 5 }, { "SRCHLAT", 0, 5 }, { "MC5_DB_PARITY_LATENCY", 0x724, 0 }, { "PARLAT", 0, 4 }, { "MC5_DB_WR_LRN_VERIFY", 0x728, 0 }, { "VWVEREN", 2, 1 }, { "LRNVEREN", 1, 1 }, { "POVEREN", 0, 1 }, { "MC5_DB_PART_ID_INDEX", 0x72c, 0 }, { "IDINDEX", 0, 4 }, { "MC5_DB_RESET_MAX", 0x730, 0 }, { "RSTMAX", 0, 4 }, { "MC5_DB_ACT_CNT", 0x734, 0 }, { "ACTCNT", 0, 20 }, { "MC5_DB_CLIP_MAP", 0x738, 0 }, { "CLIPMapOp", 31, 1 }, { "CLIPMapVal", 16, 6 }, { "CLIPMapAddr", 0, 6 }, { "MC5_DB_INT_ENABLE", 0x740, 0 }, { "MsgSel", 28, 4 }, { "DelActEmpty", 18, 1 }, { "DispQParErr", 17, 1 }, { "ReqQParErr", 16, 1 }, { "UnknownCmd", 15, 1 }, { "SYNCookieOff", 11, 1 }, { "SYNCookieBad", 10, 1 }, { "SYNCookie", 9, 1 }, { "NFASrchFail", 8, 1 }, { "ActRgnFull", 7, 1 }, { "ParityErr", 6, 1 }, { "LIPMiss", 5, 1 }, { "LIP0", 4, 1 }, { "Miss", 3, 1 }, { "RoutingHit", 2, 1 }, { "ActiveHit", 1, 1 }, { "ActiveOutHit", 0, 1 }, { "MC5_DB_INT_CAUSE", 0x744, 0 }, { "DelActEmpty", 18, 1 }, { "DispQParErr", 17, 1 }, { "ReqQParErr", 16, 1 }, { "UnknownCmd", 15, 1 }, { "SYNCookieOff", 11, 1 }, { "SYNCookieBad", 10, 1 }, { "SYNCookie", 9, 1 }, { "NFASrchFail", 8, 1 }, { "ActRgnFull", 7, 1 }, { "ParityErr", 6, 1 }, { "LIPMiss", 5, 1 }, { "LIP0", 4, 1 }, { "Miss", 3, 1 }, { "RoutingHit", 2, 1 }, { "ActiveHit", 1, 1 }, { "ActiveOutHit", 0, 1 }, { "MC5_DB_INT_TID", 0x748, 0 }, { "INTTID", 0, 20 }, { "MC5_DB_INT_PTID", 0x74c, 0 }, { "INTPTID", 0, 20 }, { "MC5_DB_DBGI_CONFIG", 0x774, 0 }, { "WRReqSize", 22, 10 }, { "SADRSel", 4, 1 }, { "CMDMode", 0, 3 }, { "MC5_DB_DBGI_REQ_CMD", 0x778, 0 }, { "MBusCmd", 0, 4 }, { "IDTCmdHi", 11, 3 }, { "IDTCmdLo", 0, 4 }, { "IDTCmd", 0, 20 }, { "LCMDB", 16, 11 }, { "LCMDA", 0, 11 }, { "MC5_DB_DBGI_REQ_ADDR0", 0x77c, 0 }, { "MC5_DB_DBGI_REQ_ADDR1", 0x780, 0 }, { "MC5_DB_DBGI_REQ_ADDR2", 0x784, 0 }, { "DBGIReqAdrHi", 0, 8 }, { "MC5_DB_DBGI_REQ_DATA0", 0x788, 0 }, { "MC5_DB_DBGI_REQ_DATA1", 0x78c, 0 }, { "MC5_DB_DBGI_REQ_DATA2", 0x790, 0 }, { "MC5_DB_DBGI_REQ_DATA3", 0x794, 0 }, { "MC5_DB_DBGI_REQ_DATA4", 0x798, 0 }, { "DBGIReqData4", 0, 16 }, { "MC5_DB_DBGI_REQ_MASK0", 0x79c, 0 }, { "MC5_DB_DBGI_REQ_MASK1", 0x7a0, 0 }, { "MC5_DB_DBGI_REQ_MASK2", 0x7a4, 0 }, { "MC5_DB_DBGI_REQ_MASK3", 0x7a8, 0 }, { "MC5_DB_DBGI_REQ_MASK4", 0x7ac, 0 }, { "DBGIReqMsk4", 0, 16 }, { "MC5_DB_DBGI_RSP_STATUS", 0x7b0, 0 }, { "DBGIRspMsg", 8, 4 }, { "DBGIRspMsgVld", 2, 1 }, { "DBGIRspHit", 1, 1 }, { "DBGIRspValid", 0, 1 }, { "MC5_DB_DBGI_RSP_DATA0", 0x7b4, 0 }, { "MC5_DB_DBGI_RSP_DATA1", 0x7b8, 0 }, { "MC5_DB_DBGI_RSP_DATA2", 0x7bc, 0 }, { "MC5_DB_DBGI_RSP_DATA3", 0x7c0, 0 }, { "MC5_DB_DBGI_RSP_DATA4", 0x7c4, 0 }, { "DBGIRspData3", 0, 16 }, { "MC5_DB_DBGI_RSP_LAST_CMD", 0x7c8, 0 }, { "LastCmdB", 16, 11 }, { "LastCmdA", 0, 11 }, { "MC5_DB_POPEN_DATA_WR_CMD", 0x7cc, 0 }, { "PO_DWR", 0, 20 }, { "MC5_DB_POPEN_MASK_WR_CMD", 0x7d0, 0 }, { "PO_MWR", 0, 20 }, { "MC5_DB_AOPEN_SRCH_CMD", 0x7d4, 0 }, { "AO_SRCH", 0, 20 }, { "MC5_DB_AOPEN_LRN_CMD", 0x7d8, 0 }, { "AO_LRN", 0, 20 }, { "MC5_DB_SYN_SRCH_CMD", 0x7dc, 0 }, { "SYN_SRCH", 0, 20 }, { "MC5_DB_SYN_LRN_CMD", 0x7e0, 0 }, { "SYN_LRN", 0, 20 }, { "MC5_DB_ACK_SRCH_CMD", 0x7e4, 0 }, { "ACK_SRCH", 0, 20 }, { "MC5_DB_ACK_LRN_CMD", 0x7e8, 0 }, { "ACK_LRN", 0, 20 }, { "MC5_DB_ILOOKUP_CMD", 0x7ec, 0 }, { "I_SRCH", 0, 20 }, { "MC5_DB_ELOOKUP_CMD", 0x7f0, 0 }, { "E_SRCH", 0, 20 }, { "MC5_DB_DATA_WRITE_CMD", 0x7f4, 0 }, { "Write", 0, 20 }, { "MC5_DB_DATA_READ_CMD", 0x7f8, 0 }, { "ReadCmd", 0, 20 }, { "MC5_DB_MASK_WRITE_CMD", 0x7fc, 0 }, { "MaskWr", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_xgmac0_0_regs[] = { { "XGM_TX_CTRL", 0x800, 0 }, { "SendPause", 2, 1 }, { "SendZeroPause", 1, 1 }, { "TxEn", 0, 1 }, { "XGM_TX_CFG", 0x804, 0 }, { "CfgClkSpeed", 2, 3 }, { "StretchMode", 1, 1 }, { "TxPauseEn", 0, 1 }, { "XGM_TX_PAUSE_QUANTA", 0x808, 0 }, { "TxPauseQuanta", 0, 16 }, { "XGM_RX_CTRL", 0x80c, 0 }, { "RxEn", 0, 1 }, { "XGM_RX_CFG", 0x810, 0 }, { "Con802_3Preamble", 12, 1 }, { "EnNon802_3Preamble", 11, 1 }, { "CopyPreamble", 10, 1 }, { "DisPauseFrames", 9, 1 }, { "En1536BFrames", 8, 1 }, { "EnJumbo", 7, 1 }, { "RmFCS", 6, 1 }, { "DisNonVlan", 5, 1 }, { "EnExtMatch", 4, 1 }, { "EnHashUcast", 3, 1 }, { "EnHashMcast", 2, 1 }, { "DisBCast", 1, 1 }, { "CopyAllFrames", 0, 1 }, { "XGM_RX_HASH_LOW", 0x814, 0 }, { "XGM_RX_HASH_HIGH", 0x818, 0 }, { "XGM_RX_EXACT_MATCH_LOW_1", 0x81c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_1", 0x820, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_2", 0x824, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_2", 0x828, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_3", 0x82c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_3", 0x830, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_4", 0x834, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_4", 0x838, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_5", 0x83c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_5", 0x840, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_6", 0x844, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_6", 0x848, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_7", 0x84c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_7", 0x850, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_8", 0x854, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_8", 0x858, 0 }, { "address_high", 0, 16 }, { "XGM_RX_TYPE_MATCH_1", 0x85c, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_2", 0x860, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_3", 0x864, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_4", 0x868, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_INT_STATUS", 0x86c, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_MASK", 0x870, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_ENABLE", 0x874, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_DISABLE", 0x878, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_TX_PAUSE_TIMER", 0x87c, 0 }, { "CurPauseTimer", 0, 16 }, { "XGM_STAT_CTRL", 0x880, 0 }, { "ReadSnpShot", 4, 1 }, { "TakeSnpShot", 3, 1 }, { "ClrStats", 2, 1 }, { "IncrStats", 1, 1 }, { "EnTestModeWr", 0, 1 }, { "XGM_RXFIFO_CFG", 0x884, 0 }, { "RxFIFOPauseHWM", 17, 12 }, { "RxFIFOPauseLWM", 5, 12 }, { "ForcedPause", 4, 1 }, { "ExternLoopback", 3, 1 }, { "RxByteSwap", 2, 1 }, { "RxStrFrwrd", 1, 1 }, { "DisErrFrames", 0, 1 }, { "XGM_TXFIFO_CFG", 0x888, 0 }, { "EnDropPkt", 21, 1 }, { "TxIPG", 13, 8 }, { "TxFIFOThresh", 4, 9 }, { "InternLoopback", 3, 1 }, { "TxByteSwap", 2, 1 }, { "DisCRC", 1, 1 }, { "DisPreAmble", 0, 1 }, { "XGM_SLOW_TIMER", 0x88c, 0 }, { "PauseSlowTimerEn", 31, 1 }, { "PauseSlowTimer", 0, 20 }, { "XGM_PAUSE_TIMER", 0x890, 0 }, { "PauseTimer", 0, 20 }, { "XGM_XAUI_PCS_TEST", 0x894, 0 }, { "TestPattern", 1, 2 }, { "EnTest", 0, 1 }, { "XGM_RGMII_CTRL", 0x898, 0 }, { "PhAlignFIFOThresh", 1, 2 }, { "TxClk90Shift", 0, 1 }, { "XGM_RGMII_IMP", 0x89c, 0 }, { "CalReset", 8, 1 }, { "CalUpdate", 7, 1 }, { "ImpSetUpdate", 6, 1 }, { "RGMIIImpPD", 3, 3 }, { "RGMIIImpPU", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE", 0x8a8, 0 }, { "RxMaxPktSize", 0, 14 }, { "XGM_RESET_CTRL", 0x8ac, 0 }, { "XG2G_Reset_", 3, 1 }, { "RGMII_Reset_", 2, 1 }, { "PCS_Reset_", 1, 1 }, { "MAC_Reset_", 0, 1 }, { "XGM_XAUI1G_CTRL", 0x8b0, 0 }, { "XAUI1GLinkId", 0, 2 }, { "XGM_SERDES_LANE_CTRL", 0x8b4, 0 }, { "LaneReversal", 8, 1 }, { "TxPolarity", 4, 4 }, { "RxPolarity", 0, 4 }, { "XGM_PORT_CFG", 0x8b8, 0 }, { "SafeSpeedChange", 4, 1 }, { "ClkDivReset_", 3, 1 }, { "PortSpeed", 1, 2 }, { "EnRGMII", 0, 1 }, { "XGM_EPIO_DATA0", 0x8c0, 0 }, { "XGM_EPIO_DATA1", 0x8c4, 0 }, { "XGM_EPIO_DATA2", 0x8c8, 0 }, { "XGM_EPIO_DATA3", 0x8cc, 0 }, { "XGM_EPIO_OP", 0x8d0, 0 }, { "PIO_Ready", 31, 1 }, { "PIO_WrRd", 24, 1 }, { "PIO_Address", 0, 8 }, { "XGM_INT_ENABLE", 0x8d4, 0 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_INT_CAUSE", 0x8d8, 0 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_XAUI_ACT_CTRL", 0x8dc, 0 }, { "TxEn", 1, 1 }, { "RxEn", 0, 1 }, { "XGM_SERDES_CTRL0", 0x8e0, 0 }, { "IntSerLPBK3", 27, 1 }, { "IntSerLPBK2", 26, 1 }, { "IntSerLPBK1", 25, 1 }, { "IntSerLPBK0", 24, 1 }, { "Reset3", 23, 1 }, { "Reset2", 22, 1 }, { "Reset1", 21, 1 }, { "Reset0", 20, 1 }, { "Pwrdn3", 19, 1 }, { "Pwrdn2", 18, 1 }, { "Pwrdn1", 17, 1 }, { "Pwrdn0", 16, 1 }, { "ResetPLL23", 15, 1 }, { "ResetPLL01", 14, 1 }, { "PW23", 12, 2 }, { "PW01", 10, 2 }, { "Deq", 6, 4 }, { "Dtx", 2, 4 }, { "LoDrv", 1, 1 }, { "HiDrv", 0, 1 }, { "XGM_SERDES_CTRL1", 0x8e4, 0 }, { "FmOffset3", 19, 5 }, { "FmOffsetEn3", 18, 1 }, { "FmOffset2", 13, 5 }, { "FmOffsetEn2", 12, 1 }, { "FmOffset1", 7, 5 }, { "FmOffsetEn1", 6, 1 }, { "FmOffset0", 1, 5 }, { "FmOffsetEn0", 0, 1 }, { "XGM_SERDES_CTRL2", 0x8e8, 0 }, { "DnIn3", 11, 1 }, { "UpIn3", 10, 1 }, { "RxSlave3", 9, 1 }, { "DnIn2", 8, 1 }, { "UpIn2", 7, 1 }, { "RxSlave2", 6, 1 }, { "DnIn1", 5, 1 }, { "UpIn1", 4, 1 }, { "RxSlave1", 3, 1 }, { "DnIn0", 2, 1 }, { "UpIn0", 1, 1 }, { "RxSlave0", 0, 1 }, { "XGM_SERDES_CTRL3", 0x8ec, 0 }, { "ExtBISTChkErrClr3", 31, 1 }, { "ExtBISTChkEn3", 30, 1 }, { "ExtBISTGenEn3", 29, 1 }, { "ExtBISTPat3", 26, 3 }, { "ExtParReset3", 25, 1 }, { "ExtParLPBK3", 24, 1 }, { "ExtBISTChkErrClr2", 23, 1 }, { "ExtBISTChkEn2", 22, 1 }, { "ExtBISTGenEn2", 21, 1 }, { "ExtBISTPat2", 18, 3 }, { "ExtParReset2", 17, 1 }, { "ExtParLPBK2", 16, 1 }, { "ExtBISTChkErrClr1", 15, 1 }, { "ExtBISTChkEn1", 14, 1 }, { "ExtBISTGenEn1", 13, 1 }, { "ExtBISTPat1", 10, 3 }, { "ExtParReset1", 9, 1 }, { "ExtParLPBK1", 8, 1 }, { "ExtBISTChkErrClr0", 7, 1 }, { "ExtBISTChkEn0", 6, 1 }, { "ExtBISTGenEn0", 5, 1 }, { "ExtBISTPat0", 2, 3 }, { "ExtParReset0", 1, 1 }, { "ExtParLPBK0", 0, 1 }, { "XGM_SERDES_STAT0", 0x8f0, 0 }, { "ExtBISTChkErrCnt0", 4, 24 }, { "ExtBISTChkFmd0", 3, 1 }, { "LowSig0", 0, 1 }, { "XGM_SERDES_STAT1", 0x8f4, 0 }, { "ExtBISTChkErrCnt1", 4, 24 }, { "ExtBISTChkFmd1", 3, 1 }, { "LowSig1", 0, 1 }, { "XGM_SERDES_STAT2", 0x8f8, 0 }, { "ExtBISTChkErrCnt2", 4, 24 }, { "ExtBISTChkFmd2", 3, 1 }, { "LowSig2", 0, 1 }, { "XGM_SERDES_STAT3", 0x8fc, 0 }, { "ExtBISTChkErrCnt3", 4, 24 }, { "ExtBISTChkFmd3", 3, 1 }, { "LowSig3", 0, 1 }, { "XGM_STAT_TX_BYTE_LOW", 0x900, 0 }, { "XGM_STAT_TX_BYTE_HIGH", 0x904, 0 }, { "TxBytes_high", 0, 13 }, { "XGM_STAT_TX_FRAME_LOW", 0x908, 0 }, { "XGM_STAT_TX_FRAME_HIGH", 0x90c, 0 }, { "TxFrames_high", 0, 4 }, { "XGM_STAT_TX_BCAST", 0x910, 0 }, { "XGM_STAT_TX_MCAST", 0x914, 0 }, { "XGM_STAT_TX_PAUSE", 0x918, 0 }, { "XGM_STAT_TX_64B_FRAMES", 0x91c, 0 }, { "XGM_STAT_TX_65_127B_FRAMES", 0x920, 0 }, { "XGM_STAT_TX_128_255B_FRAMES", 0x924, 0 }, { "XGM_STAT_TX_256_511B_FRAMES", 0x928, 0 }, { "XGM_STAT_TX_512_1023B_FRAMES", 0x92c, 0 }, { "XGM_STAT_TX_1024_1518B_FRAMES", 0x930, 0 }, { "XGM_STAT_TX_1519_MAXB_FRAMES", 0x934, 0 }, { "XGM_STAT_TX_ERR_FRAMES", 0x938, 0 }, { "XGM_STAT_RX_BYTES_LOW", 0x93c, 0 }, { "XGM_STAT_RX_BYTES_HIGH", 0x940, 0 }, { "RxBytes_high", 0, 13 }, { "XGM_STAT_RX_FRAMES_LOW", 0x944, 0 }, { "XGM_STAT_RX_FRAMES_HIGH", 0x948, 0 }, { "RxFrames_high", 0, 4 }, { "XGM_STAT_RX_BCAST_FRAMES", 0x94c, 0 }, { "XGM_STAT_RX_MCAST_FRAMES", 0x950, 0 }, { "XGM_STAT_RX_PAUSE_FRAMES", 0x954, 0 }, { "RxPauseFrames", 0, 16 }, { "XGM_STAT_RX_64B_FRAMES", 0x958, 0 }, { "XGM_STAT_RX_65_127B_FRAMES", 0x95c, 0 }, { "XGM_STAT_RX_128_255B_FRAMES", 0x960, 0 }, { "XGM_STAT_RX_256_511B_FRAMES", 0x964, 0 }, { "XGM_STAT_RX_512_1023B_FRAMES", 0x968, 0 }, { "XGM_STAT_RX_1024_1518B_FRAMES", 0x96c, 0 }, { "XGM_STAT_RX_1519_MAXB_FRAMES", 0x970, 0 }, { "XGM_STAT_RX_SHORT_FRAMES", 0x974, 0 }, { "RxShortFrames", 0, 16 }, { "XGM_STAT_RX_OVERSIZE_FRAMES", 0x978, 0 }, { "RxOversizeFrames", 0, 16 }, { "XGM_STAT_RX_JABBER_FRAMES", 0x97c, 0 }, { "RxJabberFrames", 0, 16 }, { "XGM_STAT_RX_CRC_ERR_FRAMES", 0x980, 0 }, { "RxCRCErrFrames", 0, 16 }, { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0x984, 0 }, { "RxLengthErrFrames", 0, 16 }, { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0x988, 0 }, { "RxSymCodeErrFrames", 0, 16 }, { "XGM_XAUI_PCS_ERR", 0x998, 0 }, { "PCS_SyncStatus", 5, 4 }, { "PCS_CTCFIFOErr", 1, 4 }, { "PCS_NotAligned", 0, 1 }, { "XGM_RGMII_STATUS", 0x99c, 0 }, { "GMIIDuplex", 3, 1 }, { "GMIISpeed", 1, 2 }, { "GMIILinkStatus", 0, 1 }, { "XGM_WOL_STATUS", 0x9a0, 0 }, { "PatDetected", 31, 1 }, { "MatchedFilter", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0x9a4, 0 }, { "XGM_TX_SPI4_SOP_EOP_CNT", 0x9a8, 0 }, { "TxSPI4SopCnt", 16, 16 }, { "TxSPI4EopCnt", 0, 16 }, { "XGM_RX_SPI4_SOP_EOP_CNT", 0x9ac, 0 }, { "RxSPI4SopCnt", 16, 16 }, { "RxSPI4EopCnt", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3b_xgmac0_1_regs[] = { { "XGM_TX_CTRL", 0xa00, 0 }, { "SendPause", 2, 1 }, { "SendZeroPause", 1, 1 }, { "TxEn", 0, 1 }, { "XGM_TX_CFG", 0xa04, 0 }, { "CfgClkSpeed", 2, 3 }, { "StretchMode", 1, 1 }, { "TxPauseEn", 0, 1 }, { "XGM_TX_PAUSE_QUANTA", 0xa08, 0 }, { "TxPauseQuanta", 0, 16 }, { "XGM_RX_CTRL", 0xa0c, 0 }, { "RxEn", 0, 1 }, { "XGM_RX_CFG", 0xa10, 0 }, { "Con802_3Preamble", 12, 1 }, { "EnNon802_3Preamble", 11, 1 }, { "CopyPreamble", 10, 1 }, { "DisPauseFrames", 9, 1 }, { "En1536BFrames", 8, 1 }, { "EnJumbo", 7, 1 }, { "RmFCS", 6, 1 }, { "DisNonVlan", 5, 1 }, { "EnExtMatch", 4, 1 }, { "EnHashUcast", 3, 1 }, { "EnHashMcast", 2, 1 }, { "DisBCast", 1, 1 }, { "CopyAllFrames", 0, 1 }, { "XGM_RX_HASH_LOW", 0xa14, 0 }, { "XGM_RX_HASH_HIGH", 0xa18, 0 }, { "XGM_RX_EXACT_MATCH_LOW_1", 0xa1c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_1", 0xa20, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_2", 0xa24, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_2", 0xa28, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_3", 0xa2c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_3", 0xa30, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_4", 0xa34, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_4", 0xa38, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_5", 0xa3c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_5", 0xa40, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_6", 0xa44, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_6", 0xa48, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_7", 0xa4c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_7", 0xa50, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_8", 0xa54, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_8", 0xa58, 0 }, { "address_high", 0, 16 }, { "XGM_RX_TYPE_MATCH_1", 0xa5c, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_2", 0xa60, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_3", 0xa64, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_4", 0xa68, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_INT_STATUS", 0xa6c, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_MASK", 0xa70, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_ENABLE", 0xa74, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_DISABLE", 0xa78, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_TX_PAUSE_TIMER", 0xa7c, 0 }, { "CurPauseTimer", 0, 16 }, { "XGM_STAT_CTRL", 0xa80, 0 }, { "ReadSnpShot", 4, 1 }, { "TakeSnpShot", 3, 1 }, { "ClrStats", 2, 1 }, { "IncrStats", 1, 1 }, { "EnTestModeWr", 0, 1 }, { "XGM_RXFIFO_CFG", 0xa84, 0 }, { "RxFIFOPauseHWM", 17, 12 }, { "RxFIFOPauseLWM", 5, 12 }, { "ForcedPause", 4, 1 }, { "ExternLoopback", 3, 1 }, { "RxByteSwap", 2, 1 }, { "RxStrFrwrd", 1, 1 }, { "DisErrFrames", 0, 1 }, { "XGM_TXFIFO_CFG", 0xa88, 0 }, { "EnDropPkt", 21, 1 }, { "TxIPG", 13, 8 }, { "TxFIFOThresh", 4, 9 }, { "InternLoopback", 3, 1 }, { "TxByteSwap", 2, 1 }, { "DisCRC", 1, 1 }, { "DisPreAmble", 0, 1 }, { "XGM_SLOW_TIMER", 0xa8c, 0 }, { "PauseSlowTimerEn", 31, 1 }, { "PauseSlowTimer", 0, 20 }, { "XGM_PAUSE_TIMER", 0xa90, 0 }, { "PauseTimer", 0, 20 }, { "XGM_XAUI_PCS_TEST", 0xa94, 0 }, { "TestPattern", 1, 2 }, { "EnTest", 0, 1 }, { "XGM_RGMII_CTRL", 0xa98, 0 }, { "PhAlignFIFOThresh", 1, 2 }, { "TxClk90Shift", 0, 1 }, { "XGM_RGMII_IMP", 0xa9c, 0 }, { "CalReset", 8, 1 }, { "CalUpdate", 7, 1 }, { "ImpSetUpdate", 6, 1 }, { "RGMIIImpPD", 3, 3 }, { "RGMIIImpPU", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE", 0xaa8, 0 }, { "RxMaxPktSize", 0, 14 }, { "XGM_RESET_CTRL", 0xaac, 0 }, { "XG2G_Reset_", 3, 1 }, { "RGMII_Reset_", 2, 1 }, { "PCS_Reset_", 1, 1 }, { "MAC_Reset_", 0, 1 }, { "XGM_XAUI1G_CTRL", 0xab0, 0 }, { "XAUI1GLinkId", 0, 2 }, { "XGM_SERDES_LANE_CTRL", 0xab4, 0 }, { "LaneReversal", 8, 1 }, { "TxPolarity", 4, 4 }, { "RxPolarity", 0, 4 }, { "XGM_PORT_CFG", 0xab8, 0 }, { "SafeSpeedChange", 4, 1 }, { "ClkDivReset_", 3, 1 }, { "PortSpeed", 1, 2 }, { "EnRGMII", 0, 1 }, { "XGM_EPIO_DATA0", 0xac0, 0 }, { "XGM_EPIO_DATA1", 0xac4, 0 }, { "XGM_EPIO_DATA2", 0xac8, 0 }, { "XGM_EPIO_DATA3", 0xacc, 0 }, { "XGM_EPIO_OP", 0xad0, 0 }, { "PIO_Ready", 31, 1 }, { "PIO_WrRd", 24, 1 }, { "PIO_Address", 0, 8 }, { "XGM_INT_ENABLE", 0xad4, 0 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_INT_CAUSE", 0xad8, 0 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_XAUI_ACT_CTRL", 0xadc, 0 }, { "TxEn", 1, 1 }, { "RxEn", 0, 1 }, { "XGM_SERDES_CTRL0", 0xae0, 0 }, { "IntSerLPBK3", 27, 1 }, { "IntSerLPBK2", 26, 1 }, { "IntSerLPBK1", 25, 1 }, { "IntSerLPBK0", 24, 1 }, { "Reset3", 23, 1 }, { "Reset2", 22, 1 }, { "Reset1", 21, 1 }, { "Reset0", 20, 1 }, { "Pwrdn3", 19, 1 }, { "Pwrdn2", 18, 1 }, { "Pwrdn1", 17, 1 }, { "Pwrdn0", 16, 1 }, { "ResetPLL23", 15, 1 }, { "ResetPLL01", 14, 1 }, { "PW23", 12, 2 }, { "PW01", 10, 2 }, { "Deq", 6, 4 }, { "Dtx", 2, 4 }, { "LoDrv", 1, 1 }, { "HiDrv", 0, 1 }, { "XGM_SERDES_CTRL1", 0xae4, 0 }, { "FmOffset3", 19, 5 }, { "FmOffsetEn3", 18, 1 }, { "FmOffset2", 13, 5 }, { "FmOffsetEn2", 12, 1 }, { "FmOffset1", 7, 5 }, { "FmOffsetEn1", 6, 1 }, { "FmOffset0", 1, 5 }, { "FmOffsetEn0", 0, 1 }, { "XGM_SERDES_CTRL2", 0xae8, 0 }, { "DnIn3", 11, 1 }, { "UpIn3", 10, 1 }, { "RxSlave3", 9, 1 }, { "DnIn2", 8, 1 }, { "UpIn2", 7, 1 }, { "RxSlave2", 6, 1 }, { "DnIn1", 5, 1 }, { "UpIn1", 4, 1 }, { "RxSlave1", 3, 1 }, { "DnIn0", 2, 1 }, { "UpIn0", 1, 1 }, { "RxSlave0", 0, 1 }, { "XGM_SERDES_CTRL3", 0xaec, 0 }, { "ExtBISTChkErrClr3", 31, 1 }, { "ExtBISTChkEn3", 30, 1 }, { "ExtBISTGenEn3", 29, 1 }, { "ExtBISTPat3", 26, 3 }, { "ExtParReset3", 25, 1 }, { "ExtParLPBK3", 24, 1 }, { "ExtBISTChkErrClr2", 23, 1 }, { "ExtBISTChkEn2", 22, 1 }, { "ExtBISTGenEn2", 21, 1 }, { "ExtBISTPat2", 18, 3 }, { "ExtParReset2", 17, 1 }, { "ExtParLPBK2", 16, 1 }, { "ExtBISTChkErrClr1", 15, 1 }, { "ExtBISTChkEn1", 14, 1 }, { "ExtBISTGenEn1", 13, 1 }, { "ExtBISTPat1", 10, 3 }, { "ExtParReset1", 9, 1 }, { "ExtParLPBK1", 8, 1 }, { "ExtBISTChkErrClr0", 7, 1 }, { "ExtBISTChkEn0", 6, 1 }, { "ExtBISTGenEn0", 5, 1 }, { "ExtBISTPat0", 2, 3 }, { "ExtParReset0", 1, 1 }, { "ExtParLPBK0", 0, 1 }, { "XGM_SERDES_STAT0", 0xaf0, 0 }, { "ExtBISTChkErrCnt0", 4, 24 }, { "ExtBISTChkFmd0", 3, 1 }, { "LowSig0", 0, 1 }, { "XGM_SERDES_STAT1", 0xaf4, 0 }, { "ExtBISTChkErrCnt1", 4, 24 }, { "ExtBISTChkFmd1", 3, 1 }, { "LowSig1", 0, 1 }, { "XGM_SERDES_STAT2", 0xaf8, 0 }, { "ExtBISTChkErrCnt2", 4, 24 }, { "ExtBISTChkFmd2", 3, 1 }, { "LowSig2", 0, 1 }, { "XGM_SERDES_STAT3", 0xafc, 0 }, { "ExtBISTChkErrCnt3", 4, 24 }, { "ExtBISTChkFmd3", 3, 1 }, { "LowSig3", 0, 1 }, { "XGM_STAT_TX_BYTE_LOW", 0xb00, 0 }, { "XGM_STAT_TX_BYTE_HIGH", 0xb04, 0 }, { "TxBytes_high", 0, 13 }, { "XGM_STAT_TX_FRAME_LOW", 0xb08, 0 }, { "XGM_STAT_TX_FRAME_HIGH", 0xb0c, 0 }, { "TxFrames_high", 0, 4 }, { "XGM_STAT_TX_BCAST", 0xb10, 0 }, { "XGM_STAT_TX_MCAST", 0xb14, 0 }, { "XGM_STAT_TX_PAUSE", 0xb18, 0 }, { "XGM_STAT_TX_64B_FRAMES", 0xb1c, 0 }, { "XGM_STAT_TX_65_127B_FRAMES", 0xb20, 0 }, { "XGM_STAT_TX_128_255B_FRAMES", 0xb24, 0 }, { "XGM_STAT_TX_256_511B_FRAMES", 0xb28, 0 }, { "XGM_STAT_TX_512_1023B_FRAMES", 0xb2c, 0 }, { "XGM_STAT_TX_1024_1518B_FRAMES", 0xb30, 0 }, { "XGM_STAT_TX_1519_MAXB_FRAMES", 0xb34, 0 }, { "XGM_STAT_TX_ERR_FRAMES", 0xb38, 0 }, { "XGM_STAT_RX_BYTES_LOW", 0xb3c, 0 }, { "XGM_STAT_RX_BYTES_HIGH", 0xb40, 0 }, { "RxBytes_high", 0, 13 }, { "XGM_STAT_RX_FRAMES_LOW", 0xb44, 0 }, { "XGM_STAT_RX_FRAMES_HIGH", 0xb48, 0 }, { "RxFrames_high", 0, 4 }, { "XGM_STAT_RX_BCAST_FRAMES", 0xb4c, 0 }, { "XGM_STAT_RX_MCAST_FRAMES", 0xb50, 0 }, { "XGM_STAT_RX_PAUSE_FRAMES", 0xb54, 0 }, { "RxPauseFrames", 0, 16 }, { "XGM_STAT_RX_64B_FRAMES", 0xb58, 0 }, { "XGM_STAT_RX_65_127B_FRAMES", 0xb5c, 0 }, { "XGM_STAT_RX_128_255B_FRAMES", 0xb60, 0 }, { "XGM_STAT_RX_256_511B_FRAMES", 0xb64, 0 }, { "XGM_STAT_RX_512_1023B_FRAMES", 0xb68, 0 }, { "XGM_STAT_RX_1024_1518B_FRAMES", 0xb6c, 0 }, { "XGM_STAT_RX_1519_MAXB_FRAMES", 0xb70, 0 }, { "XGM_STAT_RX_SHORT_FRAMES", 0xb74, 0 }, { "RxShortFrames", 0, 16 }, { "XGM_STAT_RX_OVERSIZE_FRAMES", 0xb78, 0 }, { "RxOversizeFrames", 0, 16 }, { "XGM_STAT_RX_JABBER_FRAMES", 0xb7c, 0 }, { "RxJabberFrames", 0, 16 }, { "XGM_STAT_RX_CRC_ERR_FRAMES", 0xb80, 0 }, { "RxCRCErrFrames", 0, 16 }, { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0xb84, 0 }, { "RxLengthErrFrames", 0, 16 }, { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0xb88, 0 }, { "RxSymCodeErrFrames", 0, 16 }, { "XGM_XAUI_PCS_ERR", 0xb98, 0 }, { "PCS_SyncStatus", 5, 4 }, { "PCS_CTCFIFOErr", 1, 4 }, { "PCS_NotAligned", 0, 1 }, { "XGM_RGMII_STATUS", 0xb9c, 0 }, { "GMIIDuplex", 3, 1 }, { "GMIISpeed", 1, 2 }, { "GMIILinkStatus", 0, 1 }, { "XGM_WOL_STATUS", 0xba0, 0 }, { "PatDetected", 31, 1 }, { "MatchedFilter", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0xba4, 0 }, { "XGM_TX_SPI4_SOP_EOP_CNT", 0xba8, 0 }, { "TxSPI4SopCnt", 16, 16 }, { "TxSPI4EopCnt", 0, 16 }, { "XGM_RX_SPI4_SOP_EOP_CNT", 0xbac, 0 }, { "RxSPI4SopCnt", 16, 16 }, { "RxSPI4EopCnt", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; Index: projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3c.c =================================================================== --- projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3c.c (revision 204271) +++ projects/ppc64/usr.sbin/cxgbtool/reg_defs_t3c.c (revision 204272) @@ -1,3119 +1,3119 @@ /* * $FreeBSD$ */ /* This file is automatically generated --- do not edit */ struct reg_info t3c_sge3_regs[] = { { "SG_CONTROL", 0x0, 0 }, { "CongMode", 29, 1 }, { "TnlFLMode", 28, 1 }, { "FatlPerrEn", 27, 1 }, { "UrgTnl", 26, 1 }, { "NewNotify", 25, 1 }, { "AvoidCqOvfl", 24, 1 }, { "OptOneIntMultQ", 23, 1 }, { "CQCrdtCtrl", 22, 1 }, { "EgrEnUpBp", 21, 1 }, { "DropPkt", 20, 1 }, { "EgrGenCtrl", 19, 1 }, { "UserSpaceSize", 14, 5 }, { "HostPageSize", 11, 3 }, { "PCIRelax", 10, 1 }, { "FLMode", 9, 1 }, { "PktShift", 6, 3 }, { "OneIntMultQ", 5, 1 }, { "FLPickAvail", 4, 1 }, { "BigEndianEgress", 3, 1 }, { "BigEndianIngress", 2, 1 }, { "IscsiCoalescing", 1, 1 }, { "GlobalEnable", 0, 1 }, { "SG_KDOORBELL", 0x4, 0 }, { "SelEgrCntx", 31, 1 }, { "EgrCntx", 0, 16 }, { "SG_GTS", 0x8, 0 }, { "RspQ", 29, 3 }, { "NewTimer", 16, 13 }, { "NewIndex", 0, 16 }, { "SG_CONTEXT_CMD", 0xc, 0 }, { "Opcode", 28, 4 }, { "Busy", 27, 1 }, { "CQ_credit", 20, 7 }, { "CQ", 19, 1 }, { "RspQ", 18, 1 }, { "Egress", 17, 1 }, { "FreeList", 16, 1 }, { "Context", 0, 16 }, { "SG_CONTEXT_DATA0", 0x10, 0 }, { "SG_CONTEXT_DATA1", 0x14, 0 }, { "SG_CONTEXT_DATA2", 0x18, 0 }, { "SG_CONTEXT_DATA3", 0x1c, 0 }, { "SG_CONTEXT_MASK0", 0x20, 0 }, { "SG_CONTEXT_MASK1", 0x24, 0 }, { "SG_CONTEXT_MASK2", 0x28, 0 }, { "SG_CONTEXT_MASK3", 0x2c, 0 }, { "SG_RSPQ_CREDIT_RETURN", 0x30, 0 }, { "RspQ", 29, 3 }, { "Data", 0, 16 }, { "SG_DATA_INTR", 0x34, 0 }, { "ErrIntr", 31, 1 }, { "DataIntr", 0, 8 }, { "SG_HI_DRB_HI_THRSH", 0x38, 0 }, { "HiDrbHiThrsh", 0, 10 }, { "SG_HI_DRB_LO_THRSH", 0x3c, 0 }, { "HiDrbLoThrsh", 0, 10 }, { "SG_LO_DRB_HI_THRSH", 0x40, 0 }, { "LoDrbHiThrsh", 0, 10 }, { "SG_LO_DRB_LO_THRSH", 0x44, 0 }, { "LoDrbLoThrsh", 0, 10 }, { "SG_ONE_INT_MULT_Q_COALESCING_TIMER", 0x48, 0 }, { "SG_RSPQ_FL_STATUS", 0x4c, 0 }, { "RspQ0Starved", 0, 1 }, { "RspQ1Starved", 1, 1 }, { "RspQ2Starved", 2, 1 }, { "RspQ3Starved", 3, 1 }, { "RspQ4Starved", 4, 1 }, { "RspQ5Starved", 5, 1 }, { "RspQ6Starved", 6, 1 }, { "RspQ7Starved", 7, 1 }, { "RspQ0Disabled", 8, 1 }, { "RspQ1Disabled", 9, 1 }, { "RspQ2Disabled", 10, 1 }, { "RspQ3Disabled", 11, 1 }, { "RspQ4Disabled", 12, 1 }, { "RspQ5Disabled", 13, 1 }, { "RspQ6Disabled", 14, 1 }, { "RspQ7Disabled", 15, 1 }, { "FL0Empty", 16, 1 }, { "FL1Empty", 17, 1 }, { "FL2Empty", 18, 1 }, { "FL3Empty", 19, 1 }, { "FL4Empty", 20, 1 }, { "FL5Empty", 21, 1 }, { "FL6Empty", 22, 1 }, { "FL7Empty", 23, 1 }, { "FL8Empty", 24, 1 }, { "FL9Empty", 25, 1 }, { "FL10Empty", 26, 1 }, { "FL11Empty", 27, 1 }, { "FL12Empty", 28, 1 }, { "FL13Empty", 29, 1 }, { "FL14Empty", 30, 1 }, { "FL15Empty", 31, 1 }, { "SG_EGR_PRI_CNT", 0x50, 0 }, { "EgrErrOpCode", 24, 8 }, { "EgrHiOpCode", 16, 8 }, { "EgrLoOpCode", 8, 8 }, { "EgrPriCnt", 0, 5 }, { "SG_EGR_RCQ_DRB_THRSH", 0x54, 0 }, { "HiRcqDrbThrsh", 16, 11 }, { "LoRcqDrbThrsh", 0, 11 }, { "SG_EGR_CNTX_BADDR", 0x58, 0 }, { "EgrCntxBAddr", 5, 27 }, { "SG_INT_CAUSE", 0x5c, 0 }, { "HiRcqParityError", 31, 1 }, { "LoRcqParityError", 30, 1 }, { "HiDrbParityError", 29, 1 }, { "LoDrbParityError", 28, 1 }, { "FlParityError", 22, 6 }, { "ItParityError", 20, 2 }, { "IrParityError", 19, 1 }, { "RcParityError", 18, 1 }, { "OcParityError", 17, 1 }, { "CpParityError", 16, 1 }, { "R_Req_FramingError", 15, 1 }, { "UC_Req_FramingError", 14, 1 }, { "HiCtlDrbDropErr", 13, 1 }, { "LoCtlDrbDropErr", 12, 1 }, { "HiPioDrbDropErr", 11, 1 }, { "LoPioDrbDropErr", 10, 1 }, { "HiCrdtUndFlowErr", 9, 1 }, { "LoCrdtUndFlowErr", 8, 1 }, { "HiPriorityDBFull", 7, 1 }, { "HiPriorityDBEmpty", 6, 1 }, { "LoPriorityDBFull", 5, 1 }, { "LoPriorityDBEmpty", 4, 1 }, { "RspQDisabled", 3, 1 }, { "RspQCreditOverfow", 2, 1 }, { "FlEmpty", 1, 1 }, { "RspQStarve", 0, 1 }, { "SG_INT_ENABLE", 0x60, 0 }, { "HiRcqParityError", 31, 1 }, { "LoRcqParityError", 30, 1 }, { "HiDrbParityError", 29, 1 }, { "LoDrbParityError", 28, 1 }, { "FlParityError", 22, 6 }, { "ItParityError", 20, 2 }, { "IrParityError", 19, 1 }, { "RcParityError", 18, 1 }, { "OcParityError", 17, 1 }, { "CpParityError", 16, 1 }, { "R_Req_FramingError", 15, 1 }, { "UC_Req_FramingError", 14, 1 }, { "HiCtlDrbDropErr", 13, 1 }, { "LoCtlDrbDropErr", 12, 1 }, { "HiPioDrbDropErr", 11, 1 }, { "LoPioDrbDropErr", 10, 1 }, { "HiCrdtUndFlowErr", 9, 1 }, { "LoCrdtUndFlowErr", 8, 1 }, { "HiPriorityDBFull", 7, 1 }, { "HiPriorityDBEmpty", 6, 1 }, { "LoPriorityDBFull", 5, 1 }, { "LoPriorityDBEmpty", 4, 1 }, { "RspQDisabled", 3, 1 }, { "RspQCreditOverfow", 2, 1 }, { "FlEmpty", 1, 1 }, { "RspQStarve", 0, 1 }, { "SG_CMDQ_CREDIT_TH", 0x64, 0 }, { "Timeout", 8, 24 }, { "Threshold", 0, 8 }, { "SG_TIMER_TICK", 0x68, 0 }, { "SG_CQ_CONTEXT_BADDR", 0x6c, 0 }, { "baseAddr", 5, 27 }, { "SG_OCO_BASE", 0x70, 0 }, { "Base1", 16, 16 }, { "Base0", 0, 16 }, { "SG_DRB_PRI_THRESH", 0x74, 0 }, { "DrbPriThrsh", 0, 16 }, { "SG_DEBUG_INDEX", 0x78, 0 }, { "SG_DEBUG_DATA", 0x7c, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_pcix1_regs[] = { { "PCIX_INT_ENABLE", 0x80, 0 }, { "MSIXParErr", 22, 3 }, { "CFParErr", 18, 4 }, { "RFParErr", 14, 4 }, { "WFParErr", 12, 2 }, { "PIOParErr", 11, 1 }, { "DetUncECCErr", 10, 1 }, { "DetCorECCErr", 9, 1 }, { "RcvSplCmpErr", 8, 1 }, { "UnxSplCmp", 7, 1 }, { "SplCmpDis", 6, 1 }, { "DetParErr", 5, 1 }, { "SigSysErr", 4, 1 }, { "RcvMstAbt", 3, 1 }, { "RcvTarAbt", 2, 1 }, { "SigTarAbt", 1, 1 }, { "MstDetParErr", 0, 1 }, { "PCIX_INT_CAUSE", 0x84, 0 }, { "MSIXParErr", 22, 3 }, { "CFParErr", 18, 4 }, { "RFParErr", 14, 4 }, { "WFParErr", 12, 2 }, { "PIOParErr", 11, 1 }, { "DetUncECCErr", 10, 1 }, { "DetCorECCErr", 9, 1 }, { "RcvSplCmpErr", 8, 1 }, { "UnxSplCmp", 7, 1 }, { "SplCmpDis", 6, 1 }, { "DetParErr", 5, 1 }, { "SigSysErr", 4, 1 }, { "RcvMstAbt", 3, 1 }, { "RcvTarAbt", 2, 1 }, { "SigTarAbt", 1, 1 }, { "MstDetParErr", 0, 1 }, { "PCIX_CFG", 0x88, 0 }, { "DMAStopEn", 19, 1 }, { "CLIDecEn", 18, 1 }, { "LatTmrDis", 17, 1 }, { "LowPwrEn", 16, 1 }, { "AsyncIntVec", 11, 5 }, { "MaxSplTrnC", 8, 3 }, { "MaxSplTrnR", 5, 3 }, { "MaxWrByteCnt", 3, 2 }, { "WrReqAtomicEn", 2, 1 }, { "CRstWrmMode", 1, 1 }, { "PIOAck64En", 0, 1 }, { "PCIX_MODE", 0x8c, 0 }, { "PClkRange", 6, 2 }, { "PCIXInitPat", 2, 4 }, { "66MHz", 1, 1 }, { "64Bit", 0, 1 }, { "PCIX_CAL", 0x90, 0 }, { "Busy", 31, 1 }, { "PerCalDiv", 22, 8 }, { "PerCalEn", 21, 1 }, { "SglCalEn", 20, 1 }, { "ZInUpdMode", 19, 1 }, { "ZInSel", 18, 1 }, { "ZPDMan", 15, 3 }, { "ZPUMan", 12, 3 }, { "ZPDOut", 9, 3 }, { "ZPUOut", 6, 3 }, { "ZPDIn", 3, 3 }, { "ZPUIn", 0, 3 }, { "PCIX_WOL", 0x94, 0 }, { "WakeUp1", 3, 1 }, { "WakeUp0", 2, 1 }, { "SleepMode1", 1, 1 }, { "SleepMode0", 0, 1 }, { "PCIX_STAT0", 0x98, 0 }, { "PIOReqFifoLevel", 26, 6 }, { "RFIniSt", 24, 2 }, { "RFRespRdSt", 22, 2 }, { "TarCSt", 19, 3 }, { "TarXSt", 16, 3 }, { "WFReqWrSt", 13, 3 }, { "WFRespFifoEmpty", 12, 1 }, { "WFReqFifoEmpty", 11, 1 }, { "RFRespFifoEmpty", 10, 1 }, { "RFReqFifoEmpty", 9, 1 }, { "PIORespFifoLevel", 7, 2 }, { "CFRespFifoEmpty", 6, 1 }, { "CFReqFifoEmpty", 5, 1 }, { "VPDRespFifoEmpty", 4, 1 }, { "VPDReqFifoEmpty", 3, 1 }, { "PIO_RspPnd", 2, 1 }, { "DlyTrnPnd", 1, 1 }, { "SplTrnPnd", 0, 1 }, { "PCIX_STAT1", 0x9c, 0 }, { "WFIniSt", 26, 4 }, { "ArbSt", 23, 3 }, { "PMISt", 21, 2 }, { "CalSt", 19, 2 }, { "CFReqRdSt", 17, 2 }, { "CFIniSt", 15, 2 }, { "CFRespRdSt", 13, 2 }, { "IniCSt", 10, 3 }, { "IniXSt", 7, 3 }, { "IntSt", 4, 3 }, { "PIOSt", 2, 2 }, { "RFReqRdSt", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_pcie0_regs[] = { { "PCIE_INT_ENABLE", 0x80, 0 }, { "BISTErr", 19, 8 }, { "TxParErr", 18, 1 }, { "RxParErr", 17, 1 }, { "RetryLUTParErr", 16, 1 }, { "RetryBUFParErr", 15, 1 }, { "MSIXParErr", 12, 3 }, { "CFParErr", 11, 1 }, { "RFParErr", 10, 1 }, { "WFParErr", 9, 1 }, { "PIOParErr", 8, 1 }, { "UnxSplCplErrC", 7, 1 }, { "UnxSplCplErrR", 6, 1 }, { "VPDAddrChng", 5, 1 }, { "BusMstrEn", 4, 1 }, { "PMStChng", 3, 1 }, { "PEXMsg", 2, 1 }, { "ZeroLenRd", 1, 1 }, { "PEXErr", 0, 1 }, { "PCIE_INT_CAUSE", 0x84, 0 }, { "BISTErr", 19, 8 }, { "TxParErr", 18, 1 }, { "RxParErr", 17, 1 }, { "RetryLUTParErr", 16, 1 }, { "RetryBUFParErr", 15, 1 }, { "MSIXParErr", 12, 3 }, { "CFParErr", 11, 1 }, { "RFParErr", 10, 1 }, { "WFParErr", 9, 1 }, { "PIOParErr", 8, 1 }, { "UnxSplCplErrC", 7, 1 }, { "UnxSplCplErrR", 6, 1 }, { "VPDAddrChng", 5, 1 }, { "BusMstrEn", 4, 1 }, { "PMStChng", 3, 1 }, { "PEXMsg", 2, 1 }, { "ZeroLenRd", 1, 1 }, { "PEXErr", 0, 1 }, { "PCIE_CFG", 0x88, 0 }, { "DMAStopEn", 24, 1 }, { "PriorityINTA", 23, 1 }, { "IniFullPkt", 22, 1 }, { "EnableLinkDwnDRst", 21, 1 }, { "EnableLinkDownRst", 20, 1 }, { "EnableHotRst", 19, 1 }, { "IniWaitForGnt", 18, 1 }, { "IniBEDis", 17, 1 }, { "CLIDecEn", 16, 1 }, { "AsyncIntVec", 11, 5 }, { "MaxSplTrnC", 7, 4 }, { "MaxSplTrnR", 1, 6 }, { "CRstWrmMode", 0, 1 }, { "PCIE_MODE", 0x8c, 0 }, { "TAR_State", 29, 3 }, { "RF_StateIni", 26, 3 }, { "CF_StateIni", 23, 3 }, { "PIO_StatePL", 20, 3 }, { "PIO_StateISC", 18, 2 }, { "NumFstTrnSeqRx", 10, 8 }, { "LnkCntlState", 2, 8 }, { "VC0Up", 1, 1 }, { "LnkInitial", 0, 1 }, { "PCIE_STAT", 0x90, 0 }, { "INI_State", 28, 4 }, { "WF_StateIni", 24, 4 }, { "PLM_ReqFIFOCnt", 22, 2 }, { "ER_ReqFIFOEmpty", 21, 1 }, { "WF_RspFIFOEmpty", 20, 1 }, { "WF_ReqFIFOEmpty", 19, 1 }, { "RF_RspFIFOEmpty", 18, 1 }, { "RF_ReqFIFOEmpty", 17, 1 }, { "RF_ActEmpty", 16, 1 }, { "PIO_RspFIFOCnt", 11, 5 }, { "PIO_ReqFIFOCnt", 5, 6 }, { "CF_RspFIFOEmpty", 4, 1 }, { "CF_ReqFIFOEmpty", 3, 1 }, { "CF_ActEmpty", 2, 1 }, { "VPD_RspFIFOEmpty", 1, 1 }, { "VPD_ReqFIFOEmpty", 0, 1 }, { "PCIE_WOL", 0x94, 0 }, { "CF_RspState", 12, 2 }, { "RF_RspState", 10, 2 }, { "PME_State", 7, 3 }, { "INT_State", 4, 3 }, { "WakeUp1", 3, 1 }, { "WakeUp0", 2, 1 }, { "SleepMode1", 1, 1 }, { "SleepMode0", 0, 1 }, { "PCIE_PEX_CTRL0", 0x98, 0 }, { "CplTimeoutRetry", 31, 1 }, { "StrictTSMN", 30, 1 }, { "NumFstTrnSeq", 22, 8 }, { "ReplayLmt", 2, 20 }, { "TxPndChkEn", 1, 1 }, { "CplPndChkEn", 0, 1 }, { "PCIE_PEX_CTRL1", 0x9c, 0 }, { "RxPhyErrEn", 31, 1 }, { "DLLPTimeoutLmt", 13, 18 }, { "AckLat", 0, 13 }, { "PCIE_PEX_CTRL2", 0xa0, 0 }, { "LnkCntlDetDir", 30, 1 }, { "EnterL1rEn", 29, 1 }, { "PMExitL1Req", 28, 1 }, { "PMTxIdle", 27, 1 }, { "PCIModeLoop", 26, 1 }, { "L1ASPMTxRxL0sTime", 14, 12 }, { "L0sIdleTime", 3, 11 }, { "EnterL1ASPMEn", 2, 1 }, { "EnterL1En", 1, 1 }, { "EnterL0sEn", 0, 1 }, { "PCIE_PEX_ERR", 0xa4, 0 }, { "CplTimeoutID", 18, 7 }, { "FlowCtlOFlowErr", 17, 1 }, { "ReplayTimeout", 16, 1 }, { "ReplayRollover", 15, 1 }, { "BadDLLP", 14, 1 }, { "DLLPErr", 13, 1 }, { "FlowCtlProtErr", 12, 1 }, { "CplTimeout", 11, 1 }, { "PHYRcvErr", 10, 1 }, { "DisTLP", 9, 1 }, { "BadECRC", 8, 1 }, { "BadTLP", 7, 1 }, { "MalTLP", 6, 1 }, { "UnxCpl", 5, 1 }, { "UnsReq", 4, 1 }, { "PsnReq", 3, 1 }, { "UnsCpl", 2, 1 }, { "CplAbt", 1, 1 }, { "PsnCpl", 0, 1 }, { "PCIE_SERDES_CTRL", 0xa8, 0 }, { "PMASel", 3, 1 }, { "Lane", 0, 3 }, { "PCIE_SERDES_QUAD_CTRL0", 0xac, 0 }, { "TestSig", 10, 19 }, { "Offset", 2, 8 }, { "OffsetEn", 1, 1 }, { "IDDQb", 0, 1 }, { "PCIE_SERDES_QUAD_CTRL1", 0xb0, 0 }, { "FastInit", 28, 1 }, { "CTCDisable", 27, 1 }, { "ManResetPLL", 26, 1 }, { "ManL2Pwrdn", 25, 1 }, { "ManQuadEn", 24, 1 }, { "RxEqCtl", 22, 2 }, { "HiVMode", 21, 1 }, { "RefSel", 19, 2 }, { "RxTermAdj", 17, 2 }, { "TxTermAdj", 15, 2 }, { "Deq", 11, 4 }, { "Dtx", 7, 4 }, { "LoDrv", 6, 1 }, { "HiDrv", 5, 1 }, { "IntParReset", 4, 1 }, { "IntParLPBK", 3, 1 }, { "IntSerLPBKwDrv", 2, 1 }, { "PW", 1, 1 }, { "PClkDetect", 0, 1 }, { "PCIE_SERDES_LANE_CTRL", 0xb4, 0 }, { "ExtBISTChkErrClr", 22, 1 }, { "ExtBISTChkEn", 21, 1 }, { "ExtBISTGenEn", 20, 1 }, { "ExtBISTPat", 17, 3 }, { "ExtParReset", 16, 1 }, { "ExtParLPBK", 15, 1 }, { "ManRxTermEn", 14, 1 }, { "ManBeaconTxEn", 13, 1 }, { "ManRxDetectEn", 12, 1 }, { "ManTxIdleEn", 11, 1 }, { "ManRxIdleEn", 10, 1 }, { "ManL1Pwrdn", 9, 1 }, { "ManReset", 8, 1 }, { "ManFmOffset", 3, 5 }, { "ManFmOffsetEn", 2, 1 }, { "ManLaneEn", 1, 1 }, { "IntSerLPBK", 0, 1 }, { "PCIE_SERDES_LANE_STAT", 0xb8, 0 }, { "ExtBISTChkErrCnt", 8, 24 }, { "ExtBISTChkFmd", 7, 1 }, { "BeaconDetectChg", 6, 1 }, { "RxDetectChg", 5, 1 }, { "TxIdleDetectChg", 4, 1 }, { "BeaconDetect", 2, 1 }, { "RxDetect", 1, 1 }, { "TxIdleDetect", 0, 1 }, { "PCIE_PEX_WMARK", 0xbc, 0 }, { "P_WMark", 18, 11 }, { "NP_WMark", 11, 7 }, { "CPL_WMark", 0, 11 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_t3dbg_regs[] = { { "T3DBG_DBG0_CFG", 0xc0, 0 }, { "RegSelect", 9, 8 }, { "ModuleSelect", 4, 5 }, { "ClkSelect", 0, 4 }, { "T3DBG_DBG0_EN", 0xc4, 0 }, { "SDRByte0", 8, 1 }, { "DDREn", 4, 1 }, { "PortEn", 0, 1 }, { "T3DBG_DBG1_CFG", 0xc8, 0 }, { "RegSelect", 9, 8 }, { "ModuleSelect", 4, 5 }, { "ClkSelect", 0, 4 }, { "T3DBG_DBG1_EN", 0xcc, 0 }, { "SDRByte0", 8, 1 }, { "DDREn", 4, 1 }, { "PortEn", 0, 1 }, { "T3DBG_GPIO_EN", 0xd0, 0 }, { "GPIO11_OEn", 27, 1 }, { "GPIO10_OEn", 26, 1 }, { "GPIO9_OEn", 25, 1 }, { "GPIO8_OEn", 24, 1 }, { "GPIO7_OEn", 23, 1 }, { "GPIO6_OEn", 22, 1 }, { "GPIO5_OEn", 21, 1 }, { "GPIO4_OEn", 20, 1 }, { "GPIO3_OEn", 19, 1 }, { "GPIO2_OEn", 18, 1 }, { "GPIO1_OEn", 17, 1 }, { "GPIO0_OEn", 16, 1 }, { "GPIO11_Out_Val", 11, 1 }, { "GPIO10_Out_Val", 10, 1 }, { "GPIO9_Out_Val", 9, 1 }, { "GPIO8_Out_Val", 8, 1 }, { "GPIO7_Out_Val", 7, 1 }, { "GPIO6_Out_Val", 6, 1 }, { "GPIO5_Out_Val", 5, 1 }, { "GPIO4_Out_Val", 4, 1 }, { "GPIO3_Out_Val", 3, 1 }, { "GPIO2_Out_Val", 2, 1 }, { "GPIO1_Out_Val", 1, 1 }, { "GPIO0_Out_Val", 0, 1 }, { "T3DBG_GPIO_IN", 0xd4, 0 }, { "GPIO11_CHG_DET", 27, 1 }, { "GPIO10_CHG_DET", 26, 1 }, { "GPIO9_CHG_DET", 25, 1 }, { "GPIO8_CHG_DET", 24, 1 }, { "GPIO7_CHG_DET", 23, 1 }, { "GPIO6_CHG_DET", 22, 1 }, { "GPIO5_CHG_DET", 21, 1 }, { "GPIO4_CHG_DET", 20, 1 }, { "GPIO3_CHG_DET", 19, 1 }, { "GPIO2_CHG_DET", 18, 1 }, { "GPIO1_CHG_DET", 17, 1 }, { "GPIO0_CHG_DET", 16, 1 }, { "GPIO11_IN", 11, 1 }, { "GPIO10_IN", 10, 1 }, { "GPIO9_IN", 9, 1 }, { "GPIO8_IN", 8, 1 }, { "GPIO7_IN", 7, 1 }, { "GPIO6_IN", 6, 1 }, { "GPIO5_IN", 5, 1 }, { "GPIO4_IN", 4, 1 }, { "GPIO3_IN", 3, 1 }, { "GPIO2_IN", 2, 1 }, { "GPIO1_IN", 1, 1 }, { "GPIO0_IN", 0, 1 }, { "T3DBG_INT_ENABLE", 0xd8, 0 }, { "C_LOCK", 21, 1 }, { "M_LOCK", 20, 1 }, { "U_LOCK", 19, 1 }, { "R_LOCK", 18, 1 }, { "PX_LOCK", 17, 1 }, { "GPIO11", 11, 1 }, { "GPIO10", 10, 1 }, { "GPIO9", 9, 1 }, { "GPIO8", 8, 1 }, { "GPIO7", 7, 1 }, { "GPIO6", 6, 1 }, { "GPIO5", 5, 1 }, { "GPIO4", 4, 1 }, { "GPIO3", 3, 1 }, { "GPIO2", 2, 1 }, { "GPIO1", 1, 1 }, { "GPIO0", 0, 1 }, { "T3DBG_INT_CAUSE", 0xdc, 0 }, { "C_LOCK", 21, 1 }, { "M_LOCK", 20, 1 }, { "U_LOCK", 19, 1 }, { "R_LOCK", 18, 1 }, { "PX_LOCK", 17, 1 }, { "GPIO11", 11, 1 }, { "GPIO10", 10, 1 }, { "GPIO9", 9, 1 }, { "GPIO8", 8, 1 }, { "GPIO7", 7, 1 }, { "GPIO6", 6, 1 }, { "GPIO5", 5, 1 }, { "GPIO4", 4, 1 }, { "GPIO3", 3, 1 }, { "GPIO2", 2, 1 }, { "GPIO1", 1, 1 }, { "GPIO0", 0, 1 }, { "T3DBG_DBG0_RST_VALUE", 0xe0, 0 }, { "DebugData", 0, 8 }, { "T3DBG_PLL_OCLK_PAD_EN", 0xe4, 0 }, { "PCIE_OCLK_En", 20, 1 }, { "PClkTree_DBG_En", 17, 1 }, { "PCIX_OCLK_En", 16, 1 }, { "U_OCLK_En", 12, 1 }, { "R_OCLK_En", 8, 1 }, { "M_OCLK_En", 4, 1 }, { "C_OCLK_En", 0, 1 }, { "T3DBG_PLL_LOCK", 0xe8, 0 }, { "PCIX_LOCK", 16, 1 }, { "U_LOCK", 12, 1 }, { "R_LOCK", 8, 1 }, { "M_LOCK", 4, 1 }, { "C_LOCK", 0, 1 }, { "T3DBG_SERDES_RBC_CFG", 0xec, 0 }, { "X_RBC_Lane_Sel", 16, 2 }, { "X_RBC_Dbg_En", 12, 1 }, { "X_Serdes_Sel", 8, 1 }, { "PE_RBC_Lane_Sel", 4, 3 }, { "PE_RBC_Dbg_En", 0, 1 }, { "T3DBG_GPIO_ACT_LOW", 0xf0, 0 }, { "C_LOCK_ACT_LOW", 21, 1 }, { "M_LOCK_ACT_LOW", 20, 1 }, { "U_LOCK_ACT_LOW", 19, 1 }, { "R_LOCK_ACT_LOW", 18, 1 }, { "PX_LOCK_ACT_LOW", 17, 1 }, { "GPIO11_ACT_LOW", 11, 1 }, { "GPIO10_ACT_LOW", 10, 1 }, { "GPIO9_ACT_LOW", 9, 1 }, { "GPIO8_ACT_LOW", 8, 1 }, { "GPIO7_ACT_LOW", 7, 1 }, { "GPIO6_ACT_LOW", 6, 1 }, { "GPIO5_ACT_LOW", 5, 1 }, { "GPIO4_ACT_LOW", 4, 1 }, { "GPIO3_ACT_LOW", 3, 1 }, { "GPIO2_ACT_LOW", 2, 1 }, { "GPIO1_ACT_LOW", 1, 1 }, { "GPIO0_ACT_LOW", 0, 1 }, { "T3DBG_PMON_CFG", 0xf4, 0 }, { "PMON_DONE", 29, 1 }, { "PMON_FAIL", 28, 1 }, { "PMON_FDEL_AUTO", 22, 6 }, { "PMON_CDEL_AUTO", 16, 6 }, { "PMON_FDEL_MANUAL", 10, 6 }, { "PMON_CDEL_MANUAL", 4, 6 }, { "PMON_MANUAL", 1, 1 }, { "PMON_AUTO", 0, 1 }, { "T3DBG_SERDES_REFCLK_CFG", 0xf8, 0 }, { "PE_REFCLK_DBG_EN", 12, 1 }, { "X_REFCLK_DBG_EN", 8, 1 }, { "PE_REFCLK_TERMADJ", 5, 2 }, { "PE_REFCLK_PD", 4, 1 }, { "X_REFCLK_TERMADJ", 1, 2 }, { "X_REFCLK_PD", 0, 1 }, { "T3DBG_PCIE_PMA_BSPIN_CFG", 0xfc, 0 }, { "BSModeQuad1", 31, 1 }, { "BSInSelLane7", 29, 2 }, { "BSEnLane7", 28, 1 }, { "BSInSelLane6", 25, 2 }, { "BSEnLane6", 24, 1 }, { "BSInSelLane5", 21, 2 }, { "BSEnLane5", 20, 1 }, { "BSInSelLane4", 17, 2 }, { "BSEnLane4", 16, 1 }, { "BSModeQuad0", 15, 1 }, { "BSInSelLane3", 13, 2 }, { "BSEnLane3", 12, 1 }, { "BSInSelLane2", 9, 2 }, { "BSEnLane2", 8, 1 }, { "BSInSelLane1", 5, 2 }, { "BSEnLane1", 4, 1 }, { "BSInSelLane0", 1, 2 }, { "BSEnLane0", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_mc7_pmrx_regs[] = { { "MC7_CFG", 0x100, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x104, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x108, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x10c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x110, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x114, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x118, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x11c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x120, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x124, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x128, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ERR_ADDR", 0x12c, 0 }, { "ErrAddress", 3, 29 }, { "ErrAgent", 1, 2 }, { "ErrOp", 0, 1 }, { "MC7_ECC", 0x130, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x134, 0 }, { "MC7_CE_DATA0", 0x138, 0 }, { "MC7_CE_DATA1", 0x13c, 0 }, { "MC7_CE_DATA2", 0x140, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x144, 0 }, { "MC7_UE_DATA0", 0x148, 0 }, { "MC7_UE_DATA1", 0x14c, 0 }, { "MC7_UE_DATA2", 0x150, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x154, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x158, 0 }, { "MC7_BD_DATA1", 0x15c, 0 }, { "MC7_BD_DATA2", 0x160, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x164, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x168, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x16c, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x170, 0 }, { "MC7_BIST_OP", 0x174, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x178, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x17c, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_mc7_pmtx_regs[] = { { "MC7_CFG", 0x180, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x184, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x188, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x18c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x190, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x194, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x198, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x19c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x1a0, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x1a4, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x1a8, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ERR_ADDR", 0x1ac, 0 }, { "ErrAddress", 3, 29 }, { "ErrAgent", 1, 2 }, { "ErrOp", 0, 1 }, { "MC7_ECC", 0x1b0, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x1b4, 0 }, { "MC7_CE_DATA0", 0x1b8, 0 }, { "MC7_CE_DATA1", 0x1bc, 0 }, { "MC7_CE_DATA2", 0x1c0, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x1c4, 0 }, { "MC7_UE_DATA0", 0x1c8, 0 }, { "MC7_UE_DATA1", 0x1cc, 0 }, { "MC7_UE_DATA2", 0x1d0, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x1d4, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x1d8, 0 }, { "MC7_BD_DATA1", 0x1dc, 0 }, { "MC7_BD_DATA2", 0x1e0, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x1e4, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x1e8, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x1ec, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x1f0, 0 }, { "MC7_BIST_OP", 0x1f4, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x1f8, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x1fc, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_mc7_cm_regs[] = { { "MC7_CFG", 0x200, 0 }, { "ImpSetUpdate", 14, 1 }, { "IFEn", 13, 1 }, { "TERM300", 12, 1 }, { "TERM150", 11, 1 }, { "Slow", 10, 1 }, { "Width", 8, 2 }, { "ODTEn", 7, 1 }, { "Bks", 6, 1 }, { "Org", 5, 1 }, { "Den", 2, 3 }, { "Rdy", 1, 1 }, { "ClkEn", 0, 1 }, { "MC7_MODE", 0x204, 0 }, { "Busy", 31, 1 }, { "Mode", 0, 16 }, { "MC7_EXT_MODE1", 0x208, 0 }, { "Busy", 31, 1 }, { "OCDAdjustMode", 20, 1 }, { "OCDCode", 16, 4 }, { "ExtMode1", 0, 16 }, { "MC7_EXT_MODE2", 0x20c, 0 }, { "Busy", 31, 1 }, { "ExtMode2", 0, 16 }, { "MC7_EXT_MODE3", 0x210, 0 }, { "Busy", 31, 1 }, { "ExtMode3", 0, 16 }, { "MC7_PRE", 0x214, 0 }, { "Busy", 31, 1 }, { "MC7_REF", 0x218, 0 }, { "Busy", 31, 1 }, { "PreRefDiv", 1, 14 }, { "PerRefEn", 0, 1 }, { "MC7_DLL", 0x21c, 0 }, { "DLLLock", 31, 1 }, { "DLLDelta", 24, 7 }, { "ManDelta", 3, 7 }, { "DLLDeltaSel", 2, 1 }, { "DLLEnb", 1, 1 }, { "DLLRst", 0, 1 }, { "MC7_PARM", 0x220, 0 }, { "ActToPreDly", 26, 4 }, { "ActToRdWrDly", 23, 3 }, { "PreCyc", 20, 3 }, { "RefCyc", 13, 7 }, { "BkCyc", 8, 5 }, { "WrToRdDly", 4, 4 }, { "RdToWrDly", 0, 4 }, { "MC7_HWM_WRR", 0x224, 0 }, { "MEM_HWM", 26, 6 }, { "ULP_HWM", 22, 4 }, { "TOT_RLD_WT", 14, 8 }, { "MEM_RLD_WT", 7, 7 }, { "ULP_RLD_WT", 0, 7 }, { "MC7_CAL", 0x228, 0 }, { "BUSY", 31, 1 }, { "CAL_FAULT", 30, 1 }, { "PER_CAL_DIV", 22, 8 }, { "PER_CAL_EN", 21, 1 }, { "SGL_CAL_EN", 20, 1 }, { "IMP_UPD_MODE", 19, 1 }, { "IMP_SEL", 18, 1 }, { "IMP_MAN_PD", 15, 3 }, { "IMP_MAN_PU", 12, 3 }, { "IMP_CAL_PD", 9, 3 }, { "IMP_CAL_PU", 6, 3 }, { "IMP_SET_PD", 3, 3 }, { "IMP_SET_PU", 0, 3 }, { "MC7_ERR_ADDR", 0x22c, 0 }, { "ErrAddress", 3, 29 }, { "ErrAgent", 1, 2 }, { "ErrOp", 0, 1 }, { "MC7_ECC", 0x230, 0 }, { "UECnt", 10, 8 }, { "CECnt", 2, 8 }, { "ECCChkEn", 1, 1 }, { "ECCGenEn", 0, 1 }, { "MC7_CE_ADDR", 0x234, 0 }, { "MC7_CE_DATA0", 0x238, 0 }, { "MC7_CE_DATA1", 0x23c, 0 }, { "MC7_CE_DATA2", 0x240, 0 }, { "Data", 0, 8 }, { "MC7_UE_ADDR", 0x244, 0 }, { "MC7_UE_DATA0", 0x248, 0 }, { "MC7_UE_DATA1", 0x24c, 0 }, { "MC7_UE_DATA2", 0x250, 0 }, { "Data", 0, 8 }, { "MC7_BD_ADDR", 0x254, 0 }, { "Addr", 3, 29 }, { "MC7_BD_DATA0", 0x258, 0 }, { "MC7_BD_DATA1", 0x25c, 0 }, { "MC7_BD_DATA2", 0x260, 0 }, { "Data", 0, 8 }, { "MC7_BD_OP", 0x264, 0 }, { "Busy", 31, 1 }, { "Op", 0, 1 }, { "MC7_BIST_ADDR_BEG", 0x268, 0 }, { "AddrBeg", 5, 27 }, { "MC7_BIST_ADDR_END", 0x26c, 0 }, { "AddrEnd", 5, 27 }, { "MC7_BIST_DATA", 0x270, 0 }, { "MC7_BIST_OP", 0x274, 0 }, { "Busy", 31, 1 }, { "Gap", 4, 5 }, { "Cont", 3, 1 }, { "DataPat", 1, 2 }, { "Op", 0, 1 }, { "MC7_INT_ENABLE", 0x278, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, { "MC7_INT_CAUSE", 0x27c, 0 }, { "AE", 17, 1 }, { "PE", 2, 15 }, { "UE", 1, 1 }, { "CE", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_cim_regs[] = { { "CIM_BOOT_CFG", 0x280, 0 }, { "BootAddr", 2, 30 }, { "BootSdram", 1, 1 }, { "uPCRst", 0, 1 }, { "CIM_FLASH_BASE_ADDR", 0x284, 0 }, { "FlashBaseAddr", 2, 22 }, { "CIM_FLASH_ADDR_SIZE", 0x288, 0 }, { "FlashAddrSize", 2, 22 }, { "CIM_SDRAM_BASE_ADDR", 0x28c, 0 }, { "SdramBaseAddr", 2, 30 }, { "CIM_SDRAM_ADDR_SIZE", 0x290, 0 }, { "SdramAddrSize", 2, 30 }, { "CIM_UP_SPARE_INT", 0x294, 0 }, { "uPSpareInt", 0, 3 }, { "CIM_HOST_INT_ENABLE", 0x298, 0 }, { "DTagParErr", 28, 1 }, { "ITagParErr", 27, 1 }, { "IBQTPParErr", 26, 1 }, { "IBQULPParErr", 25, 1 }, { "IBQSGEHIParErr", 24, 1 }, { "IBQSGELOParErr", 23, 1 }, { "OBQULPLOParErr", 22, 1 }, { "OBQULPHIParErr", 21, 1 }, { "OBQSGEParErr", 20, 1 }, { "DCacheParErr", 19, 1 }, { "ICacheParErr", 18, 1 }, { "DRamParErr", 17, 1 }, { "Timer1IntEn", 15, 1 }, { "Timer0IntEn", 14, 1 }, { "PrefDropIntEn", 13, 1 }, { "BlkWrPlIntEn", 12, 1 }, { "BlkRdPlIntEn", 11, 1 }, { "BlkWrCtlIntEn", 10, 1 }, { "BlkRdCtlIntEn", 9, 1 }, { "BlkWrFlashIntEn", 8, 1 }, { "BlkRdFlashIntEn", 7, 1 }, { "SglWrFlashIntEn", 6, 1 }, { "WrBlkFlashIntEn", 5, 1 }, { "BlkWrBootIntEn", 4, 1 }, { "BlkRdBootIntEn", 3, 1 }, { "FlashRangeIntEn", 2, 1 }, { "SdramRangeIntEn", 1, 1 }, { "RsvdSpaceIntEn", 0, 1 }, { "CIM_HOST_INT_CAUSE", 0x29c, 0 }, { "DTagParErr", 28, 1 }, { "ITagParErr", 27, 1 }, { "IBQTPParErr", 26, 1 }, { "IBQULPParErr", 25, 1 }, { "IBQSGEHIParErr", 24, 1 }, { "IBQSGELOParErr", 23, 1 }, { "OBQULPLOParErr", 22, 1 }, { "OBQULPHIParErr", 21, 1 }, { "OBQSGEParErr", 20, 1 }, { "DCacheParErr", 19, 1 }, { "ICacheParErr", 18, 1 }, { "DRamParErr", 17, 1 }, { "Timer1Int", 15, 1 }, { "Timer0Int", 14, 1 }, { "PrefDropInt", 13, 1 }, { "BlkWrPlInt", 12, 1 }, { "BlkRdPlInt", 11, 1 }, { "BlkWrCtlInt", 10, 1 }, { "BlkRdCtlInt", 9, 1 }, { "BlkWrFlashInt", 8, 1 }, { "BlkRdFlashInt", 7, 1 }, { "SglWrFlashInt", 6, 1 }, { "WrBlkFlashInt", 5, 1 }, { "BlkWrBootInt", 4, 1 }, { "BlkRdBootInt", 3, 1 }, { "FlashRangeInt", 2, 1 }, { "SdramRangeInt", 1, 1 }, { "RsvdSpaceInt", 0, 1 }, { "CIM_UP_INT_ENABLE", 0x2a0, 0 }, { "DTagParErr", 28, 1 }, { "ITagParErr", 27, 1 }, { "IBQTPParErr", 26, 1 }, { "IBQULPParErr", 25, 1 }, { "IBQSGEHIParErr", 24, 1 }, { "IBQSGELOParErr", 23, 1 }, { "OBQULPLOParErr", 22, 1 }, { "OBQULPHIParErr", 21, 1 }, { "OBQSGEParErr", 20, 1 }, { "DCacheParErr", 19, 1 }, { "ICacheParErr", 18, 1 }, { "DRamParErr", 17, 1 }, { "MstPlIntEn", 16, 1 }, { "Timer1IntEn", 15, 1 }, { "Timer0IntEn", 14, 1 }, { "PrefDropIntEn", 13, 1 }, { "BlkWrPlIntEn", 12, 1 }, { "BlkRdPlIntEn", 11, 1 }, { "BlkWrCtlIntEn", 10, 1 }, { "BlkRdCtlIntEn", 9, 1 }, { "BlkWrFlashIntEn", 8, 1 }, { "BlkRdFlashIntEn", 7, 1 }, { "SglWrFlashIntEn", 6, 1 }, { "WrBlkFlashIntEn", 5, 1 }, { "BlkWrBootIntEn", 4, 1 }, { "BlkRdBootIntEn", 3, 1 }, { "FlashRangeIntEn", 2, 1 }, { "SdramRangeIntEn", 1, 1 }, { "RsvdSpaceIntEn", 0, 1 }, { "CIM_UP_INT_CAUSE", 0x2a4, 0 }, { "DTagParErr", 28, 1 }, { "ITagParErr", 27, 1 }, { "IBQTPParErr", 26, 1 }, { "IBQULPParErr", 25, 1 }, { "IBQSGEHIParErr", 24, 1 }, { "IBQSGELOParErr", 23, 1 }, { "OBQULPLOParErr", 22, 1 }, { "OBQULPHIParErr", 21, 1 }, { "OBQSGEParErr", 20, 1 }, { "DCacheParErr", 19, 1 }, { "ICacheParErr", 18, 1 }, { "DRamParErr", 17, 1 }, { "MstPlInt", 16, 1 }, { "Timer1Int", 15, 1 }, { "Timer0Int", 14, 1 }, { "PrefDropInt", 13, 1 }, { "BlkWrPlInt", 12, 1 }, { "BlkRdPlInt", 11, 1 }, { "BlkWrCtlInt", 10, 1 }, { "BlkRdCtlInt", 9, 1 }, { "BlkWrFlashInt", 8, 1 }, { "BlkRdFlashInt", 7, 1 }, { "SglWrFlashInt", 6, 1 }, { "WrBlkFlashInt", 5, 1 }, { "BlkWrBootInt", 4, 1 }, { "BlkRdBootInt", 3, 1 }, { "FlashRangeInt", 2, 1 }, { "SdramRangeInt", 1, 1 }, { "RsvdSpaceInt", 0, 1 }, { "CIM_IBQ_FULLA_THRSH", 0x2a8, 0 }, { "Ibq0FullThrsh", 0, 9 }, { "Ibq1FullThrsh", 16, 9 }, { "CIM_IBQ_FULLB_THRSH", 0x2ac, 0 }, { "Ibq2FullThrsh", 0, 9 }, { "Ibq3FullThrsh", 16, 9 }, { "CIM_HOST_ACC_CTRL", 0x2b0, 0 }, { "HostBusy", 17, 1 }, { "HostWrite", 16, 1 }, { "HostAddr", 0, 16 }, { "CIM_HOST_ACC_DATA", 0x2b4, 0 }, { "CIM_IBQ_DBG_CFG", 0x2c0, 0 }, { "IbqDbgAddr", 16, 9 }, { "IbqDbgQID", 3, 2 }, { "IbqDbgWr", 2, 1 }, { "IbqDbgBusy", 1, 1 }, { "IbqDbgEn", 0, 1 }, { "CIM_OBQ_DBG_CFG", 0x2c4, 0 }, { "ObqDbgAddr", 16, 9 }, { "ObqDbgQID", 3, 2 }, { "ObqDbgWr", 2, 1 }, { "ObqDbgBusy", 1, 1 }, { "ObqDbgEn", 0, 1 }, { "CIM_IBQ_DBG_DATA", 0x2c8, 0 }, { "CIM_OBQ_DBG_DATA", 0x2cc, 0 }, { "CIM_CDEBUGDATA", 0x2d0, 0 }, { "CDebugDataH", 16, 16 }, { "CDebugDataL", 0, 16 }, { "CIM_DEBUGCFG", 0x2e0, 0 }, { "POLADbgRdPtr", 23, 9 }, { "PILADbgRdPtr", 14, 9 }, { "LADbgEn", 12, 1 }, { "DebugSelH", 5, 5 }, { "DebugSelL", 0, 5 }, { "CIM_DEBUGSTS", 0x2e4, 0 }, { "POLADbgWrPtr", 16, 9 }, { "PILADbgWrPtr", 0, 9 }, { "CIM_PO_LA_DEBUGDATA", 0x2e8, 0 }, { "CIM_PI_LA_DEBUGDATA", 0x2ec, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_tp1_regs[] = { { "TP_IN_CONFIG", 0x300, 0 }, { "RXFbArbPrio", 25, 1 }, { "TXFbArbPrio", 24, 1 }, { "DBMaxOpCnt", 16, 8 }, { "IPv6Enable", 15, 1 }, { "NICMode", 14, 1 }, { "EChecksumCheckTCP", 13, 1 }, { "EChecksumCheckIP", 12, 1 }, { "ECPL", 10, 1 }, { "EEthernet", 8, 1 }, { "ETunnel", 7, 1 }, { "CChecksumCheckTCP", 6, 1 }, { "CChecksumCheckIP", 5, 1 }, { "CCPL", 3, 1 }, { "CEthernet", 1, 1 }, { "CTunnel", 0, 1 }, { "TP_OUT_CONFIG", 0x304, 0 }, { "IPIDSplitMode", 16, 1 }, { "VLANExtractionEnable2ndPort", 13, 1 }, { "VLANExtractionEnable", 12, 1 }, { "EChecksumGenerateTCP", 11, 1 }, { "EChecksumGenerateIP", 10, 1 }, { "ECPL", 8, 1 }, { "EEthernet", 6, 1 }, { "CChecksumGenerateTCP", 5, 1 }, { "CChecksumGenerateIP", 4, 1 }, { "CCPL", 2, 1 }, { "CEthernet", 0, 1 }, { "TP_GLOBAL_CONFIG", 0x308, 0 }, { "SYNCookieParams", 26, 6 }, { "RXFlowControlDisable", 25, 1 }, { "TXPacingEnable", 24, 1 }, { "AttackFilterEnable", 23, 1 }, { "SYNCookieNoOptions", 22, 1 }, { "ProtectedMode", 21, 1 }, { "PingDrop", 20, 1 }, { "FragmentDrop", 19, 1 }, { "FiveTupleLookup", 17, 2 }, { "PathMTU", 15, 1 }, { "IPIdentSplit", 14, 1 }, { "IPChecksumOffload", 13, 1 }, { "UDPChecksumOffload", 12, 1 }, { "TCPChecksumOffload", 11, 1 }, { "QOSMapping", 10, 1 }, { "TCAMServerUse", 8, 2 }, { "IPTTL", 0, 8 }, { "TP_GLOBAL_RX_CREDIT", 0x30c, 0 }, { "TP_CMM_SIZE", 0x310, 0 }, { "CMMemMgrSize", 0, 28 }, { "TP_CMM_MM_BASE", 0x314, 0 }, { "CMMemMgrBase", 0, 28 }, { "TP_CMM_TIMER_BASE", 0x318, 0 }, { "CMTimerMaxNum", 28, 2 }, { "CMTimerBase", 0, 28 }, { "TP_PMM_SIZE", 0x31c, 0 }, { "PMSize", 0, 28 }, { "TP_PMM_TX_BASE", 0x320, 0 }, { "TP_PMM_DEFRAG_BASE", 0x324, 0 }, { "TP_PMM_RX_BASE", 0x328, 0 }, { "TP_PMM_RX_PAGE_SIZE", 0x32c, 0 }, { "TP_PMM_RX_MAX_PAGE", 0x330, 0 }, { "PMRxMaxPage", 0, 21 }, { "TP_PMM_TX_PAGE_SIZE", 0x334, 0 }, { "TP_PMM_TX_MAX_PAGE", 0x338, 0 }, { "PMTxMaxPage", 0, 21 }, { "TP_TCP_OPTIONS", 0x340, 0 }, { "MTUDefault", 16, 16 }, { "MTUEnable", 10, 1 }, { "SACKTx", 9, 1 }, { "SACKRx", 8, 1 }, { "SACKMode", 4, 2 }, { "WindowScaleMode", 2, 2 }, { "TimestampsMode", 0, 2 }, { "TP_DACK_CONFIG", 0x344, 0 }, { "AutoState3", 30, 2 }, { "AutoState2", 28, 2 }, { "AutoState1", 26, 2 }, { "ByteThreshold", 5, 20 }, { "MSSThreshold", 3, 2 }, { "AutoCareful", 2, 1 }, { "AutoEnable", 1, 1 }, { "Mode", 0, 1 }, { "TP_PC_CONFIG", 0x348, 0 }, { "CMCacheDisable", 31, 1 }, { "EnableOcspiFull", 30, 1 }, { "EnableFLMErrorDDP", 29, 1 }, { "LockTid", 28, 1 }, { "FixRcvWnd", 27, 1 }, { "TxTosQueueMapMode", 26, 1 }, { "RddpCongEn", 25, 1 }, { "EnableOnFlyPDU", 24, 1 }, { "EnableEPCMDAFull", 23, 1 }, { "ModulateUnionMode", 22, 1 }, { "TxDataAckRateEnable", 21, 1 }, { "TxDeferEnable", 20, 1 }, { "RxCongestionMode", 19, 1 }, { "HearbeatOnceDACK", 18, 1 }, { "HearbeatOnceHeap", 17, 1 }, { "HearbeatDACK", 16, 1 }, { "TxCongestionMode", 15, 1 }, { "AcceptLatestRcvAdv", 14, 1 }, { "DisableSYNData", 13, 1 }, { "DisableWindowPSH", 12, 1 }, { "DisableFINOldData", 11, 1 }, { "EnableFLMError", 10, 1 }, { "DisableNextMtu", 9, 1 }, { "FilterPeerFIN", 8, 1 }, { "EnableFeedbackSend", 7, 1 }, { "EnableRDMAError", 6, 1 }, { "EnableDDPFlowControl", 5, 1 }, { "DisableHeldFIN", 4, 1 }, { "TableLatencyDelta", 0, 4 }, { "TP_PC_CONFIG2", 0x34c, 0 }, { "DisbleDaParbit0", 15, 1 }, { "EnableArpMiss", 13, 1 }, { "EnableNonOfdTnlSyn", 12, 1 }, { "EnableIPv6RSS", 11, 1 }, { "EnableDropRQEmptyPkt", 10, 1 }, { "EnableTxPortfromDA2", 9, 1 }, { "EnableRxPktTmstpRss", 8, 1 }, { "EnableSndUnaInRxData", 7, 1 }, { "EnableRxPortFromAddr", 6, 1 }, { "EnableTxPortfromDA", 5, 1 }, { "EnableCHdrAFull", 4, 1 }, { "EnableNonOfdScbBit", 3, 1 }, { "EnableNonOfdTidRss", 2, 1 }, { "EnableNonOfdTcbRss", 1, 1 }, { "EnableOldRxForward", 0, 1 }, { "TP_TCP_BACKOFF_REG0", 0x350, 0 }, { "TimerBackoffIndex3", 24, 8 }, { "TimerBackoffIndex2", 16, 8 }, { "TimerBackoffIndex1", 8, 8 }, { "TimerBackoffIndex0", 0, 8 }, { "TP_TCP_BACKOFF_REG1", 0x354, 0 }, { "TimerBackoffIndex7", 24, 8 }, { "TimerBackoffIndex6", 16, 8 }, { "TimerBackoffIndex5", 8, 8 }, { "TimerBackoffIndex4", 0, 8 }, { "TP_TCP_BACKOFF_REG2", 0x358, 0 }, { "TimerBackoffIndex11", 24, 8 }, { "TimerBackoffIndex10", 16, 8 }, { "TimerBackoffIndex9", 8, 8 }, { "TimerBackoffIndex8", 0, 8 }, { "TP_TCP_BACKOFF_REG3", 0x35c, 0 }, { "TimerBackoffIndex15", 24, 8 }, { "TimerBackoffIndex14", 16, 8 }, { "TimerBackoffIndex13", 8, 8 }, { "TimerBackoffIndex12", 0, 8 }, { "TP_PARA_REG0", 0x360, 0 }, { "InitCwnd", 24, 3 }, { "DupAckThresh", 20, 4 }, { "TP_PARA_REG1", 0x364, 0 }, { "InitRwnd", 16, 16 }, { "InitialSSThresh", 0, 16 }, { "TP_PARA_REG2", 0x368, 0 }, { "MaxRxData", 16, 16 }, { "RxCoalesceSize", 0, 16 }, { "TP_PARA_REG3", 0x36c, 0 }, { "TunnelCngDrop1", 21, 1 }, { "TunnelCngDrop0", 20, 1 }, { "TxDataAckIdx", 16, 4 }, { "RxFragEnable", 12, 3 }, { "TxPaceFixedStrict", 11, 1 }, { "TxPaceAutoStrict", 10, 1 }, { "TxPaceFixed", 9, 1 }, { "TxPaceAuto", 8, 1 }, { "RxUrgTunnel", 6, 1 }, { "RxUrgMode", 5, 1 }, { "TxUrgMode", 4, 1 }, { "CngCtrlMode", 2, 2 }, { "RxCoalesceEnable", 1, 1 }, { "RxCoalescePshEn", 0, 1 }, { "TP_PARA_REG4", 0x370, 0 }, { "HighSpeedCfg", 24, 8 }, { "NewRenoCfg", 16, 8 }, { "TahoeCfg", 8, 8 }, { "RenoCfg", 0, 8 }, { "TP_PARA_REG5", 0x374, 0 }, { "IndicateSize", 16, 16 }, { "SchdEnable", 8, 1 }, { "RxDdpOffInit", 3, 1 }, { "OnFlyDDPEnable", 2, 1 }, { "DackTimerSpin", 1, 1 }, { "PushTimerEnable", 0, 1 }, { "TP_PARA_REG6", 0x378, 0 }, { "TxPDUSizeAdj", 16, 8 }, { "EnableDeferACK", 12, 1 }, { "EnableESnd", 11, 1 }, { "EnableCSnd", 10, 1 }, { "EnablePDUE", 9, 1 }, { "EnablePDUC", 8, 1 }, { "EnableBUFI", 7, 1 }, { "EnableBUFE", 6, 1 }, { "EnableDefer", 5, 1 }, { "EnableClearRxmtOos", 4, 1 }, { "DisablePDUCng", 3, 1 }, { "DisablePDUTimeout", 2, 1 }, { "DisablePDURxmt", 1, 1 }, { "DisablePDUxmt", 0, 1 }, { "TP_PARA_REG7", 0x37c, 0 }, { "PMMaxXferLen1", 16, 16 }, { "PMMaxXferLen0", 0, 16 }, { "TP_TIMER_RESOLUTION", 0x390, 0 }, { "TimerResolution", 16, 8 }, { "TimestampResolution", 8, 8 }, { "DelayedACKResolution", 0, 8 }, { "TP_MSL", 0x394, 0 }, { "MSL", 0, 30 }, { "TP_RXT_MIN", 0x398, 0 }, { "RxtMin", 0, 30 }, { "TP_RXT_MAX", 0x39c, 0 }, { "RxtMax", 0, 30 }, { "TP_PERS_MIN", 0x3a0, 0 }, { "PersMin", 0, 30 }, { "TP_PERS_MAX", 0x3a4, 0 }, { "PersMax", 0, 30 }, { "TP_KEEP_IDLE", 0x3a8, 0 }, { "KeepaliveIdle", 0, 30 }, { "TP_KEEP_INTVL", 0x3ac, 0 }, { "KeepaliveIntvl", 0, 30 }, { "TP_INIT_SRTT", 0x3b0, 0 }, { "InitSrtt", 0, 16 }, { "TP_DACK_TIMER", 0x3b4, 0 }, { "DackTime", 0, 12 }, { "TP_FINWAIT2_TIMER", 0x3b8, 0 }, { "Finwait2Time", 0, 30 }, { "TP_FAST_FINWAIT2_TIMER", 0x3bc, 0 }, { "FastFinwait2Time", 0, 30 }, { "TP_SHIFT_CNT", 0x3c0, 0 }, { "SynShiftMax", 24, 8 }, { "RxtShiftMaxR1", 20, 4 }, { "RxtShiftMaxR2", 16, 4 }, { "PerShiftBackoffMax", 12, 4 }, { "PerShiftMax", 8, 4 }, { "KeepaliveMax", 0, 8 }, { "TP_TIME_HI", 0x3c8, 0 }, { "TP_TIME_LO", 0x3cc, 0 }, { "TP_MTU_PORT_TABLE", 0x3d0, 0 }, { "Port1MTUValue", 16, 16 }, { "Port0MTUValue", 0, 16 }, { "TP_ULP_TABLE", 0x3d4, 0 }, { "ULPType7Field", 28, 4 }, { "ULPType6Field", 24, 4 }, { "ULPType5Field", 20, 4 }, { "ULPType4Field", 16, 4 }, { "ULPType3Field", 12, 4 }, { "ULPType2Field", 8, 4 }, { "ULPType1Field", 4, 4 }, { "ULPType0Field", 0, 4 }, { "TP_PACE_TABLE", 0x3d8, 0 }, { "TP_CCTRL_TABLE", 0x3dc, 0 }, { "TP_TOS_TABLE", 0x3e0, 0 }, { "TP_MTU_TABLE", 0x3e4, 0 }, { "TP_RSS_MAP_TABLE", 0x3e8, 0 }, { "TP_RSS_LKP_TABLE", 0x3ec, 0 }, { "TP_RSS_CONFIG", 0x3f0, 0 }, { "TNL4tupEn", 29, 1 }, { "TNL2tupEn", 28, 1 }, { "TNLprtEn", 26, 1 }, { "TNLMapEn", 25, 1 }, { "TNLLkpEn", 24, 1 }, { "OFD4tupEn", 21, 1 }, { "OFD2tupEn", 20, 1 }, { "OFDMapEn", 17, 1 }, { "OFDLkpEn", 16, 1 }, { "SYN4tupEn", 13, 1 }, { "SYN2tupEn", 12, 1 }, { "SYNMapEn", 9, 1 }, { "SYNLkpEn", 8, 1 }, { "RRCPLMapEn", 7, 1 }, { "RRCPLCPUSIZE", 4, 3 }, { "RQFeedbackEnable", 3, 1 }, { "HashToeplitz", 2, 1 }, { "HashSave", 1, 1 }, { "Disable", 0, 1 }, { "TP_RSS_CONFIG_TNL", 0x3f4, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_CONFIG_OFD", 0x3f8, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_CONFIG_SYN", 0x3fc, 0 }, { "MaskSize", 28, 3 }, { "DefaultCPUBase", 22, 6 }, { "DefaultCPU", 16, 6 }, { "DefaultQueue", 0, 16 }, { "TP_RSS_SECRET_KEY0", 0x400, 0 }, { "TP_RSS_SECRET_KEY1", 0x404, 0 }, { "TP_RSS_SECRET_KEY2", 0x408, 0 }, { "TP_RSS_SECRET_KEY3", 0x40c, 0 }, { "TP_TM_PIO_ADDR", 0x418, 0 }, { "TP_TM_PIO_DATA", 0x41c, 0 }, { "TP_TX_MOD_QUE_TABLE", 0x420, 0 }, { "TP_TX_RESOURCE_LIMIT", 0x424, 0 }, { "TX_RESOURCE_LIMIT_CH1_PC", 24, 8 }, { "TX_RESOURCE_LIMIT_CH1_NON_PC", 16, 8 }, { "TX_RESOURCE_LIMIT_CH0_PC", 8, 8 }, { "TX_RESOURCE_LIMIT_CH0_NON_PC", 0, 8 }, { "TP_TX_MOD_QUEUE_REQ_MAP", 0x428, 0 }, { "RX_MOD_WEIGHT", 24, 8 }, { "TX_MOD_WEIGHT", 16, 8 }, { "TX_MOD_TIMER_MODE", 8, 8 }, { "TX_MOD_QUEUE_REQ_MAP", 0, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT1", 0x42c, 0 }, { "TP_TX_MOD_QUEUE_WEIGHT7", 24, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT6", 16, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT5", 8, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT4", 0, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT0", 0x430, 0 }, { "TP_TX_MOD_QUEUE_WEIGHT3", 24, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT2", 16, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT1", 8, 8 }, { "TP_TX_MOD_QUEUE_WEIGHT0", 0, 8 }, { "TP_MOD_CHANNEL_WEIGHT", 0x434, 0 }, { "RX_MOD_CHANNEL_WEIGHT1", 24, 8 }, { "RX_MOD_CHANNEL_WEIGHT0", 16, 8 }, { "TX_MOD_CHANNEL_WEIGHT1", 8, 8 }, { "TX_MOD_CHANNEL_WEIGHT0", 0, 8 }, { "TP_MOD_RATE_LIMIT", 0x438, 0 }, { "RX_MOD_RATE_LIMIT_INC", 24, 8 }, { "RX_MOD_RATE_LIMIT_TICK", 16, 8 }, { "TX_MOD_RATE_LIMIT_INC", 8, 8 }, { "TX_MOD_RATE_LIMIT_TICK", 0, 8 }, { "TP_PIO_ADDR", 0x440, 0 }, { "TP_PIO_DATA", 0x444, 0 }, { "TP_RESET", 0x44c, 0 }, { "FlstInitEnable", 1, 1 }, { "TPReset", 0, 1 }, { "TP_MIB_INDEX", 0x450, 0 }, { "TP_MIB_RDATA", 0x454, 0 }, { "TP_SYNC_TIME_HI", 0x458, 0 }, { "TP_SYNC_TIME_LO", 0x45c, 0 }, { "TP_CMM_MM_RX_FLST_BASE", 0x460, 0 }, { "CMRxFlstBase", 0, 28 }, { "TP_CMM_MM_TX_FLST_BASE", 0x464, 0 }, { "CMTxFlstBase", 0, 28 }, { "TP_CMM_MM_PS_FLST_BASE", 0x468, 0 }, { "CMPsFlstBase", 0, 28 }, { "TP_CMM_MM_MAX_PSTRUCT", 0x46c, 0 }, { "CMMaxPstruct", 0, 21 }, { "TP_INT_ENABLE", 0x470, 0 }, { "FlmTxFlstEmpty", 30, 1 }, { "FlmRxFlstEmpty", 29, 1 }, { "FlmPerrSet", 28, 1 }, { "ProtocolSramPerr", 27, 1 }, { "ArpLutPerr", 26, 1 }, { "CmRcfOpPerr", 25, 1 }, { "CmCachePerr", 24, 1 }, { "CmRcfDataPerr", 23, 1 }, { "DbL2tLutPerr", 22, 1 }, { "DbTxTidPerr", 21, 1 }, { "DbExtPerr", 20, 1 }, { "DbOpPerr", 19, 1 }, { "TmCachePerr", 18, 1 }, { "ETpOutCplFifoPerr", 17, 1 }, { "ETpOutTcpFifoPerr", 16, 1 }, { "ETpOutIpFifoPerr", 15, 1 }, { "ETpOutEthFifoPerr", 14, 1 }, { "ETpInCplFifoPerr", 13, 1 }, { "ETpInTcpOptFifoPerr", 12, 1 }, { "ETpInTcpFifoPerr", 11, 1 }, { "ETpInIpFifoPerr", 10, 1 }, { "ETpInEthFifoPerr", 9, 1 }, { "CTpOutCplFifoPerr", 8, 1 }, { "CTpOutTcpFifoPerr", 7, 1 }, { "CTpOutIpFifoPerr", 6, 1 }, { "CTpOutEthFifoPerr", 5, 1 }, { "CTpInCplFifoPerr", 4, 1 }, { "CTpInTcpOpFifoPerr", 3, 1 }, { "CTpInTcpFifoPerr", 2, 1 }, { "CTpInIpFifoPerr", 1, 1 }, { "CTpInEthFifoPerr", 0, 1 }, { "TP_INT_CAUSE", 0x474, 0 }, { "FlmTxFlstEmpty", 30, 1 }, { "FlmRxFlstEmpty", 29, 1 }, { "FlmPerrSet", 28, 1 }, { "ProtocolSramPerr", 27, 1 }, { "ArpLutPerr", 26, 1 }, { "CmRcfOpPerr", 25, 1 }, { "CmCachePerr", 24, 1 }, { "CmRcfDataPerr", 23, 1 }, { "DbL2tLutPerr", 22, 1 }, { "DbTxTidPerr", 21, 1 }, { "DbExtPerr", 20, 1 }, { "DbOpPerr", 19, 1 }, { "TmCachePerr", 18, 1 }, { "ETpOutCplFifoPerr", 17, 1 }, { "ETpOutTcpFifoPerr", 16, 1 }, { "ETpOutIpFifoPerr", 15, 1 }, { "ETpOutEthFifoPerr", 14, 1 }, { "ETpInCplFifoPerr", 13, 1 }, { "ETpInTcpOptFifoPerr", 12, 1 }, { "ETpInTcpFifoPerr", 11, 1 }, { "ETpInIpFifoPerr", 10, 1 }, { "ETpInEthFifoPerr", 9, 1 }, { "CTpOutCplFifoPerr", 8, 1 }, { "CTpOutTcpFifoPerr", 7, 1 }, { "CTpOutIpFifoPerr", 6, 1 }, { "CTpOutEthFifoPerr", 5, 1 }, { "CTpInCplFifoPerr", 4, 1 }, { "CTpInTcpOpFifoPerr", 3, 1 }, { "CTpInTcpFifoPerr", 2, 1 }, { "CTpInIpFifoPerr", 1, 1 }, { "CTpInEthFifoPerr", 0, 1 }, { "TP_FLM_FREE_PS_CNT", 0x480, 0 }, { "FreePstructCount", 0, 21 }, { "TP_FLM_FREE_RX_CNT", 0x484, 0 }, { "FreeRxPageCount", 0, 21 }, { "TP_FLM_FREE_TX_CNT", 0x488, 0 }, { "FreeTxPageCount", 0, 21 }, { "TP_TM_HEAP_PUSH_CNT", 0x48c, 0 }, { "TP_TM_HEAP_POP_CNT", 0x490, 0 }, { "TP_TM_DACK_PUSH_CNT", 0x494, 0 }, { "TP_TM_DACK_POP_CNT", 0x498, 0 }, { "TP_TM_MOD_PUSH_CNT", 0x49c, 0 }, { "TP_MOD_POP_CNT", 0x4a0, 0 }, { "TP_TIMER_SEPARATOR", 0x4a4, 0 }, { "TP_DEBUG_SEL", 0x4a8, 0 }, { "TP_DEBUG_FLAGS", 0x4ac, 0 }, { "RxTimerDackFirst", 26, 1 }, { "RxTimerDack", 25, 1 }, { "RxTimerHeartbeat", 24, 1 }, { "RxPawsDrop", 23, 1 }, { "RxUrgDataDrop", 22, 1 }, { "RxFutureData", 21, 1 }, { "RxRcvRxmData", 20, 1 }, { "RxRcvOooDataFin", 19, 1 }, { "RxRcvOooData", 18, 1 }, { "RxRcvWndZero", 17, 1 }, { "RxRcvWndLtMss", 16, 1 }, { "TxDupAckInc", 11, 1 }, { "TxRxmUrg", 10, 1 }, { "TxRxmFin", 9, 1 }, { "TxRxmSyn", 8, 1 }, { "TxRxmNewReno", 7, 1 }, { "TxRxmFast", 6, 1 }, { "TxRxmTimer", 5, 1 }, { "TxRxmTimerKeepalive", 4, 1 }, { "TxRxmTimerPersist", 3, 1 }, { "TxRcvAdvShrunk", 2, 1 }, { "TxRcvAdvZero", 1, 1 }, { "TxRcvAdvLtMss", 0, 1 }, { "TP_PROXY_FLOW_CNTL", 0x4b0, 0 }, { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 }, { "EDropTunnel", 19, 1 }, { "CDropTunnel", 18, 1 }, { "EThreshold", 12, 6 }, { "CThreshold", 6, 6 }, { "TxThreshold", 0, 6 }, { "TP_TX_DROP_COUNT", 0x4bc, 0 }, { "TP_CLEAR_DEBUG", 0x4c0, 0 }, { "ClrDebug", 0, 1 }, { "TP_DEBUG_VEC", 0x4c4, 0 }, { "TP_DEBUG_VEC2", 0x4c8, 0 }, { "TP_DEBUG_REG_SEL", 0x4cc, 0 }, { "TP_DEBUG", 0x4d0, 0 }, { "TP_DBG_LA_CONFIG", 0x4d4, 0 }, { "TP_DBG_LA_DATAH", 0x4d8, 0 }, { "TP_DBG_LA_DATAL", 0x4dc, 0 }, { "TP_EMBED_OP_FIELD0", 0x4e8, 0 }, { "TP_EMBED_OP_FIELD1", 0x4ec, 0 }, { "TP_EMBED_OP_FIELD2", 0x4f0, 0 }, { "TP_EMBED_OP_FIELD3", 0x4f4, 0 }, { "TP_EMBED_OP_FIELD4", 0x4f8, 0 }, { "TP_EMBED_OP_FIELD5", 0x4fc, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_ulp2_rx_regs[] = { { "ULPRX_CTL", 0x500, 0 }, { "PCMD1Threshold", 24, 8 }, { "PCMD0Threshold", 16, 8 }, { "round_robin", 4, 1 }, { "RDMA_permissive_mode", 3, 1 }, { "PagePodME", 2, 1 }, { "IscsiTagTcb", 1, 1 }, { "TddpTagTcb", 0, 1 }, { "ULPRX_INT_ENABLE", 0x504, 0 }, { "DataSelFrameErr0", 7, 1 }, { "DataSelFrameErr1", 6, 1 }, { "PcmdMuxPerr", 5, 1 }, { "ArbFPerr", 4, 1 }, { "ArbPF0Perr", 3, 1 }, { "ArbPF1Perr", 2, 1 }, { "ParErrPcmd", 1, 1 }, { "ParErrData", 0, 1 }, { "ULPRX_INT_CAUSE", 0x508, 0 }, { "DataSelFrameErr0", 7, 1 }, { "DataSelFrameErr1", 6, 1 }, { "PcmdMuxPerr", 5, 1 }, { "ArbFPerr", 4, 1 }, { "ArbPF0Perr", 3, 1 }, { "ArbPF1Perr", 2, 1 }, { "ParErrPcmd", 1, 1 }, { "ParErrData", 0, 1 }, { "ULPRX_ISCSI_LLIMIT", 0x50c, 0 }, { "IscsiLlimit", 6, 26 }, { "ULPRX_ISCSI_ULIMIT", 0x510, 0 }, { "IscsiUlimit", 6, 26 }, { "ULPRX_ISCSI_TAGMASK", 0x514, 0 }, { "IscsiTagMask", 6, 26 }, { "ULPRX_ISCSI_PSZ", 0x518, 0 }, { "Hpz3", 24, 4 }, { "Hpz2", 16, 4 }, { "Hpz1", 8, 4 }, { "Hpz0", 0, 4 }, { "ULPRX_TDDP_LLIMIT", 0x51c, 0 }, { "TddpLlimit", 6, 26 }, { "ULPRX_TDDP_ULIMIT", 0x520, 0 }, { "TddpUlimit", 6, 26 }, { "ULPRX_TDDP_TAGMASK", 0x524, 0 }, { "TddpTagMask", 6, 26 }, { "ULPRX_TDDP_PSZ", 0x528, 0 }, { "Hpz3", 24, 4 }, { "Hpz2", 16, 4 }, { "Hpz1", 8, 4 }, { "Hpz0", 0, 4 }, { "ULPRX_STAG_LLIMIT", 0x52c, 0 }, { "ULPRX_STAG_ULIMIT", 0x530, 0 }, { "ULPRX_RQ_LLIMIT", 0x534, 0 }, { "ULPRX_RQ_ULIMIT", 0x538, 0 }, { "ULPRX_PBL_LLIMIT", 0x53c, 0 }, { "ULPRX_PBL_ULIMIT", 0x540, 0 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_ulp2_tx_regs[] = { { "ULPTX_CONFIG", 0x580, 0 }, { "CFG_CQE_SOP_MASK", 1, 1 }, { "CFG_RR_ARB", 0, 1 }, { "ULPTX_INT_ENABLE", 0x584, 0 }, { "cmd_fifo_perr_set1", 7, 1 }, { "cmd_fifo_perr_set0", 6, 1 }, { "lso_hdr_sram_perr_set1", 5, 1 }, { "lso_hdr_sram_perr_set0", 4, 1 }, { "imm_data_perr_set_ch1", 3, 1 }, { "imm_data_perr_set_ch0", 2, 1 }, { "Pbl_bound_err_ch1", 1, 1 }, { "Pbl_bound_err_ch0", 0, 1 }, { "ULPTX_INT_CAUSE", 0x588, 0 }, { "cmd_fifo_perr_set1", 7, 1 }, { "cmd_fifo_perr_set0", 6, 1 }, { "lso_hdr_sram_perr_set1", 5, 1 }, { "lso_hdr_sram_perr_set0", 4, 1 }, { "imm_data_perr_set_ch1", 3, 1 }, { "imm_data_perr_set_ch0", 2, 1 }, { "Pbl_bound_err_ch1", 1, 1 }, { "Pbl_bound_err_ch0", 0, 1 }, { "ULPTX_TPT_LLIMIT", 0x58c, 0 }, { "ULPTX_TPT_ULIMIT", 0x590, 0 }, { "ULPTX_PBL_LLIMIT", 0x594, 0 }, { "ULPTX_PBL_ULIMIT", 0x598, 0 }, { "ULPTX_CPL_ERR_OFFSET", 0x59c, 0 }, { "ULPTX_CPL_ERR_MASK", 0x5a0, 0 }, { "ULPTX_CPL_ERR_VALUE", 0x5a4, 0 }, { "ULPTX_CPL_PACK_SIZE", 0x5a8, 0 }, { "value", 24, 8 }, { "Ch1Size2", 24, 8 }, { "Ch1Size1", 16, 8 }, { "Ch0Size2", 8, 8 }, { "Ch0Size1", 0, 8 }, { "ULPTX_DMA_WEIGHT", 0x5ac, 0 }, { "D1_WEIGHT", 16, 16 }, { "D0_WEIGHT", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_pm1_rx_regs[] = { { "PM1_RX_CFG", 0x5c0, 0 }, { "PM1_RX_MODE", 0x5c4, 0 }, { "stat_channel", 1, 1 }, { "priority_ch", 0, 1 }, { "PM1_RX_STAT_CONFIG", 0x5c8, 0 }, { "PM1_RX_STAT_COUNT", 0x5cc, 0 }, { "PM1_RX_STAT_MSB", 0x5d0, 0 }, { "PM1_RX_STAT_LSB", 0x5d4, 0 }, { "PM1_RX_INT_ENABLE", 0x5d8, 0 }, { "zero_e_cmd_error", 18, 1 }, { "iespi0_fifo2x_Rx_framing_error", 17, 1 }, { "iespi1_fifo2x_Rx_framing_error", 16, 1 }, { "iespi0_Rx_framing_error", 15, 1 }, { "iespi1_Rx_framing_error", 14, 1 }, { "iespi0_Tx_framing_error", 13, 1 }, { "iespi1_Tx_framing_error", 12, 1 }, { "ocspi0_Rx_framing_error", 11, 1 }, { "ocspi1_Rx_framing_error", 10, 1 }, { "ocspi0_Tx_framing_error", 9, 1 }, { "ocspi1_Tx_framing_error", 8, 1 }, { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 }, { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 }, { "iespi_par_error", 3, 3 }, { "ocspi_par_error", 0, 3 }, { "PM1_RX_INT_CAUSE", 0x5dc, 0 }, { "zero_e_cmd_error", 18, 1 }, { "iespi0_fifo2x_Rx_framing_error", 17, 1 }, { "iespi1_fifo2x_Rx_framing_error", 16, 1 }, { "iespi0_Rx_framing_error", 15, 1 }, { "iespi1_Rx_framing_error", 14, 1 }, { "iespi0_Tx_framing_error", 13, 1 }, { "iespi1_Tx_framing_error", 12, 1 }, { "ocspi0_Rx_framing_error", 11, 1 }, { "ocspi1_Rx_framing_error", 10, 1 }, { "ocspi0_Tx_framing_error", 9, 1 }, { "ocspi1_Tx_framing_error", 8, 1 }, { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 }, { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 }, { "iespi_par_error", 3, 3 }, { "ocspi_par_error", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_pm1_tx_regs[] = { { "PM1_TX_CFG", 0x5e0, 0 }, { "PM1_TX_MODE", 0x5e4, 0 }, { "stat_channel", 1, 1 }, { "priority_ch", 0, 1 }, { "PM1_TX_STAT_CONFIG", 0x5e8, 0 }, { "PM1_TX_STAT_COUNT", 0x5ec, 0 }, { "PM1_TX_STAT_MSB", 0x5f0, 0 }, { "PM1_TX_STAT_LSB", 0x5f4, 0 }, { "PM1_TX_INT_ENABLE", 0x5f8, 0 }, { "zero_c_cmd_error", 18, 1 }, { "icspi0_fifo2x_Rx_framing_error", 17, 1 }, { "icspi1_fifo2x_Rx_framing_error", 16, 1 }, { "icspi0_Rx_framing_error", 15, 1 }, { "icspi1_Rx_framing_error", 14, 1 }, { "icspi0_Tx_framing_error", 13, 1 }, { "icspi1_Tx_framing_error", 12, 1 }, { "oespi0_Rx_framing_error", 11, 1 }, { "oespi1_Rx_framing_error", 10, 1 }, { "oespi0_Tx_framing_error", 9, 1 }, { "oespi1_Tx_framing_error", 8, 1 }, { "oespi0_ofifo2x_Tx_framing_error", 7, 1 }, { "oespi1_ofifo2x_Tx_framing_error", 6, 1 }, { "icspi_par_error", 3, 3 }, { "oespi_par_error", 0, 3 }, { "PM1_TX_INT_CAUSE", 0x5fc, 0 }, { "zero_c_cmd_error", 18, 1 }, { "icspi0_fifo2x_Rx_framing_error", 17, 1 }, { "icspi1_fifo2x_Rx_framing_error", 16, 1 }, { "icspi0_Rx_framing_error", 15, 1 }, { "icspi1_Rx_framing_error", 14, 1 }, { "icspi0_Tx_framing_error", 13, 1 }, { "icspi1_Tx_framing_error", 12, 1 }, { "oespi0_Rx_framing_error", 11, 1 }, { "oespi1_Rx_framing_error", 10, 1 }, { "oespi0_Tx_framing_error", 9, 1 }, { "oespi1_Tx_framing_error", 8, 1 }, { "oespi0_ofifo2x_Tx_framing_error", 7, 1 }, { "oespi1_ofifo2x_Tx_framing_error", 6, 1 }, { "icspi_par_error", 3, 3 }, { "oespi_par_error", 0, 3 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_mps0_regs[] = { { "MPS_CFG", 0x600, 0 }, { "EnForcePkt", 11, 1 }, { "SGETPQid", 8, 3 }, { "TPRxPortSize", 7, 1 }, { "TPTxPort1Size", 6, 1 }, { "TPTxPort0Size", 5, 1 }, { "TPRxPortEn", 4, 1 }, { "TPTxPort1En", 3, 1 }, { "TPTxPort0En", 2, 1 }, { "Port1Active", 1, 1 }, { "Port0Active", 0, 1 }, { "MPS_DRR_CFG1", 0x604, 0 }, { "RldWtTPD1", 11, 11 }, { "RldWtTPD0", 0, 11 }, { "MPS_DRR_CFG2", 0x608, 0 }, { "RldWtTotal", 0, 12 }, { "MPS_MCA_STATUS", 0x60c, 0 }, { "MCAPktCnt", 12, 20 }, { "MCADepth", 0, 12 }, { "MPS_TX0_TP_CNT", 0x610, 0 }, { "TX0TPDisCnt", 24, 8 }, { "TX0TPCnt", 0, 24 }, { "MPS_TX1_TP_CNT", 0x614, 0 }, { "TX1TPDisCnt", 24, 8 }, { "TX1TPCnt", 0, 24 }, { "MPS_RX_TP_CNT", 0x618, 0 }, { "RXTPDisCnt", 24, 8 }, { "RXTPCnt", 0, 24 }, { "MPS_INT_ENABLE", 0x61c, 0 }, { "MCAParErrEnb", 6, 3 }, { "RXTpParErrEnb", 4, 2 }, { "TX1TpParErrEnb", 2, 2 }, { "TX0TpParErrEnb", 0, 2 }, { "MPS_INT_CAUSE", 0x620, 0 }, { "MCAParErr", 6, 3 }, { "RXTpParErr", 4, 2 }, { "TX1TpParErr", 2, 2 }, { "TX0TpParErr", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_cpl_switch_regs[] = { { "CPL_SWITCH_CNTRL", 0x640, 0 }, { "cpl_pkt_tid", 8, 24 }, { "cim_to_up_full_size", 4, 1 }, { "cpu_no_3F_CIM_enable", 3, 1 }, { "switch_table_enable", 2, 1 }, { "sge_enable", 1, 1 }, { "cim_enable", 0, 1 }, { "CPL_SWITCH_TBL_IDX", 0x644, 0 }, { "switch_tbl_idx", 0, 4 }, { "CPL_SWITCH_TBL_DATA", 0x648, 0 }, { "CPL_SWITCH_ZERO_ERROR", 0x64c, 0 }, { "zero_cmd", 0, 8 }, { "CPL_INTR_ENABLE", 0x650, 0 }, { "cim_op_map_perr", 5, 1 }, { "cim_ovfl_error", 4, 1 }, { "tp_framing_error", 3, 1 }, { "sge_framing_error", 2, 1 }, { "cim_framing_error", 1, 1 }, { "zero_switch_error", 0, 1 }, { "CPL_INTR_CAUSE", 0x654, 0 }, { "cim_op_map_perr", 5, 1 }, { "cim_ovfl_error", 4, 1 }, { "tp_framing_error", 3, 1 }, { "sge_framing_error", 2, 1 }, { "cim_framing_error", 1, 1 }, { "zero_switch_error", 0, 1 }, { "CPL_MAP_TBL_IDX", 0x658, 0 }, { "cpl_map_tbl_idx", 0, 8 }, { "CPL_MAP_TBL_DATA", 0x65c, 0 }, { "cpl_map_tbl_data", 0, 8 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_smb0_regs[] = { { "SMB_GLOBAL_TIME_CFG", 0x660, 0 }, { "LADbgWrPtr", 24, 8 }, { "LADbgRdPtr", 16, 8 }, { "LADbgEn", 13, 1 }, { "MacroCntCfg", 8, 5 }, { "MicroCntCfg", 0, 8 }, { "SMB_MST_TIMEOUT_CFG", 0x664, 0 }, { "DebugSelH", 28, 4 }, { "DebugSelL", 24, 4 }, { "MstTimeOutCfg", 0, 24 }, { "SMB_MST_CTL_CFG", 0x668, 0 }, { "MstFifoDbg", 31, 1 }, { "MstFifoDbgClr", 30, 1 }, { "MstRxByteCfg", 12, 6 }, { "MstTxByteCfg", 6, 6 }, { "MstReset", 1, 1 }, { "MstCtlEn", 0, 1 }, { "SMB_MST_CTL_STS", 0x66c, 0 }, { "MstRxByteCnt", 12, 6 }, { "MstTxByteCnt", 6, 6 }, { "MstBusySts", 0, 1 }, { "SMB_MST_TX_FIFO_RDWR", 0x670, 0 }, { "SMB_MST_RX_FIFO_RDWR", 0x674, 0 }, { "SMB_SLV_TIMEOUT_CFG", 0x678, 0 }, { "SlvTimeOutCfg", 0, 24 }, { "SMB_SLV_CTL_CFG", 0x67c, 0 }, { "SlvFifoDbg", 31, 1 }, { "SlvFifoDbgClr", 30, 1 }, { "SlvAddrCfg", 4, 7 }, { "SlvAlrtSet", 2, 1 }, { "SlvReset", 1, 1 }, { "SlvCtlEn", 0, 1 }, { "SMB_SLV_CTL_STS", 0x680, 0 }, { "SlvFifoTxCnt", 12, 6 }, { "SlvFifoCnt", 6, 6 }, { "SlvAlrtSts", 2, 1 }, { "SlvBusySts", 0, 1 }, { "SMB_SLV_FIFO_RDWR", 0x684, 0 }, { "SMB_SLV_CMD_FIFO_RDWR", 0x688, 0 }, { "SMB_INT_ENABLE", 0x68c, 0 }, { "SlvTimeOutIntEn", 7, 1 }, { "SlvErrIntEn", 6, 1 }, { "SlvDoneIntEn", 5, 1 }, { "SlvRxRdyIntEn", 4, 1 }, { "MstTimeOutIntEn", 3, 1 }, { "MstNAckIntEn", 2, 1 }, { "MstLostArbIntEn", 1, 1 }, { "MstDoneIntEn", 0, 1 }, { "SMB_INT_CAUSE", 0x690, 0 }, { "SlvTimeOutInt", 7, 1 }, { "SlvErrInt", 6, 1 }, { "SlvDoneInt", 5, 1 }, { "SlvRxRdyInt", 4, 1 }, { "MstTimeOutInt", 3, 1 }, { "MstNAckInt", 2, 1 }, { "MstLostArbInt", 1, 1 }, { "MstDoneInt", 0, 1 }, { "SMB_DEBUG_DATA", 0x694, 0 }, { "DebugDataH", 16, 16 }, { "DebugDataL", 0, 16 }, { "SMB_DEBUG_LA", 0x69c, 0 }, { "DebugLAReqAddr", 0, 10 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_i2cm0_regs[] = { { "I2C_CFG", 0x6a0, 0 }, { "ClkDiv", 0, 12 }, { "I2C_DATA", 0x6a4, 0 }, { "Data", 0, 8 }, { "I2C_OP", 0x6a8, 0 }, { "Busy", 31, 1 }, { "Ack", 30, 1 }, { "Cont", 1, 1 }, { "Op", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_mi1_regs[] = { { "MI1_CFG", 0x6b0, 0 }, { "ClkDiv", 5, 8 }, { "St", 3, 2 }, { "PreEn", 2, 1 }, { "MDIInv", 1, 1 }, { "MDIEn", 0, 1 }, { "MI1_ADDR", 0x6b4, 0 }, { "PhyAddr", 5, 5 }, { "RegAddr", 0, 5 }, { "MI1_DATA", 0x6b8, 0 }, { "Data", 0, 16 }, { "MI1_OP", 0x6bc, 0 }, { "Busy", 31, 1 }, { "Inc", 2, 1 }, { "Op", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_jm1_regs[] = { { "JM_CFG", 0x6c0, 0 }, { "ClkDiv", 2, 8 }, { "TRst", 1, 1 }, { "En", 0, 1 }, { "JM_MODE", 0x6c4, 0 }, { "JM_DATA", 0x6c8, 0 }, { "JM_OP", 0x6cc, 0 }, { "Busy", 31, 1 }, { "Cnt", 0, 5 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_sf1_regs[] = { { "SF_DATA", 0x6d8, 0 }, { "SF_OP", 0x6dc, 0 }, { "Busy", 31, 1 }, { "Cont", 3, 1 }, { "ByteCnt", 1, 2 }, { "Op", 0, 1 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_pl3_regs[] = { { "PL_INT_ENABLE0", 0x6e0, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_CAUSE0", 0x6e4, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_ENABLE1", 0x6e8, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_INT_CAUSE1", 0x6ec, 0 }, { "SW", 25, 1 }, { "EXT", 24, 1 }, { "T3DBG", 23, 1 }, { "XGMAC0_1", 20, 1 }, { "XGMAC0_0", 19, 1 }, { "MC5A", 18, 1 }, { "SF1", 17, 1 }, { "SMB0", 15, 1 }, { "I2CM0", 14, 1 }, { "MI1", 13, 1 }, { "CPL_SWITCH", 12, 1 }, { "MPS0", 11, 1 }, { "PM1_TX", 10, 1 }, { "PM1_RX", 9, 1 }, { "ULP2_TX", 8, 1 }, { "ULP2_RX", 7, 1 }, { "TP1", 6, 1 }, { "CIM", 5, 1 }, { "MC7_CM", 4, 1 }, { "MC7_PMTX", 3, 1 }, { "MC7_PMRX", 2, 1 }, { "PCIM0", 1, 1 }, { "SGE3", 0, 1 }, { "PL_RST", 0x6f0, 0 }, { "FatalPerrEn", 4, 1 }, { "SWInt1", 3, 1 }, { "SWInt0", 2, 1 }, { "CRstWrm", 1, 1 }, { "CRstWrmMode", 0, 1 }, { "PL_REV", 0x6f4, 0 }, { "Rev", 0, 4 }, { "PL_CLI", 0x6f8, 0 }, { "PL_LCK", 0x6fc, 0 }, { "Lck", 0, 2 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_mc5a_regs[] = { { "MC5_BUF_CONFIG", 0x700, 0 }, { "term300_240", 31, 1 }, { "term150", 30, 1 }, { "term60", 29, 1 }, { "gddriii", 28, 1 }, { "gddrii", 27, 1 }, { "gddri", 26, 1 }, { "read", 25, 1 }, { "imp_set_update", 24, 1 }, { "cal_update", 23, 1 }, { "cal_busy", 22, 1 }, { "cal_error", 21, 1 }, { "sgl_cal_en", 20, 1 }, { "imp_upd_mode", 19, 1 }, { "imp_sel", 18, 1 }, { "man_pu", 15, 3 }, { "man_pd", 12, 3 }, { "cal_pu", 9, 3 }, { "cal_pd", 6, 3 }, { "set_pu", 3, 3 }, { "set_pd", 0, 3 }, { "MC5_DB_CONFIG", 0x704, 0 }, { "TMCfgWrLock", 31, 1 }, { "TMTypeHi", 30, 1 }, { "TMPartSize", 28, 2 }, { "TMType", 26, 2 }, { "TMPartCount", 24, 2 }, { "nLIP", 18, 6 }, { "COMPEN", 17, 1 }, { "BUILD", 16, 1 }, { "FilterEn", 11, 1 }, { "CLIPUpdate", 10, 1 }, { "TM_IO_PDOWN", 9, 1 }, { "SYNMode", 7, 2 }, { "PRTYEN", 6, 1 }, { "MBUSEN", 5, 1 }, { "DBGIEN", 4, 1 }, { "TcmCfgOvr", 3, 1 }, { "TMRDY", 2, 1 }, { "TMRST", 1, 1 }, { "TMMode", 0, 1 }, { "MC5_MISC", 0x708, 0 }, { "LIP_Cmp_Unavailable", 0, 4 }, { "MC5_DB_ROUTING_TABLE_INDEX", 0x70c, 0 }, { "RTINDX", 0, 22 }, { "MC5_DB_FILTER_TABLE", 0x710, 0 }, { "SRINDX", 0, 22 }, { "MC5_DB_SERVER_INDEX", 0x714, 0 }, { "SRINDX", 0, 22 }, { "MC5_DB_LIP_RAM_ADDR", 0x718, 0 }, { "RAMWR", 8, 1 }, { "RAMADDR", 0, 6 }, { "MC5_DB_LIP_RAM_DATA", 0x71c, 0 }, { "MC5_DB_RSP_LATENCY", 0x720, 0 }, { "RDLAT", 16, 5 }, { "LRNLAT", 8, 5 }, { "SRCHLAT", 0, 5 }, { "MC5_DB_PARITY_LATENCY", 0x724, 0 }, { "PARLAT", 0, 4 }, { "MC5_DB_WR_LRN_VERIFY", 0x728, 0 }, { "VWVEREN", 2, 1 }, { "LRNVEREN", 1, 1 }, { "POVEREN", 0, 1 }, { "MC5_DB_PART_ID_INDEX", 0x72c, 0 }, { "IDINDEX", 0, 4 }, { "MC5_DB_RESET_MAX", 0x730, 0 }, { "RSTMAX", 0, 4 }, { "MC5_DB_ACT_CNT", 0x734, 0 }, { "ACTCNT", 0, 20 }, { "MC5_DB_CLIP_MAP", 0x738, 0 }, { "CLIPMapOp", 31, 1 }, { "CLIPMapVal", 16, 6 }, { "CLIPMapAddr", 0, 6 }, { "MC5_DB_SIZE", 0x73c, 0 }, { "MC5_DB_INT_ENABLE", 0x740, 0 }, { "MsgSel", 28, 4 }, { "DelActEmpty", 18, 1 }, { "DispQParErr", 17, 1 }, { "ReqQParErr", 16, 1 }, { "UnknownCmd", 15, 1 }, { "SYNCookieOff", 11, 1 }, { "SYNCookieBad", 10, 1 }, { "SYNCookie", 9, 1 }, { "NFASrchFail", 8, 1 }, { "ActRgnFull", 7, 1 }, { "ParityErr", 6, 1 }, { "LIPMiss", 5, 1 }, { "LIP0", 4, 1 }, { "Miss", 3, 1 }, { "RoutingHit", 2, 1 }, { "ActiveHit", 1, 1 }, { "ActiveOutHit", 0, 1 }, { "MC5_DB_INT_CAUSE", 0x744, 0 }, { "DelActEmpty", 18, 1 }, { "DispQParErr", 17, 1 }, { "ReqQParErr", 16, 1 }, { "UnknownCmd", 15, 1 }, { "SYNCookieOff", 11, 1 }, { "SYNCookieBad", 10, 1 }, { "SYNCookie", 9, 1 }, { "NFASrchFail", 8, 1 }, { "ActRgnFull", 7, 1 }, { "ParityErr", 6, 1 }, { "LIPMiss", 5, 1 }, { "LIP0", 4, 1 }, { "Miss", 3, 1 }, { "RoutingHit", 2, 1 }, { "ActiveHit", 1, 1 }, { "ActiveOutHit", 0, 1 }, { "MC5_DB_INT_TID", 0x748, 0 }, { "INTTID", 0, 20 }, { "MC5_DB_INT_PTID", 0x74c, 0 }, { "INTPTID", 0, 20 }, { "MC5_DB_DBGI_CONFIG", 0x774, 0 }, { "WRReqSize", 22, 10 }, { "SADRSel", 4, 1 }, { "CMDMode", 0, 3 }, { "MC5_DB_DBGI_REQ_CMD", 0x778, 0 }, { "MBusCmd", 0, 4 }, { "IDTCmdHi", 11, 3 }, { "IDTCmdLo", 0, 4 }, { "IDTCmd", 0, 20 }, { "LCMDB", 16, 11 }, { "LCMDA", 0, 11 }, { "MC5_DB_DBGI_REQ_ADDR0", 0x77c, 0 }, { "MC5_DB_DBGI_REQ_ADDR1", 0x780, 0 }, { "MC5_DB_DBGI_REQ_ADDR2", 0x784, 0 }, { "DBGIReqAdrHi", 0, 8 }, { "MC5_DB_DBGI_REQ_DATA0", 0x788, 0 }, { "MC5_DB_DBGI_REQ_DATA1", 0x78c, 0 }, { "MC5_DB_DBGI_REQ_DATA2", 0x790, 0 }, { "MC5_DB_DBGI_REQ_DATA3", 0x794, 0 }, { "MC5_DB_DBGI_REQ_DATA4", 0x798, 0 }, { "DBGIReqData4", 0, 16 }, { "MC5_DB_DBGI_REQ_MASK0", 0x79c, 0 }, { "MC5_DB_DBGI_REQ_MASK1", 0x7a0, 0 }, { "MC5_DB_DBGI_REQ_MASK2", 0x7a4, 0 }, { "MC5_DB_DBGI_REQ_MASK3", 0x7a8, 0 }, { "MC5_DB_DBGI_REQ_MASK4", 0x7ac, 0 }, { "DBGIReqMsk4", 0, 16 }, { "MC5_DB_DBGI_RSP_STATUS", 0x7b0, 0 }, { "DBGIRspMsg", 8, 4 }, { "DBGIRspMsgVld", 2, 1 }, { "DBGIRspHit", 1, 1 }, { "DBGIRspValid", 0, 1 }, { "MC5_DB_DBGI_RSP_DATA0", 0x7b4, 0 }, { "MC5_DB_DBGI_RSP_DATA1", 0x7b8, 0 }, { "MC5_DB_DBGI_RSP_DATA2", 0x7bc, 0 }, { "MC5_DB_DBGI_RSP_DATA3", 0x7c0, 0 }, { "MC5_DB_DBGI_RSP_DATA4", 0x7c4, 0 }, { "DBGIRspData3", 0, 16 }, { "MC5_DB_DBGI_RSP_LAST_CMD", 0x7c8, 0 }, { "LastCmdB", 16, 11 }, { "LastCmdA", 0, 11 }, { "MC5_DB_POPEN_DATA_WR_CMD", 0x7cc, 0 }, { "PO_DWR", 0, 20 }, { "MC5_DB_POPEN_MASK_WR_CMD", 0x7d0, 0 }, { "PO_MWR", 0, 20 }, { "MC5_DB_AOPEN_SRCH_CMD", 0x7d4, 0 }, { "AO_SRCH", 0, 20 }, { "MC5_DB_AOPEN_LRN_CMD", 0x7d8, 0 }, { "AO_LRN", 0, 20 }, { "MC5_DB_SYN_SRCH_CMD", 0x7dc, 0 }, { "SYN_SRCH", 0, 20 }, { "MC5_DB_SYN_LRN_CMD", 0x7e0, 0 }, { "SYN_LRN", 0, 20 }, { "MC5_DB_ACK_SRCH_CMD", 0x7e4, 0 }, { "ACK_SRCH", 0, 20 }, { "MC5_DB_ACK_LRN_CMD", 0x7e8, 0 }, { "ACK_LRN", 0, 20 }, { "MC5_DB_ILOOKUP_CMD", 0x7ec, 0 }, { "I_SRCH", 0, 20 }, { "MC5_DB_ELOOKUP_CMD", 0x7f0, 0 }, { "E_SRCH", 0, 20 }, { "MC5_DB_DATA_WRITE_CMD", 0x7f4, 0 }, { "Write", 0, 20 }, { "MC5_DB_DATA_READ_CMD", 0x7f8, 0 }, { "ReadCmd", 0, 20 }, { "MC5_DB_MASK_WRITE_CMD", 0x7fc, 0 }, { "MaskWr", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_xgmac0_0_regs[] = { { "XGM_TX_CTRL", 0x800, 0 }, { "SendPause", 2, 1 }, { "SendZeroPause", 1, 1 }, { "TxEn", 0, 1 }, { "XGM_TX_CFG", 0x804, 0 }, { "CfgClkSpeed", 2, 3 }, { "StretchMode", 1, 1 }, { "TxPauseEn", 0, 1 }, { "XGM_TX_PAUSE_QUANTA", 0x808, 0 }, { "TxPauseQuanta", 0, 16 }, { "XGM_RX_CTRL", 0x80c, 0 }, { "RxEn", 0, 1 }, { "XGM_RX_CFG", 0x810, 0 }, { "Con802_3Preamble", 12, 1 }, { "EnNon802_3Preamble", 11, 1 }, { "CopyPreamble", 10, 1 }, { "DisPauseFrames", 9, 1 }, { "En1536BFrames", 8, 1 }, { "EnJumbo", 7, 1 }, { "RmFCS", 6, 1 }, { "DisNonVlan", 5, 1 }, { "EnExtMatch", 4, 1 }, { "EnHashUcast", 3, 1 }, { "EnHashMcast", 2, 1 }, { "DisBCast", 1, 1 }, { "CopyAllFrames", 0, 1 }, { "XGM_RX_HASH_LOW", 0x814, 0 }, { "XGM_RX_HASH_HIGH", 0x818, 0 }, { "XGM_RX_EXACT_MATCH_LOW_1", 0x81c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_1", 0x820, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_2", 0x824, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_2", 0x828, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_3", 0x82c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_3", 0x830, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_4", 0x834, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_4", 0x838, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_5", 0x83c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_5", 0x840, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_6", 0x844, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_6", 0x848, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_7", 0x84c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_7", 0x850, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_8", 0x854, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_8", 0x858, 0 }, { "address_high", 0, 16 }, { "XGM_RX_TYPE_MATCH_1", 0x85c, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_2", 0x860, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_3", 0x864, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_4", 0x868, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_INT_STATUS", 0x86c, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_MASK", 0x870, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_ENABLE", 0x874, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_DISABLE", 0x878, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_TX_PAUSE_TIMER", 0x87c, 0 }, { "CurPauseTimer", 0, 16 }, { "XGM_STAT_CTRL", 0x880, 0 }, { "ReadSnpShot", 4, 1 }, { "TakeSnpShot", 3, 1 }, { "ClrStats", 2, 1 }, { "IncrStats", 1, 1 }, { "EnTestModeWr", 0, 1 }, { "XGM_RXFIFO_CFG", 0x884, 0 }, { "RxFIFO_empty", 31, 1 }, { "RxFIFO_full", 30, 1 }, { "RxFIFOPauseHWM", 17, 12 }, { "RxFIFOPauseLWM", 5, 12 }, { "ForcedPause", 4, 1 }, { "ExternLoopback", 3, 1 }, { "RxByteSwap", 2, 1 }, { "RxStrFrwrd", 1, 1 }, { "DisErrFrames", 0, 1 }, { "XGM_TXFIFO_CFG", 0x888, 0 }, { "TxFIFO_empty", 31, 1 }, { "TxFIFO_full", 30, 1 }, { "UnderunFix", 22, 1 }, { "EnDropPkt", 21, 1 }, { "TxIPG", 13, 8 }, { "TxFIFOThresh", 4, 9 }, { "InternLoopback", 3, 1 }, { "TxByteSwap", 2, 1 }, { "DisCRC", 1, 1 }, { "DisPreAmble", 0, 1 }, { "XGM_SLOW_TIMER", 0x88c, 0 }, { "PauseSlowTimerEn", 31, 1 }, { "PauseSlowTimer", 0, 20 }, { "XGM_PAUSE_TIMER", 0x890, 0 }, { "PauseTimer", 0, 20 }, { "XGM_XAUI_PCS_TEST", 0x894, 0 }, { "TestPattern", 1, 2 }, { "EnTest", 0, 1 }, { "XGM_RGMII_CTRL", 0x898, 0 }, { "PhAlignFIFOThresh", 1, 2 }, { "TxClk90Shift", 0, 1 }, { "XGM_RGMII_IMP", 0x89c, 0 }, { "CalReset", 8, 1 }, { "CalUpdate", 7, 1 }, { "ImpSetUpdate", 6, 1 }, { "RGMIIImpPD", 3, 3 }, { "RGMIIImpPU", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE", 0x8a8, 0 }, { "RxMaxFramerSize", 17, 14 }, { "RxEnErrorGather", 16, 1 }, { "RxEnSingleFlit", 15, 1 }, { "RxEnFramer", 14, 1 }, { "RxMaxPktSize", 0, 14 }, { "XGM_RESET_CTRL", 0x8ac, 0 }, { "XGMAC_STOP_EN", 4, 1 }, { "XG2G_Reset_", 3, 1 }, { "RGMII_Reset_", 2, 1 }, { "PCS_Reset_", 1, 1 }, { "MAC_Reset_", 0, 1 }, { "XGM_XAUI1G_CTRL", 0x8b0, 0 }, { "XAUI1GLinkId", 0, 2 }, { "XGM_SERDES_LANE_CTRL", 0x8b4, 0 }, { "LaneReversal", 8, 1 }, { "TxPolarity", 4, 4 }, { "RxPolarity", 0, 4 }, { "XGM_PORT_CFG", 0x8b8, 0 }, { "SafeSpeedChange", 4, 1 }, { "ClkDivReset_", 3, 1 }, { "PortSpeed", 1, 2 }, { "EnRGMII", 0, 1 }, { "XGM_EPIO_DATA0", 0x8c0, 0 }, { "XGM_EPIO_DATA1", 0x8c4, 0 }, { "XGM_EPIO_DATA2", 0x8c8, 0 }, { "XGM_EPIO_DATA3", 0x8cc, 0 }, { "XGM_EPIO_OP", 0x8d0, 0 }, { "PIO_Ready", 31, 1 }, { "PIO_WrRd", 24, 1 }, { "PIO_Address", 0, 8 }, { "XGM_INT_ENABLE", 0x8d4, 0 }, { "XAUIPCSDECErr", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_INT_CAUSE", 0x8d8, 0 }, { "XAUIPCSDECErr", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_XAUI_ACT_CTRL", 0x8dc, 0 }, { "TxEn", 1, 1 }, { "RxEn", 0, 1 }, { "XGM_SERDES_CTRL0", 0x8e0, 0 }, { "IntSerLPBK3", 27, 1 }, { "IntSerLPBK2", 26, 1 }, { "IntSerLPBK1", 25, 1 }, { "IntSerLPBK0", 24, 1 }, { "Reset3", 23, 1 }, { "Reset2", 22, 1 }, { "Reset1", 21, 1 }, { "Reset0", 20, 1 }, { "Pwrdn3", 19, 1 }, { "Pwrdn2", 18, 1 }, { "Pwrdn1", 17, 1 }, { "Pwrdn0", 16, 1 }, { "ResetPLL23", 15, 1 }, { "ResetPLL01", 14, 1 }, { "PW23", 12, 2 }, { "PW01", 10, 2 }, { "Deq", 6, 4 }, { "Dtx", 2, 4 }, { "LoDrv", 1, 1 }, { "HiDrv", 0, 1 }, { "XGM_SERDES_CTRL1", 0x8e4, 0 }, { "FmOffset3", 19, 5 }, { "FmOffsetEn3", 18, 1 }, { "FmOffset2", 13, 5 }, { "FmOffsetEn2", 12, 1 }, { "FmOffset1", 7, 5 }, { "FmOffsetEn1", 6, 1 }, { "FmOffset0", 1, 5 }, { "FmOffsetEn0", 0, 1 }, { "XGM_SERDES_CTRL2", 0x8e8, 0 }, { "DnIn3", 11, 1 }, { "UpIn3", 10, 1 }, { "RxSlave3", 9, 1 }, { "DnIn2", 8, 1 }, { "UpIn2", 7, 1 }, { "RxSlave2", 6, 1 }, { "DnIn1", 5, 1 }, { "UpIn1", 4, 1 }, { "RxSlave1", 3, 1 }, { "DnIn0", 2, 1 }, { "UpIn0", 1, 1 }, { "RxSlave0", 0, 1 }, { "XGM_SERDES_CTRL3", 0x8ec, 0 }, { "ExtBISTChkErrClr3", 31, 1 }, { "ExtBISTChkEn3", 30, 1 }, { "ExtBISTGenEn3", 29, 1 }, { "ExtBISTPat3", 26, 3 }, { "ExtParReset3", 25, 1 }, { "ExtParLPBK3", 24, 1 }, { "ExtBISTChkErrClr2", 23, 1 }, { "ExtBISTChkEn2", 22, 1 }, { "ExtBISTGenEn2", 21, 1 }, { "ExtBISTPat2", 18, 3 }, { "ExtParReset2", 17, 1 }, { "ExtParLPBK2", 16, 1 }, { "ExtBISTChkErrClr1", 15, 1 }, { "ExtBISTChkEn1", 14, 1 }, { "ExtBISTGenEn1", 13, 1 }, { "ExtBISTPat1", 10, 3 }, { "ExtParReset1", 9, 1 }, { "ExtParLPBK1", 8, 1 }, { "ExtBISTChkErrClr0", 7, 1 }, { "ExtBISTChkEn0", 6, 1 }, { "ExtBISTGenEn0", 5, 1 }, { "ExtBISTPat0", 2, 3 }, { "ExtParReset0", 1, 1 }, { "ExtParLPBK0", 0, 1 }, { "XGM_SERDES_STAT0", 0x8f0, 0 }, { "ExtBISTChkErrCnt0", 4, 24 }, { "ExtBISTChkFmd0", 3, 1 }, { "LowSigForceEn0", 2, 1 }, { "LowSigForceValue0", 1, 1 }, { "LowSig0", 0, 1 }, { "XGM_SERDES_STAT1", 0x8f4, 0 }, { "ExtBISTChkErrCnt1", 4, 24 }, { "ExtBISTChkFmd1", 3, 1 }, { "LowSigForceEn1", 2, 1 }, { "LowSigForceValue1", 1, 1 }, { "LowSig1", 0, 1 }, { "XGM_SERDES_STAT2", 0x8f8, 0 }, { "ExtBISTChkErrCnt2", 4, 24 }, { "ExtBISTChkFmd2", 3, 1 }, { "LowSigForceEn2", 2, 1 }, { "LowSigForceValue2", 1, 1 }, { "LowSig2", 0, 1 }, { "XGM_SERDES_STAT3", 0x8fc, 0 }, { "ExtBISTChkErrCnt3", 4, 24 }, { "ExtBISTChkFmd3", 3, 1 }, { "LowSigForceEn3", 2, 1 }, { "LowSigForceValue3", 1, 1 }, { "LowSig3", 0, 1 }, { "XGM_STAT_TX_BYTE_LOW", 0x900, 0 }, { "XGM_STAT_TX_BYTE_HIGH", 0x904, 0 }, { "TxBytes_high", 0, 13 }, { "XGM_STAT_TX_FRAME_LOW", 0x908, 0 }, { "XGM_STAT_TX_FRAME_HIGH", 0x90c, 0 }, { "TxFrames_high", 0, 4 }, { "XGM_STAT_TX_BCAST", 0x910, 0 }, { "XGM_STAT_TX_MCAST", 0x914, 0 }, { "XGM_STAT_TX_PAUSE", 0x918, 0 }, { "XGM_STAT_TX_64B_FRAMES", 0x91c, 0 }, { "XGM_STAT_TX_65_127B_FRAMES", 0x920, 0 }, { "XGM_STAT_TX_128_255B_FRAMES", 0x924, 0 }, { "XGM_STAT_TX_256_511B_FRAMES", 0x928, 0 }, { "XGM_STAT_TX_512_1023B_FRAMES", 0x92c, 0 }, { "XGM_STAT_TX_1024_1518B_FRAMES", 0x930, 0 }, { "XGM_STAT_TX_1519_MAXB_FRAMES", 0x934, 0 }, { "XGM_STAT_TX_ERR_FRAMES", 0x938, 0 }, { "XGM_STAT_RX_BYTES_LOW", 0x93c, 0 }, { "XGM_STAT_RX_BYTES_HIGH", 0x940, 0 }, { "RxBytes_high", 0, 13 }, { "XGM_STAT_RX_FRAMES_LOW", 0x944, 0 }, { "XGM_STAT_RX_FRAMES_HIGH", 0x948, 0 }, { "RxFrames_high", 0, 4 }, { "XGM_STAT_RX_BCAST_FRAMES", 0x94c, 0 }, { "XGM_STAT_RX_MCAST_FRAMES", 0x950, 0 }, { "XGM_STAT_RX_PAUSE_FRAMES", 0x954, 0 }, { "RxPauseFrames", 0, 16 }, { "XGM_STAT_RX_64B_FRAMES", 0x958, 0 }, { "XGM_STAT_RX_65_127B_FRAMES", 0x95c, 0 }, { "XGM_STAT_RX_128_255B_FRAMES", 0x960, 0 }, { "XGM_STAT_RX_256_511B_FRAMES", 0x964, 0 }, { "XGM_STAT_RX_512_1023B_FRAMES", 0x968, 0 }, { "XGM_STAT_RX_1024_1518B_FRAMES", 0x96c, 0 }, { "XGM_STAT_RX_1519_MAXB_FRAMES", 0x970, 0 }, { "XGM_STAT_RX_SHORT_FRAMES", 0x974, 0 }, { "RxShortFrames", 0, 16 }, { "XGM_STAT_RX_OVERSIZE_FRAMES", 0x978, 0 }, { "RxOversizeFrames", 0, 16 }, { "XGM_STAT_RX_JABBER_FRAMES", 0x97c, 0 }, { "RxJabberFrames", 0, 16 }, { "XGM_STAT_RX_CRC_ERR_FRAMES", 0x980, 0 }, { "RxCRCErrFrames", 0, 16 }, { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0x984, 0 }, { "RxLengthErrFrames", 0, 16 }, { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0x988, 0 }, { "RxSymCodeErrFrames", 0, 16 }, { "XGM_XAUI_PCS_ERR", 0x998, 0 }, { "PCS_SyncStatus", 5, 4 }, { "PCS_CTCFIFOErr", 1, 4 }, { "PCS_NotAligned", 0, 1 }, { "XGM_RGMII_STATUS", 0x99c, 0 }, { "GMIIDuplex", 3, 1 }, { "GMIISpeed", 1, 2 }, { "GMIILinkStatus", 0, 1 }, { "XGM_WOL_STATUS", 0x9a0, 0 }, { "PatDetected", 31, 1 }, { "MatchedFilter", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0x9a4, 0 }, { "XGM_TX_SPI4_SOP_EOP_CNT", 0x9a8, 0 }, { "TxSPI4SopCnt", 16, 16 }, { "TxSPI4EopCnt", 0, 16 }, { "XGM_RX_SPI4_SOP_EOP_CNT", 0x9ac, 0 }, { "RxSPI4SopCnt", 16, 16 }, { "RxSPI4EopCnt", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; struct reg_info t3c_xgmac0_1_regs[] = { { "XGM_TX_CTRL", 0xa00, 0 }, { "SendPause", 2, 1 }, { "SendZeroPause", 1, 1 }, { "TxEn", 0, 1 }, { "XGM_TX_CFG", 0xa04, 0 }, { "CfgClkSpeed", 2, 3 }, { "StretchMode", 1, 1 }, { "TxPauseEn", 0, 1 }, { "XGM_TX_PAUSE_QUANTA", 0xa08, 0 }, { "TxPauseQuanta", 0, 16 }, { "XGM_RX_CTRL", 0xa0c, 0 }, { "RxEn", 0, 1 }, { "XGM_RX_CFG", 0xa10, 0 }, { "Con802_3Preamble", 12, 1 }, { "EnNon802_3Preamble", 11, 1 }, { "CopyPreamble", 10, 1 }, { "DisPauseFrames", 9, 1 }, { "En1536BFrames", 8, 1 }, { "EnJumbo", 7, 1 }, { "RmFCS", 6, 1 }, { "DisNonVlan", 5, 1 }, { "EnExtMatch", 4, 1 }, { "EnHashUcast", 3, 1 }, { "EnHashMcast", 2, 1 }, { "DisBCast", 1, 1 }, { "CopyAllFrames", 0, 1 }, { "XGM_RX_HASH_LOW", 0xa14, 0 }, { "XGM_RX_HASH_HIGH", 0xa18, 0 }, { "XGM_RX_EXACT_MATCH_LOW_1", 0xa1c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_1", 0xa20, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_2", 0xa24, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_2", 0xa28, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_3", 0xa2c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_3", 0xa30, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_4", 0xa34, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_4", 0xa38, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_5", 0xa3c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_5", 0xa40, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_6", 0xa44, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_6", 0xa48, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_7", 0xa4c, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_7", 0xa50, 0 }, { "address_high", 0, 16 }, { "XGM_RX_EXACT_MATCH_LOW_8", 0xa54, 0 }, { "XGM_RX_EXACT_MATCH_HIGH_8", 0xa58, 0 }, { "address_high", 0, 16 }, { "XGM_RX_TYPE_MATCH_1", 0xa5c, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_2", 0xa60, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_3", 0xa64, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_RX_TYPE_MATCH_4", 0xa68, 0 }, { "EnTypeMatch", 31, 1 }, { "type", 0, 16 }, { "XGM_INT_STATUS", 0xa6c, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_MASK", 0xa70, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_ENABLE", 0xa74, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_XGM_INT_DISABLE", 0xa78, 0 }, { "XGMIIExtInt", 10, 1 }, { "LinkFaultChange", 9, 1 }, { "PhyFrameComplete", 8, 1 }, { "PauseFrameTxmt", 7, 1 }, { "PauseCntrTimeOut", 6, 1 }, { "Non0PauseRcvd", 5, 1 }, { "StatOFlow", 4, 1 }, { "TxErrFIFO", 3, 1 }, { "TxUFlow", 2, 1 }, { "FrameTxmt", 1, 1 }, { "FrameRcvd", 0, 1 }, { "XGM_TX_PAUSE_TIMER", 0xa7c, 0 }, { "CurPauseTimer", 0, 16 }, { "XGM_STAT_CTRL", 0xa80, 0 }, { "ReadSnpShot", 4, 1 }, { "TakeSnpShot", 3, 1 }, { "ClrStats", 2, 1 }, { "IncrStats", 1, 1 }, { "EnTestModeWr", 0, 1 }, { "XGM_RXFIFO_CFG", 0xa84, 0 }, { "RxFIFO_empty", 31, 1 }, { "RxFIFO_full", 30, 1 }, { "RxFIFOPauseHWM", 17, 12 }, { "RxFIFOPauseLWM", 5, 12 }, { "ForcedPause", 4, 1 }, { "ExternLoopback", 3, 1 }, { "RxByteSwap", 2, 1 }, { "RxStrFrwrd", 1, 1 }, { "DisErrFrames", 0, 1 }, { "XGM_TXFIFO_CFG", 0xa88, 0 }, { "TxFIFO_empty", 31, 1 }, { "TxFIFO_full", 30, 1 }, { "UnderunFix", 22, 1 }, { "EnDropPkt", 21, 1 }, { "TxIPG", 13, 8 }, { "TxFIFOThresh", 4, 9 }, { "InternLoopback", 3, 1 }, { "TxByteSwap", 2, 1 }, { "DisCRC", 1, 1 }, { "DisPreAmble", 0, 1 }, { "XGM_SLOW_TIMER", 0xa8c, 0 }, { "PauseSlowTimerEn", 31, 1 }, { "PauseSlowTimer", 0, 20 }, { "XGM_PAUSE_TIMER", 0xa90, 0 }, { "PauseTimer", 0, 20 }, { "XGM_XAUI_PCS_TEST", 0xa94, 0 }, { "TestPattern", 1, 2 }, { "EnTest", 0, 1 }, { "XGM_RGMII_CTRL", 0xa98, 0 }, { "PhAlignFIFOThresh", 1, 2 }, { "TxClk90Shift", 0, 1 }, { "XGM_RGMII_IMP", 0xa9c, 0 }, { "CalReset", 8, 1 }, { "CalUpdate", 7, 1 }, { "ImpSetUpdate", 6, 1 }, { "RGMIIImpPD", 3, 3 }, { "RGMIIImpPU", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE", 0xaa8, 0 }, { "RxMaxFramerSize", 17, 14 }, { "RxEnErrorGather", 16, 1 }, { "RxEnSingleFlit", 15, 1 }, { "RxEnFramer", 14, 1 }, { "RxMaxPktSize", 0, 14 }, { "XGM_RESET_CTRL", 0xaac, 0 }, { "XGMAC_STOP_EN", 4, 1 }, { "XG2G_Reset_", 3, 1 }, { "RGMII_Reset_", 2, 1 }, { "PCS_Reset_", 1, 1 }, { "MAC_Reset_", 0, 1 }, { "XGM_XAUI1G_CTRL", 0xab0, 0 }, { "XAUI1GLinkId", 0, 2 }, { "XGM_SERDES_LANE_CTRL", 0xab4, 0 }, { "LaneReversal", 8, 1 }, { "TxPolarity", 4, 4 }, { "RxPolarity", 0, 4 }, { "XGM_PORT_CFG", 0xab8, 0 }, { "SafeSpeedChange", 4, 1 }, { "ClkDivReset_", 3, 1 }, { "PortSpeed", 1, 2 }, { "EnRGMII", 0, 1 }, { "XGM_EPIO_DATA0", 0xac0, 0 }, { "XGM_EPIO_DATA1", 0xac4, 0 }, { "XGM_EPIO_DATA2", 0xac8, 0 }, { "XGM_EPIO_DATA3", 0xacc, 0 }, { "XGM_EPIO_OP", 0xad0, 0 }, { "PIO_Ready", 31, 1 }, { "PIO_WrRd", 24, 1 }, { "PIO_Address", 0, 8 }, { "XGM_INT_ENABLE", 0xad4, 0 }, { "XAUIPCSDECErr", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_INT_CAUSE", 0xad8, 0 }, { "XAUIPCSDECErr", 24, 1 }, { "RGMIIRxFIFOOverflow", 23, 1 }, { "RGMIIRxFIFOUnderflow", 22, 1 }, { "RxPktSizeError", 21, 1 }, { "WOLPatDetected", 20, 1 }, { "TXFIFO_prty_err", 17, 3 }, { "RXFIFO_prty_err", 14, 3 }, { "TXFIFO_underrun", 13, 1 }, { "RXFIFO_overflow", 12, 1 }, { "SERDESBISTErr", 8, 4 }, { "SERDESLowSigChange", 4, 4 }, { "XAUIPCSCTCErr", 3, 1 }, { "XAUIPCSAlignChange", 2, 1 }, { "RGMIILinkStsChange", 1, 1 }, { "xgm_int", 0, 1 }, { "XGM_XAUI_ACT_CTRL", 0xadc, 0 }, { "TxEn", 1, 1 }, { "RxEn", 0, 1 }, { "XGM_SERDES_CTRL0", 0xae0, 0 }, { "IntSerLPBK3", 27, 1 }, { "IntSerLPBK2", 26, 1 }, { "IntSerLPBK1", 25, 1 }, { "IntSerLPBK0", 24, 1 }, { "Reset3", 23, 1 }, { "Reset2", 22, 1 }, { "Reset1", 21, 1 }, { "Reset0", 20, 1 }, { "Pwrdn3", 19, 1 }, { "Pwrdn2", 18, 1 }, { "Pwrdn1", 17, 1 }, { "Pwrdn0", 16, 1 }, { "ResetPLL23", 15, 1 }, { "ResetPLL01", 14, 1 }, { "PW23", 12, 2 }, { "PW01", 10, 2 }, { "Deq", 6, 4 }, { "Dtx", 2, 4 }, { "LoDrv", 1, 1 }, { "HiDrv", 0, 1 }, { "XGM_SERDES_CTRL1", 0xae4, 0 }, { "FmOffset3", 19, 5 }, { "FmOffsetEn3", 18, 1 }, { "FmOffset2", 13, 5 }, { "FmOffsetEn2", 12, 1 }, { "FmOffset1", 7, 5 }, { "FmOffsetEn1", 6, 1 }, { "FmOffset0", 1, 5 }, { "FmOffsetEn0", 0, 1 }, { "XGM_SERDES_CTRL2", 0xae8, 0 }, { "DnIn3", 11, 1 }, { "UpIn3", 10, 1 }, { "RxSlave3", 9, 1 }, { "DnIn2", 8, 1 }, { "UpIn2", 7, 1 }, { "RxSlave2", 6, 1 }, { "DnIn1", 5, 1 }, { "UpIn1", 4, 1 }, { "RxSlave1", 3, 1 }, { "DnIn0", 2, 1 }, { "UpIn0", 1, 1 }, { "RxSlave0", 0, 1 }, { "XGM_SERDES_CTRL3", 0xaec, 0 }, { "ExtBISTChkErrClr3", 31, 1 }, { "ExtBISTChkEn3", 30, 1 }, { "ExtBISTGenEn3", 29, 1 }, { "ExtBISTPat3", 26, 3 }, { "ExtParReset3", 25, 1 }, { "ExtParLPBK3", 24, 1 }, { "ExtBISTChkErrClr2", 23, 1 }, { "ExtBISTChkEn2", 22, 1 }, { "ExtBISTGenEn2", 21, 1 }, { "ExtBISTPat2", 18, 3 }, { "ExtParReset2", 17, 1 }, { "ExtParLPBK2", 16, 1 }, { "ExtBISTChkErrClr1", 15, 1 }, { "ExtBISTChkEn1", 14, 1 }, { "ExtBISTGenEn1", 13, 1 }, { "ExtBISTPat1", 10, 3 }, { "ExtParReset1", 9, 1 }, { "ExtParLPBK1", 8, 1 }, { "ExtBISTChkErrClr0", 7, 1 }, { "ExtBISTChkEn0", 6, 1 }, { "ExtBISTGenEn0", 5, 1 }, { "ExtBISTPat0", 2, 3 }, { "ExtParReset0", 1, 1 }, { "ExtParLPBK0", 0, 1 }, { "XGM_SERDES_STAT0", 0xaf0, 0 }, { "ExtBISTChkErrCnt0", 4, 24 }, { "ExtBISTChkFmd0", 3, 1 }, { "LowSigForceEn0", 2, 1 }, { "LowSigForceValue0", 1, 1 }, { "LowSig0", 0, 1 }, { "XGM_SERDES_STAT1", 0xaf4, 0 }, { "ExtBISTChkErrCnt1", 4, 24 }, { "ExtBISTChkFmd1", 3, 1 }, { "LowSigForceEn1", 2, 1 }, { "LowSigForceValue1", 1, 1 }, { "LowSig1", 0, 1 }, { "XGM_SERDES_STAT2", 0xaf8, 0 }, { "ExtBISTChkErrCnt2", 4, 24 }, { "ExtBISTChkFmd2", 3, 1 }, { "LowSigForceEn2", 2, 1 }, { "LowSigForceValue2", 1, 1 }, { "LowSig2", 0, 1 }, { "XGM_SERDES_STAT3", 0xafc, 0 }, { "ExtBISTChkErrCnt3", 4, 24 }, { "ExtBISTChkFmd3", 3, 1 }, { "LowSigForceEn3", 2, 1 }, { "LowSigForceValue3", 1, 1 }, { "LowSig3", 0, 1 }, { "XGM_STAT_TX_BYTE_LOW", 0xb00, 0 }, { "XGM_STAT_TX_BYTE_HIGH", 0xb04, 0 }, { "TxBytes_high", 0, 13 }, { "XGM_STAT_TX_FRAME_LOW", 0xb08, 0 }, { "XGM_STAT_TX_FRAME_HIGH", 0xb0c, 0 }, { "TxFrames_high", 0, 4 }, { "XGM_STAT_TX_BCAST", 0xb10, 0 }, { "XGM_STAT_TX_MCAST", 0xb14, 0 }, { "XGM_STAT_TX_PAUSE", 0xb18, 0 }, { "XGM_STAT_TX_64B_FRAMES", 0xb1c, 0 }, { "XGM_STAT_TX_65_127B_FRAMES", 0xb20, 0 }, { "XGM_STAT_TX_128_255B_FRAMES", 0xb24, 0 }, { "XGM_STAT_TX_256_511B_FRAMES", 0xb28, 0 }, { "XGM_STAT_TX_512_1023B_FRAMES", 0xb2c, 0 }, { "XGM_STAT_TX_1024_1518B_FRAMES", 0xb30, 0 }, { "XGM_STAT_TX_1519_MAXB_FRAMES", 0xb34, 0 }, { "XGM_STAT_TX_ERR_FRAMES", 0xb38, 0 }, { "XGM_STAT_RX_BYTES_LOW", 0xb3c, 0 }, { "XGM_STAT_RX_BYTES_HIGH", 0xb40, 0 }, { "RxBytes_high", 0, 13 }, { "XGM_STAT_RX_FRAMES_LOW", 0xb44, 0 }, { "XGM_STAT_RX_FRAMES_HIGH", 0xb48, 0 }, { "RxFrames_high", 0, 4 }, { "XGM_STAT_RX_BCAST_FRAMES", 0xb4c, 0 }, { "XGM_STAT_RX_MCAST_FRAMES", 0xb50, 0 }, { "XGM_STAT_RX_PAUSE_FRAMES", 0xb54, 0 }, { "RxPauseFrames", 0, 16 }, { "XGM_STAT_RX_64B_FRAMES", 0xb58, 0 }, { "XGM_STAT_RX_65_127B_FRAMES", 0xb5c, 0 }, { "XGM_STAT_RX_128_255B_FRAMES", 0xb60, 0 }, { "XGM_STAT_RX_256_511B_FRAMES", 0xb64, 0 }, { "XGM_STAT_RX_512_1023B_FRAMES", 0xb68, 0 }, { "XGM_STAT_RX_1024_1518B_FRAMES", 0xb6c, 0 }, { "XGM_STAT_RX_1519_MAXB_FRAMES", 0xb70, 0 }, { "XGM_STAT_RX_SHORT_FRAMES", 0xb74, 0 }, { "RxShortFrames", 0, 16 }, { "XGM_STAT_RX_OVERSIZE_FRAMES", 0xb78, 0 }, { "RxOversizeFrames", 0, 16 }, { "XGM_STAT_RX_JABBER_FRAMES", 0xb7c, 0 }, { "RxJabberFrames", 0, 16 }, { "XGM_STAT_RX_CRC_ERR_FRAMES", 0xb80, 0 }, { "RxCRCErrFrames", 0, 16 }, { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0xb84, 0 }, { "RxLengthErrFrames", 0, 16 }, { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0xb88, 0 }, { "RxSymCodeErrFrames", 0, 16 }, { "XGM_XAUI_PCS_ERR", 0xb98, 0 }, { "PCS_SyncStatus", 5, 4 }, { "PCS_CTCFIFOErr", 1, 4 }, { "PCS_NotAligned", 0, 1 }, { "XGM_RGMII_STATUS", 0xb9c, 0 }, { "GMIIDuplex", 3, 1 }, { "GMIISpeed", 1, 2 }, { "GMIILinkStatus", 0, 1 }, { "XGM_WOL_STATUS", 0xba0, 0 }, { "PatDetected", 31, 1 }, { "MatchedFilter", 0, 3 }, { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0xba4, 0 }, { "XGM_TX_SPI4_SOP_EOP_CNT", 0xba8, 0 }, { "TxSPI4SopCnt", 16, 16 }, { "TxSPI4EopCnt", 0, 16 }, { "XGM_RX_SPI4_SOP_EOP_CNT", 0xbac, 0 }, { "RxSPI4SopCnt", 16, 16 }, { "RxSPI4EopCnt", 0, 16 }, - { NULL } + { NULL, 0, 0 } }; Index: projects/ppc64 =================================================================== --- projects/ppc64 (revision 204271) +++ projects/ppc64 (revision 204272) Property changes on: projects/ppc64 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r204217-204271