ath9k_hw: add support for reading EEPROM data from the internal OTP ROM

Some of the new AR9003 cards do not come with an external EEPROM chip
anymore. Calibration data on these cards is stored in the OTP ROM on
the chip.
This patch adds support for reading this data, and also adds support
for different EEPROM chip sizes (512 bytes instead of 1K).

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 1b4e991..230a122 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -3104,6 +3104,36 @@
 	return false;
 }
 
+static bool ar9300_otp_read_word(struct ath_hw *ah, int addr, u32 *data)
+{
+	REG_READ(ah, AR9300_OTP_BASE + (4 * addr));
+
+	if (!ath9k_hw_wait(ah, AR9300_OTP_STATUS, AR9300_OTP_STATUS_TYPE,
+			   AR9300_OTP_STATUS_VALID, 1000))
+		return false;
+
+	*data = REG_READ(ah, AR9300_OTP_READ_DATA);
+	return true;
+}
+
+static bool ar9300_read_otp(struct ath_hw *ah, int address, u8 *buffer,
+			    int count)
+{
+	u32 data;
+	int i;
+
+	for (i = 0; i < count; i++) {
+		int offset = 8 * ((address - i) % 4);
+		if (!ar9300_otp_read_word(ah, (address - i) / 4, &data))
+			return false;
+
+		buffer[i] = (data >> offset) & 0xff;
+	}
+
+	return true;
+}
+
+
 static void ar9300_comp_hdr_unpack(u8 *best, int *code, int *reference,
 				   int *length, int *major, int *minor)
 {
@@ -3221,6 +3251,26 @@
 	return 0;
 }
 
+typedef bool (*eeprom_read_op)(struct ath_hw *ah, int address, u8 *buffer,
+			       int count);
+
+static bool ar9300_check_header(void *data)
+{
+	u32 *word = data;
+	return !(*word == 0 || *word == ~0);
+}
+
+static bool ar9300_check_eeprom_header(struct ath_hw *ah, eeprom_read_op read,
+				       int base_addr)
+{
+	u8 header[4];
+
+	if (!read(ah, base_addr, header, 4))
+		return false;
+
+	return ar9300_check_header(header);
+}
+
 /*
  * Read the configuration data from the eeprom.
  * The data can be put in any specified memory buffer.
@@ -3241,6 +3291,7 @@
 	int it;
 	u16 checksum, mchecksum;
 	struct ath_common *common = ath9k_hw_common(ah);
+	eeprom_read_op read;
 
 	word = kzalloc(2048, GFP_KERNEL);
 	if (!word)
@@ -3248,14 +3299,42 @@
 
 	memcpy(mptr, &ar9300_default, mdata_size);
 
+	read = ar9300_read_eeprom;
 	cptr = AR9300_BASE_ADDR;
+	ath_print(common, ATH_DBG_EEPROM,
+		"Trying EEPROM accesss at Address 0x%04x\n", cptr);
+	if (ar9300_check_eeprom_header(ah, read, cptr))
+		goto found;
+
+	cptr = AR9300_BASE_ADDR_512;
+	ath_print(common, ATH_DBG_EEPROM,
+		"Trying EEPROM accesss at Address 0x%04x\n", cptr);
+	if (ar9300_check_eeprom_header(ah, read, cptr))
+		goto found;
+
+	read = ar9300_read_otp;
+	cptr = AR9300_BASE_ADDR;
+	ath_print(common, ATH_DBG_EEPROM,
+		"Trying OTP accesss at Address 0x%04x\n", cptr);
+	if (ar9300_check_eeprom_header(ah, read, cptr))
+		goto found;
+
+	cptr = AR9300_BASE_ADDR_512;
+	ath_print(common, ATH_DBG_EEPROM,
+		"Trying OTP accesss at Address 0x%04x\n", cptr);
+	if (ar9300_check_eeprom_header(ah, read, cptr))
+		goto found;
+
+	goto fail;
+
+found:
+	ath_print(common, ATH_DBG_EEPROM, "Found valid EEPROM data");
+
 	for (it = 0; it < MSTATE; it++) {
-		if (!ar9300_read_eeprom(ah, cptr, word, COMP_HDR_LEN))
+		if (!read(ah, cptr, word, COMP_HDR_LEN))
 			goto fail;
 
-		if ((word[0] == 0 && word[1] == 0 && word[2] == 0 &&
-		     word[3] == 0) || (word[0] == 0xff && word[1] == 0xff
-				       && word[2] == 0xff && word[3] == 0xff))
+		if (!ar9300_check_header(word))
 			break;
 
 		ar9300_comp_hdr_unpack(word, &code, &reference,
@@ -3272,8 +3351,7 @@
 		}
 
 		osize = length;
-		ar9300_read_eeprom(ah, cptr, word,
-				   COMP_HDR_LEN + osize + COMP_CKSUM_LEN);
+		read(ah, cptr, word, COMP_HDR_LEN + osize + COMP_CKSUM_LEN);
 		checksum = ar9300_comp_cksum(&word[COMP_HDR_LEN], length);
 		mchecksum = word[COMP_HDR_LEN + osize] |
 		    (word[COMP_HDR_LEN + osize + 1] << 8);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
index 5301df3..57f64db 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
@@ -79,6 +79,15 @@
 #define FIXED_CCA_THRESHOLD 15
 
 #define AR9300_BASE_ADDR 0x3ff
+#define AR9300_BASE_ADDR_512 0x1ff
+
+#define AR9300_OTP_BASE			0x14000
+#define AR9300_OTP_STATUS		0x15f18
+#define AR9300_OTP_STATUS_TYPE		0x7
+#define AR9300_OTP_STATUS_VALID		0x4
+#define AR9300_OTP_STATUS_ACCESS_BUSY	0x2
+#define AR9300_OTP_STATUS_SM_BUSY	0x1
+#define AR9300_OTP_READ_DATA		0x15f1c
 
 enum targetPowerHTRates {
 	HT_TARGET_RATE_0_8_16,