Commit d63609e4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'platform-drivers-x86-v6.14-3' of...

Merge tag 'platform-drivers-x86-v6.14-3' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver fixes from Ilpo Järvinen:

 - thinkpad_acpi:
     - Fix registration of tpacpi platform driver
     - Support fan speed in ticks per revolution (Thinkpad X120e)
     - Support V9 DYTC profiles (new Thinkpad AMD platforms)

 - int3472: Handle GPIO "enable" vs "reset" variation (ov7251)

* tag 'platform-drivers-x86-v6.14-3' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86:
  platform/x86: thinkpad_acpi: Fix registration of tpacpi platform driver
  platform/x86: int3472: Call "reset" GPIO "enable" for INT347E
  platform/x86: int3472: Use correct type for "polarity", call it gpio_flags
  platform/x86: thinkpad_acpi: Support for V9 DYTC platform profiles
  platform/x86: thinkpad_acpi: Fix invalid fan speed on ThinkPad X120e
parents 4dc1d1be b3e127da
Loading
Loading
Loading
Loading
+65 −20
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
/* Author: Dan Scally <djrscally@gmail.com> */

#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
@@ -55,7 +56,7 @@ static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *i

static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
					 struct acpi_resource_gpio *agpio,
					 const char *func, u32 polarity)
					 const char *func, unsigned long gpio_flags)
{
	char *path = agpio->resource_source.string_ptr;
	struct acpi_device *adev;
@@ -70,14 +71,14 @@ static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
	if (!adev)
		return -ENODEV;

	*table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, polarity);
	*table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, gpio_flags);

	return 0;
}

static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
					  struct acpi_resource_gpio *agpio,
					  const char *func, u32 polarity)
					  const char *func, unsigned long gpio_flags)
{
	int ret;

@@ -87,7 +88,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
	}

	ret = skl_int3472_fill_gpiod_lookup(&int3472->gpios.table[int3472->n_sensor_gpios],
					    agpio, func, polarity);
					    agpio, func, gpio_flags);
	if (ret)
		return ret;

@@ -100,7 +101,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
static struct gpio_desc *
skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
				       struct acpi_resource_gpio *agpio,
				       const char *func, u32 polarity)
				       const char *func, unsigned long gpio_flags)
{
	struct gpio_desc *desc;
	int ret;
@@ -111,7 +112,7 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
		return ERR_PTR(-ENOMEM);

	lookup->dev_id = dev_name(int3472->dev);
	ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, func, polarity);
	ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, func, gpio_flags);
	if (ret)
		return ERR_PTR(ret);

@@ -122,32 +123,76 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
	return desc;
}

static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)
/**
 * struct int3472_gpio_map - Map GPIOs to whatever is expected by the
 * sensor driver (as in DT bindings)
 * @hid: The ACPI HID of the device without the instance number e.g. INT347E
 * @type_from: The GPIO type from ACPI ?SDT
 * @type_to: The assigned GPIO type, typically same as @type_from
 * @func: The function, e.g. "enable"
 * @polarity_low: GPIO_ACTIVE_LOW true if the @polarity_low is true,
 * GPIO_ACTIVE_HIGH otherwise
 */
struct int3472_gpio_map {
	const char *hid;
	u8 type_from;
	u8 type_to;
	bool polarity_low;
	const char *func;
};

static const struct int3472_gpio_map int3472_gpio_map[] = {
	{ "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" },
};

static void int3472_get_func_and_polarity(struct acpi_device *adev, u8 *type,
					  const char **func, unsigned long *gpio_flags)
{
	switch (type) {
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(int3472_gpio_map); i++) {
		/*
		 * Map the firmware-provided GPIO to whatever a driver expects
		 * (as in DT bindings). First check if the type matches with the
		 * GPIO map, then further check that the device _HID matches.
		 */
		if (*type != int3472_gpio_map[i].type_from)
			continue;

		if (!acpi_dev_hid_uid_match(adev, int3472_gpio_map[i].hid, NULL))
			continue;

		*type = int3472_gpio_map[i].type_to;
		*gpio_flags = int3472_gpio_map[i].polarity_low ?
			      GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH;
		*func = int3472_gpio_map[i].func;
		return;
	}

	switch (*type) {
	case INT3472_GPIO_TYPE_RESET:
		*func = "reset";
		*polarity = GPIO_ACTIVE_LOW;
		*gpio_flags = GPIO_ACTIVE_LOW;
		break;
	case INT3472_GPIO_TYPE_POWERDOWN:
		*func = "powerdown";
		*polarity = GPIO_ACTIVE_LOW;
		*gpio_flags = GPIO_ACTIVE_LOW;
		break;
	case INT3472_GPIO_TYPE_CLK_ENABLE:
		*func = "clk-enable";
		*polarity = GPIO_ACTIVE_HIGH;
		*gpio_flags = GPIO_ACTIVE_HIGH;
		break;
	case INT3472_GPIO_TYPE_PRIVACY_LED:
		*func = "privacy-led";
		*polarity = GPIO_ACTIVE_HIGH;
		*gpio_flags = GPIO_ACTIVE_HIGH;
		break;
	case INT3472_GPIO_TYPE_POWER_ENABLE:
		*func = "power-enable";
		*polarity = GPIO_ACTIVE_HIGH;
		*gpio_flags = GPIO_ACTIVE_HIGH;
		break;
	default:
		*func = "unknown";
		*polarity = GPIO_ACTIVE_HIGH;
		*gpio_flags = GPIO_ACTIVE_HIGH;
		break;
	}
}
@@ -194,7 +239,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
	struct gpio_desc *gpio;
	const char *err_msg;
	const char *func;
	u32 polarity;
	unsigned long gpio_flags;
	int ret;

	if (!acpi_gpio_get_io_resource(ares, &agpio))
@@ -217,7 +262,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,

	type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);

	int3472_get_func_and_polarity(type, &func, &polarity);
	int3472_get_func_and_polarity(int3472->sensor, &type, &func, &gpio_flags);

	pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
	/* Pin field is not really used under Windows and wraps around at 8 bits */
@@ -227,16 +272,16 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,

	active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value);
	if (!active_value)
		polarity ^= GPIO_ACTIVE_LOW;
		gpio_flags ^= GPIO_ACTIVE_LOW;

	dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func,
		agpio->resource_source.string_ptr, agpio->pin_table[0],
		str_high_low(polarity == GPIO_ACTIVE_HIGH));
		str_high_low(gpio_flags == GPIO_ACTIVE_HIGH));

	switch (type) {
	case INT3472_GPIO_TYPE_RESET:
	case INT3472_GPIO_TYPE_POWERDOWN:
		ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity);
		ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, gpio_flags);
		if (ret)
			err_msg = "Failed to map GPIO pin to sensor\n";

@@ -244,7 +289,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
	case INT3472_GPIO_TYPE_CLK_ENABLE:
	case INT3472_GPIO_TYPE_PRIVACY_LED:
	case INT3472_GPIO_TYPE_POWER_ENABLE:
		gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, func, polarity);
		gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, func, gpio_flags);
		if (IS_ERR(gpio)) {
			ret = PTR_ERR(gpio);
			err_msg = "Failed to get GPIO\n";
+45 −16
Original line number Diff line number Diff line
@@ -7885,6 +7885,7 @@ static struct ibm_struct volume_driver_data = {

#define FAN_NS_CTRL_STATUS	BIT(2)		/* Bit which determines control is enabled or not */
#define FAN_NS_CTRL		BIT(4)		/* Bit which determines control is by host or EC */
#define FAN_CLOCK_TPM		(22500*60)	/* Ticks per minute for a 22.5 kHz clock */

enum {					/* Fan control constants */
	fan_status_offset = 0x2f,	/* EC register 0x2f */
@@ -7940,6 +7941,7 @@ static int fan_watchdog_maxinterval;

static bool fan_with_ns_addr;
static bool ecfw_with_fan_dec_rpm;
static bool fan_speed_in_tpr;

static struct mutex fan_mutex;

@@ -8142,8 +8144,11 @@ static int fan_get_speed(unsigned int *speed)
			     !acpi_ec_read(fan_rpm_offset + 1, &hi)))
			return -EIO;

		if (likely(speed))
		if (likely(speed)) {
			*speed = (hi << 8) | lo;
			if (fan_speed_in_tpr && *speed != 0)
				*speed = FAN_CLOCK_TPM / *speed;
		}
		break;
	case TPACPI_FAN_RD_TPEC_NS:
		if (!acpi_ec_read(fan_rpm_status_ns, &lo))
@@ -8176,8 +8181,11 @@ static int fan2_get_speed(unsigned int *speed)
		if (rc)
			return -EIO;

		if (likely(speed))
		if (likely(speed)) {
			*speed = (hi << 8) | lo;
			if (fan_speed_in_tpr && *speed != 0)
				*speed = FAN_CLOCK_TPM / *speed;
		}
		break;

	case TPACPI_FAN_RD_TPEC_NS:
@@ -8788,6 +8796,7 @@ static const struct attribute_group fan_driver_attr_group = {
#define TPACPI_FAN_NOFAN	0x0008		/* no fan available */
#define TPACPI_FAN_NS		0x0010		/* For EC with non-Standard register addresses */
#define TPACPI_FAN_DECRPM	0x0020		/* For ECFW's with RPM in register as decimal */
#define TPACPI_FAN_TPR		0x0040		/* Fan speed is in Ticks Per Revolution */

static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
	TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
@@ -8817,6 +8826,7 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
	TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS),	/* 11e Gen5 KL-Y */
	TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN),	/* X1 Tablet (2nd gen) */
	TPACPI_Q_LNV3('R', '0', 'Q', TPACPI_FAN_DECRPM),/* L480 */
	TPACPI_Q_LNV('8', 'F', TPACPI_FAN_TPR),		/* ThinkPad x120e */
};

static int __init fan_init(struct ibm_init_struct *iibm)
@@ -8887,6 +8897,8 @@ static int __init fan_init(struct ibm_init_struct *iibm)

			if (quirks & TPACPI_FAN_Q1)
				fan_quirk1_setup();
			if (quirks & TPACPI_FAN_TPR)
				fan_speed_in_tpr = true;
			/* Try and probe the 2nd fan */
			tp_features.second_fan = 1; /* needed for get_speed to work */
			res = fan2_get_speed(&speed);
@@ -10319,6 +10331,10 @@ static struct ibm_struct proxsensor_driver_data = {
#define DYTC_MODE_PSC_BALANCE  5  /* Default mode aka balanced */
#define DYTC_MODE_PSC_PERFORM  7  /* High power mode aka performance */

#define DYTC_MODE_PSCV9_LOWPOWER 1  /* Low power mode */
#define DYTC_MODE_PSCV9_BALANCE  3  /* Default mode aka balanced */
#define DYTC_MODE_PSCV9_PERFORM  4  /* High power mode aka performance */

#define DYTC_ERR_MASK       0xF  /* Bits 0-3 in cmd result are the error result */
#define DYTC_ERR_SUCCESS      1  /* CMD completed successful */

@@ -10339,6 +10355,10 @@ static int dytc_capabilities;
static bool dytc_mmc_get_available;
static int profile_force;

static int platform_psc_profile_lowpower = DYTC_MODE_PSC_LOWPOWER;
static int platform_psc_profile_balanced = DYTC_MODE_PSC_BALANCE;
static int platform_psc_profile_performance = DYTC_MODE_PSC_PERFORM;

static int convert_dytc_to_profile(int funcmode, int dytcmode,
		enum platform_profile_option *profile)
{
@@ -10360,19 +10380,15 @@ static int convert_dytc_to_profile(int funcmode, int dytcmode,
		}
		return 0;
	case DYTC_FUNCTION_PSC:
		switch (dytcmode) {
		case DYTC_MODE_PSC_LOWPOWER:
		if (dytcmode == platform_psc_profile_lowpower)
			*profile = PLATFORM_PROFILE_LOW_POWER;
			break;
		case DYTC_MODE_PSC_BALANCE:
		else if (dytcmode == platform_psc_profile_balanced)
			*profile =  PLATFORM_PROFILE_BALANCED;
			break;
		case DYTC_MODE_PSC_PERFORM:
		else if (dytcmode == platform_psc_profile_performance)
			*profile =  PLATFORM_PROFILE_PERFORMANCE;
			break;
		default: /* Unknown mode */
		else
			return -EINVAL;
		}

		return 0;
	case DYTC_FUNCTION_AMT:
		/* For now return balanced. It's the closest we have to 'auto' */
@@ -10393,19 +10409,19 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe
		if (dytc_capabilities & BIT(DYTC_FC_MMC))
			*perfmode = DYTC_MODE_MMC_LOWPOWER;
		else if (dytc_capabilities & BIT(DYTC_FC_PSC))
			*perfmode = DYTC_MODE_PSC_LOWPOWER;
			*perfmode = platform_psc_profile_lowpower;
		break;
	case PLATFORM_PROFILE_BALANCED:
		if (dytc_capabilities & BIT(DYTC_FC_MMC))
			*perfmode = DYTC_MODE_MMC_BALANCE;
		else if (dytc_capabilities & BIT(DYTC_FC_PSC))
			*perfmode = DYTC_MODE_PSC_BALANCE;
			*perfmode = platform_psc_profile_balanced;
		break;
	case PLATFORM_PROFILE_PERFORMANCE:
		if (dytc_capabilities & BIT(DYTC_FC_MMC))
			*perfmode = DYTC_MODE_MMC_PERFORM;
		else if (dytc_capabilities & BIT(DYTC_FC_PSC))
			*perfmode = DYTC_MODE_PSC_PERFORM;
			*perfmode = platform_psc_profile_performance;
		break;
	default: /* Unknown profile */
		return -EOPNOTSUPP;
@@ -10599,6 +10615,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
	if (output & BIT(DYTC_QUERY_ENABLE_BIT))
		dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;

	dbg_printk(TPACPI_DBG_INIT, "DYTC version %d\n", dytc_version);
	/* Check DYTC is enabled and supports mode setting */
	if (dytc_version < 5)
		return -ENODEV;
@@ -10637,6 +10654,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
		}
	} else if (dytc_capabilities & BIT(DYTC_FC_PSC)) { /* PSC MODE */
		pr_debug("PSC is supported\n");
		if (dytc_version >= 9) { /* update profiles for DYTC 9 and up */
			platform_psc_profile_lowpower = DYTC_MODE_PSCV9_LOWPOWER;
			platform_psc_profile_balanced = DYTC_MODE_PSCV9_BALANCE;
			platform_psc_profile_performance = DYTC_MODE_PSCV9_PERFORM;
		}
	} else {
		dbg_printk(TPACPI_DBG_INIT, "No DYTC support available\n");
		return -ENODEV;
@@ -10646,7 +10668,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
			"DYTC version %d: thermal mode available\n", dytc_version);

	/* Create platform_profile structure and register */
	tpacpi_pprof = devm_platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi",
	tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile",
						 NULL, &dytc_profile_ops);
	/*
	 * If for some reason platform_profiles aren't enabled
@@ -10665,8 +10687,15 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
	return 0;
}

static void dytc_profile_exit(void)
{
	if (!IS_ERR_OR_NULL(tpacpi_pprof))
		platform_profile_remove(tpacpi_pprof);
}

static struct ibm_struct  dytc_profile_driver_data = {
	.name = "dytc-profile",
	.exit = dytc_profile_exit,
};

/*************************************************************************