platform-drivers-x86 for v6.18-1

Highlights
 
 - amd/pmf:
 
   - Add support for adjusting PMF PPT and PPT APU thresholds
 
   - Extend custom BIOS inputs for more policies
 
   - Update ta_pmf_action structure to the latest PMF TA
 
 - arm64: thinkpad-t14s-ec: Add EC driver for ThinkPad T14s Gen6 Snapdragon
 
 - int3472: Increase handshake GPIO delay
 
 - intel/pmc:
 
   - SSRAM support for Lunar Lake and Panther Lake
 
   - Support reading substate requirements data from S0ix blockers
     (for platforms starting from Panther Lake)
 
   - Wildcat Lake support
 
 - intel-uncore-freq:
 
   - Solve duplicate sysfs entry warnings
 
   - Present unique domain ID per package
 
 - portwell-ec:
 
   - Support suspend/resume
 
   - Add hwmon support for voltage and temperature
 
 - redmi-wmi: Add WMI driver for Redmibook keyboard
 
 - think-lmi Certificate support for ThinkCenter
 
 - x86-android-tables + others: Convert away from legacy GPIO APIs
 
 - x86-android-tables:
 
   - Add support for Acer A1-840 tablet
 
   - Fix modules list for Lenovo devices
 
   - Stop using EPROBE_DEFER
 
 - Miscellaneous cleanups / refactoring / improvements
 
 The following is an automated shortlog grouped by driver:
 
 Add WMI driver for Redmibook keyboard:
  - Add WMI driver for Redmibook keyboard
 
 amd/hsmp:
  -  Replace dev_err() with dev_info() for non-fatal errors
 
 amd/pmf:
  -  Add custom BIOS input support for AMD_CPU_ID_PS
  -  Add debug logs for pending requests and custom BIOS inputs
  -  Add helper to verify BIOS input notifications are enable/disable
  -  Add support for adjusting PMF PPT and PPT APU thresholds
  -  Call enact function sooner to process early pending requests
  -  Extend custom BIOS inputs for more policies
  -  Fix the custom bios input handling mechanism
  -  Preserve custom BIOS inputs for evaluating the policies
  -  Remove redundant ternary operators
  -  Update ta_pmf_action structure member
 
 arm64: dts: qcom: x1e80100-t14s:
  -  add EC
 
 arm64: thinkpad-t14s-ec:
  -  new driver
 
 barco-p50-gpio:
  -  use software nodes for gpio-leds/keys
 
 dell_rbu:
  -  fix assignment in if condition warning
 
 dt-bindings: embedded-controller:
  -  Add Lenovo Thinkpad T14s EC
 
 int3472:
  -  Convert int3472_gpio_map to use C99 initializers
  -  Increase ov08x40 handshake GPIO delay to 45 ms
  -  Rework regulator enable-time handling
 
 intel/pmc:
  -  Add Wildcat Lake support to intel_pmc_core
  -  Add Wildcat Lake support to Intel PMC SSRAM Telemetry
  -  Enable SSRAM support for Lunar Lake
  -  Enable SSRAM support for Panther Lake
  -  Improve function to show substate header
  -  Move telemetry endpoint register handling
  -  Replace dev_warn() with dev_dbg()
  -  Show substate requirement for S0ix blockers
  -  use kcalloc() instead of kzalloc()
 
 intel-uncore-freq:
  -  Fix warning in partitioned system
  -  Present unique domain ID per package
 
 meraki-mx100:
  -  Use static device properties
 
 pcengines-apuv2:
  -  Use static device properties
 
 portwell-ec:
  -  Add hwmon support for voltage and temperature
  -  Add suspend/resume support for watchdog
  -  don't print superfluous errors
 
 quickstart:
  -  Use devm_mutex_init()
 
 think-lmi:
  -  Add certificate GUID structure
  -  Add extra TC BIOS error messages
  -  Certificate support for ThinkCenter
 
 wmi-capdata01:
  -  Remove unneeded semicolon
 
 x86-android-tablets:
  -  Add support for Acer A1-840 tablet
  -  convert EDT devices to GPIO references
  -  convert Goodix devices to GPIO references
  -  convert gpio_keys devices to GPIO references
  -  convert HiDeep devices to GPIO references
  -  convert HID-I2C devices to GPIO references
  -  convert int3496 devices to GPIO references
  -  convert Novatek devices to GPIO references
  -  convert Wacom devices to GPIO references
  -  convert wm1502 devices to GPIO references
  -  convert Yoga Tab2 fast charger to GPIO references
  -  Fix modules lists for Lenovo devices
  -  Move Acer info to its own file
  -  remove support for GPIO lookup tables
  -  Remove the use of dev_err_probe()
  -  replace bat_swnode with swnode_group
  -  Simplify lenovo_yoga_tab2_830_1050_exit()
  -  Simplify node-group [un]registration
  -  Stop using EPROBE_DEFER
  -  Update my email address
  -  use swnode_group instead of manual registering
 
 xiaomi-wmi:
  -  Use devm_mutex_init()
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCaNu7iwAKCRBZrE9hU+XO
 MV77AQCJzhaNcx/xhJ4RQZFOZfugIlRljMwmiBOHRAuh5tL59AD/R755jdRsrqgy
 TralVm1h3aeGUeqpxtAxCx8xa+cI6gA=
 =bJ/l
 -----END PGP SIGNATURE-----

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

Pull x86 platform driver updates from Ilpo Järvinen:

 - amd/pmf:
    - Add support for adjusting PMF PPT and PPT APU thresholds
    - Extend custom BIOS inputs for more policies
    - Update ta_pmf_action structure to the latest PMF TA

 - arm64:
    - thinkpad-t14s-ec: Add EC driver for ThinkPad T14s Gen6 Snapdragon

 - int3472:
    - Increase handshake GPIO delay

 - intel/pmc:
    - SSRAM support for Lunar Lake and Panther Lake
    - Support reading substate requirements data from S0ix blockers
      (for platforms starting from Panther Lake)
    - Wildcat Lake support

 - intel-uncore-freq:
    - Solve duplicate sysfs entry warnings
    - Present unique domain ID per package

 - portwell-ec:
    - Support suspend/resume
    - Add hwmon support for voltage and temperature

 - redmi-wmi:
    - Add WMI driver for Redmibook keyboard

 - think-lmi:
    - Certificate support for ThinkCenter

 - x86-android-tables + others:
    - Convert away from legacy GPIO APIs

 - x86-android-tables:
    - Add support for Acer A1-840 tablet
    - Fix modules list for Lenovo devices
    - Stop using EPROBE_DEFER

 - Miscellaneous cleanups / refactoring / improvements

* tag 'platform-drivers-x86-v6.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (63 commits)
  platform/x86: pcengines-apuv2: Use static device properties
  platform/x86: meraki-mx100: Use static device properties
  platform/x86: barco-p50-gpio: use software nodes for gpio-leds/keys
  platform/x86: x86-android-tablets: Stop using EPROBE_DEFER
  platform/x86: x86-android-tablets: Fix modules lists for Lenovo devices
  platform/x86: x86-android-tablets: Simplify lenovo_yoga_tab2_830_1050_exit()
  platform/x86: x86-android-tablets: Add support for Acer A1-840 tablet
  platform/x86: x86-android-tablets: Move Acer info to its own file
  platform/x86: x86-android-tablets: Update my email address
  platform/x86: x86-android-tablets: Simplify node-group [un]registration
  platform/x86: x86-android-tablets: use swnode_group instead of manual registering
  platform/x86: x86-android-tablets: replace bat_swnode with swnode_group
  platform/x86: x86-android-tablets: convert gpio_keys devices to GPIO references
  platform/x86: x86-android-tablets: remove support for GPIO lookup tables
  platform/x86: x86-android-tablets: convert Yoga Tab2 fast charger to GPIO references
  platform/x86: x86-android-tablets: convert HID-I2C devices to GPIO references
  platform/x86: x86-android-tablets: convert wm1502 devices to GPIO references
  platform/x86: x86-android-tablets: convert int3496 devices to GPIO references
  platform/x86: x86-android-tablets: convert EDT devices to GPIO references
  platform/x86: x86-android-tablets: convert Novatek devices to GPIO references
  ...
This commit is contained in:
Linus Torvalds 2025-10-04 15:28:18 -07:00
commit b66451723c
51 changed files with 3390 additions and 892 deletions

View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/embedded-controller/lenovo,thinkpad-t14s-ec.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Lenovo Thinkpad T14s Embedded Controller
maintainers:
- Sebastian Reichel <sre@kernel.org>
description:
The Qualcomm Snapdragon-based Lenovo Thinkpad T14s has an Embedded Controller
(EC) which handles things such as keyboard backlight, LEDs or non-standard
keys.
properties:
compatible:
const: lenovo,thinkpad-t14s-ec
reg:
const: 0x28
interrupts:
maxItems: 1
wakeup-source: true
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |+
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
embedded-controller@28 {
compatible = "lenovo,thinkpad-t14s-ec";
reg = <0x28>;
interrupts-extended = <&tlmm 66 IRQ_TYPE_LEVEL_LOW>;
wakeup-source;
};
};
...

View File

@ -21605,6 +21605,12 @@ S: Maintained
T: git https://github.com/pkshih/rtw.git
F: drivers/net/wireless/realtek/rtw89/
REDMIBOOK WMI DRIVERS
M: Gladyshev Ilya <foxido@foxido.dev>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/redmi-wmi.c
REDPINE WIRELESS DRIVER
L: linux-wireless@vger.kernel.org
S: Orphan
@ -25454,6 +25460,12 @@ W: http://thinkwiki.org/wiki/Ibm-acpi
T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
F: drivers/platform/x86/lenovo/thinkpad_acpi.c
THINKPAD T14S EMBEDDED CONTROLLER DRIVER
M: Sebastian Reichel <sre@kernel.org>
S: Maintained
F: Documentation/devicetree/bindings/embedded-controller/lenovo,thinkpad-t14s-ec.yaml
F: drivers/platform/arm64/lenovo-thinkpad-t14s.c
THINKPAD LMI DRIVER
M: Mark Pearson <mpearson-lenovo@squebb.ca>
L: platform-driver-x86@vger.kernel.org

View File

@ -887,6 +887,24 @@
};
};
&i2c6 {
clock-frequency = <400000>;
status = "okay";
embedded-controller@28 {
compatible = "lenovo,thinkpad-t14s-ec";
reg = <0x28>;
interrupts-extended = <&tlmm 66 IRQ_TYPE_LEVEL_LOW>;
pinctrl-0 = <&ec_int_n_default>;
pinctrl-names = "default";
wakeup-source;
};
};
&i2c7 {
clock-frequency = <400000>;
@ -1269,6 +1287,12 @@
<72 2>, /* Secure EC I2C connection (?) */
<238 1>; /* UFS Reset */
ec_int_n_default: ec-int-n-state {
pins = "gpio66";
function = "gpio";
bias-disable;
};
eusb3_reset_n: eusb3-reset-n-state {
pins = "gpio6";
function = "gpio";

View File

@ -70,4 +70,24 @@ config EC_LENOVO_YOGA_C630
Say M or Y here to include this support.
config EC_LENOVO_THINKPAD_T14S
tristate "Lenovo Thinkpad T14s Embedded Controller driver"
depends on ARCH_QCOM || COMPILE_TEST
depends on I2C
depends on INPUT
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
select SND_CTL_LED if SND
help
Driver for the Embedded Controller in the Qualcomm Snapdragon-based
Lenovo Thinkpad T14s, which provides access to keyboard backlight
and status LEDs.
This driver provides support for the mentioned laptop where this
information is not properly exposed via the standard Qualcomm
devices.
Say M or Y here to include this support.
endif # ARM64_PLATFORM_DEVICES

View File

@ -8,3 +8,4 @@
obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o
obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
obj-$(CONFIG_EC_LENOVO_THINKPAD_T14S) += lenovo-thinkpad-t14s.o

View File

@ -0,0 +1,616 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025, Sebastian Reichel
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define T14S_EC_CMD_ECRD 0x02
#define T14S_EC_CMD_ECWR 0x03
#define T14S_EC_CMD_EVT 0xf0
#define T14S_EC_REG_LED 0x0c
#define T14S_EC_REG_KBD_BL1 0x0d
#define T14S_EC_REG_KBD_BL2 0xe1
#define T14S_EC_KBD_BL1_MASK GENMASK_U8(7, 6)
#define T14S_EC_KBD_BL2_MASK GENMASK_U8(3, 2)
#define T14S_EC_REG_AUD 0x30
#define T14S_EC_MIC_MUTE_LED BIT(5)
#define T14S_EC_SPK_MUTE_LED BIT(6)
#define T14S_EC_EVT_NONE 0x00
#define T14S_EC_EVT_KEY_FN_4 0x13
#define T14S_EC_EVT_KEY_FN_F7 0x16
#define T14S_EC_EVT_KEY_FN_SPACE 0x1f
#define T14S_EC_EVT_KEY_TP_DOUBLE_TAP 0x20
#define T14S_EC_EVT_AC_CONNECTED 0x26
#define T14S_EC_EVT_AC_DISCONNECTED 0x27
#define T14S_EC_EVT_KEY_POWER 0x28
#define T14S_EC_EVT_LID_OPEN 0x2a
#define T14S_EC_EVT_LID_CLOSED 0x2b
#define T14S_EC_EVT_THERMAL_TZ40 0x5c
#define T14S_EC_EVT_THERMAL_TZ42 0x5d
#define T14S_EC_EVT_THERMAL_TZ39 0x5e
#define T14S_EC_EVT_KEY_FN_F12 0x62
#define T14S_EC_EVT_KEY_FN_TAB 0x63
#define T14S_EC_EVT_KEY_FN_F8 0x64
#define T14S_EC_EVT_KEY_FN_F10 0x65
#define T14S_EC_EVT_KEY_FN_F4 0x6a
#define T14S_EC_EVT_KEY_FN_D 0x6b
#define T14S_EC_EVT_KEY_FN_T 0x6c
#define T14S_EC_EVT_KEY_FN_H 0x6d
#define T14S_EC_EVT_KEY_FN_M 0x6e
#define T14S_EC_EVT_KEY_FN_L 0x6f
#define T14S_EC_EVT_KEY_FN_RIGHT_SHIFT 0x71
#define T14S_EC_EVT_KEY_FN_ESC 0x74
#define T14S_EC_EVT_KEY_FN_N 0x79
#define T14S_EC_EVT_KEY_FN_F11 0x7a
#define T14S_EC_EVT_KEY_FN_G 0x7e
/* Hardware LED blink rate is 1 Hz (500ms off, 500ms on) */
#define T14S_EC_BLINK_RATE_ON_OFF_MS 500
/*
* Add a virtual offset on all key event codes for sparse keymap handling,
* since the sparse keymap infrastructure does not map some raw key event
* codes used by the EC. For example 0x16 (T14S_EC_EVT_KEY_FN_F7) is mapped
* to KEY_MUTE if no offset is applied.
*/
#define T14S_EC_KEY_EVT_OFFSET 0x1000
#define T14S_EC_KEY_ENTRY(key, value) \
{ KE_KEY, T14S_EC_KEY_EVT_OFFSET + T14S_EC_EVT_KEY_##key, { value } }
enum t14s_ec_led_status_t {
T14S_EC_LED_OFF = 0x00,
T14S_EC_LED_ON = 0x80,
T14S_EC_LED_BLINK = 0xc0,
};
struct t14s_ec_led_classdev {
struct led_classdev led_classdev;
int led;
enum t14s_ec_led_status_t cache;
struct t14s_ec *ec;
};
struct t14s_ec {
struct regmap *regmap;
struct device *dev;
struct t14s_ec_led_classdev led_pwr_btn;
struct t14s_ec_led_classdev led_chrg_orange;
struct t14s_ec_led_classdev led_chrg_white;
struct t14s_ec_led_classdev led_lid_logo_dot;
struct led_classdev kbd_backlight;
struct led_classdev led_mic_mute;
struct led_classdev led_spk_mute;
struct input_dev *inputdev;
};
static const struct regmap_config t14s_ec_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xff,
};
static int t14s_ec_write(void *context, unsigned int reg,
unsigned int val)
{
struct t14s_ec *ec = context;
struct i2c_client *client = to_i2c_client(ec->dev);
u8 buf[5] = {T14S_EC_CMD_ECWR, reg, 0x00, 0x01, val};
int ret;
ret = i2c_master_send(client, buf, sizeof(buf));
if (ret < 0)
return ret;
return 0;
}
static int t14s_ec_read(void *context, unsigned int reg,
unsigned int *val)
{
struct t14s_ec *ec = context;
struct i2c_client *client = to_i2c_client(ec->dev);
u8 buf[4] = {T14S_EC_CMD_ECRD, reg, 0x00, 0x01};
struct i2c_msg request, response;
u8 result;
int ret;
request.addr = client->addr;
request.flags = I2C_M_STOP;
request.len = sizeof(buf);
request.buf = buf;
response.addr = client->addr;
response.flags = I2C_M_RD;
response.len = 1;
response.buf = &result;
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = __i2c_transfer(client->adapter, &request, 1);
if (ret < 0)
goto out;
ret = __i2c_transfer(client->adapter, &response, 1);
if (ret < 0)
goto out;
*val = result;
ret = 0;
out:
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
return ret;
}
static const struct regmap_bus t14s_ec_regmap_bus = {
.reg_write = t14s_ec_write,
.reg_read = t14s_ec_read,
};
static int t14s_ec_read_evt(struct t14s_ec *ec, u8 *val)
{
struct i2c_client *client = to_i2c_client(ec->dev);
u8 buf[4] = {T14S_EC_CMD_EVT, 0x00, 0x00, 0x01};
struct i2c_msg request, response;
int ret;
request.addr = client->addr;
request.flags = I2C_M_STOP;
request.len = sizeof(buf);
request.buf = buf;
response.addr = client->addr;
response.flags = I2C_M_RD;
response.len = 1;
response.buf = val;
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
ret = __i2c_transfer(client->adapter, &request, 1);
if (ret < 0)
goto out;
ret = __i2c_transfer(client->adapter, &response, 1);
if (ret < 0)
goto out;
ret = 0;
out:
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
return ret;
}
static int t14s_led_set_status(struct t14s_ec *ec,
struct t14s_ec_led_classdev *led,
const enum t14s_ec_led_status_t ledstatus)
{
int ret;
ret = regmap_write(ec->regmap, T14S_EC_REG_LED,
led->led | ledstatus);
if (ret < 0)
return ret;
led->cache = ledstatus;
return 0;
}
static int t14s_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct t14s_ec_led_classdev *led = container_of(led_cdev,
struct t14s_ec_led_classdev, led_classdev);
enum t14s_ec_led_status_t new_state;
if (brightness == LED_OFF)
new_state = T14S_EC_LED_OFF;
else if (led->cache == T14S_EC_LED_BLINK)
new_state = T14S_EC_LED_BLINK;
else
new_state = T14S_EC_LED_ON;
return t14s_led_set_status(led->ec, led, new_state);
}
static int t14s_led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct t14s_ec_led_classdev *led = container_of(led_cdev,
struct t14s_ec_led_classdev, led_classdev);
if (*delay_on == 0 && *delay_off == 0) {
/* Userspace does not provide a blink rate; we can choose it */
*delay_on = T14S_EC_BLINK_RATE_ON_OFF_MS;
*delay_off = T14S_EC_BLINK_RATE_ON_OFF_MS;
} else if ((*delay_on != T14S_EC_BLINK_RATE_ON_OFF_MS) ||
(*delay_off != T14S_EC_BLINK_RATE_ON_OFF_MS))
return -EINVAL;
return t14s_led_set_status(led->ec, led, T14S_EC_LED_BLINK);
}
static int t14s_init_led(struct t14s_ec *ec, struct t14s_ec_led_classdev *led,
u8 id, const char *name)
{
led->led_classdev.name = name;
led->led_classdev.flags = LED_RETAIN_AT_SHUTDOWN;
led->led_classdev.max_brightness = 1;
led->led_classdev.brightness_set_blocking = t14s_led_brightness_set;
led->led_classdev.blink_set = t14s_led_blink_set;
led->ec = ec;
led->led = id;
return devm_led_classdev_register(ec->dev, &led->led_classdev);
}
static int t14s_leds_probe(struct t14s_ec *ec)
{
int ret;
ret = t14s_init_led(ec, &ec->led_pwr_btn, 0, "platform::power");
if (ret)
return ret;
ret = t14s_init_led(ec, &ec->led_chrg_orange, 1,
"platform:amber:battery-charging");
if (ret)
return ret;
ret = t14s_init_led(ec, &ec->led_chrg_white, 2,
"platform:white:battery-full");
if (ret)
return ret;
ret = t14s_init_led(ec, &ec->led_lid_logo_dot, 10,
"platform::lid_logo_dot");
if (ret)
return ret;
return 0;
}
static int t14s_kbd_bl_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct t14s_ec *ec = container_of(led_cdev, struct t14s_ec,
kbd_backlight);
int ret;
u8 val;
val = FIELD_PREP(T14S_EC_KBD_BL1_MASK, brightness);
ret = regmap_update_bits(ec->regmap, T14S_EC_REG_KBD_BL1,
T14S_EC_KBD_BL1_MASK, val);
if (ret < 0)
return ret;
val = FIELD_PREP(T14S_EC_KBD_BL2_MASK, brightness);
ret = regmap_update_bits(ec->regmap, T14S_EC_REG_KBD_BL2,
T14S_EC_KBD_BL2_MASK, val);
if (ret < 0)
return ret;
return 0;
}
static enum led_brightness t14s_kbd_bl_get(struct led_classdev *led_cdev)
{
struct t14s_ec *ec = container_of(led_cdev, struct t14s_ec,
kbd_backlight);
unsigned int val;
int ret;
ret = regmap_read(ec->regmap, T14S_EC_REG_KBD_BL1, &val);
if (ret < 0)
return ret;
return FIELD_GET(T14S_EC_KBD_BL1_MASK, val);
}
static void t14s_kbd_bl_update(struct t14s_ec *ec)
{
enum led_brightness brightness = t14s_kbd_bl_get(&ec->kbd_backlight);
led_classdev_notify_brightness_hw_changed(&ec->kbd_backlight, brightness);
}
static int t14s_kbd_backlight_probe(struct t14s_ec *ec)
{
ec->kbd_backlight.name = "platform::kbd_backlight";
ec->kbd_backlight.flags = LED_BRIGHT_HW_CHANGED;
ec->kbd_backlight.max_brightness = 2;
ec->kbd_backlight.brightness_set_blocking = t14s_kbd_bl_set;
ec->kbd_backlight.brightness_get = t14s_kbd_bl_get;
return devm_led_classdev_register(ec->dev, &ec->kbd_backlight);
}
static enum led_brightness t14s_audio_led_get(struct t14s_ec *ec, u8 led_bit)
{
unsigned int val;
int ret;
ret = regmap_read(ec->regmap, T14S_EC_REG_AUD, &val);
if (ret < 0)
return ret;
return !!(val & led_bit) ? LED_ON : LED_OFF;
}
static enum led_brightness t14s_audio_led_set(struct t14s_ec *ec,
u8 led_mask,
enum led_brightness brightness)
{
return regmap_assign_bits(ec->regmap, T14S_EC_REG_AUD, led_mask, brightness > 0);
}
static enum led_brightness t14s_mic_mute_led_get(struct led_classdev *led_cdev)
{
struct t14s_ec *ec = container_of(led_cdev, struct t14s_ec,
led_mic_mute);
return t14s_audio_led_get(ec, T14S_EC_MIC_MUTE_LED);
}
static int t14s_mic_mute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct t14s_ec *ec = container_of(led_cdev, struct t14s_ec,
led_mic_mute);
return t14s_audio_led_set(ec, T14S_EC_MIC_MUTE_LED, brightness);
}
static enum led_brightness t14s_spk_mute_led_get(struct led_classdev *led_cdev)
{
struct t14s_ec *ec = container_of(led_cdev, struct t14s_ec,
led_spk_mute);
return t14s_audio_led_get(ec, T14S_EC_SPK_MUTE_LED);
}
static int t14s_spk_mute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct t14s_ec *ec = container_of(led_cdev, struct t14s_ec,
led_spk_mute);
return t14s_audio_led_set(ec, T14S_EC_SPK_MUTE_LED, brightness);
}
static int t14s_kbd_audio_led_probe(struct t14s_ec *ec)
{
int ret;
ec->led_mic_mute.name = "platform::micmute";
ec->led_mic_mute.max_brightness = 1;
ec->led_mic_mute.default_trigger = "audio-micmute";
ec->led_mic_mute.brightness_set_blocking = t14s_mic_mute_led_set;
ec->led_mic_mute.brightness_get = t14s_mic_mute_led_get;
ec->led_spk_mute.name = "platform::mute";
ec->led_spk_mute.max_brightness = 1;
ec->led_spk_mute.default_trigger = "audio-mute";
ec->led_spk_mute.brightness_set_blocking = t14s_spk_mute_led_set;
ec->led_spk_mute.brightness_get = t14s_spk_mute_led_get;
ret = devm_led_classdev_register(ec->dev, &ec->led_mic_mute);
if (ret)
return ret;
return devm_led_classdev_register(ec->dev, &ec->led_spk_mute);
}
static const struct key_entry t14s_keymap[] = {
T14S_EC_KEY_ENTRY(FN_4, KEY_SLEEP),
T14S_EC_KEY_ENTRY(FN_N, KEY_VENDOR),
T14S_EC_KEY_ENTRY(FN_F4, KEY_MICMUTE),
T14S_EC_KEY_ENTRY(FN_F7, KEY_SWITCHVIDEOMODE),
T14S_EC_KEY_ENTRY(FN_F8, KEY_PERFORMANCE),
T14S_EC_KEY_ENTRY(FN_F10, KEY_SELECTIVE_SCREENSHOT),
T14S_EC_KEY_ENTRY(FN_F11, KEY_LINK_PHONE),
T14S_EC_KEY_ENTRY(FN_F12, KEY_BOOKMARKS),
T14S_EC_KEY_ENTRY(FN_SPACE, KEY_KBDILLUMTOGGLE),
T14S_EC_KEY_ENTRY(FN_ESC, KEY_FN_ESC),
T14S_EC_KEY_ENTRY(FN_TAB, KEY_ZOOM),
T14S_EC_KEY_ENTRY(FN_RIGHT_SHIFT, KEY_FN_RIGHT_SHIFT),
T14S_EC_KEY_ENTRY(TP_DOUBLE_TAP, KEY_PROG4),
{ KE_END }
};
static int t14s_input_probe(struct t14s_ec *ec)
{
int ret;
ec->inputdev = devm_input_allocate_device(ec->dev);
if (!ec->inputdev)
return -ENOMEM;
ec->inputdev->name = "ThinkPad Extra Buttons";
ec->inputdev->phys = "thinkpad/input0";
ec->inputdev->id.bustype = BUS_HOST;
ec->inputdev->dev.parent = ec->dev;
ret = sparse_keymap_setup(ec->inputdev, t14s_keymap, NULL);
if (ret)
return ret;
return input_register_device(ec->inputdev);
}
static irqreturn_t t14s_ec_irq_handler(int irq, void *data)
{
struct t14s_ec *ec = data;
int ret;
u8 val;
ret = t14s_ec_read_evt(ec, &val);
if (ret < 0) {
dev_err(ec->dev, "Failed to read event\n");
return IRQ_HANDLED;
}
switch (val) {
case T14S_EC_EVT_NONE:
break;
case T14S_EC_EVT_KEY_FN_SPACE:
t14s_kbd_bl_update(ec);
fallthrough;
case T14S_EC_EVT_KEY_FN_F4:
case T14S_EC_EVT_KEY_FN_F7:
case T14S_EC_EVT_KEY_FN_4:
case T14S_EC_EVT_KEY_FN_F8:
case T14S_EC_EVT_KEY_FN_F12:
case T14S_EC_EVT_KEY_FN_TAB:
case T14S_EC_EVT_KEY_FN_F10:
case T14S_EC_EVT_KEY_FN_N:
case T14S_EC_EVT_KEY_FN_F11:
case T14S_EC_EVT_KEY_FN_ESC:
case T14S_EC_EVT_KEY_FN_RIGHT_SHIFT:
case T14S_EC_EVT_KEY_TP_DOUBLE_TAP:
sparse_keymap_report_event(ec->inputdev,
T14S_EC_KEY_EVT_OFFSET + val, 1, true);
break;
case T14S_EC_EVT_AC_CONNECTED:
dev_dbg(ec->dev, "AC connected\n");
break;
case T14S_EC_EVT_AC_DISCONNECTED:
dev_dbg(ec->dev, "AC disconnected\n");
break;
case T14S_EC_EVT_KEY_POWER:
dev_dbg(ec->dev, "power button\n");
break;
case T14S_EC_EVT_LID_OPEN:
dev_dbg(ec->dev, "LID open\n");
break;
case T14S_EC_EVT_LID_CLOSED:
dev_dbg(ec->dev, "LID closed\n");
break;
case T14S_EC_EVT_THERMAL_TZ40:
dev_dbg(ec->dev, "Thermal Zone 40 Status Change Event (CPU/GPU)\n");
break;
case T14S_EC_EVT_THERMAL_TZ42:
dev_dbg(ec->dev, "Thermal Zone 42 Status Change Event (Battery)\n");
break;
case T14S_EC_EVT_THERMAL_TZ39:
dev_dbg(ec->dev, "Thermal Zone 39 Status Change Event (CPU/GPU)\n");
break;
case T14S_EC_EVT_KEY_FN_G:
dev_dbg(ec->dev, "FN + G - toggle double-tapping\n");
break;
case T14S_EC_EVT_KEY_FN_L:
dev_dbg(ec->dev, "FN + L - low performance mode\n");
break;
case T14S_EC_EVT_KEY_FN_M:
dev_dbg(ec->dev, "FN + M - medium performance mode\n");
break;
case T14S_EC_EVT_KEY_FN_H:
dev_dbg(ec->dev, "FN + H - high performance mode\n");
break;
case T14S_EC_EVT_KEY_FN_T:
dev_dbg(ec->dev, "FN + T - toggle intelligent cooling mode\n");
break;
case T14S_EC_EVT_KEY_FN_D:
dev_dbg(ec->dev, "FN + D - toggle privacy guard mode\n");
break;
default:
dev_info(ec->dev, "Unknown EC event: 0x%02x\n", val);
break;
}
return IRQ_HANDLED;
}
static int t14s_ec_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct t14s_ec *ec;
int ret;
ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
if (!ec)
return -ENOMEM;
ec->dev = dev;
ec->regmap = devm_regmap_init(dev, &t14s_ec_regmap_bus,
ec, &t14s_ec_regmap_config);
if (IS_ERR(ec->regmap))
return dev_err_probe(dev, PTR_ERR(ec->regmap),
"Failed to init regmap\n");
ret = devm_request_threaded_irq(dev, client->irq, NULL,
t14s_ec_irq_handler,
IRQF_ONESHOT, dev_name(dev), ec);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get IRQ\n");
ret = t14s_leds_probe(ec);
if (ret < 0)
return ret;
ret = t14s_kbd_backlight_probe(ec);
if (ret < 0)
return ret;
ret = t14s_kbd_audio_led_probe(ec);
if (ret < 0)
return ret;
ret = t14s_input_probe(ec);
if (ret < 0)
return ret;
/*
* Disable wakeup support by default, because the driver currently does
* not support masking any events and the laptop should not wake up when
* the LID is closed.
*/
device_wakeup_disable(dev);
return 0;
}
static const struct of_device_id t14s_ec_of_match[] = {
{ .compatible = "lenovo,thinkpad-t14s-ec" },
{}
};
MODULE_DEVICE_TABLE(of, t14s_ec_of_match);
static const struct i2c_device_id t14s_ec_i2c_id_table[] = {
{ "thinkpad-t14s-ec", },
{}
};
MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table);
static struct i2c_driver t14s_ec_i2c_driver = {
.driver = {
.name = "thinkpad-t14s-ec",
.of_match_table = t14s_ec_of_match,
},
.probe = t14s_ec_probe,
.id_table = t14s_ec_i2c_id_table,
};
module_i2c_driver(t14s_ec_i2c_driver);
MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
MODULE_DESCRIPTION("Lenovo Thinkpad T14s Embedded Controller");
MODULE_LICENSE("GPL");

View File

@ -118,6 +118,18 @@ config XIAOMI_WMI
To compile this driver as a module, choose M here: the module will
be called xiaomi-wmi.
config REDMI_WMI
tristate "Redmibook WMI key driver"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
help
Say Y here if you want support for WMI-based hotkey events on
Xiaomi Redmibook devices.
To compile this driver as a module, choose M here: the module will
be called redmi-wmi.
config GIGABYTE_WMI
tristate "Gigabyte WMI temperature driver"
depends on ACPI_WMI

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
obj-$(CONFIG_REDMI_WMI) += redmi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
# Acer

View File

@ -495,12 +495,12 @@ static int init_acpi(struct device *dev)
if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) {
ret = hsmp_get_tbl_dram_base(sock_ind);
if (ret)
dev_err(dev, "Failed to init metric table\n");
dev_info(dev, "Failed to init metric table\n");
}
ret = hsmp_create_sensor(dev, sock_ind);
if (ret)
dev_err(dev, "Failed to register HSMP sensors with hwmon\n");
dev_info(dev, "Failed to register HSMP sensors with hwmon\n");
dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]);

View File

@ -189,13 +189,13 @@ static int init_platform_device(struct device *dev)
if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) {
ret = hsmp_get_tbl_dram_base(i);
if (ret)
dev_err(dev, "Failed to init metric table\n");
dev_info(dev, "Failed to init metric table\n");
}
/* Register with hwmon interface for reporting power */
ret = hsmp_create_sensor(dev, i);
if (ret)
dev_err(dev, "Failed to register HSMP sensors with hwmon\n");
dev_info(dev, "Failed to register HSMP sensors with hwmon\n");
}
return 0;

View File

@ -161,6 +161,11 @@ int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
return !!(pdev->supported_func & BIT(index - 1));
}
int is_apmf_bios_input_notifications_supported(struct amd_pmf_dev *pdev)
{
return !!(pdev->notifications & CUSTOM_BIOS_INPUT_BITS);
}
int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
struct amd_pmf_apts_granular_output *data, u32 apts_idx)
{
@ -315,12 +320,26 @@ int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req));
}
int apmf_get_sbios_requests_v1(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v1 *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req));
}
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS,
req, sizeof(*req));
}
static void amd_pmf_handle_early_preq(struct amd_pmf_dev *pdev)
{
if (!pdev->cb_flag)
return;
amd_pmf_invoke_cmd_enact(pdev);
pdev->cb_flag = false;
}
static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data)
{
struct amd_pmf_dev *pmf_dev = data;
@ -329,8 +348,32 @@ static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data)
guard(mutex)(&pmf_dev->cb_mutex);
ret = apmf_get_sbios_requests_v2(pmf_dev, &pmf_dev->req);
if (ret)
if (ret) {
dev_err(pmf_dev->dev, "Failed to get v2 SBIOS requests: %d\n", ret);
return;
}
dev_dbg(pmf_dev->dev, "Pending request (preq): 0x%x\n", pmf_dev->req.pending_req);
amd_pmf_handle_early_preq(pmf_dev);
}
static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data)
{
struct amd_pmf_dev *pmf_dev = data;
int ret;
guard(mutex)(&pmf_dev->cb_mutex);
ret = apmf_get_sbios_requests_v1(pmf_dev, &pmf_dev->req1);
if (ret) {
dev_err(pmf_dev->dev, "Failed to get v1 SBIOS requests: %d\n", ret);
return;
}
dev_dbg(pmf_dev->dev, "Pending request (preq1): 0x%x\n", pmf_dev->req1.pending_req);
amd_pmf_handle_early_preq(pmf_dev);
}
static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
@ -385,6 +428,7 @@ static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
pdev->pmf_if_version = output.version;
pdev->notifications = output.notification_mask;
return 0;
}
@ -421,6 +465,11 @@ int apmf_get_dyn_slider_def_dc(struct amd_pmf_dev *pdev, struct apmf_dyn_slider_
return apmf_if_call_store_buffer(pdev, APMF_FUNC_DYN_SLIDER_DC, data, sizeof(*data));
}
static apmf_event_handler_t apmf_event_handlers[] = {
[PMF_IF_V1] = apmf_event_handler_v1,
[PMF_IF_V2] = apmf_event_handler_v2,
};
int apmf_install_handler(struct amd_pmf_dev *pmf_dev)
{
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
@ -440,13 +489,26 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev)
apmf_event_handler(ahandle, 0, pmf_dev);
}
if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) {
if (!pmf_dev->smart_pc_enabled)
return -EINVAL;
switch (pmf_dev->pmf_if_version) {
case PMF_IF_V1:
if (!is_apmf_bios_input_notifications_supported(pmf_dev))
break;
fallthrough;
case PMF_IF_V2:
status = acpi_install_notify_handler(ahandle, ACPI_ALL_NOTIFY,
apmf_event_handler_v2, pmf_dev);
apmf_event_handlers[pmf_dev->pmf_if_version], pmf_dev);
if (ACPI_FAILURE(status)) {
dev_err(pmf_dev->dev, "failed to install notify handler for custom BIOS inputs\n");
dev_err(pmf_dev->dev,
"failed to install notify handler v%d for custom BIOS inputs\n",
pmf_dev->pmf_if_version);
return -ENODEV;
}
break;
default:
break;
}
return 0;
@ -500,8 +562,21 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
is_apmf_func_supported(pmf_dev, APMF_FUNC_SBIOS_REQUESTS))
acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler);
if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2)
acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler_v2);
if (!pmf_dev->smart_pc_enabled)
return;
switch (pmf_dev->pmf_if_version) {
case PMF_IF_V1:
if (!is_apmf_bios_input_notifications_supported(pmf_dev))
break;
fallthrough;
case PMF_IF_V2:
acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY,
apmf_event_handlers[pmf_dev->pmf_if_version]);
break;
default:
break;
}
}
int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)

View File

@ -93,6 +93,8 @@ struct cookie_header {
#define PMF_POLICY_BIOS_OUTPUT_1 10
#define PMF_POLICY_BIOS_OUTPUT_2 11
#define PMF_POLICY_P3T 38
#define PMF_POLICY_PMF_PPT 54
#define PMF_POLICY_PMF_PPT_APU_ONLY 55
#define PMF_POLICY_BIOS_OUTPUT_3 57
#define PMF_POLICY_BIOS_OUTPUT_4 58
#define PMF_POLICY_BIOS_OUTPUT_5 59
@ -116,6 +118,9 @@ struct cookie_header {
#define PMF_IF_V2 2
#define APTS_MAX_STATES 16
#define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7)
typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
/* APTS PMF BIOS Interface */
struct amd_pmf_apts_output {
@ -184,6 +189,24 @@ struct apmf_sbios_req {
u8 skin_temp_hs2;
} __packed;
/* As per APMF spec 1.3 */
struct apmf_sbios_req_v1 {
u16 size;
u32 pending_req;
u8 rsvd;
u8 cql_event;
u8 amt_event;
u32 fppt;
u32 sppt;
u32 sppt_apu_only;
u32 spl;
u32 stt_min_limit;
u8 skin_temp_apu;
u8 skin_temp_hs2;
u8 enable_cnqf;
u32 custom_policy[10];
} __packed;
struct apmf_sbios_req_v2 {
u16 size;
u32 pending_req;
@ -331,6 +354,10 @@ enum power_modes_v2 {
POWER_MODE_V2_MAX,
};
struct pmf_bios_inputs_prev {
u32 custom_bios_inputs[10];
};
struct amd_pmf_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
@ -375,6 +402,10 @@ struct amd_pmf_dev {
struct resource *res;
struct apmf_sbios_req_v2 req; /* To get custom bios pending request */
struct mutex cb_mutex;
u32 notifications;
struct apmf_sbios_req_v1 req1;
struct pmf_bios_inputs_prev cb_prev; /* To preserve custom BIOS inputs */
bool cb_flag; /* To handle first custom BIOS input */
};
struct apmf_sps_prop_granular_v2 {
@ -621,14 +652,35 @@ enum ta_slider {
TA_MAX,
};
enum apmf_smartpc_custom_bios_inputs {
APMF_SMARTPC_CUSTOM_BIOS_INPUT1,
APMF_SMARTPC_CUSTOM_BIOS_INPUT2,
struct amd_pmf_pb_bitmap {
const char *name;
u32 bit_mask;
};
enum apmf_preq_smartpc {
NOTIFY_CUSTOM_BIOS_INPUT1 = 5,
NOTIFY_CUSTOM_BIOS_INPUT2,
static const struct amd_pmf_pb_bitmap custom_bios_inputs[] __used = {
{"NOTIFY_CUSTOM_BIOS_INPUT1", BIT(5)},
{"NOTIFY_CUSTOM_BIOS_INPUT2", BIT(6)},
{"NOTIFY_CUSTOM_BIOS_INPUT3", BIT(7)},
{"NOTIFY_CUSTOM_BIOS_INPUT4", BIT(8)},
{"NOTIFY_CUSTOM_BIOS_INPUT5", BIT(9)},
{"NOTIFY_CUSTOM_BIOS_INPUT6", BIT(10)},
{"NOTIFY_CUSTOM_BIOS_INPUT7", BIT(11)},
{"NOTIFY_CUSTOM_BIOS_INPUT8", BIT(12)},
{"NOTIFY_CUSTOM_BIOS_INPUT9", BIT(13)},
{"NOTIFY_CUSTOM_BIOS_INPUT10", BIT(14)},
};
static const struct amd_pmf_pb_bitmap custom_bios_inputs_v1[] __used = {
{"NOTIFY_CUSTOM_BIOS_INPUT1", BIT(7)},
{"NOTIFY_CUSTOM_BIOS_INPUT2", BIT(8)},
{"NOTIFY_CUSTOM_BIOS_INPUT3", BIT(9)},
{"NOTIFY_CUSTOM_BIOS_INPUT4", BIT(10)},
{"NOTIFY_CUSTOM_BIOS_INPUT5", BIT(11)},
{"NOTIFY_CUSTOM_BIOS_INPUT6", BIT(12)},
{"NOTIFY_CUSTOM_BIOS_INPUT7", BIT(13)},
{"NOTIFY_CUSTOM_BIOS_INPUT8", BIT(14)},
{"NOTIFY_CUSTOM_BIOS_INPUT9", BIT(15)},
{"NOTIFY_CUSTOM_BIOS_INPUT10", BIT(16)},
};
enum platform_type {
@ -677,6 +729,8 @@ struct pmf_action_table {
u32 stt_skintemp_apu; /* in C */
u32 stt_skintemp_hs2; /* in C */
u32 p3t_limit; /* in mW */
u32 pmf_ppt; /* in mW */
u32 pmf_ppt_apu_only; /* in mW */
};
/* Input conditions */
@ -686,8 +740,7 @@ struct ta_pmf_condition_info {
u32 power_slider;
u32 lid_state;
bool user_present;
u32 bios_input1;
u32 bios_input2;
u32 bios_input_1[2];
u32 monitor_count;
u32 rsvd2[2];
u32 bat_design;
@ -711,7 +764,9 @@ struct ta_pmf_condition_info {
u32 workload_type;
u32 display_type;
u32 display_state;
u32 rsvd5[150];
u32 rsvd5_1[17];
u32 bios_input_2[8];
u32 rsvd5[125];
};
struct ta_pmf_load_policy_table {
@ -737,6 +792,7 @@ struct ta_pmf_enact_table {
struct ta_pmf_action {
u32 action_index;
u32 value;
u32 spl_arg;
};
/* Output actions from TA */
@ -778,6 +834,7 @@ int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
u32 fixp_q88_fromint(u32 val);
int is_apmf_bios_input_notifications_supported(struct amd_pmf_dev *pdev);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
@ -805,6 +862,7 @@ void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req);
int apmf_get_sbios_requests_v1(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v1 *req);
int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req);
void amd_pmf_update_2_cql(struct amd_pmf_dev *dev, bool is_cql_event);
@ -828,5 +886,6 @@ int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq
/* Smart PC - TA interfaces */
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev);
#endif /* PMF_H */

View File

@ -70,8 +70,22 @@ static const char *ta_slider_as_str(unsigned int state)
}
}
static u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index)
{
switch (index) {
case 0 ... 1:
return in->ev_info.bios_input_1[index];
case 2 ... 9:
return in->ev_info.bios_input_2[index - 2];
default:
return 0;
}
}
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
int i;
dev_dbg(dev->dev, "==== TA inputs START ====\n");
dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
@ -90,33 +104,81 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *
dev_dbg(dev->dev, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type));
dev_dbg(dev->dev, "Laptop placement: %s\n",
laptop_placement_as_str(in->ev_info.device_state));
dev_dbg(dev->dev, "Custom BIOS input1: %u\n", in->ev_info.bios_input1);
dev_dbg(dev->dev, "Custom BIOS input2: %u\n", in->ev_info.bios_input2);
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
dev_dbg(dev->dev, "Custom BIOS input%d: %u\n", i + 1,
amd_pmf_get_ta_custom_bios_inputs(in, i));
dev_dbg(dev->dev, "==== TA inputs END ====\n");
}
#else
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
#endif
/*
* This helper function sets the appropriate BIOS input value in the TA enact
* table based on the provided index. We need this approach because the custom
* BIOS input array is not continuous, due to the existing TA structure layout.
*/
static void amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table *in, int index, u32 value)
{
switch (index) {
case 0 ... 1:
in->ev_info.bios_input_1[index] = value;
break;
case 2 ... 9:
in->ev_info.bios_input_2[index - 2] = value;
break;
default:
return;
}
}
static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, u32 pending_req,
const struct amd_pmf_pb_bitmap *inputs,
const u32 *custom_policy, struct ta_pmf_enact_table *in)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) {
if (!(pending_req & inputs[i].bit_mask))
continue;
amd_pmf_set_ta_custom_bios_input(in, i, custom_policy[i]);
pdev->cb_prev.custom_bios_inputs[i] = custom_policy[i];
dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, custom_policy[i]);
}
}
static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
struct ta_pmf_enact_table *in)
{
if (!pdev->req.pending_req)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]);
if (!(pdev->req.pending_req || pdev->req1.pending_req))
return;
switch (pdev->req.pending_req) {
case BIT(NOTIFY_CUSTOM_BIOS_INPUT1):
in->ev_info.bios_input1 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT1];
if (!pdev->smart_pc_enabled)
return;
switch (pdev->pmf_if_version) {
case PMF_IF_V1:
if (!is_apmf_bios_input_notifications_supported(pdev))
return;
amd_pmf_update_bios_inputs(pdev, pdev->req1.pending_req, custom_bios_inputs_v1,
pdev->req1.custom_policy, in);
break;
case BIT(NOTIFY_CUSTOM_BIOS_INPUT2):
in->ev_info.bios_input2 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT2];
case PMF_IF_V2:
amd_pmf_update_bios_inputs(pdev, pdev->req.pending_req, custom_bios_inputs,
pdev->req.custom_policy, in);
break;
default:
dev_dbg(pdev->dev, "Invalid preq for BIOS input: 0x%x\n", pdev->req.pending_req);
break;
}
/* Clear pending requests after handling */
memset(&pdev->req, 0, sizeof(pdev->req));
memset(&pdev->req1, 0, sizeof(pdev->req1));
}
static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)

View File

@ -283,7 +283,7 @@ int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
bool is_pprof_balanced(struct amd_pmf_dev *pmf)
{
return (pmf->current_profile == PLATFORM_PROFILE_BALANCED) ? true : false;
return pmf->current_profile == PLATFORM_PROFILE_BALANCED;
}
static int amd_pmf_profile_get(struct device *dev,

View File

@ -147,6 +147,22 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
}
break;
case PMF_POLICY_PMF_PPT:
if (dev->prev_data->pmf_ppt != val) {
amd_pmf_send_cmd(dev, SET_PMF_PPT, false, val, NULL);
dev_dbg(dev->dev, "update PMF PPT: %u\n", val);
dev->prev_data->pmf_ppt = val;
}
break;
case PMF_POLICY_PMF_PPT_APU_ONLY:
if (dev->prev_data->pmf_ppt_apu_only != val) {
amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false, val, NULL);
dev_dbg(dev->dev, "update PMF PPT APU ONLY: %u\n", val);
dev->prev_data->pmf_ppt_apu_only = val;
}
break;
case PMF_POLICY_SYSTEM_STATE:
switch (val) {
case 0:
@ -209,7 +225,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
}
}
static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev)
int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev)
{
struct ta_pmf_shared_memory *ta_sm = NULL;
struct ta_pmf_enact_result *out = NULL;
@ -561,8 +577,10 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
ret = amd_pmf_start_policy_engine(dev);
dev_dbg(dev->dev, "start policy engine ret: %d\n", ret);
status = ret == TA_PMF_TYPE_SUCCESS;
if (status)
if (status) {
dev->cb_flag = true;
break;
}
amd_pmf_tee_deinit(dev);
}

View File

@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/io.h>
@ -18,10 +19,11 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio_keys.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/input.h>
#include <linux/gpio/property.h>
#include <linux/input-event-codes.h>
#include <linux/property.h>
#define DRIVER_NAME "barco-p50-gpio"
@ -78,44 +80,57 @@ static const char * const gpio_names[] = {
[P50_GPIO_LINE_BTN] = "identify-button",
};
static struct gpiod_lookup_table p50_gpio_led_table = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
{}
}
static const struct software_node gpiochip_node = {
.name = DRIVER_NAME,
};
/* GPIO LEDs */
static struct gpio_led leds[] = {
{ .name = "identify" }
static const struct software_node gpio_leds_node = {
.name = "gpio-leds-identify",
};
static struct gpio_led_platform_data leds_pdata = {
.num_leds = ARRAY_SIZE(leds),
.leds = leds,
static const struct property_entry identify_led_props[] = {
PROPERTY_ENTRY_GPIO("gpios", &gpiochip_node, P50_GPIO_LINE_LED, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node identify_led_node = {
.parent = &gpio_leds_node,
.name = "identify",
.properties = identify_led_props,
};
/* GPIO keyboard */
static struct gpio_keys_button buttons[] = {
{
.code = KEY_VENDOR,
.gpio = P50_GPIO_LINE_BTN,
.active_low = 1,
.type = EV_KEY,
.value = 1,
},
static const struct property_entry gpio_keys_props[] = {
PROPERTY_ENTRY_STRING("label", "identify"),
PROPERTY_ENTRY_U32("poll-interval", 100),
{ }
};
static struct gpio_keys_platform_data keys_pdata = {
.buttons = buttons,
.nbuttons = ARRAY_SIZE(buttons),
.poll_interval = 100,
.rep = 0,
.name = "identify",
static const struct software_node gpio_keys_node = {
.name = "gpio-keys-identify",
.properties = gpio_keys_props,
};
static struct property_entry vendor_key_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_VENDOR),
PROPERTY_ENTRY_GPIO("gpios", &gpiochip_node, P50_GPIO_LINE_BTN, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node vendor_key_node = {
.parent = &gpio_keys_node,
.properties = vendor_key_props,
};
static const struct software_node *p50_swnodes[] = {
&gpiochip_node,
&gpio_leds_node,
&identify_led_node,
&gpio_keys_node,
&vendor_key_node,
NULL
};
/* low level access routines */
@ -285,6 +300,16 @@ static int p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
static int p50_gpio_probe(struct platform_device *pdev)
{
struct platform_device_info key_info = {
.name = "gpio-keys-polled",
.id = PLATFORM_DEVID_NONE,
.parent = &pdev->dev,
};
struct platform_device_info led_info = {
.name = "leds-gpio",
.id = PLATFORM_DEVID_NONE,
.parent = &pdev->dev,
};
struct p50_gpio *p50;
struct resource *res;
int ret;
@ -339,25 +364,20 @@ static int p50_gpio_probe(struct platform_device *pdev)
return ret;
}
gpiod_add_lookup_table(&p50_gpio_led_table);
p50->leds_pdev = platform_device_register_data(&pdev->dev,
"leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
ret = software_node_register_node_group(p50_swnodes);
if (ret)
return dev_err_probe(&pdev->dev, ret, "failed to register software nodes");
led_info.fwnode = software_node_fwnode(&gpio_leds_node);
p50->leds_pdev = platform_device_register_full(&led_info);
if (IS_ERR(p50->leds_pdev)) {
ret = PTR_ERR(p50->leds_pdev);
dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
goto err_leds;
}
/* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
buttons[0].gpio += p50->gc.base;
p50->keys_pdev =
platform_device_register_data(&pdev->dev, "gpio-keys-polled",
PLATFORM_DEVID_NONE,
&keys_pdata, sizeof(keys_pdata));
key_info.fwnode = software_node_fwnode(&gpio_keys_node);
p50->keys_pdev = platform_device_register_full(&key_info);
if (IS_ERR(p50->keys_pdev)) {
ret = PTR_ERR(p50->keys_pdev);
dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
@ -369,7 +389,7 @@ static int p50_gpio_probe(struct platform_device *pdev)
err_keys:
platform_device_unregister(p50->leds_pdev);
err_leds:
gpiod_remove_lookup_table(&p50_gpio_led_table);
software_node_unregister_node_group(p50_swnodes);
return ret;
}
@ -381,7 +401,7 @@ static void p50_gpio_remove(struct platform_device *pdev)
platform_device_unregister(p50->keys_pdev);
platform_device_unregister(p50->leds_pdev);
gpiod_remove_lookup_table(&p50_gpio_led_table);
software_node_unregister_node_group(p50_swnodes);
}
static struct platform_driver p50_gpio_driver = {

View File

@ -232,7 +232,8 @@ static int packetize_data(const u8 *data, size_t length)
done = 1;
}
if ((rc = create_packet(temp, packet_length)))
rc = create_packet(temp, packet_length);
if (rc)
return rc;
pr_debug("%p:%td\n", temp, (end - temp));
@ -276,7 +277,7 @@ static int do_packet_read(char *data, struct packet_data *newpacket,
return bytes_copied;
}
static int packet_read_list(char *data, size_t * pread_length)
static int packet_read_list(char *data, size_t *pread_length)
{
struct packet_data *newpacket;
int temp_count = 0;
@ -445,7 +446,8 @@ static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count)
bytes_left = rbu_data.imagesize - pos;
data_length = min(bytes_left, count);
if ((retval = packet_read_list(ptempBuf, &data_length)) < 0)
retval = packet_read_list(ptempBuf, &data_length);
if (retval < 0)
goto read_rbu_data_exit;
if ((pos + count) > rbu_data.imagesize) {

View File

@ -129,6 +129,7 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
* @hid: The ACPI HID of the device without the instance number e.g. INT347E
* @type_from: The GPIO type from ACPI ?SDT
* @type_to: The assigned GPIO type, typically same as @type_from
* @enable_time_us: Enable time in usec for GPIOs mapped to regulators
* @con_id: The name of the GPIO for the device
* @polarity_low: GPIO_ACTIVE_LOW true if the @polarity_low is true,
* GPIO_ACTIVE_HIGH otherwise
@ -138,18 +139,36 @@ struct int3472_gpio_map {
u8 type_from;
u8 type_to;
bool polarity_low;
unsigned int enable_time_us;
const char *con_id;
};
static const struct int3472_gpio_map int3472_gpio_map[] = {
/* mt9m114 designs declare a powerdown pin which controls the regulators */
{ "INT33F0", INT3472_GPIO_TYPE_POWERDOWN, INT3472_GPIO_TYPE_POWER_ENABLE, false, "vdd" },
/* ov7251 driver / DT-bindings expect "enable" as con_id for reset */
{ "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" },
{ /* mt9m114 designs declare a powerdown pin which controls the regulators */
.hid = "INT33F0",
.type_from = INT3472_GPIO_TYPE_POWERDOWN,
.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
.con_id = "vdd",
.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
},
{ /* ov7251 driver / DT-bindings expect "enable" as con_id for reset */
.hid = "INT347E",
.type_from = INT3472_GPIO_TYPE_RESET,
.type_to = INT3472_GPIO_TYPE_RESET,
.con_id = "enable",
},
{ /* ov08x40's handshake pin needs a 45 ms delay on some HP laptops */
.hid = "OVTI08F4",
.type_from = INT3472_GPIO_TYPE_HANDSHAKE,
.type_to = INT3472_GPIO_TYPE_HANDSHAKE,
.con_id = "dvdd",
.enable_time_us = 45 * USEC_PER_MSEC,
},
};
static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
const char **con_id, unsigned long *gpio_flags)
const char **con_id, unsigned long *gpio_flags,
unsigned int *enable_time_us)
{
struct acpi_device *adev = int3472->sensor;
unsigned int i;
@ -173,9 +192,12 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
*gpio_flags = int3472_gpio_map[i].polarity_low ?
GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH;
*con_id = int3472_gpio_map[i].con_id;
*enable_time_us = int3472_gpio_map[i].enable_time_us;
return;
}
*enable_time_us = GPIO_REGULATOR_ENABLE_TIME;
switch (*type) {
case INT3472_GPIO_TYPE_RESET:
*con_id = "reset";
@ -204,6 +226,8 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
case INT3472_GPIO_TYPE_HANDSHAKE:
*con_id = "dvdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
/* Setups using a handshake pin need 25 ms enable delay */
*enable_time_us = 25 * USEC_PER_MSEC;
break;
default:
*con_id = "unknown";
@ -249,13 +273,15 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
void *data)
{
struct int3472_discrete_device *int3472 = data;
const char *second_sensor = NULL;
struct acpi_resource_gpio *agpio;
unsigned int enable_time_us;
u8 active_value, pin, type;
unsigned long gpio_flags;
union acpi_object *obj;
struct gpio_desc *gpio;
const char *err_msg;
const char *con_id;
unsigned long gpio_flags;
int ret;
if (!acpi_gpio_get_io_resource(ares, &agpio))
@ -278,7 +304,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);
int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags);
int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags, &enable_time_us);
pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
/* Pin field is not really used under Windows and wraps around at 8 bits */
@ -328,21 +354,13 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
ret = skl_int3472_register_regulator(int3472, gpio,
GPIO_REGULATOR_ENABLE_TIME,
con_id,
int3472->quirks.avdd_second_sensor);
if (ret)
err_msg = "Failed to map power-enable to sensor\n";
break;
second_sensor = int3472->quirks.avdd_second_sensor;
fallthrough;
case INT3472_GPIO_TYPE_HANDSHAKE:
/* Setups using a handshake pin need 25 ms enable delay */
ret = skl_int3472_register_regulator(int3472, gpio,
25 * USEC_PER_MSEC,
con_id, NULL);
ret = skl_int3472_register_regulator(int3472, gpio, enable_time_us,
con_id, second_sensor);
if (ret)
err_msg = "Failed to map handshake to sensor\n";
err_msg = "Failed to register regulator\n";
break;
default: /* Never reached */

View File

@ -4,7 +4,7 @@
#
intel_pmc_core-y := core.o spt.o cnp.o icl.o \
tgl.o adl.o mtl.o arl.o lnl.o ptl.o
tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
intel_pmc_core_pltdrv-y := pltdrv.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o

View File

@ -725,9 +725,11 @@ struct pmc_dev_info arl_pmc_dev = {
.dmu_guid = ARL_PMT_DMU_GUID,
.regmap_list = arl_pmc_info_list,
.map = &arl_socs_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
.suspend = cnl_suspend,
.resume = arl_resume,
.init = arl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
};
struct pmc_dev_info arl_h_pmc_dev = {
@ -735,7 +737,9 @@ struct pmc_dev_info arl_h_pmc_dev = {
.dmu_guid = ARL_PMT_DMU_GUID,
.regmap_list = arl_pmc_info_list,
.map = &mtl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
.suspend = cnl_suspend,
.resume = arl_h_resume,
.init = arl_h_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
};

View File

@ -11,6 +11,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
enum header_type {
HEADER_STATUS,
HEADER_VALUE,
};
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
@ -828,19 +833,86 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs);
static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index)
static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index,
enum header_type type)
{
struct pmc_dev *pmcdev = s->private;
int mode;
seq_printf(s, "%30s |", "Element");
seq_printf(s, "%40s |", "Element");
pmc_for_each_mode(mode, pmcdev)
seq_printf(s, " %9s |", pmc_lpm_modes[mode]);
seq_printf(s, " %9s |", "Status");
seq_printf(s, " %11s |\n", "Live Status");
if (type == HEADER_STATUS) {
seq_printf(s, " %9s |", "Status");
seq_printf(s, " %11s |\n", "Live Status");
} else {
seq_printf(s, " %9s |\n", "Value");
}
}
static int pmc_core_substate_blk_req_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
unsigned int pmc_idx;
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) {
const struct pmc_bit_map **maps;
unsigned int arr_size, r_idx;
u32 offset, counter;
u32 *lpm_req_regs;
struct pmc *pmc;
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc || !pmc->lpm_req_regs)
continue;
lpm_req_regs = pmc->lpm_req_regs;
maps = pmc->map->s0ix_blocker_maps;
offset = pmc->map->s0ix_blocker_offset;
arr_size = pmc_core_lpm_get_arr_size(maps);
/* Display the header */
pmc_core_substate_req_header_show(s, pmc_idx, HEADER_VALUE);
for (r_idx = 0; r_idx < arr_size; r_idx++) {
const struct pmc_bit_map *map;
for (map = maps[r_idx]; map->name; map++) {
int mode;
if (!map->blk)
continue;
counter = pmc_core_reg_read(pmc, offset);
seq_printf(s, "pmc%u: %34s |", pmc_idx, map->name);
pmc_for_each_mode(mode, pmcdev) {
bool required = *lpm_req_regs & BIT(mode);
seq_printf(s, " %9s |", required ? "Required" : " ");
}
seq_printf(s, " %9u |\n", counter);
offset += map->blk * S0IX_BLK_SIZE;
lpm_req_regs++;
}
}
}
return 0;
}
static int pmc_core_substate_blk_req_open(struct inode *inode, struct file *file)
{
return single_open(file, pmc_core_substate_blk_req_show, inode->i_private);
}
const struct file_operations pmc_core_substate_blk_req_fops = {
.owner = THIS_MODULE,
.open = pmc_core_substate_blk_req_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
@ -872,7 +944,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
continue;
/* Display the header */
pmc_core_substate_req_header_show(s, pmc_index);
pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS);
/* Loop over maps */
for (mp = 0; mp < num_maps; mp++) {
@ -910,7 +982,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
}
/* Display the element name in the first column */
seq_printf(s, "pmc%d: %26s |", pmc_index, map[i].name);
seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name);
/* Loop over the enabled states and display if required */
pmc_for_each_mode(mode, pmcdev) {
@ -931,7 +1003,19 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs);
static int pmc_core_substate_req_regs_open(struct inode *inode, struct file *file)
{
return single_open(file, pmc_core_substate_req_regs_show, inode->i_private);
}
const struct file_operations pmc_core_substate_req_regs_fops = {
.owner = THIS_MODULE,
.open = pmc_core_substate_req_regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static unsigned int pmc_core_get_crystal_freq(void)
{
@ -1160,7 +1244,7 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev)
for (mode = 0; mode < LPM_MAX_NUM_MODES; mode++)
pri_order[mode_order[mode]] = mode;
else
dev_warn(&pmcdev->pdev->dev,
dev_dbg(&pmcdev->pdev->dev,
"Assuming a default substate order for this platform\n");
/*
@ -1264,7 +1348,7 @@ static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
debugfs_remove_recursive(pmcdev->dbgfs_dir);
}
static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
struct pmc *primary_pmc = pmcdev->pmcs[PMC_IDX_MAIN];
struct dentry *dir;
@ -1331,7 +1415,7 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
if (primary_pmc->lpm_req_regs) {
debugfs_create_file("substate_requirements", 0444,
pmcdev->dbgfs_dir, pmcdev,
&pmc_core_substate_req_regs_fops);
pmc_dev_info->sub_req_show);
}
if (primary_pmc->map->pson_residency_offset && pmc_core_is_pson_residency_enabled(pmcdev)) {
@ -1399,36 +1483,22 @@ static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *m
* +----+---------------------------------------------------------+
*
*/
static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct pci_dev *pcidev)
int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep)
{
struct telem_endpoint *ep;
const u8 *lpm_indices;
int num_maps, mode_offset = 0;
int ret, mode;
int lpm_size;
u32 guid;
lpm_indices = pmc->map->lpm_reg_index;
num_maps = pmc->map->lpm_num_maps;
lpm_size = LPM_MAX_NUM_MODES * num_maps;
guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map);
if (!guid)
return -ENXIO;
ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0);
if (IS_ERR(ep)) {
dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep);
return -EPROBE_DEFER;
}
pmc->lpm_req_regs = devm_kzalloc(&pmcdev->pdev->dev,
lpm_size * sizeof(u32),
GFP_KERNEL);
if (!pmc->lpm_req_regs) {
ret = -ENOMEM;
goto unregister_ep;
}
if (!pmc->lpm_req_regs)
return -ENOMEM;
mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET;
pmc_for_each_mode(mode, pmcdev) {
@ -1442,34 +1512,74 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct
if (ret) {
dev_err(&pmcdev->pdev->dev,
"couldn't read Low Power Mode requirements: %d\n", ret);
goto unregister_ep;
return ret;
}
++req_offset;
}
mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET;
}
unregister_ep:
pmt_telem_unregister_endpoint(ep);
return ret;
}
static int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev, int func)
int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
struct telem_endpoint *ep)
{
struct pci_dev *pcidev __free(pci_dev_put) = NULL;
unsigned int i;
u32 num_blocker, sample_offset;
unsigned int index;
u32 *req_offset;
int ret;
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func));
num_blocker = pmc->map->num_s0ix_blocker;
sample_offset = pmc->map->blocker_req_offset;
pmc->lpm_req_regs = devm_kcalloc(&pmcdev->pdev->dev, num_blocker,
sizeof(u32), GFP_KERNEL);
if (!pmc->lpm_req_regs)
return -ENOMEM;
req_offset = pmc->lpm_req_regs;
for (index = 0; index < num_blocker; index++, req_offset++) {
ret = pmt_telem_read32(ep, index + sample_offset, req_offset, 1);
if (ret) {
dev_err(&pmcdev->pdev->dev,
"couldn't read Low Power Mode requirements: %d\n", ret);
return ret;
}
}
return 0;
}
static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
struct pci_dev *pcidev __free(pci_dev_put) = NULL;
struct telem_endpoint *ep;
unsigned int i;
u32 guid;
int ret;
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func));
if (!pcidev)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
if (!pmcdev->pmcs[i])
struct pmc *pmc;
pmc = pmcdev->pmcs[i];
if (!pmc)
continue;
ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i], pcidev);
guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map);
if (!guid)
return -ENXIO;
ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0);
if (IS_ERR(ep)) {
dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep);
return -EPROBE_DEFER;
}
ret = pmc_dev_info->sub_req(pmcdev, pmc, ep);
pmt_telem_unregister_endpoint(ep);
if (ret)
return ret;
}
@ -1583,7 +1693,7 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid);
if (ssram) {
ret = pmc_core_ssram_get_lpm_reqs(pmcdev, pmc_dev_info->pci_func);
ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info);
if (ret)
goto unmap_regbase;
}
@ -1632,6 +1742,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
X86_MATCH_VFM(INTEL_ARROWLAKE_U, &arl_h_pmc_dev),
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev),
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev),
X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev),
{}
};
@ -1758,7 +1869,7 @@ static int pmc_core_probe(struct platform_device *pdev)
pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(primary_pmc);
pmc_core_do_dmi_quirks(primary_pmc);
pmc_core_dbgfs_register(pmcdev);
pmc_core_dbgfs_register(pmcdev, pmc_dev_info);
pm_report_max_hw_sleep(FIELD_MAX(SLP_S0_RES_COUNTER_MASK) *
pmc_core_adjust_slp_s0_step(primary_pmc, 1));

View File

@ -297,6 +297,12 @@ enum ppfear_regs {
#define PTL_PMC_LTR_CUR_ASLT 0x1C28
#define PTL_PMC_LTR_CUR_PLT 0x1C2C
#define PTL_PCD_PMC_MMIO_REG_LEN 0x31A8
#define PTL_NUM_S0IX_BLOCKER 106
#define PTL_BLK_REQ_OFFSET 55
/* Wildcat Lake */
#define WCL_PMC_LTR_RESERVED 0x1B64
#define WCL_PCD_PMC_MMIO_REG_LEN 0x3178
/* SSRAM PMC Device ID */
/* LNL */
@ -306,6 +312,9 @@ enum ppfear_regs {
#define PMC_DEVID_PTL_PCDH 0xe37f
#define PMC_DEVID_PTL_PCDP 0xe47f
/* WCL */
#define PMC_DEVID_WCL_PCDN 0x4d7f
/* ARL */
#define PMC_DEVID_ARL_SOCM 0x777f
#define PMC_DEVID_ARL_SOCS 0xae7f
@ -344,6 +353,8 @@ struct pmc_bit_map {
* @pm_read_disable_bit: Bit index to read PMC_READ_DISABLE
* @slps0_dbg_offset: PWRMBASE offset to SLP_S0_DEBUG_REG*
* @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter
* @num_s0ix_blocker: Number of S0ix blockers
* @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table
*
* Each PCH has unique set of register offsets and bit indexes. This structure
* captures them to have a common implementation.
@ -369,6 +380,8 @@ struct pmc_reg_map {
const u32 ltr_ignore_max;
const u32 pm_vric1_offset;
const u32 s0ix_blocker_offset;
const u32 num_s0ix_blocker;
const u32 blocker_req_offset;
/* Low Power Mode registers */
const int lpm_num_maps;
const int lpm_num_modes;
@ -474,18 +487,22 @@ enum pmc_index {
* SSRAM support.
* @map: Pointer to a pmc_reg_map struct that contains platform
* specific attributes of the primary PMC
* @sub_req_show: File operations to show substate requirements
* @suspend: Function to perform platform specific suspend
* @resume: Function to perform platform specific resume
* @init: Function to perform platform specific init action
* @sub_req: Function to achieve low power mode substate requirements
*/
struct pmc_dev_info {
u8 pci_func;
u32 dmu_guid;
struct pmc_info *regmap_list;
const struct pmc_reg_map *map;
const struct file_operations *sub_req_show;
void (*suspend)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
};
extern const struct pmc_bit_map msr_map[];
@ -505,6 +522,9 @@ extern const struct pmc_bit_map mtl_socm_vnn_misc_status_map[];
extern const struct pmc_bit_map mtl_socm_signal_status_map[];
extern const struct pmc_reg_map mtl_socm_reg_map;
extern const struct pmc_reg_map mtl_ioep_reg_map;
extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[];
extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[];
extern const struct pmc_bit_map ptl_pcdp_signal_status_map[];
void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
@ -528,9 +548,16 @@ extern struct pmc_dev_info arl_pmc_dev;
extern struct pmc_dev_info arl_h_pmc_dev;
extern struct pmc_dev_info lnl_pmc_dev;
extern struct pmc_dev_info ptl_pmc_dev;
extern struct pmc_dev_info wcl_pmc_dev;
void cnl_suspend(struct pmc_dev *pmcdev);
int cnl_resume(struct pmc_dev *pmcdev);
int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
struct telem_endpoint *ep);
extern const struct file_operations pmc_core_substate_req_regs_fops;
extern const struct file_operations pmc_core_substate_blk_req_fops;
#define pmc_for_each_mode(mode, pmcdev) \
for (unsigned int __i = 0, __cond; \

View File

@ -13,6 +13,10 @@
#include "core.h"
#define SOCM_LPM_REQ_GUID 0x15099748
static const u8 LNL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20};
static const struct pmc_bit_map lnl_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
@ -528,6 +532,16 @@ static const struct pmc_reg_map lnl_socm_reg_map = {
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.s0ix_blocker_maps = lnl_blk_maps,
.s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
.lpm_reg_index = LNL_LPM_REG_INDEX,
};
static struct pmc_info lnl_pmc_info_list[] = {
{
.guid = SOCM_LPM_REQ_GUID,
.devid = PMC_DEVID_LNL_SOCM,
.map = &lnl_socm_reg_map,
},
{}
};
#define LNL_NPU_PCI_DEV 0x643e
@ -557,8 +571,12 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info lnl_pmc_dev = {
.pci_func = 2,
.regmap_list = lnl_pmc_info_list,
.map = &lnl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
.suspend = cnl_suspend,
.resume = lnl_resume,
.init = lnl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
};

View File

@ -997,7 +997,9 @@ struct pmc_dev_info mtl_pmc_dev = {
.dmu_guid = MTL_PMT_DMU_GUID,
.regmap_list = mtl_pmc_info_list,
.map = &mtl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
.suspend = cnl_suspend,
.resume = mtl_resume,
.init = mtl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
};

View File

@ -10,6 +10,17 @@
#include "core.h"
/* PMC SSRAM PMT Telemetry GUIDS */
#define PCDP_LPM_REQ_GUID 0x47179370
/*
* Die Mapping to Product.
* Product PCDDie
* PTL-H PCD-H
* PTL-P PCD-P
* PTL-U PCD-P
*/
static const struct pmc_bit_map ptl_pcdp_pfear_map[] = {
{"PMC_0", BIT(0)},
{"FUSE_OSSE", BIT(1)},
@ -162,7 +173,7 @@ static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = {
{}
};
static const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = {
const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = {
{"AON2_OFF_STS", BIT(0), 1},
{"AON3_OFF_STS", BIT(1), 0},
{"AON4_OFF_STS", BIT(2), 1},
@ -382,7 +393,7 @@ static const struct pmc_bit_map ptl_pcdp_vnn_req_status_2_map[] = {
{}
};
static const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = {
const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = {
{"DTS0_VNN_REQ_STS", BIT(7), 0},
{"GPIOCOM5_VNN_REQ_STS", BIT(11), 1},
{}
@ -421,7 +432,7 @@ static const struct pmc_bit_map ptl_pcdp_vnn_misc_status_map[] = {
{}
};
static const struct pmc_bit_map ptl_pcdp_signal_status_map[] = {
const struct pmc_bit_map ptl_pcdp_signal_status_map[] = {
{"LSX_Wake0_STS", BIT(0), 0},
{"LSX_Wake1_STS", BIT(1), 0},
{"LSX_Wake2_STS", BIT(2), 0},
@ -515,6 +526,22 @@ static const struct pmc_reg_map ptl_pcdp_reg_map = {
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.s0ix_blocker_maps = ptl_pcdp_blk_maps,
.s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
.num_s0ix_blocker = PTL_NUM_S0IX_BLOCKER,
.blocker_req_offset = PTL_BLK_REQ_OFFSET,
};
static struct pmc_info ptl_pmc_info_list[] = {
{
.guid = PCDP_LPM_REQ_GUID,
.devid = PMC_DEVID_PTL_PCDH,
.map = &ptl_pcdp_reg_map,
},
{
.guid = PCDP_LPM_REQ_GUID,
.devid = PMC_DEVID_PTL_PCDP,
.map = &ptl_pcdp_reg_map,
},
{}
};
#define PTL_NPU_PCI_DEV 0xb03e
@ -543,8 +570,12 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info ptl_pmc_dev = {
.pci_func = 2,
.regmap_list = ptl_pmc_info_list,
.map = &ptl_pcdp_reg_map,
.sub_req_show = &pmc_core_substate_blk_req_fops,
.suspend = cnl_suspend,
.resume = ptl_resume,
.init = ptl_core_init,
.sub_req = pmc_core_pmt_get_blk_sub_req,
};

View File

@ -190,6 +190,7 @@ static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) },
{ }
};
MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids);

View File

@ -273,8 +273,8 @@ void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev)
addr = (u32 *)out_obj->buffer.pointer;
lpm_req_regs = devm_kzalloc(&pdev->dev, lpm_size * sizeof(u32),
GFP_KERNEL);
lpm_req_regs = devm_kcalloc(&pdev->dev, lpm_size, sizeof(u32),
GFP_KERNEL);
if (!lpm_req_regs)
goto free_acpi_obj;

View File

@ -0,0 +1,486 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains platform specific structure definitions
* and init function used by Wildcat Lake PCH.
*
* Copyright (c) 2025, Intel Corporation.
*/
#include <linux/bits.h>
#include <linux/pci.h>
#include "core.h"
static const struct pmc_bit_map wcl_pcdn_pfear_map[] = {
{"PMC_0", BIT(0)},
{"FUSE_OSSE", BIT(1)},
{"ESPISPI", BIT(2)},
{"XHCI", BIT(3)},
{"SPA", BIT(4)},
{"RSVD", BIT(5)},
{"MPFPW2", BIT(6)},
{"GBE", BIT(7)},
{"SBR16B21", BIT(0)},
{"SBR16B5", BIT(1)},
{"SBR8B1", BIT(2)},
{"SBR8B0", BIT(3)},
{"P2SB0", BIT(4)},
{"D2D_DISP_1", BIT(5)},
{"LPSS", BIT(6)},
{"LPC", BIT(7)},
{"SMB", BIT(0)},
{"ISH", BIT(1)},
{"DBG_SBR16B", BIT(2)},
{"NPK_0", BIT(3)},
{"D2D_NOC_1", BIT(4)},
{"FIA_P", BIT(5)},
{"FUSE", BIT(6)},
{"DBG_PSF", BIT(7)},
{"DISP_PGA1", BIT(0)},
{"XDCI", BIT(1)},
{"EXI", BIT(2)},
{"CSE", BIT(3)},
{"KVMCC", BIT(4)},
{"PMT", BIT(5)},
{"CLINK", BIT(6)},
{"PTIO", BIT(7)},
{"USBR0", BIT(0)},
{"SBR16B22", BIT(1)},
{"SMT1", BIT(2)},
{"MPFPW1", BIT(3)},
{"SMS2", BIT(4)},
{"SMS1", BIT(5)},
{"CSMERTC", BIT(6)},
{"CSMEPSF", BIT(7)},
{"D2D_NOC_0", BIT(0)},
{"ESE", BIT(1)},
{"FIACPCB_P", BIT(2)},
{"RSVD", BIT(3)},
{"SBR8B2", BIT(4)},
{"OSSE_SMT1", BIT(5)},
{"D2D_DISP", BIT(6)},
{"P2SB1", BIT(7)},
{"U3FPW1", BIT(0)},
{"SBR16B3", BIT(1)},
{"PSF4", BIT(2)},
{"CNVI", BIT(3)},
{"UFSX2", BIT(4)},
{"ENDBG", BIT(5)},
{"DBC", BIT(6)},
{"SBRG", BIT(7)},
{"RSVD", BIT(0)},
{"NPK1", BIT(1)},
{"SBR16B7", BIT(2)},
{"SBR16B4", BIT(3)},
{"FIA_XG", BIT(4)},
{"PSF6", BIT(5)},
{"UFSPW1", BIT(6)},
{"FIA_U", BIT(7)},
{"PSF8", BIT(0)},
{"PSF0", BIT(1)},
{"RSVD", BIT(2)},
{"FIACPCB_U", BIT(3)},
{"TAM", BIT(4)},
{"SBR16B0", BIT(5)},
{"TBTLSX", BIT(6)},
{"THC0", BIT(7)},
{"THC1", BIT(0)},
{"PMC_1", BIT(1)},
{"FIACPCB_XG", BIT(2)},
{"TCSS", BIT(3)},
{"DISP_PGA", BIT(4)},
{"SBR16B20", BIT(5)},
{"SBR8B20", BIT(6)},
{"DBG_SBR", BIT(7)},
{"SPC", BIT(0)},
{"ACE_0", BIT(1)},
{"ACE_1", BIT(2)},
{"ACE_2", BIT(3)},
{"ACE_3", BIT(4)},
{"ACE_4", BIT(5)},
{"ACE_5", BIT(6)},
{"ACE_6", BIT(7)},
{"ACE_7", BIT(0)},
{"ACE_8", BIT(1)},
{"ACE_9", BIT(2)},
{"ACE_10", BIT(3)},
{"SBR16B2", BIT(4)},
{"SBR8B4", BIT(5)},
{"OSSE", BIT(6)},
{"SBR16B1", BIT(7)},
{}
};
static const struct pmc_bit_map *ext_wcl_pcdn_pfear_map[] = {
wcl_pcdn_pfear_map,
NULL
};
static const struct pmc_bit_map wcl_pcdn_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"RSVD", WCL_PMC_LTR_RESERVED},
{"SATA", CNP_PMC_LTR_SATA},
{"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE},
{"XHCI", CNP_PMC_LTR_XHCI},
{"SOUTHPORT_F", ADL_PMC_LTR_SPF},
{"ME", CNP_PMC_LTR_ME},
{"SATA1", CNP_PMC_LTR_EVA},
{"SOUTHPORT_C", CNP_PMC_LTR_SPC},
{"HD_AUDIO", CNP_PMC_LTR_AZ},
{"CNV", CNP_PMC_LTR_CNV},
{"LPSS", CNP_PMC_LTR_LPSS},
{"SOUTHPORT_D", CNP_PMC_LTR_SPD},
{"SOUTHPORT_E", CNP_PMC_LTR_SPE},
{"SATA2", PTL_PMC_LTR_SATA2},
{"ESPI", CNP_PMC_LTR_ESPI},
{"SCC", CNP_PMC_LTR_SCC},
{"ISH", CNP_PMC_LTR_ISH},
{"UFSX2", CNP_PMC_LTR_UFSX2},
{"EMMC", CNP_PMC_LTR_EMMC},
{"WIGIG", ICL_PMC_LTR_WIGIG},
{"THC0", TGL_PMC_LTR_THC0},
{"THC1", TGL_PMC_LTR_THC1},
{"SOUTHPORT_G", MTL_PMC_LTR_SPG},
{"ESE", MTL_PMC_LTR_ESE},
{"IOE_PMC", MTL_PMC_LTR_IOE_PMC},
{"DMI3", ARL_PMC_LTR_DMI3},
{"OSSE", LNL_PMC_LTR_OSSE},
/* Below two cannot be used for LTR_IGNORE */
{"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT},
{"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT},
{}
};
static const struct pmc_bit_map wcl_pcdn_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0), 0},
{"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0},
{"ESPISPI_PGD0_PG_STS", BIT(2), 0},
{"XHCI_PGD0_PG_STS", BIT(3), 1},
{"SPA_PGD0_PG_STS", BIT(4), 1},
{"RSVD_5", BIT(5), 0},
{"MPFPW2_PGD0_PG_STS", BIT(6), 0},
{"GBE_PGD0_PG_STS", BIT(7), 1},
{"SBR16B21_PGD0_PG_STS", BIT(8), 0},
{"SBR16B5_PGD0_PG_STS", BIT(9), 0},
{"SBR8B1_PGD0_PG_STS", BIT(10), 0},
{"SBR8B0_PGD0_PG_STS", BIT(11), 0},
{"P2SB0_PG_STS", BIT(12), 1},
{"D2D_DISP_PGD1_PG_STS", BIT(13), 0},
{"LPSS_PGD0_PG_STS", BIT(14), 1},
{"LPC_PGD0_PG_STS", BIT(15), 0},
{"SMB_PGD0_PG_STS", BIT(16), 0},
{"ISH_PGD0_PG_STS", BIT(17), 0},
{"DBG_SBR16B_PGD0_PG_STS", BIT(18), 0},
{"NPK_PGD0_PG_STS", BIT(19), 0},
{"D2D_NOC_PGD1_PG_STS", BIT(20), 0},
{"FIA_P_PGD0_PG_STS", BIT(21), 0},
{"FUSE_PGD0_PG_STS", BIT(22), 0},
{"DBG_PSF_PGD0_PG_STS", BIT(23), 0},
{"DISP_PGA1_PGD0_PG_STS", BIT(24), 0},
{"XDCI_PGD0_PG_STS", BIT(25), 1},
{"EXI_PGD0_PG_STS", BIT(26), 0},
{"CSE_PGD0_PG_STS", BIT(27), 1},
{"KVMCC_PGD0_PG_STS", BIT(28), 1},
{"PMT_PGD0_PG_STS", BIT(29), 1},
{"CLINK_PGD0_PG_STS", BIT(30), 1},
{"PTIO_PGD0_PG_STS", BIT(31), 1},
{}
};
static const struct pmc_bit_map wcl_pcdn_power_gating_status_1_map[] = {
{"USBR0_PGD0_PG_STS", BIT(0), 1},
{"SBR16B22_PGD0_PG_STS", BIT(1), 0},
{"SMT1_PGD0_PG_STS", BIT(2), 1},
{"MPFPW1_PGD0_PG_STS", BIT(3), 0},
{"SMS2_PGD0_PG_STS", BIT(4), 1},
{"SMS1_PGD0_PG_STS", BIT(5), 1},
{"CSMERTC_PGD0_PG_STS", BIT(6), 0},
{"CSMEPSF_PGD0_PG_STS", BIT(7), 0},
{"D2D_NOC_PGD0_PG_STS", BIT(8), 0},
{"ESE_PGD0_PG_STS", BIT(9), 1},
{"FIACPCB_P_PGD0_PG_STS", BIT(10), 0},
{"SBR8B2_PGD0_PG_STS", BIT(12), 0},
{"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1},
{"D2D_DISP_PGD0_PG_STS", BIT(14), 0},
{"P2SB1_PGD0_PG_STS", BIT(15), 1},
{"U3FPW1_PGD0_PG_STS", BIT(16), 0},
{"SBR16B3_PGD0_PG_STS", BIT(17), 0},
{"PSF4_PGD0_PG_STS", BIT(18), 0},
{"CNVI_PGD0_PG_STS", BIT(19), 0},
{"UFSX2_PGD0_PG_STS", BIT(20), 1},
{"ENDBG_PGD0_PG_STS", BIT(21), 0},
{"DBC_PGD0_PG_STS", BIT(22), 0},
{"SBRG_PGD0_PG_STS", BIT(23), 0},
{"NPK_PGD1_PG_STS", BIT(25), 0},
{"SBR16B7_PGD0_PG_STS", BIT(26), 0},
{"SBR16B4_PGD0_PG_STS", BIT(27), 0},
{"FIA_XG_PSF_PGD0_PG_STS", BIT(28), 0},
{"PSF6_PGD0_PG_STS", BIT(29), 0},
{"UFSPW1_PGD0_PG_STS", BIT(30), 0},
{"FIA_U_PGD0_PG_STS", BIT(31), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_power_gating_status_2_map[] = {
{"PSF8_PGD0_PG_STS", BIT(0), 0},
{"PSF0_PGD0_PG_STS", BIT(1), 0},
{"FIACPCB_U_PGD0_PG_STS", BIT(3), 0},
{"TAM_PGD0_PG_STS", BIT(4), 1},
{"SBR16B0_PGD0_PG_STS", BIT(5), 0},
{"TBTLSX_PGD0_PG_STS", BIT(6), 1},
{"THC0_PGD0_PG_STS", BIT(7), 1},
{"THC1_PGD0_PG_STS", BIT(8), 1},
{"PMC_PGD1_PG_STS", BIT(9), 0},
{"FIACPCB_XG_PGD0_PG_STS", BIT(10), 0},
{"TCSS_PGD0_PG_STS", BIT(11), 0},
{"DISP_PGA_PGD0_PG_STS", BIT(12), 0},
{"SBR8B4_PGD0_PG_STS", BIT(13), 0},
{"SBR8B20_PGD0_PG_STS", BIT(14), 0},
{"DBG_PGD0_PG_STS", BIT(15), 0},
{"SPC_PGD0_PG_STS", BIT(16), 1},
{"ACE_PGD0_PG_STS", BIT(17), 0},
{"ACE_PGD1_PG_STS", BIT(18), 0},
{"ACE_PGD2_PG_STS", BIT(19), 0},
{"ACE_PGD3_PG_STS", BIT(20), 0},
{"ACE_PGD4_PG_STS", BIT(21), 0},
{"ACE_PGD5_PG_STS", BIT(22), 0},
{"ACE_PGD6_PG_STS", BIT(23), 0},
{"ACE_PGD7_PG_STS", BIT(24), 0},
{"ACE_PGD8_PG_STS", BIT(25), 0},
{"ACE_PGD9_PG_STS", BIT(26), 0},
{"ACE_PGD10_PG_STS", BIT(27), 0},
{"SBR16B2_PG_PGD0_PG_STS", BIT(28), 0},
{"SBR16B20_PGD0_PG_STS", BIT(29), 0},
{"OSSE_PGD0_PG_STS", BIT(30), 1},
{"SBR16B1_PGD0_PG_STS", BIT(31), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_d3_status_0_map[] = {
{"LPSS_D3_STS", BIT(3), 1},
{"XDCI_D3_STS", BIT(4), 1},
{"XHCI_D3_STS", BIT(5), 1},
{"SPA_D3_STS", BIT(12), 0},
{"SPC_D3_STS", BIT(14), 0},
{"OSSE_D3_STS", BIT(15), 0},
{"ESPISPI_D3_STS", BIT(18), 0},
{"PSTH_D3_STS", BIT(21), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_d3_status_1_map[] = {
{"OSSE_SMT1_D3_STS", BIT(16), 0},
{"GBE_D3_STS", BIT(19), 0},
{"ITSS_D3_STS", BIT(23), 0},
{"CNVI_D3_STS", BIT(27), 0},
{"UFSX2_D3_STS", BIT(28), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_d3_status_2_map[] = {
{"CSMERTC_D3_STS", BIT(1), 0},
{"ESE_D3_STS", BIT(2), 0},
{"CSE_D3_STS", BIT(4), 0},
{"KVMCC_D3_STS", BIT(5), 0},
{"USBR0_D3_STS", BIT(6), 0},
{"ISH_D3_STS", BIT(7), 0},
{"SMT1_D3_STS", BIT(8), 0},
{"SMT2_D3_STS", BIT(9), 0},
{"SMT3_D3_STS", BIT(10), 0},
{"CLINK_D3_STS", BIT(14), 0},
{"PTIO_D3_STS", BIT(16), 0},
{"PMT_D3_STS", BIT(17), 0},
{"SMS1_D3_STS", BIT(18), 0},
{"SMS2_D3_STS", BIT(19), 0},
{"OSSE_SMT2_D3_STS", BIT(22), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_d3_status_3_map[] = {
{"THC0_D3_STS", BIT(14), 1},
{"THC1_D3_STS", BIT(15), 1},
{"OSSE_SMT3_D3_STS", BIT(16), 0},
{"ACE_D3_STS", BIT(23), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_vnn_req_status_0_map[] = {
{"LPSS_VNN_REQ_STS", BIT(3), 1},
{"OSSE_VNN_REQ_STS", BIT(15), 1},
{"ESPISPI_VNN_REQ_STS", BIT(18), 1},
{}
};
static const struct pmc_bit_map wcl_pcdn_vnn_req_status_1_map[] = {
{"NPK_VNN_REQ_STS", BIT(4), 1},
{"DFXAGG_VNN_REQ_STS", BIT(8), 0},
{"EXI_VNN_REQ_STS", BIT(9), 1},
{"OSSE_SMT1_VNN_REQ_STS", BIT(16), 1},
{"P2D_VNN_REQ_STS", BIT(18), 1},
{"GBE_VNN_REQ_STS", BIT(19), 1},
{"SMB_VNN_REQ_STS", BIT(25), 1},
{"LPC_VNN_REQ_STS", BIT(26), 0},
{}
};
static const struct pmc_bit_map wcl_pcdn_vnn_req_status_2_map[] = {
{"CSMERTC_VNN_REQ_STS", BIT(1), 1},
{"ESE_VNN_REQ_STS", BIT(2), 1},
{"CSE_VNN_REQ_STS", BIT(4), 1},
{"ISH_VNN_REQ_STS", BIT(7), 1},
{"SMT1_VNN_REQ_STS", BIT(8), 1},
{"CLINK_VNN_REQ_STS", BIT(14), 1},
{"SMS1_VNN_REQ_STS", BIT(18), 1},
{"SMS2_VNN_REQ_STS", BIT(19), 1},
{"GPIOCOM4_VNN_REQ_STS", BIT(20), 1},
{"GPIOCOM3_VNN_REQ_STS", BIT(21), 1},
{"GPIOCOM1_VNN_REQ_STS", BIT(23), 1},
{"GPIOCOM0_VNN_REQ_STS", BIT(24), 1},
{"DISP_SHIM_VNN_REQ_STS", BIT(31), 1},
{}
};
static const struct pmc_bit_map wcl_pcdn_vnn_misc_status_map[] = {
{"CPU_C10_REQ_STS", BIT(0), 0},
{"TS_OFF_REQ_STS", BIT(1), 0},
{"PNDE_MET_REQ_STS", BIT(2), 1},
{"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0},
{"VNN_SOC_REQ_STS", BIT(6), 1},
{"ISH_VNNAON_REQ_STS", BIT(7), 0},
{"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1},
{"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1},
{"PLT_GREATER_REQ_STS", BIT(11), 1},
{"ALL_SBR_IDLE_REQ_STS", BIT(12), 0},
{"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0},
{"PM_SYNC_STATES_REQ_STS", BIT(14), 0},
{"EA_REQ_STS", BIT(15), 0},
{"MPHY_CORE_OFF_REQ_STS", BIT(16), 0},
{"BRK_EV_EN_REQ_STS", BIT(17), 0},
{"AUTO_DEMO_EN_REQ_STS", BIT(18), 0},
{"ITSS_CLK_SRC_REQ_STS", BIT(19), 1},
{"ARC_IDLE_REQ_STS", BIT(21), 0},
{"FIA_DEEP_PM_REQ_STS", BIT(23), 0},
{"XDCI_ATTACHED_REQ_STS", BIT(24), 1},
{"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0},
{"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1},
{"PRE_WAKE0_REQ_STS", BIT(27), 1},
{"PRE_WAKE1_REQ_STS", BIT(28), 1},
{"PRE_WAKE2_REQ_STS", BIT(29), 1},
{}
};
static const struct pmc_bit_map wcl_pcdn_rsc_status_map[] = {
{"Memory", 0, 1},
{"PSF0", 0, 1},
{"PSF6", 0, 1},
{"PSF8", 0, 1},
{"SAF_CFI_LINK", 0, 1},
{"SB", 0, 1},
{}
};
static const struct pmc_bit_map *wcl_pcdn_lpm_maps[] = {
ptl_pcdp_clocksource_status_map,
wcl_pcdn_power_gating_status_0_map,
wcl_pcdn_power_gating_status_1_map,
wcl_pcdn_power_gating_status_2_map,
wcl_pcdn_d3_status_0_map,
wcl_pcdn_d3_status_1_map,
wcl_pcdn_d3_status_2_map,
wcl_pcdn_d3_status_3_map,
wcl_pcdn_vnn_req_status_0_map,
wcl_pcdn_vnn_req_status_1_map,
wcl_pcdn_vnn_req_status_2_map,
ptl_pcdp_vnn_req_status_3_map,
wcl_pcdn_vnn_misc_status_map,
ptl_pcdp_signal_status_map,
NULL
};
static const struct pmc_bit_map *wcl_pcdn_blk_maps[] = {
wcl_pcdn_power_gating_status_0_map,
wcl_pcdn_power_gating_status_1_map,
wcl_pcdn_power_gating_status_2_map,
wcl_pcdn_rsc_status_map,
wcl_pcdn_vnn_req_status_0_map,
wcl_pcdn_vnn_req_status_1_map,
wcl_pcdn_vnn_req_status_2_map,
ptl_pcdp_vnn_req_status_3_map,
wcl_pcdn_d3_status_0_map,
wcl_pcdn_d3_status_1_map,
wcl_pcdn_d3_status_2_map,
wcl_pcdn_d3_status_3_map,
ptl_pcdp_clocksource_status_map,
wcl_pcdn_vnn_misc_status_map,
ptl_pcdp_signal_status_map,
NULL
};
static const struct pmc_reg_map wcl_pcdn_reg_map = {
.pfear_sts = ext_wcl_pcdn_pfear_map,
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
.slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
.ltr_show_sts = wcl_pcdn_ltr_show_map,
.msr_sts = msr_map,
.ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
.regmap_length = WCL_PCD_PMC_MMIO_REG_LEN,
.ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
.ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES,
.pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
.pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
.lpm_num_maps = PTL_LPM_NUM_MAPS,
.ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED,
.lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
.etr3_offset = ETR3_OFFSET,
.lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
.lpm_priority_offset = MTL_LPM_PRI_OFFSET,
.lpm_en_offset = MTL_LPM_EN_OFFSET,
.lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
.lpm_sts = wcl_pcdn_lpm_maps,
.lpm_status_offset = MTL_LPM_STATUS_OFFSET,
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.s0ix_blocker_maps = wcl_pcdn_blk_maps,
.s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
};
#define WCL_NPU_PCI_DEV 0xfd3e
/*
* Set power state of select devices that do not have drivers to D3
* so that they do not block Package C entry.
*/
static void wcl_d3_fixup(void)
{
pmc_core_set_device_d3(WCL_NPU_PCI_DEV);
}
static int wcl_resume(struct pmc_dev *pmcdev)
{
wcl_d3_fixup();
return cnl_resume(pmcdev);
}
static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
wcl_d3_fixup();
return generic_core_init(pmcdev, pmc_dev_info);
}
struct pmc_dev_info wcl_pmc_dev = {
.map = &wcl_pcdn_reg_map,
.suspend = cnl_suspend,
.resume = wcl_resume,
.init = wcl_core_init,
};

View File

@ -374,6 +374,77 @@ static void uncore_set_agent_type(struct tpmi_uncore_cluster_info *cluster_info)
cluster_info->uncore_data.agent_type_mask = FIELD_GET(UNCORE_AGENT_TYPES, status);
}
#define MAX_PARTITIONS 2
/* IO domain ID start index for a partition */
static u8 io_die_start[MAX_PARTITIONS];
/* Next IO domain ID index after the current partition IO die IDs */
static u8 io_die_index_next;
/* Lock to protect io_die_start, io_die_index_next */
static DEFINE_MUTEX(domain_lock);
static void set_domain_id(int id, int num_resources,
struct oobmsm_plat_info *plat_info,
struct tpmi_uncore_cluster_info *cluster_info)
{
u8 part_io_index, cdie_range, pkg_io_index, max_dies;
if (plat_info->partition >= MAX_PARTITIONS) {
cluster_info->uncore_data.domain_id = id;
return;
}
if (cluster_info->uncore_data.agent_type_mask & AGENT_TYPE_CORE) {
cluster_info->uncore_data.domain_id = cluster_info->cdie_id;
return;
}
/* Unlikely but cdie_mask may have holes, so take range */
cdie_range = fls(plat_info->cdie_mask) - ffs(plat_info->cdie_mask) + 1;
max_dies = topology_max_dies_per_package();
/*
* If the CPU doesn't enumerate dies, then use current cdie range
* as the max.
*/
if (cdie_range > max_dies)
max_dies = cdie_range;
guard(mutex)(&domain_lock);
if (!io_die_index_next)
io_die_index_next = max_dies;
if (!io_die_start[plat_info->partition]) {
io_die_start[plat_info->partition] = io_die_index_next;
/*
* number of IO dies = num_resources - cdie_range. Hence
* next partition io_die_index_next is set after IO dies
* in the current partition.
*/
io_die_index_next += (num_resources - cdie_range);
}
/*
* Index from IO die start within the partition:
* This is the first valid domain after the cdies.
* For example the current resource index 5 and cdies end at
* index 3 (cdie_cnt = 4). Then the IO only index 5 - 4 = 1.
*/
part_io_index = id - cdie_range;
/*
* Add to the IO die start index for this partition in this package
* to make unique in the package.
*/
pkg_io_index = io_die_start[plat_info->partition] + part_io_index;
/* Assign this to domain ID */
cluster_info->uncore_data.domain_id = pkg_io_index;
}
/* Callback for sysfs read for TPMI uncore values. Called under mutex locks. */
static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncore_index index)
{
@ -610,11 +681,12 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
cluster_info->uncore_data.package_id = pkg;
/* There are no dies like Cascade Lake */
cluster_info->uncore_data.die_id = 0;
cluster_info->uncore_data.domain_id = i;
cluster_info->uncore_data.cluster_id = j;
set_cdie_id(i, cluster_info, plat_info);
set_domain_id(i, num_resources, plat_info, cluster_info);
cluster_info->uncore_root = tpmi_uncore;
if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) >= UNCORE_ELC_SUPPORTED_VERSION)
@ -638,7 +710,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
auxiliary_set_drvdata(auxdev, tpmi_uncore);
if (topology_max_dies_per_package() > 1)
if (topology_max_dies_per_package() > 1 || plat_info->partition)
return 0;
tpmi_uncore->root_cluster.root_domain = true;

View File

@ -119,6 +119,7 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
* You must reboot the computer before the changes will take effect.
*/
#define LENOVO_SET_BIOS_CERT_GUID "26861C9F-47E9-44C4-BD8B-DFE7FA2610FE"
#define LENOVO_TC_SET_BIOS_CERT_GUID "955aaf7d-8bc4-4f04-90aa-97469512f167"
/*
* Name: UpdateBiosCert
@ -128,6 +129,7 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
* You must reboot the computer before the changes will take effect.
*/
#define LENOVO_UPDATE_BIOS_CERT_GUID "9AA3180A-9750-41F7-B9F7-D5D3B1BAC3CE"
#define LENOVO_TC_UPDATE_BIOS_CERT_GUID "5f5bbbb2-c72f-4fb8-8129-228eef4fdbed"
/*
* Name: ClearBiosCert
@ -137,6 +139,8 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
* You must reboot the computer before the changes will take effect.
*/
#define LENOVO_CLEAR_BIOS_CERT_GUID "B2BC39A7-78DD-4D71-B059-A510DEC44890"
#define LENOVO_TC_CLEAR_BIOS_CERT_GUID "97849cb6-cb44-42d1-a750-26a596a9eec4"
/*
* Name: CertToPassword
* Description: Switch from certificate to password authentication.
@ -145,6 +149,7 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
* You must reboot the computer before the changes will take effect.
*/
#define LENOVO_CERT_TO_PASSWORD_GUID "0DE8590D-5510-4044-9621-77C227F5A70D"
#define LENOVO_TC_CERT_TO_PASSWORD_GUID "ef65480d-38c9-420d-b700-ab3d6c8ebaca"
/*
* Name: SetBiosSettingCert
@ -153,6 +158,7 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
* Format: "Item,Value,Signature"
*/
#define LENOVO_SET_BIOS_SETTING_CERT_GUID "34A008CC-D205-4B62-9E67-31DFA8B90003"
#define LENOVO_TC_SET_BIOS_SETTING_CERT_GUID "19ecba3b-b318-4192-a89b-43d94bc60cea"
/*
* Name: SaveBiosSettingCert
@ -161,6 +167,7 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
* Format: "Signature"
*/
#define LENOVO_SAVE_BIOS_SETTING_CERT_GUID "C050FB9D-DF5F-4606-B066-9EFC401B2551"
#define LENOVO_TC_SAVE_BIOS_SETTING_CERT_GUID "0afaf46f-7cca-450a-b455-a826a0bf1af5"
/*
* Name: CertThumbprint
@ -177,12 +184,43 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
#define TLMI_CERT_SVC BIT(7) /* Admin Certificate Based */
#define TLMI_CERT_SMC BIT(8) /* System Certificate Based */
static const struct tlmi_cert_guids thinkpad_cert_guid = {
.thumbprint = LENOVO_CERT_THUMBPRINT_GUID,
.set_bios_setting = LENOVO_SET_BIOS_SETTING_CERT_GUID,
.save_bios_setting = LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
.cert_to_password = LENOVO_CERT_TO_PASSWORD_GUID,
.clear_bios_cert = LENOVO_CLEAR_BIOS_CERT_GUID,
.update_bios_cert = LENOVO_UPDATE_BIOS_CERT_GUID,
.set_bios_cert = LENOVO_SET_BIOS_CERT_GUID,
};
static const struct tlmi_cert_guids thinkcenter_cert_guid = {
.thumbprint = NULL,
.set_bios_setting = LENOVO_TC_SET_BIOS_SETTING_CERT_GUID,
.save_bios_setting = LENOVO_TC_SAVE_BIOS_SETTING_CERT_GUID,
.cert_to_password = LENOVO_TC_CERT_TO_PASSWORD_GUID,
.clear_bios_cert = LENOVO_TC_CLEAR_BIOS_CERT_GUID,
.update_bios_cert = LENOVO_TC_UPDATE_BIOS_CERT_GUID,
.set_bios_cert = LENOVO_TC_SET_BIOS_CERT_GUID,
};
static const struct tlmi_err_codes tlmi_errs[] = {
{"Success", 0},
{"Set Certificate operation was successful.", 0},
{"Not Supported", -EOPNOTSUPP},
{"Invalid Parameter", -EINVAL},
{"Access Denied", -EACCES},
{"System Busy", -EBUSY},
{"Set Certificate operation failed with status:Invalid Parameter.", -EINVAL},
{"Set Certificate operation failed with status:Invalid certificate type.", -EINVAL},
{"Set Certificate operation failed with status:Invalid password format.", -EINVAL},
{"Set Certificate operation failed with status:Password retry count exceeded.", -EACCES},
{"Set Certificate operation failed with status:Password Invalid.", -EACCES},
{"Set Certificate operation failed with status:Operation aborted.", -EBUSY},
{"Set Certificate operation failed with status:No free slots to write.", -ENOSPC},
{"Set Certificate operation failed with status:Certificate not found.", -EEXIST},
{"Set Certificate operation failed with status:Internal error.", -EFAULT},
{"Set Certificate operation failed with status:Certificate too large.", -EFBIG},
};
static const char * const encoding_options[] = {
@ -668,7 +706,10 @@ static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
const union acpi_object *obj;
acpi_status status;
status = wmi_evaluate_method(LENOVO_CERT_THUMBPRINT_GUID, 0, 0, &input, &output);
if (!tlmi_priv.cert_guid->thumbprint)
return -EOPNOTSUPP;
status = wmi_evaluate_method(tlmi_priv.cert_guid->thumbprint, 0, 0, &input, &output);
if (ACPI_FAILURE(status)) {
kfree(output.pointer);
return -EIO;
@ -751,7 +792,7 @@ static ssize_t cert_to_password_store(struct kobject *kobj,
kfree_sensitive(passwd);
return -ENOMEM;
}
ret = tlmi_simple_call(LENOVO_CERT_TO_PASSWORD_GUID, auth_str);
ret = tlmi_simple_call(tlmi_priv.cert_guid->cert_to_password, auth_str);
kfree(auth_str);
kfree_sensitive(passwd);
@ -774,7 +815,7 @@ static ssize_t certificate_store(struct kobject *kobj,
char *auth_str, *new_cert;
const char *serial;
char *signature;
char *guid;
const char *guid;
int ret;
if (!capable(CAP_SYS_ADMIN))
@ -797,7 +838,7 @@ static ssize_t certificate_store(struct kobject *kobj,
if (!auth_str)
return -ENOMEM;
ret = tlmi_simple_call(LENOVO_CLEAR_BIOS_CERT_GUID, auth_str);
ret = tlmi_simple_call(tlmi_priv.cert_guid->clear_bios_cert, auth_str);
kfree(auth_str);
return ret ?: count;
@ -834,7 +875,7 @@ static ssize_t certificate_store(struct kobject *kobj,
kfree(new_cert);
return -EACCES;
}
guid = LENOVO_UPDATE_BIOS_CERT_GUID;
guid = tlmi_priv.cert_guid->update_bios_cert;
/* Format: 'Certificate,Signature' */
auth_str = cert_command(setting, new_cert, signature);
} else {
@ -845,9 +886,17 @@ static ssize_t certificate_store(struct kobject *kobj,
kfree(new_cert);
return -EACCES;
}
guid = LENOVO_SET_BIOS_CERT_GUID;
/* Format: 'Certificate, password' */
auth_str = cert_command(setting, new_cert, setting->password);
guid = tlmi_priv.cert_guid->set_bios_cert;
if (tlmi_priv.thinkcenter_mode) {
/* Format: 'Certificate, password, encoding, kbdlang' */
auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s", new_cert,
setting->password,
encoding_options[setting->encoding],
setting->kbdlang);
} else {
/* Format: 'Certificate, password' */
auth_str = cert_command(setting, new_cert, setting->password);
}
}
kfree(new_cert);
if (!auth_str)
@ -1071,13 +1120,13 @@ static ssize_t current_value_store(struct kobject *kobj,
goto out;
}
ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, set_str);
ret = tlmi_simple_call(tlmi_priv.cert_guid->set_bios_setting, set_str);
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,
ret = tlmi_simple_call(tlmi_priv.cert_guid->save_bios_setting,
tlmi_priv.pwd_admin->save_signature);
} else if (tlmi_priv.opcode_support) {
/*
@ -1282,7 +1331,7 @@ static ssize_t save_settings_store(struct kobject *kobj, struct kobj_attribute *
ret = -EINVAL;
goto out;
}
ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
ret = tlmi_simple_call(tlmi_priv.cert_guid->save_bios_setting,
tlmi_priv.pwd_admin->save_signature);
if (ret)
goto out;
@ -1583,6 +1632,15 @@ static int tlmi_analyze(struct wmi_device *wdev)
wmi_has_guid(LENOVO_SAVE_BIOS_SETTING_CERT_GUID))
tlmi_priv.certificate_support = true;
/* ThinkCenter uses different GUIDs for certificate support */
if (wmi_has_guid(LENOVO_TC_SET_BIOS_CERT_GUID) &&
wmi_has_guid(LENOVO_TC_SET_BIOS_SETTING_CERT_GUID) &&
wmi_has_guid(LENOVO_TC_SAVE_BIOS_SETTING_CERT_GUID)) {
tlmi_priv.certificate_support = true;
tlmi_priv.thinkcenter_mode = true;
pr_info("ThinkCenter modified support being used\n");
}
/*
* Try to find the number of valid settings of this machine
* and use it to create sysfs attributes.
@ -1728,10 +1786,16 @@ static int tlmi_analyze(struct wmi_device *wdev)
}
if (tlmi_priv.certificate_support) {
tlmi_priv.pwd_admin->cert_installed =
tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SVC;
tlmi_priv.pwd_system->cert_installed =
tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SMC;
if (tlmi_priv.thinkcenter_mode) {
tlmi_priv.cert_guid = &thinkcenter_cert_guid;
tlmi_priv.pwd_admin->cert_installed = tlmi_priv.pwdcfg.core.password_mode;
} else {
tlmi_priv.cert_guid = &thinkpad_cert_guid;
tlmi_priv.pwd_admin->cert_installed =
tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SVC;
tlmi_priv.pwd_system->cert_installed =
tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SMC;
}
}
return 0;

View File

@ -41,6 +41,17 @@ enum save_mode {
TLMI_SAVE_SAVE,
};
/* GUIDs can differ between platforms */
struct tlmi_cert_guids {
const char *thumbprint;
const char *set_bios_setting;
const char *save_bios_setting;
const char *cert_to_password;
const char *clear_bios_cert;
const char *update_bios_cert;
const char *set_bios_cert;
};
/* password configuration details */
#define TLMI_PWDCFG_MODE_LEGACY 0
#define TLMI_PWDCFG_MODE_PASSWORD 1
@ -109,6 +120,7 @@ struct think_lmi {
enum save_mode save_mode;
bool save_required;
bool reboot_required;
bool thinkcenter_mode;
struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT];
struct device *class_dev;
@ -121,6 +133,8 @@ struct think_lmi {
struct tlmi_pwd_setting *pwd_system;
struct tlmi_pwd_setting *pwd_hdd;
struct tlmi_pwd_setting *pwd_nvme;
const struct tlmi_cert_guids *cert_guid;
};
#endif /* !_THINK_LMI_H_ */

View File

@ -93,7 +93,7 @@ int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata0
continue;
memcpy(output, &list->data[idx], sizeof(list->data[idx]));
return 0;
};
}
return -EINVAL;
}

View File

@ -255,6 +255,11 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)
if (!serdev)
return -ENOMEM;
/* Propagate pdev-fwnode set by x86-android-tablets to serdev */
device_set_node(&serdev->dev, dev_fwnode(&pdev->dev));
/* The fwnode is a managed node, so it will be auto-put on serdev_device_put() */
fwnode_handle_get(dev_fwnode(&serdev->dev));
ret = serdev_device_add(serdev);
if (ret) {
serdev_device_put(serdev);

View File

@ -15,135 +15,256 @@
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/gpio_keys.h>
#include <linux/gpio/machine.h>
#include <linux/input.h>
#include <linux/gpio/property.h>
#include <linux/input-event-codes.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#define TINK_GPIO_DRIVER_NAME "gpio_ich"
static const struct software_node gpio_ich_node = {
.name = TINK_GPIO_DRIVER_NAME,
};
/* LEDs */
static const struct gpio_led tink_leds[] = {
{
.name = "mx100:green:internet",
.default_trigger = "default-on",
},
{
.name = "mx100:green:lan2",
},
{
.name = "mx100:green:lan3",
},
{
.name = "mx100:green:lan4",
},
{
.name = "mx100:green:lan5",
},
{
.name = "mx100:green:lan6",
},
{
.name = "mx100:green:lan7",
},
{
.name = "mx100:green:lan8",
},
{
.name = "mx100:green:lan9",
},
{
.name = "mx100:green:lan10",
},
{
.name = "mx100:green:lan11",
},
{
.name = "mx100:green:ha",
},
{
.name = "mx100:orange:ha",
},
{
.name = "mx100:green:usb",
},
{
.name = "mx100:orange:usb",
},
static const struct software_node tink_gpio_leds_node = {
.name = "meraki-mx100-leds",
};
static const struct gpio_led_platform_data tink_leds_pdata = {
.num_leds = ARRAY_SIZE(tink_leds),
.leds = tink_leds,
static const struct property_entry tink_internet_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:internet"),
PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW),
{ }
};
static struct gpiod_lookup_table tink_leds_table = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 11,
NULL, 0, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 18,
NULL, 1, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 20,
NULL, 2, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 22,
NULL, 3, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 23,
NULL, 4, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 32,
NULL, 5, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 34,
NULL, 6, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 35,
NULL, 7, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 36,
NULL, 8, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 37,
NULL, 9, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 48,
NULL, 10, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 16,
NULL, 11, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 7,
NULL, 12, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 21,
NULL, 13, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 19,
NULL, 14, GPIO_ACTIVE_LOW),
{} /* Terminating entry */
}
static const struct software_node tink_internet_led_node = {
.name = "internet-led",
.parent = &tink_gpio_leds_node,
.properties = tink_internet_led_props,
};
static const struct property_entry tink_lan2_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan2_led_node = {
.name = "lan2-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan2_led_props,
};
static const struct property_entry tink_lan3_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan3_led_node = {
.name = "lan3-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan3_led_props,
};
static const struct property_entry tink_lan4_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan4_led_node = {
.name = "lan4-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan4_led_props,
};
static const struct property_entry tink_lan5_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan5_led_node = {
.name = "lan5-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan5_led_props,
};
static const struct property_entry tink_lan6_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan6_led_node = {
.name = "lan6-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan6_led_props,
};
static const struct property_entry tink_lan7_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan7_led_node = {
.name = "lan7-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan7_led_props,
};
static const struct property_entry tink_lan8_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan8_led_node = {
.name = "lan8-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan8_led_props,
};
static const struct property_entry tink_lan9_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan9_led_node = {
.name = "lan9-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan9_led_props,
};
static const struct property_entry tink_lan10_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan10_led_node = {
.name = "lan10-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan10_led_props,
};
static const struct property_entry tink_lan11_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node tink_lan11_led_node = {
.name = "lan11-led",
.parent = &tink_gpio_leds_node,
.properties = tink_lan11_led_props,
};
static const struct property_entry tink_ha_green_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:ha"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node tink_ha_green_led_node = {
.name = "ha-green-led",
.parent = &tink_gpio_leds_node,
.properties = tink_ha_green_led_props,
};
static const struct property_entry tink_ha_orange_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node tink_ha_orange_led_node = {
.name = "ha-orange-led",
.parent = &tink_gpio_leds_node,
.properties = tink_ha_orange_led_props,
};
static const struct property_entry tink_usb_green_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:green:usb"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node tink_usb_green_led_node = {
.name = "usb-green-led",
.parent = &tink_gpio_leds_node,
.properties = tink_usb_green_led_props,
};
static const struct property_entry tink_usb_orange_led_props[] = {
PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node tink_usb_orange_led_node = {
.name = "usb-orange-led",
.parent = &tink_gpio_leds_node,
.properties = tink_usb_orange_led_props,
};
/* Reset Button */
static struct gpio_keys_button tink_buttons[] = {
{
.desc = "Reset",
.type = EV_KEY,
.code = KEY_RESTART,
.active_low = 1,
.debounce_interval = 100,
},
static const struct property_entry tink_gpio_keys_props[] = {
PROPERTY_ENTRY_U32("poll-interval", 20),
{ }
};
static const struct gpio_keys_platform_data tink_buttons_pdata = {
.buttons = tink_buttons,
.nbuttons = ARRAY_SIZE(tink_buttons),
.poll_interval = 20,
.rep = 0,
.name = "mx100-keys",
static const struct software_node tink_gpio_keys_node = {
.name = "mx100-keys",
.properties = tink_gpio_keys_props,
};
static struct gpiod_lookup_table tink_keys_table = {
.dev_id = "gpio-keys-polled",
.table = {
GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 60,
NULL, 0, GPIO_ACTIVE_LOW),
{} /* Terminating entry */
}
static const struct property_entry tink_reset_key_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
PROPERTY_ENTRY_STRING("label", "Reset"),
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("linux,input-type", EV_KEY),
PROPERTY_ENTRY_U32("debounce-interval", 100),
{ }
};
static const struct software_node tink_reset_key_node = {
.name = "reset",
.parent = &tink_gpio_keys_node,
.properties = tink_reset_key_props,
};
static const struct software_node *tink_swnodes[] = {
&gpio_ich_node,
/* LEDs nodes */
&tink_gpio_leds_node,
&tink_internet_led_node,
&tink_lan2_led_node,
&tink_lan3_led_node,
&tink_lan4_led_node,
&tink_lan5_led_node,
&tink_lan6_led_node,
&tink_lan7_led_node,
&tink_lan8_led_node,
&tink_lan9_led_node,
&tink_lan10_led_node,
&tink_lan11_led_node,
&tink_ha_green_led_node,
&tink_ha_orange_led_node,
&tink_usb_green_led_node,
&tink_usb_orange_led_node,
/* Keys nodes */
&tink_gpio_keys_node,
&tink_reset_key_node,
NULL
};
/* Board setup */
@ -161,22 +282,17 @@ MODULE_DEVICE_TABLE(dmi, tink_systems);
static struct platform_device *tink_leds_pdev;
static struct platform_device *tink_keys_pdev;
static struct platform_device * __init tink_create_dev(
const char *name, const void *pdata, size_t sz)
{
struct platform_device *pdev;
pdev = platform_device_register_data(NULL,
name, PLATFORM_DEVID_NONE, pdata, sz);
if (IS_ERR(pdev))
pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
return pdev;
}
static int __init tink_board_init(void)
{
int ret;
struct platform_device_info keys_info = {
.name = "gpio-keys-polled",
.id = PLATFORM_DEVID_NONE,
};
struct platform_device_info leds_info = {
.name = "leds-gpio",
.id = PLATFORM_DEVID_NONE,
};
int err;
if (!dmi_first_match(tink_systems))
return -ENODEV;
@ -188,30 +304,35 @@ static int __init tink_board_init(void)
*/
outl(inl(0x530) | BIT(28), 0x530);
gpiod_add_lookup_table(&tink_leds_table);
gpiod_add_lookup_table(&tink_keys_table);
tink_leds_pdev = tink_create_dev("leds-gpio",
&tink_leds_pdata, sizeof(tink_leds_pdata));
if (IS_ERR(tink_leds_pdev)) {
ret = PTR_ERR(tink_leds_pdev);
goto err;
err = software_node_register_node_group(tink_swnodes);
if (err) {
pr_err("failed to register software nodes: %d\n", err);
return err;
}
tink_keys_pdev = tink_create_dev("gpio-keys-polled",
&tink_buttons_pdata, sizeof(tink_buttons_pdata));
leds_info.fwnode = software_node_fwnode(&tink_gpio_leds_node);
tink_leds_pdev = platform_device_register_full(&leds_info);
if (IS_ERR(tink_leds_pdev)) {
err = PTR_ERR(tink_leds_pdev);
pr_err("failed to create LED device: %d\n", err);
goto err_unregister_swnodes;
}
keys_info.fwnode = software_node_fwnode(&tink_gpio_keys_node);
tink_keys_pdev = platform_device_register_full(&keys_info);
if (IS_ERR(tink_keys_pdev)) {
ret = PTR_ERR(tink_keys_pdev);
platform_device_unregister(tink_leds_pdev);
goto err;
err = PTR_ERR(tink_keys_pdev);
pr_err("failed to create key device: %d\n", err);
goto err_unregister_leds;
}
return 0;
err:
gpiod_remove_lookup_table(&tink_keys_table);
gpiod_remove_lookup_table(&tink_leds_table);
return ret;
err_unregister_leds:
platform_device_unregister(tink_leds_pdev);
err_unregister_swnodes:
software_node_unregister_node_group(tink_swnodes);
return err;
}
module_init(tink_board_init);
@ -219,8 +340,7 @@ static void __exit tink_board_exit(void)
{
platform_device_unregister(tink_keys_pdev);
platform_device_unregister(tink_leds_pdev);
gpiod_remove_lookup_table(&tink_keys_table);
gpiod_remove_lookup_table(&tink_leds_table);
software_node_unregister_node_group(tink_swnodes);
}
module_exit(tink_board_exit);

View File

@ -12,13 +12,13 @@
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/input-event-codes.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio_keys.h>
#include <linux/gpio/machine.h>
#include <linux/input.h>
#include <linux/property.h>
#include <linux/platform_data/gpio/gpio-amd-fch.h>
/*
@ -72,60 +72,91 @@ static const struct amd_fch_gpio_pdata board_apu2 = {
.gpio_names = apu2_gpio_names,
};
static const struct software_node apu2_gpiochip_node = {
.name = AMD_FCH_GPIO_DRIVER_NAME,
};
/* GPIO LEDs device */
static const struct gpio_led apu2_leds[] = {
{ .name = "apu:green:1" },
{ .name = "apu:green:2" },
{ .name = "apu:green:3" },
static const struct software_node apu2_leds_node = {
.name = "apu2-leds",
};
static const struct gpio_led_platform_data apu2_leds_pdata = {
.num_leds = ARRAY_SIZE(apu2_leds),
.leds = apu2_leds,
static const struct property_entry apu2_led1_props[] = {
PROPERTY_ENTRY_STRING("label", "apu:green:1"),
PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
APU2_GPIO_LINE_LED1, GPIO_ACTIVE_LOW),
{ }
};
static struct gpiod_lookup_table gpios_led_table = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
NULL, 0, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2,
NULL, 1, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3,
NULL, 2, GPIO_ACTIVE_LOW),
{} /* Terminating entry */
}
static const struct software_node apu2_led1_swnode = {
.name = "led-1",
.parent = &apu2_leds_node,
.properties = apu2_led1_props,
};
static const struct property_entry apu2_led2_props[] = {
PROPERTY_ENTRY_STRING("label", "apu:green:2"),
PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
APU2_GPIO_LINE_LED2, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node apu2_led2_swnode = {
.name = "led-2",
.parent = &apu2_leds_node,
.properties = apu2_led2_props,
};
static const struct property_entry apu2_led3_props[] = {
PROPERTY_ENTRY_STRING("label", "apu:green:3"),
PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
APU2_GPIO_LINE_LED3, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node apu2_led3_swnode = {
.name = "led-3",
.parent = &apu2_leds_node,
.properties = apu2_led3_props,
};
/* GPIO keyboard device */
static struct gpio_keys_button apu2_keys_buttons[] = {
{
.code = KEY_RESTART,
.active_low = 1,
.desc = "front button",
.type = EV_KEY,
.debounce_interval = 10,
.value = 1,
},
static const struct property_entry apu2_keys_props[] = {
PROPERTY_ENTRY_U32("poll-interval", 100),
{ }
};
static const struct gpio_keys_platform_data apu2_keys_pdata = {
.buttons = apu2_keys_buttons,
.nbuttons = ARRAY_SIZE(apu2_keys_buttons),
.poll_interval = 100,
.rep = 0,
.name = "apu2-keys",
static const struct software_node apu2_keys_node = {
.name = "apu2-keys",
.properties = apu2_keys_props,
};
static struct gpiod_lookup_table gpios_key_table = {
.dev_id = "gpio-keys-polled",
.table = {
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
NULL, 0, GPIO_ACTIVE_LOW),
{} /* Terminating entry */
}
static const struct property_entry apu2_front_button_props[] = {
PROPERTY_ENTRY_STRING("label", "front button"),
PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
APU2_GPIO_LINE_MODESW, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 10),
{ }
};
static const struct software_node apu2_front_button_swnode = {
.name = "front-button",
.parent = &apu2_keys_node,
.properties = apu2_front_button_props,
};
static const struct software_node *apu2_swnodes[] = {
&apu2_gpiochip_node,
/* LEDs nodes */
&apu2_leds_node,
&apu2_led1_swnode,
&apu2_led2_swnode,
&apu2_led3_swnode,
/* Keys nodes */
&apu2_keys_node,
&apu2_front_button_swnode,
NULL
};
/* Board setup */
@ -222,23 +253,25 @@ static struct platform_device *apu_gpio_pdev;
static struct platform_device *apu_leds_pdev;
static struct platform_device *apu_keys_pdev;
static struct platform_device * __init apu_create_pdev(
const char *name,
const void *pdata,
size_t sz)
static struct platform_device * __init apu_create_pdev(const char *name,
const void *data, size_t size,
const struct software_node *swnode)
{
struct platform_device_info pdev_info = {
.name = name,
.id = PLATFORM_DEVID_NONE,
.data = data,
.size_data = size,
.fwnode = software_node_fwnode(swnode),
};
struct platform_device *pdev;
int err;
pdev = platform_device_register_resndata(NULL,
name,
PLATFORM_DEVID_NONE,
NULL,
0,
pdata,
sz);
pdev = platform_device_register_full(&pdev_info);
if (IS_ERR(pdev))
pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
err = PTR_ERR_OR_ZERO(pdev);
if (err)
pr_err("failed registering %s: %d\n", name, err);
return pdev;
}
@ -246,6 +279,7 @@ static struct platform_device * __init apu_create_pdev(
static int __init apu_board_init(void)
{
const struct dmi_system_id *id;
int err;
id = dmi_first_match(apu_gpio_dmi_table);
if (!id) {
@ -253,35 +287,45 @@ static int __init apu_board_init(void)
return -ENODEV;
}
gpiod_add_lookup_table(&gpios_led_table);
gpiod_add_lookup_table(&gpios_key_table);
err = software_node_register_node_group(apu2_swnodes);
if (err) {
pr_err("failed to register software nodes: %d\n", err);
return err;
}
apu_gpio_pdev = apu_create_pdev(
AMD_FCH_GPIO_DRIVER_NAME,
id->driver_data,
sizeof(struct amd_fch_gpio_pdata));
apu_gpio_pdev = apu_create_pdev(AMD_FCH_GPIO_DRIVER_NAME,
id->driver_data, sizeof(struct amd_fch_gpio_pdata), NULL);
err = PTR_ERR_OR_ZERO(apu_gpio_pdev);
if (err)
goto err_unregister_swnodes;
apu_leds_pdev = apu_create_pdev(
"leds-gpio",
&apu2_leds_pdata,
sizeof(apu2_leds_pdata));
apu_leds_pdev = apu_create_pdev("leds-gpio", NULL, 0, &apu2_leds_node);
err = PTR_ERR_OR_ZERO(apu_leds_pdev);
if (err)
goto err_unregister_gpio;
apu_keys_pdev = apu_create_pdev(
"gpio-keys-polled",
&apu2_keys_pdata,
sizeof(apu2_keys_pdata));
apu_keys_pdev = apu_create_pdev("gpio-keys-polled", NULL, 0, &apu2_keys_node);
err = PTR_ERR_OR_ZERO(apu_keys_pdev);
if (err)
goto err_unregister_leds;
return 0;
err_unregister_leds:
platform_device_unregister(apu_leds_pdev);
err_unregister_gpio:
platform_device_unregister(apu_gpio_pdev);
err_unregister_swnodes:
software_node_unregister_node_group(apu2_swnodes);
return err;
}
static void __exit apu_board_exit(void)
{
gpiod_remove_lookup_table(&gpios_led_table);
gpiod_remove_lookup_table(&gpios_key_table);
platform_device_unregister(apu_keys_pdev);
platform_device_unregister(apu_leds_pdev);
platform_device_unregister(apu_gpio_pdev);
software_node_unregister_node_group(apu2_swnodes);
}
module_init(apu_board_init);

View File

@ -5,15 +5,13 @@
* Tested on:
* - Portwell NANO-6064
*
* This driver provides support for GPIO and Watchdog Timer
* functionalities of the Portwell boards with ITE embedded controller (EC).
* This driver supports Portwell boards with an ITE embedded controller (EC).
* The EC is accessed through I/O ports and provides:
* - Temperature and voltage readings (hwmon)
* - 8 GPIO pins for control and monitoring
* - Hardware watchdog with 1-15300 second timeout range
*
* It integrates with the Linux GPIO and Watchdog subsystems, allowing
* userspace interaction with EC GPIO pins and watchdog control,
* ensuring system stability and configurability.
* It integrates with the Linux hwmon, GPIO and Watchdog subsystems.
*
* (C) Copyright 2025 Portwell, Inc.
* Author: Yen-Chi Huang (jesse.huang@portwell.com.tw)
@ -22,16 +20,20 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/dmi.h>
#include <linux/gpio/driver.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/units.h>
#include <linux/watchdog.h>
#define PORTWELL_EC_IOSPACE 0xe300
@ -41,6 +43,9 @@
#define PORTWELL_GPIO_DIR_REG 0x2b
#define PORTWELL_GPIO_VAL_REG 0x2c
#define PORTWELL_HWMON_TEMP_NUM 3
#define PORTWELL_HWMON_VOLT_NUM 5
#define PORTWELL_WDT_EC_CONFIG_ADDR 0x06
#define PORTWELL_WDT_CONFIG_ENABLE 0x1
#define PORTWELL_WDT_CONFIG_DISABLE 0x0
@ -52,16 +57,60 @@
#define PORTWELL_EC_FW_VENDOR_LENGTH 3
#define PORTWELL_EC_FW_VENDOR_NAME "PWG"
#define PORTWELL_EC_ADC_MAX 1023
static bool force;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force loading EC driver without checking DMI boardname");
/* A sensor's metadata (label, scale, and register) */
struct pwec_sensor_prop {
const char *label;
u8 reg;
u32 scale;
};
/* Master configuration with properties for all possible sensors */
static const struct {
const struct pwec_sensor_prop temp_props[PORTWELL_HWMON_TEMP_NUM];
const struct pwec_sensor_prop in_props[PORTWELL_HWMON_VOLT_NUM];
} pwec_master_data = {
.temp_props = {
{ "CPU Temperature", 0x00, 0 },
{ "System Temperature", 0x02, 0 },
{ "Aux Temperature", 0x04, 0 },
},
.in_props = {
{ "Vcore", 0x20, 3000 },
{ "3.3V", 0x22, 6000 },
{ "5V", 0x24, 9600 },
{ "12V", 0x30, 19800 },
{ "VDIMM", 0x32, 3000 },
},
};
struct pwec_board_info {
u32 temp_mask; /* bit N = temperature channel N */
u32 in_mask; /* bit N = voltage channel N */
};
static const struct pwec_board_info pwec_board_info_default = {
.temp_mask = GENMASK(PORTWELL_HWMON_TEMP_NUM - 1, 0),
.in_mask = GENMASK(PORTWELL_HWMON_VOLT_NUM - 1, 0),
};
static const struct pwec_board_info pwec_board_info_nano = {
.temp_mask = BIT(0) | BIT(1),
.in_mask = GENMASK(4, 0),
};
static const struct dmi_system_id pwec_dmi_table[] = {
{
.ident = "NANO-6064 series",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "NANO-6064"),
},
.driver_data = (void *)&pwec_board_info_nano,
},
{ }
};
@ -79,6 +128,20 @@ static u8 pwec_read(u8 address)
return inb(PORTWELL_EC_IOSPACE + address);
}
/* Ensure consistent 16-bit read across potential MSB rollover. */
static u16 pwec_read16_stable(u8 lsb_reg)
{
u8 lsb, msb, old_msb;
do {
old_msb = pwec_read(lsb_reg + 1);
lsb = pwec_read(lsb_reg);
msb = pwec_read(lsb_reg + 1);
} while (msb != old_msb);
return (msb << 8) | lsb;
}
/* GPIO functions */
static int pwec_gpio_get(struct gpio_chip *chip, unsigned int offset)
@ -204,6 +267,81 @@ static struct watchdog_device ec_wdt_dev = {
.max_timeout = PORTWELL_WDT_EC_MAX_COUNT_SECOND,
};
/* HWMON functions */
static umode_t pwec_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct pwec_board_info *info = drvdata;
switch (type) {
case hwmon_temp:
return (info->temp_mask & BIT(channel)) ? 0444 : 0;
case hwmon_in:
return (info->in_mask & BIT(channel)) ? 0444 : 0;
default:
return 0;
}
}
static int pwec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
u16 tmp16;
switch (type) {
case hwmon_temp:
*val = pwec_read(pwec_master_data.temp_props[channel].reg) * MILLIDEGREE_PER_DEGREE;
return 0;
case hwmon_in:
tmp16 = pwec_read16_stable(pwec_master_data.in_props[channel].reg);
*val = (tmp16 * pwec_master_data.in_props[channel].scale) / PORTWELL_EC_ADC_MAX;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int pwec_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
switch (type) {
case hwmon_temp:
*str = pwec_master_data.temp_props[channel].label;
return 0;
case hwmon_in:
*str = pwec_master_data.in_props[channel].label;
return 0;
default:
return -EOPNOTSUPP;
}
}
static const struct hwmon_channel_info *pwec_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
NULL
};
static const struct hwmon_ops pwec_hwmon_ops = {
.is_visible = pwec_hwmon_is_visible,
.read = pwec_hwmon_read,
.read_string = pwec_hwmon_read_string,
};
static const struct hwmon_chip_info pwec_chip_info = {
.ops = &pwec_hwmon_ops,
.info = pwec_hwmon_info,
};
static int pwec_firmware_vendor_check(void)
{
u8 buf[PORTWELL_EC_FW_VENDOR_LENGTH + 1];
@ -218,6 +356,8 @@ static int pwec_firmware_vendor_check(void)
static int pwec_probe(struct platform_device *pdev)
{
struct device *hwmon_dev;
void *drvdata = dev_get_platdata(&pdev->dev);
int ret;
if (!devm_request_region(&pdev->dev, PORTWELL_EC_IOSPACE,
@ -236,19 +376,40 @@ static int pwec_probe(struct platform_device *pdev)
return ret;
}
ec_wdt_dev.parent = &pdev->dev;
ret = devm_watchdog_register_device(&pdev->dev, &ec_wdt_dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register Portwell EC Watchdog\n");
return ret;
if (IS_REACHABLE(CONFIG_HWMON)) {
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"portwell_ec", drvdata, &pwec_chip_info, NULL);
ret = PTR_ERR_OR_ZERO(hwmon_dev);
if (ret)
return ret;
}
ec_wdt_dev.parent = &pdev->dev;
return devm_watchdog_register_device(&pdev->dev, &ec_wdt_dev);
}
static int pwec_suspend(struct device *dev)
{
if (watchdog_active(&ec_wdt_dev))
return pwec_wdt_stop(&ec_wdt_dev);
return 0;
}
static int pwec_resume(struct device *dev)
{
if (watchdog_active(&ec_wdt_dev))
return pwec_wdt_start(&ec_wdt_dev);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(pwec_dev_pm_ops, pwec_suspend, pwec_resume);
static struct platform_driver pwec_driver = {
.driver = {
.name = "portwell-ec",
.pm = pm_sleep_ptr(&pwec_dev_pm_ops),
},
.probe = pwec_probe,
};
@ -257,19 +418,26 @@ static struct platform_device *pwec_dev;
static int __init pwec_init(void)
{
const struct dmi_system_id *match;
const struct pwec_board_info *hwmon_data;
int ret;
if (!dmi_check_system(pwec_dmi_table)) {
match = dmi_first_match(pwec_dmi_table);
if (!match) {
if (!force)
return -ENODEV;
pr_warn("force load portwell-ec without DMI check\n");
hwmon_data = &pwec_board_info_default;
pr_warn("force load portwell-ec without DMI check, using full display config\n");
} else {
hwmon_data = match->driver_data;
}
ret = platform_driver_register(&pwec_driver);
if (ret)
return ret;
pwec_dev = platform_device_register_simple("portwell-ec", -1, NULL, 0);
pwec_dev = platform_device_register_data(NULL, "portwell-ec", PLATFORM_DEVID_NONE,
hwmon_data, sizeof(*hwmon_data));
if (IS_ERR(pwec_dev)) {
platform_driver_unregister(&pwec_driver);
return PTR_ERR(pwec_dev);

View File

@ -154,13 +154,6 @@ static void quickstart_notify_remove(void *context)
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, quickstart_notify);
}
static void quickstart_mutex_destroy(void *data)
{
struct mutex *lock = data;
mutex_destroy(lock);
}
static int quickstart_probe(struct platform_device *pdev)
{
struct quickstart_data *data;
@ -179,8 +172,7 @@ static int quickstart_probe(struct platform_device *pdev)
data->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, data);
mutex_init(&data->input_lock);
ret = devm_add_action_or_reset(&pdev->dev, quickstart_mutex_destroy, &data->input_lock);
ret = devm_mutex_init(&pdev->dev, &data->input_lock);
if (ret < 0)
return ret;

View File

@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0
/* WMI driver for Xiaomi Redmibooks */
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/unaligned.h>
#include <linux/wmi.h>
#include <uapi/linux/input-event-codes.h>
#define WMI_REDMIBOOK_KEYBOARD_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
#define AI_KEY_VALUE_MASK BIT(8)
static const struct key_entry redmi_wmi_keymap[] = {
{KE_KEY, 0x00000201, {KEY_SELECTIVE_SCREENSHOT}},
{KE_KEY, 0x00000301, {KEY_ALL_APPLICATIONS}},
{KE_KEY, 0x00001b01, {KEY_SETUP}},
/* AI button has code for each position */
{KE_KEY, 0x00011801, {KEY_ASSISTANT}},
{KE_KEY, 0x00011901, {KEY_ASSISTANT}},
/* Keyboard backlight */
{KE_IGNORE, 0x00000501, {}},
{KE_IGNORE, 0x00800501, {}},
{KE_IGNORE, 0x00050501, {}},
{KE_IGNORE, 0x000a0501, {}},
{KE_END}
};
struct redmi_wmi {
struct input_dev *input_dev;
/* Protects the key event sequence */
struct mutex key_lock;
};
static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct redmi_wmi *data;
int err;
/* Init dev */
data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, data);
err = devm_mutex_init(&wdev->dev, &data->key_lock);
if (err)
return err;
data->input_dev = devm_input_allocate_device(&wdev->dev);
if (!data->input_dev)
return -ENOMEM;
data->input_dev->name = "Redmibook WMI keys";
data->input_dev->phys = "wmi/input0";
err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL);
if (err)
return err;
return input_register_device(data->input_dev);
}
static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
{
struct key_entry *entry;
struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
bool autorelease = true;
u32 payload;
int value = 1;
if (obj->type != ACPI_TYPE_BUFFER) {
dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
return;
}
if (obj->buffer.length < 32) {
dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
return;
}
payload = get_unaligned_le32(obj->buffer.pointer);
entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
if (!entry) {
dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
return;
}
/* AI key quirk */
if (entry->keycode == KEY_ASSISTANT) {
value = !(payload & AI_KEY_VALUE_MASK);
autorelease = false;
}
guard(mutex)(&data->key_lock);
sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
}
static const struct wmi_device_id redmi_wmi_id_table[] = {
{ WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL },
{ }
};
static struct wmi_driver redmi_wmi_driver = {
.driver = {
.name = "redmi-wmi",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = redmi_wmi_id_table,
.probe = redmi_wmi_probe,
.notify = redmi_wmi_notify,
.no_singleton = true,
};
module_wmi_driver(redmi_wmi_driver);
MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table);
MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>");
MODULE_DESCRIPTION("Redmibook WMI driver");
MODULE_LICENSE("GPL");

View File

@ -6,4 +6,4 @@
obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
asus.o lenovo.o other.o
acer.o asus.o lenovo.o other.o

View File

@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Board info for Acer X86 tablets which ship with Android as the factory image
* and which have broken DSDT tables. The factory kernels shipped on these
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2025 Hans de Goede <hansg@kernel.org>
*/
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include "shared-psy-info.h"
#include "x86-android-tablets.h"
/* Acer Iconia One 8 A1-840 (non FHD version) */
static const struct property_entry acer_a1_840_bq24190_props[] = {
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node),
PROPERTY_ENTRY_BOOL("omit-battery-class"),
PROPERTY_ENTRY_BOOL("disable-reset"),
{ }
};
static const struct software_node acer_a1_840_bq24190_node = {
.properties = acer_a1_840_bq24190_props,
};
static const struct property_entry acer_a1_840_touchscreen_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 800),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[1], 26, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node acer_a1_840_touchscreen_node = {
.properties = acer_a1_840_touchscreen_props,
};
static const struct x86_i2c_client_info acer_a1_840_i2c_clients[] __initconst = {
{
/* BQ24297 charger IC */
.board_info = {
.type = "bq24297",
.addr = 0x6b,
.dev_name = "bq24297",
.swnode = &acer_a1_840_bq24190_node,
.platform_data = &bq24190_pdata,
},
.adapter_path = "\\_SB_.I2C1",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
.chip = "INT33FC:02",
.index = 2,
.trigger = ACPI_EDGE_SENSITIVE,
.polarity = ACPI_ACTIVE_LOW,
.con_id = "bq24297_irq",
},
}, {
/* MPU6515 sensors */
.board_info = {
.type = "mpu6515",
.addr = 0x69,
.dev_name = "mpu6515",
},
.adapter_path = "\\_SB_.I2C3",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_APIC,
.index = 0x47,
.trigger = ACPI_EDGE_SENSITIVE,
.polarity = ACPI_ACTIVE_HIGH,
},
}, {
/* FT5416 touchscreen controller */
.board_info = {
.type = "edt-ft5x06",
.addr = 0x38,
.dev_name = "ft5416",
.swnode = &acer_a1_840_touchscreen_node,
},
.adapter_path = "\\_SB_.I2C4",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_APIC,
.index = 0x45,
.trigger = ACPI_EDGE_SENSITIVE,
.polarity = ACPI_ACTIVE_HIGH,
},
}
};
static const struct property_entry acer_a1_840_int3496_props[] __initconst = {
PROPERTY_ENTRY_GPIO("mux-gpios", &baytrail_gpiochip_nodes[2], 1, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("id-gpios", &baytrail_gpiochip_nodes[2], 18, GPIO_ACTIVE_HIGH),
{ }
};
static const struct platform_device_info acer_a1_840_pdevs[] __initconst = {
{
/* For micro USB ID pin handling */
.name = "intel-int3496",
.id = PLATFORM_DEVID_NONE,
.properties = acer_a1_840_int3496_props,
},
};
/* Properties for the Dollar Cove TI PMIC battery MFD child used as fuel-gauge */
static const struct property_entry acer_a1_840_fg_props[] = {
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node),
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1),
PROPERTY_ENTRY_GPIO("charged-gpios", &baytrail_gpiochip_nodes[2], 10, GPIO_ACTIVE_HIGH),
{ }
};
static struct device *acer_a1_840_fg_dev;
static struct fwnode_handle *acer_a1_840_fg_node;
static int __init acer_a1_840_init(struct device *dev)
{
int ret;
acer_a1_840_fg_dev = bus_find_device_by_name(&platform_bus_type, NULL, "chtdc_ti_battery");
if (!acer_a1_840_fg_dev)
return dev_err_probe(dev, -EPROBE_DEFER, "getting chtdc_ti_battery dev\n");
acer_a1_840_fg_node = fwnode_create_software_node(acer_a1_840_fg_props, NULL);
if (IS_ERR(acer_a1_840_fg_node)) {
ret = PTR_ERR(acer_a1_840_fg_node);
goto err_put;
}
ret = device_add_software_node(acer_a1_840_fg_dev,
to_software_node(acer_a1_840_fg_node));
if (ret)
goto err_put;
return 0;
err_put:
fwnode_handle_put(acer_a1_840_fg_node);
acer_a1_840_fg_node = NULL;
put_device(acer_a1_840_fg_dev);
acer_a1_840_fg_dev = NULL;
return ret;
}
static void acer_a1_840_exit(void)
{
device_remove_software_node(acer_a1_840_fg_dev);
/*
* Skip fwnode_handle_put(acer_a1_840_fg_node), instead leak the node.
* The intel_dc_ti_battery driver may still reference the strdup-ed
* "supplied-from" string. This string will be free-ed if the node
* is released.
*/
acer_a1_840_fg_node = NULL;
put_device(acer_a1_840_fg_dev);
acer_a1_840_fg_dev = NULL;
}
static const char * const acer_a1_840_modules[] __initconst = {
"bq24190_charger", /* For the Vbus regulator for intel-int3496 */
NULL
};
const struct x86_dev_info acer_a1_840_info __initconst = {
.i2c_client_info = acer_a1_840_i2c_clients,
.i2c_client_count = ARRAY_SIZE(acer_a1_840_i2c_clients),
.pdev_info = acer_a1_840_pdevs,
.pdev_count = ARRAY_SIZE(acer_a1_840_pdevs),
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
.swnode_group = generic_lipo_4v2_battery_swnodes,
.modules = acer_a1_840_modules,
.init = acer_a1_840_init,
.exit = acer_a1_840_exit,
};
/* Acer Iconia One 7 B1-750 has an Android factory image with everything hardcoded */
static const char * const acer_b1_750_mount_matrix[] = {
"-1", "0", "0",
"0", "1", "0",
"0", "0", "1"
};
static const struct property_entry acer_b1_750_bma250e_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix),
{ }
};
static const struct software_node acer_b1_750_bma250e_node = {
.properties = acer_b1_750_bma250e_props,
};
static const struct property_entry acer_b1_750_novatek_props[] = {
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[1], 26, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node acer_b1_750_novatek_node = {
.properties = acer_b1_750_novatek_props,
};
static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = {
{
/* Novatek NVT-ts touchscreen */
.board_info = {
.type = "nt11205-ts",
.addr = 0x34,
.dev_name = "NVT-ts",
.swnode = &acer_b1_750_novatek_node,
},
.adapter_path = "\\_SB_.I2C4",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
.chip = "INT33FC:02",
.index = 3,
.trigger = ACPI_EDGE_SENSITIVE,
.polarity = ACPI_ACTIVE_LOW,
.con_id = "NVT-ts_irq",
},
}, {
/* BMA250E accelerometer */
.board_info = {
.type = "bma250e",
.addr = 0x18,
.swnode = &acer_b1_750_bma250e_node,
},
.adapter_path = "\\_SB_.I2C3",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
.chip = "INT33FC:02",
.index = 25,
.trigger = ACPI_LEVEL_SENSITIVE,
.polarity = ACPI_ACTIVE_HIGH,
.con_id = "bma250e_irq",
},
},
};
const struct x86_dev_info acer_b1_750_info __initconst = {
.i2c_client_info = acer_b1_750_i2c_clients,
.i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients),
.pdev_info = int3496_pdevs,
.pdev_count = 1,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};

View File

@ -5,36 +5,55 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#include <linux/gpio/machine.h>
#include <linux/input.h>
#include <linux/gpio/property.h>
#include <linux/input-event-codes.h>
#include <linux/platform_device.h>
#include "shared-psy-info.h"
#include "x86-android-tablets.h"
/* Asus ME176C and TF103C tablets shared data */
static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = {
.dev_id = "intel-int3496",
.table = {
GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH),
{ }
static const struct property_entry asus_me176c_tf103c_int3496_props[] __initconst = {
PROPERTY_ENTRY_GPIO("id-gpios", &baytrail_gpiochip_nodes[2], 22, GPIO_ACTIVE_HIGH),
{ }
};
static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = {
{
/* For micro USB ID pin handling */
.name = "intel-int3496",
.id = PLATFORM_DEVID_NONE,
.properties = asus_me176c_tf103c_int3496_props,
},
};
static const struct x86_gpio_button asus_me176c_tf103c_lid __initconst = {
.button = {
.code = SW_LID,
.active_low = true,
.desc = "lid_sw",
.type = EV_SW,
.wakeup = true,
.debounce_interval = 50,
},
.chip = "INT33FC:02",
.pin = 12,
static const struct software_node asus_me176c_tf103c_gpio_keys_node = {
.name = "lid_sw",
};
static const struct property_entry asus_me176c_tf103c_lid_props[] = {
PROPERTY_ENTRY_U32("linux,input-type", EV_SW),
PROPERTY_ENTRY_U32("linux,code", SW_LID),
PROPERTY_ENTRY_STRING("label", "lid_sw"),
PROPERTY_ENTRY_GPIO("gpios", &baytrail_gpiochip_nodes[2], 12, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
PROPERTY_ENTRY_BOOL("wakeup-source"),
{ }
};
static const struct software_node asus_me176c_tf103c_lid_node = {
.parent = &asus_me176c_tf103c_gpio_keys_node,
.properties = asus_me176c_tf103c_lid_props,
};
static const struct software_node *asus_me176c_tf103c_lid_swnodes[] = {
&asus_me176c_tf103c_gpio_keys_node,
&asus_me176c_tf103c_lid_node,
NULL
};
/* Asus ME176C tablets have an Android factory image with everything hardcoded */
@ -77,6 +96,16 @@ static const struct software_node asus_me176c_ug3105_node = {
.properties = asus_me176c_ug3105_props,
};
static const struct property_entry asus_me176c_touchscreen_props[] = {
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[0], 60, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("irq-gpios", &baytrail_gpiochip_nodes[2], 28, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node asus_me176c_touchscreen_node = {
.properties = asus_me176c_touchscreen_props,
};
static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = {
{
/* bq24297 battery charger */
@ -132,6 +161,7 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst =
.type = "GDIX1001:00",
.addr = 0x14,
.dev_name = "goodix_ts",
.swnode = &asus_me176c_touchscreen_node,
},
.adapter_path = "\\_SB_.I2C6",
.irq_data = {
@ -152,33 +182,17 @@ static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = {
},
};
static struct gpiod_lookup_table asus_me176c_goodix_gpios = {
.dev_id = "i2c-goodix_ts",
.table = {
GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table * const asus_me176c_gpios[] = {
&int3496_gpo2_pin22_gpios,
&asus_me176c_goodix_gpios,
NULL
};
const struct x86_dev_info asus_me176c_info __initconst = {
.i2c_client_info = asus_me176c_i2c_clients,
.i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients),
.pdev_info = int3496_pdevs,
.pdev_count = 1,
.pdev_info = asus_me176c_tf103c_pdevs,
.pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs),
.serdev_info = asus_me176c_serdevs,
.serdev_count = ARRAY_SIZE(asus_me176c_serdevs),
.gpio_button = &asus_me176c_tf103c_lid,
.gpio_button_count = 1,
.gpiod_lookup_tables = asus_me176c_gpios,
.bat_swnode = &generic_lipo_hv_4v35_battery_node,
.gpio_button_swnodes = asus_me176c_tf103c_lid_swnodes,
.swnode_group = generic_lipo_hv_4v35_battery_swnodes,
.modules = bq24190_modules,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/* Asus TF103C tablets have an Android factory image with everything hardcoded */
@ -293,19 +307,13 @@ static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst =
},
};
static struct gpiod_lookup_table * const asus_tf103c_gpios[] = {
&int3496_gpo2_pin22_gpios,
NULL
};
const struct x86_dev_info asus_tf103c_info __initconst = {
.i2c_client_info = asus_tf103c_i2c_clients,
.i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients),
.pdev_info = int3496_pdevs,
.pdev_count = 1,
.gpio_button = &asus_me176c_tf103c_lid,
.gpio_button_count = 1,
.gpiod_lookup_tables = asus_tf103c_gpios,
.bat_swnode = &generic_lipo_4v2_battery_node,
.pdev_info = asus_me176c_tf103c_pdevs,
.pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs),
.gpio_button_swnodes = asus_me176c_tf103c_lid_swnodes,
.swnode_group = generic_lipo_4v2_battery_swnodes,
.modules = bq24190_modules,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};

View File

@ -5,7 +5,7 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -152,9 +152,9 @@ static struct i2c_client **i2c_clients;
static struct spi_device **spi_devs;
static struct platform_device **pdevs;
static struct serdev_device **serdevs;
static struct gpio_keys_button *buttons;
static struct gpiod_lookup_table * const *gpiod_lookup_tables;
static const struct software_node *bat_swnode;
static const struct software_node **gpio_button_swnodes;
static const struct software_node **swnode_group;
static const struct software_node **gpiochip_node_group;
static void (*exit_handler)(void);
static __init struct i2c_adapter *
@ -265,8 +265,7 @@ static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, i
spi_devs[idx] = spi_new_device(controller, &board_info);
put_device(&controller->dev);
if (!spi_devs[idx])
return dev_err_probe(&controller->dev, -ENOMEM,
"creating SPI-device %d\n", idx);
return -ENOMEM;
return 0;
}
@ -277,8 +276,10 @@ get_serdev_controller_by_pci_parent(const struct x86_serdev_info *info)
struct pci_dev *pdev;
pdev = pci_get_domain_bus_and_slot(0, 0, info->ctrl.pci.devfn);
if (!pdev)
return ERR_PTR(-EPROBE_DEFER);
if (!pdev) {
pr_err("error could not get PCI serdev at devfn 0x%02x\n", info->ctrl.pci.devfn);
return ERR_PTR(-ENODEV);
}
/* This puts our reference on pdev and returns a ref on the ctrl */
return get_serdev_controller_from_parent(&pdev->dev, 0, info->ctrl_devname);
@ -331,6 +332,34 @@ put_ctrl_dev:
return ret;
}
const struct software_node baytrail_gpiochip_nodes[] = {
{ .name = "INT33FC:00" },
{ .name = "INT33FC:01" },
{ .name = "INT33FC:02" },
};
static const struct software_node *baytrail_gpiochip_node_group[] = {
&baytrail_gpiochip_nodes[0],
&baytrail_gpiochip_nodes[1],
&baytrail_gpiochip_nodes[2],
NULL
};
const struct software_node cherryview_gpiochip_nodes[] = {
{ .name = "INT33FF:00" },
{ .name = "INT33FF:01" },
{ .name = "INT33FF:02" },
{ .name = "INT33FF:03" },
};
static const struct software_node *cherryview_gpiochip_node_group[] = {
&cherryview_gpiochip_nodes[0],
&cherryview_gpiochip_nodes[1],
&cherryview_gpiochip_nodes[2],
&cherryview_gpiochip_nodes[3],
NULL
};
static void x86_android_tablet_remove(struct platform_device *pdev)
{
int i;
@ -346,7 +375,6 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
platform_device_unregister(pdevs[i]);
kfree(pdevs);
kfree(buttons);
for (i = spi_dev_count - 1; i >= 0; i--)
spi_unregister_device(spi_devs[i]);
@ -361,10 +389,9 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
if (exit_handler)
exit_handler();
for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
gpiod_remove_lookup_table(gpiod_lookup_tables[i]);
software_node_unregister(bat_swnode);
software_node_unregister_node_group(gpio_button_swnodes);
software_node_unregister_node_group(swnode_group);
software_node_unregister_node_group(gpiochip_node_group);
}
static __init int x86_android_tablet_probe(struct platform_device *pdev)
@ -388,16 +415,28 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
for (i = 0; dev_info->modules && dev_info->modules[i]; i++)
request_module(dev_info->modules[i]);
bat_swnode = dev_info->bat_swnode;
if (bat_swnode) {
ret = software_node_register(bat_swnode);
if (ret)
return ret;
switch (dev_info->gpiochip_type) {
case X86_GPIOCHIP_BAYTRAIL:
gpiochip_node_group = baytrail_gpiochip_node_group;
break;
case X86_GPIOCHIP_CHERRYVIEW:
gpiochip_node_group = cherryview_gpiochip_node_group;
break;
case X86_GPIOCHIP_UNSPECIFIED:
gpiochip_node_group = NULL;
break;
}
gpiod_lookup_tables = dev_info->gpiod_lookup_tables;
for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
gpiod_add_lookup_table(gpiod_lookup_tables[i]);
ret = software_node_register_node_group(gpiochip_node_group);
if (ret)
return ret;
ret = software_node_register_node_group(dev_info->swnode_group);
if (ret) {
x86_android_tablet_remove(pdev);
return ret;
}
swnode_group = dev_info->swnode_group;
if (dev_info->init) {
ret = dev_info->init(&pdev->dev);
@ -470,38 +509,22 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
}
}
if (dev_info->gpio_button_count) {
struct gpio_keys_platform_data pdata = { };
struct gpio_desc *gpiod;
if (dev_info->gpio_button_swnodes) {
struct platform_device_info button_info = {
.name = "gpio-keys",
.id = PLATFORM_DEVID_AUTO,
};
buttons = kcalloc(dev_info->gpio_button_count, sizeof(*buttons), GFP_KERNEL);
if (!buttons) {
ret = software_node_register_node_group(dev_info->gpio_button_swnodes);
if (ret < 0) {
x86_android_tablet_remove(pdev);
return -ENOMEM;
return ret;
}
for (i = 0; i < dev_info->gpio_button_count; i++) {
ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip,
dev_info->gpio_button[i].pin,
dev_info->gpio_button[i].button.desc,
false, GPIOD_IN, &gpiod);
if (ret < 0) {
x86_android_tablet_remove(pdev);
return ret;
}
gpio_button_swnodes = dev_info->gpio_button_swnodes;
buttons[i] = dev_info->gpio_button[i].button;
buttons[i].gpio = desc_to_gpio(gpiod);
/* Release GPIO descriptor so that gpio-keys can request it */
devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
}
pdata.buttons = buttons;
pdata.nbuttons = dev_info->gpio_button_count;
pdevs[pdev_count] = platform_device_register_data(&pdev->dev, "gpio-keys",
PLATFORM_DEVID_AUTO,
&pdata, sizeof(pdata));
button_info.fwnode = software_node_fwnode(dev_info->gpio_button_swnodes[0]);
pdevs[pdev_count] = platform_device_register_full(&button_info);
if (IS_ERR(pdevs[pdev_count])) {
ret = PTR_ERR(pdevs[pdev_count]);
x86_android_tablet_remove(pdev);
@ -537,6 +560,6 @@ static void __exit x86_android_tablet_exit(void)
}
module_exit(x86_android_tablet_exit);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver");
MODULE_LICENSE("GPL");

View File

@ -5,7 +5,7 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#include <linux/dmi.h>
@ -16,6 +16,16 @@
#include "x86-android-tablets.h"
const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
{
/* Acer Iconia One 8 A1-840 (non FHD version) */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
/* Above strings are too generic also match BIOS date */
DMI_MATCH(DMI_BIOS_DATE, "04/01/2014"),
},
.driver_data = (void *)&acer_a1_840_info,
},
{
/* Acer Iconia One 7 B1-750 */
.matches = {

View File

@ -5,13 +5,15 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/efi.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/input-event-codes.h>
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
#include <linux/mfd/intel_soc_pmic.h>
@ -59,11 +61,30 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = {
.initial_brightness = 128,
};
static const struct software_node arizona_gpiochip_node = {
.name = "arizona",
};
static const struct software_node crystalcove_gpiochip_node = {
.name = "gpio_crystalcove",
};
/* Lenovo Yoga Book X90F / X90L's Android factory image has everything hardcoded */
static const struct property_entry lenovo_yb1_x90_goodix_props[] = {
PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[1], 53, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("irq-gpios", &cherryview_gpiochip_nodes[1], 56, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node lenovo_yb1_x90_goodix_node = {
.properties = lenovo_yb1_x90_goodix_props,
};
static const struct property_entry lenovo_yb1_x90_wacom_props[] = {
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001),
PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 150),
PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 82, GPIO_ACTIVE_LOW),
{ }
};
@ -85,6 +106,7 @@ static const struct property_entry lenovo_yb1_x90_hideep_ts_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1920),
PROPERTY_ENTRY_U32("touchscreen-max-pressure", 16384),
PROPERTY_ENTRY_BOOL("hideep,force-native-protocol"),
PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 7, GPIO_ACTIVE_LOW),
{ }
};
@ -108,6 +130,7 @@ static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst
.type = "GDIX1001:00",
.addr = 0x14,
.dev_name = "goodix_ts",
.swnode = &lenovo_yb1_x90_goodix_node,
},
.adapter_path = "\\_SB_.PCI0.I2C2",
.irq_data = {
@ -185,48 +208,33 @@ static const struct x86_serdev_info lenovo_yb1_x90_serdevs[] __initconst = {
},
};
static const struct x86_gpio_button lenovo_yb1_x90_lid __initconst = {
.button = {
.code = SW_LID,
.active_low = true,
.desc = "lid_sw",
.type = EV_SW,
.wakeup = true,
.debounce_interval = 50,
},
.chip = "INT33FF:02",
.pin = 19,
/*
* Software node attached to gpio-keys device representing the LID and
* serving as a parent to software nodes representing individual keys/buttons
* as required by the device tree binding.
*/
static const struct software_node lenovo_lid_gpio_keys_node = {
.name = "lid_sw",
};
static struct gpiod_lookup_table lenovo_yb1_x90_goodix_gpios = {
.dev_id = "i2c-goodix_ts",
.table = {
GPIO_LOOKUP("INT33FF:01", 53, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FF:01", 56, "irq", GPIO_ACTIVE_HIGH),
{ }
},
static const struct property_entry lenovo_yb1_x90_lid_props[] = {
PROPERTY_ENTRY_U32("linux,input-type", EV_SW),
PROPERTY_ENTRY_U32("linux,code", SW_LID),
PROPERTY_ENTRY_STRING("label", "lid_sw"),
PROPERTY_ENTRY_GPIO("gpios", &cherryview_gpiochip_nodes[2], 19, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
PROPERTY_ENTRY_BOOL("wakeup-source"),
{ }
};
static struct gpiod_lookup_table lenovo_yb1_x90_hideep_gpios = {
.dev_id = "i2c-hideep_ts",
.table = {
GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW),
{ }
},
static const struct software_node lenovo_yb1_x90_lid_node = {
.parent = &lenovo_lid_gpio_keys_node,
.properties = lenovo_yb1_x90_lid_props,
};
static struct gpiod_lookup_table lenovo_yb1_x90_wacom_gpios = {
.dev_id = "i2c-wacom",
.table = {
GPIO_LOOKUP("INT33FF:00", 82, "reset", GPIO_ACTIVE_LOW),
{ }
},
};
static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = {
&lenovo_yb1_x90_hideep_gpios,
&lenovo_yb1_x90_goodix_gpios,
&lenovo_yb1_x90_wacom_gpios,
static const struct software_node *lenovo_yb1_x90_lid_swnodes[] = {
&lenovo_lid_gpio_keys_node,
&lenovo_yb1_x90_lid_node,
NULL
};
@ -256,9 +264,8 @@ const struct x86_dev_info lenovo_yogabook_x90_info __initconst = {
.pdev_count = ARRAY_SIZE(lenovo_yb1_x90_pdevs),
.serdev_info = lenovo_yb1_x90_serdevs,
.serdev_count = ARRAY_SIZE(lenovo_yb1_x90_serdevs),
.gpio_button = &lenovo_yb1_x90_lid,
.gpio_button_count = 1,
.gpiod_lookup_tables = lenovo_yb1_x90_gpios,
.gpio_button_swnodes = lenovo_yb1_x90_lid_swnodes,
.gpiochip_type = X86_GPIOCHIP_CHERRYVIEW,
.init = lenovo_yb1_x90_init,
};
@ -294,17 +301,25 @@ static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = {
.properties = lenovo_yoga_tab2_830_1050_bq24190_props,
};
static const struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid __initconst = {
.button = {
.code = SW_LID,
.active_low = true,
.desc = "lid_sw",
.type = EV_SW,
.wakeup = true,
.debounce_interval = 50,
},
.chip = "INT33FC:02",
.pin = 26,
static const struct property_entry lenovo_yoga_tab2_830_1050_lid_props[] = {
PROPERTY_ENTRY_U32("linux,input-type", EV_SW),
PROPERTY_ENTRY_U32("linux,code", SW_LID),
PROPERTY_ENTRY_STRING("label", "lid_sw"),
PROPERTY_ENTRY_GPIO("gpios", &baytrail_gpiochip_nodes[2], 26, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
PROPERTY_ENTRY_BOOL("wakeup-source"),
{ }
};
static const struct software_node lenovo_yoga_tab2_830_1050_lid_node = {
.parent = &lenovo_lid_gpio_keys_node,
.properties = lenovo_yoga_tab2_830_1050_lid_props,
};
static const struct software_node *lenovo_yoga_tab2_830_1050_lid_swnodes[] = {
&lenovo_lid_gpio_keys_node,
&lenovo_yoga_tab2_830_1050_lid_node,
NULL
};
/* This gets filled by lenovo_yoga_tab2_830_1050_init() */
@ -384,47 +399,65 @@ static struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __init
},
};
static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = {
.dev_id = "intel-int3496",
.table = {
GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW),
GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH),
{ }
static const struct property_entry lenovo_yoga_tab2_830_1050_int3496_props[] __initconst = {
PROPERTY_ENTRY_GPIO("mux-gpios", &baytrail_gpiochip_nodes[2], 1, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_GPIO("id-gpios", &baytrail_gpiochip_nodes[2], 24, GPIO_ACTIVE_HIGH),
{ }
};
static const struct platform_device_info lenovo_yoga_tab2_830_1050_pdevs[] __initconst = {
{
/* For micro USB ID pin handling */
.name = "intel-int3496",
.id = PLATFORM_DEVID_NONE,
.properties = lenovo_yoga_tab2_830_1050_int3496_props,
},
};
#define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00"
static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = {
.dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME,
.table = {
GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW),
{ }
},
static const struct property_entry lenovo_yoga_tab2_830_1050_wm1502_props[] = {
PROPERTY_ENTRY_GPIO("reset-gpios",
&crystalcove_gpiochip_node, 3, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios",
&baytrail_gpiochip_nodes[1], 23, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios",
&arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios",
&arizona_gpiochip_node, 4, GPIO_ACTIVE_LOW),
{ }
};
static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = {
&lenovo_yoga_tab2_830_1050_int3496_gpios,
&lenovo_yoga_tab2_830_1050_codec_gpios,
static const struct software_node lenovo_yoga_tab2_830_1050_wm5102 = {
.properties = lenovo_yoga_tab2_830_1050_wm1502_props,
};
static const struct software_node *lenovo_yoga_tab2_830_1050_swnodes[] = {
&crystalcove_gpiochip_node,
&arizona_gpiochip_node,
&lenovo_yoga_tab2_830_1050_wm5102,
&generic_lipo_hv_4v35_battery_node,
NULL
};
static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev);
static void lenovo_yoga_tab2_830_1050_exit(void);
static const char * const lenovo_yoga_tab2_modules[] __initconst = {
"spi_pxa2xx_platform", /* For the SPI codec device */
"bq24190_charger", /* For the Vbus regulator for int3496/lc824206xa */
NULL
};
const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = {
.i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients,
.i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients),
.pdev_info = int3496_pdevs,
.pdev_count = 1,
.gpio_button = &lenovo_yoga_tab2_830_1050_lid,
.gpio_button_count = 1,
.gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios,
.bat_swnode = &generic_lipo_hv_4v35_battery_node,
.modules = bq24190_modules,
.pdev_info = lenovo_yoga_tab2_830_1050_pdevs,
.pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_pdevs),
.gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes,
.swnode_group = lenovo_yoga_tab2_830_1050_swnodes,
.modules = lenovo_yoga_tab2_modules,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
.init = lenovo_yoga_tab2_830_1050_init,
.exit = lenovo_yoga_tab2_830_1050_exit,
};
@ -481,6 +514,7 @@ static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map =
PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk",
"INT33FC:02", "pmu_clk2_grp", "pmu_clk");
static struct device *lenovo_yoga_tab2_830_1050_codec_dev;
static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl;
static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler;
@ -507,12 +541,18 @@ static int __init lenovo_yoga_tab2_830_1050_init_codec(void)
goto err_unregister_mappings;
}
/* We're done with the codec_dev now */
put_device(codec_dev);
ret = device_add_software_node(codec_dev, &lenovo_yoga_tab2_830_1050_wm5102);
if (ret) {
ret = dev_err_probe(codec_dev, ret, "adding software node\n");
goto err_put_pinctrl;
}
lenovo_yoga_tab2_830_1050_codec_dev = codec_dev;
lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl;
return 0;
err_put_pinctrl:
pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl);
err_unregister_mappings:
pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map);
err_put_device:
@ -560,10 +600,10 @@ static void lenovo_yoga_tab2_830_1050_exit(void)
{
unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler);
if (lenovo_yoga_tab2_830_1050_codec_pinctrl) {
pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl);
pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map);
}
device_remove_software_node(lenovo_yoga_tab2_830_1050_codec_dev);
pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl);
pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map);
put_device(lenovo_yoga_tab2_830_1050_codec_dev);
}
/*
@ -718,19 +758,21 @@ static const struct x86_i2c_client_info lenovo_yoga_tab2_1380_i2c_clients[] __in
}
};
static const struct property_entry lenovo_yoga_tab2_1380_fc_props[] __initconst = {
PROPERTY_ENTRY_GPIO("uart3_txd-gpios", &baytrail_gpiochip_nodes[0], 57, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("uart3_rxd-gpios", &baytrail_gpiochip_nodes[0], 61, GPIO_ACTIVE_HIGH),
{ }
};
static const struct platform_device_info lenovo_yoga_tab2_1380_pdevs[] __initconst = {
{
/* For the Tablet 2 Pro 1380's custom fast charging driver */
.name = "lenovo-yoga-tab2-pro-1380-fastcharger",
.id = PLATFORM_DEVID_NONE,
.properties = lenovo_yoga_tab2_1380_fc_props,
},
};
static const char * const lenovo_yoga_tab2_1380_modules[] __initconst = {
"bq24190_charger", /* For the Vbus regulator for lc824206xa */
NULL
};
static int __init lenovo_yoga_tab2_1380_init(struct device *dev)
{
int ret;
@ -752,31 +794,15 @@ static int __init lenovo_yoga_tab2_1380_init(struct device *dev)
return 0;
}
static struct gpiod_lookup_table lenovo_yoga_tab2_1380_fc_gpios = {
.dev_id = "serial0-0",
.table = {
GPIO_LOOKUP("INT33FC:00", 57, "uart3_txd", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:00", 61, "uart3_rxd", GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table * const lenovo_yoga_tab2_1380_gpios[] = {
&lenovo_yoga_tab2_830_1050_codec_gpios,
&lenovo_yoga_tab2_1380_fc_gpios,
NULL
};
const struct x86_dev_info lenovo_yoga_tab2_1380_info __initconst = {
.i2c_client_info = lenovo_yoga_tab2_1380_i2c_clients,
.i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_i2c_clients),
.pdev_info = lenovo_yoga_tab2_1380_pdevs,
.pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_pdevs),
.gpio_button = &lenovo_yoga_tab2_830_1050_lid,
.gpio_button_count = 1,
.gpiod_lookup_tables = lenovo_yoga_tab2_1380_gpios,
.bat_swnode = &generic_lipo_hv_4v35_battery_node,
.modules = lenovo_yoga_tab2_1380_modules,
.gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes,
.swnode_group = lenovo_yoga_tab2_830_1050_swnodes,
.modules = lenovo_yoga_tab2_modules,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
.init = lenovo_yoga_tab2_1380_init,
.exit = lenovo_yoga_tab2_830_1050_exit,
};
@ -824,6 +850,7 @@ static const struct property_entry lenovo_yt3_hideep_ts_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1600),
PROPERTY_ENTRY_U32("touchscreen-size-y", 2560),
PROPERTY_ENTRY_U32("touchscreen-max-pressure", 255),
PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 7, GPIO_ACTIVE_LOW),
{ }
};
@ -958,12 +985,34 @@ static struct arizona_pdata lenovo_yt3_wm5102_pdata = {
},
};
static const struct property_entry lenovo_yt3_wm1502_props[] = {
PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios",
&cherryview_gpiochip_nodes[0], 75, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios",
&cherryview_gpiochip_nodes[0], 81, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 82, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH),
{ }
};
static const struct software_node lenovo_yt3_wm5102 = {
.properties = lenovo_yt3_wm1502_props,
.name = "wm5102",
};
static const struct software_node *lenovo_yt3_swnodes[] = {
&arizona_gpiochip_node,
&lenovo_yt3_wm5102,
NULL
};
static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = {
{
/* WM5102 codec */
.board_info = {
.modalias = "wm5102",
.platform_data = &lenovo_yt3_wm5102_pdata,
.swnode = &lenovo_yt3_wm5102,
.max_speed_hz = 5000000,
},
.ctrl_path = "\\_SB_.PCI0.SPI1",
@ -1013,28 +1062,8 @@ static int __init lenovo_yt3_init(struct device *dev)
return 0;
}
static struct gpiod_lookup_table lenovo_yt3_hideep_gpios = {
.dev_id = "i2c-hideep_ts",
.table = {
GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW),
{ }
},
};
static struct gpiod_lookup_table lenovo_yt3_wm5102_gpios = {
.dev_id = "spi1.0",
.table = {
GPIO_LOOKUP("INT33FF:00", 75, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FF:00", 81, "wlf,ldoena", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FF:00", 82, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("arizona", 2, "wlf,micd-pol", GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table * const lenovo_yt3_gpios[] = {
&lenovo_yt3_hideep_gpios,
&lenovo_yt3_wm5102_gpios,
static const char * const lenovo_yt3_modules[] __initconst = {
"spi_pxa2xx_platform", /* For the SPI codec device */
NULL
};
@ -1043,6 +1072,8 @@ const struct x86_dev_info lenovo_yt3_info __initconst = {
.i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients),
.spi_dev_info = lenovo_yt3_spi_devs,
.spi_dev_count = ARRAY_SIZE(lenovo_yt3_spi_devs),
.gpiod_lookup_tables = lenovo_yt3_gpios,
.swnode_group = lenovo_yt3_swnodes,
.modules = lenovo_yt3_modules,
.gpiochip_type = X86_GPIOCHIP_CHERRYVIEW,
.init = lenovo_yt3_init,
};

View File

@ -5,12 +5,13 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#include <linux/acpi.h>
#include <linux/gpio/machine.h>
#include <linux/input.h>
#include <linux/gpio/property.h>
#include <linux/input-event-codes.h>
#include <linux/leds.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
@ -21,102 +22,38 @@
#include "shared-psy-info.h"
#include "x86-android-tablets.h"
/* Acer Iconia One 7 B1-750 has an Android factory image with everything hardcoded */
static const char * const acer_b1_750_mount_matrix[] = {
"-1", "0", "0",
"0", "1", "0",
"0", "0", "1"
};
static const struct property_entry acer_b1_750_bma250e_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix),
{ }
};
static const struct software_node acer_b1_750_bma250e_node = {
.properties = acer_b1_750_bma250e_props,
};
static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = {
{
/* Novatek NVT-ts touchscreen */
.board_info = {
.type = "nt11205-ts",
.addr = 0x34,
.dev_name = "NVT-ts",
},
.adapter_path = "\\_SB_.I2C4",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
.chip = "INT33FC:02",
.index = 3,
.trigger = ACPI_EDGE_SENSITIVE,
.polarity = ACPI_ACTIVE_LOW,
.con_id = "NVT-ts_irq",
},
}, {
/* BMA250E accelerometer */
.board_info = {
.type = "bma250e",
.addr = 0x18,
.swnode = &acer_b1_750_bma250e_node,
},
.adapter_path = "\\_SB_.I2C3",
.irq_data = {
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
.chip = "INT33FC:02",
.index = 25,
.trigger = ACPI_LEVEL_SENSITIVE,
.polarity = ACPI_ACTIVE_HIGH,
.con_id = "bma250e_irq",
},
},
};
static struct gpiod_lookup_table acer_b1_750_nvt_ts_gpios = {
.dev_id = "i2c-NVT-ts",
.table = {
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
{ }
},
};
static struct gpiod_lookup_table * const acer_b1_750_gpios[] = {
&acer_b1_750_nvt_ts_gpios,
&int3496_reference_gpios,
NULL
};
const struct x86_dev_info acer_b1_750_info __initconst = {
.i2c_client_info = acer_b1_750_i2c_clients,
.i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients),
.pdev_info = int3496_pdevs,
.pdev_count = 1,
.gpiod_lookup_tables = acer_b1_750_gpios,
};
/*
* Advantech MICA-071
* This is a standard Windows tablet, but it has an extra "quick launch" button
* which is not described in the ACPI tables in anyway.
* Use the x86-android-tablets infra to create a gpio-keys device for this.
*/
static const struct x86_gpio_button advantech_mica_071_button __initconst = {
.button = {
.code = KEY_PROG1,
.active_low = true,
.desc = "prog1_key",
.type = EV_KEY,
.wakeup = false,
.debounce_interval = 50,
},
.chip = "INT33FC:00",
.pin = 2,
static const struct software_node advantech_mica_071_gpio_keys_node = {
.name = "prog1_key",
};
static const struct property_entry advantech_mica_071_prog1_key_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_PROG1),
PROPERTY_ENTRY_STRING("label", "prog1_key"),
PROPERTY_ENTRY_GPIO("gpios", &baytrail_gpiochip_nodes[0], 2, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
{ }
};
static const struct software_node advantech_mica_071_prog1_key_node = {
.parent = &advantech_mica_071_gpio_keys_node,
.properties = advantech_mica_071_prog1_key_props,
};
static const struct software_node *advantech_mica_071_button_swnodes[] = {
&advantech_mica_071_gpio_keys_node,
&advantech_mica_071_prog1_key_node,
NULL
};
const struct x86_dev_info advantech_mica_071_info __initconst = {
.gpio_button = &advantech_mica_071_button,
.gpio_button_count = 1,
.gpio_button_swnodes = advantech_mica_071_button_swnodes,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/*
@ -212,36 +149,46 @@ const struct x86_dev_info chuwi_hi8_info __initconst = {
* in the button row with the power + volume-buttons labeled P and F.
* Use the x86-android-tablets infra to create a gpio-keys device for these.
*/
static const struct x86_gpio_button cyberbook_t116_buttons[] __initconst = {
{
.button = {
.code = KEY_PROG1,
.active_low = true,
.desc = "prog1_key",
.type = EV_KEY,
.wakeup = false,
.debounce_interval = 50,
},
.chip = "INT33FF:00",
.pin = 30,
},
{
.button = {
.code = KEY_PROG2,
.active_low = true,
.desc = "prog2_key",
.type = EV_KEY,
.wakeup = false,
.debounce_interval = 50,
},
.chip = "INT33FF:03",
.pin = 48,
},
static const struct software_node cyberbook_t116_gpio_keys_node = {
.name = "prog_keys",
};
static const struct property_entry cyberbook_t116_prog1_key_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_PROG1),
PROPERTY_ENTRY_STRING("label", "prog1_key"),
PROPERTY_ENTRY_GPIO("gpios", &cherryview_gpiochip_nodes[0], 30, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
{ }
};
static const struct software_node cyberbook_t116_prog1_key_node = {
.parent = &cyberbook_t116_gpio_keys_node,
.properties = cyberbook_t116_prog1_key_props,
};
static const struct property_entry cyberbook_t116_prog2_key_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_PROG2),
PROPERTY_ENTRY_STRING("label", "prog2_key"),
PROPERTY_ENTRY_GPIO("gpios", &cherryview_gpiochip_nodes[3], 48, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
{ }
};
static const struct software_node cyberbook_t116_prog2_key_node = {
.parent = &cyberbook_t116_gpio_keys_node,
.properties = cyberbook_t116_prog2_key_props,
};
static const struct software_node *cyberbook_t116_buttons_swnodes[] = {
&cyberbook_t116_gpio_keys_node,
&cyberbook_t116_prog1_key_node,
&cyberbook_t116_prog2_key_node,
NULL
};
const struct x86_dev_info cyberbook_t116_info __initconst = {
.gpio_button = cyberbook_t116_buttons,
.gpio_button_count = ARRAY_SIZE(cyberbook_t116_buttons),
.gpio_button_swnodes = cyberbook_t116_buttons_swnodes,
.gpiochip_type = X86_GPIOCHIP_CHERRYVIEW,
};
#define CZC_EC_EXTRA_PORT 0x68
@ -297,6 +244,8 @@ static const struct software_node medion_lifetab_s10346_accel_node = {
static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[1], 26, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("irq-gpios", &baytrail_gpiochip_nodes[2], 3, GPIO_ACTIVE_HIGH),
{ }
};
@ -340,24 +289,10 @@ static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __in
},
};
static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = {
.dev_id = "i2c-goodix_ts",
.table = {
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = {
&medion_lifetab_s10346_goodix_gpios,
NULL
};
const struct x86_dev_info medion_lifetab_s10346_info __initconst = {
.i2c_client_info = medion_lifetab_s10346_i2c_clients,
.i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients),
.gpiod_lookup_tables = medion_lifetab_s10346_gpios,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/* Nextbook Ares 8 (BYT) tablets have an Android factory image with everything hardcoded */
@ -416,17 +351,12 @@ static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst
},
};
static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = {
&int3496_reference_gpios,
NULL
};
const struct x86_dev_info nextbook_ares8_info __initconst = {
.i2c_client_info = nextbook_ares8_i2c_clients,
.i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients),
.pdev_info = int3496_pdevs,
.pdev_count = 1,
.gpiod_lookup_tables = nextbook_ares8_gpios,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/* Nextbook Ares 8A (CHT) tablets have an Android factory image with everything hardcoded */
@ -445,6 +375,17 @@ static const struct software_node nextbook_ares8a_accel_node = {
.properties = nextbook_ares8a_accel_props,
};
static const struct property_entry nextbook_ares8a_ft5416_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 800),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[1], 25, GPIO_ACTIVE_LOW),
{ }
};
static const struct software_node nextbook_ares8a_ft5416_node = {
.properties = nextbook_ares8a_ft5416_props,
};
static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initconst = {
{
/* Freescale MMA8653FC accelerometer */
@ -461,7 +402,7 @@ static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initcons
.type = "edt-ft5x06",
.addr = 0x38,
.dev_name = "ft5416",
.swnode = &nextbook_ares8_touchscreen_node,
.swnode = &nextbook_ares8a_ft5416_node,
},
.adapter_path = "\\_SB_.PCI0.I2C6",
.irq_data = {
@ -475,23 +416,10 @@ static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initcons
},
};
static struct gpiod_lookup_table nextbook_ares8a_ft5416_gpios = {
.dev_id = "i2c-ft5416",
.table = {
GPIO_LOOKUP("INT33FF:01", 25, "reset", GPIO_ACTIVE_LOW),
{ }
},
};
static struct gpiod_lookup_table * const nextbook_ares8a_gpios[] = {
&nextbook_ares8a_ft5416_gpios,
NULL
};
const struct x86_dev_info nextbook_ares8a_info __initconst = {
.i2c_client_info = nextbook_ares8a_i2c_clients,
.i2c_client_count = ARRAY_SIZE(nextbook_ares8a_i2c_clients),
.gpiod_lookup_tables = nextbook_ares8a_gpios,
.gpiochip_type = X86_GPIOCHIP_CHERRYVIEW,
};
/*
@ -500,22 +428,32 @@ const struct x86_dev_info nextbook_ares8a_info __initconst = {
* This button has a WMI interface, but that is broken. Instead of trying to
* use the broken WMI interface, instantiate a gpio-keys device for this.
*/
static const struct x86_gpio_button peaq_c1010_button __initconst = {
.button = {
.code = KEY_SOUND,
.active_low = true,
.desc = "dolby_key",
.type = EV_KEY,
.wakeup = false,
.debounce_interval = 50,
},
.chip = "INT33FC:00",
.pin = 3,
static const struct software_node peaq_c1010_gpio_keys_node = {
.name = "gpio_keys",
};
static const struct property_entry peaq_c1010_dolby_key_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_SOUND),
PROPERTY_ENTRY_STRING("label", "dolby_key"),
PROPERTY_ENTRY_GPIO("gpios", &baytrail_gpiochip_nodes[0], 3, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_U32("debounce-interval", 50),
{ }
};
static const struct software_node peaq_c1010_dolby_key_node = {
.parent = &peaq_c1010_gpio_keys_node,
.properties = peaq_c1010_dolby_key_props,
};
static const struct software_node *peaq_c1010_button_swnodes[] = {
&peaq_c1010_gpio_keys_node,
&peaq_c1010_dolby_key_node,
NULL
};
const struct x86_dev_info peaq_c1010_info __initconst = {
.gpio_button = &peaq_c1010_button,
.gpio_button_count = 1,
.gpio_button_swnodes = peaq_c1010_button_swnodes,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/*
@ -543,6 +481,8 @@ static const struct property_entry whitelabel_tm800a550l_goodix_props[] = {
PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"),
PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"),
PROPERTY_ENTRY_U32("goodix,main-clk", 54),
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[1], 26, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("irq-gpios", &baytrail_gpiochip_nodes[2], 3, GPIO_ACTIVE_HIGH),
{ }
};
@ -578,24 +518,10 @@ static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __in
},
};
static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = {
.dev_id = "i2c-goodix_ts",
.table = {
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = {
&whitelabel_tm800a550l_goodix_gpios,
NULL
};
const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
.i2c_client_info = whitelabel_tm800a550l_i2c_clients,
.i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients),
.gpiod_lookup_tables = whitelabel_tm800a550l_gpios,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/*
@ -605,6 +531,7 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
static const struct property_entry vexia_edu_atla10_5v_touchscreen_props[] = {
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[1], 26, GPIO_ACTIVE_LOW),
{ }
};
@ -639,23 +566,10 @@ static const struct x86_i2c_client_info vexia_edu_atla10_5v_i2c_clients[] __init
}
};
static struct gpiod_lookup_table vexia_edu_atla10_5v_ft5416_gpios = {
.dev_id = "i2c-FTSC1000",
.table = {
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
{ }
},
};
static struct gpiod_lookup_table * const vexia_edu_atla10_5v_gpios[] = {
&vexia_edu_atla10_5v_ft5416_gpios,
NULL
};
const struct x86_dev_info vexia_edu_atla10_5v_info __initconst = {
.i2c_client_info = vexia_edu_atla10_5v_i2c_clients,
.i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_5v_i2c_clients),
.gpiod_lookup_tables = vexia_edu_atla10_5v_gpios,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/*
@ -691,6 +605,7 @@ static const struct software_node vexia_edu_atla10_9v_accel_node = {
static const struct property_entry vexia_edu_atla10_9v_touchscreen_props[] = {
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
PROPERTY_ENTRY_GPIO("reset-gpios", &baytrail_gpiochip_nodes[0], 60, GPIO_ACTIVE_LOW),
{ }
};
@ -783,19 +698,6 @@ static const struct x86_serdev_info vexia_edu_atla10_9v_serdevs[] __initconst =
},
};
static struct gpiod_lookup_table vexia_edu_atla10_9v_ft5416_gpios = {
.dev_id = "i2c-FTSC1000",
.table = {
GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_LOW),
{ }
},
};
static struct gpiod_lookup_table * const vexia_edu_atla10_9v_gpios[] = {
&vexia_edu_atla10_9v_ft5416_gpios,
NULL
};
static int __init vexia_edu_atla10_9v_init(struct device *dev)
{
struct pci_dev *pdev;
@ -809,8 +711,10 @@ static int __init vexia_edu_atla10_9v_init(struct device *dev)
/* Reprobe the SDIO controller to enumerate the now enabled Wifi module */
pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x11, 0));
if (!pdev)
return -EPROBE_DEFER;
if (!pdev) {
pr_warn("Could not get PCI SDIO at devfn 0x%02x\n", PCI_DEVFN(0x11, 0));
return 0;
}
ret = device_reprobe(&pdev->dev);
if (ret)
@ -825,9 +729,9 @@ const struct x86_dev_info vexia_edu_atla10_9v_info __initconst = {
.i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_9v_i2c_clients),
.serdev_info = vexia_edu_atla10_9v_serdevs,
.serdev_count = ARRAY_SIZE(vexia_edu_atla10_9v_serdevs),
.gpiod_lookup_tables = vexia_edu_atla10_9v_gpios,
.init = vexia_edu_atla10_9v_init,
.use_pci = true,
.gpiochip_type = X86_GPIOCHIP_BAYTRAIL,
};
/*
@ -923,7 +827,6 @@ static int xiaomi_mipad2_brightness_set(struct led_classdev *led_cdev,
static int __init xiaomi_mipad2_init(struct device *dev)
{
struct led_classdev *led_cdev;
int ret;
xiaomi_mipad2_led_pwm = devm_pwm_get(dev, "pwm_soc_lpss_2");
if (IS_ERR(xiaomi_mipad2_led_pwm))
@ -940,16 +843,7 @@ static int __init xiaomi_mipad2_init(struct device *dev)
/* Turn LED off during suspend */
led_cdev->flags = LED_CORE_SUSPENDRESUME;
ret = devm_led_classdev_register(dev, led_cdev);
if (ret)
return dev_err_probe(dev, ret, "registering LED\n");
return software_node_register_node_group(ktd2026_node_group);
}
static void xiaomi_mipad2_exit(void)
{
software_node_unregister_node_group(ktd2026_node_group);
return devm_led_classdev_register(dev, led_cdev);
}
/*
@ -984,6 +878,6 @@ static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst
const struct x86_dev_info xiaomi_mipad2_info __initconst = {
.i2c_client_info = xiaomi_mipad2_i2c_clients,
.i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients),
.swnode_group = ktd2026_node_group,
.init = xiaomi_mipad2_init,
.exit = xiaomi_mipad2_exit,
};

View File

@ -5,16 +5,18 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/platform_device.h>
#include <linux/power/bq24190_charger.h>
#include <linux/property.h>
#include <linux/regulator/machine.h>
#include "shared-psy-info.h"
#include "x86-android-tablets.h"
/* Generic / shared charger / battery settings */
const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" };
@ -111,6 +113,11 @@ const struct software_node generic_lipo_4v2_battery_node = {
.properties = generic_lipo_4v2_battery_props,
};
const struct software_node *generic_lipo_4v2_battery_swnodes[] = {
&generic_lipo_4v2_battery_node,
NULL
};
/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */
static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
@ -131,6 +138,11 @@ const struct software_node generic_lipo_hv_4v35_battery_node = {
.properties = generic_lipo_hv_4v35_battery_props,
};
const struct software_node *generic_lipo_hv_4v35_battery_swnodes[] = {
&generic_lipo_hv_4v35_battery_node,
NULL
};
/* For enabling the bq24190 5V boost based on id-pin */
static struct regulator_consumer_supply intel_int3496_consumer = {
.supply = "vbus",
@ -156,21 +168,19 @@ const char * const bq24190_modules[] __initconst = {
NULL
};
/* Generic platform device array and GPIO lookup table for micro USB ID pin handling */
static const struct property_entry int3496_reference_props[] __initconst = {
PROPERTY_ENTRY_GPIO("vbus-gpios", &baytrail_gpiochip_nodes[1], 15, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("mux-gpios", &baytrail_gpiochip_nodes[2], 1, GPIO_ACTIVE_HIGH),
PROPERTY_ENTRY_GPIO("id-gpios", &baytrail_gpiochip_nodes[2], 18, GPIO_ACTIVE_HIGH),
{ }
};
/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */
const struct platform_device_info int3496_pdevs[] __initconst = {
{
/* For micro USB ID pin handling */
.name = "intel-int3496",
.id = PLATFORM_DEVID_NONE,
},
};
struct gpiod_lookup_table int3496_reference_gpios = {
.dev_id = "intel-int3496",
.table = {
GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH),
{ }
.properties = int3496_reference_props,
},
};

View File

@ -5,13 +5,12 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#ifndef __PDX86_SHARED_PSY_INFO_H
#define __PDX86_SHARED_PSY_INFO_H
struct bq24190_platform_data;
struct gpiod_lookup_table;
struct platform_device_info;
struct software_node;
@ -21,13 +20,16 @@ extern const char * const bq25890_psy[];
extern const struct software_node fg_bq24190_supply_node;
extern const struct software_node fg_bq25890_supply_node;
extern const struct software_node generic_lipo_4v2_battery_node;
extern const struct software_node *generic_lipo_4v2_battery_swnodes[];
extern const struct software_node generic_lipo_hv_4v35_battery_node;
extern const struct software_node *generic_lipo_hv_4v35_battery_swnodes[];
extern struct bq24190_platform_data bq24190_pdata;
extern const char * const bq24190_modules[];
extern const struct platform_device_info int3496_pdevs[];
extern struct gpiod_lookup_table int3496_reference_gpios;
#endif

View File

@ -256,6 +256,6 @@ static struct i2c_driver atla10_ec_driver = {
};
module_i2c_driver(atla10_ec_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
MODULE_DESCRIPTION("Battery driver for Vexia EDU ATLA 10 tablet EC");
MODULE_LICENSE("GPL");

View File

@ -5,19 +5,17 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#ifndef __PDX86_X86_ANDROID_TABLETS_H
#define __PDX86_X86_ANDROID_TABLETS_H
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
#include <linux/i2c.h>
#include <linux/irqdomain_defs.h>
#include <linux/spi/spi.h>
struct gpio_desc;
struct gpiod_lookup_table;
struct platform_device_info;
struct software_node;
@ -32,6 +30,12 @@ enum x86_acpi_irq_type {
X86_ACPI_IRQ_TYPE_PMIC,
};
enum x86_gpiochip_type {
X86_GPIOCHIP_UNSPECIFIED = 0,
X86_GPIOCHIP_BAYTRAIL,
X86_GPIOCHIP_CHERRYVIEW,
};
struct x86_acpi_irq_data {
char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */
enum x86_acpi_irq_type type;
@ -76,29 +80,22 @@ struct x86_serdev_info {
const char *serdev_hid;
};
struct x86_gpio_button {
struct gpio_keys_button button;
const char *chip;
int pin;
};
struct x86_dev_info {
const char * const *modules;
const struct software_node *bat_swnode;
struct gpiod_lookup_table * const *gpiod_lookup_tables;
const struct software_node **swnode_group;
const struct x86_i2c_client_info *i2c_client_info;
const struct x86_spi_dev_info *spi_dev_info;
const struct platform_device_info *pdev_info;
const struct x86_serdev_info *serdev_info;
const struct x86_gpio_button *gpio_button;
const struct software_node **gpio_button_swnodes;
int i2c_client_count;
int spi_dev_count;
int pdev_count;
int serdev_count;
int gpio_button_count;
int (*init)(struct device *dev);
void (*exit)(void);
bool use_pci;
enum x86_gpiochip_type gpiochip_type;
};
int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
@ -106,10 +103,15 @@ int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
struct gpio_desc **desc);
int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data);
/* Software nodes representing GPIO chips used by various tablets */
extern const struct software_node baytrail_gpiochip_nodes[];
extern const struct software_node cherryview_gpiochip_nodes[];
/*
* Extern declarations of x86_dev_info structs so there can be a single
* MODULE_DEVICE_TABLE(dmi, ...), while splitting the board descriptions.
*/
extern const struct x86_dev_info acer_a1_840_info;
extern const struct x86_dev_info acer_b1_750_info;
extern const struct x86_dev_info advantech_mica_071_info;
extern const struct x86_dev_info asus_me176c_info;

View File

@ -26,13 +26,6 @@ struct xiaomi_wmi {
unsigned int key_code;
};
static void xiaomi_mutex_destroy(void *data)
{
struct mutex *lock = data;
mutex_destroy(lock);
}
static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct xiaomi_wmi *data;
@ -46,8 +39,7 @@ static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, data);
mutex_init(&data->key_lock);
ret = devm_add_action_or_reset(&wdev->dev, xiaomi_mutex_destroy, &data->key_lock);
ret = devm_mutex_init(&wdev->dev, &data->key_lock);
if (ret < 0)
return ret;

View File

@ -552,7 +552,7 @@ static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
acpi_dev_put(adev);
} else {
/* Special case for when the codec is missing from the DSTD */
strscpy(codec_name, "spi1.0", sizeof(codec_name));
strscpy(codec_name, "spi-wm5102", sizeof(codec_name));
}
codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, codec_name);