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:
commit
b66451723c
|
@ -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;
|
||||
};
|
||||
};
|
||||
...
|
12
MAINTAINERS
12
MAINTAINERS
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue