Unverified Commit 66e92e23 authored by Vishnu Sankar's avatar Vishnu Sankar Committed by Ilpo Järvinen
Browse files

platform/x86: thinkpad_acpi: fix for incorrect fan reporting on some ThinkPad systems



Some ThinkPad systems ECFW use non-standard addresses for fan control
and reporting. This patch adds support for such ECFW so that it can report
the correct fan values.
Tested on Thinkpads L13 Yoga Gen 2 and X13 Yoga Gen 2.

Suggested-by: default avatarMark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: default avatarVishnu Sankar <vishnuocv@gmail.com>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Reviewed-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20231214134702.166464-1-vishnuocv@gmail.com


Signed-off-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
parent 7bcd0323
Loading
Loading
Loading
Loading
+85 −13
Original line number Diff line number Diff line
@@ -7948,8 +7948,19 @@ static struct ibm_struct volume_driver_data = {
 * 	TPACPI_FAN_WR_TPEC is also available and should be used to
 * 	command the fan.  The X31/X40/X41 seems to have 8 fan levels,
 * 	but the ACPI tables just mention level 7.
 *
 * TPACPI_FAN_RD_TPEC_NS:
 *	This mode is used for a few ThinkPads (L13 Yoga Gen2, X13 Yoga Gen2 etc.)
 *	that are using non-standard EC locations for reporting fan speeds.
 *	Currently these platforms only provide fan rpm reporting.
 *
 */

#define FAN_RPM_CAL_CONST 491520	/* FAN RPM calculation offset for some non-standard ECFW */

#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 */

enum {					/* Fan control constants */
	fan_status_offset = 0x2f,	/* EC register 0x2f */
	fan_rpm_offset = 0x84,		/* EC register 0x84: LSB, 0x85 MSB (RPM)
@@ -7957,6 +7968,11 @@ enum { /* Fan control constants */
	fan_select_offset = 0x31,	/* EC register 0x31 (Firmware 7M)
					   bit 0 selects which fan is active */

	fan_status_offset_ns = 0x93,	/* Special status/control offset for non-standard EC Fan1 */
	fan2_status_offset_ns = 0x96,	/* Special status/control offset for non-standard EC Fan2 */
	fan_rpm_status_ns = 0x95,	/* Special offset for Fan1 RPM status for non-standard EC */
	fan2_rpm_status_ns = 0x98,	/* Special offset for Fan2 RPM status for non-standard EC */

	TP_EC_FAN_FULLSPEED = 0x40,	/* EC fan mode: full speed */
	TP_EC_FAN_AUTO	    = 0x80,	/* EC fan mode: auto fan control */

@@ -7967,6 +7983,7 @@ enum fan_status_access_mode {
	TPACPI_FAN_NONE = 0,		/* No fan status or control */
	TPACPI_FAN_RD_ACPI_GFAN,	/* Use ACPI GFAN */
	TPACPI_FAN_RD_TPEC,		/* Use ACPI EC regs 0x2f, 0x84-0x85 */
	TPACPI_FAN_RD_TPEC_NS,		/* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */
};

enum fan_control_access_mode {
@@ -7994,6 +8011,8 @@ static u8 fan_control_desired_level;
static u8 fan_control_resume_level;
static int fan_watchdog_maxinterval;

static bool fan_with_ns_addr;

static struct mutex fan_mutex;

static void fan_watchdog_fire(struct work_struct *ignored);
@@ -8123,6 +8142,15 @@ static int fan_get_status(u8 *status)
		}

		break;
	case TPACPI_FAN_RD_TPEC_NS:
		/* Default mode is AUTO which means controlled by EC */
		if (!acpi_ec_read(fan_status_offset_ns, &s))
			return -EIO;

		if (status)
			*status = s;

		break;

	default:
		return -ENXIO;
@@ -8139,7 +8167,8 @@ static int fan_get_status_safe(u8 *status)
	if (mutex_lock_killable(&fan_mutex))
		return -ERESTARTSYS;
	rc = fan_get_status(&s);
	if (!rc)
	/* NS EC doesn't have register with level settings */
	if (!rc && !fan_with_ns_addr)
		fan_update_desired_level(s);
	mutex_unlock(&fan_mutex);

@@ -8166,7 +8195,13 @@ static int fan_get_speed(unsigned int *speed)

		if (likely(speed))
			*speed = (hi << 8) | lo;
		break;
	case TPACPI_FAN_RD_TPEC_NS:
		if (!acpi_ec_read(fan_rpm_status_ns, &lo))
			return -EIO;

		if (speed)
			*speed = lo ? FAN_RPM_CAL_CONST / lo : 0;
		break;

	default:
@@ -8178,7 +8213,7 @@ static int fan_get_speed(unsigned int *speed)

static int fan2_get_speed(unsigned int *speed)
{
	u8 hi, lo;
	u8 hi, lo, status;
	bool rc;

	switch (fan_status_access_mode) {
@@ -8194,7 +8229,21 @@ static int fan2_get_speed(unsigned int *speed)

		if (likely(speed))
			*speed = (hi << 8) | lo;
		break;

	case TPACPI_FAN_RD_TPEC_NS:
		rc = !acpi_ec_read(fan2_status_offset_ns, &status);
		if (rc)
			return -EIO;
		if (!(status & FAN_NS_CTRL_STATUS)) {
			pr_info("secondary fan control not supported\n");
			return -EIO;
		}
		rc = !acpi_ec_read(fan2_rpm_status_ns, &lo);
		if (rc)
			return -EIO;
		if (speed)
			*speed = lo ? FAN_RPM_CAL_CONST / lo : 0;
		break;

	default:
@@ -8697,6 +8746,7 @@ static const struct attribute_group fan_driver_attr_group = {
#define TPACPI_FAN_2FAN		0x0002		/* EC 0x31 bit 0 selects fan2 */
#define TPACPI_FAN_2CTL		0x0004		/* selects fan2 control */
#define TPACPI_FAN_NOFAN	0x0008		/* no fan available */
#define TPACPI_FAN_NS		0x0010		/* For EC with non-Standard register addresses */

static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
	TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
@@ -8715,6 +8765,8 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
	TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL),	/* P1 / X1 Extreme (2nd gen) */
	TPACPI_Q_LNV3('N', '3', '0', TPACPI_FAN_2CTL),	/* P15 (1st gen) / P15v (1st gen) */
	TPACPI_Q_LNV3('N', '3', '7', TPACPI_FAN_2CTL),  /* T15g (2nd gen) */
	TPACPI_Q_LNV3('R', '1', 'F', TPACPI_FAN_NS),	/* L13 Yoga Gen 2 */
	TPACPI_Q_LNV3('N', '2', 'U', TPACPI_FAN_NS),	/* X13 Yoga Gen 2*/
	TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN),	/* X1 Tablet (2nd gen) */
};

@@ -8749,18 +8801,27 @@ static int __init fan_init(struct ibm_init_struct *iibm)
		return -ENODEV;
	}

	if (quirks & TPACPI_FAN_NS) {
		pr_info("ECFW with non-standard fan reg control found\n");
		fan_with_ns_addr = 1;
		/* Fan ctrl support from host is undefined for now */
		tp_features.fan_ctrl_status_undef = 1;
	}

	if (gfan_handle) {
		/* 570, 600e/x, 770e, 770x */
		fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
	} else {
		/* all other ThinkPads: note that even old-style
		 * ThinkPad ECs supports the fan control register */
		if (likely(acpi_ec_read(fan_status_offset,
					&fan_control_initial_status))) {
		if (fan_with_ns_addr ||
		    likely(acpi_ec_read(fan_status_offset, &fan_control_initial_status))) {
			int res;
			unsigned int speed;

			fan_status_access_mode = TPACPI_FAN_RD_TPEC;
			fan_status_access_mode = fan_with_ns_addr ?
				TPACPI_FAN_RD_TPEC_NS : TPACPI_FAN_RD_TPEC;

			if (quirks & TPACPI_FAN_Q1)
				fan_quirk1_setup();
			/* Try and probe the 2nd fan */
@@ -8769,7 +8830,8 @@ static int __init fan_init(struct ibm_init_struct *iibm)
			if (res >= 0 && speed != FAN_NOT_PRESENT) {
				/* It responded - so let's assume it's there */
				tp_features.second_fan = 1;
				tp_features.second_fan_ctl = 1;
				/* fan control not currently available for ns ECFW */
				tp_features.second_fan_ctl = !fan_with_ns_addr;
				pr_info("secondary fan control detected & enabled\n");
			} else {
				/* Fan not auto-detected */
@@ -8944,6 +9006,7 @@ static int fan_read(struct seq_file *m)
			       str_enabled_disabled(status), status);
		break;

	case TPACPI_FAN_RD_TPEC_NS:
	case TPACPI_FAN_RD_TPEC:
		/* all except 570, 600e/x, 770e, 770x */
		rc = fan_get_status_safe(&status);
@@ -8958,6 +9021,14 @@ static int fan_read(struct seq_file *m)

		seq_printf(m, "speed:\t\t%d\n", speed);

		if (fan_status_access_mode == TPACPI_FAN_RD_TPEC_NS) {
			/*
			 * No full speed bit in NS EC
			 * EC Auto mode is set by default.
			 * No other levels settings available
			 */
			seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto");
		} else {
			if (status & TP_EC_FAN_FULLSPEED)
				/* Disengaged mode takes precedence */
				seq_printf(m, "level:\t\tdisengaged\n");
@@ -8965,6 +9036,7 @@ static int fan_read(struct seq_file *m)
				seq_printf(m, "level:\t\tauto\n");
			else
				seq_printf(m, "level:\t\t%d\n", status);
		}
		break;

	case TPACPI_FAN_NONE: