From eee9cd5d25f1be6120fdf1c90c81324d22b4cc49 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 11 Sep 2023 23:09:28 +0200 Subject: platform/x86: x86-android-tablets: Add a comment about x86_android_tablet_get_gpiod() Add a comment explaining why the special x86_android_tablet_get_gpiod() helper is necessary for some of the GPIOs used in the x86-android-tablets; and that for normal GPIO lookups this example should not be followed. Signed-off-by: Hans de Goede Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20230911210928.264908-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 8a1f22aaac00..b55957bde034 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -24,6 +24,21 @@ static struct platform_device *x86_android_tablet_device; +/* + * This helper allows getting a gpio_desc *before* the actual device consuming + * the GPIO has been instantiated. This function _must_ only be used to handle + * this special case such as e.g. : + * + * 1. Getting an IRQ from a GPIO for i2c_board_info.irq which is passed to + * i2c_client_new() to instantiate i2c_client-s; or + * 2. Calling desc_to_gpio() to get an old style GPIO number for gpio_keys + * platform_data which still uses old style GPIO numbers. + * + * Since the consuming device has not been instatiated yet a dynamic lookup + * is generated using the special x86_android_tablet dev for dev_id. + * + * For normal GPIO lookups a standard static gpiod_lookup_table _must_ be used. + */ int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, bool active_low, enum gpiod_flags dflags, struct gpio_desc **desc) -- cgit From 2c97d3e55b70edf33b6e7f211bab8a748a0a2bcc Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Wed, 30 Aug 2023 15:22:37 +1200 Subject: platform/x86: asus-wmi: add support for ASUS screenpad Add support for the WMI methods used to turn off and adjust the brightness of the secondary "screenpad" device found on some high-end ASUS laptops like the GX650P series and others. There are some small quirks with this device when considering only the raw WMI methods: 1. The Off method can only switch the device off 2. Changing the brightness turns the device back on 3. To turn the device back on the brightness must be > 1 4. When the device is off the brightness can't be changed (so it is stored by the driver if device is off). 5. Booting with a value of 0 brightness (retained by bios) means the bios will set a value of >0 <15 6. When the device is off it is "unplugged" asus_wmi sets the minimum brightness as 20 in general use, and 60 for booting with values <= min. The ACPI methods are used in a new backlight device named asus_screenpad. Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20230830032237.42987-2-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 133 +++++++++++++++++++++++++++++ drivers/platform/x86/asus-wmi.h | 1 + include/linux/platform_data/x86/asus-wmi.h | 4 + 3 files changed, 138 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 9f8cea5f9615..928fc74e79b4 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,10 @@ module_param(fnlock_default, bool, 0444); #define NVIDIA_TEMP_MIN 75 #define NVIDIA_TEMP_MAX 87 +#define ASUS_SCREENPAD_BRIGHT_MIN 20 +#define ASUS_SCREENPAD_BRIGHT_MAX 255 +#define ASUS_SCREENPAD_BRIGHT_DEFAULT 60 + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static int throttle_thermal_policy_write(struct asus_wmi *); @@ -212,6 +217,7 @@ struct asus_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; + struct backlight_device *screenpad_backlight_device; struct platform_device *platform_device; struct led_classdev wlan_led; @@ -3776,6 +3782,124 @@ static int is_display_toggle(int code) return 0; } +/* Screenpad backlight *******************************************************/ + +static int read_screenpad_backlight_power(struct asus_wmi *asus) +{ + int ret; + + ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_POWER); + if (ret < 0) + return ret; + /* 1 == powered */ + return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; +} + +static int read_screenpad_brightness(struct backlight_device *bd) +{ + struct asus_wmi *asus = bl_get_data(bd); + u32 retval; + int err; + + err = read_screenpad_backlight_power(asus); + if (err < 0) + return err; + /* The device brightness can only be read if powered, so return stored */ + if (err == FB_BLANK_POWERDOWN) + return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &retval); + if (err < 0) + return err; + + return (retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK) - ASUS_SCREENPAD_BRIGHT_MIN; +} + +static int update_screenpad_bl_status(struct backlight_device *bd) +{ + struct asus_wmi *asus = bl_get_data(bd); + int power, err = 0; + u32 ctrl_param; + + power = read_screenpad_backlight_power(asus); + if (power < 0) + return power; + + if (bd->props.power != power) { + if (power != FB_BLANK_UNBLANK) { + /* Only brightness > 0 can power it back on */ + ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, + ctrl_param, NULL); + } else { + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); + } + } else if (power == FB_BLANK_UNBLANK) { + /* Only set brightness if powered on or we get invalid/unsync state */ + ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL); + } + + /* Ensure brightness is stored to turn back on with */ + if (err == 0) + asus->driver->screenpad_brightness = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; + + return err; +} + +static const struct backlight_ops asus_screenpad_bl_ops = { + .get_brightness = read_screenpad_brightness, + .update_status = update_screenpad_bl_status, + .options = BL_CORE_SUSPENDRESUME, +}; + +static int asus_screenpad_init(struct asus_wmi *asus) +{ + struct backlight_device *bd; + struct backlight_properties props; + int err, power; + int brightness = 0; + + power = read_screenpad_backlight_power(asus); + if (power < 0) + return power; + + if (power != FB_BLANK_POWERDOWN) { + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness); + if (err < 0) + return err; + } + /* default to an acceptable min brightness on boot if too low */ + if (brightness < ASUS_SCREENPAD_BRIGHT_MIN) + brightness = ASUS_SCREENPAD_BRIGHT_DEFAULT; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_RAW; /* ensure this bd is last to be picked */ + props.max_brightness = ASUS_SCREENPAD_BRIGHT_MAX - ASUS_SCREENPAD_BRIGHT_MIN; + bd = backlight_device_register("asus_screenpad", + &asus->platform_device->dev, asus, + &asus_screenpad_bl_ops, &props); + if (IS_ERR(bd)) { + pr_err("Could not register backlight device\n"); + return PTR_ERR(bd); + } + + asus->screenpad_backlight_device = bd; + asus->driver->screenpad_brightness = brightness; + bd->props.brightness = brightness - ASUS_SCREENPAD_BRIGHT_MIN; + bd->props.power = power; + backlight_update_status(bd); + + return 0; +} + +static void asus_screenpad_exit(struct asus_wmi *asus) +{ + backlight_device_unregister(asus->screenpad_backlight_device); + + asus->screenpad_backlight_device = NULL; +} + /* Fn-lock ********************************************************************/ static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus) @@ -4431,6 +4555,12 @@ static int asus_wmi_add(struct platform_device *pdev) } else if (asus->driver->quirks->wmi_backlight_set_devstate) err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL); + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT)) { + err = asus_screenpad_init(asus); + if (err && err != -ENODEV) + goto fail_screenpad; + } + if (asus_wmi_has_fnlock_key(asus)) { asus->fnlock_locked = fnlock_default; asus_wmi_fnlock_update(asus); @@ -4454,6 +4584,8 @@ fail_wmi_handler: asus_wmi_backlight_exit(asus); fail_backlight: asus_wmi_rfkill_exit(asus); +fail_screenpad: + asus_screenpad_exit(asus); fail_rfkill: asus_wmi_led_exit(asus); fail_leds: @@ -4480,6 +4612,7 @@ static int asus_wmi_remove(struct platform_device *device) asus = platform_get_drvdata(device); wmi_remove_notify_handler(asus->driver->event_guid); asus_wmi_backlight_exit(asus); + asus_screenpad_exit(asus); asus_wmi_input_exit(asus); asus_wmi_led_exit(asus); asus_wmi_rfkill_exit(asus); diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index a478ebfd34df..5fbdd0eafa02 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -57,6 +57,7 @@ struct quirk_entry { struct asus_wmi_driver { int brightness; int panel_power; + int screenpad_brightness; int wlan_ctrl_by_user; const char *name; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 16e99a1c37fc..63e630276499 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -58,6 +58,10 @@ #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ #define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 +/* This can only be used to disable the screen, not re-enable */ +#define ASUS_WMI_DEVID_SCREENPAD_POWER 0x00050031 +/* Writing a brightness re-enables the screen if disabled */ +#define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032 #define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018 #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075 -- cgit From 423c3361855c1e81f1cb91728a2ac5ddfd2cbf16 Mon Sep 17 00:00:00 2001 From: Shravan Kumar Ramani Date: Tue, 5 Sep 2023 08:49:34 -0400 Subject: platform/mellanox: mlxbf-pmc: Add support for BlueField-3 Add new access mechanism and list of supported events to program and read the counters in BlueField-3. Performance counter blocks being added for BlueField-3 include: - Memory Sub-system (mss) which has counters for monitoring various DRAM and related skylib events - Last level Tile, which has 2 sets of counters (llt, llt_miss) for monitoring Tile and cache metrics Signed-off-by: Shravan Kumar Ramani Reviewed-by: Vadim Pasternak Reviewed-by: David Thompson Link: https://lore.kernel.org/r/d5feee745f6bfd163e0c361e300d4b2ef1d72e00.1693917738.git.shravankr@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-pmc.c | 686 ++++++++++++++++++++++++++++++++-- 1 file changed, 654 insertions(+), 32 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 2d4bbe99959e..0b427fc24a96 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -30,14 +30,16 @@ #define MLXBF_PMC_EVENT_SET_BF1 0 #define MLXBF_PMC_EVENT_SET_BF2 1 +#define MLXBF_PMC_EVENT_SET_BF3 2 #define MLXBF_PMC_EVENT_INFO_LEN 100 #define MLXBF_PMC_MAX_BLOCKS 30 -#define MLXBF_PMC_MAX_ATTRS 30 +#define MLXBF_PMC_MAX_ATTRS 70 #define MLXBF_PMC_INFO_SZ 4 #define MLXBF_PMC_REG_SIZE 8 #define MLXBF_PMC_L3C_REG_SIZE 4 +#define MLXBF_PMC_TYPE_CRSPACE 2 #define MLXBF_PMC_TYPE_COUNTER 1 #define MLXBF_PMC_TYPE_REGISTER 0 @@ -78,6 +80,16 @@ #define MLXBF_PMC_L3C_PERF_CNT_LOW_VAL GENMASK(31, 0) #define MLXBF_PMC_L3C_PERF_CNT_HIGH_VAL GENMASK(24, 0) +#define MLXBF_PMC_CRSPACE_PERFMON_REG0 0x0 +#define MLXBF_PMC_CRSPACE_PERFSEL_SZ 4 +#define MLXBF_PMC_CRSPACE_PERFSEL0 GENMASK(23, 16) +#define MLXBF_PMC_CRSPACE_PERFSEL1 GENMASK(7, 0) +#define MLXBF_PMC_CRSPACE_PERFMON_REG0_SZ 0x2 +#define MLXBF_PMC_CRSPACE_PERFMON_CTL(n) (n * MLXBF_PMC_CRSPACE_PERFMON_REG0_SZ) +#define MLXBF_PMC_CRSPACE_PERFMON_EN BIT(30) +#define MLXBF_PMC_CRSPACE_PERFMON_CLR BIT(28) +#define MLXBF_PMC_CRSPACE_PERFMON_VAL0(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0xc) + /** * struct mlxbf_pmc_attribute - Structure to hold attribute and block info * for each sysfs entry @@ -124,6 +136,9 @@ struct mlxbf_pmc_block_info { * @pdev: The kernel structure representing the device * @total_blocks: Total number of blocks * @tile_count: Number of tiles in the system + * @llt_enable: Info on enabled LLTs + * @mss_enable: Info on enabled MSSs + * @group_num: Group number assigned to each valid block * @hwmon_dev: Hwmon device for bfperf * @block_name: Block name * @block: Block info @@ -136,6 +151,9 @@ struct mlxbf_pmc_context { struct platform_device *pdev; uint32_t total_blocks; uint32_t tile_count; + uint8_t llt_enable; + uint8_t mss_enable; + uint32_t group_num; struct device *hwmon_dev; const char *block_name[MLXBF_PMC_MAX_BLOCKS]; struct mlxbf_pmc_block_info block[MLXBF_PMC_MAX_BLOCKS]; @@ -260,7 +278,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_ecc_events[] = { { 0x348, "DRAM_ECC_ERROR" }, }; -static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { +static const struct mlxbf_pmc_events mlxbf_pmc_mss_events_1[] = { { 0x0, "DISABLE" }, { 0xc0, "RXREQ_MSS" }, { 0xc1, "RXDAT_MSS" }, @@ -268,6 +286,164 @@ static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { { 0xc3, "TXDAT_MSS" }, }; +static const struct mlxbf_pmc_events mlxbf_pmc_mss_events_3[] = { + {0, "SKYLIB_CDN_TX_FLITS"}, + {1, "SKYLIB_DDN_TX_FLITS"}, + {2, "SKYLIB_NDN_TX_FLITS"}, + {3, "SKYLIB_SDN_TX_FLITS"}, + {4, "SKYLIB_UDN_TX_FLITS"}, + {5, "SKYLIB_CDN_RX_FLITS"}, + {6, "SKYLIB_DDN_RX_FLITS"}, + {7, "SKYLIB_NDN_RX_FLITS"}, + {8, "SKYLIB_SDN_RX_FLITS"}, + {9, "SKYLIB_UDN_RX_FLITS"}, + {10, "SKYLIB_CDN_TX_STALL"}, + {11, "SKYLIB_DDN_TX_STALL"}, + {12, "SKYLIB_NDN_TX_STALL"}, + {13, "SKYLIB_SDN_TX_STALL"}, + {14, "SKYLIB_UDN_TX_STALL"}, + {15, "SKYLIB_CDN_RX_STALL"}, + {16, "SKYLIB_DDN_RX_STALL"}, + {17, "SKYLIB_NDN_RX_STALL"}, + {18, "SKYLIB_SDN_RX_STALL"}, + {19, "SKYLIB_UDN_RX_STALL"}, + {20, "SKYLIB_CHI_REQ0_TX_FLITS"}, + {21, "SKYLIB_CHI_DATA0_TX_FLITS"}, + {22, "SKYLIB_CHI_RESP0_TX_FLITS"}, + {23, "SKYLIB_CHI_SNP0_TX_FLITS"}, + {24, "SKYLIB_CHI_REQ1_TX_FLITS"}, + {25, "SKYLIB_CHI_DATA1_TX_FLITS"}, + {26, "SKYLIB_CHI_RESP1_TX_FLITS"}, + {27, "SKYLIB_CHI_SNP1_TX_FLITS"}, + {28, "SKYLIB_CHI_REQ2_TX_FLITS"}, + {29, "SKYLIB_CHI_DATA2_TX_FLITS"}, + {30, "SKYLIB_CHI_RESP2_TX_FLITS"}, + {31, "SKYLIB_CHI_SNP2_TX_FLITS"}, + {32, "SKYLIB_CHI_REQ3_TX_FLITS"}, + {33, "SKYLIB_CHI_DATA3_TX_FLITS"}, + {34, "SKYLIB_CHI_RESP3_TX_FLITS"}, + {35, "SKYLIB_CHI_SNP3_TX_FLITS"}, + {36, "SKYLIB_TLP_REQ_TX_FLITS"}, + {37, "SKYLIB_TLP_RESP_TX_FLITS"}, + {38, "SKYLIB_TLP_META_TX_FLITS"}, + {39, "SKYLIB_AXIS_DATA_TX_FLITS"}, + {40, "SKYLIB_AXIS_CRED_TX_FLITS"}, + {41, "SKYLIB_APB_TX_FLITS"}, + {42, "SKYLIB_VW_TX_FLITS"}, + {43, "SKYLIB_GGA_MSN_W_TX_FLITS"}, + {44, "SKYLIB_GGA_MSN_N_TX_FLITS"}, + {45, "SKYLIB_CR_REQ_TX_FLITS"}, + {46, "SKYLIB_CR_RESP_TX_FLITS"}, + {47, "SKYLIB_MSN_PRNF_TX_FLITS"}, + {48, "SKYLIB_DBG_DATA_TX_FLITS"}, + {49, "SKYLIB_DBG_CRED_TX_FLITS"}, + {50, "SKYLIB_CHI_REQ0_RX_FLITS"}, + {51, "SKYLIB_CHI_DATA0_RX_FLITS"}, + {52, "SKYLIB_CHI_RESP0_RX_FLITS"}, + {53, "SKYLIB_CHI_SNP0_RX_FLITS"}, + {54, "SKYLIB_CHI_REQ1_RX_FLITS"}, + {55, "SKYLIB_CHI_DATA1_RX_FLITS"}, + {56, "SKYLIB_CHI_RESP1_RX_FLITS"}, + {57, "SKYLIB_CHI_SNP1_RX_FLITS"}, + {58, "SKYLIB_CHI_REQ2_RX_FLITS"}, + {59, "SKYLIB_CHI_DATA2_RX_FLITS"}, + {60, "SKYLIB_CHI_RESP2_RX_FLITS"}, + {61, "SKYLIB_CHI_SNP2_RX_FLITS"}, + {62, "SKYLIB_CHI_REQ3_RX_FLITS"}, + {63, "SKYLIB_CHI_DATA3_RX_FLITS"}, + {64, "SKYLIB_CHI_RESP3_RX_FLITS"}, + {65, "SKYLIB_CHI_SNP3_RX_FLITS"}, + {66, "SKYLIB_TLP_REQ_RX_FLITS"}, + {67, "SKYLIB_TLP_RESP_RX_FLITS"}, + {68, "SKYLIB_TLP_META_RX_FLITS"}, + {69, "SKYLIB_AXIS_DATA_RX_FLITS"}, + {70, "SKYLIB_AXIS_CRED_RX_FLITS"}, + {71, "SKYLIB_APB_RX_FLITS"}, + {72, "SKYLIB_VW_RX_FLITS"}, + {73, "SKYLIB_GGA_MSN_W_RX_FLITS"}, + {74, "SKYLIB_GGA_MSN_N_RX_FLITS"}, + {75, "SKYLIB_CR_REQ_RX_FLITS"}, + {76, "SKYLIB_CR_RESP_RX_FLITS"}, + {77, "SKYLIB_MSN_PRNF_RX_FLITS"}, + {78, "SKYLIB_DBG_DATA_RX_FLITS"}, + {79, "SKYLIB_DBG_CRED_RX_FLITS"}, + {80, "SKYLIB_CHI_REQ0_TX_STALL"}, + {81, "SKYLIB_CHI_DATA0_TX_STALL"}, + {82, "SKYLIB_CHI_RESP0_TX_STALL"}, + {83, "SKYLIB_CHI_SNP0_TX_STALL"}, + {84, "SKYLIB_CHI_REQ1_TX_STALL"}, + {85, "SKYLIB_CHI_DATA1_TX_STALL"}, + {86, "SKYLIB_CHI_RESP1_TX_STALL"}, + {87, "SKYLIB_CHI_SNP1_TX_STALL"}, + {88, "SKYLIB_CHI_REQ2_TX_STALL"}, + {89, "SKYLIB_CHI_DATA2_TX_STALL"}, + {90, "SKYLIB_CHI_RESP2_TX_STALL"}, + {91, "SKYLIB_CHI_SNP2_TX_STALL"}, + {92, "SKYLIB_CHI_REQ3_TX_STALL"}, + {93, "SKYLIB_CHI_DATA3_TX_STALL"}, + {94, "SKYLIB_CHI_RESP3_TX_STALL"}, + {95, "SKYLIB_CHI_SNP3_TX_STALL"}, + {96, "SKYLIB_TLP_REQ_TX_STALL"}, + {97, "SKYLIB_TLP_RESP_TX_STALL"}, + {98, "SKYLIB_TLP_META_TX_STALL"}, + {99, "SKYLIB_AXIS_DATA_TX_STALL"}, + {100, "SKYLIB_AXIS_CRED_TX_STALL"}, + {101, "SKYLIB_APB_TX_STALL"}, + {102, "SKYLIB_VW_TX_STALL"}, + {103, "SKYLIB_GGA_MSN_W_TX_STALL"}, + {104, "SKYLIB_GGA_MSN_N_TX_STALL"}, + {105, "SKYLIB_CR_REQ_TX_STALL"}, + {106, "SKYLIB_CR_RESP_TX_STALL"}, + {107, "SKYLIB_MSN_PRNF_TX_STALL"}, + {108, "SKYLIB_DBG_DATA_TX_STALL"}, + {109, "SKYLIB_DBG_CRED_TX_STALL"}, + {110, "SKYLIB_CHI_REQ0_RX_STALL"}, + {111, "SKYLIB_CHI_DATA0_RX_STALL"}, + {112, "SKYLIB_CHI_RESP0_RX_STALL"}, + {113, "SKYLIB_CHI_SNP0_RX_STALL"}, + {114, "SKYLIB_CHI_REQ1_RX_STALL"}, + {115, "SKYLIB_CHI_DATA1_RX_STALL"}, + {116, "SKYLIB_CHI_RESP1_RX_STALL"}, + {117, "SKYLIB_CHI_SNP1_RX_STALL"}, + {118, "SKYLIB_CHI_REQ2_RX_STALL"}, + {119, "SKYLIB_CHI_DATA2_RX_STALL"}, + {120, "SKYLIB_CHI_RESP2_RX_STALL"}, + {121, "SKYLIB_CHI_SNP2_RX_STALL"}, + {122, "SKYLIB_CHI_REQ3_RX_STALL"}, + {123, "SKYLIB_CHI_DATA3_RX_STALL"}, + {124, "SKYLIB_CHI_RESP3_RX_STALL"}, + {125, "SKYLIB_CHI_SNP3_RX_STALL"}, + {126, "SKYLIB_TLP_REQ_RX_STALL"}, + {127, "SKYLIB_TLP_RESP_RX_STALL"}, + {128, "SKYLIB_TLP_META_RX_STALL"}, + {129, "SKYLIB_AXIS_DATA_RX_STALL"}, + {130, "SKYLIB_AXIS_CRED_RX_STALL"}, + {131, "SKYLIB_APB_RX_STALL"}, + {132, "SKYLIB_VW_RX_STALL"}, + {133, "SKYLIB_GGA_MSN_W_RX_STALL"}, + {134, "SKYLIB_GGA_MSN_N_RX_STALL"}, + {135, "SKYLIB_CR_REQ_RX_STALL"}, + {136, "SKYLIB_CR_RESP_RX_STALL"}, + {137, "SKYLIB_MSN_PRNF_RX_STALL"}, + {138, "SKYLIB_DBG_DATA_RX_STALL"}, + {139, "SKYLIB_DBG_CRED_RX_STALL"}, + {140, "SKYLIB_CDN_LOOPBACK_FLITS"}, + {141, "SKYLIB_DDN_LOOPBACK_FLITS"}, + {142, "SKYLIB_NDN_LOOPBACK_FLITS"}, + {143, "SKYLIB_SDN_LOOPBACK_FLITS"}, + {144, "SKYLIB_UDN_LOOPBACK_FLITS"}, + {145, "HISTOGRAM_HISTOGRAM_BIN0"}, + {146, "HISTOGRAM_HISTOGRAM_BIN1"}, + {147, "HISTOGRAM_HISTOGRAM_BIN2"}, + {148, "HISTOGRAM_HISTOGRAM_BIN3"}, + {149, "HISTOGRAM_HISTOGRAM_BIN4"}, + {150, "HISTOGRAM_HISTOGRAM_BIN5"}, + {151, "HISTOGRAM_HISTOGRAM_BIN6"}, + {152, "HISTOGRAM_HISTOGRAM_BIN7"}, + {153, "HISTOGRAM_HISTOGRAM_BIN8"}, + {154, "HISTOGRAM_HISTOGRAM_BIN9"}, +}; + static const struct mlxbf_pmc_events mlxbf_pmc_hnf_events[] = { { 0x0, "DISABLE" }, { 0x45, "HNF_REQUESTS" }, @@ -429,6 +605,260 @@ static const struct mlxbf_pmc_events mlxbf_pmc_l3c_events[] = { { 0x2b, "ANY_REJECT_BANK1" }, }; +static const struct mlxbf_pmc_events mlxbf_pmc_llt_events[] = { + {0, "HNF0_CYCLES"}, + {1, "HNF0_REQS_RECEIVED"}, + {2, "HNF0_REQS_PROCESSED"}, + {3, "HNF0_DIR_HIT"}, + {4, "HNF0_DIR_MISS"}, + {5, "HNF0_DIR_RD_ALLOC"}, + {6, "HNF0_DIR_WR_ALLOC"}, + {7, "HNF0_DIR_VICTIM"}, + {8, "HNF0_CL_HAZARD"}, + {9, "HNF0_ALL_HAZARD"}, + {10, "HNF0_PIPE_STALLS"}, + {11, "HNF0_MEM_READS"}, + {12, "HNF0_MEM_WRITES"}, + {13, "HNF0_MEM_ACCESS"}, + {14, "HNF0_DCL_READ"}, + {15, "HNF0_DCL_INVAL"}, + {16, "HNF0_CHI_RXDAT"}, + {17, "HNF0_CHI_RXRSP"}, + {18, "HNF0_CHI_TXDAT"}, + {19, "HNF0_CHI_TXRSP"}, + {20, "HNF0_CHI_TXSNP"}, + {21, "HNF0_DCT_SNP"}, + {22, "HNF0_SNP_FWD_DATA"}, + {23, "HNF0_SNP_FWD_RSP"}, + {24, "HNF0_SNP_RSP"}, + {25, "HNF0_EXCL_FULL"}, + {26, "HNF0_EXCL_WRITE_F"}, + {27, "HNF0_EXCL_WRITE_S"}, + {28, "HNF0_EXCL_WRITE"}, + {29, "HNF0_EXCL_READ"}, + {30, "HNF0_REQ_BUF_EMPTY"}, + {31, "HNF0_ALL_MAFS_BUSY"}, + {32, "HNF0_TXDAT_NO_LCRD"}, + {33, "HNF0_TXSNP_NO_LCRD"}, + {34, "HNF0_TXRSP_NO_LCRD"}, + {35, "HNF0_TXREQ_NO_LCRD"}, + {36, "HNF0_WRITE"}, + {37, "HNF0_READ"}, + {38, "HNF0_ACCESS"}, + {39, "HNF0_MAF_N_BUSY"}, + {40, "HNF0_MAF_N_REQS"}, + {41, "HNF0_SEL_OPCODE"}, + {42, "HNF1_CYCLES"}, + {43, "HNF1_REQS_RECEIVED"}, + {44, "HNF1_REQS_PROCESSED"}, + {45, "HNF1_DIR_HIT"}, + {46, "HNF1_DIR_MISS"}, + {47, "HNF1_DIR_RD_ALLOC"}, + {48, "HNF1_DIR_WR_ALLOC"}, + {49, "HNF1_DIR_VICTIM"}, + {50, "HNF1_CL_HAZARD"}, + {51, "HNF1_ALL_HAZARD"}, + {52, "HNF1_PIPE_STALLS"}, + {53, "HNF1_MEM_READS"}, + {54, "HNF1_MEM_WRITES"}, + {55, "HNF1_MEM_ACCESS"}, + {56, "HNF1_DCL_READ"}, + {57, "HNF1_DCL_INVAL"}, + {58, "HNF1_CHI_RXDAT"}, + {59, "HNF1_CHI_RXRSP"}, + {60, "HNF1_CHI_TXDAT"}, + {61, "HNF1_CHI_TXRSP"}, + {62, "HNF1_CHI_TXSNP"}, + {63, "HNF1_DCT_SNP"}, + {64, "HNF1_SNP_FWD_DATA"}, + {65, "HNF1_SNP_FWD_RSP"}, + {66, "HNF1_SNP_RSP"}, + {67, "HNF1_EXCL_FULL"}, + {68, "HNF1_EXCL_WRITE_F"}, + {69, "HNF1_EXCL_WRITE_S"}, + {70, "HNF1_EXCL_WRITE"}, + {71, "HNF1_EXCL_READ"}, + {72, "HNF1_REQ_BUF_EMPTY"}, + {73, "HNF1_ALL_MAFS_BUSY"}, + {74, "HNF1_TXDAT_NO_LCRD"}, + {75, "HNF1_TXSNP_NO_LCRD"}, + {76, "HNF1_TXRSP_NO_LCRD"}, + {77, "HNF1_TXREQ_NO_LCRD"}, + {78, "HNF1_WRITE"}, + {79, "HNF1_READ"}, + {80, "HNF1_ACCESS"}, + {81, "HNF1_MAF_N_BUSY"}, + {82, "HNF1_MAF_N_REQS"}, + {83, "HNF1_SEL_OPCODE"}, + {84, "GDC_BANK0_RD_REQ"}, + {85, "GDC_BANK0_WR_REQ"}, + {86, "GDC_BANK0_ALLOCATE"}, + {87, "GDC_BANK0_HIT"}, + {88, "GDC_BANK0_MISS"}, + {89, "GDC_BANK0_INVALIDATE"}, + {90, "GDC_BANK0_EVICT"}, + {91, "GDC_BANK0_RD_RESP"}, + {92, "GDC_BANK0_WR_ACK"}, + {93, "GDC_BANK0_SNOOP"}, + {94, "GDC_BANK0_SNOOP_NORMAL"}, + {95, "GDC_BANK0_SNOOP_FWD"}, + {96, "GDC_BANK0_SNOOP_STASH"}, + {97, "GDC_BANK0_SNOOP_STASH_INDPND_RD"}, + {98, "GDC_BANK0_FOLLOWER"}, + {99, "GDC_BANK0_FW"}, + {100, "GDC_BANK0_HIT_DCL_BOTH"}, + {101, "GDC_BANK0_HIT_DCL_PARTIAL"}, + {102, "GDC_BANK0_EVICT_DCL"}, + {103, "GDC_BANK0_G_RSE_PIPE_CACHE_DATA0"}, + {103, "GDC_BANK0_G_RSE_PIPE_CACHE_DATA1"}, + {105, "GDC_BANK0_ARB_STRB"}, + {106, "GDC_BANK0_ARB_WAIT"}, + {107, "GDC_BANK0_GGA_STRB"}, + {108, "GDC_BANK0_GGA_WAIT"}, + {109, "GDC_BANK0_FW_STRB"}, + {110, "GDC_BANK0_FW_WAIT"}, + {111, "GDC_BANK0_SNP_STRB"}, + {112, "GDC_BANK0_SNP_WAIT"}, + {113, "GDC_BANK0_MISS_INARB_STRB"}, + {114, "GDC_BANK0_MISS_INARB_WAIT"}, + {115, "GDC_BANK0_G_FIFO_FF_GGA_RSP_RD0"}, + {116, "GDC_BANK0_G_FIFO_FF_GGA_RSP_RD1"}, + {117, "GDC_BANK0_G_FIFO_FF_GGA_RSP_RD2"}, + {118, "GDC_BANK0_G_FIFO_FF_GGA_RSP_RD3"}, + {119, "GDC_BANK0_G_FIFO_FF_GGA_RSP_WR0"}, + {120, "GDC_BANK0_G_FIFO_FF_GGA_RSP_WR1"}, + {121, "GDC_BANK0_G_FIFO_FF_GGA_RSP_WR2"}, + {122, "GDC_BANK0_G_FIFO_FF_GGA_RSP_WR3"}, + {123, "GDC_BANK1_RD_REQ"}, + {124, "GDC_BANK1_WR_REQ"}, + {125, "GDC_BANK1_ALLOCATE"}, + {126, "GDC_BANK1_HIT"}, + {127, "GDC_BANK1_MISS"}, + {128, "GDC_BANK1_INVALIDATE"}, + {129, "GDC_BANK1_EVICT"}, + {130, "GDC_BANK1_RD_RESP"}, + {131, "GDC_BANK1_WR_ACK"}, + {132, "GDC_BANK1_SNOOP"}, + {133, "GDC_BANK1_SNOOP_NORMAL"}, + {134, "GDC_BANK1_SNOOP_FWD"}, + {135, "GDC_BANK1_SNOOP_STASH"}, + {136, "GDC_BANK1_SNOOP_STASH_INDPND_RD"}, + {137, "GDC_BANK1_FOLLOWER"}, + {138, "GDC_BANK1_FW"}, + {139, "GDC_BANK1_HIT_DCL_BOTH"}, + {140, "GDC_BANK1_HIT_DCL_PARTIAL"}, + {141, "GDC_BANK1_EVICT_DCL"}, + {142, "GDC_BANK1_G_RSE_PIPE_CACHE_DATA0"}, + {143, "GDC_BANK1_G_RSE_PIPE_CACHE_DATA1"}, + {144, "GDC_BANK1_ARB_STRB"}, + {145, "GDC_BANK1_ARB_WAIT"}, + {146, "GDC_BANK1_GGA_STRB"}, + {147, "GDC_BANK1_GGA_WAIT"}, + {148, "GDC_BANK1_FW_STRB"}, + {149, "GDC_BANK1_FW_WAIT"}, + {150, "GDC_BANK1_SNP_STRB"}, + {151, "GDC_BANK1_SNP_WAIT"}, + {152, "GDC_BANK1_MISS_INARB_STRB"}, + {153, "GDC_BANK1_MISS_INARB_WAIT"}, + {154, "GDC_BANK1_G_FIFO_FF_GGA_RSP_RD0"}, + {155, "GDC_BANK1_G_FIFO_FF_GGA_RSP_RD1"}, + {156, "GDC_BANK1_G_FIFO_FF_GGA_RSP_RD2"}, + {157, "GDC_BANK1_G_FIFO_FF_GGA_RSP_RD3"}, + {158, "GDC_BANK1_G_FIFO_FF_GGA_RSP_WR0"}, + {159, "GDC_BANK1_G_FIFO_FF_GGA_RSP_WR1"}, + {160, "GDC_BANK1_G_FIFO_FF_GGA_RSP_WR2"}, + {161, "GDC_BANK1_G_FIFO_FF_GGA_RSP_WR3"}, + {162, "HISTOGRAM_HISTOGRAM_BIN0"}, + {163, "HISTOGRAM_HISTOGRAM_BIN1"}, + {164, "HISTOGRAM_HISTOGRAM_BIN2"}, + {165, "HISTOGRAM_HISTOGRAM_BIN3"}, + {166, "HISTOGRAM_HISTOGRAM_BIN4"}, + {167, "HISTOGRAM_HISTOGRAM_BIN5"}, + {168, "HISTOGRAM_HISTOGRAM_BIN6"}, + {169, "HISTOGRAM_HISTOGRAM_BIN7"}, + {170, "HISTOGRAM_HISTOGRAM_BIN8"}, + {171, "HISTOGRAM_HISTOGRAM_BIN9"}, +}; + +static const struct mlxbf_pmc_events mlxbf_pmc_llt_miss_events[] = { + {0, "GDC_MISS_MACHINE_RD_REQ"}, + {1, "GDC_MISS_MACHINE_WR_REQ"}, + {2, "GDC_MISS_MACHINE_SNP_REQ"}, + {3, "GDC_MISS_MACHINE_EVICT_REQ"}, + {4, "GDC_MISS_MACHINE_FW_REQ"}, + {5, "GDC_MISS_MACHINE_RD_RESP"}, + {6, "GDC_MISS_MACHINE_WR_RESP"}, + {7, "GDC_MISS_MACHINE_SNP_STASH_DATAPULL_DROP"}, + {8, "GDC_MISS_MACHINE_SNP_STASH_DATAPULL_DROP_TXDAT"}, + {9, "GDC_MISS_MACHINE_CHI_TXREQ"}, + {10, "GDC_MISS_MACHINE_CHI_RXRSP"}, + {11, "GDC_MISS_MACHINE_CHI_TXDAT"}, + {12, "GDC_MISS_MACHINE_CHI_RXDAT"}, + {13, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC0_0"}, + {14, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC0_1 "}, + {15, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC0_2"}, + {16, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC0_3 "}, + {17, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC1_0 "}, + {18, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC1_1 "}, + {19, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC1_2 "}, + {20, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC1_3 "}, + {21, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE0_0"}, + {22, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE0_1"}, + {23, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE0_2"}, + {24, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE0_3"}, + {25, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE1_0 "}, + {26, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE1_1"}, + {27, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE1_2"}, + {28, "GDC_MISS_MACHINE_G_FIFO_FF_EXEC_DONE1_3"}, + {29, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_0"}, + {30, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_1"}, + {31, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_2"}, + {32, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_3"}, + {33, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_4"}, + {34, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_5"}, + {35, "GDC_MISS_MACHINE_GDC_LINK_LIST_FF_6"}, + {36, "GDC_MISS_MACHINE_G_RSE_PIPE_TXREQ_0"}, + {37, "GDC_MISS_MACHINE_G_RSE_PIPE_TXREQ_1"}, + {38, "GDC_MISS_MACHINE_G_CREDIT_TXREQ_0"}, + {39, "GDC_MISS_MACHINE_G_CREDIT_TXREQ_1"}, + {40, "GDC_MISS_MACHINE_G_RSE_PIPE_TXDAT_0"}, + {41, "GDC_MISS_MACHINE_G_RSE_PIPE_TXDAT_1"}, + {42, "GDC_MISS_MACHINE_G_CREDIT_TXDAT_0"}, + {43, "GDC_MISS_MACHINE_G_CREDIT_TXDAT_1"}, + {44, "GDC_MISS_MACHINE_G_FIFO_FF_COMPACK_0"}, + {45, "GDC_MISS_MACHINE_G_FIFO_FF_COMPACK_1"}, + {46, "GDC_MISS_MACHINE_G_FIFO_FF_COMPACK_2"}, + {47, "GDC_MISS_MACHINE_G_FIFO_FF_COMPACK_3"}, + {48, "GDC_MISS_MACHINE_G_RSE_PIPE_TXRSP_0"}, + {49, "GDC_MISS_MACHINE_G_RSE_PIPE_TXRSP_1"}, + {50, "GDC_MISS_MACHINE_G_CREDIT_TXRSP_0"}, + {51, "GDC_MISS_MACHINE_G_CREDIT_TXRSP_1"}, + {52, "GDC_MISS_MACHINE_G_RSE_PIPE_INARB_0"}, + {53, "GDC_MISS_MACHINE_G_RSE_PIPE_INARB_1"}, + {54, "GDC_MISS_MACHINE_G_FIFO_FF_SNOOP_IN_0"}, + {55, "GDC_MISS_MACHINE_G_FIFO_FF_SNOOP_IN_1"}, + {56, "GDC_MISS_MACHINE_G_FIFO_FF_SNOOP_IN_2"}, + {57, "GDC_MISS_MACHINE_G_FIFO_FF_SNOOP_IN_3"}, + {58, "GDC_MISS_MACHINE_G_FIFO_FF_TXRSP_SNOOP_DATAPULL_0"}, + {59, "GDC_MISS_MACHINE_G_FIFO_FF_TXRSP_SNOOP_DATAPULL_1"}, + {60, "GDC_MISS_MACHINE_G_FIFO_FF_TXRSP_SNOOP_DATAPULL_2"}, + {61, "GDC_MISS_MACHINE_G_FIFO_FF_TXRSP_SNOOP_DATAPULL_3"}, + {62, "GDC_MISS_MACHINE_G_FIFO_FF_TXDAT_SNOOP_DATAPULL_4"}, + {63, "GDC_MISS_MACHINE_G_FIFO_FF_TXDAT_SNOOP_DATAPULL_5"}, + {64, "GDC_MISS_MACHINE_G_FIFO_FF_TXDAT_SNOOP_DATAPULL_6"}, + {65, "GDC_MISS_MACHINE_G_FIFO_FF_TXDAT_SNOOP_DATAPULL_7"}, + {66, "HISTOGRAM_HISTOGRAM_BIN0"}, + {67, "HISTOGRAM_HISTOGRAM_BIN1"}, + {68, "HISTOGRAM_HISTOGRAM_BIN2"}, + {69, "HISTOGRAM_HISTOGRAM_BIN3"}, + {70, "HISTOGRAM_HISTOGRAM_BIN4"}, + {71, "HISTOGRAM_HISTOGRAM_BIN5"}, + {72, "HISTOGRAM_HISTOGRAM_BIN6"}, + {73, "HISTOGRAM_HISTOGRAM_BIN7"}, + {74, "HISTOGRAM_HISTOGRAM_BIN8"}, + {75, "HISTOGRAM_HISTOGRAM_BIN9"}, +}; + static struct mlxbf_pmc_context *pmc; /* UUID used to probe ATF service. */ @@ -569,8 +999,21 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, break; } } else if (strstr(blk, "mss")) { - events = mlxbf_pmc_mss_events; - *size = ARRAY_SIZE(mlxbf_pmc_mss_events); + switch (pmc->event_set) { + case MLXBF_PMC_EVENT_SET_BF1: + case MLXBF_PMC_EVENT_SET_BF2: + events = mlxbf_pmc_mss_events_1; + *size = ARRAY_SIZE(mlxbf_pmc_mss_events_1); + break; + case MLXBF_PMC_EVENT_SET_BF3: + events = mlxbf_pmc_mss_events_3; + *size = ARRAY_SIZE(mlxbf_pmc_mss_events_3); + break; + default: + events = NULL; + *size = 0; + break; + } } else if (strstr(blk, "ecc")) { events = mlxbf_pmc_ecc_events; *size = ARRAY_SIZE(mlxbf_pmc_ecc_events); @@ -586,6 +1029,12 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, } else if (strstr(blk, "smmu")) { events = mlxbf_pmc_smgen_events; *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); + } else if (strstr(blk, "llt_miss")) { + events = mlxbf_pmc_llt_miss_events; + *size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events); + } else if (strstr(blk, "llt")) { + events = mlxbf_pmc_llt_events; + *size = ARRAY_SIZE(mlxbf_pmc_llt_events); } else { events = NULL; *size = 0; @@ -712,6 +1161,43 @@ static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num, return mlxbf_pmc_write(pmcaddr, MLXBF_PMC_WRITE_REG_32, *wordaddr); } +/* Method to handle crspace counter programming */ +static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num, + uint32_t evt) +{ + uint32_t word; + void *addr; + int ret; + + addr = pmc->block[blk_num].mmio_base + + (rounddown(cnt_num, 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ); + ret = mlxbf_pmc_readl(addr, &word); + if (ret) + return ret; + + if (cnt_num % 2) { + word &= ~MLXBF_PMC_CRSPACE_PERFSEL1; + word |= FIELD_PREP(MLXBF_PMC_CRSPACE_PERFSEL1, evt); + } else { + word &= ~MLXBF_PMC_CRSPACE_PERFSEL0; + word |= FIELD_PREP(MLXBF_PMC_CRSPACE_PERFSEL0, evt); + } + + return mlxbf_pmc_write(addr, MLXBF_PMC_WRITE_REG_32, word); +} + +/* Method to clear crspace counter value */ +static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num) +{ + void *addr; + + addr = pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) + + (cnt_num * 4); + + return mlxbf_pmc_write(addr, MLXBF_PMC_WRITE_REG_32, 0x0); +} + /* Method to program a counter to monitor an event */ static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num, uint32_t evt, bool is_l3) @@ -724,6 +1210,10 @@ static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num, if (is_l3) return mlxbf_pmc_program_l3_counter(blk_num, cnt_num, evt); + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) + return mlxbf_pmc_program_crspace_counter(blk_num, cnt_num, + evt); + /* Configure the counter */ perfctl = FIELD_PREP(MLXBF_PMC_PERFCTL_EN0, 1); perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_EB0, 0); @@ -778,7 +1268,7 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num, { uint32_t perfcnt_low = 0, perfcnt_high = 0; uint64_t value; - int status = 0; + int status; status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + MLXBF_PMC_L3C_PERF_CNT_LOW + @@ -804,6 +1294,24 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num, return 0; } +/* Method to handle crspace counter reads */ +static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num, + uint64_t *result) +{ + uint32_t value; + int status = 0; + + status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) + + (cnt_num * 4), &value); + if (status) + return status; + + *result = value; + + return 0; +} + /* Method to read the counter value */ static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3, uint64_t *result) @@ -818,6 +1326,9 @@ static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3, if (is_l3) return mlxbf_pmc_read_l3_counter(blk_num, cnt_num, result); + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) + return mlxbf_pmc_read_crspace_counter(blk_num, cnt_num, result); + perfcfg_offset = cnt_num * MLXBF_PMC_REG_SIZE; perfval_offset = perfcfg_offset + pmc->block[blk_num].counters * MLXBF_PMC_REG_SIZE; @@ -893,6 +1404,30 @@ static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num, return 0; } +/* Method to read crspace block event */ +static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num, + uint64_t *result) +{ + uint32_t word, evt; + void *addr; + int ret; + + addr = pmc->block[blk_num].mmio_base + + (rounddown(cnt_num, 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ); + ret = mlxbf_pmc_readl(addr, &word); + if (ret) + return ret; + + if (cnt_num % 2) + evt = FIELD_GET(MLXBF_PMC_CRSPACE_PERFSEL1, word); + else + evt = FIELD_GET(MLXBF_PMC_CRSPACE_PERFSEL0, word); + + *result = evt; + + return 0; +} + /* Method to find the event currently being monitored by a counter */ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, uint64_t *result) @@ -906,6 +1441,9 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, if (is_l3) return mlxbf_pmc_read_l3_event(blk_num, cnt_num, result); + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) + return mlxbf_pmc_read_crspace_event(blk_num, cnt_num, result); + perfcfg_offset = cnt_num * MLXBF_PMC_REG_SIZE; perfval_offset = perfcfg_offset + pmc->block[blk_num].counters * MLXBF_PMC_REG_SIZE; @@ -982,7 +1520,8 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev, if (strstr(pmc->block_name[blk_num], "l3cache")) is_l3 = true; - if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) { + if ((pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) || + (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE)) { if (mlxbf_pmc_read_counter(blk_num, cnt_num, is_l3, &value)) return -EINVAL; } else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER) { @@ -1040,6 +1579,10 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev, err = mlxbf_pmc_write_reg(blk_num, offset, data); if (err) return err; + } else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) { + if (sscanf(attr->attr.name, "counter%d", &cnt_num) != 1) + return -EINVAL; + err = mlxbf_pmc_clear_crspace_counter(blk_num, cnt_num); } else return -EINVAL; @@ -1137,28 +1680,37 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev, return ret; } -/* Show function for "enable" sysfs files - only for l3cache */ +/* Show function for "enable" sysfs files - only for l3cache & crspace */ static ssize_t mlxbf_pmc_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mlxbf_pmc_attribute *attr_enable = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); - uint32_t perfcnt_cfg; + uint32_t perfcnt_cfg, word; int blk_num, value; blk_num = attr_enable->nr; - if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + - MLXBF_PMC_L3C_PERF_CNT_CFG, - &perfcnt_cfg)) - return -EINVAL; + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) { + if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_CTL(pmc->block[blk_num].counters), + &word)) + return -EINVAL; - value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg); + value = FIELD_GET(MLXBF_PMC_CRSPACE_PERFMON_EN, word); + } else { + if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + + MLXBF_PMC_L3C_PERF_CNT_CFG, + &perfcnt_cfg)) + return -EINVAL; + + value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg); + } return sysfs_emit(buf, "%d\n", value); } -/* Store function for "enable" sysfs files - only for l3cache */ +/* Store function for "enable" sysfs files - only for l3cache & crspace */ static ssize_t mlxbf_pmc_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1166,6 +1718,7 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev, struct mlxbf_pmc_attribute *attr_enable = container_of( attr, struct mlxbf_pmc_attribute, dev_attr); int err, en, blk_num; + uint32_t word; blk_num = attr_enable->nr; @@ -1173,19 +1726,35 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev, if (err < 0) return err; - if (!en) { - err = mlxbf_pmc_config_l3_counters(blk_num, false, false); + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) { + err = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_CTL(pmc->block[blk_num].counters), + &word); if (err) - return err; - } else if (en == 1) { - err = mlxbf_pmc_config_l3_counters(blk_num, false, true); - if (err) - return err; - err = mlxbf_pmc_config_l3_counters(blk_num, true, false); + return -EINVAL; + + word &= ~MLXBF_PMC_CRSPACE_PERFMON_EN; + word |= FIELD_PREP(MLXBF_PMC_CRSPACE_PERFMON_EN, en); + if (en) + word |= FIELD_PREP(MLXBF_PMC_CRSPACE_PERFMON_CLR, 1); + + mlxbf_pmc_write(pmc->block[blk_num].mmio_base + + MLXBF_PMC_CRSPACE_PERFMON_CTL(pmc->block[blk_num].counters), + MLXBF_PMC_WRITE_REG_32, word); + } else { + if (en && en != 1) + return -EINVAL; + + err = mlxbf_pmc_config_l3_counters(blk_num, false, !!en); if (err) return err; - } else - return -EINVAL; + + if (en == 1) { + err = mlxbf_pmc_config_l3_counters(blk_num, true, false); + if (err) + return err; + } + } return count; } @@ -1206,7 +1775,8 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num) attr = NULL; /* "enable" sysfs to start/stop the counters. Only in L3C blocks */ - if (strstr(pmc->block_name[blk_num], "l3cache")) { + if (strstr(pmc->block_name[blk_num], "l3cache") || + ((pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE))) { attr = &pmc->block[blk_num].attr_enable; attr->dev_attr.attr.mode = 0644; attr->dev_attr.show = mlxbf_pmc_enable_show; @@ -1297,7 +1867,8 @@ static int mlxbf_pmc_create_groups(struct device *dev, int blk_num) int err; /* Populate attributes based on counter type */ - if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) + if ((pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) || + (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE)) err = mlxbf_pmc_init_perftype_counter(dev, blk_num); else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER) err = mlxbf_pmc_init_perftype_reg(dev, blk_num); @@ -1311,7 +1882,8 @@ static int mlxbf_pmc_create_groups(struct device *dev, int blk_num) pmc->block[blk_num].block_attr_grp.attrs = pmc->block[blk_num].block_attr; pmc->block[blk_num].block_attr_grp.name = devm_kasprintf( dev, GFP_KERNEL, pmc->block_name[blk_num]); - pmc->groups[blk_num] = &pmc->block[blk_num].block_attr_grp; + pmc->groups[pmc->group_num] = &pmc->block[blk_num].block_attr_grp; + pmc->group_num++; return 0; } @@ -1334,13 +1906,52 @@ static int mlxbf_pmc_map_counters(struct device *dev) int i, tile_num, ret; for (i = 0; i < pmc->total_blocks; ++i) { - if (strstr(pmc->block_name[i], "tile")) { + /* Create sysfs for tiles only if block number < tile_count */ + if (strstr(pmc->block_name[i], "tilenet")) { + if (sscanf(pmc->block_name[i], "tilenet%d", &tile_num) != 1) + continue; + + if (tile_num >= pmc->tile_count) + continue; + } else if (strstr(pmc->block_name[i], "tile")) { if (sscanf(pmc->block_name[i], "tile%d", &tile_num) != 1) - return -EINVAL; + continue; if (tile_num >= pmc->tile_count) continue; } + + /* Create sysfs only for enabled MSS blocks */ + if (strstr(pmc->block_name[i], "mss") && + pmc->event_set == MLXBF_PMC_EVENT_SET_BF3) { + int mss_num; + + if (sscanf(pmc->block_name[i], "mss%d", &mss_num) != 1) + continue; + + if (!((pmc->mss_enable >> mss_num) & 0x1)) + continue; + } + + /* Create sysfs only for enabled LLT blocks */ + if (strstr(pmc->block_name[i], "llt_miss")) { + int llt_num; + + if (sscanf(pmc->block_name[i], "llt_miss%d", &llt_num) != 1) + continue; + + if (!((pmc->llt_enable >> llt_num) & 0x1)) + continue; + } else if (strstr(pmc->block_name[i], "llt")) { + int llt_num; + + if (sscanf(pmc->block_name[i], "llt%d", &llt_num) != 1) + continue; + + if (!((pmc->llt_enable >> llt_num) & 0x1)) + continue; + } + ret = device_property_read_u64_array(dev, pmc->block_name[i], info, MLXBF_PMC_INFO_SZ); if (ret) @@ -1417,6 +2028,8 @@ static int mlxbf_pmc_probe(struct platform_device *pdev) pmc->event_set = MLXBF_PMC_EVENT_SET_BF1; else if (!strcmp(hid, "MLNXBFD1")) pmc->event_set = MLXBF_PMC_EVENT_SET_BF2; + else if (!strcmp(hid, "MLNXBFD2")) + pmc->event_set = MLXBF_PMC_EVENT_SET_BF3; else return -ENODEV; @@ -1430,11 +2043,19 @@ static int mlxbf_pmc_probe(struct platform_device *pdev) if (ret != pmc->total_blocks) return -EFAULT; - ret = device_property_read_u32(dev, "tile_num", &pmc->tile_count); - if (ret) - return ret; + if (device_property_read_u32(dev, "tile_num", &pmc->tile_count)) { + if (device_property_read_u8(dev, "llt_enable", &pmc->llt_enable)) { + dev_err(dev, "Number of tiles/LLTs undefined\n"); + return -EINVAL; + } + if (device_property_read_u8(dev, "mss_enable", &pmc->mss_enable)) { + dev_err(dev, "Number of tiles/MSSs undefined\n"); + return -EINVAL; + } + } pmc->pdev = pdev; + pmc->group_num = 0; ret = mlxbf_pmc_map_counters(dev); if (ret) @@ -1449,6 +2070,7 @@ static int mlxbf_pmc_probe(struct platform_device *pdev) static const struct acpi_device_id mlxbf_pmc_acpi_ids[] = { { "MLNXBFD0", 0 }, { "MLNXBFD1", 0 }, + { "MLNXBFD2", 0 }, {}, }; MODULE_DEVICE_TABLE(acpi, mlxbf_pmc_acpi_ids); -- cgit From 318d97849fc2cb45b2238fdb9eaa714b5d669df5 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 19 Sep 2023 10:15:26 -0400 Subject: platform/x86: think-lmi: Add bulk save feature On Lenovo platforms there is a limitation in the number of times an attribute can be saved. This is an architectural limitation and it limits the number of attributes that can be modified to 48. A solution for this is instead of the attribute being saved after every modification allow a user to bulk set the attributes and then trigger a final save. This allows unlimited attributes. This patch introduces a save_settings attribute that can be configured to either single or bulk mode by the user. Single mode is the default but customers who want to avoid the 48 attribute limit can enable bulk mode. Displaying the save_settings attribute will display the enabled mode. When in bulk mode writing 'save' to the save_settings attribute will trigger a save. Once this has been done a reboot is required before more attributes can be modified. Signed-off-by: Mark Pearson Link: https://lore.kernel.org/r/20230919141530.4805-1-mpearson-lenovo@squebb.ca Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../ABI/testing/sysfs-class-firmware-attributes | 30 ++++ drivers/platform/x86/think-lmi.c | 152 +++++++++++++++++++-- drivers/platform/x86/think-lmi.h | 16 +++ 3 files changed, 183 insertions(+), 15 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes index f205d39409a3..9c82c7b42ff8 100644 --- a/Documentation/ABI/testing/sysfs-class-firmware-attributes +++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes @@ -383,6 +383,36 @@ Description: Note that any changes to this attribute requires a reboot for changes to take effect. +What: /sys/class/firmware-attributes/*/attributes/save_settings +Date: August 2023 +KernelVersion: 6.6 +Contact: Mark Pearson +Description: + On Lenovo platforms there is a limitation in the number of times an attribute can be + saved. This is an architectural limitation and it limits the number of attributes + that can be modified to 48. + A solution for this is instead of the attribute being saved after every modification, + to allow a user to bulk set the attributes, and then trigger a final save. This allows + unlimited attributes. + + Read the attribute to check what save mode is enabled (single or bulk). + E.g: + # cat /sys/class/firmware-attributes/thinklmi/attributes/save_settings + single + + Write the attribute with 'bulk' to enable bulk save mode. + Write the attribute with 'single' to enable saving, after every attribute set. + The default setting is single mode. + E.g: + # echo bulk > /sys/class/firmware-attributes/thinklmi/attributes/save_settings + + When in bulk mode write 'save' to trigger a save of all currently modified attributes. + Note, once a save has been triggered, in bulk mode, attributes can no longer be set and + will return a permissions error. This is to prevent users hitting the 48+ save limitation + (which requires entering the BIOS to clear the error condition) + E.g: + # echo save > /sys/class/firmware-attributes/thinklmi/attributes/save_settings + What: /sys/class/firmware-attributes/*/attributes/debug_cmd Date: July 2021 KernelVersion: 5.14 diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 79346881cadb..d329626720df 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -985,6 +985,13 @@ static ssize_t current_value_store(struct kobject *kobj, if (!tlmi_priv.can_set_bios_settings) return -EOPNOTSUPP; + /* + * If we are using bulk saves a reboot should be done once save has + * been called + */ + if (tlmi_priv.save_mode == TLMI_SAVE_BULK && tlmi_priv.reboot_required) + return -EPERM; + new_setting = kstrdup(buf, GFP_KERNEL); if (!new_setting) return -ENOMEM; @@ -1011,10 +1018,11 @@ static ssize_t current_value_store(struct kobject *kobj, ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, set_str); if (ret) goto out; - ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID, - tlmi_priv.pwd_admin->save_signature); - if (ret) - goto out; + if (tlmi_priv.save_mode == TLMI_SAVE_BULK) + tlmi_priv.save_required = true; + else + ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID, + tlmi_priv.pwd_admin->save_signature); } else if (tlmi_priv.opcode_support) { /* * If opcode support is present use that interface. @@ -1033,14 +1041,17 @@ static ssize_t current_value_store(struct kobject *kobj, if (ret) goto out; - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { - ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", - tlmi_priv.pwd_admin->password); - if (ret) - goto out; + if (tlmi_priv.save_mode == TLMI_SAVE_BULK) { + tlmi_priv.save_required = true; + } else { + if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", + tlmi_priv.pwd_admin->password); + if (ret) + goto out; + } + ret = tlmi_save_bios_settings(""); } - - ret = tlmi_save_bios_settings(""); } else { /* old non-opcode based authentication method (deprecated) */ if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", @@ -1068,10 +1079,14 @@ static ssize_t current_value_store(struct kobject *kobj, if (ret) goto out; - if (auth_str) - ret = tlmi_save_bios_settings(auth_str); - else - ret = tlmi_save_bios_settings(""); + if (tlmi_priv.save_mode == TLMI_SAVE_BULK) { + tlmi_priv.save_required = true; + } else { + if (auth_str) + ret = tlmi_save_bios_settings(auth_str); + else + ret = tlmi_save_bios_settings(""); + } } if (!ret && !tlmi_priv.pending_changes) { tlmi_priv.pending_changes = true; @@ -1152,6 +1167,107 @@ static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute * static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); +static const char * const save_mode_strings[] = { + [TLMI_SAVE_SINGLE] = "single", + [TLMI_SAVE_BULK] = "bulk", + [TLMI_SAVE_SAVE] = "save" +}; + +static ssize_t save_settings_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + /* Check that setting is valid */ + if (WARN_ON(tlmi_priv.save_mode < TLMI_SAVE_SINGLE || + tlmi_priv.save_mode > TLMI_SAVE_BULK)) + return -EIO; + return sysfs_emit(buf, "%s\n", save_mode_strings[tlmi_priv.save_mode]); +} + +static ssize_t save_settings_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + char *auth_str = NULL; + int ret = 0; + int cmd; + + cmd = sysfs_match_string(save_mode_strings, buf); + if (cmd < 0) + return cmd; + + /* Use lock in case multiple WMI operations needed */ + mutex_lock(&tlmi_mutex); + + switch (cmd) { + case TLMI_SAVE_SINGLE: + case TLMI_SAVE_BULK: + tlmi_priv.save_mode = cmd; + goto out; + case TLMI_SAVE_SAVE: + /* Check if supported*/ + if (!tlmi_priv.can_set_bios_settings || + tlmi_priv.save_mode == TLMI_SAVE_SINGLE) { + ret = -EOPNOTSUPP; + goto out; + } + /* Check there is actually something to save */ + if (!tlmi_priv.save_required) { + ret = -ENOENT; + goto out; + } + /* Check if certificate authentication is enabled and active */ + if (tlmi_priv.certificate_support && tlmi_priv.pwd_admin->cert_installed) { + if (!tlmi_priv.pwd_admin->signature || + !tlmi_priv.pwd_admin->save_signature) { + ret = -EINVAL; + goto out; + } + ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID, + tlmi_priv.pwd_admin->save_signature); + if (ret) + goto out; + } else if (tlmi_priv.opcode_support) { + if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", + tlmi_priv.pwd_admin->password); + if (ret) + goto out; + } + ret = tlmi_save_bios_settings(""); + } else { /* old non-opcode based authentication method (deprecated) */ + if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", + tlmi_priv.pwd_admin->password, + encoding_options[tlmi_priv.pwd_admin->encoding], + tlmi_priv.pwd_admin->kbdlang); + if (!auth_str) { + ret = -ENOMEM; + goto out; + } + } + + if (auth_str) + ret = tlmi_save_bios_settings(auth_str); + else + ret = tlmi_save_bios_settings(""); + } + tlmi_priv.save_required = false; + tlmi_priv.reboot_required = true; + + if (!ret && !tlmi_priv.pending_changes) { + tlmi_priv.pending_changes = true; + /* let userland know it may need to check reboot pending again */ + kobject_uevent(&tlmi_priv.class_dev->kobj, KOBJ_CHANGE); + } + break; + } +out: + mutex_unlock(&tlmi_mutex); + kfree(auth_str); + return ret ?: count; +} + +static struct kobj_attribute save_settings = __ATTR_RW(save_settings); + /* ---- Debug interface--------------------------------------------------------- */ static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -1221,6 +1337,8 @@ static void tlmi_release_attr(void) } } sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &pending_reboot.attr); + sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &save_settings.attr); + if (tlmi_priv.can_debug_cmd && debug_support) sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr); @@ -1302,6 +1420,10 @@ static int tlmi_sysfs_init(void) if (ret) goto fail_create_attr; + ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &save_settings.attr); + if (ret) + goto fail_create_attr; + if (tlmi_priv.can_debug_cmd && debug_support) { ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr); if (ret) diff --git a/drivers/platform/x86/think-lmi.h b/drivers/platform/x86/think-lmi.h index 4daba6151cd6..e1975ffebeb4 100644 --- a/drivers/platform/x86/think-lmi.h +++ b/drivers/platform/x86/think-lmi.h @@ -27,6 +27,19 @@ enum level_option { TLMI_LEVEL_MASTER, }; +/* + * There are a limit on the number of WMI operations you can do if you use + * the default implementation of saving on every set. This is due to a + * limitation in EFI variable space used. + * Have a 'bulk save' mode where you can manually trigger the save, and can + * therefore set unlimited variables - for users that need it. + */ +enum save_mode { + TLMI_SAVE_SINGLE, + TLMI_SAVE_BULK, + TLMI_SAVE_SAVE, +}; + /* password configuration details */ struct tlmi_pwdcfg_core { uint32_t password_mode; @@ -86,6 +99,9 @@ struct think_lmi { bool can_debug_cmd; bool opcode_support; bool certificate_support; + enum save_mode save_mode; + bool save_required; + bool reboot_required; struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT]; struct device *class_dev; -- cgit From 45ae16ec3a3d63375cd95317af66b844661e86c4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 15 Sep 2023 23:49:33 +0200 Subject: platform/x86: x86-android-tablets: Fix Lenovo Yoga Tablet 2 830F/L vs 1050F/L detection gpio_crystalcove pin 10 is already in input mode and passing GPIOD_IN when requesting the GPIO changes its pull-up/-down settings causing the 830F/L to get misdetected as 1050F/L. Switch to using GPIOD_ASIS when requesting the GPIO to fix the misdetection. Fixes: 4014ae236b1d ("platform/x86: x86-android-tablets: Stop using gpiolib private APIs") Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230915214933.62595-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 5c803cdb5586..c1e68211283f 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -436,7 +436,7 @@ static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void) /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, "yoga_bootstrap", - false, GPIOD_IN, &gpiod); + false, GPIOD_ASIS, &gpiod); if (ret) return ret; -- cgit From 8d53109dd66f20fe987206258d1371b163f48ead Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:37:59 +0200 Subject: platform/surface: surface3-wmi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-2-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface3-wmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c index ca4602bcc7de..c15ed7a12784 100644 --- a/drivers/platform/surface/surface3-wmi.c +++ b/drivers/platform/surface/surface3-wmi.c @@ -226,14 +226,13 @@ static int __init s3_wmi_probe(struct platform_device *pdev) return error; } -static int s3_wmi_remove(struct platform_device *device) +static void s3_wmi_remove(struct platform_device *device) { /* remove the hotplug context from the acpi device */ s3_wmi.touchscreen_adev->hp = NULL; /* reinstall the actual PNPC0C0D LID default handle */ acpi_bus_scan(s3_wmi.pnp0c0d_adev->handle); - return 0; } static int __maybe_unused s3_wmi_resume(struct device *dev) @@ -248,7 +247,7 @@ static struct platform_driver s3_wmi_driver = { .name = "surface3-wmi", .pm = &s3_wmi_pm, }, - .remove = s3_wmi_remove, + .remove_new = s3_wmi_remove, }; static int __init s3_wmi_init(void) -- cgit From 342d7dce2f12f31b3397043c2ddb6e2c84503b32 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:38:00 +0200 Subject: platform/surface: acpi-notify: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-3-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_acpi_notify.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c index 897cdd9c3aae..2c091066b6a1 100644 --- a/drivers/platform/surface/surface_acpi_notify.c +++ b/drivers/platform/surface/surface_acpi_notify.c @@ -850,7 +850,7 @@ err_enable_events: return status; } -static int san_remove(struct platform_device *pdev) +static void san_remove(struct platform_device *pdev) { acpi_handle san = ACPI_HANDLE(&pdev->dev); @@ -864,8 +864,6 @@ static int san_remove(struct platform_device *pdev) * all delayed works they may have spawned are run to completion. */ flush_workqueue(san_wq); - - return 0; } static const struct acpi_device_id san_match[] = { @@ -876,7 +874,7 @@ MODULE_DEVICE_TABLE(acpi, san_match); static struct platform_driver surface_acpi_notify = { .probe = san_probe, - .remove = san_remove, + .remove_new = san_remove, .driver = { .name = "surface_acpi_notify", .acpi_match_table = san_match, -- cgit From 1cc995a6e4e81cdef91628a929992f2142836ffc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:38:01 +0200 Subject: platform/surface: aggregator-cdev: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-4-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_aggregator_cdev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c index 07f0ed658369..07e065b9159f 100644 --- a/drivers/platform/surface/surface_aggregator_cdev.c +++ b/drivers/platform/surface/surface_aggregator_cdev.c @@ -714,7 +714,7 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) return 0; } -static int ssam_dbg_device_remove(struct platform_device *pdev) +static void ssam_dbg_device_remove(struct platform_device *pdev) { struct ssam_cdev *cdev = platform_get_drvdata(pdev); struct ssam_cdev_client *client; @@ -757,14 +757,13 @@ static int ssam_dbg_device_remove(struct platform_device *pdev) misc_deregister(&cdev->mdev); ssam_cdev_put(cdev); - return 0; } static struct platform_device *ssam_cdev_device; static struct platform_driver ssam_cdev_driver = { .probe = ssam_dbg_device_probe, - .remove = ssam_dbg_device_remove, + .remove_new = ssam_dbg_device_remove, .driver = { .name = SSAM_CDEV_DEVICE_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, -- cgit From 09416e0f88a96d4783c17cfc29b9c9fd6d0000b1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:38:02 +0200 Subject: platform/surface: aggregator-registry: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-5-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_aggregator_registry.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 0fe5be539652..aeb3feae40ff 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -418,19 +418,18 @@ static int ssam_platform_hub_probe(struct platform_device *pdev) return status; } -static int ssam_platform_hub_remove(struct platform_device *pdev) +static void ssam_platform_hub_remove(struct platform_device *pdev) { const struct software_node **nodes = platform_get_drvdata(pdev); ssam_remove_clients(&pdev->dev); set_secondary_fwnode(&pdev->dev, NULL); software_node_unregister_node_group(nodes); - return 0; } static struct platform_driver ssam_platform_hub_driver = { .probe = ssam_platform_hub_probe, - .remove = ssam_platform_hub_remove, + .remove_new = ssam_platform_hub_remove, .driver = { .name = "surface_aggregator_platform_hub", .acpi_match_table = ssam_platform_hub_match, -- cgit From 0c845611a20edcd803f9ab7fd2725922008fa178 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:38:03 +0200 Subject: platform/surface: dtx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-6-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_dtx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/surface/surface_dtx.c b/drivers/platform/surface/surface_dtx.c index 30cbde278c59..2de843b7ea70 100644 --- a/drivers/platform/surface/surface_dtx.c +++ b/drivers/platform/surface/surface_dtx.c @@ -1168,10 +1168,9 @@ static int surface_dtx_platform_probe(struct platform_device *pdev) return 0; } -static int surface_dtx_platform_remove(struct platform_device *pdev) +static void surface_dtx_platform_remove(struct platform_device *pdev) { sdtx_device_destroy(platform_get_drvdata(pdev)); - return 0; } static const struct acpi_device_id surface_dtx_acpi_match[] = { @@ -1182,7 +1181,7 @@ MODULE_DEVICE_TABLE(acpi, surface_dtx_acpi_match); static struct platform_driver surface_dtx_platform_driver = { .probe = surface_dtx_platform_probe, - .remove = surface_dtx_platform_remove, + .remove_new = surface_dtx_platform_remove, .driver = { .name = "surface_dtx_pltf", .acpi_match_table = surface_dtx_acpi_match, -- cgit From 52f8a4b63b0e289b2b83b55a65993b3a7997836e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:38:04 +0200 Subject: platform/surface: gpe: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-7-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_gpe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c index c219b840d491..62fd4004db31 100644 --- a/drivers/platform/surface/surface_gpe.c +++ b/drivers/platform/surface/surface_gpe.c @@ -267,20 +267,18 @@ static int surface_gpe_probe(struct platform_device *pdev) return ret; } -static int surface_gpe_remove(struct platform_device *pdev) +static void surface_gpe_remove(struct platform_device *pdev) { struct surface_lid_device *lid = dev_get_drvdata(&pdev->dev); /* restore default behavior without this module */ surface_lid_enable_wakeup(&pdev->dev, false); acpi_disable_gpe(NULL, lid->gpe_number); - - return 0; } static struct platform_driver surface_gpe_driver = { .probe = surface_gpe_probe, - .remove = surface_gpe_remove, + .remove_new = surface_gpe_remove, .driver = { .name = "surface_gpe", .pm = &surface_gpe_pm, -- cgit From d5e2c23366d8fb86d06a7d8eb949f0dffc41b50c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 22:38:05 +0200 Subject: platform/surface: hotplug: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230917203805.1149595-8-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_hotplug.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/surface/surface_hotplug.c b/drivers/platform/surface/surface_hotplug.c index 7b6d887dccdb..a404f26cfae8 100644 --- a/drivers/platform/surface/surface_hotplug.c +++ b/drivers/platform/surface/surface_hotplug.c @@ -183,7 +183,7 @@ static int shps_setup_irq(struct platform_device *pdev, enum shps_irq_type type) return 0; } -static int surface_hotplug_remove(struct platform_device *pdev) +static void surface_hotplug_remove(struct platform_device *pdev) { struct shps_device *sdev = platform_get_drvdata(pdev); int i; @@ -195,8 +195,6 @@ static int surface_hotplug_remove(struct platform_device *pdev) mutex_destroy(&sdev->lock[i]); } - - return 0; } static int surface_hotplug_probe(struct platform_device *pdev) @@ -261,7 +259,7 @@ MODULE_DEVICE_TABLE(acpi, surface_hotplug_acpi_match); static struct platform_driver surface_hotplug_driver = { .probe = surface_hotplug_probe, - .remove = surface_hotplug_remove, + .remove_new = surface_hotplug_remove, .driver = { .name = "surface_hotplug", .acpi_match_table = surface_hotplug_acpi_match, -- cgit From e621198016e99cddc097d32cd3b56b15cee27d91 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 18 Sep 2023 16:51:15 +0300 Subject: platform/x86: think-lmi: Replace kstrdup() + strreplace() with kstrdup_and_replace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace open coded functionalify of kstrdup_and_replace() with a call. Reviewed-by: Ilpo Järvinen Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230918135116.1248560-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/think-lmi.c | 43 ++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index d329626720df..a5e87aa3395c 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -432,13 +432,11 @@ static ssize_t new_password_store(struct kobject *kobj, if (!tlmi_priv.can_set_bios_password) return -EOPNOTSUPP; - new_pwd = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present, setting password won't work if it is present */ + new_pwd = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!new_pwd) return -ENOMEM; - /* Strip out CR if one is present, setting password won't work if it is present */ - strip_cr(new_pwd); - /* Use lock in case multiple WMI operations needed */ mutex_lock(&tlmi_mutex); @@ -709,13 +707,11 @@ static ssize_t cert_to_password_store(struct kobject *kobj, if (!setting->signature || !setting->signature[0]) return -EACCES; - passwd = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present */ + passwd = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!passwd) return -ENOMEM; - /* Strip out CR if one is present */ - strip_cr(passwd); - /* Format: 'Password,Signature' */ auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature); if (!auth_str) { @@ -765,11 +761,10 @@ static ssize_t certificate_store(struct kobject *kobj, return ret ?: count; } - new_cert = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present */ + new_cert = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!new_cert) return -ENOMEM; - /* Strip out CR if one is present */ - strip_cr(new_cert); if (setting->cert_installed) { /* Certificate is installed so this is an update */ @@ -817,13 +812,11 @@ static ssize_t signature_store(struct kobject *kobj, if (!tlmi_priv.certificate_support) return -EOPNOTSUPP; - new_signature = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present */ + new_signature = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!new_signature) return -ENOMEM; - /* Strip out CR if one is present */ - strip_cr(new_signature); - /* Free any previous signature */ kfree(setting->signature); setting->signature = new_signature; @@ -846,13 +839,11 @@ static ssize_t save_signature_store(struct kobject *kobj, if (!tlmi_priv.certificate_support) return -EOPNOTSUPP; - new_signature = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present */ + new_signature = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!new_signature) return -ENOMEM; - /* Strip out CR if one is present */ - strip_cr(new_signature); - /* Free any previous signature */ kfree(setting->save_signature); setting->save_signature = new_signature; @@ -992,13 +983,11 @@ static ssize_t current_value_store(struct kobject *kobj, if (tlmi_priv.save_mode == TLMI_SAVE_BULK && tlmi_priv.reboot_required) return -EPERM; - new_setting = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present */ + new_setting = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!new_setting) return -ENOMEM; - /* Strip out CR if one is present */ - strip_cr(new_setting); - /* Use lock in case multiple WMI operations needed */ mutex_lock(&tlmi_mutex); @@ -1279,13 +1268,11 @@ static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr if (!tlmi_priv.can_debug_cmd) return -EOPNOTSUPP; - new_setting = kstrdup(buf, GFP_KERNEL); + /* Strip out CR if one is present */ + new_setting = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL); if (!new_setting) return -ENOMEM; - /* Strip out CR if one is present */ - strip_cr(new_setting); - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", tlmi_priv.pwd_admin->password, -- cgit From 6cb9c8637c672584d4915559001020237840acba Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 18 Sep 2023 16:51:16 +0300 Subject: platform/x86: think-lmi: Use strreplace() to replace a character by nul MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can replace p = strchrnul(str, '$OLD'); *p = '\0'; with strreplace(str, '$OLD', '\0'); that does the compatible modification without a need of the temporary variable. Reviewed-by: Ilpo Järvinen Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230918135116.1248560-2-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/think-lmi.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index a5e87aa3395c..4be6f28d4600 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -198,14 +198,6 @@ static struct think_lmi tlmi_priv; static struct class *fw_attr_class; static DEFINE_MUTEX(tlmi_mutex); -/* ------ Utility functions ------------*/ -/* Strip out CR if one is present */ -static void strip_cr(char *str) -{ - char *p = strchrnul(str, '\n'); - *p = '\0'; -} - /* Convert BIOS WMI error string to suitable error code */ static int tlmi_errstr_to_err(const char *errstr) { @@ -411,7 +403,7 @@ static ssize_t current_password_store(struct kobject *kobj, strscpy(setting->password, buf, setting->maxlen); /* Strip out CR if one is present, setting password won't work if it is present */ - strip_cr(setting->password); + strreplace(setting->password, '\n', '\0'); return count; } @@ -921,7 +913,7 @@ static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *at static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); - char *item, *value, *p; + char *item, *value; int ret; ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID); @@ -934,8 +926,7 @@ static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *a ret = -EINVAL; else { /* On Workstations remove the Options part after the value */ - p = strchrnul(value, ';'); - *p = '\0'; + strreplace(value, ';', '\0'); ret = sysfs_emit(buf, "%s\n", value + 1); } kfree(item); @@ -1540,7 +1531,6 @@ static int tlmi_analyze(void) for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { struct tlmi_attr_setting *setting; char *item = NULL; - char *p; tlmi_priv.setting[i] = NULL; ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); @@ -1557,8 +1547,7 @@ static int tlmi_analyze(void) strreplace(item, '/', '\\'); /* Remove the value part */ - p = strchrnul(item, ','); - *p = '\0'; + strreplace(item, ',', '\0'); /* Create a setting entry */ setting = kzalloc(sizeof(*setting), GFP_KERNEL); -- cgit From 55ec81f7517fad09135f65552cea0a3ee84fff30 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Sep 2023 10:54:21 -0700 Subject: platform/x86: hp-bioscfg: Annotate struct bios_args with __counted_by MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct bios_args. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Cc: Jorge Lopez Cc: Hans de Goede Cc: Mark Gross Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Tom Rix Cc: platform-driver-x86@vger.kernel.org Cc: llvm@lists.linux.dev Signed-off-by: Kees Cook Reviewed-by: "Gustavo A. R. Silva" Link: https://lore.kernel.org/r/20230922175420.work.701-kees@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c b/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c index dea54f35b8b5..4da99cb7218d 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c +++ b/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c @@ -19,7 +19,7 @@ struct bios_args { u32 command; u32 commandtype; u32 datasize; - u8 data[]; + u8 data[] __counted_by(datasize); }; /** -- cgit From 9cf63f3a33e929f7eca36409914b8c12102b9984 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Sep 2023 10:54:37 -0700 Subject: platform/surface: aggregator: Annotate struct ssam_event with __counted_by MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct ssam_event. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Cc: Maximilian Luz Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Kees Cook Reviewed-by: "Gustavo A. R. Silva" Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20230922175436.work.031-kees@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/linux/surface_aggregator/controller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h index cb7980805920..5b67f0f47d80 100644 --- a/include/linux/surface_aggregator/controller.h +++ b/include/linux/surface_aggregator/controller.h @@ -44,7 +44,7 @@ struct ssam_event { u8 command_id; u8 instance_id; u16 length; - u8 data[]; + u8 data[] __counted_by(length); }; /** -- cgit From 18801efed74a671f5c3bd1869739854cf2a5568f Mon Sep 17 00:00:00 2001 From: Fernando Eckhardt Valle Date: Tue, 26 Sep 2023 17:21:44 -0300 Subject: platform/x86: thinkpad_acpi: sysfs interface to auxmac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer Thinkpads have a feature called MAC Address Pass-through. This patch provides a sysfs interface that userspace can use to get this auxiliary mac address. Signed-off-by: Fernando Eckhardt Valle Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230926202144.5906-1-fevalle@ipt.br Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../admin-guide/laptops/thinkpad-acpi.rst | 20 +++++ drivers/platform/x86/thinkpad_acpi.c | 88 ++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst index e27a1c3f634e..98d304010170 100644 --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst @@ -53,6 +53,7 @@ detailed description): - Lap mode sensor - Setting keyboard language - WWAN Antenna type + - Auxmac A compatibility table by model and feature is maintained on the web site, http://ibm-acpi.sf.net/. I appreciate any success or failure @@ -1511,6 +1512,25 @@ Currently 2 antenna types are supported as mentioned below: The property is read-only. If the platform doesn't have support the sysfs class is not created. +Auxmac +------ + +sysfs: auxmac + +Some newer Thinkpads have a feature called MAC Address Pass-through. This +feature is implemented by the system firmware to provide a system unique MAC, +that can override a dock or USB ethernet dongle MAC, when connected to a +network. This property enables user-space to easily determine the MAC address +if the feature is enabled. + +The values of this auxiliary MAC are: + + cat /sys/devices/platform/thinkpad_acpi/auxmac + +If the feature is disabled, the value will be 'disabled'. + +This property is read-only. + Adaptive keyboard ----------------- diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d70c89d32534..9c19624a7454 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -10785,6 +10785,89 @@ static struct ibm_struct dprc_driver_data = { .name = "dprc", }; +/* + * Auxmac + * + * This auxiliary mac address is enabled in the bios through the + * MAC Address Pass-through feature. In most cases, there are three + * possibilities: Internal Mac, Second Mac, and disabled. + * + */ + +#define AUXMAC_LEN 12 +#define AUXMAC_START 9 +#define AUXMAC_STRLEN 22 +#define AUXMAC_BEGIN_MARKER 8 +#define AUXMAC_END_MARKER 21 + +static char auxmac[AUXMAC_LEN + 1]; + +static int auxmac_init(struct ibm_init_struct *iibm) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + status = acpi_evaluate_object(NULL, "\\MACA", NULL, &buffer); + + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = buffer.pointer; + + if (obj->type != ACPI_TYPE_STRING || obj->string.length != AUXMAC_STRLEN) { + pr_info("Invalid buffer for MAC address pass-through.\n"); + goto auxmacinvalid; + } + + if (obj->string.pointer[AUXMAC_BEGIN_MARKER] != '#' || + obj->string.pointer[AUXMAC_END_MARKER] != '#') { + pr_info("Invalid header for MAC address pass-through.\n"); + goto auxmacinvalid; + } + + if (strncmp(obj->string.pointer + AUXMAC_START, "XXXXXXXXXXXX", AUXMAC_LEN) != 0) + strscpy(auxmac, obj->string.pointer + AUXMAC_START, sizeof(auxmac)); + else + strscpy(auxmac, "disabled", sizeof(auxmac)); + +free: + kfree(obj); + return 0; + +auxmacinvalid: + strscpy(auxmac, "unavailable", sizeof(auxmac)); + goto free; +} + +static struct ibm_struct auxmac_data = { + .name = "auxmac", +}; + +static ssize_t auxmac_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", auxmac); +} +static DEVICE_ATTR_RO(auxmac); + +static umode_t auxmac_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return auxmac[0] == 0 ? 0 : attr->mode; +} + +static struct attribute *auxmac_attributes[] = { + &dev_attr_auxmac.attr, + NULL +}; + +static const struct attribute_group auxmac_attr_group = { + .is_visible = auxmac_attr_is_visible, + .attrs = auxmac_attributes, +}; + /* --------------------------------------------------------------------- */ static struct attribute *tpacpi_driver_attributes[] = { @@ -10843,6 +10926,7 @@ static const struct attribute_group *tpacpi_groups[] = { &proxsensor_attr_group, &kbdlang_attr_group, &dprc_attr_group, + &auxmac_attr_group, NULL, }; @@ -11414,6 +11498,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = tpacpi_dprc_init, .data = &dprc_driver_data, }, + { + .init = auxmac_init, + .data = &auxmac_data, + }, }; static int __init set_ibm_param(const char *val, const struct kernel_param *kp) -- cgit From a6cff4212984010c119263593c0a249e15ad5caf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:27 +0200 Subject: platform/mellanox: mlxbf-bootctl: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-15-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-bootctl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index 4ee7bb431b7c..1ac7dab22c63 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -1028,17 +1028,15 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev) return ret; } -static int mlxbf_bootctl_remove(struct platform_device *pdev) +static void mlxbf_bootctl_remove(struct platform_device *pdev) { sysfs_remove_bin_file(&pdev->dev.kobj, &mlxbf_bootctl_bootfifo_sysfs_attr); - - return 0; } static struct platform_driver mlxbf_bootctl_driver = { .probe = mlxbf_bootctl_probe, - .remove = mlxbf_bootctl_remove, + .remove_new = mlxbf_bootctl_remove, .driver = { .name = "mlxbf-bootctl", .dev_groups = mlxbf_bootctl_groups, -- cgit From db971cf26014948d642522dae9a01968d009751c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:28 +0200 Subject: platform/mellanox: mlxbf-tmfifo: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-16-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxbf-tmfifo.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index f3696a54a2bd..69729ba8f505 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -1361,13 +1361,11 @@ fail: } /* Device remove function. */ -static int mlxbf_tmfifo_remove(struct platform_device *pdev) +static void mlxbf_tmfifo_remove(struct platform_device *pdev) { struct mlxbf_tmfifo *fifo = platform_get_drvdata(pdev); mlxbf_tmfifo_cleanup(fifo); - - return 0; } static const struct acpi_device_id mlxbf_tmfifo_acpi_match[] = { @@ -1378,7 +1376,7 @@ MODULE_DEVICE_TABLE(acpi, mlxbf_tmfifo_acpi_match); static struct platform_driver mlxbf_tmfifo_driver = { .probe = mlxbf_tmfifo_probe, - .remove = mlxbf_tmfifo_remove, + .remove_new = mlxbf_tmfifo_remove, .driver = { .name = "bf-tmfifo", .acpi_match_table = mlxbf_tmfifo_acpi_match, -- cgit From a62507eb57124f4889b97de9917a164bf3109aa6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:29 +0200 Subject: platform/mellanox: mlxreg-hotplug: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-17-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxreg-hotplug.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index eb5ad35274dd..5c022b258f91 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -786,15 +786,13 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) return 0; } -static int mlxreg_hotplug_remove(struct platform_device *pdev) +static void mlxreg_hotplug_remove(struct platform_device *pdev) { struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(&pdev->dev); /* Clean interrupts setup. */ mlxreg_hotplug_unset_irq(priv); devm_free_irq(&pdev->dev, priv->irq, priv); - - return 0; } static struct platform_driver mlxreg_hotplug_driver = { @@ -802,7 +800,7 @@ static struct platform_driver mlxreg_hotplug_driver = { .name = "mlxreg-hotplug", }, .probe = mlxreg_hotplug_probe, - .remove = mlxreg_hotplug_remove, + .remove_new = mlxreg_hotplug_remove, }; module_platform_driver(mlxreg_hotplug_driver); -- cgit From e0489bb002ff581f9439de2d22b49e140e73e6b4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:30 +0200 Subject: platform/mellanox: mlxreg-io: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-18-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxreg-io.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c index 83ba037408cd..ee7bd623ba44 100644 --- a/drivers/platform/mellanox/mlxreg-io.c +++ b/drivers/platform/mellanox/mlxreg-io.c @@ -263,13 +263,11 @@ static int mlxreg_io_probe(struct platform_device *pdev) return 0; } -static int mlxreg_io_remove(struct platform_device *pdev) +static void mlxreg_io_remove(struct platform_device *pdev) { struct mlxreg_io_priv_data *priv = dev_get_drvdata(&pdev->dev); mutex_destroy(&priv->io_lock); - - return 0; } static struct platform_driver mlxreg_io_driver = { @@ -277,7 +275,7 @@ static struct platform_driver mlxreg_io_driver = { .name = "mlxreg-io", }, .probe = mlxreg_io_probe, - .remove = mlxreg_io_remove, + .remove_new = mlxreg_io_remove, }; module_platform_driver(mlxreg_io_driver); -- cgit From cfae9860ca990c5a642da85e01e0eb2e7dd9db35 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:31 +0200 Subject: platform/mellanox: mlxreg-lc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-19-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxreg-lc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/platform/mellanox/mlxreg-lc.c b/drivers/platform/mellanox/mlxreg-lc.c index 8d833836a6d3..43d119e3a473 100644 --- a/drivers/platform/mellanox/mlxreg-lc.c +++ b/drivers/platform/mellanox/mlxreg-lc.c @@ -907,7 +907,7 @@ i2c_get_adapter_fail: return err; } -static int mlxreg_lc_remove(struct platform_device *pdev) +static void mlxreg_lc_remove(struct platform_device *pdev) { struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev); struct mlxreg_lc *mlxreg_lc = platform_get_drvdata(pdev); @@ -921,7 +921,7 @@ static int mlxreg_lc_remove(struct platform_device *pdev) * is nothing to remove. */ if (!data->notifier || !data->notifier->handle) - return 0; + return; /* Clear event notification callback and handle. */ data->notifier->user_handler = NULL; @@ -940,13 +940,11 @@ static int mlxreg_lc_remove(struct platform_device *pdev) i2c_put_adapter(data->hpdev.adapter); data->hpdev.adapter = NULL; } - - return 0; } static struct platform_driver mlxreg_lc_driver = { .probe = mlxreg_lc_probe, - .remove = mlxreg_lc_remove, + .remove_new = mlxreg_lc_remove, .driver = { .name = "mlxreg-lc", }, -- cgit From 61e2e9ee39d921756050dafeb91537284139f611 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:32 +0200 Subject: platform/mellanox: nvsw-sn2201: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-20-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/nvsw-sn2201.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/mellanox/nvsw-sn2201.c b/drivers/platform/mellanox/nvsw-sn2201.c index 75b699676ca6..3ef655591424 100644 --- a/drivers/platform/mellanox/nvsw-sn2201.c +++ b/drivers/platform/mellanox/nvsw-sn2201.c @@ -1217,7 +1217,7 @@ static int nvsw_sn2201_probe(struct platform_device *pdev) return nvsw_sn2201_config_pre_init(nvsw_sn2201); } -static int nvsw_sn2201_remove(struct platform_device *pdev) +static void nvsw_sn2201_remove(struct platform_device *pdev) { struct nvsw_sn2201 *nvsw_sn2201 = platform_get_drvdata(pdev); @@ -1239,8 +1239,6 @@ static int nvsw_sn2201_remove(struct platform_device *pdev) /* Unregister I2C controller. */ if (nvsw_sn2201->pdev_i2c) platform_device_unregister(nvsw_sn2201->pdev_i2c); - - return 0; } static const struct acpi_device_id nvsw_sn2201_acpi_ids[] = { @@ -1252,7 +1250,7 @@ MODULE_DEVICE_TABLE(acpi, nvsw_sn2201_acpi_ids); static struct platform_driver nvsw_sn2201_driver = { .probe = nvsw_sn2201_probe, - .remove = nvsw_sn2201_remove, + .remove_new = nvsw_sn2201_remove, .driver = { .name = "nvsw-sn2201", .acpi_match_table = nvsw_sn2201_acpi_ids, -- cgit From 3e29812e6e172ba772761e6995753b729e8a6f77 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:33 +0200 Subject: platform/x86/intel: bytcrc_pwrsrc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-21-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/bytcrc_pwrsrc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c index 8a022b90d12d..418b71af27ff 100644 --- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c +++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c @@ -158,17 +158,16 @@ static int crc_pwrsrc_probe(struct platform_device *pdev) return 0; } -static int crc_pwrsrc_remove(struct platform_device *pdev) +static void crc_pwrsrc_remove(struct platform_device *pdev) { struct crc_pwrsrc_data *data = platform_get_drvdata(pdev); debugfs_remove_recursive(data->debug_dentry); - return 0; } static struct platform_driver crc_pwrsrc_driver = { .probe = crc_pwrsrc_probe, - .remove = crc_pwrsrc_remove, + .remove_new = crc_pwrsrc_remove, .driver = { .name = "crystal_cove_pwrsrc", }, -- cgit From 1348317db770681cde4c6ac04afd75a1b0a99098 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:34 +0200 Subject: platform/x86: mlx-platform: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-22-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/mlx-platform.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 3d96dbf79a72..03ccadee35d0 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -6606,7 +6606,7 @@ fail_alloc: return err; } -static int mlxplat_remove(struct platform_device *pdev) +static void mlxplat_remove(struct platform_device *pdev) { struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); @@ -6617,7 +6617,6 @@ static int mlxplat_remove(struct platform_device *pdev) mlxplat_pre_exit(priv); mlxplat_i2c_main_exit(priv); mlxplat_post_exit(); - return 0; } static const struct acpi_device_id mlxplat_acpi_table[] = { @@ -6633,7 +6632,7 @@ static struct platform_driver mlxplat_driver = { .probe_type = PROBE_FORCE_SYNCHRONOUS, }, .probe = mlxplat_probe, - .remove = mlxplat_remove, + .remove_new = mlxplat_remove, }; static int __init mlxplat_init(void) -- cgit From 9e6b44d43e31160bfba568244c785f439ac33d0e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:35 +0200 Subject: platform/x86: sel3350-platform: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-23-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/sel3350-platform.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/sel3350-platform.c b/drivers/platform/x86/sel3350-platform.c index fa267d0d3778..d09e976e7148 100644 --- a/drivers/platform/x86/sel3350-platform.c +++ b/drivers/platform/x86/sel3350-platform.c @@ -218,15 +218,13 @@ err_platform: return rs; } -static int sel3350_remove(struct platform_device *pdev) +static void sel3350_remove(struct platform_device *pdev) { struct sel3350_data *sel3350 = platform_get_drvdata(pdev); platform_device_unregister(sel3350->leds_pdev); gpiod_remove_lookup_table(&sel3350_gpios_table); gpiod_remove_lookup_table(&sel3350_leds_table); - - return 0; } static const struct acpi_device_id sel3350_device_ids[] = { @@ -237,7 +235,7 @@ MODULE_DEVICE_TABLE(acpi, sel3350_device_ids); static struct platform_driver sel3350_platform_driver = { .probe = sel3350_probe, - .remove = sel3350_remove, + .remove_new = sel3350_remove, .driver = { .name = "sel3350-platform", .acpi_match_table = sel3350_device_ids, -- cgit From 0580422560e2b93fe2e343d8438823824729a574 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:36 +0200 Subject: platform/x86/siemens: simatic-ipc-batt: Simplify simatic_ipc_batt_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of unconditionally returning zero, let simatic_ipc_batt_remove() return no value. This is a preparation to convert the drivers using this function to struct platform_driver::remove_new(). Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-24-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c | 4 +++- drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c | 4 +++- drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c | 4 +++- drivers/platform/x86/siemens/simatic-ipc-batt.c | 7 ++++--- drivers/platform/x86/siemens/simatic-ipc-batt.h | 4 ++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c index 8a67979d8f96..3e22a6bc9118 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c @@ -27,7 +27,9 @@ static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = { static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev) { - return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e); + simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e); + + return 0; } static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c index 607d033911a2..9e08c659f27d 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c @@ -27,7 +27,9 @@ static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = { static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev) { - return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a); + simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a); + + return 0; } static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c index a66107e0fe1e..19c302a68911 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c @@ -47,7 +47,9 @@ static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_59a = { static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev) { - return simatic_ipc_batt_remove(pdev, batt_lookup_table); + simatic_ipc_batt_remove(pdev, batt_lookup_table); + + return 0; } static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt.c b/drivers/platform/x86/siemens/simatic-ipc-batt.c index ef28c806b383..07cd4944de32 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt.c @@ -146,10 +146,9 @@ static const struct hwmon_chip_info simatic_ipc_batt_chip_info = { .info = simatic_ipc_batt_info, }; -int simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table) +void simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table) { gpiod_remove_lookup_table(table); - return 0; } EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove); @@ -230,7 +229,9 @@ EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe); static int simatic_ipc_batt_io_remove(struct platform_device *pdev) { - return simatic_ipc_batt_remove(pdev, NULL); + simatic_ipc_batt_remove(pdev, NULL); + + return 0; } static int simatic_ipc_batt_io_probe(struct platform_device *pdev) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt.h b/drivers/platform/x86/siemens/simatic-ipc-batt.h index 4545cd3e3026..89891db26a2c 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt.h +++ b/drivers/platform/x86/siemens/simatic-ipc-batt.h @@ -14,7 +14,7 @@ int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table); -int simatic_ipc_batt_remove(struct platform_device *pdev, - struct gpiod_lookup_table *table); +void simatic_ipc_batt_remove(struct platform_device *pdev, + struct gpiod_lookup_table *table); #endif /* _SIMATIC_IPC_BATT_H */ -- cgit From 2f46e417384e0412c5e239fe6a84ace7d955b33e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:37 +0200 Subject: platform/x86/siemens: simatic-ipc-batt: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-25-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/siemens/simatic-ipc-batt.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt.c b/drivers/platform/x86/siemens/simatic-ipc-batt.c index 07cd4944de32..c6dd263b4ee3 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt.c @@ -227,11 +227,9 @@ out: } EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe); -static int simatic_ipc_batt_io_remove(struct platform_device *pdev) +static void simatic_ipc_batt_io_remove(struct platform_device *pdev) { simatic_ipc_batt_remove(pdev, NULL); - - return 0; } static int simatic_ipc_batt_io_probe(struct platform_device *pdev) @@ -241,7 +239,7 @@ static int simatic_ipc_batt_io_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_io_probe, - .remove = simatic_ipc_batt_io_remove, + .remove_new = simatic_ipc_batt_io_remove, .driver = { .name = KBUILD_MODNAME, }, -- cgit From e139d97f049a8d924f91700a9a5f28c8bfe676d3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:38 +0200 Subject: platform/x86/siemens: simatic-ipc-batt-apollolake: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-26-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c index 3e22a6bc9118..31a139d87d9a 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c @@ -25,11 +25,9 @@ static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = { }, }; -static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev) +static void simatic_ipc_batt_apollolake_remove(struct platform_device *pdev) { simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e); - - return 0; } static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev) @@ -39,7 +37,7 @@ static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_apollolake_probe, - .remove = simatic_ipc_batt_apollolake_remove, + .remove_new = simatic_ipc_batt_apollolake_remove, .driver = { .name = KBUILD_MODNAME, }, -- cgit From 0d643dcd78423afad7204a280dc25dc67bcd85c0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:39 +0200 Subject: platform/x86/siemens: simatic-ipc-batt-elkhartlake: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-27-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c index 9e08c659f27d..a7676f224075 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c @@ -25,11 +25,9 @@ static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = { }, }; -static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev) +static void simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev) { simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a); - - return 0; } static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev) @@ -39,7 +37,7 @@ static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_elkhartlake_probe, - .remove = simatic_ipc_batt_elkhartlake_remove, + .remove_new = simatic_ipc_batt_elkhartlake_remove, .driver = { .name = KBUILD_MODNAME, }, -- cgit From 49d371ab9d2c850867332b469fa6b3a2d5cf338c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 27 Sep 2023 10:10:40 +0200 Subject: platform/x86/siemens: simatic-ipc-batt-f7188x: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230927081040.2198742-28-u.kleine-koenig@pengutronix.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c index 19c302a68911..5e77e05fdb5d 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c @@ -45,11 +45,9 @@ static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_59a = { } }; -static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev) +static void simatic_ipc_batt_f7188x_remove(struct platform_device *pdev) { simatic_ipc_batt_remove(pdev, batt_lookup_table); - - return 0; } static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev) @@ -75,7 +73,7 @@ static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_f7188x_probe, - .remove = simatic_ipc_batt_f7188x_remove, + .remove_new = simatic_ipc_batt_f7188x_remove, .driver = { .name = KBUILD_MODNAME, }, -- cgit From da4082841ccf022beae73e63d3f476f59777172b Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 09:32:31 -0700 Subject: platform/x86: ISST: Use fuse enabled mask instead of allowed levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allowed level mask is a mask of levels, which are currently allowed to dynamically switch by the OS. Fused mask is a mask of all levels even if OS is not allowed to switch. Even if OS is not allowed to dynamically switch, it is still possible for user to boot to a level by using BIOS option. To decide which level to boot next time, user wants to check parameters (power, performance or thermal) of that level to decide. So, when passing the level mask for display to user space, use fuse enabled mask, which has all levels. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20231003163234.1856669-2-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 63faa2ea8327..a672a1c814af 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -704,7 +704,7 @@ static int isst_if_get_perf_level(void __user *argp) return -EINVAL; perf_level.max_level = power_domain_info->max_level; - perf_level.level_mask = power_domain_info->pp_header.allowed_level_mask; + perf_level.level_mask = power_domain_info->pp_header.level_en_mask; perf_level.feature_rev = power_domain_info->pp_header.feature_rev; _read_pp_info("current_level", perf_level.current_level, SST_PP_STATUS_OFFSET, SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) -- cgit From a22d36eb5b150913325640cb793e13e08d1bd715 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 09:32:32 -0700 Subject: platform/x86: ISST: Allow level 0 to be not present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible that SST level 0 or base level is not present in some configurations. So don't set level 0 mask in level_en_mask by default. Signed-off-by: Srinivas Pandruvada Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231003163234.1856669-3-srinivas.pandruvada@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index a672a1c814af..ac5c6a812592 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -364,9 +364,6 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai /* Read PP header */ *((u64 *)&pd_info->pp_header) = readq(pd_info->sst_base + pd_info->sst_header.pp_offset); - /* Force level_en_mask level 0 */ - pd_info->pp_header.level_en_mask |= 0x01; - mask = 0x01; levels = 0; for (i = 0; i < 8; ++i) { -- cgit From 7525cea3ef9384054a30f25ebb501234befecdcb Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 09:32:33 -0700 Subject: platform/x86: intel_speed_select_if: Remove hardcoded map size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver is using 256 as the size while calling devm_ioremap(). The maximum offset can be obtained from isst_mmio_range. Add a field "size" to the isst_mmio_range and use it instead of hardcoding. No functional impact is expected. Signed-off-by: Srinivas Pandruvada Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231003163234.1856669-4-srinivas.pandruvada@linux.intel.com Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/speed_select_if/isst_if_mmio.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c index ff49025ec085..13e068c77d50 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c @@ -18,16 +18,17 @@ struct isst_mmio_range { int beg; int end; + int size; }; static struct isst_mmio_range mmio_range_devid_0[] = { - {0x04, 0x14}, - {0x20, 0xD0}, + {0x04, 0x14, 0x18}, + {0x20, 0xD0, 0xD4}, }; static struct isst_mmio_range mmio_range_devid_1[] = { - {0x04, 0x14}, - {0x20, 0x11C}, + {0x04, 0x14, 0x18}, + {0x20, 0x11C, 0x120}, }; struct isst_if_device { @@ -114,13 +115,16 @@ static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pcu_base &= GENMASK(10, 0); base_addr = (u64)mmio_base << 23 | (u64) pcu_base << 12; - punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, 256); + + punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data; + + punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, + punit_dev->mmio_range[1].size); if (!punit_dev->punit_mmio) return -ENOMEM; mutex_init(&punit_dev->mutex); pci_set_drvdata(pdev, punit_dev); - punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data; memset(&cb, 0, sizeof(cb)); cb.cmd_size = sizeof(struct isst_if_io_reg); -- cgit From 23f392ea6d1916f68be8067e2a038ef9a746a94b Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 09:32:34 -0700 Subject: platform/x86: intel_speed_select_if: Use devm_ioremap_resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace devm_ioremap() with devm_ioremap_resource() by defining a resource. Signed-off-by: Srinivas Pandruvada Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231003163234.1856669-5-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c index 13e068c77d50..3f4343147dad 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c @@ -94,6 +94,7 @@ static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct isst_if_device *punit_dev; struct isst_if_cmd_cb cb; u32 mmio_base, pcu_base; + struct resource r; u64 base_addr; int ret; @@ -118,10 +119,10 @@ static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent) punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data; - punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, - punit_dev->mmio_range[1].size); - if (!punit_dev->punit_mmio) - return -ENOMEM; + r = DEFINE_RES_MEM(base_addr, punit_dev->mmio_range[1].size); + punit_dev->punit_mmio = devm_ioremap_resource(&pdev->dev, &r); + if (IS_ERR(punit_dev->punit_mmio)) + return PTR_ERR(punit_dev->punit_mmio); mutex_init(&punit_dev->mutex); pci_set_drvdata(pdev, punit_dev); -- cgit From 8874e414fe78718d0f2861fe511cecbd1cd73f4d Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 11:49:14 -0700 Subject: platform/x86/intel/tpmi: Add defines to get version information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add defines to get major and minor version from a TPMI version field value. This will avoid code duplication to convert in every feature driver. Also add define for invalid version field. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20231003184916.1860084-2-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/linux/intel_tpmi.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index 04d937ad4dc4..ee07393445f9 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -6,6 +6,12 @@ #ifndef _INTEL_TPMI_H_ #define _INTEL_TPMI_H_ +#include + +#define TPMI_VERSION_INVALID 0xff +#define TPMI_MINOR_VERSION(val) FIELD_GET(GENMASK(4, 0), val) +#define TPMI_MAJOR_VERSION(val) FIELD_GET(GENMASK(7, 5), val) + /** * struct intel_tpmi_plat_info - Platform information for a TPMI device instance * @package_id: CPU Package id -- cgit From 07510a59b316445311775ee53adf10448140607a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 11:49:15 -0700 Subject: platform/x86: ISST: Ignore minor version change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware definition of every TPMI feature contains a major and minor version. When there is a change in the MMIO offset or change in the definition of a field, hardware will change major version. For addition of new fields without modifying existing MMIO offsets or fields, only the minor version is changed. Driver is developed to support SST functionality for a major and minor version. If the hardware changes major version, since offsets and definitions are changed, driver cannot continue to provide SST interface to users. Driver can still function with a minor version change as it will just miss the new functionality added by the hardware. The current implementation doesn't ignore any version change. If there is mismatch with the minor version, continue with an information log message. If there is mismatch with the major version, log error and exit. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20231003184916.1860084-3-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/speed_select_if/isst_tpmi_core.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index ac5c6a812592..0b6d2c864437 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -30,7 +30,8 @@ #include "isst_if_common.h" /* Supported SST hardware version by this driver */ -#define ISST_HEADER_VERSION 1 +#define ISST_MAJOR_VERSION 0 +#define ISST_MINOR_VERSION 1 /* * Used to indicate if value read from MMIO needs to get multiplied @@ -352,12 +353,19 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai pd_info->sst_header.cp_offset *= 8; pd_info->sst_header.pp_offset *= 8; - if (pd_info->sst_header.interface_version != ISST_HEADER_VERSION) { - dev_err(&auxdev->dev, "SST: Unsupported version:%x\n", - pd_info->sst_header.interface_version); + if (pd_info->sst_header.interface_version == TPMI_VERSION_INVALID) + return -ENODEV; + + if (TPMI_MAJOR_VERSION(pd_info->sst_header.interface_version) != ISST_MAJOR_VERSION) { + dev_err(&auxdev->dev, "SST: Unsupported major version:%lx\n", + TPMI_MAJOR_VERSION(pd_info->sst_header.interface_version)); return -ENODEV; } + if (TPMI_MINOR_VERSION(pd_info->sst_header.interface_version) != ISST_MINOR_VERSION) + dev_info(&auxdev->dev, "SST: Ignore: Unsupported minor version:%lx\n", + TPMI_MINOR_VERSION(pd_info->sst_header.interface_version)); + /* Read SST CP Header */ *((u64 *)&pd_info->cp_header) = readq(pd_info->sst_base + pd_info->sst_header.cp_offset); -- cgit From 4b0d9c8f8f55562ed5346723e66eec64f22887f4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 11:49:16 -0700 Subject: platform/x86/intel-uncore-freq: Ignore minor version change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware definition of every TPMI feature contains a major and minor version. When there is a change in the MMIO offset or change in the definition of a field, hardware will change major version. For addition of new fields without modifying existing MMIO offsets or fields, only the minor version is changed. Driver is developed to support uncore frequency control (UFS) for a major and minor version. If the hardware changes major version, since offsets and definitions are changed, driver cannot continue to provide UFS interface to users. Driver can still function with minor version change as it will just miss the new functionality added by the hardware. The current implementation logs information message and skips adding uncore sysfs entry for a resource for any version mismatch. Check major and minor version mismatch for every valid resource and fail on any major version mismatch by logging an error message. A valid resource has a version which is not 0xFF. If there is mismatch with the minor version, continue with a log message. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20231003184916.1860084-4-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/uncore-frequency/uncore-frequency-tpmi.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 7d0a67f8b517..4fb790552c47 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -28,7 +28,8 @@ #include "uncore-frequency-common.h" -#define UNCORE_HEADER_VERSION 1 +#define UNCORE_MAJOR_VERSION 0 +#define UNCORE_MINOR_VERSION 1 #define UNCORE_HEADER_INDEX 0 #define UNCORE_FABRIC_CLUSTER_OFFSET 8 @@ -302,12 +303,21 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ /* Check for version and skip this resource if there is mismatch */ header = readq(pd_info->uncore_base); pd_info->ufs_header_ver = header & UNCORE_VERSION_MASK; - if (pd_info->ufs_header_ver != UNCORE_HEADER_VERSION) { - dev_info(&auxdev->dev, "Uncore: Unsupported version:%d\n", - pd_info->ufs_header_ver); + + if (pd_info->ufs_header_ver == TPMI_VERSION_INVALID) continue; + + if (TPMI_MAJOR_VERSION(pd_info->ufs_header_ver) != UNCORE_MAJOR_VERSION) { + dev_err(&auxdev->dev, "Uncore: Unsupported major version:%lx\n", + TPMI_MAJOR_VERSION(pd_info->ufs_header_ver)); + ret = -ENODEV; + goto remove_clusters; } + if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) != UNCORE_MINOR_VERSION) + dev_info(&auxdev->dev, "Uncore: Ignore: Unsupported minor version:%lx\n", + TPMI_MINOR_VERSION(pd_info->ufs_header_ver)); + /* Get Cluster ID Mask */ cluster_mask = FIELD_GET(UNCORE_LOCAL_FABRIC_CLUSTER_ID_MASK, header); if (!cluster_mask) { -- cgit From 8df012a7f513141412b3c35af204ccdb810fcc81 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 25 Sep 2023 12:42:19 -0700 Subject: platform/x86/intel/tpmi: Add debugfs support for read/write blocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display read and write blocked status of each TPMI feature in addition to disabled and locked status. This will require reading of read/write blocked state from the hardware. Currently tpmi_read_feature_status(), doesn't provide this state. Define TPMI feature state as defined in the TPMI spec. Modify the function tpmi_read_feature_status() to update full feature state instead of just disabled and locked state. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230925194219.966602-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/tpmi.c | 81 +++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 0a95736d97e4..311abcac894a 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -143,6 +143,33 @@ struct tpmi_info_header { u64 lock:1; } __packed; +/** + * struct tpmi_feature_state - Structure to read hardware state of a feature + * @enabled: Enable state of a feature, 1: enabled, 0: disabled + * @reserved_1: Reserved for future use + * @write_blocked: Writes are blocked means all write operations are ignored + * @read_blocked: Reads are blocked means will read 0xFFs + * @pcs_select: Interface used by out of band software, not used in OS + * @reserved_2: Reserved for future use + * @id: TPMI ID of the feature + * @reserved_3: Reserved for future use + * @locked: When set to 1, OS can't change this register. + * + * The structure is used to read hardware state of a TPMI feature. This + * information is used for debug and restricting operations for this feature. + */ +struct tpmi_feature_state { + u32 enabled:1; + u32 reserved_1:3; + u32 write_blocked:1; + u32 read_blocked:1; + u32 pcs_select:1; + u32 reserved_2:1; + u32 id:8; + u32 reserved_3:15; + u32 locked:1; +} __packed; + /* * List of supported TMPI IDs. * Some TMPI IDs are not used by Linux, so the numbers are not consecutive. @@ -202,6 +229,7 @@ EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI); #define TPMI_CONTROL_STATUS_OFFSET 0x00 #define TPMI_COMMAND_OFFSET 0x08 +#define TMPI_CONTROL_DATA_VAL_OFFSET 0x0c /* * Spec is calling for max 1 seconds to get ownership at the worst @@ -230,7 +258,6 @@ EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI); /* TPMI command data registers */ #define TMPI_CONTROL_DATA_CMD GENMASK_ULL(7, 0) -#define TMPI_CONTROL_DATA_VAL GENMASK_ULL(63, 32) #define TPMI_CONTROL_DATA_VAL_FEATURE GENMASK_ULL(48, 40) /* Command to send via control interface */ @@ -240,9 +267,6 @@ EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI); #define TPMI_CMD_LEN_MASK GENMASK_ULL(18, 16) -#define TPMI_STATE_DISABLED BIT_ULL(0) -#define TPMI_STATE_LOCKED BIT_ULL(31) - /* Mutex to complete get feature status without interruption */ static DEFINE_MUTEX(tpmi_dev_lock); @@ -256,7 +280,7 @@ static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner) } static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id, - int *locked, int *disabled) + struct tpmi_feature_state *feature_state) { u64 control, data; int ret; @@ -306,17 +330,8 @@ static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int featu } /* Response is ready */ - data = readq(tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET); - data = FIELD_GET(TMPI_CONTROL_DATA_VAL, data); - - *disabled = 0; - *locked = 0; - - if (!(data & TPMI_STATE_DISABLED)) - *disabled = 1; - - if (data & TPMI_STATE_LOCKED) - *locked = 1; + memcpy_fromio(feature_state, tpmi_info->tpmi_control_mem + TMPI_CONTROL_DATA_VAL_OFFSET, + sizeof(*feature_state)); ret = 0; @@ -335,34 +350,50 @@ int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, { struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent); struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev); + struct tpmi_feature_state feature_state; + int ret; + + ret = tpmi_read_feature_status(tpmi_info, feature_id, &feature_state); + if (ret) + return ret; - return tpmi_read_feature_status(tpmi_info, feature_id, locked, disabled); + *locked = feature_state.locked; + *disabled = !feature_state.enabled; + + return 0; } EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI); static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) { struct intel_tpmi_info *tpmi_info = s->private; + int locked, disabled, read_blocked, write_blocked; + struct tpmi_feature_state feature_state; struct intel_tpmi_pm_feature *pfs; - int locked, disabled, ret, i; + int ret, i; + seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start); - seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\n"); + seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\tread_blocked\twrite_blocked\n"); for (i = 0; i < tpmi_info->feature_count; ++i) { pfs = &tpmi_info->tpmi_features[i]; - ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &locked, - &disabled); + ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state); if (ret) { locked = 'U'; disabled = 'U'; + read_blocked = 'U'; + write_blocked = 'U'; } else { - disabled = disabled ? 'Y' : 'N'; - locked = locked ? 'Y' : 'N'; + disabled = feature_state.enabled ? 'N' : 'Y'; + locked = feature_state.locked ? 'Y' : 'N'; + read_blocked = feature_state.read_blocked ? 'Y' : 'N'; + write_blocked = feature_state.write_blocked ? 'Y' : 'N'; } - seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\n", + seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\t\t%c\t\t%c\n", pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries, pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset, - pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled); + pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled, + read_blocked, write_blocked); } return 0; -- cgit From cdb1013808bed0c0c392b44ed4eed6c7743028c3 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Sun, 10 Sep 2023 19:50:33 +0530 Subject: platform/x86/amd/pmc: Add PMFW command id to support S2D force flush MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent PMFW have the capability that can force flush the FIFO contents to DRAM on sending a command id via the mailbox. Add this support to the driver. Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20230910142034.2967110-1-Shyam-sundar.S-k@amd.com [ij: Corrected whitespace in dev_dbg_once()] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index c1e788b67a74..922932a71233 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -55,6 +55,9 @@ #define S2D_TELEMETRY_BYTES_MAX 0x100000 #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 +/* STB Spill to DRAM Message Definition */ +#define STB_FORCE_FLUSH_DATA 0xCF + /* Base address of SMU for mapping physical address to virtual address */ #define AMD_PMC_MAPPING_SIZE 0x01000 #define AMD_PMC_BASE_ADDR_OFFSET 0x10000 @@ -236,7 +239,7 @@ static const struct file_operations amd_pmc_stb_debugfs_fops = { static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) { struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 *buf, fsize, num_samples, stb_rdptr_offset = 0; + u32 *buf, fsize, num_samples, val, stb_rdptr_offset = 0; int ret; /* Write dummy postcode while reading the STB buffer */ @@ -251,6 +254,10 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) /* Spill to DRAM num_samples uses separate SMU message port */ dev->msg_port = 1; + ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); + if (ret) + dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); + /* Get the num_samples to calculate the last push location */ ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true); /* Clear msg_port for other SMU operation */ -- cgit From 97a5e801b3045c1e800f76bc0fb544972538089d Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:29 -0700 Subject: platform/x86/intel/ifs: Store IFS generation number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IFS generation number is reported via MSR_INTEGRITY_CAPS. As IFS support gets added to newer CPUs, some differences are expected during IFS image loading and test flows. Define MSR bitmasks to extract and store the generation in driver data, so that driver can modify its MSR interaction appropriately. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-2-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- arch/x86/include/asm/msr-index.h | 1 + drivers/platform/x86/intel/ifs/core.c | 3 +++ drivers/platform/x86/intel/ifs/ifs.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 1d111350197f..838e5a013a07 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -222,6 +222,7 @@ #define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT) #define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4 #define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT) +#define MSR_INTEGRITY_CAPS_SAF_GEN_MASK GENMASK_ULL(10, 9) #define MSR_LBR_NHM_FROM 0x00000680 #define MSR_LBR_NHM_TO 0x000006c0 diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 306f886b52d2..4ff2aa4b484b 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2022 Intel Corporation. */ +#include #include #include #include @@ -94,6 +95,8 @@ static int __init ifs_init(void) for (i = 0; i < IFS_NUMTESTS; i++) { if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit))) continue; + ifs_devices[i].rw_data.generation = FIELD_GET(MSR_INTEGRITY_CAPS_SAF_GEN_MASK, + msrval); ret = misc_register(&ifs_devices[i].misc); if (ret) goto err_exit; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 93191855890f..d666aeed20fc 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -229,6 +229,7 @@ struct ifs_test_caps { * @status: it holds simple status pass/fail/untested * @scan_details: opaque scan status code from h/w * @cur_batch: number indicating the currently loaded test file + * @generation: IFS test generation enumerated by hardware */ struct ifs_data { int loaded_version; @@ -238,6 +239,7 @@ struct ifs_data { int status; u64 scan_details; u32 cur_batch; + u32 generation; }; struct ifs_work { -- cgit From a138ac2656d1329c3994a227769b7ba3926818a7 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:30 -0700 Subject: platform/x86/intel/ifs: Refactor image loading code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IFS image loading flow is slightly different for newer IFS generations. In preparation for adding support for newer IFS generations, refactor portions of existing image loading code for reuse. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-3-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/load.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index cefd0d886cfd..851c97cc6a6b 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -80,6 +80,23 @@ static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_typ return NULL; } +static void hashcopy_err_message(struct device *dev, u32 err_code) +{ + if (err_code >= ARRAY_SIZE(scan_hash_status)) + dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); + else + dev_err(dev, "Hash copy error : %s\n", scan_hash_status[err_code]); +} + +static void auth_err_message(struct device *dev, u32 err_code) +{ + if (err_code >= ARRAY_SIZE(scan_authentication_status)) + dev_err(dev, "invalid error code 0x%x for authentication\n", err_code); + else + dev_err(dev, "Chunk authentication error : %s\n", + scan_authentication_status[err_code]); +} + /* * To copy scan hashes and authenticate test chunks, the initiating cpu must point * to the EDX:EAX to the test image in linear address. @@ -109,11 +126,7 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work) if (!hashes_status.valid) { ifsd->loading_error = true; - if (err_code >= ARRAY_SIZE(scan_hash_status)) { - dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); - goto done; - } - dev_err(dev, "Hash copy error : %s", scan_hash_status[err_code]); + hashcopy_err_message(dev, err_code); goto done; } @@ -133,13 +146,7 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work) if (err_code) { ifsd->loading_error = true; - if (err_code >= ARRAY_SIZE(scan_authentication_status)) { - dev_err(dev, - "invalid error code 0x%x for authentication\n", err_code); - goto done; - } - dev_err(dev, "Chunk authentication error %s\n", - scan_authentication_status[err_code]); + auth_err_message(dev, err_code); goto done; } } -- cgit From 07f47c01b3bc2a42c4d4da35831edab10aa60449 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:31 -0700 Subject: platform/x86/intel/ifs: Gen2 scan image loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scan image loading flow for newer IFS generations are slightly different from that of current generation. In newer schemes, loading need not be done once for each socket as was done in gen0. Also the width of NUM_CHUNKS bitfield in SCAN_HASHES_STATUS MSR has increased from 8 -> 16 bits. Similarly there are width differences for CHUNK_AUTHENTICATION_STATUS too. Further the parameter to AUTHENTICATE_AND_COPY_CHUNK is passed differently in newer generations. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-4-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/ifs.h | 27 ++++++++ drivers/platform/x86/intel/ifs/load.c | 112 +++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index d666aeed20fc..4824316b3acd 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -137,6 +137,8 @@ #define MSR_CHUNKS_AUTHENTICATION_STATUS 0x000002c5 #define MSR_ACTIVATE_SCAN 0x000002c6 #define MSR_SCAN_STATUS 0x000002c7 +#define MSR_SAF_CTRL 0x000004f0 + #define SCAN_NOT_TESTED 0 #define SCAN_TEST_PASS 1 #define SCAN_TEST_FAIL 2 @@ -158,6 +160,19 @@ union ifs_scan_hashes_status { }; }; +union ifs_scan_hashes_status_gen2 { + u64 data; + struct { + u16 chunk_size; + u16 num_chunks; + u32 error_code :8; + u32 chunks_in_stride :9; + u32 rsvd :2; + u32 max_core_limit :12; + u32 valid :1; + }; +}; + /* MSR_CHUNKS_AUTH_STATUS bit fields */ union ifs_chunks_auth_status { u64 data; @@ -170,6 +185,16 @@ union ifs_chunks_auth_status { }; }; +union ifs_chunks_auth_status_gen2 { + u64 data; + struct { + u16 valid_chunks; + u16 total_chunks; + u32 error_code :8; + u32 rsvd2 :24; + }; +}; + /* MSR_ACTIVATE_SCAN bit fields */ union ifs_scan { u64 data; @@ -230,6 +255,7 @@ struct ifs_test_caps { * @scan_details: opaque scan status code from h/w * @cur_batch: number indicating the currently loaded test file * @generation: IFS test generation enumerated by hardware + * @chunk_size: size of a test chunk */ struct ifs_data { int loaded_version; @@ -240,6 +266,7 @@ struct ifs_data { u64 scan_details; u32 cur_batch; u32 generation; + u32 chunk_size; }; struct ifs_work { diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index 851c97cc6a6b..6b827247945b 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -2,6 +2,7 @@ /* Copyright(c) 2022 Intel Corporation. */ #include +#include #include #include @@ -26,6 +27,11 @@ union meta_data { #define IFS_HEADER_SIZE (sizeof(struct microcode_header_intel)) #define META_TYPE_IFS 1 +#define INVALIDATE_STRIDE 0x1UL +#define IFS_GEN_STRIDE_AWARE 2 +#define AUTH_INTERRUPTED_ERROR 5 +#define IFS_AUTH_RETRY_CT 10 + static struct microcode_header_intel *ifs_header_ptr; /* pointer to the ifs image header */ static u64 ifs_hash_ptr; /* Address of ifs metadata (hash) */ static u64 ifs_test_image_ptr; /* 256B aligned address of test pattern */ @@ -44,7 +50,10 @@ static const char * const scan_hash_status[] = { static const char * const scan_authentication_status[] = { [0] = "No error reported", [1] = "Attempt to authenticate a chunk which is already marked as authentic", - [2] = "Chunk authentication error. The hash of chunk did not match expected value" + [2] = "Chunk authentication error. The hash of chunk did not match expected value", + [3] = "Reserved", + [4] = "Chunk outside the current stride", + [5] = "Authentication flow interrupted", }; #define MC_HEADER_META_TYPE_END (0) @@ -154,6 +163,102 @@ done: complete(&ifs_done); } +static int get_num_chunks(int gen, union ifs_scan_hashes_status_gen2 status) +{ + return gen >= IFS_GEN_STRIDE_AWARE ? status.chunks_in_stride : status.num_chunks; +} + +static bool need_copy_scan_hashes(struct ifs_data *ifsd) +{ + return !ifsd->loaded || + ifsd->generation < IFS_GEN_STRIDE_AWARE || + ifsd->loaded_version != ifs_header_ptr->rev; +} + +static int copy_hashes_authenticate_chunks_gen2(struct device *dev) +{ + union ifs_scan_hashes_status_gen2 hashes_status; + union ifs_chunks_auth_status_gen2 chunk_status; + u32 err_code, valid_chunks, total_chunks; + int i, num_chunks, chunk_size; + union meta_data *ifs_meta; + int starting_chunk_nr; + struct ifs_data *ifsd; + u64 linear_addr, base; + u64 chunk_table[2]; + int retry_count; + + ifsd = ifs_get_data(dev); + + if (need_copy_scan_hashes(ifsd)) { + wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); + rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); + + /* enumerate the scan image information */ + chunk_size = hashes_status.chunk_size * SZ_1K; + err_code = hashes_status.error_code; + + num_chunks = get_num_chunks(ifsd->generation, hashes_status); + + if (!hashes_status.valid) { + hashcopy_err_message(dev, err_code); + return -EIO; + } + ifsd->loaded_version = ifs_header_ptr->rev; + ifsd->chunk_size = chunk_size; + } else { + num_chunks = ifsd->valid_chunks; + chunk_size = ifsd->chunk_size; + } + + if (ifsd->generation >= IFS_GEN_STRIDE_AWARE) { + wrmsrl(MSR_SAF_CTRL, INVALIDATE_STRIDE); + rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); + if (chunk_status.valid_chunks != 0) { + dev_err(dev, "Couldn't invalidate installed stride - %d\n", + chunk_status.valid_chunks); + return -EIO; + } + } + + base = ifs_test_image_ptr; + ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS); + starting_chunk_nr = ifs_meta->starting_chunk; + + /* scan data authentication and copy chunks to secured memory */ + for (i = 0; i < num_chunks; i++) { + retry_count = IFS_AUTH_RETRY_CT; + linear_addr = base + i * chunk_size; + + chunk_table[0] = starting_chunk_nr + i; + chunk_table[1] = linear_addr; + do { + wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table); + rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); + err_code = chunk_status.error_code; + } while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count); + + if (err_code) { + ifsd->loading_error = true; + auth_err_message(dev, err_code); + return -EIO; + } + } + + valid_chunks = chunk_status.valid_chunks; + total_chunks = chunk_status.total_chunks; + + if (valid_chunks != total_chunks) { + ifsd->loading_error = true; + dev_err(dev, "Couldn't authenticate all the chunks. Authenticated %d total %d.\n", + valid_chunks, total_chunks); + return -EIO; + } + ifsd->valid_chunks = valid_chunks; + + return 0; +} + static int validate_ifs_metadata(struct device *dev) { struct ifs_data *ifsd = ifs_get_data(dev); @@ -206,7 +311,9 @@ static int scan_chunks_sanity_check(struct device *dev) return ret; ifsd->loading_error = false; - ifsd->loaded_version = ifs_header_ptr->rev; + + if (ifsd->generation > 0) + return copy_hashes_authenticate_chunks_gen2(dev); /* copy the scan hash and authenticate per package */ cpus_read_lock(); @@ -226,6 +333,7 @@ static int scan_chunks_sanity_check(struct device *dev) ifs_pkg_auth[curr_pkg] = 1; } ret = 0; + ifsd->loaded_version = ifs_header_ptr->rev; out: cpus_read_unlock(); -- cgit From 72b96ee29ed6f7670bbb180ba694816e33d361d1 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:32 -0700 Subject: platform/x86/intel/ifs: Gen2 Scan test support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Width of chunk related bitfields is ACTIVATE_SCAN and SCAN_STATUS MSRs are different in newer IFS generation compared to gen0. Make changes to scan test flow such that MSRs are populated appropriately based on the generation supported by hardware. Account for the 8/16 bit MSR bitfield width differences between gen0 and newer generations for the scan test trace event too. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-5-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/ifs.h | 28 ++++++++++++++++++++++------ drivers/platform/x86/intel/ifs/runtest.c | 29 ++++++++++++++++++++++------- include/trace/events/intel_ifs.h | 16 ++++++++-------- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 4824316b3acd..f0dd849b3400 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -199,9 +199,17 @@ union ifs_chunks_auth_status_gen2 { union ifs_scan { u64 data; struct { - u32 start :8; - u32 stop :8; - u32 rsvd :16; + union { + struct { + u8 start; + u8 stop; + u16 rsvd; + } gen0; + struct { + u16 start; + u16 stop; + } gen2; + }; u32 delay :31; u32 sigmce :1; }; @@ -211,9 +219,17 @@ union ifs_scan { union ifs_status { u64 data; struct { - u32 chunk_num :8; - u32 chunk_stop_index :8; - u32 rsvd1 :16; + union { + struct { + u8 chunk_num; + u8 chunk_stop_index; + u16 rsvd1; + } gen0; + struct { + u16 chunk_num; + u16 chunk_stop_index; + } gen2; + }; u32 error_code :8; u32 rsvd2 :22; u32 control_error :1; diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 1061eb7ec399..925c30c79011 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -171,21 +171,31 @@ static void ifs_test_core(int cpu, struct device *dev) union ifs_status status; unsigned long timeout; struct ifs_data *ifsd; + int to_start, to_stop; + int status_chunk; u64 msrvals[2]; int retries; ifsd = ifs_get_data(dev); - activate.rsvd = 0; + activate.gen0.rsvd = 0; activate.delay = IFS_THREAD_WAIT; activate.sigmce = 0; - activate.start = 0; - activate.stop = ifsd->valid_chunks - 1; + to_start = 0; + to_stop = ifsd->valid_chunks - 1; + + if (ifsd->generation) { + activate.gen2.start = to_start; + activate.gen2.stop = to_stop; + } else { + activate.gen0.start = to_start; + activate.gen0.stop = to_stop; + } timeout = jiffies + HZ / 2; retries = MAX_IFS_RETRIES; - while (activate.start <= activate.stop) { + while (to_start <= to_stop) { if (time_after(jiffies, timeout)) { status.error_code = IFS_SW_TIMEOUT; break; @@ -196,13 +206,14 @@ static void ifs_test_core(int cpu, struct device *dev) status.data = msrvals[1]; - trace_ifs_status(cpu, activate, status); + trace_ifs_status(cpu, to_start, to_stop, status.data); /* Some cases can be retried, give up for others */ if (!can_restart(status)) break; - if (status.chunk_num == activate.start) { + status_chunk = ifsd->generation ? status.gen2.chunk_num : status.gen0.chunk_num; + if (status_chunk == to_start) { /* Check for forward progress */ if (--retries == 0) { if (status.error_code == IFS_NO_ERROR) @@ -211,7 +222,11 @@ static void ifs_test_core(int cpu, struct device *dev) } } else { retries = MAX_IFS_RETRIES; - activate.start = status.chunk_num; + if (ifsd->generation) + activate.gen2.start = status_chunk; + else + activate.gen0.start = status_chunk; + to_start = status_chunk; } } diff --git a/include/trace/events/intel_ifs.h b/include/trace/events/intel_ifs.h index d7353024016c..af0af3f1d9b7 100644 --- a/include/trace/events/intel_ifs.h +++ b/include/trace/events/intel_ifs.h @@ -10,25 +10,25 @@ TRACE_EVENT(ifs_status, - TP_PROTO(int cpu, union ifs_scan activate, union ifs_status status), + TP_PROTO(int cpu, int start, int stop, u64 status), - TP_ARGS(cpu, activate, status), + TP_ARGS(cpu, start, stop, status), TP_STRUCT__entry( __field( u64, status ) __field( int, cpu ) - __field( u8, start ) - __field( u8, stop ) + __field( u16, start ) + __field( u16, stop ) ), TP_fast_assign( __entry->cpu = cpu; - __entry->start = activate.start; - __entry->stop = activate.stop; - __entry->status = status.data; + __entry->start = start; + __entry->stop = stop; + __entry->status = status; ), - TP_printk("cpu: %d, start: %.2x, stop: %.2x, status: %llx", + TP_printk("cpu: %d, start: %.4x, stop: %.4x, status: %.16llx", __entry->cpu, __entry->start, __entry->stop, -- cgit From 25a76dbb36dd58ad4df7f6a4dc43061a10b0d817 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:33 -0700 Subject: platform/x86/intel/ifs: Validate image size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perform additional validation prior to loading IFS image. Error out if the size of the file being loaded doesn't match the size specified in the header. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-6-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/load.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index 6b827247945b..582f1801aaaa 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -375,6 +375,7 @@ int ifs_load_firmware(struct device *dev) { const struct ifs_test_caps *test = ifs_get_test_caps(dev); struct ifs_data *ifsd = ifs_get_data(dev); + unsigned int expected_size; const struct firmware *fw; char scan_path[64]; int ret = -EINVAL; @@ -389,6 +390,13 @@ int ifs_load_firmware(struct device *dev) goto done; } + expected_size = ((struct microcode_header_intel *)fw->data)->totalsize; + if (fw->size != expected_size) { + dev_err(dev, "File size mismatch (expected %u, actual %zu). Corrupted IFS image.\n", + expected_size, fw->size); + return -EINVAL; + } + ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data); if (ret) goto release; -- cgit From 60d2e1b37d530d6b1f8b7773cebaf8bbc1536b28 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:34 -0700 Subject: platform/x86/intel/ifs: Metadata validation for start_chunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an additional check to validate IFS image metadata field prior to loading the test image. If start_chunk is not a multiple of chunks_per_stride error out. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-7-jithu.joseph@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/load.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index 582f1801aaaa..959b1878cae6 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -291,6 +291,13 @@ static int validate_ifs_metadata(struct device *dev) return ret; } + if (ifs_meta->chunks_per_stride && + (ifs_meta->starting_chunk % ifs_meta->chunks_per_stride != 0)) { + dev_warn(dev, "Starting chunk num %u not a multiple of chunks_per_stride %u\n", + ifs_meta->starting_chunk, ifs_meta->chunks_per_stride); + return ret; + } + return 0; } -- cgit From e6483a0b59026ded36a6f5eba1425a6b0965984a Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:35 -0700 Subject: platform/x86/intel/ifs: Add new CPU support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Granite Rapids(GNR) and Sierra Forest(SRF) cpuids to x86 match table so that IFS driver can be loaded for those. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-8-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 4ff2aa4b484b..0c8927916373 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -18,6 +18,9 @@ static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { X86_MATCH(SAPPHIRERAPIDS_X), X86_MATCH(EMERALDRAPIDS_X), + X86_MATCH(GRANITERAPIDS_X), + X86_MATCH(GRANITERAPIDS_D), + X86_MATCH(ATOM_CRESTMONT_X), {} }; MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); -- cgit From b9aa9e4c8b4e52b6f2f5986b27e97f4b6163f0bf Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:36 -0700 Subject: platform/x86/intel/ifs: Add new error code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make driver aware of a newly added error code so that it can provide a more appropriate error message. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Ilpo Järvinen Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-9-jithu.joseph@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/runtest.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 925c30c79011..4fe544d79946 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -40,6 +40,8 @@ enum ifs_status_err_code { IFS_UNASSIGNED_ERROR_CODE = 7, IFS_EXCEED_NUMBER_OF_THREADS_CONCURRENT = 8, IFS_INTERRUPTED_DURING_EXECUTION = 9, + IFS_UNASSIGNED_ERROR_CODE_0xA = 0xA, + IFS_CORRUPTED_CHUNK = 0xB, }; static const char * const scan_test_status[] = { @@ -55,6 +57,8 @@ static const char * const scan_test_status[] = { [IFS_EXCEED_NUMBER_OF_THREADS_CONCURRENT] = "Exceeded number of Logical Processors (LP) allowed to run Scan-At-Field concurrently", [IFS_INTERRUPTED_DURING_EXECUTION] = "Interrupt occurred prior to SCAN start", + [IFS_UNASSIGNED_ERROR_CODE_0xA] = "Unassigned error code 0xA", + [IFS_CORRUPTED_CHUNK] = "Scan operation aborted due to corrupted image. Try reloading", }; static void message_not_tested(struct device *dev, int cpu, union ifs_status status) @@ -123,6 +127,8 @@ static bool can_restart(union ifs_status status) case IFS_MISMATCH_ARGUMENTS_BETWEEN_THREADS: case IFS_CORE_NOT_CAPABLE_CURRENTLY: case IFS_UNASSIGNED_ERROR_CODE: + case IFS_UNASSIGNED_ERROR_CODE_0xA: + case IFS_CORRUPTED_CHUNK: break; } return false; -- cgit From 06d65b2bc532fc9af1c55aa7a18cfd237ce46588 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Thu, 5 Oct 2023 12:51:37 -0700 Subject: platform/x86/intel/ifs: ARRAY BIST for Sierra Forest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Array BIST MSR addresses, bit definition and semantics are different for Sierra Forest. Branch into a separate Array BIST flow on Sierra Forest when user invokes Array Test. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Tested-by: Pengfei Xu Link: https://lore.kernel.org/r/20231005195137.3117166-10-jithu.joseph@intel.com [ij: ARRAY_GEN_* -> ARRAY_GEN* for consistency] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/ifs/core.c | 15 +++++++------ drivers/platform/x86/intel/ifs/ifs.h | 7 ++++++ drivers/platform/x86/intel/ifs/runtest.c | 37 +++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 0c8927916373..7b11198d85a1 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -11,16 +11,16 @@ #include "ifs.h" -#define X86_MATCH(model) \ +#define X86_MATCH(model, array_gen) \ X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, \ - INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, NULL) + INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, array_gen) static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { - X86_MATCH(SAPPHIRERAPIDS_X), - X86_MATCH(EMERALDRAPIDS_X), - X86_MATCH(GRANITERAPIDS_X), - X86_MATCH(GRANITERAPIDS_D), - X86_MATCH(ATOM_CRESTMONT_X), + X86_MATCH(SAPPHIRERAPIDS_X, ARRAY_GEN0), + X86_MATCH(EMERALDRAPIDS_X, ARRAY_GEN0), + X86_MATCH(GRANITERAPIDS_X, ARRAY_GEN0), + X86_MATCH(GRANITERAPIDS_D, ARRAY_GEN0), + X86_MATCH(ATOM_CRESTMONT_X, ARRAY_GEN1), {} }; MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); @@ -100,6 +100,7 @@ static int __init ifs_init(void) continue; ifs_devices[i].rw_data.generation = FIELD_GET(MSR_INTEGRITY_CAPS_SAF_GEN_MASK, msrval); + ifs_devices[i].rw_data.array_gen = (u32)m->driver_data; ret = misc_register(&ifs_devices[i].misc); if (ret) goto err_exit; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index f0dd849b3400..56b9f3e3cf76 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -137,6 +137,8 @@ #define MSR_CHUNKS_AUTHENTICATION_STATUS 0x000002c5 #define MSR_ACTIVATE_SCAN 0x000002c6 #define MSR_SCAN_STATUS 0x000002c7 +#define MSR_ARRAY_TRIGGER 0x000002d6 +#define MSR_ARRAY_STATUS 0x000002d7 #define MSR_SAF_CTRL 0x000004f0 #define SCAN_NOT_TESTED 0 @@ -146,6 +148,9 @@ #define IFS_TYPE_SAF 0 #define IFS_TYPE_ARRAY_BIST 1 +#define ARRAY_GEN0 0 +#define ARRAY_GEN1 1 + /* MSR_SCAN_HASHES_STATUS bit fields */ union ifs_scan_hashes_status { u64 data; @@ -272,6 +277,7 @@ struct ifs_test_caps { * @cur_batch: number indicating the currently loaded test file * @generation: IFS test generation enumerated by hardware * @chunk_size: size of a test chunk + * @array_gen: test generation of array test */ struct ifs_data { int loaded_version; @@ -283,6 +289,7 @@ struct ifs_data { u32 cur_batch; u32 generation; u32 chunk_size; + u32 array_gen; }; struct ifs_work { diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 4fe544d79946..9c1a027bc591 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -329,6 +329,38 @@ static void ifs_array_test_core(int cpu, struct device *dev) ifsd->status = SCAN_TEST_PASS; } +#define ARRAY_GEN1_TEST_ALL_ARRAYS 0x0ULL +#define ARRAY_GEN1_STATUS_FAIL 0x1ULL + +static int do_array_test_gen1(void *status) +{ + int cpu = smp_processor_id(); + int first; + + first = cpumask_first(cpu_smt_mask(cpu)); + + if (cpu == first) { + wrmsrl(MSR_ARRAY_TRIGGER, ARRAY_GEN1_TEST_ALL_ARRAYS); + rdmsrl(MSR_ARRAY_STATUS, *((u64 *)status)); + } + + return 0; +} + +static void ifs_array_test_gen1(int cpu, struct device *dev) +{ + struct ifs_data *ifsd = ifs_get_data(dev); + u64 status = 0; + + stop_core_cpuslocked(cpu, do_array_test_gen1, &status); + ifsd->scan_details = status; + + if (status & ARRAY_GEN1_STATUS_FAIL) + ifsd->status = SCAN_TEST_FAIL; + else + ifsd->status = SCAN_TEST_PASS; +} + /* * Initiate per core test. It wakes up work queue threads on the target cpu and * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and @@ -356,7 +388,10 @@ int do_core_test(int cpu, struct device *dev) ifs_test_core(cpu, dev); break; case IFS_TYPE_ARRAY_BIST: - ifs_array_test_core(cpu, dev); + if (ifsd->array_gen == ARRAY_GEN0) + ifs_array_test_core(cpu, dev); + else + ifs_array_test_gen1(cpu, dev); break; default: return -EINVAL; -- cgit From b26bcdd5f03e0b9fdd5dd8567b620538c5058c54 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Thu, 5 Oct 2023 07:56:16 +0000 Subject: platform: mellanox: Rename some init()/exit() functions for consistent naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently some names of init()/exit() pairing function are not consistent. Rename pair mlxplat_pre_init()/mlxplat_post_exit() to respectively mlxplat_logicdev_init()/mlxplat_logicdev_exit(). Rename pair mlxplat_post_init()/mlxplat_pre_exit() to respectively mlxplat_platdevs_init()/mlxplat_platdevs_exit(). Signed-off-by: Vadim Pasternak Link: https://lore.kernel.org/r/20231005075616.42777-4-vadimp@nvidia.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/mlx-platform.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index a2ffe4157df1..b9c709d7dbe0 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -6291,7 +6291,7 @@ static void mlxplat_pci_fpga_devices_exit(void) } static int -mlxplat_pre_init(struct resource **hotplug_resources, unsigned int *hotplug_resources_size) +mlxplat_logicdev_init(struct resource **hotplug_resources, unsigned int *hotplug_resources_size) { int err; @@ -6302,7 +6302,7 @@ mlxplat_pre_init(struct resource **hotplug_resources, unsigned int *hotplug_reso return err; } -static void mlxplat_post_exit(void) +static void mlxplat_logicdev_exit(void) { if (lpc_bridge) mlxplat_pci_fpga_devices_exit(); @@ -6310,7 +6310,7 @@ static void mlxplat_post_exit(void) mlxplat_lpc_cpld_device_exit(); } -static int mlxplat_post_init(struct mlxplat_priv *priv) +static int mlxplat_platdevs_init(struct mlxplat_priv *priv) { int i = 0, err; @@ -6407,7 +6407,7 @@ fail_platform_hotplug_register: return err; } -static void mlxplat_pre_exit(struct mlxplat_priv *priv) +static void mlxplat_platdevs_exit(struct mlxplat_priv *priv) { int i; @@ -6429,7 +6429,7 @@ mlxplat_i2c_mux_complition_notify(void *handle, struct i2c_adapter *parent, { struct mlxplat_priv *priv = handle; - return mlxplat_post_init(priv); + return mlxplat_platdevs_init(priv); } static int mlxplat_i2c_mux_topology_init(struct mlxplat_priv *priv) @@ -6522,7 +6522,7 @@ fail_mlxplat_mlxcpld_verify_bus_topology: static void mlxplat_i2c_main_exit(struct mlxplat_priv *priv) { - mlxplat_pre_exit(priv); + mlxplat_platdevs_exit(priv); mlxplat_i2c_mux_topology_exit(priv); if (priv->pdev_i2c) platform_device_unregister(priv->pdev_i2c); @@ -6544,7 +6544,7 @@ static int mlxplat_probe(struct platform_device *pdev) mlxplat_dev = pdev; } - err = mlxplat_pre_init(&hotplug_resources, &hotplug_resources_size); + err = mlxplat_logicdev_init(&hotplug_resources, &hotplug_resources_size); if (err) return err; @@ -6603,7 +6603,7 @@ fail_regcache_sync: fail_mlxplat_i2c_main_init: fail_regmap_write: fail_alloc: - mlxplat_post_exit(); + mlxplat_logicdev_exit(); return err; } @@ -6617,7 +6617,7 @@ static int mlxplat_remove(struct platform_device *pdev) if (mlxplat_reboot_nb) unregister_reboot_notifier(mlxplat_reboot_nb); mlxplat_i2c_main_exit(priv); - mlxplat_post_exit(); + mlxplat_logicdev_exit(); return 0; } -- cgit From 8634c1114b94049b48805b412b28160cb25a3198 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Thu, 5 Oct 2023 07:56:15 +0000 Subject: platform: mellanox: Fix misspelling error in routine name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change mlxplat_i2c_main_complition_notify() to mlxplat_i2c_main_completion_notify(). Signed-off-by: Vadim Pasternak Link: https://lore.kernel.org/r/20231005075616.42777-3-vadimp@nvidia.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/mlx-platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index b9c709d7dbe0..1bad4c64f36c 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -368,7 +368,7 @@ struct mlxplat_priv { }; static struct platform_device *mlxplat_dev; -static int mlxplat_i2c_main_complition_notify(void *handle, int id); +static int mlxplat_i2c_main_completion_notify(void *handle, int id); static void __iomem *i2c_bridge_addr, *jtag_bridge_addr; /* Regions for LPC I2C controller and LPC base register space */ @@ -384,7 +384,7 @@ static const struct resource mlxplat_lpc_resources[] = { /* Platform systems default i2c data */ static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_i2c_default_data = { - .completion_notify = mlxplat_i2c_main_complition_notify, + .completion_notify = mlxplat_i2c_main_completion_notify, }; /* Platform i2c next generation systems data */ @@ -409,7 +409,7 @@ static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_i2c_ng_data = { .mask = MLXPLAT_CPLD_AGGR_MASK_COMEX, .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET, .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_I2C, - .completion_notify = mlxplat_i2c_main_complition_notify, + .completion_notify = mlxplat_i2c_main_completion_notify, }; /* Platform default channels */ @@ -6471,7 +6471,7 @@ static void mlxplat_i2c_mux_topology_exit(struct mlxplat_priv *priv) } } -static int mlxplat_i2c_main_complition_notify(void *handle, int id) +static int mlxplat_i2c_main_completion_notify(void *handle, int id) { struct mlxplat_priv *priv = handle; -- cgit From ff54dc31fbee363f3bbae85b99b1390dc02ce062 Mon Sep 17 00:00:00 2001 From: Nikita Kravets Date: Fri, 6 Oct 2023 20:53:55 +0300 Subject: platform/x86: msi-ec: rename fn_super_swap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch renames fn_super_swap to fn_win_swap for consistency with the downstream version of the driver. Renaming the field to fn_super_swap in the downstream driver would require modifying several branches that are yet to be merged into the main branch, so I decided to do it here instead. Cc: Aakash Singh Cc: Jose Angel Pastrana Signed-off-by: Nikita Kravets Link: https://lore.kernel.org/r/20231006175352.1753017-7-teackot@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/msi-ec.c | 16 ++++++++-------- drivers/platform/x86/msi-ec.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c index f26a3121092f..48311d5a41ce 100644 --- a/drivers/platform/x86/msi-ec.c +++ b/drivers/platform/x86/msi-ec.c @@ -58,7 +58,7 @@ static struct msi_ec_conf CONF0 __initdata = { .block_address = 0x2f, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = 0xbf, .bit = 4, }, @@ -138,7 +138,7 @@ static struct msi_ec_conf CONF1 __initdata = { .block_address = 0x2f, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = 0xbf, .bit = 4, }, @@ -215,7 +215,7 @@ static struct msi_ec_conf CONF2 __initdata = { .block_address = 0x2f, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = 0xe8, .bit = 4, }, @@ -294,7 +294,7 @@ static struct msi_ec_conf CONF3 __initdata = { .block_address = 0x2f, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = 0xe8, .bit = 4, }, @@ -372,7 +372,7 @@ static struct msi_ec_conf CONF4 __initdata = { .block_address = 0x2f, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown .bit = 4, }, @@ -451,7 +451,7 @@ static struct msi_ec_conf CONF5 __initdata = { .block_address = 0x2f, .bit = 1, }, - .fn_super_swap = { // todo: reverse + .fn_win_swap = { // todo: reverse .address = 0xbf, .bit = 4, }, @@ -529,7 +529,7 @@ static struct msi_ec_conf CONF6 __initdata = { .block_address = MSI_EC_ADDR_UNSUPP, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = 0xbf, // todo: reverse .bit = 4, }, @@ -609,7 +609,7 @@ static struct msi_ec_conf CONF7 __initdata = { .block_address = MSI_EC_ADDR_UNSUPP, .bit = 1, }, - .fn_super_swap = { + .fn_win_swap = { .address = 0xbf, // needs testing .bit = 4, }, diff --git a/drivers/platform/x86/msi-ec.h b/drivers/platform/x86/msi-ec.h index be3533dc9cc6..086351217505 100644 --- a/drivers/platform/x86/msi-ec.h +++ b/drivers/platform/x86/msi-ec.h @@ -40,7 +40,7 @@ struct msi_ec_webcam_conf { int bit; }; -struct msi_ec_fn_super_swap_conf { +struct msi_ec_fn_win_swap_conf { int address; int bit; }; @@ -108,7 +108,7 @@ struct msi_ec_conf { struct msi_ec_charge_control_conf charge_control; struct msi_ec_webcam_conf webcam; - struct msi_ec_fn_super_swap_conf fn_super_swap; + struct msi_ec_fn_win_swap_conf fn_win_swap; struct msi_ec_cooler_boost_conf cooler_boost; struct msi_ec_shift_mode_conf shift_mode; struct msi_ec_super_battery_conf super_battery; -- cgit From 8abba08944663add37111dd6bc4f2916fd6d9085 Mon Sep 17 00:00:00 2001 From: Nikita Kravets Date: Fri, 6 Oct 2023 20:53:57 +0300 Subject: platform/x86: msi-ec: Add more EC configs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds configurations for new EC firmware from the downstream version of the driver. Cc: Aakash Singh Cc: Jose Angel Pastrana Signed-off-by: Nikita Kravets Link: https://lore.kernel.org/r/20231006175352.1753017-9-teackot@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/msi-ec.c | 467 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+) diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c index 48311d5a41ce..2be92ba45926 100644 --- a/drivers/platform/x86/msi-ec.c +++ b/drivers/platform/x86/msi-ec.c @@ -668,6 +668,467 @@ static struct msi_ec_conf CONF7 __initdata = { }, }; +static const char * const ALLOWED_FW_8[] __initconst = { + "14F1EMS1.115", + NULL +}; + +static struct msi_ec_conf CONF8 __initdata = { + .allowed_fw = ALLOWED_FW_8, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = MSI_EC_ADDR_UNSUPP, + .bit = 1, + }, + .fn_win_swap = { + .address = 0xe8, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = 0xeb, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_BASIC_NAME, 0x4d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = MSI_EC_ADDR_UNKNOWN, + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, + }, + .leds = { + .micmute_led_address = MSI_EC_ADDR_UNSUPP, + .mute_led_address = 0x2d, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = MSI_EC_ADDR_UNSUPP, // not functional + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_9[] __initconst = { + "14JKEMS1.104", + NULL +}; + +static struct msi_ec_conf CONF9 __initdata = { + .allowed_fw = ALLOWED_FW_9, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_win_swap = { + .address = 0xbf, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = MSI_EC_ADDR_UNSUPP, // unsupported or enabled by ECO shift + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xf4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, + .rt_fan_speed_base_min = 0x00, + .rt_fan_speed_base_max = 0x96, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = MSI_EC_ADDR_UNSUPP, + .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, + }, + .leds = { + .micmute_led_address = 0x2b, + .mute_led_address = 0x2c, + .bit = 2, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNSUPP, // not presented in MSI app + .bl_modes = { 0x00, 0x08 }, + .max_mode = 1, + .bl_state_address = 0xf3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_10[] __initconst = { + "1582EMS1.107", // GF66 11UC + NULL +}; + +static struct msi_ec_conf CONF10 __initdata = { + .allowed_fw = ALLOWED_FW_10, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_win_swap = { + .address = MSI_EC_ADDR_UNSUPP, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + { SM_TURBO_NAME, 0xc4 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = 0xe5, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, // ? + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, // ? + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = 0x2c, + .mute_led_address = 0x2d, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = 0x2c, + .bl_modes = { 0x00, 0x08 }, + .max_mode = 1, + .bl_state_address = 0xd3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_11[] __initconst = { + "16S6EMS1.111", // Prestige 15 a11scx + "1552EMS1.115", // Modern 15 a11m + NULL +}; + +static struct msi_ec_conf CONF11 __initdata = { + .allowed_fw = ALLOWED_FW_11, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = MSI_EC_ADDR_UNKNOWN, + .bit = 1, + }, + .fn_win_swap = { + .address = 0xe8, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = 0xeb, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x4d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + }, + .gpu = { + .rt_temp_address = MSI_EC_ADDR_UNSUPP, + .rt_fan_speed_address = MSI_EC_ADDR_UNSUPP, + }, + .leds = { + .micmute_led_address = 0x2c, + .mute_led_address = 0x2d, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, + .bl_modes = {}, // ? + .max_mode = 1, // ? + .bl_state_address = 0xd3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_12[] __initconst = { + "16R6EMS1.104", // GF63 Thin 11UC + NULL +}; + +static struct msi_ec_conf CONF12 __initdata = { + .allowed_fw = ALLOWED_FW_12, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_win_swap = { + .address = 0xe8, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + { SM_TURBO_NAME, 0xc4 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = MSI_EC_ADDR_UNSUPP, // 0xeb + .mask = 0x0f, // 00, 0f + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = MSI_EC_ADDR_UNSUPP, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = MSI_EC_ADDR_UNSUPP, + .mute_led_address = 0x2d, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, + .bl_modes = { 0x00, 0x08 }, + .max_mode = 1, + .bl_state_address = 0xd3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_13[] __initconst = { + "1594EMS1.109", // MSI Prestige 16 Studio A13VE + NULL +}; + +static struct msi_ec_conf CONF13 __initdata = { + .allowed_fw = ALLOWED_FW_13, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_win_swap = { + .address = 0xe8, + .bit = 4, // 0x00-0x10 + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, // super battery + { SM_COMFORT_NAME, 0xc1 }, // balanced + { SM_TURBO_NAME, 0xc4 }, // extreme + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = MSI_EC_ADDR_UNSUPP, + .mask = 0x0f, // 00, 0f + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, // 0x0-0x96 + .rt_fan_speed_base_min = 0x00, + .rt_fan_speed_base_max = 0x96, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = 0x2c, + .mute_led_address = 0x2d, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = 0x2c, // KB auto turn off + .bl_modes = { 0x00, 0x08 }, // always on; off after 10 sec + .max_mode = 1, + .bl_state_address = 0xd3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + static struct msi_ec_conf *CONFIGS[] __initdata = { &CONF0, &CONF1, @@ -677,6 +1138,12 @@ static struct msi_ec_conf *CONFIGS[] __initdata = { &CONF5, &CONF6, &CONF7, + &CONF8, + &CONF9, + &CONF10, + &CONF11, + &CONF12, + &CONF13, NULL }; -- cgit From 01d2adcfe0f1bc0f08d822a04b2c354c106d4d2f Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Tue, 10 Oct 2023 12:03:08 +0000 Subject: platform/x86/amd/hsmp: create plat specific struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having a separate platform device structure helps in future, to contain platform specific variables and other data. Also, define macros for dev nodes Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20231010120310.3464066-1-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 61 +++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 31382ef52efb..99727cd705cf 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -47,9 +47,22 @@ #define HSMP_INDEX_REG 0xc4 #define HSMP_DATA_REG 0xc8 -static struct semaphore *hsmp_sem; +#define HSMP_CDEV_NAME "hsmp_cdev" +#define HSMP_DEVNODE_NAME "hsmp" -static struct miscdevice hsmp_device; +struct hsmp_socket { + struct semaphore hsmp_sem; + u16 sock_ind; +}; + +struct hsmp_plat_device { + struct miscdevice hsmp_device; + struct hsmp_socket *sock; + struct device *dev; + u16 num_sockets; +}; + +static struct hsmp_plat_device plat_dev; static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, u32 *value, bool write) @@ -188,6 +201,7 @@ static int validate_message(struct hsmp_message *msg) int hsmp_send_message(struct hsmp_message *msg) { + struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind]; struct amd_northbridge *nb; int ret; @@ -208,14 +222,13 @@ int hsmp_send_message(struct hsmp_message *msg) * In SMP system timeout of 100 millisecs should * be enough for the previous thread to finish the operation */ - ret = down_timeout(&hsmp_sem[msg->sock_ind], - msecs_to_jiffies(HSMP_MSG_TIMEOUT)); + ret = down_timeout(&sock->hsmp_sem, msecs_to_jiffies(HSMP_MSG_TIMEOUT)); if (ret < 0) return ret; ret = __hsmp_send_message(nb->root, msg); - up(&hsmp_sem[msg->sock_ind]); + up(&sock->hsmp_sem); return ret; } @@ -321,28 +334,31 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) { int i; - hsmp_sem = devm_kzalloc(&pdev->dev, - (amd_nb_num() * sizeof(struct semaphore)), - GFP_KERNEL); - if (!hsmp_sem) + plat_dev.sock = devm_kzalloc(&pdev->dev, + (plat_dev.num_sockets * sizeof(struct hsmp_socket)), + GFP_KERNEL); + if (!plat_dev.sock) return -ENOMEM; + plat_dev.dev = &pdev->dev; - for (i = 0; i < amd_nb_num(); i++) - sema_init(&hsmp_sem[i], 1); + for (i = 0; i < plat_dev.num_sockets; i++) { + sema_init(&plat_dev.sock[i].hsmp_sem, 1); + plat_dev.sock[i].sock_ind = i; + } - hsmp_device.name = "hsmp_cdev"; - hsmp_device.minor = MISC_DYNAMIC_MINOR; - hsmp_device.fops = &hsmp_fops; - hsmp_device.parent = &pdev->dev; - hsmp_device.nodename = "hsmp"; - hsmp_device.mode = 0644; + plat_dev.hsmp_device.name = HSMP_CDEV_NAME; + plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; + plat_dev.hsmp_device.fops = &hsmp_fops; + plat_dev.hsmp_device.parent = &pdev->dev; + plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; + plat_dev.hsmp_device.mode = 0644; - return misc_register(&hsmp_device); + return misc_register(&plat_dev.hsmp_device); } static void hsmp_pltdrv_remove(struct platform_device *pdev) { - misc_deregister(&hsmp_device); + misc_deregister(&plat_dev.hsmp_device); } static struct platform_driver amd_hsmp_driver = { @@ -358,7 +374,6 @@ static struct platform_device *amd_hsmp_platdev; static int __init hsmp_plt_init(void) { int ret = -ENODEV; - u16 num_sockets; int i; if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { @@ -371,12 +386,12 @@ static int __init hsmp_plt_init(void) * amd_nb_num() returns number of SMN/DF interfaces present in the system * if we have N SMN/DF interfaces that ideally means N sockets */ - num_sockets = amd_nb_num(); - if (num_sockets == 0) + plat_dev.num_sockets = amd_nb_num(); + if (plat_dev.num_sockets == 0) return ret; /* Test the hsmp interface on each socket */ - for (i = 0; i < num_sockets; i++) { + for (i = 0; i < plat_dev.num_sockets; i++) { ret = hsmp_test(i, 0xDEADBEEF); if (ret) { pr_err("HSMP is not supported on Fam:%x model:%x\n", -- cgit From 5150542b8ec5fb561be080ed0ef3bab8598154c3 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Tue, 10 Oct 2023 12:03:09 +0000 Subject: platform/x86/amd/hsmp: add support for metrics tbl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMD MI300 MCM provides GET_METRICS_TABLE message to retrieve all the system management information from SMU. The metrics table is made available as hexadecimal sysfs binary file under per socket sysfs directory created at /sys/devices/platform/amd_hsmp/socket%d/metrics_bin Metrics table definitions will be documented as part of Public PPR. The same is defined in the amd_hsmp.h header. Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20231010120310.3464066-2-suma.hegde@amd.com [ij: lseek -> lseek(), dram -> DRAM in dev_err()] [ij: added period to terminate a documentation sentence] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/arch/x86/amd_hsmp.rst | 18 ++++ arch/x86/include/uapi/asm/amd_hsmp.h | 109 ++++++++++++++++++++++ drivers/platform/x86/amd/hsmp.c | 174 ++++++++++++++++++++++++++++++++++- 3 files changed, 299 insertions(+), 2 deletions(-) diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index 440e4b645a1c..c92bfd55359f 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -41,6 +41,24 @@ In-kernel integration: * Locking across callers is taken care by the driver. +HSMP sysfs interface +==================== + +1. Metrics table binary sysfs + +AMD MI300A MCM provides GET_METRICS_TABLE message to retrieve +most of the system management information from SMU in one go. + +The metrics table is made available as hexadecimal sysfs binary file +under per socket sysfs directory created at +/sys/devices/platform/amd_hsmp/socket%d/metrics_bin + +Note: lseek() is not supported as entire metrics table is read. + +Metrics table definitions will be documented as part of Public PPR. +The same is defined in the amd_hsmp.h header. + + An example ========== diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h index 769b939444ae..fce22686c834 100644 --- a/arch/x86/include/uapi/asm/amd_hsmp.h +++ b/arch/x86/include/uapi/asm/amd_hsmp.h @@ -47,6 +47,9 @@ enum hsmp_message_ids { HSMP_SET_PCI_RATE, /* 20h Control link rate on PCIe devices */ HSMP_SET_POWER_MODE, /* 21h Select power efficiency profile policy */ HSMP_SET_PSTATE_MAX_MIN, /* 22h Set the max and min DF P-State */ + HSMP_GET_METRIC_TABLE_VER, /* 23h Get metrics table version */ + HSMP_GET_METRIC_TABLE, /* 24h Get metrics table */ + HSMP_GET_METRIC_TABLE_DRAM_ADDR,/* 25h Get metrics table dram address */ HSMP_MSG_ID_MAX, }; @@ -64,6 +67,14 @@ enum hsmp_msg_type { HSMP_GET = 1, }; +enum hsmp_proto_versions { + HSMP_PROTO_VER2 = 2, + HSMP_PROTO_VER3, + HSMP_PROTO_VER4, + HSMP_PROTO_VER5, + HSMP_PROTO_VER6 +}; + struct hsmp_msg_desc { int num_args; int response_sz; @@ -295,6 +306,104 @@ static const struct hsmp_msg_desc hsmp_msg_desc_table[] = { * input: args[0] = min df pstate[15:8] + max df pstate[7:0] */ {1, 0, HSMP_SET}, + + /* + * HSMP_GET_METRIC_TABLE_VER, num_args = 0, response_sz = 1 + * output: args[0] = metrics table version + */ + {0, 1, HSMP_GET}, + + /* + * HSMP_GET_METRIC_TABLE, num_args = 0, response_sz = 0 + */ + {0, 0, HSMP_GET}, + + /* + * HSMP_GET_METRIC_TABLE_DRAM_ADDR, num_args = 0, response_sz = 2 + * output: args[0] = lower 32 bits of the address + * output: args[1] = upper 32 bits of the address + */ + {0, 2, HSMP_GET}, +}; + +/* Metrics table (supported only with proto version 6) */ +struct hsmp_metric_table { + __u32 accumulation_counter; + + /* TEMPERATURE */ + __u32 max_socket_temperature; + __u32 max_vr_temperature; + __u32 max_hbm_temperature; + __u64 max_socket_temperature_acc; + __u64 max_vr_temperature_acc; + __u64 max_hbm_temperature_acc; + + /* POWER */ + __u32 socket_power_limit; + __u32 max_socket_power_limit; + __u32 socket_power; + + /* ENERGY */ + __u64 timestamp; + __u64 socket_energy_acc; + __u64 ccd_energy_acc; + __u64 xcd_energy_acc; + __u64 aid_energy_acc; + __u64 hbm_energy_acc; + + /* FREQUENCY */ + __u32 cclk_frequency_limit; + __u32 gfxclk_frequency_limit; + __u32 fclk_frequency; + __u32 uclk_frequency; + __u32 socclk_frequency[4]; + __u32 vclk_frequency[4]; + __u32 dclk_frequency[4]; + __u32 lclk_frequency[4]; + __u64 gfxclk_frequency_acc[8]; + __u64 cclk_frequency_acc[96]; + + /* FREQUENCY RANGE */ + __u32 max_cclk_frequency; + __u32 min_cclk_frequency; + __u32 max_gfxclk_frequency; + __u32 min_gfxclk_frequency; + __u32 fclk_frequency_table[4]; + __u32 uclk_frequency_table[4]; + __u32 socclk_frequency_table[4]; + __u32 vclk_frequency_table[4]; + __u32 dclk_frequency_table[4]; + __u32 lclk_frequency_table[4]; + __u32 max_lclk_dpm_range; + __u32 min_lclk_dpm_range; + + /* XGMI */ + __u32 xgmi_width; + __u32 xgmi_bitrate; + __u64 xgmi_read_bandwidth_acc[8]; + __u64 xgmi_write_bandwidth_acc[8]; + + /* ACTIVITY */ + __u32 socket_c0_residency; + __u32 socket_gfx_busy; + __u32 dram_bandwidth_utilization; + __u64 socket_c0_residency_acc; + __u64 socket_gfx_busy_acc; + __u64 dram_bandwidth_acc; + __u32 max_dram_bandwidth; + __u64 dram_bandwidth_utilization_acc; + __u64 pcie_bandwidth_acc[4]; + + /* THROTTLERS */ + __u32 prochot_residency_acc; + __u32 ppt_residency_acc; + __u32 socket_thm_residency_acc; + __u32 vr_thm_residency_acc; + __u32 hbm_thm_residency_acc; + __u32 spare; + + /* New items at the end to maintain driver compatibility */ + __u32 gfxclk_frequency[8]; }; /* Reset to default packing */ diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 99727cd705cf..af8adfbd9369 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -20,7 +20,7 @@ #include #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "1.0" +#define DRIVER_VERSION "2.0" /* HSMP Status / Error codes */ #define HSMP_STATUS_NOT_READY 0x00 @@ -49,9 +49,15 @@ #define HSMP_CDEV_NAME "hsmp_cdev" #define HSMP_DEVNODE_NAME "hsmp" +#define HSMP_METRICS_TABLE_NAME "metrics_bin" + +#define HSMP_ATTR_GRP_NAME_SIZE 10 struct hsmp_socket { + struct bin_attribute hsmp_attr; + void __iomem *metric_tbl_addr; struct semaphore hsmp_sem; + char name[HSMP_ATTR_GRP_NAME_SIZE]; u16 sock_ind; }; @@ -59,6 +65,7 @@ struct hsmp_plat_device { struct miscdevice hsmp_device; struct hsmp_socket *sock; struct device *dev; + u32 proto_ver; u16 num_sockets; }; @@ -330,9 +337,162 @@ static const struct file_operations hsmp_fops = { .compat_ioctl = hsmp_ioctl, }; +static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct hsmp_socket *sock = bin_attr->private; + struct hsmp_message msg = { 0 }; + int ret; + + /* Do not support lseek(), reads entire metric table */ + if (count < bin_attr->size) { + dev_err(plat_dev.dev, "Wrong buffer size\n"); + return -EINVAL; + } + + if (!sock) { + dev_err(plat_dev.dev, "Failed to read attribute private data\n"); + return -EINVAL; + } + + msg.msg_id = HSMP_GET_METRIC_TABLE; + msg.sock_ind = sock->sock_ind; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + memcpy(buf, sock->metric_tbl_addr, bin_attr->size); + + return bin_attr->size; +} + +static int hsmp_get_tbl_dram_base(u16 sock_ind) +{ + struct hsmp_socket *sock = &plat_dev.sock[sock_ind]; + struct hsmp_message msg = { 0 }; + phys_addr_t dram_addr; + int ret; + + msg.sock_ind = sock_ind; + msg.response_sz = hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; + msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + + /* + * calculate the metric table DRAM address from lower and upper 32 bits + * sent from SMU and ioremap it to virtual address. + */ + dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); + if (!dram_addr) { + dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n"); + return -ENOMEM; + } + sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr, + sizeof(struct hsmp_metric_table)); + if (!sock->metric_tbl_addr) { + dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n"); + return -ENOMEM; + } + return 0; +} + +static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, + struct bin_attribute *battr, int id) +{ + if (plat_dev.proto_ver == HSMP_PROTO_VER6) + return battr->attr.mode; + else + return 0; +} + +static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock_ind) +{ + struct bin_attribute *hattr = &plat_dev.sock[sock_ind].hsmp_attr; + + sysfs_bin_attr_init(hattr); + hattr->attr.name = HSMP_METRICS_TABLE_NAME; + hattr->attr.mode = 0444; + hattr->read = hsmp_metric_tbl_read; + hattr->size = sizeof(struct hsmp_metric_table); + hattr->private = &plat_dev.sock[sock_ind]; + hattrs[0] = hattr; + + if (plat_dev.proto_ver == HSMP_PROTO_VER6) + return (hsmp_get_tbl_dram_base(sock_ind)); + else + return 0; +} + +/* One bin sysfs for metrics table*/ +#define NUM_HSMP_ATTRS 1 + +static int hsmp_create_sysfs_interface(void) +{ + const struct attribute_group **hsmp_attr_grps; + struct bin_attribute **hsmp_bin_attrs; + struct attribute_group *attr_grp; + int ret; + u16 i; + + /* String formatting is currently limited to u8 sockets */ + if (WARN_ON(plat_dev.num_sockets > U8_MAX)) + return -ERANGE; + + hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) * + (plat_dev.num_sockets + 1), GFP_KERNEL); + if (!hsmp_attr_grps) + return -ENOMEM; + + /* Create a sysfs directory for each socket */ + for (i = 0; i < plat_dev.num_sockets; i++) { + attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL); + if (!attr_grp) + return -ENOMEM; + + snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i); + attr_grp->name = plat_dev.sock[i].name; + + /* Null terminated list of attributes */ + hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) * + (NUM_HSMP_ATTRS + 1), GFP_KERNEL); + if (!hsmp_bin_attrs) + return -ENOMEM; + + attr_grp->bin_attrs = hsmp_bin_attrs; + attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; + hsmp_attr_grps[i] = attr_grp; + + /* Now create the leaf nodes */ + ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i); + if (ret) + return ret; + } + return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps); +} + +static int hsmp_cache_proto_ver(void) +{ + struct hsmp_message msg = { 0 }; + int ret; + + msg.msg_id = HSMP_GET_PROTO_VER; + msg.sock_ind = 0; + msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; + + ret = hsmp_send_message(&msg); + if (!ret) + plat_dev.proto_ver = msg.args[0]; + + return ret; +} + static int hsmp_pltdrv_probe(struct platform_device *pdev) { - int i; + int ret, i; plat_dev.sock = devm_kzalloc(&pdev->dev, (plat_dev.num_sockets * sizeof(struct hsmp_socket)), @@ -353,6 +513,16 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; plat_dev.hsmp_device.mode = 0644; + ret = hsmp_cache_proto_ver(); + if (ret) { + dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n"); + return ret; + } + + ret = hsmp_create_sysfs_interface(); + if (ret) + dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n"); + return misc_register(&plat_dev.hsmp_device); } -- cgit From 48173b0003c5dc556e262e1b57c25efc36db1c6a Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Tue, 10 Oct 2023 12:03:10 +0000 Subject: platform/x86/amd/hsmp: improve the error log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Change print message during platform init to a more meaningful clear message. 2. Return the error code returned by hsmp_test() itself, rather then returning a common EOPNOTSUPP error. Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20231010120310.3464066-3-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index af8adfbd9369..b056a5c8654a 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -564,10 +564,10 @@ static int __init hsmp_plt_init(void) for (i = 0; i < plat_dev.num_sockets; i++) { ret = hsmp_test(i, 0xDEADBEEF); if (ret) { - pr_err("HSMP is not supported on Fam:%x model:%x\n", + pr_err("HSMP test message failed on Fam:%x model:%x\n", boot_cpu_data.x86, boot_cpu_data.x86_model); - pr_err("Or Is HSMP disabled in BIOS ?\n"); - return -EOPNOTSUPP; + pr_err("Is HSMP disabled in BIOS ?\n"); + return ret; } } -- cgit From 39789590cff4043d81774d03777dabf7ba263176 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 10 Oct 2023 16:39:36 +0300 Subject: platform/x86: xo15-ebook: Replace open coded acpi_match_acpi_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace open coded acpi_match_acpi_device() in ebook_switch_add(). Note, while it is a bit longer it is more robust in case more IDs will be added. Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231010133936.3891996-1-andriy.shevchenko@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 391f7ea4431e..df2bf1c58523 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -81,9 +81,9 @@ static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); static int ebook_switch_add(struct acpi_device *device) { + const struct acpi_device_id *id; struct ebook_switch *button; struct input_dev *input; - const char *hid = acpi_device_hid(device); char *name, *class; int error; @@ -102,8 +102,9 @@ static int ebook_switch_add(struct acpi_device *device) name = acpi_device_name(device); class = acpi_device_class(device); - if (strcmp(hid, XO15_EBOOK_HID)) { - pr_err("Unsupported hid [%s]\n", hid); + id = acpi_match_acpi_device(ebook_device_ids, device); + if (!id) { + dev_err(&device->dev, "Unsupported hid\n"); error = -ENODEV; goto err_free_input; } @@ -111,7 +112,7 @@ static int ebook_switch_add(struct acpi_device *device) strcpy(name, XO15_EBOOK_DEVICE_NAME); sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); - snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); + snprintf(button->phys, sizeof(button->phys), "%s/button/input0", id->id); input->name = name; input->phys = button->phys; -- cgit From 5a026767275ef0ba6d7aba37629d9ec638aabb7d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 10 Oct 2023 16:40:19 +0300 Subject: platform/x86: asus-wireless: Replace open coded acpi_match_acpi_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace open coded acpi_match_acpi_device() in asus_wireless_add(). Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231010134019.3892062-1-andriy.shevchenko@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wireless.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index abf01e00b799..41227bf95878 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -148,16 +148,12 @@ static int asus_wireless_add(struct acpi_device *adev) if (err) return err; - for (id = device_ids; id->id[0]; id++) { - if (!strcmp((char *) id->id, acpi_device_hid(adev))) { - data->hswc_params = - (const struct hswc_params *)id->driver_data; - break; - } - } - if (!data->hswc_params) + id = acpi_match_acpi_device(device_ids, adev); + if (!id) return 0; + data->hswc_params = (const struct hswc_params *)id->driver_data; + data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); if (!data->wq) return -ENOMEM; -- cgit From d9f421da6b9287000f8253628d54cf89b0c14da5 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 10 Oct 2023 20:20:01 +0530 Subject: platform/x86/amd/pmc: Use flex array when calling amd_pmc_stb_debugfs_open_v2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently in amd_pmc_stb_debugfs_open_v2() the buffer size is assumed to be fixed and a second call to amd_pmc_stb_debugfs_open_v2() may race with a process holding open another fd. This could change "fsize" to a bigger size causing an out of bounds read. Instead create a struct with a flexarray to solve this. Suggested-by: Hans de Goede Reviewed-by: Hans de Goede Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231010145003.139932-1-Shyam-sundar.S-k@amd.com [ij: renamed flex_arr -> stb_data_arr] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 922932a71233..77dca9a1f00f 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -52,7 +52,7 @@ #define AMD_S2D_REGISTER_ARGUMENT 0xA88 /* STB Spill to DRAM Parameters */ -#define S2D_TELEMETRY_BYTES_MAX 0x100000 +#define S2D_TELEMETRY_BYTES_MAX 0x100000U #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 /* STB Spill to DRAM Message Definition */ @@ -122,6 +122,11 @@ enum s2d_arg { S2D_DRAM_SIZE, }; +struct amd_pmc_stb_v2_data { + size_t size; + u8 data[] __counted_by(size); +}; + struct amd_pmc_bit_map { const char *name; u32 bit_mask; @@ -239,7 +244,8 @@ static const struct file_operations amd_pmc_stb_debugfs_fops = { static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) { struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 *buf, fsize, num_samples, val, stb_rdptr_offset = 0; + u32 fsize, num_samples, val, stb_rdptr_offset = 0; + struct amd_pmc_stb_v2_data *stb_data_arr; int ret; /* Write dummy postcode while reading the STB buffer */ @@ -247,10 +253,6 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) if (ret) dev_err(dev->dev, "error writing to STB: %d\n", ret); - buf = kzalloc(S2D_TELEMETRY_BYTES_MAX, GFP_KERNEL); - if (!buf) - return -ENOMEM; - /* Spill to DRAM num_samples uses separate SMU message port */ dev->msg_port = 1; @@ -264,10 +266,16 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) dev->msg_port = 0; if (ret) { dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); - kfree(buf); return ret; } + fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); + stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); + if (!stb_data_arr) + return -ENOMEM; + + stb_data_arr->size = fsize; + /* Start capturing data from the last push location */ if (num_samples > S2D_TELEMETRY_BYTES_MAX) { fsize = S2D_TELEMETRY_BYTES_MAX; @@ -277,8 +285,8 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) stb_rdptr_offset = 0; } - memcpy_fromio(buf, dev->stb_virt_addr + stb_rdptr_offset, fsize); - filp->private_data = buf; + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); + filp->private_data = stb_data_arr; return 0; } @@ -286,11 +294,9 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, loff_t *pos) { - if (!filp->private_data) - return -EINVAL; + struct amd_pmc_stb_v2_data *data = filp->private_data; - return simple_read_from_buffer(buf, size, pos, filp->private_data, - S2D_TELEMETRY_BYTES_MAX); + return simple_read_from_buffer(buf, size, pos, data->data, data->size); } static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp) -- cgit From b136225746a9793bdff9c78b215eac4f64bf21f2 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 10 Oct 2023 20:20:02 +0530 Subject: platform/x86/amd/pmc: Handle overflow cases where the num_samples range is higher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In amd_pmc_stb_debugfs_open_v2(), the stb buffer is created based on the num_samples and the read/write pointer offset. This holds good when the num_samples reported by PMFW is less than S2D_TELEMETRY_BYTES_MAX; where the stb buffer gets filled from 0th position until S2D_TELEMETRY_BYTES_MAX - 1 based on the read/write pointer offset. But when the num_samples exceeds the S2D_TELEMETRY_BYTES_MAX, the current code does not handle it well as it does not account for the cases where the stb buffer has to filled up as a circular buffer. Handle this scenario into two cases, where first memcpy will have the samples from location: (num_samples % S2D_TELEMETRY_BYTES_MAX) - (S2D_TELEMETRY_BYTES_MAX - 1) and next memcpy will have the newest ones i.e. 0 - (num_samples % S2D_TELEMETRY_BYTES_MAX - 1) Suggested-by: Hans de Goede Reviewed-by: Hans de Goede Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231010145003.139932-2-Shyam-sundar.S-k@amd.com [ij: renamed flex_arr -> stb_data_arr] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 77dca9a1f00f..bc254e1fa71a 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -276,16 +276,23 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) stb_data_arr->size = fsize; - /* Start capturing data from the last push location */ + /* + * Start capturing data from the last push location. + * This is for general cases, where the stb limits + * are meant for standard usage. + */ if (num_samples > S2D_TELEMETRY_BYTES_MAX) { - fsize = S2D_TELEMETRY_BYTES_MAX; - stb_rdptr_offset = num_samples - fsize; + /* First read oldest data starting 1 behind last write till end of ringbuffer */ + stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; + fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; + + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); + /* Second copy the newer samples from offset 0 - last write */ + memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset); } else { - fsize = num_samples; - stb_rdptr_offset = 0; + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); } - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); filp->private_data = stb_data_arr; return 0; -- cgit From 3f720b21ec5af466e50e99dc517af267b67d248c Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 10 Oct 2023 20:20:03 +0530 Subject: platform/x86/amd/pmc: Add dump_custom_stb module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There have been instances when the default size (1M) of the STB is not sufficient to get the complete traces of the failure. In such scenarios we can use a module_param to enable full trace that shall contain more debugging data. This is not a regular case and hence not enabling this capability by default. With this change, there will be two cases on how the driver fetches the stb data: 1) A special case (proposed now) - which is required only for certain platforms. Here, a new module param will be supplied to the driver that will have a special PMFW supporting enhanced dram sizes for getting the stb data. Without the special PMFW support, just setting the module param will not help to get the enhanced stb data. To adapt to this change, we will have a new amd_pmc_stb_handle_efr() to handle enhanced firmware reporting mechanism. Note that, since num_samples based r/w pointer offset calculation is not required for enhanced firmware reporting we will have this mailbox command sent only in case of regular STB cases. 2) Current code branch which fetches the stb data based on the parameters like the num_samples, fsize and the r/w pointer. Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Co-developed-by: Harsh Jain Signed-off-by: Harsh Jain Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231010145003.139932-3-Shyam-sundar.S-k@amd.com [ij: Renamed flex_arr -> stb_data_arr] Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index bc254e1fa71a..cd6ac04c1468 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -53,6 +53,7 @@ /* STB Spill to DRAM Parameters */ #define S2D_TELEMETRY_BYTES_MAX 0x100000U +#define S2D_RSVD_RAM_SPACE 0x100000 #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 /* STB Spill to DRAM Message Definition */ @@ -165,6 +166,10 @@ static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); +static bool dump_custom_stb; +module_param(dump_custom_stb, bool, 0644); +MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); + static struct amd_pmc_dev pmc; static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf); @@ -241,6 +246,25 @@ static const struct file_operations amd_pmc_stb_debugfs_fops = { .release = amd_pmc_stb_debugfs_release, }; +/* Enhanced STB Firmware Reporting Mechanism */ +static int amd_pmc_stb_handle_efr(struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + struct amd_pmc_stb_v2_data *stb_data_arr; + u32 fsize; + + fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; + stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); + if (!stb_data_arr) + return -ENOMEM; + + stb_data_arr->size = fsize; + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); + filp->private_data = stb_data_arr; + + return 0; +} + static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) { struct amd_pmc_dev *dev = filp->f_inode->i_private; @@ -260,6 +284,14 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) if (ret) dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); + /* + * We have a custom stb size and the PMFW is supposed to give + * the enhanced dram size. Note that we land here only for the + * platforms that support enhanced dram size reporting. + */ + if (dump_custom_stb) + return amd_pmc_stb_handle_efr(filp); + /* Get the num_samples to calculate the last push location */ ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true); /* Clear msg_port for other SMU operation */ -- cgit From d3b614d993781afca1832bd2cbc39da64306d0ba Mon Sep 17 00:00:00 2001 From: Edson Juliano Drosdeck Date: Mon, 16 Oct 2023 15:29:10 -0300 Subject: platform/x86: acer-wmi: Remove void function return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adhere to Linux kernel coding style. Reported by checkpatch: WARNING: void function return statements are not generally useful Signed-off-by: Edson Juliano Drosdeck Link: https://lore.kernel.org/r/20231016182910.3344-1-edson.drosdeck@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/acer-wmi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 377a0becd1a1..0e472aa9bf41 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1922,7 +1922,6 @@ static void acer_rfkill_exit(void) rfkill_unregister(threeg_rfkill); rfkill_destroy(threeg_rfkill); } - return; } static void acer_wmi_notify(u32 value, void *context) @@ -2517,7 +2516,6 @@ static void __exit acer_wmi_exit(void) platform_driver_unregister(&acer_platform_driver); pr_info("Acer Laptop WMI Extras unloaded\n"); - return; } module_init(acer_wmi_init); -- cgit From 61f3d868b3d6f5e23a6d25597133d76d2186eddd Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 7 Sep 2023 15:46:39 -0700 Subject: tools/power/x86/intel-speed-select: Sanitize integer arguments If the command takes some integer arguments, make sure the command contains only digits. Same for Hex arguments. Otherwise return error. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 5fcc2a92957e..9992a1cbd95d 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -4,6 +4,7 @@ * Copyright (c) 2019 Intel Corporation. */ +#include #include #include @@ -2730,6 +2731,43 @@ error: exit(-1); } +static void check_optarg(char *option, int hex) +{ + if (optarg) { + char *start = optarg; + int i; + + if (hex && strlen(optarg) < 3) { + /* At least 0x plus one character must be present */ + fprintf(stderr, "malformed arguments for:%s [%s]\n", option, optarg); + exit(0); + } + + if (hex) { + if (optarg[0] != '0' || tolower(optarg[1]) != 'x') { + fprintf(stderr, "malformed arguments for:%s [%s]\n", + option, optarg); + exit(0); + } + start = &optarg[2]; + } + + for (i = 0; i < strlen(start); ++i) { + if (hex) { + if (!isxdigit(start[i])) { + fprintf(stderr, "malformed arguments for:%s [%s]\n", + option, optarg); + exit(0); + } + } else if (!isdigit(start[i])) { + fprintf(stderr, "malformed arguments for:%s [%s]\n", + option, optarg); + exit(0); + } + } + } +} + static void parse_cmd_args(int argc, int start, char **argv) { int opt; @@ -2763,18 +2801,21 @@ static void parse_cmd_args(int argc, int start, char **argv) auto_mode = 1; break; case 'b': + check_optarg("bucket", 0); fact_bucket = atoi(optarg); break; case 'h': cmd_help = 1; break; case 'l': + check_optarg("level", 0); tdp_level = atoi(optarg); break; case 'o': force_online_offline = 1; break; case 't': + check_optarg("trl", 1); sscanf(optarg, "0x%llx", &fact_trl); break; case 'r': @@ -2791,13 +2832,16 @@ static void parse_cmd_args(int argc, int start, char **argv) break; /* CLOS related */ case 'c': + check_optarg("clos", 0); current_clos = atoi(optarg); break; case 'd': + check_optarg("desired", 0); clos_desired = atoi(optarg); clos_desired /= isst_get_disp_freq_multiplier(); break; case 'e': + check_optarg("epp", 0); clos_epp = atoi(optarg); if (is_skx_based_platform()) { isst_display_error_info_message(1, "epp can't be specified on this platform", 0, 0); @@ -2805,14 +2849,17 @@ static void parse_cmd_args(int argc, int start, char **argv) } break; case 'n': + check_optarg("min", 0); clos_min = atoi(optarg); clos_min /= isst_get_disp_freq_multiplier(); break; case 'm': + check_optarg("max", 0); clos_max = atoi(optarg); clos_max /= isst_get_disp_freq_multiplier(); break; case 'p': + check_optarg("priority", 0); clos_priority_type = atoi(optarg); if (is_skx_based_platform() && !clos_priority_type) { isst_display_error_info_message(1, "Invalid clos priority type: proportional for this platform", 0, 0); @@ -2820,6 +2867,7 @@ static void parse_cmd_args(int argc, int start, char **argv) } break; case 'w': + check_optarg("weight", 0); clos_prop_prio = atoi(optarg); if (is_skx_based_platform()) { isst_display_error_info_message(1, "weight can't be specified on this platform", 0, 0); -- cgit From 3076db34b5ba87bb7a3335d16415befdea056d57 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 7 Sep 2023 17:01:55 -0700 Subject: tools/power/x86/intel-speed-select: Update help for TRL TRL (turbo ratio limit) argument is passed in hex string. Clarify that in the help. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 9992a1cbd95d..9306436bb5b0 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -2126,7 +2126,7 @@ static void set_fact_enable(int arg) fprintf(stderr, "Enable Intel Speed Select Technology Turbo frequency feature\n"); fprintf(stderr, - "Optional: -t|--trl : Specify turbo ratio limit\n"); + "Optional: -t|--trl : Specify turbo ratio limit in hex starting with 0x\n"); fprintf(stderr, "\tOptional Arguments: -a|--auto : Designate specified target CPUs with"); fprintf(stderr, @@ -2135,7 +2135,7 @@ static void set_fact_enable(int arg) fprintf(stderr, "Disable Intel Speed Select Technology turbo frequency feature\n"); fprintf(stderr, - "Optional: -t|--trl : Specify turbo ratio limit\n"); + "Optional: -t|--trl : Specify turbo ratio limit in hex starting with 0x\n"); fprintf(stderr, "\tOptional Arguments: -a|--auto : Also disable core-power associations\n"); } @@ -2597,7 +2597,7 @@ static void process_trl(int arg) if (cmd_help) { if (arg) { fprintf(stderr, "Set TRL (turbo ratio limits)\n"); - fprintf(stderr, "\t t|--trl: Specify turbo ratio limit for setting TRL\n"); + fprintf(stderr, "\t t|--trl: Specify turbo ratio limit for setting TRL in hex starting with 0x\n"); } else { fprintf(stderr, "Get TRL (turbo ratio limits)\n"); } -- cgit From 7b00d1018c8c8ed55aa1393b2ca234400eb6ca86 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 7 Sep 2023 17:09:52 -0700 Subject: tools/power/x86/intel-speed-select: turbo-mode enable disable swapped The command for turbo-mode enable and disable is swapped. Fix that. Previously turbo-mode enable was actually disabling and disable was enabling. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 9306436bb5b0..72be2d4db21c 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -2527,22 +2527,22 @@ static void set_turbo_mode_for_cpu(struct isst_id *id, int status) } if (status) { - isst_display_result(id, outf, "turbo-mode", "enable", 0); - } else { isst_display_result(id, outf, "turbo-mode", "disable", 0); + } else { + isst_display_result(id, outf, "turbo-mode", "enable", 0); } } static void set_turbo_mode(int arg) { - int i, enable = arg; + int i, disable = arg; struct isst_id id; if (cmd_help) { - if (enable) - fprintf(stderr, "Set turbo mode enable\n"); - else + if (disable) fprintf(stderr, "Set turbo mode disable\n"); + else + fprintf(stderr, "Set turbo mode enable\n"); exit(0); } @@ -2560,7 +2560,7 @@ static void set_turbo_mode(int arg) if (online) { set_isst_id(&id, i); - set_turbo_mode_for_cpu(&id, enable); + set_turbo_mode_for_cpu(&id, disable); } } -- cgit From 2fe8d2d7911fb4e380c3dcb5655e1a62bb14f75e Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 7 Sep 2023 17:12:55 -0700 Subject: tools/power/x86/intel-speed-select: No TRL for non compute domains Don't call to set or get TRL for domains in which there are no CPUs. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 72be2d4db21c..1afaaa833a54 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -2574,6 +2574,9 @@ static void get_set_trl(struct isst_id *id, void *arg1, void *arg2, void *arg3, int set = *(int *)arg4; int ret; + if (id->cpu < 0) + return; + if (set && !fact_trl) { isst_display_error_info_message(1, "Invalid TRL. Specify with [-t|--trl]", 0, 0); exit(0); -- cgit From da4c1b9e8f22d0567e308428e588e1f9e004ce74 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 7 Sep 2023 17:07:19 -0700 Subject: tools/power/x86/intel-speed-select: Display error for core-power support When core-power is getting enabled, if the feaure is not supported, display error. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 1afaaa833a54..b7bd14618128 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -2242,6 +2242,14 @@ static void enable_clos_qos_config(struct isst_id *id, void *arg1, void *arg2, v { int ret; int status = *(int *)arg4; + int cp_state, cp_cap; + + if (!isst_read_pm_config(id, &cp_state, &cp_cap)) { + if (!cp_cap) { + isst_display_error_info_message(1, "core-power not supported", 0, 0); + return; + } + } if (is_skx_based_platform()) clos_priority_type = 1; -- cgit From bc5370cca03ba9d1634fecd85e0cf99a0a3c0483 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 13 Oct 2023 11:20:56 -0700 Subject: tools/power/x86/intel-speed-select: Increase max CPUs in one request With the increase in the CPU count, this count needs to be updated. Increase max CPU count to 512. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index b7bd14618128..44e1302dd8f9 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -28,7 +28,7 @@ static FILE *outf; static int cpu_model; static int cpu_stepping; -#define MAX_CPUS_IN_ONE_REQ 256 +#define MAX_CPUS_IN_ONE_REQ 512 static short max_target_cpus; static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ]; -- cgit From 3bc0f20a8c59daa79aee2da119564f668f458afd Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 3 Oct 2023 14:46:30 -0700 Subject: tools/power/x86/intel-speed-select: Use cgroup isolate for CPU 0 From kernel version 6.5, CPU 0 hotplug capability is deprecated. If some SST profile doesn't have CPU 0, then it is no longer possible to offline CPU 0. This means that user space threads will still run on CPU 0. To workaround this issue, use cgroup v2 isolation feature. Whenever there /sys/devices/system/cpu/cpu0/online file is absent or open fails, isolate CPU 0 via CPU cgroup v2 isolation. Also add a command line option to force even if the /sys/devices/system/cpu/cpu0/online is present. The previous commit "01bcb56f059e ("tools/power/x86/intel-speed-select: Prevent CPU 0 offline") was just warning about this issue based on the kernel version 6.5 and above. With this new approach, instead of warning take action to mitigate the issue. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 130 ++++++++++++++++------- tools/power/x86/intel-speed-select/isst-daemon.c | 3 +- tools/power/x86/intel-speed-select/isst.h | 3 +- 3 files changed, 97 insertions(+), 39 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 44e1302dd8f9..b51c21ae2c83 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -6,7 +6,6 @@ #include #include -#include #include "isst.h" @@ -56,6 +55,8 @@ static int clos_min = -1; static int clos_max = -1; static int clos_desired = -1; static int clos_priority_type; +static int cpu_0_cgroupv2; +static int cpu_0_workaround(int isolate); struct _cpu_map { unsigned short core_id; @@ -475,42 +476,15 @@ static unsigned int is_cpu_online(int cpu) return online; } -static int get_kernel_version(int *major, int *minor) -{ - struct utsname buf; - int ret; - - ret = uname(&buf); - if (ret) - return ret; - - ret = sscanf(buf.release, "%d.%d", major, minor); - if (ret != 2) - return ret; - - return 0; -} - -#define CPU0_HOTPLUG_DEPRECATE_MAJOR_VER 6 -#define CPU0_HOTPLUG_DEPRECATE_MINOR_VER 5 - void set_cpu_online_offline(int cpu, int state) { char buffer[128]; int fd, ret; - if (!cpu) { - int major, minor; - - ret = get_kernel_version(&major, &minor); - if (!ret) { - if (major > CPU0_HOTPLUG_DEPRECATE_MAJOR_VER || (major == CPU0_HOTPLUG_DEPRECATE_MAJOR_VER && - minor >= CPU0_HOTPLUG_DEPRECATE_MINOR_VER)) { - debug_printf("Ignore CPU 0 offline/online for kernel version >= %d.%d\n", major, minor); - debug_printf("Use cgroups to isolate CPU 0\n"); - return; - } - } + if (cpu_0_cgroupv2 && !cpu) { + fprintf(stderr, "Will use cgroup v2 for CPU 0\n"); + cpu_0_workaround(!state); + return; } snprintf(buffer, sizeof(buffer), @@ -518,9 +492,10 @@ void set_cpu_online_offline(int cpu, int state) fd = open(buffer, O_WRONLY); if (fd < 0) { - if (!cpu && state) { + if (!cpu) { fprintf(stderr, "This system is not configured for CPU 0 online/offline\n"); - fprintf(stderr, "Ignoring online request for CPU 0 as this is already online\n"); + fprintf(stderr, "Will use cgroup v2\n"); + cpu_0_workaround(!state); return; } err(-1, "%s open failed", buffer); @@ -907,7 +882,7 @@ int enable_cpuset_controller(void) return 0; } -int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level) +int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level, int cpu_0_only) { int i, first, curr_index, index, ret, fd; static char str[512], dir_name[64]; @@ -950,6 +925,12 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev curr_index = 0; first = 1; str[0] = '\0'; + + if (cpu_0_only) { + snprintf(str, str_len, "0"); + goto create_partition; + } + for (i = 0; i < get_topo_max_cpus(); ++i) { if (!is_cpu_in_power_domain(i, id)) continue; @@ -972,6 +953,7 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev first = 0; } +create_partition: debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str); snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name); @@ -1012,6 +994,74 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev return 0; } +static int cpu_0_workaround(int isolate) +{ + int fd, fd1, len, ret; + cpu_set_t cpu_mask; + struct isst_id id; + char str[2]; + + debug_printf("isolate CPU 0 state: %d\n", isolate); + + if (isolate) + goto isolate; + + /* First check if CPU 0 was isolated to remove isolation. */ + + /* If the cpuset.cpus doesn't exist, that means that none of the CPUs are isolated*/ + fd = open("/sys/fs/cgroup/0-0-0/cpuset.cpus", O_RDONLY, 0); + if (fd < 0) + return 0; + + len = read(fd, str, sizeof(str)); + /* Error check, but unlikely to fail. If fails that means that not isolated */ + if (len == -1) + return 0; + + + /* Is CPU 0 is in isolate list, the display is sorted so first element will be CPU 0*/ + if (str[0] != '0') { + close(fd); + return 0; + } + + fd1 = open("/sys/fs/cgroup/0-0-0/cpuset.cpus.partition", O_RDONLY, 0); + /* Unlikely that, this attribute is not present, but handle error */ + if (fd1 < 0) { + close(fd); + return 0; + } + + /* Is CPU 0 already changed partition to "member" */ + len = read(fd1, str, sizeof(str)); + if (len != -1 && str[0] == 'm') { + close(fd1); + close(fd); + return 0; + } + + close(fd1); + close(fd); + + debug_printf("CPU 0 was isolated before, so remove isolation\n"); + +isolate: + ret = enable_cpuset_controller(); + if (ret) + goto isolate_fail; + + CPU_ZERO(&cpu_mask); + memset(&id, 0, sizeof(struct isst_id)); + CPU_SET(0, &cpu_mask); + + ret = isolate_cpus(&id, sizeof(cpu_mask), &cpu_mask, isolate, 1); +isolate_fail: + if (ret) + fprintf(stderr, "Can't isolate CPU 0\n"); + + return ret; +} + static int isst_fill_platform_info(void) { const char *pathname = "/dev/isst_interface"; @@ -1458,7 +1508,8 @@ display_result: if (ret) goto use_offline; - ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level); + ret = isolate_cpus(id, ctdp_level.core_cpumask_size, + ctdp_level.core_cpumask, tdp_level, 0); if (ret) goto use_offline; @@ -3054,6 +3105,7 @@ static void usage(void) printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n"); printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n"); printf("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\n"); + printf("\t[-u|--cpu0-workaround : Don't try to online/offline CPU0 instead use cgroup v2.\n"); printf("\nResult format\n"); printf("\tResult display uses a common format for each command:\n"); printf("\tResults are formatted in text/JSON with\n"); @@ -3107,6 +3159,7 @@ static void cmdline(int argc, char **argv) { "no-daemon", no_argument, 0, 'n' }, { "poll-interval", required_argument, 0, 'w' }, { "cgroupv2", required_argument, 0, 'g' }, + { "cpu0-workaround", required_argument, 0, 'u' }, { 0, 0, 0, 0 } }; @@ -3137,7 +3190,7 @@ static void cmdline(int argc, char **argv) goto out; progname = argv[0]; - while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options, + while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ngu", long_options, &option_index)) != -1) { switch (opt) { case 'a': @@ -3199,6 +3252,9 @@ static void cmdline(int argc, char **argv) case 'g': cgroupv2 = 1; break; + case 'u': + cpu_0_cgroupv2 = 1; + break; default: usage(); } diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 12053fa43542..66df21b2b573 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -90,7 +90,8 @@ void process_level_change(struct isst_id *id) if (ret) goto use_offline; - isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, pkg_dev.current_level); + isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, + pkg_dev.current_level, 0); goto free_mask; } diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 8def22dec4a2..4bddd3c66bf7 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -318,7 +318,8 @@ extern struct isst_platform_ops *tpmi_get_platform_ops(void); /* Cgroup related interface */ extern int enable_cpuset_controller(void); -extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level); +extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, + int level, int cpu_0_only); extern int use_cgroupv2(void); #endif -- cgit From a590ed62269a049a72484ce617fe2f34e2da66cf Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 13 Oct 2023 11:15:41 -0700 Subject: tools/power/x86/intel-speed-select: v1.18 release This version addresses issues with: - When CPU 0 hotplug is not possible, try cgroup v2 isolation without any user input - Fix turbo mode enable/disable swapped - Sanitize command line integer and hex arguments - Add more error messages - Increase CPU count in one request Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index b51c21ae2c83..d865dc1f89ee 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -16,7 +16,7 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.17"; +static const char *version_str = "v1.18"; static const int supported_api_ver = 2; static struct isst_if_platform_info isst_platform_info; -- cgit From 942a4a61b64e182d756a1a5776aa500d3b3d862f Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Fri, 20 Oct 2023 17:52:43 +0000 Subject: platform/x86: thinkpad_acpi: replace deprecated strncpy with memcpy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strncpy() is deprecated for use on NUL-terminated destination strings [1] and as such we should prefer more robust and less ambiguous interfaces. We expect ec_fw_string to be NUL-terminated based on its use with format strings in thinkpad_acpi.c: 11241 | pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n", 11242 | ec_fw_string); Moreover, NUL-padding is not required since ec_fw_string is explicitly zero-initialized: 11185 | char ec_fw_string[18] = {0}; When carefully copying bytes from one buffer to another in pre-determined blocks (like what's happening here with dmi_data): | static void find_new_ec_fwstr(const struct dmi_header *dm, void *private) | { | char *ec_fw_string = (char *) private; | const char *dmi_data = (const char *)dm; | /* | * ThinkPad Embedded Controller Program Table on newer models | * | * Offset | Name | Width | Description | * ---------------------------------------------------- | * 0x00 | Type | BYTE | 0x8C | * 0x01 | Length | BYTE | | * 0x02 | Handle | WORD | Varies | * 0x04 | Signature | BYTEx6 | ASCII for "LENOVO" | * 0x0A | OEM struct offset | BYTE | 0x0B | * 0x0B | OEM struct number | BYTE | 0x07, for this structure | * 0x0C | OEM struct revision | BYTE | 0x01, for this format | * 0x0D | ECP version ID | STR ID | | * 0x0E | ECP release date | STR ID | | */ | | /* Return if data structure not match */ | if (dm->type != 140 || dm->length < 0x0F || | memcmp(dmi_data + 4, "LENOVO", 6) != 0 || | dmi_data[0x0A] != 0x0B || dmi_data[0x0B] != 0x07 || | dmi_data[0x0C] != 0x01) | return; | | /* fwstr is the first 8byte string */ | strncpy(ec_fw_string, dmi_data + 0x0F, 8); ... we shouldn't be using a C string api. Let's instead use memcpy() as this more properly relays the intended behavior. Do note that ec_fw_string will still end up being NUL-terminated since we are memcpy'ing only 8 bytes into a buffer full of 18 zeroes. There's still some trailing NUL-bytes there. To ensure this behavior, let's add a BUILD_BUG_ON checking the length leaves space for at least one trailing NUL-byte. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] Link: https://github.com/KSPP/linux/issues/90 Cc: Kees Cook Signed-off-by: Justin Stitt Reviewed-by: Mark Pearson Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20231020-strncpy-drivers-platform-x86-thinkpad_acpi-c-v1-1-312f2e33034f@google.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9c19624a7454..c3499bf60582 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -11226,6 +11226,8 @@ invalid: return '\0'; } +#define EC_FW_STRING_LEN 18 + static void find_new_ec_fwstr(const struct dmi_header *dm, void *private) { char *ec_fw_string = (char *) private; @@ -11254,7 +11256,8 @@ static void find_new_ec_fwstr(const struct dmi_header *dm, void *private) return; /* fwstr is the first 8byte string */ - strncpy(ec_fw_string, dmi_data + 0x0F, 8); + BUILD_BUG_ON(EC_FW_STRING_LEN <= 8); + memcpy(ec_fw_string, dmi_data + 0x0F, 8); } /* returns 0 - probe ok, or < 0 - probe error. @@ -11264,7 +11267,7 @@ static int __must_check __init get_thinkpad_model_data( struct thinkpad_id_data *tp) { const struct dmi_device *dev = NULL; - char ec_fw_string[18] = {0}; + char ec_fw_string[EC_FW_STRING_LEN] = {0}; char const *s; char t; -- cgit From 858bd78c9d324781217bb42a87766ca9194809ca Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 21 Oct 2023 11:48:41 +0200 Subject: platform/x86: asus-wmi: Do not report brightness up/down keys when also reported by acpi_video MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a long time now the acpi_video driver reports evdev brightness up/down key events for the brightness hotkeys on most (non ancient) laptops. asus-wmi also reports evdev brightness up/down key events for these keys leading to each press being reported twice and e.g. GNOME increasing the brightness by 2 steps instead of 1 step. Use the acpi_video_handles_brightness_key_presses() helper to detect if acpi_video is reporting brightness key-presses and if it is then don't report the same events also from the asus-wmi driver. Note there is a chance that this may lead to regressions where the brightness hotkeys stop working because they are not actually reported by the acpi_video driver. Unfortunately the only way to find out if this is a problem is to try. To at least avoid regressions on old hw using the eeepc-wmi driver, implement this as a key filter in asus-nb-wmi so that the eeepc-wmi driver is not affected. Reported-by: James John Closes: https://lore.kernel.org/platform-driver-x86/a2c441fe-457e-44cf-a146-0ecd86b037cf@donjajo.com/ Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20231021094841.7419-1-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-nb-wmi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index d85d895fee89..67e4111716ca 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -16,6 +16,8 @@ #include #include +#include + #include "asus-wmi.h" #define ASUS_NB_WMI_FILE "asus-nb-wmi" @@ -603,6 +605,19 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_END, 0}, }; +static void asus_nb_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code, + unsigned int *value, bool *autorelease) +{ + switch (*code) { + case ASUS_WMI_BRN_DOWN: + case ASUS_WMI_BRN_UP: + if (acpi_video_handles_brightness_key_presses()) + *code = ASUS_WMI_KEY_IGNORE; + + break; + } +} + static struct asus_wmi_driver asus_nb_wmi_driver = { .name = ASUS_NB_WMI_FILE, .owner = THIS_MODULE, @@ -611,6 +626,7 @@ static struct asus_wmi_driver asus_nb_wmi_driver = { .input_name = "Asus WMI hotkeys", .input_phys = ASUS_NB_WMI_FILE "/input0", .detect_quirks = asus_nb_wmi_quirks, + .key_filter = asus_nb_wmi_key_filter, }; -- cgit From 02a258ac3ebf8a530d340ec24a694c13ac9d25c0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Thu, 19 Oct 2023 15:51:22 +0200 Subject: platform/x86/amd/hsmp: Fix iomem handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the metrics table is marked as io memory, use memcpy_fromio() when copying its contents. Compile-tested only. Fixes: 5150542b8ec5 ("platform/x86/amd/hsmp: add support for metrics tbl") Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231019135122.21774-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index b056a5c8654a..b55d80e29139 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -362,7 +362,7 @@ static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, ret = hsmp_send_message(&msg); if (ret) return ret; - memcpy(buf, sock->metric_tbl_addr, bin_attr->size); + memcpy_fromio(buf, sock->metric_tbl_addr, bin_attr->size); return bin_attr->size; } -- cgit From 4186a47dbfbce2f97b0c025a7eac32bb5130df05 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 20 Oct 2023 23:10:01 +0200 Subject: platform/x86: wmi: Decouple probe deferring from wmi_block_list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many aggregate WMI drivers do not use -EPROBE_DEFER when they cannot find a WMI device during probe, instead they require all WMI devices associated with an platform device to become available at once. This is currently achieved by adding those WMI devices to the wmi_block_list before they are registered, which is then used by the deprecated GUID-based functions to search for WMI devices. Replace this approach with a device link which defers probing of the WMI device until the associated platform device has finished probing (and has registered all WMI devices). New aggregate WMI drivers should not rely on this behaviour. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231020211005.38216-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index a78ddd83cda0..1dbef16acdeb 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1221,6 +1221,26 @@ static int wmi_create_device(struct device *wmi_bus_dev, return 0; } +static int wmi_add_device(struct platform_device *pdev, struct wmi_device *wdev) +{ + struct device_link *link; + + /* + * Many aggregate WMI drivers do not use -EPROBE_DEFER when they + * are unable to find a WMI device during probe, instead they require + * all WMI devices associated with an platform device to become available + * at once. This device link thus prevents WMI drivers from probing until + * the associated platform device has finished probing (and has registered + * all discovered WMI devices). + */ + + link = device_link_add(&wdev->dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); + if (!link) + return -EINVAL; + + return device_add(&wdev->dev); +} + static void wmi_free_devices(struct acpi_device *device) { struct wmi_block *wblock, *next; @@ -1263,11 +1283,12 @@ static bool guid_already_parsed_for_legacy(struct acpi_device *device, const gui /* * Parse the _WDG method for the GUID data blocks */ -static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) +static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; const struct guid_block *gblock; - struct wmi_block *wblock, *next; + struct wmi_block *wblock; union acpi_object *obj; acpi_status status; int retval = 0; @@ -1317,22 +1338,14 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) wblock->handler = wmi_notify_debug; wmi_method_enable(wblock, true); } - } - /* - * Now that all of the devices are created, add them to the - * device tree and probe subdrivers. - */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { - if (wblock->acpi_device != device) - continue; - - retval = device_add(&wblock->dev.dev); + retval = wmi_add_device(pdev, &wblock->dev); if (retval) { dev_err(wmi_bus_dev, "failed to register %pUL\n", &wblock->gblock.guid); if (debug_event) wmi_method_enable(wblock, false); + list_del(&wblock->list); put_device(&wblock->dev.dev); } @@ -1487,7 +1500,7 @@ static int acpi_wmi_probe(struct platform_device *device) } dev_set_drvdata(&device->dev, wmi_bus_dev); - error = parse_wdg(wmi_bus_dev, acpi_device); + error = parse_wdg(wmi_bus_dev, device); if (error) { pr_err("Failed to parse WDG method\n"); goto err_remove_busdev; -- cgit From 7444f8347487df8d898fbc022249942806c80fc9 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 20 Oct 2023 23:10:02 +0200 Subject: platform/x86: wmi: Fix refcounting of WMI devices in legacy functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, legacy GUID-based functions where using find_guid() when searching for WMI devices, which did no refcounting on the returned WMI device. This meant that the WMI device could disappear at any moment, potentially leading to various errors. Fix this by using bus_find_device() which returns an actual reference to the found WMI device. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231020211005.38216-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 167 +++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 60 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 1dbef16acdeb..e3984801883a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -109,33 +109,13 @@ static const char * const allow_duplicates[] = { NULL }; +#define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) +#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) + /* * GUID parsing functions */ -static acpi_status find_guid(const char *guid_string, struct wmi_block **out) -{ - guid_t guid_input; - struct wmi_block *wblock; - - if (!guid_string) - return AE_BAD_PARAMETER; - - if (guid_parse(guid_string, &guid_input)) - return AE_BAD_PARAMETER; - - list_for_each_entry(wblock, &wmi_block_list, list) { - if (guid_equal(&wblock->gblock.guid, &guid_input)) { - if (out) - *out = wblock; - - return AE_OK; - } - } - - return AE_NOT_FOUND; -} - static bool guid_parse_and_compare(const char *string, const guid_t *guid) { guid_t guid_input; @@ -245,6 +225,41 @@ static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_bu return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out); } +static int wmidev_match_guid(struct device *dev, const void *data) +{ + struct wmi_block *wblock = dev_to_wblock(dev); + const guid_t *guid = data; + + if (guid_equal(guid, &wblock->gblock.guid)) + return 1; + + return 0; +} + +static struct bus_type wmi_bus_type; + +static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) +{ + struct device *dev; + guid_t guid; + int ret; + + ret = guid_parse(guid_string, &guid); + if (ret < 0) + return ERR_PTR(ret); + + dev = bus_find_device(&wmi_bus_type, NULL, &guid, wmidev_match_guid); + if (!dev) + return ERR_PTR(-ENODEV); + + return dev_to_wdev(dev); +} + +static void wmi_device_put(struct wmi_device *wdev) +{ + put_device(&wdev->dev); +} + /* * Exported WMI functions */ @@ -279,18 +294,17 @@ EXPORT_SYMBOL_GPL(set_required_buffer_size); */ int wmi_instance_count(const char *guid_string) { - struct wmi_block *wblock; - acpi_status status; + struct wmi_device *wdev; + int ret; - status = find_guid(guid_string, &wblock); - if (ACPI_FAILURE(status)) { - if (status == AE_BAD_PARAMETER) - return -EINVAL; + wdev = wmi_find_device_by_guid(guid_string); + if (IS_ERR(wdev)) + return PTR_ERR(wdev); - return -ENODEV; - } + ret = wmidev_instance_count(wdev); + wmi_device_put(wdev); - return wmidev_instance_count(&wblock->dev); + return ret; } EXPORT_SYMBOL_GPL(wmi_instance_count); @@ -325,15 +339,18 @@ EXPORT_SYMBOL_GPL(wmidev_instance_count); acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) { - struct wmi_block *wblock = NULL; + struct wmi_device *wdev; acpi_status status; - status = find_guid(guid_string, &wblock); - if (ACPI_FAILURE(status)) - return status; + wdev = wmi_find_device_by_guid(guid_string); + if (IS_ERR(wdev)) + return AE_ERROR; + + status = wmidev_evaluate_method(wdev, instance, method_id, in, out); - return wmidev_evaluate_method(&wblock->dev, instance, method_id, - in, out); + wmi_device_put(wdev); + + return status; } EXPORT_SYMBOL_GPL(wmi_evaluate_method); @@ -472,13 +489,19 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance, struct acpi_buffer *out) { struct wmi_block *wblock; + struct wmi_device *wdev; acpi_status status; - status = find_guid(guid_string, &wblock); - if (ACPI_FAILURE(status)) - return status; + wdev = wmi_find_device_by_guid(guid_string); + if (IS_ERR(wdev)) + return AE_ERROR; - return __query_block(wblock, instance, out); + wblock = container_of(wdev, struct wmi_block, dev); + status = __query_block(wblock, instance, out); + + wmi_device_put(wdev); + + return status; } EXPORT_SYMBOL_GPL(wmi_query_block); @@ -516,8 +539,9 @@ EXPORT_SYMBOL_GPL(wmidev_block_query); acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acpi_buffer *in) { - struct wmi_block *wblock = NULL; + struct wmi_block *wblock; struct guid_block *block; + struct wmi_device *wdev; acpi_handle handle; struct acpi_object_list input; union acpi_object params[2]; @@ -527,19 +551,26 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, if (!in) return AE_BAD_DATA; - status = find_guid(guid_string, &wblock); - if (ACPI_FAILURE(status)) - return status; + wdev = wmi_find_device_by_guid(guid_string); + if (IS_ERR(wdev)) + return AE_ERROR; + wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; handle = wblock->acpi_device->handle; - if (block->instance_count <= instance) - return AE_BAD_PARAMETER; + if (block->instance_count <= instance) { + status = AE_BAD_PARAMETER; + + goto err_wdev_put; + } /* Check GUID is a data block */ - if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) - return AE_ERROR; + if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) { + status = AE_ERROR; + + goto err_wdev_put; + } input.count = 2; input.pointer = params; @@ -551,7 +582,12 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, get_acpi_method_name(wblock, 'S', method); - return acpi_evaluate_object(handle, method, &input, NULL); + status = acpi_evaluate_object(handle, method, &input, NULL); + +err_wdev_put: + wmi_device_put(wdev); + + return status; } EXPORT_SYMBOL_GPL(wmi_set_block); @@ -742,7 +778,15 @@ EXPORT_SYMBOL_GPL(wmi_get_event_data); */ bool wmi_has_guid(const char *guid_string) { - return ACPI_SUCCESS(find_guid(guid_string, NULL)); + struct wmi_device *wdev; + + wdev = wmi_find_device_by_guid(guid_string); + if (IS_ERR(wdev)) + return false; + + wmi_device_put(wdev); + + return true; } EXPORT_SYMBOL_GPL(wmi_has_guid); @@ -756,20 +800,23 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); */ char *wmi_get_acpi_device_uid(const char *guid_string) { - struct wmi_block *wblock = NULL; - acpi_status status; + struct wmi_block *wblock; + struct wmi_device *wdev; + char *uid; - status = find_guid(guid_string, &wblock); - if (ACPI_FAILURE(status)) + wdev = wmi_find_device_by_guid(guid_string); + if (IS_ERR(wdev)) return NULL; - return acpi_device_uid(wblock->acpi_device); + wblock = container_of(wdev, struct wmi_block, dev); + uid = acpi_device_uid(wblock->acpi_device); + + wmi_device_put(wdev); + + return uid; } EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); -#define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) -#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) - static inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv) { return container_of(drv, struct wmi_driver, driver); -- cgit From ed85891a276edaf7a867de0e9acd0837bc3008f2 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 20 Oct 2023 23:10:03 +0200 Subject: platform/x86: wmi: Fix probe failure when failing to register WMI devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a WMI device besides the first one somehow fails to register, retval is returned while still containing a negative error code. This causes the ACPI device fail to probe, leaving behind zombie WMI devices leading to various errors later. Handle the single error path separately and return 0 unconditionally after trying to register all WMI devices to solve the issue. Also continue to register WMI devices even if some fail to allocate memory. Fixes: 6ee50aaa9a20 ("platform/x86: wmi: Instantiate all devices before adding them") Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231020211005.38216-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e3984801883a..ab24ea9ffc9a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1338,8 +1338,8 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) struct wmi_block *wblock; union acpi_object *obj; acpi_status status; - int retval = 0; u32 i, total; + int retval; status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); if (ACPI_FAILURE(status)) @@ -1350,8 +1350,8 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) return -ENXIO; if (obj->type != ACPI_TYPE_BUFFER) { - retval = -ENXIO; - goto out_free_pointer; + kfree(obj); + return -ENXIO; } gblock = (const struct guid_block *)obj->buffer.pointer; @@ -1366,8 +1366,8 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); if (!wblock) { - retval = -ENOMEM; - break; + dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid); + continue; } wblock->acpi_device = device; @@ -1398,9 +1398,9 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) } } -out_free_pointer: - kfree(out.pointer); - return retval; + kfree(obj); + + return 0; } /* -- cgit From eba9ac7abab91c8f6d351460239108bef5e7a0b6 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 20 Oct 2023 23:10:04 +0200 Subject: platform/x86: wmi: Fix opening of char device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit fa1f68db6ca7 ("drivers: misc: pass miscdevice pointer via file private data"), the miscdevice stores a pointer to itself inside filp->private_data, which means that private_data will not be NULL when wmi_char_open() is called. This might cause memory corruption should wmi_char_open() be unable to find its driver, something which can happen when the associated WMI device is deleted in wmi_free_devices(). Fix the problem by using the miscdevice pointer to retrieve the WMI device data associated with a char device using container_of(). This also avoids wmi_char_open() picking a wrong WMI device bound to a driver with the same name as the original driver. Fixes: 44b6b7661132 ("platform/x86: wmi: create userspace interface for drivers") Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231020211005.38216-5-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index ab24ea9ffc9a..6b3b2fe464d2 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -958,21 +958,13 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) } static int wmi_char_open(struct inode *inode, struct file *filp) { - const char *driver_name = filp->f_path.dentry->d_iname; - struct wmi_block *wblock; - struct wmi_block *next; - - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { - if (!wblock->dev.dev.driver) - continue; - if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) { - filp->private_data = wblock; - break; - } - } + /* + * The miscdevice already stores a pointer to itself + * inside filp->private_data + */ + struct wmi_block *wblock = container_of(filp->private_data, struct wmi_block, char_dev); - if (!filp->private_data) - return -ENODEV; + filp->private_data = wblock; return nonseekable_open(inode, filp); } -- cgit From ac9bc85c49ffcfca8c1b117e1a9e94dd32680c68 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 20 Oct 2023 23:10:05 +0200 Subject: platform/x86: wmi: Decouple WMI device removal from wmi_block_list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use device_for_each_child_reverse() to find and unregister WMI devices belonging to a WMI bus device instead of iterating thru the entire wmi_block_list. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231020211005.38216-6-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wmi.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 6b3b2fe464d2..5c27b4aa9690 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1280,19 +1280,6 @@ static int wmi_add_device(struct platform_device *pdev, struct wmi_device *wdev) return device_add(&wdev->dev); } -static void wmi_free_devices(struct acpi_device *device) -{ - struct wmi_block *wblock, *next; - - /* Delete devices for all the GUIDs */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { - if (wblock->acpi_device == device) { - list_del(&wblock->list); - device_unregister(&wblock->dev.dev); - } - } -} - static bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid) { struct wmi_block *wblock; @@ -1487,16 +1474,28 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, event, 0); } +static int wmi_remove_device(struct device *dev, void *data) +{ + struct wmi_block *wblock = dev_to_wblock(dev); + + list_del(&wblock->list); + device_unregister(dev); + + return 0; +} + static void acpi_wmi_remove(struct platform_device *device) { struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); + struct device *wmi_bus_device = dev_get_drvdata(&device->dev); acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); - wmi_free_devices(acpi_device); - device_unregister(dev_get_drvdata(&device->dev)); + + device_for_each_child_reverse(wmi_bus_device, NULL, wmi_remove_device); + device_unregister(wmi_bus_device); } static int acpi_wmi_probe(struct platform_device *device) -- cgit From 916646758aea81a143ce89103910f715ed923346 Mon Sep 17 00:00:00 2001 From: Olli Asikainen Date: Tue, 24 Oct 2023 22:09:21 +0300 Subject: platform/x86: thinkpad_acpi: Add battery quirk for Thinkpad X120e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thinkpad X120e also needs this battery quirk. Signed-off-by: Olli Asikainen Link: https://lore.kernel.org/r/20231024190922.2742-1-olli.asikainen@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c3499bf60582..fce16b016e88 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -9814,6 +9814,7 @@ static const struct tpacpi_quirk battery_quirk_table[] __initconst = { * Individual addressing is broken on models that expose the * primary battery as BAT1. */ + TPACPI_Q_LNV('8', 'F', true), /* Thinkpad X120e */ TPACPI_Q_LNV('J', '7', true), /* B5400 */ TPACPI_Q_LNV('J', 'I', true), /* Thinkpad 11e */ TPACPI_Q_LNV3('R', '0', 'B', true), /* Thinkpad 11e gen 3 */ -- cgit From 94ace9eda88229c73698b8dd8d3c06dd0831319c Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Fri, 20 Oct 2023 10:40:07 +0800 Subject: platform/x86: inspur-platform-profile: Add platform profile support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Inspur platforms to used the platform profile feature. This will allow users to determine and control the platform modes between low-power, balanced and performance modes. Signed-off-by: Ai Chao Reviewed-by: Thomas Weißschuh Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20231020024007.1677962-1-aichao@kylinos.cn [ij: Removed kerneldoc markers from non-kerneldoc comments.] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 11 ++ drivers/platform/x86/Makefile | 3 + drivers/platform/x86/inspur_platform_profile.c | 216 +++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 drivers/platform/x86/inspur_platform_profile.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2a1070543391..7e69fdaccdd5 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -988,6 +988,17 @@ config TOUCHSCREEN_DMI the OS-image for the device. This option supplies the missing info. Enable this for x86 tablets with Silead or Chipone touchscreens. +config INSPUR_PLATFORM_PROFILE + tristate "Inspur WMI platform profile driver" + depends on ACPI_WMI + select ACPI_PLATFORM_PROFILE + help + This will allow users to determine and control the platform modes + between low-power, balanced and performance modes. + + To compile this driver as a module, choose M here: the module + will be called inspur-platform-profile. + source "drivers/platform/x86/x86-android-tablets/Kconfig" config FW_ATTR_CLASS diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index b457de5abf7d..c7a18e95ad8c 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -98,6 +98,9 @@ obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o # before toshiba_acpi initializes obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o +# Inspur +obj-$(CONFIG_INSPUR_PLATFORM_PROFILE) += inspur_platform_profile.o + # Laptop drivers obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c new file mode 100644 index 000000000000..743705bddda3 --- /dev/null +++ b/drivers/platform/x86/inspur_platform_profile.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Inspur WMI Platform Profile + * + * Copyright (C) 2018 Ai Chao + */ + +#include +#include +#include +#include +#include + +#define WMI_INSPUR_POWERMODE_BIOS_GUID "596C31E3-332D-43C9-AEE9-585493284F5D" + +enum inspur_wmi_method_ids { + INSPUR_WMI_GET_POWERMODE = 0x02, + INSPUR_WMI_SET_POWERMODE = 0x03, +}; + +/* + * Power Mode: + * 0x0: Balance Mode + * 0x1: Performance Mode + * 0x2: Power Saver Mode + */ +enum inspur_tmp_profile { + INSPUR_TMP_PROFILE_BALANCE = 0, + INSPUR_TMP_PROFILE_PERFORMANCE = 1, + INSPUR_TMP_PROFILE_POWERSAVE = 2, +}; + +struct inspur_wmi_priv { + struct wmi_device *wdev; + struct platform_profile_handler handler; +}; + +static int inspur_wmi_perform_query(struct wmi_device *wdev, + enum inspur_wmi_method_ids query_id, + void *buffer, size_t insize, + size_t outsize) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer input = { insize, buffer}; + union acpi_object *obj; + acpi_status status; + int ret = 0; + + status = wmidev_evaluate_method(wdev, 0, query_id, &input, &output); + if (ACPI_FAILURE(status)) { + dev_err(&wdev->dev, "EC Powermode control failed: %s\n", + acpi_format_exception(status)); + return -EIO; + } + + obj = output.pointer; + if (!obj) + return -EINVAL; + + if (obj->type != ACPI_TYPE_BUFFER || + obj->buffer.length != outsize) { + ret = -EINVAL; + goto out_free; + } + + memcpy(buffer, obj->buffer.pointer, obj->buffer.length); + +out_free: + kfree(obj); + return ret; +} + +/* + * Set Power Mode to EC RAM. If Power Mode value greater than 0x3, + * return error + * Method ID: 0x3 + * Arg: 4 Bytes + * Byte [0]: Power Mode: + * 0x0: Balance Mode + * 0x1: Performance Mode + * 0x2: Power Saver Mode + * Return Value: 4 Bytes + * Byte [0]: Return Code + * 0x0: No Error + * 0x1: Error + */ +static int inspur_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv, + handler); + u8 ret_code[4] = {0, 0, 0, 0}; + int ret; + + switch (profile) { + case PLATFORM_PROFILE_BALANCED: + ret_code[0] = INSPUR_TMP_PROFILE_BALANCE; + break; + case PLATFORM_PROFILE_PERFORMANCE: + ret_code[0] = INSPUR_TMP_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_LOW_POWER: + ret_code[0] = INSPUR_TMP_PROFILE_POWERSAVE; + break; + default: + return -EOPNOTSUPP; + } + + ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_SET_POWERMODE, + ret_code, sizeof(ret_code), + sizeof(ret_code)); + + if (ret < 0) + return ret; + + if (ret_code[0]) + return -EBADRQC; + + return 0; +} + +/* + * Get Power Mode from EC RAM, If Power Mode value greater than 0x3, + * return error + * Method ID: 0x2 + * Return Value: 4 Bytes + * Byte [0]: Return Code + * 0x0: No Error + * 0x1: Error + * Byte [1]: Power Mode + * 0x0: Balance Mode + * 0x1: Performance Mode + * 0x2: Power Saver Mode + */ +static int inspur_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv, + handler); + u8 ret_code[4] = {0, 0, 0, 0}; + int ret; + + ret = inspur_wmi_perform_query(priv->wdev, INSPUR_WMI_GET_POWERMODE, + &ret_code, sizeof(ret_code), + sizeof(ret_code)); + if (ret < 0) + return ret; + + if (ret_code[0]) + return -EBADRQC; + + switch (ret_code[1]) { + case INSPUR_TMP_PROFILE_BALANCE: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case INSPUR_TMP_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case INSPUR_TMP_PROFILE_POWERSAVE: + *profile = PLATFORM_PROFILE_LOW_POWER; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int inspur_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct inspur_wmi_priv *priv; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + + priv->handler.profile_get = inspur_platform_profile_get; + priv->handler.profile_set = inspur_platform_profile_set; + + set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices); + set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices); + + return platform_profile_register(&priv->handler); +} + +static void inspur_wmi_remove(struct wmi_device *wdev) +{ + platform_profile_remove(); +} + +static const struct wmi_device_id inspur_wmi_id_table[] = { + { .guid_string = WMI_INSPUR_POWERMODE_BIOS_GUID }, + { } +}; + +MODULE_DEVICE_TABLE(wmi, inspur_wmi_id_table); + +static struct wmi_driver inspur_wmi_driver = { + .driver = { + .name = "inspur-wmi-platform-profile", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = inspur_wmi_id_table, + .probe = inspur_wmi_probe, + .remove = inspur_wmi_remove, +}; + +module_wmi_driver(inspur_wmi_driver); + +MODULE_AUTHOR("Ai Chao "); +MODULE_DESCRIPTION("Platform Profile Support for Inspur"); +MODULE_LICENSE("GPL"); -- cgit