Hi!

> [ Upstream commit 66e92e23a72761f5b53f970aeb1badc5fd92fc74 ]
> 
> 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.

This is just a new feature, and is > 200 lines. We should not have
this in stable.

BR,
                                                                Pavel





> Suggested-by: Mark Pearson <mpearson-len...@squebb.ca>
> Signed-off-by: Vishnu Sankar <vishnu...@gmail.com>
> Reviewed-by: Hans de Goede <hdego...@redhat.com>
> Reviewed-by: Ilpo Järvinen <ilpo.jarvi...@linux.intel.com>
> Link: https://lore.kernel.org/r/20231214134702.166464-1-vishnu...@gmail.com
> Signed-off-by: Ilpo Järvinen <ilpo.jarvi...@linux.intel.com>
> Signed-off-by: Sasha Levin <sas...@kernel.org>
> ---
>  drivers/platform/x86/thinkpad_acpi.c | 98 ++++++++++++++++++++++++----
>  1 file changed, 85 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/platform/x86/thinkpad_acpi.c 
> b/drivers/platform/x86/thinkpad_acpi.c
> index 05a55bc31c796..6edd2e294750e 100644
> --- a/drivers/platform/x86/thinkpad_acpi.c
> +++ b/drivers/platform/x86/thinkpad_acpi.c
> @@ -8149,8 +8149,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)
> @@ -8158,6 +8169,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 */
>  
> @@ -8168,6 +8184,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 {
> @@ -8195,6 +8212,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);
> @@ -8325,6 +8344,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;
> @@ -8341,7 +8369,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);
>  
> @@ -8368,7 +8397,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:
> @@ -8380,7 +8415,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) {
> @@ -8396,7 +8431,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:
> @@ -8899,6 +8948,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),
> @@ -8917,6 +8967,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) 
> */
>  };
>  
> @@ -8951,18 +9003,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 */
> @@ -8971,7 +9032,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 */
> @@ -9146,6 +9208,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);
> @@ -9160,13 +9223,22 @@ static int fan_read(struct seq_file *m)
>  
>               seq_printf(m, "speed:\t\t%d\n", speed);
>  
> -             if (status & TP_EC_FAN_FULLSPEED)
> -                     /* Disengaged mode takes precedence */
> -                     seq_printf(m, "level:\t\tdisengaged\n");
> -             else if (status & TP_EC_FAN_AUTO)
> -                     seq_printf(m, "level:\t\tauto\n");
> -             else
> -                     seq_printf(m, "level:\t\t%d\n", status);
> +             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");
> +                     else if (status & TP_EC_FAN_AUTO)
> +                             seq_printf(m, "level:\t\tauto\n");
> +                     else
> +                             seq_printf(m, "level:\t\t%d\n", status);
> +             }
>               break;
>  
>       case TPACPI_FAN_NONE:

-- 
People of Russia, stop Putin before his war on Ukraine escalates.

Attachment: signature.asc
Description: PGP signature

_______________________________________________
ibm-acpi-devel mailing list
ibm-acpi-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ibm-acpi-devel

Reply via email to