summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 09:05:49 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 09:05:49 +0000
commit2b1aee95bc070a2557dea7d23cba035a47369570 (patch)
tree6c3bc843b1099803aced1227583563bfe4fc791f /sys
parentc63dc2cd28bc7c6086012d11ab0560e76e203098 (diff)
Add compile-time support for coalescing command interrupts to reduce the
overall interrupt rate. #define AHCI_COALESCE to enable.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/ahci.c75
1 files changed, 73 insertions, 2 deletions
diff --git a/sys/dev/pci/ahci.c b/sys/dev/pci/ahci.c
index 80040577a38..2cd70111132 100644
--- a/sys/dev/pci/ahci.c
+++ b/sys/dev/pci/ahci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ahci.c,v 1.110 2007/04/06 04:04:29 pascoe Exp $ */
+/* $OpenBSD: ahci.c,v 1.111 2007/04/08 09:05:48 pascoe Exp $ */
/*
* Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
@@ -91,6 +91,7 @@ int ahcidebug = AHCI_D_VERBOSE;
#define AHCI_REG_VS_1_0 0x00010000 /* 1.0 */
#define AHCI_REG_VS_1_1 0x00010100 /* 1.1 */
#define AHCI_REG_CCC_CTL 0x014 /* Coalescing Control */
+#define AHCI_REG_CCC_CTL_INT(_r) (((_r) & 0xf8) >> 3) /* CCC INT slot */
#define AHCI_REG_CCC_PORTS 0x018 /* Coalescing Ports */
#define AHCI_REG_EM_LOC 0x01c /* Enclosure Mgmt Location */
#define AHCI_REG_EM_CTL 0x020 /* Enclosure Mgmt Control */
@@ -331,6 +332,10 @@ struct ahci_port {
struct ahci_softc *ap_sc;
bus_space_handle_t ap_ioh;
+#ifdef AHCI_COALESCE
+ int ap_num;
+#endif
+
struct ahci_rfis *ap_rfis;
struct ahci_dmamem *ap_dmamem_rfis;
@@ -387,6 +392,8 @@ struct ahci_softc {
#ifdef AHCI_COALESCE
u_int32_t sc_ccc_mask;
+ u_int32_t sc_ccc_ports;
+ u_int32_t sc_ccc_ports_cur;
#endif
};
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
@@ -655,6 +662,42 @@ ahci_attach(struct device *parent, struct device *self, void *aux)
pi = ahci_read(sc, AHCI_REG_PI);
DPRINTF(AHCI_D_VERBOSE, "%s: ports implemented: 0x%08x\n",
DEVNAME(sc), pi);
+
+#ifdef AHCI_COALESCE
+ /* Naive coalescing support - enable for all ports. */
+ if (cap & AHCI_REG_CAP_CCCS) {
+ u_int16_t ccc_timeout = 20;
+ u_int8_t ccc_numcomplete = 12;
+ u_int32_t ccc_ctl;
+
+ /* disable coalescing during reconfiguration. */
+ ccc_ctl = ahci_read(sc, AHCI_REG_CCC_CTL);
+ ccc_ctl &= ~0x00000001;
+ ahci_write(sc, AHCI_REG_CCC_CTL, ccc_ctl);
+
+ sc->sc_ccc_mask = 1 << AHCI_REG_CCC_CTL_INT(ccc_ctl);
+ if (pi & sc->sc_ccc_mask) {
+ /* A conflict with the implemented port list? */
+ printf("%s: coalescing interrupt/implemented port list "
+ "conflict, PI: %08x, ccc_mask: %08x\n",
+ DEVNAME(sc), pi, sc->sc_ccc_mask);
+ sc->sc_ccc_mask = 0;
+ goto noccc;
+ }
+
+ /* ahci_port_start will enable each port when it starts. */
+ sc->sc_ccc_ports = pi;
+ sc->sc_ccc_ports_cur = 0;
+
+ /* program thresholds and enable overall coalescing. */
+ ccc_ctl &= ~0xffffff00;
+ ccc_ctl |= (ccc_timeout << 16) | (ccc_numcomplete << 8);
+ ahci_write(sc, AHCI_REG_CCC_CTL, ccc_ctl);
+ ahci_write(sc, AHCI_REG_CCC_PORTS, 0);
+ ahci_write(sc, AHCI_REG_CCC_CTL, ccc_ctl | 1);
+ }
+noccc:
+#endif
for (i = 0; i < AHCI_MAX_PORTS; i++) {
if (!ISSET(pi, 1 << i)) {
/* dont allocate stuff if the port isnt implemented */
@@ -833,6 +876,9 @@ ahci_port_alloc(struct ahci_softc *sc, u_int port)
}
ap->ap_sc = sc;
+#ifdef AHCI_COALESCE
+ ap->ap_num = port;
+#endif
TAILQ_INIT(&ap->ap_ccb_free);
TAILQ_INIT(&ap->ap_ccb_pending);
@@ -999,7 +1045,14 @@ nomem:
/* Enable port interrupts */
ahci_pwrite(ap, AHCI_PREG_IE, AHCI_PREG_IE_TFEE | AHCI_PREG_IE_HBFE |
AHCI_PREG_IE_IFE | AHCI_PREG_IE_OFE | AHCI_PREG_IE_DPE |
- AHCI_PREG_IE_UFE | AHCI_PREG_IE_SDBE | AHCI_PREG_IE_DHRE);
+ AHCI_PREG_IE_UFE |
+#ifdef AHCI_COALESCE
+ ((sc->sc_ccc_ports & (1 << port)) ? 0 : (AHCI_PREG_IE_SDBE |
+ AHCI_PREG_IE_DHRE))
+#else
+ AHCI_PREG_IE_SDBE | AHCI_PREG_IE_DHRE
+#endif
+ );
freeport:
if (rc != 0)
@@ -1053,6 +1106,15 @@ ahci_port_start(struct ahci_port *ap, int fre_only)
r |= AHCI_PREG_CMD_ST;
ahci_pwrite(ap, AHCI_PREG_CMD, r);
+#ifdef AHCI_COALESCE
+ /* (Re-)enable coalescing on the port. */
+ if (ap->ap_sc->sc_ccc_ports & (1 << ap->ap_num)) {
+ ap->ap_sc->sc_ccc_ports_cur |= (1 << ap->ap_num);
+ ahci_write(ap->ap_sc, AHCI_REG_CCC_PORTS,
+ ap->ap_sc->sc_ccc_ports_cur);
+ }
+#endif
+
/* Wait for FR to come on */
if (ahci_pwait_set(ap, AHCI_PREG_CMD, AHCI_PREG_CMD_FR))
return (2);
@@ -1069,6 +1131,14 @@ ahci_port_stop(struct ahci_port *ap, int stop_fis_rx)
{
u_int32_t r;
+#ifdef AHCI_COALESCE
+ /* Disable coalescing on the port while it is stopped. */
+ if (ap->ap_sc->sc_ccc_ports & (1 << ap->ap_num)) {
+ ap->ap_sc->sc_ccc_ports_cur &= ~(1 << ap->ap_num);
+ ahci_write(ap->ap_sc, AHCI_REG_CCC_PORTS, ap->ap_sc->sc_ccc_ports_cur);
+ }
+#endif
+
/* Turn off ST (and FRE) */
r = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
r &= ~AHCI_PREG_CMD_ST;
@@ -1529,6 +1599,7 @@ ahci_intr(void *arg)
DPRINTF(AHCI_D_INTR, "%s: command coalescing interrupt\n",
DEVNAME(sc));
is &= ~sc->sc_ccc_mask;
+ is |= sc->sc_ccc_ports_cur;
}
#endif