Commit 6b27354c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i2c fixes from Wolfram Sang:
 "An I2C core fix to prevent a use-after-free in a rare error path,
  and an I2C ACPI addition to work around broken HW/firmware related
  to touchscreens"

* 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  i2c: core: fix use after free in of_i2c_notify
  i2c: acpi: Force bus speed to 400KHz if a Silead touchscreen is present
parents 1d4c79ed a4c2fec1
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ struct i2c_acpi_lookup {
	int index;
	u32 speed;
	u32 min_speed;
	u32 force_speed;
};

/**
@@ -285,6 +286,19 @@ i2c_acpi_match_device(const struct acpi_device_id *matches,
	return acpi_match_device(matches, &client->dev);
}

static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = {
	/*
	 * These Silead touchscreen controllers only work at 400KHz, for
	 * some reason they do not work at 100KHz. On some devices the ACPI
	 * tables list another device at their bus as only being capable
	 * of 100KHz, testing has shown that these other devices work fine
	 * at 400KHz (as can be expected of any recent i2c hw) so we force
	 * the speed of the bus to 400 KHz if a Silead device is present.
	 */
	{ "MSSL1680", 0 },
	{}
};

static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
					   void *data, void **return_value)
{
@@ -303,6 +317,9 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
	if (lookup->speed <= lookup->min_speed)
		lookup->min_speed = lookup->speed;

	if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0)
		lookup->force_speed = 400000;

	return AE_OK;
}

@@ -340,7 +357,16 @@ u32 i2c_acpi_find_bus_speed(struct device *dev)
		return 0;
	}

	return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0;
	if (lookup.force_speed) {
		if (lookup.force_speed != lookup.min_speed)
			dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n",
				 lookup.min_speed, lookup.force_speed);
		return lookup.force_speed;
	} else if (lookup.min_speed != UINT_MAX) {
		return lookup.min_speed;
	} else {
		return 0;
	}
}
EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed);

+2 −2
Original line number Diff line number Diff line
@@ -245,14 +245,14 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
		}

		client = of_i2c_register_device(adap, rd->dn);
		put_device(&adap->dev);

		if (IS_ERR(client)) {
			dev_err(&adap->dev, "failed to create client for '%pOF'\n",
				 rd->dn);
			put_device(&adap->dev);
			of_node_clear_flag(rd->dn, OF_POPULATED);
			return notifier_from_errno(PTR_ERR(client));
		}
		put_device(&adap->dev);
		break;
	case OF_RECONFIG_CHANGE_REMOVE:
		/* already depopulated? */