From 7f3e76169ebb97f6e4e00db3115fb336d61e95d8 Mon Sep 17 00:00:00 2001
From: Damien Bergamini <damien@cvs.openbsd.org>
Date: Wed, 20 May 2009 16:31:51 +0000
Subject: add code to read from OTPROM (1000 and 6000 series only).

---
 sys/dev/pci/if_iwn.c    | 67 +++++++++++++++++++++++++++++++++++++++++++------
 sys/dev/pci/if_iwnreg.h | 16 ++++++++++--
 sys/dev/pci/if_iwnvar.h |  5 ++--
 3 files changed, 77 insertions(+), 11 deletions(-)

(limited to 'sys')

diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c
index 296ca0dd63a..b878f846ac5 100644
--- a/sys/dev/pci/if_iwn.c
+++ b/sys/dev/pci/if_iwn.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: if_iwn.c,v 1.53 2009/05/12 19:10:57 damien Exp $	*/
+/*	$OpenBSD: if_iwn.c,v 1.54 2009/05/20 16:31:50 damien Exp $	*/
 
 /*-
  * Copyright (c) 2007-2009 Damien Bergamini <damien.bergamini@free.fr>
@@ -102,6 +102,7 @@ void		iwn_radiotap_attach(struct iwn_softc *);
 void		iwn_power(int, void *);
 int		iwn_nic_lock(struct iwn_softc *);
 int		iwn_eeprom_lock(struct iwn_softc *);
+int		iwn_init_otprom(struct iwn_softc *);
 int		iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
 int		iwn_dma_contig_alloc(bus_dma_tag_t, struct iwn_dma_info *,
 		    void **, bus_size_t, bus_size_t);
@@ -783,6 +784,33 @@ iwn_eeprom_unlock(struct iwn_softc *sc)
 	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
 }
 
+/*
+ * Initialize access by host to One Time Programmable ROM.
+ * NB: This kind of ROM can be found on 1000 or 6000 Series only.
+ */
+int
+iwn_init_otprom(struct iwn_softc *sc)
+{
+	int error;
+
+	if ((error = iwn_clock_wait(sc)) != 0)
+		return error;
+
+	if ((error = iwn_nic_lock(sc)) != 0)
+		return error;
+	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
+	DELAY(5);
+	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
+	iwn_nic_unlock(sc);
+
+	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
+	/* Clear ECC status. */
+	IWN_SETBITS(sc, IWN_OTP_GP,
+	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
+
+	return 0;
+}
+
 int
 iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
 {
@@ -792,8 +820,6 @@ iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
 
 	for (; count > 0; count -= 2, addr++) {
 		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
-		IWN_CLRBITS(sc, IWN_EEPROM, IWN_EEPROM_CMD);
-
 		for (ntries = 0; ntries < 10; ntries++) {
 			val = IWN_READ(sc, IWN_EEPROM);
 			if (val & IWN_EEPROM_READ_VALID)
@@ -801,10 +827,24 @@ iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
 			DELAY(5);
 		}
 		if (ntries == 10) {
-			printf("%s: could not read EEPROM\n",
-			    sc->sc_dev.dv_xname);
+			printf("%s: timeout reading ROM at 0x%x\n",
+			    sc->sc_dev.dv_xname, addr);
 			return ETIMEDOUT;
 		}
+		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
+			/* OTPROM, check for ECC errors. */
+			uint32_t tmp = IWN_READ(sc, IWN_OTP_GP);
+			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
+				printf("%s: OTPROM ECC error at 0x%x\n",
+				    sc->sc_dev.dv_xname, addr);
+				return EIO;
+			}
+			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
+				/* Correctable ECC error, clear bit. */
+				IWN_SETBITS(sc, IWN_OTP_GP,
+				    IWN_OTP_GP_ECC_CORR_STTS);
+			}
+		}
 		*out++ = val >> 16;
 		if (count > 1)
 			*out++ = val >> 24;
@@ -1158,12 +1198,25 @@ iwn_read_eeprom(struct iwn_softc *sc)
 	uint16_t val;
 	int error;
 
+	/* Check whether adapter has an EEPROM or an OTPROM. */
+	if (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP)
+		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
+	DPRINTF(("%s found\n", (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ?
+	    "OTPROM" : "EEPROM"));
+
 	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
-		printf("%s: bad EEPROM signature\n", sc->sc_dev.dv_xname);
+		printf("%s: bad ROM signature\n", sc->sc_dev.dv_xname);
 		return EIO;
 	}
 	if ((error = iwn_eeprom_lock(sc)) != 0) {
-		printf("%s: could not lock EEPROM (error=%d)\n",
+		printf("%s: could not lock ROM (error=%d)\n",
+		    sc->sc_dev.dv_xname, error);
+		return error;
+	}
+
+	if ((sc->sc_flags & IWN_FLAG_HAS_OTPROM) &&
+	    ((error = iwn_init_otprom(sc)) != 0)) {
+		printf("%s: could not initialize OTPROM (error=%d)\n",
 		    sc->sc_dev.dv_xname, error);
 		return error;
 	}
diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h
index d6ee11d82ab..d9be5efd0a8 100644
--- a/sys/dev/pci/if_iwnreg.h
+++ b/sys/dev/pci/if_iwnreg.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: if_iwnreg.h,v 1.22 2009/03/10 20:39:21 damien Exp $	*/
+/*	$OpenBSD: if_iwnreg.h,v 1.23 2009/05/20 16:31:50 damien Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2008
@@ -58,6 +58,7 @@
 #define IWN_HW_REV		0x028
 #define IWN_EEPROM		0x02c
 #define IWN_EEPROM_GP		0x030
+#define IWN_OTP_GP		0x034
 #define IWN_GIO			0x03c
 #define IWN_UCODE_GP1_CLR	0x05c
 #define IWN_LED			0x094
@@ -272,6 +273,15 @@
 #define IWN_EEPROM_READ_VALID	(1 << 0)
 #define IWN_EEPROM_CMD		(1 << 1)
 
+/* Possible flags for register IWN_EEPROM_GP. */
+#define IWN_EEPROM_GP_IF_OWNER	0x00000180
+
+/* Possible flags for register IWN_OTP_GP. */
+#define IWN_OTP_GP_DEV_SEL_OTP		(1 << 16)
+#define IWN_OTP_GP_RELATIVE_ACCESS	(1 << 17)
+#define IWN_OTP_GP_ECC_CORR_STTS	(1 << 20)
+#define IWN_OTP_GP_ECC_UNCORR_STTS	(1 << 21)
+
 /* Possible flags for register IWN_SCHED_QUEUE_STATUS. */
 #define IWN4965_TXQ_STATUS_ACTIVE	0x0007fc01
 #define IWN4965_TXQ_STATUS_INACTIVE	0x0007fc00
@@ -287,9 +297,11 @@
 
 /* Possible flags for register IWN_APMG_PS. */
 #define IWN_APMG_PS_EARLY_PWROFF_DIS	(1 << 22)
-#define IWN_APMG_PS_PWR_SRC_MASK	(3 << 24)
 #define IWN_APMG_PS_PWR_SRC(x)		((x) << 24)
 #define IWN_APMG_PS_PWR_SRC_VMAIN	0
+#define IWN_APMG_PS_PWR_SRC_VAUX	2
+#define IWN_APMG_PS_PWR_SRC_MASK	IWN_APMG_PS_PWR_SRC(3)
+#define IWN_APMG_PS_RESET_REQ		(1 << 26)
 
 /* Possible flags for IWN_APMG_PCI_STT. */
 #define IWN_APMG_PCI_STT_L1A_DIS	(1 << 11)
diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h
index 827b17ee693..35390eec9e1 100644
--- a/sys/dev/pci/if_iwnvar.h
+++ b/sys/dev/pci/if_iwnvar.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: if_iwnvar.h,v 1.10 2009/05/12 19:10:57 damien Exp $	*/
+/*	$OpenBSD: if_iwnvar.h,v 1.11 2009/05/20 16:31:50 damien Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2008
@@ -203,7 +203,8 @@ struct iwn_softc {
 
 	u_int			sc_flags;
 #define IWN_FLAG_HAS_5GHZ	(1 << 0)
-#define IWN_FLAG_FIRST_BOOT	(1 << 1)
+#define IWN_FLAG_HAS_OTPROM	(1 << 1)
+#define IWN_FLAG_FIRST_BOOT	(1 << 2)
 
 	uint8_t 		hw_type;
 	const struct iwn_hal	*sc_hal;
-- 
cgit v1.2.3