浏览代码

sound: oxygen: automatically restore overwritten EEPROM

If the EEPROM was partially overwritten (which seems to happen before the OS is
booted), restore its entire contents by deducing it from the remaining
information.

This does not have any effect on the Linux driver, which works even with
incomplete information in the EEPROM, but it makes other drivers work again.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Clemens Ladisch 16 年之前
父节点
当前提交
1275d6f608
共有 3 个文件被更改,包括 48 次插入0 次删除
  1. 3 0
      sound/pci/oxygen/oxygen.h
  2. 16 0
      sound/pci/oxygen/oxygen_io.c
  3. 29 0
      sound/pci/oxygen/oxygen_lib.c

+ 3 - 0
sound/pci/oxygen/oxygen.h

@@ -18,6 +18,8 @@
 
 #define OXYGEN_IO_SIZE	0x100
 
+#define OXYGEN_EEPROM_ID	0x434d	/* "CM" */
+
 /* model-specific configuration of outputs/inputs */
 #define PLAYBACK_0_TO_I2S	0x0001
      /* PLAYBACK_0_TO_AC97_0		not implemented */
@@ -190,6 +192,7 @@ void oxygen_reset_uart(struct oxygen *chip);
 void oxygen_write_uart(struct oxygen *chip, u8 data);
 
 u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index);
+void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value);
 
 static inline void oxygen_set_bits8(struct oxygen *chip,
 				    unsigned int reg, u8 value)

+ 16 - 0
sound/pci/oxygen/oxygen_io.c

@@ -269,3 +269,19 @@ u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index)
 	}
 	return oxygen_read16(chip, OXYGEN_EEPROM_DATA);
 }
+
+void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)
+{
+	unsigned int timeout;
+
+	oxygen_write16(chip, OXYGEN_EEPROM_DATA, value);
+	oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
+		      index | OXYGEN_EEPROM_DIR_WRITE);
+	for (timeout = 0; timeout < 10; ++timeout) {
+		msleep(1);
+		if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
+		      & OXYGEN_EEPROM_BUSY))
+			return;
+	}
+	snd_printk(KERN_ERR "EEPROM write timeout\n");
+}

+ 29 - 0
sound/pci/oxygen/oxygen_lib.c

@@ -272,6 +272,34 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
 	return NULL;
 }
 
+static void oxygen_restore_eeprom(struct oxygen *chip,
+				  const struct pci_device_id *id)
+{
+	if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
+		/*
+		 * This function gets called only when a known card model has
+		 * been detected, i.e., we know there is a valid subsystem
+		 * product ID at index 2 in the EEPROM.  Therefore, we have
+		 * been able to deduce the correct subsystem vendor ID, and
+		 * this is enough information to restore the original EEPROM
+		 * contents.
+		 */
+		oxygen_write_eeprom(chip, 1, id->subvendor);
+		oxygen_write_eeprom(chip, 0, OXYGEN_EEPROM_ID);
+
+		oxygen_set_bits8(chip, OXYGEN_MISC,
+				 OXYGEN_MISC_WRITE_PCI_SUBID);
+		pci_write_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID,
+				      id->subvendor);
+		pci_write_config_word(chip->pci, PCI_SUBSYSTEM_ID,
+				      id->subdevice);
+		oxygen_clear_bits8(chip, OXYGEN_MISC,
+				   OXYGEN_MISC_WRITE_PCI_SUBID);
+
+		snd_printk(KERN_INFO "EEPROM ID restored\n");
+	}
+}
+
 static void oxygen_init(struct oxygen *chip)
 {
 	unsigned int i;
@@ -532,6 +560,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
 		err = -ENODEV;
 		goto err_pci_regions;
 	}
+	oxygen_restore_eeprom(chip, pci_id);
 	err = get_model(chip, pci_id);
 	if (err < 0)
 		goto err_pci_regions;