2022-02-06 22:28:23

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH] platform/x86: Add Steam Deck driver

On 2/5/22 18:20, Andrey Smirnov wrote:
> Add a driver exposing various bits and pieces of functionality
> provided by Steam Deck specific VLV0100 device presented by EC
> firmware. This includes but not limited to:
>
> - CPU/device's fan control
> - Read-only access to DDIC registers
> - Battery tempreature measurements
> - Various display related control knobs
> - USB Type-C connector event notification
>
> Cc: Hans de Goede <[email protected]>
> Cc: Mark Gross <[email protected]>
> Cc: Jean Delvare <[email protected]>
> Cc: Guenter Roeck <[email protected]>
> Cc: [email protected] (open list)
> Cc: [email protected]
> Cc: [email protected]
> Signed-off-by: Andrey Smirnov <[email protected]>
> ---
>
...

> +config STEAMDECK
> + tristate "Valve Steam Deck platform driver"
> + depends on X86_64
> + help
> + Driver exposing various bits and pieces of functionality
> + provided by Steam Deck specific VLV0100 device presented by
> + EC firmware. This includes but not limited to:

There seems to be some indentation issue.

> + - CPU/device's fan control
> + - Read-only access to DDIC registers
> + - Battery tempreature measurements
> + - Various display related control knobs
> + - USB Type-C connector event notification
> +
> + Say N unless you are running on a Steam Deck.
> +

This doesn't depend on hwmon, yet it fails if devm_hwmon_device_register_with_info()
returns an eror. That has a couple of problems: if HWMON=n, it won't compile,
and if STEAMDECK=y and HWMON=m it won't compile either. You'll have to provide
some dependency against HWMON to make this work.

...

> +
> +static int steamdeck_read_fan_speed(struct steamdeck *jup, long *speed)
> +{
> + unsigned long long val;
> +
> + if (ACPI_FAILURE(acpi_evaluate_integer(jup->adev->handle,
> + "FANR", NULL, &val)))
> + return -EIO;
> +
> + *speed = val;
> + return 0;
> +}
> +
> +static int
> +steamdeck_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *out)
> +{
> + struct steamdeck *sd = dev_get_drvdata(dev);
> + unsigned long long val;
> +
> + switch (type) {
> + case hwmon_temp:
> + if (attr != hwmon_temp_input)
> + return -EOPNOTSUPP;
> +
> + if (ACPI_FAILURE(acpi_evaluate_integer(sd->adev->handle,
> + "BATT", NULL, &val)))
> + return -EIO;
> + /*
> + * Assuming BATT returns deg C we need to mutiply it
> + * by 1000 to convert to mC
> + */
> + *out = val * 1000;
> + break;
> + case hwmon_fan:
> + switch (attr) {
> + case hwmon_fan_input:
> + return steamdeck_read_fan_speed(sd, out);

There is a bit of inconsistency here: All other atributes are handled directly,
except for this one, yet there isn't really a difference in the actual operation.
Maybe I am missing something. What is the reason for using a function here
but not for the other attributes ?

> + case hwmon_fan_target:
> + *out = sd->fan_target;
> + break;
> + case hwmon_fan_fault:
> + if (ACPI_FAILURE(acpi_evaluate_integer(
> + sd->adev->handle,
> + "FANC", NULL, &val)))
> + return -EIO;
> + /*
> + * FANC (Fan check):
> + * 0: Abnormal
> + * 1: Normal
> + */
> + *out = !val;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +steamdeck_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, const char **str)
> +{
> + switch (type) {
> + case hwmon_temp:
> + *str = "Battery Temp";
> + break;
> + case hwmon_fan:
> + *str = "System Fan";
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +steamdeck_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + struct steamdeck *sd = dev_get_drvdata(dev);
> +
> + if (type != hwmon_fan ||
> + attr != hwmon_fan_target)
> + return -EOPNOTSUPP;
> +
> + if (val > U16_MAX)
> + return -EINVAL;

This accepts negative values, and it expects the user to find
valid ranges. I suggest to use clamp_val() instead.

Guenter


2022-02-13 13:06:14

by Andrey Smirnov

[permalink] [raw]
Subject: Re: [PATCH] platform/x86: Add Steam Deck driver

On Sun, Feb 6, 2022 at 9:51 AM Guenter Roeck <[email protected]> wrote:
>
> On 2/5/22 18:20, Andrey Smirnov wrote:
> > Add a driver exposing various bits and pieces of functionality
> > provided by Steam Deck specific VLV0100 device presented by EC
> > firmware. This includes but not limited to:
> >
> > - CPU/device's fan control
> > - Read-only access to DDIC registers
> > - Battery tempreature measurements
> > - Various display related control knobs
> > - USB Type-C connector event notification
> >
> > Cc: Hans de Goede <[email protected]>
> > Cc: Mark Gross <[email protected]>
> > Cc: Jean Delvare <[email protected]>
> > Cc: Guenter Roeck <[email protected]>
> > Cc: [email protected] (open list)
> > Cc: [email protected]
> > Cc: [email protected]
> > Signed-off-by: Andrey Smirnov <[email protected]>
> > ---
> >
> ...
>
> > +config STEAMDECK
> > + tristate "Valve Steam Deck platform driver"
> > + depends on X86_64
> > + help
> > + Driver exposing various bits and pieces of functionality
> > + provided by Steam Deck specific VLV0100 device presented by
> > + EC firmware. This includes but not limited to:
>
> There seems to be some indentation issue.
>
> > + - CPU/device's fan control
> > + - Read-only access to DDIC registers
> > + - Battery tempreature measurements
> > + - Various display related control knobs
> > + - USB Type-C connector event notification
> > +
> > + Say N unless you are running on a Steam Deck.
> > +
>
> This doesn't depend on hwmon, yet it fails if devm_hwmon_device_register_with_info()
> returns an eror. That has a couple of problems: if HWMON=n, it won't compile,
> and if STEAMDECK=y and HWMON=m it won't compile either. You'll have to provide
> some dependency against HWMON to make this work.
>
> ...
>

Yeah, my bad, will fix.

> > +
> > +static int steamdeck_read_fan_speed(struct steamdeck *jup, long *speed)
> > +{
> > + unsigned long long val;
> > +
> > + if (ACPI_FAILURE(acpi_evaluate_integer(jup->adev->handle,
> > + "FANR", NULL, &val)))
> > + return -EIO;
> > +
> > + *speed = val;
> > + return 0;
> > +}
> > +
> > +static int
> > +steamdeck_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> > + u32 attr, int channel, long *out)
> > +{
> > + struct steamdeck *sd = dev_get_drvdata(dev);
> > + unsigned long long val;
> > +
> > + switch (type) {
> > + case hwmon_temp:
> > + if (attr != hwmon_temp_input)
> > + return -EOPNOTSUPP;
> > +
> > + if (ACPI_FAILURE(acpi_evaluate_integer(sd->adev->handle,
> > + "BATT", NULL, &val)))
> > + return -EIO;
> > + /*
> > + * Assuming BATT returns deg C we need to mutiply it
> > + * by 1000 to convert to mC
> > + */
> > + *out = val * 1000;
> > + break;
> > + case hwmon_fan:
> > + switch (attr) {
> > + case hwmon_fan_input:
> > + return steamdeck_read_fan_speed(sd, out);
>
> There is a bit of inconsistency here: All other atributes are handled directly,
> except for this one, yet there isn't really a difference in the actual operation.
> Maybe I am missing something. What is the reason for using a function here
> but not for the other attributes ?
>

It is also used to initialize "fan_target" to its initial value in
steamdeck_probe(). There's no ACPI method to read set fan target
speed, so I have to cache it in "fan_target".

> > + case hwmon_fan_target:
> > + *out = sd->fan_target;
> > + break;
> > + case hwmon_fan_fault:
> > + if (ACPI_FAILURE(acpi_evaluate_integer(
> > + sd->adev->handle,
> > + "FANC", NULL, &val)))
> > + return -EIO;
> > + /*
> > + * FANC (Fan check):
> > + * 0: Abnormal
> > + * 1: Normal
> > + */
> > + *out = !val;
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +steamdeck_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
> > + u32 attr, int channel, const char **str)
> > +{
> > + switch (type) {
> > + case hwmon_temp:
> > + *str = "Battery Temp";
> > + break;
> > + case hwmon_fan:
> > + *str = "System Fan";
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +steamdeck_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> > + u32 attr, int channel, long val)
> > +{
> > + struct steamdeck *sd = dev_get_drvdata(dev);
> > +
> > + if (type != hwmon_fan ||
> > + attr != hwmon_fan_target)
> > + return -EOPNOTSUPP;
> > +
> > + if (val > U16_MAX)
> > + return -EINVAL;
>
> This accepts negative values, and it expects the user to find
> valid ranges. I suggest to use clamp_val() instead.

Will do.