On Sat, Mar 12, 2016 at 02:19:49PM +0100, Stefan Sperling wrote:
> On Fri, Mar 11, 2016 at 11:56:41AM -0700, Theo de Raadt wrote:
> > > But what we really should do the SMBALERT# dance of reading the Alert
> > > Response Address, which will make sure the device de-asserts the
> > > SMBALERT# line.
> >
> > Ah, I did not check the spec. That sounds very reasonable...
>
> I've tried doing this without success.
> I'm getting a slave address in polling mode. It's always 0x0 which
> is not a valid address for a slave device. Most likely a bug in the
> crude changes I've made. Or perhaps the hardware is really triggering
> a bogus alert.
FWIW, these are the changes I used to test this approach.
This diff is most likely full of stupidity...
Prints 'ichiic0: alert from slave 0x0'.
Index: ichiic.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/ichiic.c,v
retrieving revision 1.37
diff -u -p -r1.37 ichiic.c
--- ichiic.c 7 Dec 2015 02:56:36 -0000 1.37
+++ ichiic.c 12 Mar 2016 13:05:39 -0000
@@ -25,6 +25,7 @@
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>
+#include <sys/task.h>
#include <machine/bus.h>
@@ -52,6 +53,8 @@ struct ichiic_softc {
bus_space_handle_t sc_ioh;
void * sc_ih;
int sc_poll;
+ struct task sc_alert_task;
+ int sc_task_added;
struct i2c_controller sc_i2c_tag;
struct rwlock sc_i2c_lock;
@@ -71,7 +74,8 @@ int ichiic_i2c_acquire_bus(void *, int);
void ichiic_i2c_release_bus(void *, int);
int ichiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
void *, size_t, int);
-
+void ichiic_xfer_timeout(struct ichiic_softc *);
+void ichiic_alert_task(void *);
int ichiic_intr(void *);
struct cfattach ichiic_ca = {
@@ -178,6 +182,8 @@ ichiic_attach(struct device *parent, str
printf("\n");
+ task_set(&sc->sc_alert_task, ichiic_alert_task, sc);
+
/* Attach I2C bus */
rw_init(&sc->sc_i2c_lock, "iiclk");
sc->sc_i2c_tag.ic_cookie = sc;
@@ -316,6 +322,15 @@ ichiic_i2c_exec(void *cookie, i2c_op_t o
return (0);
timeout:
+ ichiic_xfer_timeout(sc);
+ return (1);
+
+}
+
+void ichiic_xfer_timeout(struct ichiic_softc *sc)
+{
+ u_int8_t st;
+
/*
* Transfer timeout. Kill the transaction and clear status bits.
*/
@@ -327,7 +342,69 @@ timeout:
printf("%s: abort failed, status 0x%b\n",
sc->sc_dev.dv_xname, st, ICH_SMB_HS_BITS);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
- return (1);
+}
+
+void
+ichiic_alert_task(void *arg)
+{
+ struct ichiic_softc *sc = arg;
+ int retries, s;
+ uint8_t st, ctl, slave_addr;
+
+ s = splbio();
+ /* Wait for bus to be idle */
+ for (retries = 100; retries > 0; retries--) {
+ st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
+ if (!(st & ICH_SMB_HS_BUSY))
+ break;
+ DELAY(ICHIIC_DELAY);
+ }
+ printf("%s: alert: st 0x%b\n", sc->sc_dev.dv_xname, st,
+ ICH_SMB_HS_BITS);
+ if (st & ICH_SMB_HS_BUSY) {
+ splx(s);
+ return;
+ }
+
+ /*
+ * Read the Alert Response Address.
+ * The device which pulled ALERT# will respond with its address.
+ */
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA,
+ ICH_SMB_TXSLVA_ADDR(ICH_SMB_ARA) | ICH_SMB_TXSLVA_READ);
+ ctl = (ICH_SMB_HC_CMD_BDATA | ICH_SMB_HC_START);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl);
+
+ if (cold || sc->sc_poll) {
+ /* Poll for completion */
+ DELAY(ICHIIC_DELAY);
+ for (retries = 1000; retries > 0; retries--) {
+ st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+ ICH_SMB_HS);
+ if ((st & ICH_SMB_HS_BUSY) == 0)
+ break;
+ DELAY(ICHIIC_DELAY);
+ }
+ if (st & ICH_SMB_HS_BUSY)
+ goto timeout;
+ } else {
+ /* Wait for interrupt */
+ if (tsleep(&sc->sc_alert_task, PRIBIO, "ichiica",
+ ICHIIC_TIMEOUT * hz))
+ goto timeout;
+ }
+
+ slave_addr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HD0);
+ printf("%s: alert from slave 0x%x\n", sc->sc_dev.dv_xname,
+ ICH_SMB_NDADDR_ADDR(slave_addr));
+
+ splx(s);
+ return;
+
+timeout:
+ ichiic_xfer_timeout(sc);
+ splx(s);
+ return;
}
int
@@ -348,6 +425,8 @@ ichiic_intr(void *arg)
DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
ICH_SMB_HS_BITS));
+ printf("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
+ ICH_SMB_HS_BITS);
/* Clear status bits */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
@@ -373,8 +452,21 @@ ichiic_intr(void *arg)
ICH_SMB_HD1);
}
+ if (st & ICH_SMB_HS_SMBAL) {
+ if (cold || sc->sc_poll)
+ ichiic_alert_task(sc);
+ else {
+ task_add(systq, &sc->sc_alert_task);
+ sc->sc_task_added = 1;
+ }
+ }
+
done:
if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
wakeup(sc);
+ if (sc->sc_task_added) {
+ wakeup(&sc->sc_alert_task);
+ sc->sc_task_added = 0;
+ }
return (1);
}
Index: ichreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/ichreg.h,v
retrieving revision 1.7
diff -u -p -r1.7 ichreg.h
--- ichreg.h 18 Dec 2005 12:09:04 -0000 1.7
+++ ichreg.h 12 Mar 2016 11:34:26 -0000
@@ -118,6 +118,8 @@
#define ICH_SMB_NDLOW 0x16 /* notify data low byte */
#define ICH_SMB_NDHIGH 0x17 /* notify data high byte */
+#define ICH_SMB_ARA 0x0c /* alert response address */
+
/*
* 6300ESB watchdog timer registers.
*/