|
@@ -409,13 +409,18 @@ static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16,
|
|
|
return len;
|
|
|
}
|
|
|
|
|
|
-static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len)
|
|
|
+static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len)
|
|
|
{
|
|
|
- u8 buf, *data;
|
|
|
- struct em28xx_eeprom *em_eeprom;
|
|
|
+ const u16 len = 256;
|
|
|
+ /* FIXME common length/size for bytes to read, to display, hash
|
|
|
+ * calculation and returned device dataset. Simplifies the code a lot,
|
|
|
+ * but we might have to deal with multiple sizes in the future ! */
|
|
|
int i, err;
|
|
|
+ struct em28xx_eeprom *dev_config;
|
|
|
+ u8 buf, *data;
|
|
|
|
|
|
*eedata = NULL;
|
|
|
+ *eedata_len = 0;
|
|
|
|
|
|
dev->i2c_client.addr = 0xa0 >> 1;
|
|
|
|
|
@@ -435,8 +440,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
|
|
len, data);
|
|
|
if (err != len) {
|
|
|
em28xx_errdev("failed to read eeprom (err=%d)\n", err);
|
|
|
- kfree(data);
|
|
|
- return err;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
/* Display eeprom content */
|
|
@@ -451,15 +455,25 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
|
|
if (15 == (i % 16))
|
|
|
printk("\n");
|
|
|
}
|
|
|
+ if (dev->eeprom_addrwidth_16bit)
|
|
|
+ em28xx_info("i2c eeprom %04x: ... (skipped)\n", i);
|
|
|
|
|
|
if (dev->eeprom_addrwidth_16bit &&
|
|
|
data[0] == 0x26 && data[3] == 0x00) {
|
|
|
/* new eeprom format; size 4-64kb */
|
|
|
+ u16 mc_start;
|
|
|
+ u16 hwconf_offset;
|
|
|
+
|
|
|
dev->hash = em28xx_hash_mem(data, len, 32);
|
|
|
- em28xx_info("EEPROM hash = 0x%08lx\n", dev->hash);
|
|
|
- em28xx_info("EEPROM info: boot page address = 0x%02x04, "
|
|
|
+ mc_start = (data[1] << 8) + 4; /* usually 0x0004 */
|
|
|
+
|
|
|
+ em28xx_info("EEPROM ID = %02x %02x %02x %02x, "
|
|
|
+ "EEPROM hash = 0x%08lx\n",
|
|
|
+ data[0], data[1], data[2], data[3], dev->hash);
|
|
|
+ em28xx_info("EEPROM info:\n");
|
|
|
+ em28xx_info("\tmicrocode start address = 0x%04x, "
|
|
|
"boot configuration = 0x%02x\n",
|
|
|
- data[1], data[2]);
|
|
|
+ mc_start, data[2]);
|
|
|
/* boot configuration (address 0x0002):
|
|
|
* [0] microcode download speed: 1 = 400 kHz; 0 = 100 kHz
|
|
|
* [1] always selects 12 kb RAM
|
|
@@ -469,32 +483,61 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
|
|
* characterization
|
|
|
*/
|
|
|
|
|
|
- /* FIXME:
|
|
|
- * - read more than 256 bytes / addresses above 0x00ff
|
|
|
- * - find offset for device config dataset and extract it
|
|
|
- * - decrypt eeprom data for camera bridges (em25xx, em276x+)
|
|
|
- * - use separate/different eeprom hashes (not yet used)
|
|
|
+ /* Read hardware config dataset offset from address
|
|
|
+ * (microcode start + 46) */
|
|
|
+ err = em28xx_i2c_read_block(dev, mc_start + 46, 1, 2, data);
|
|
|
+ if (err != 2) {
|
|
|
+ em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n",
|
|
|
+ err);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Calculate hardware config dataset start address */
|
|
|
+ hwconf_offset = mc_start + data[0] + (data[1] << 8);
|
|
|
+
|
|
|
+ /* Read hardware config dataset */
|
|
|
+ /* NOTE: the microcode copy can be multiple pages long, but
|
|
|
+ * we assume the hardware config dataset is the same as in
|
|
|
+ * the old eeprom and not longer than 256 bytes.
|
|
|
+ * tveeprom is currently also limited to 256 bytes.
|
|
|
*/
|
|
|
+ err = em28xx_i2c_read_block(dev, hwconf_offset, 1, len, data);
|
|
|
+ if (err != len) {
|
|
|
+ em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n",
|
|
|
+ err);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
|
|
|
- return 0;
|
|
|
- } else if (data[0] != 0x1a || data[1] != 0xeb ||
|
|
|
- data[2] != 0x67 || data[3] != 0x95) {
|
|
|
+ /* Verify hardware config dataset */
|
|
|
+ /* NOTE: not all devices provide this type of dataset */
|
|
|
+ if (data[0] != 0x1a || data[1] != 0xeb ||
|
|
|
+ data[2] != 0x67 || data[3] != 0x95) {
|
|
|
+ em28xx_info("\tno hardware configuration dataset found in eeprom\n");
|
|
|
+ kfree(data);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* TODO: decrypt eeprom data for camera bridges (em25xx, em276x+) */
|
|
|
+
|
|
|
+ } else if (!dev->eeprom_addrwidth_16bit &&
|
|
|
+ data[0] == 0x1a && data[1] == 0xeb &&
|
|
|
+ data[2] == 0x67 && data[3] == 0x95) {
|
|
|
+ dev->hash = em28xx_hash_mem(data, len, 32);
|
|
|
+ em28xx_info("EEPROM ID = %02x %02x %02x %02x, "
|
|
|
+ "EEPROM hash = 0x%08lx\n",
|
|
|
+ data[0], data[1], data[2], data[3], dev->hash);
|
|
|
+ em28xx_info("EEPROM info:\n");
|
|
|
+ } else {
|
|
|
em28xx_info("unknown eeprom format or eeprom corrupted !\n");
|
|
|
- return -ENODEV;
|
|
|
+ err = -ENODEV;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
*eedata = data;
|
|
|
- em_eeprom = (void *)eedata;
|
|
|
+ *eedata_len = len;
|
|
|
+ dev_config = (void *)eedata;
|
|
|
|
|
|
- dev->hash = em28xx_hash_mem(data, len, 32);
|
|
|
-
|
|
|
- em28xx_info("EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n",
|
|
|
- em_eeprom->id[0], em_eeprom->id[1],
|
|
|
- em_eeprom->id[2], em_eeprom->id[3], dev->hash);
|
|
|
-
|
|
|
- em28xx_info("EEPROM info:\n");
|
|
|
-
|
|
|
- switch (le16_to_cpu(em_eeprom->chip_conf) >> 4 & 0x3) {
|
|
|
+ switch (le16_to_cpu(dev_config->chip_conf) >> 4 & 0x3) {
|
|
|
case 0:
|
|
|
em28xx_info("\tNo audio on board.\n");
|
|
|
break;
|
|
@@ -509,13 +552,13 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- if (le16_to_cpu(em_eeprom->chip_conf) & 1 << 3)
|
|
|
+ if (le16_to_cpu(dev_config->chip_conf) & 1 << 3)
|
|
|
em28xx_info("\tUSB Remote wakeup capable\n");
|
|
|
|
|
|
- if (le16_to_cpu(em_eeprom->chip_conf) & 1 << 2)
|
|
|
+ if (le16_to_cpu(dev_config->chip_conf) & 1 << 2)
|
|
|
em28xx_info("\tUSB Self power capable\n");
|
|
|
|
|
|
- switch (le16_to_cpu(em_eeprom->chip_conf) & 0x3) {
|
|
|
+ switch (le16_to_cpu(dev_config->chip_conf) & 0x3) {
|
|
|
case 0:
|
|
|
em28xx_info("\t500mA max power\n");
|
|
|
break;
|
|
@@ -530,12 +573,16 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
|
|
break;
|
|
|
}
|
|
|
em28xx_info("\tTable at offset 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
|
|
|
- em_eeprom->string_idx_table,
|
|
|
- le16_to_cpu(em_eeprom->string1),
|
|
|
- le16_to_cpu(em_eeprom->string2),
|
|
|
- le16_to_cpu(em_eeprom->string3));
|
|
|
+ dev_config->string_idx_table,
|
|
|
+ le16_to_cpu(dev_config->string1),
|
|
|
+ le16_to_cpu(dev_config->string2),
|
|
|
+ le16_to_cpu(dev_config->string3));
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ kfree(data);
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------- */
|
|
@@ -644,7 +691,7 @@ int em28xx_i2c_register(struct em28xx *dev)
|
|
|
dev->i2c_client = em28xx_client_template;
|
|
|
dev->i2c_client.adapter = &dev->i2c_adap;
|
|
|
|
|
|
- retval = em28xx_i2c_eeprom(dev, &dev->eedata, 256);
|
|
|
+ retval = em28xx_i2c_eeprom(dev, &dev->eedata, &dev->eedata_len);
|
|
|
if ((retval < 0) && (retval != -ENODEV)) {
|
|
|
em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
|
|
|
__func__, retval);
|