X86 platform drivers
 help / color / mirror / Atom feed
* [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order
@ 2024-04-06 12:50 Hans de Goede
  2024-04-06 12:50 ` [PATCH 2/3] platform/x86: x86-android-tablets: Add Lenovo Yoga Tablet 2 Pro 1380F/L data Hans de Goede
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Hans de Goede @ 2024-04-06 12:50 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko; +Cc: Hans de Goede, platform-driver-x86

Not all subsystems support a device getting removed while there are
still consumers of the device with a reference to the device.

One example of this is the regulator subsystem. If a regulator gets
unregistered while there are still drivers holding a reference
a WARN() at drivers/regulator/core.c:5829 triggers, e.g.:

 WARNING: CPU: 1 PID: 1587 at drivers/regulator/core.c:5829 regulator_unregister
 Hardware name: Intel Corp. VALLEYVIEW C0 PLATFORM/BYT-T FFD8, BIOS BLADE_21.X64.0005.R00.1504101516 FFD8_X64_R_2015_04_10_1516 04/10/2015
 RIP: 0010:regulator_unregister
 Call Trace:
  <TASK>
  regulator_unregister
  devres_release_group
  i2c_device_remove
  device_release_driver_internal
  bus_remove_device
  device_del
  device_unregister
  x86_android_tablet_remove

On the Lenovo Yoga Tablet 2 series the bq24190 charger chip also provides
a 5V boost converter output for powering USB devices connected to the micro
USB port, the bq24190-charger driver exports this as a Vbus regulator.

On the 830 (8") and 1050 ("10") models this regulator is controlled by
a platform_device and x86_android_tablet_remove() removes platform_device-s
before i2c_clients so the consumer gets removed first.

But on the 1380 (13") model there is a lc824206xa micro-USB switch
connected over I2C and the extcon driver for that controls the regulator.
The bq24190 i2c-client *must* be registered first, because that creates
the regulator with the lc824206xa listed as its consumer. If the regulator
has not been registered yet the lc824206xa driver will end up getting
a dummy regulator.

Since in this case both the regulator provider and consumer are I2C
devices, the only way to ensure that the consumer is unregistered first
is to unregister the I2C devices in reverse order of in which they were
created.

For consistency and to avoid similar problems in the future change
x86_android_tablet_remove() to unregister all device types in reverse
order.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/platform/x86/x86-android-tablets/core.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c
index a3415f1c0b5f..6559bb4ea730 100644
--- a/drivers/platform/x86/x86-android-tablets/core.c
+++ b/drivers/platform/x86/x86-android-tablets/core.c
@@ -278,25 +278,25 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
 {
 	int i;
 
-	for (i = 0; i < serdev_count; i++) {
+	for (i = serdev_count - 1; i >= 0; i--) {
 		if (serdevs[i])
 			serdev_device_remove(serdevs[i]);
 	}
 
 	kfree(serdevs);
 
-	for (i = 0; i < pdev_count; i++)
+	for (i = pdev_count - 1; i >= 0; i--)
 		platform_device_unregister(pdevs[i]);
 
 	kfree(pdevs);
 	kfree(buttons);
 
-	for (i = 0; i < spi_dev_count; i++)
+	for (i = spi_dev_count - 1; i >= 0; i--)
 		spi_unregister_device(spi_devs[i]);
 
 	kfree(spi_devs);
 
-	for (i = 0; i < i2c_client_count; i++)
+	for (i = i2c_client_count - 1; i >= 0; i--)
 		i2c_unregister_device(i2c_clients[i]);
 
 	kfree(i2c_clients);
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] platform/x86: x86-android-tablets: Add Lenovo Yoga Tablet 2 Pro 1380F/L data
  2024-04-06 12:50 [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede
@ 2024-04-06 12:50 ` Hans de Goede
  2024-04-06 12:50 ` [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver Hans de Goede
  2024-04-15 13:43 ` [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede
  2 siblings, 0 replies; 6+ messages in thread
From: Hans de Goede @ 2024-04-06 12:50 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko; +Cc: Hans de Goede, platform-driver-x86

The Lenovo Yoga Tablet 2 Pro 1380F/L is a x86 ACPI tablet which ships with
Android x86 as factory OS. Its DSDT contains a bunch of I2C devices which
are not actually there, causing various resource conflicts. Enumeration of
these is skipped through the acpi_quirk_skip_i2c_client_enumeration().

Add support for manually instantiating the I2C + other devices which are
actually present on this tablet by adding the necessary device info to
the x86-android-tablets module.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 .../platform/x86/x86-android-tablets/dmi.c    |  18 ++
 .../platform/x86/x86-android-tablets/lenovo.c | 216 ++++++++++++++++++
 .../x86-android-tablets/x86-android-tablets.h |   1 +
 3 files changed, 235 insertions(+)

diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c
index 5d6c12494f08..141a2d25e83b 100644
--- a/drivers/platform/x86/x86-android-tablets/dmi.c
+++ b/drivers/platform/x86/x86-android-tablets/dmi.c
@@ -104,6 +104,24 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
 		},
 		.driver_data = (void *)&lenovo_yogabook_x91_info,
 	},
+	{
+		/*
+		 * Lenovo Yoga Tablet 2 Pro 1380F/L (13") This has more or less
+		 * the same BIOS as the 830F/L or 1050F/L (8" and 10") below,
+		 * but unlike the 8" / 10" models which share the same mainboard
+		 * this model has a different mainboard.
+		 * This match for the 13" model MUST come before the 8" + 10"
+		 * match since that one will also match the 13" model!
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
+			DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
+			/* Full match so as to NOT match the 830/1050 BIOS */
+			DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21.X64.0005.R00.1504101516"),
+		},
+		.driver_data = (void *)&lenovo_yoga_tab2_1380_info,
+	},
 	{
 		/*
 		 * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10"
diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c
index c297391955ad..16fa04d604a0 100644
--- a/drivers/platform/x86/x86-android-tablets/lenovo.c
+++ b/drivers/platform/x86/x86-android-tablets/lenovo.c
@@ -19,6 +19,7 @@
 #include <linux/pinctrl/machine.h>
 #include <linux/platform_data/lp855x.h>
 #include <linux/platform_device.h>
+#include <linux/power/bq24190_charger.h>
 #include <linux/reboot.h>
 #include <linux/rmi.h>
 #include <linux/spi/spi.h>
@@ -565,6 +566,221 @@ static void lenovo_yoga_tab2_830_1050_exit(void)
 	}
 }
 
+/*
+ * Lenovo Yoga Tablet 2 Pro 1380F/L
+ *
+ * The Lenovo Yoga Tablet 2 Pro 1380F/L mostly has the same design as the 830F/L
+ * and the 1050F/L so this re-uses some of the handling for that from above.
+ */
+static const char * const lc824206xa_chg_det_psy[] = { "lc824206xa-charger-detect" };
+
+static const struct property_entry lenovo_yoga_tab2_1380_bq24190_props[] = {
+	PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lc824206xa_chg_det_psy),
+	PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node),
+	PROPERTY_ENTRY_BOOL("omit-battery-class"),
+	PROPERTY_ENTRY_BOOL("disable-reset"),
+	{ }
+};
+
+static const struct software_node lenovo_yoga_tab2_1380_bq24190_node = {
+	.properties = lenovo_yoga_tab2_1380_bq24190_props,
+};
+
+/* For enabling the bq24190 5V boost based on id-pin */
+static struct regulator_consumer_supply lc824206xa_consumer = {
+	.supply = "vbus",
+	.dev_name = "i2c-lc824206xa",
+};
+
+static const struct regulator_init_data lenovo_yoga_tab2_1380_bq24190_vbus_init_data = {
+	.constraints = {
+		.name = "bq24190_vbus",
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+	},
+	.consumer_supplies = &lc824206xa_consumer,
+	.num_consumer_supplies = 1,
+};
+
+struct bq24190_platform_data lenovo_yoga_tab2_1380_bq24190_pdata = {
+	.regulator_init_data = &lenovo_yoga_tab2_1380_bq24190_vbus_init_data,
+};
+
+static const struct property_entry lenovo_yoga_tab2_1380_lc824206xa_props[] = {
+	PROPERTY_ENTRY_BOOL("onnn,enable-miclr-for-dcp"),
+	{ }
+};
+
+static const struct software_node lenovo_yoga_tab2_1380_lc824206xa_node = {
+	.properties = lenovo_yoga_tab2_1380_lc824206xa_props,
+};
+
+static const char * const lenovo_yoga_tab2_1380_lms303d_mount_matrix[] = {
+	"0", "-1", "0",
+	"-1", "0", "0",
+	"0", "0", "1"
+};
+
+static const struct property_entry lenovo_yoga_tab2_1380_lms303d_props[] = {
+	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", lenovo_yoga_tab2_1380_lms303d_mount_matrix),
+	{ }
+};
+
+static const struct software_node lenovo_yoga_tab2_1380_lms303d_node = {
+	.properties = lenovo_yoga_tab2_1380_lms303d_props,
+};
+
+static const struct x86_i2c_client_info lenovo_yoga_tab2_1380_i2c_clients[] __initconst = {
+	{
+		/* BQ27541 fuel-gauge */
+		.board_info = {
+			.type = "bq27541",
+			.addr = 0x55,
+			.dev_name = "bq27541",
+			.swnode = &fg_bq24190_supply_node,
+		},
+		.adapter_path = "\\_SB_.I2C1",
+	}, {
+		/* bq24292i battery charger */
+		.board_info = {
+			.type = "bq24190",
+			.addr = 0x6b,
+			.dev_name = "bq24292i",
+			.swnode = &lenovo_yoga_tab2_1380_bq24190_node,
+			.platform_data = &lenovo_yoga_tab2_1380_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_HIGH,
+			.con_id = "bq24292i_irq",
+		},
+	}, {
+		/* LP8557 Backlight controller */
+		.board_info = {
+			.type = "lp8557",
+			.addr = 0x2c,
+			.dev_name = "lp8557",
+			.platform_data = &lenovo_lp8557_pwm_and_reg_pdata,
+		},
+		.adapter_path = "\\_SB_.I2C3",
+	}, {
+		/* LC824206XA Micro USB Switch */
+		.board_info = {
+			.type = "lc824206xa",
+			.addr = 0x48,
+			.dev_name = "lc824206xa",
+			.swnode = &lenovo_yoga_tab2_1380_lc824206xa_node,
+		},
+		.adapter_path = "\\_SB_.I2C3",
+		.irq_data = {
+			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
+			.chip = "INT33FC:02",
+			.index = 1,
+			.trigger = ACPI_LEVEL_SENSITIVE,
+			.polarity = ACPI_ACTIVE_LOW,
+			.con_id = "lc824206xa_irq",
+		},
+	}, {
+		/* AL3320A ambient light sensor */
+		.board_info = {
+			.type = "al3320a",
+			.addr = 0x1c,
+			.dev_name = "al3320a",
+		},
+		.adapter_path = "\\_SB_.I2C5",
+	}, {
+		/* LSM303DA accelerometer + magnetometer */
+		.board_info = {
+			.type = "lsm303d",
+			.addr = 0x1d,
+			.dev_name = "lsm303d",
+			.swnode = &lenovo_yoga_tab2_1380_lms303d_node,
+		},
+		.adapter_path = "\\_SB_.I2C5",
+	}, {
+		/* Synaptics RMI touchscreen */
+		.board_info = {
+			.type = "rmi4_i2c",
+			.addr = 0x38,
+			.dev_name = "rmi4_i2c",
+			.platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata,
+		},
+		.adapter_path = "\\_SB_.I2C6",
+		.irq_data = {
+			.type = X86_ACPI_IRQ_TYPE_APIC,
+			.index = 0x45,
+			.trigger = ACPI_EDGE_SENSITIVE,
+			.polarity = ACPI_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,
+	},
+};
+
+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(void)
+{
+	int ret;
+
+	/* To verify that the DMI matching works vs the 830 / 1050 models */
+	pr_info("detected Lenovo Yoga Tablet 2 Pro 1380F/L\n");
+
+	ret = lenovo_yoga_tab2_830_1050_init_codec();
+	if (ret)
+		return ret;
+
+	/* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */
+	lenovo_yoga_tab2_830_1050_sys_off_handler =
+		register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
+					 lenovo_yoga_tab2_830_1050_power_off, NULL);
+	if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler))
+		return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler);
+
+	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,
+	.init = lenovo_yoga_tab2_1380_init,
+	.exit = lenovo_yoga_tab2_830_1050_exit,
+};
+
 /* Lenovo Yoga Tab 3 Pro YT3-X90F */
 
 /*
diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
index 468993edfeee..821dc094b025 100644
--- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
+++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
@@ -112,6 +112,7 @@ extern const struct x86_dev_info czc_p10t;
 extern const struct x86_dev_info lenovo_yogabook_x90_info;
 extern const struct x86_dev_info lenovo_yogabook_x91_info;
 extern const struct x86_dev_info lenovo_yoga_tab2_830_1050_info;
+extern const struct x86_dev_info lenovo_yoga_tab2_1380_info;
 extern const struct x86_dev_info lenovo_yt3_info;
 extern const struct x86_dev_info medion_lifetab_s10346_info;
 extern const struct x86_dev_info nextbook_ares8_info;
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver
  2024-04-06 12:50 [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede
  2024-04-06 12:50 ` [PATCH 2/3] platform/x86: x86-android-tablets: Add Lenovo Yoga Tablet 2 Pro 1380F/L data Hans de Goede
@ 2024-04-06 12:50 ` Hans de Goede
  2024-04-06 14:19   ` Andy Shevchenko
  2024-04-15 13:43 ` [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede
  2 siblings, 1 reply; 6+ messages in thread
From: Hans de Goede @ 2024-04-06 12:50 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko; +Cc: Hans de Goede, platform-driver-x86

Add a new driver for the custom fast charging protocol found on Lenovo Yoga
Tablet 2 1380F / 1380L models.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/platform/x86/Kconfig                  |  11 +
 drivers/platform/x86/Makefile                 |   1 +
 .../lenovo-yoga-tab2-pro-1380-fastcharger.c   | 340 ++++++++++++++++++
 3 files changed, 352 insertions(+)
 create mode 100644 drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index ae7ad6360954..a64aab3ed511 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -133,6 +133,17 @@ config YOGABOOK
 	  To compile this driver as a module, choose M here: the module will
 	  be called lenovo-yogabook.
 
+config YT2_1380
+	tristate "Lenovo Yoga Tablet 2 1380 fast charge driver"
+	depends on SERIAL_DEV_BUS
+	depends on ACPI
+	help
+	  Say Y here to enable support for the custom fast charging protocol
+	  found on the Lenovo Yoga Tablet 2 1380F / 1380L models.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called lenovo-yogabook.
+
 config ACERHDF
 	tristate "Acer Aspire One temperature and fan driver"
 	depends on ACPI && THERMAL
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 5521a87f0718..2640475a9f97 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 obj-$(CONFIG_THINKPAD_LMI)	+= think-lmi.o
 obj-$(CONFIG_YOGABOOK)		+= lenovo-yogabook.o
+obj-$(CONFIG_YT2_1380)		+= lenovo-yoga-tab2-pro-1380-fastcharger.o
 obj-$(CONFIG_LENOVO_WMI_CAMERA)	+= lenovo-wmi-camera.o
 
 # Intel
diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
new file mode 100644
index 000000000000..7281cc412193
--- /dev/null
+++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for the custom fast charging protocol found on the Lenovo Yoga
+ * Tablet 2 1380F / 1380L models.
+ *
+ * Copyright (C) 2024 Hans de Goede <hansg@kernel.org>
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/serdev.h>
+#include <linux/workqueue.h>
+#include "serdev_helpers.h"
+
+#define YT2_1380_FC_PDEV_NAME		"lenovo-yoga-tab2-pro-1380-fastcharger"
+#define YT2_1380_FC_SERDEV_CTRL		"serial0"
+#define YT2_1380_FC_SERDEV_NAME		"serial0-0"
+#define YT2_1380_FC_EXTCON_NAME		"i2c-lc824206xa"
+
+#define YT2_1380_FC_MAX_TRIES		5
+#define YT2_1380_FC_PIN_SW_DELAY_US	10000
+#define YT2_1380_FC_UART_DRAIN_DELAY_US	50000
+#define YT2_1380_FC_VOLT_SW_DELAY_US	1000000
+
+struct yt2_1380_fc {
+	struct device *dev;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *gpio_state;
+	struct pinctrl_state *uart_state;
+	struct gpio_desc *uart3_txd;
+	struct gpio_desc *uart3_rxd;
+	struct extcon_dev *extcon;
+	struct notifier_block nb;
+	struct work_struct work;
+	bool fast_charging;
+};
+
+static int yt2_1380_fc_set_gpio_mode(struct yt2_1380_fc *fc, bool enable)
+{
+	struct pinctrl_state *state = enable ? fc->gpio_state : fc->uart_state;
+	int ret;
+
+	ret = pinctrl_select_state(fc->pinctrl, state);
+	if (ret) {
+		dev_err(fc->dev, "Error %d setting pinctrl state\n", ret);
+		return ret;
+	}
+
+	fsleep(YT2_1380_FC_PIN_SW_DELAY_US);
+	return 0;
+}
+
+static bool yt2_1380_fc_dedicated_charger_connected(struct yt2_1380_fc *fc)
+{
+	int ret;
+
+	ret = extcon_get_state(fc->extcon, EXTCON_CHG_USB_DCP);
+	return ret > 0;
+}
+
+static bool yt2_1380_fc_fast_charger_connected(struct yt2_1380_fc *fc)
+{
+	int ret;
+
+	ret = extcon_get_state(fc->extcon, EXTCON_CHG_USB_FAST);
+	return ret > 0;
+}
+
+static void yt2_1380_fc_worker(struct work_struct *work)
+{
+	struct yt2_1380_fc *fc = container_of(work, struct yt2_1380_fc, work);
+	int i, ret;
+
+	/* Do nothing if already fast charging */
+	if (yt2_1380_fc_fast_charger_connected(fc))
+		return;
+
+	for (i = 0; i < YT2_1380_FC_MAX_TRIES; i++) {
+		/* Set pins to UART mode (for charger disconnect and retries) */
+		ret = yt2_1380_fc_set_gpio_mode(fc, false);
+		if (ret)
+			return;
+
+		/* Only try 12V charging if a dedicated charger is detected */
+		if (!yt2_1380_fc_dedicated_charger_connected(fc))
+			return;
+
+		/* Send the command to switch to 12V charging */
+		ret = serdev_device_write_buf(to_serdev_device(fc->dev), "SC", 2);
+		if (ret != 2) {
+			dev_err(fc->dev, "Error %d writing to uart\n", ret);
+			return;
+		}
+
+		fsleep(YT2_1380_FC_UART_DRAIN_DELAY_US);
+
+		/* Re-check a charger is still connected */
+		if (!yt2_1380_fc_dedicated_charger_connected(fc))
+			return;
+
+		/*
+		 * Now switch the lines to GPIO (output, high). The charger
+		 * expects the lines being driven high after the command.
+		 * Presumably this is used to detect the tablet getting
+		 * unplugged (to switch back to 5V output on unplug).
+		 */
+		ret = yt2_1380_fc_set_gpio_mode(fc, true);
+		if (ret)
+			return;
+
+		fsleep(YT2_1380_FC_VOLT_SW_DELAY_US);
+
+		if (yt2_1380_fc_fast_charger_connected(fc))
+			return; /* Success */
+	}
+
+	dev_dbg(fc->dev, "Failed to switch to 12V charging (not the original charger?)\n");
+	/* Failed to enable 12V fast charging, reset pins to default UART mode */
+	yt2_1380_fc_set_gpio_mode(fc, false);
+}
+
+static int yt2_1380_fc_extcon_evt(struct notifier_block *nb,
+				  unsigned long event, void *param)
+{
+	struct yt2_1380_fc *fc = container_of(nb, struct yt2_1380_fc, nb);
+
+	schedule_work(&fc->work);
+	return NOTIFY_OK;
+}
+
+static size_t yt2_1380_fc_receive(struct serdev_device *serdev, const u8 *data, size_t len)
+{
+	/*
+	 * Since the USB data lines are shorted for DCP detection, echos of
+	 * the "SC" command send in yt2_1380_fc_worker() will be received.
+	 */
+	dev_dbg(&serdev->dev, "recv: %*ph\n", (int)len, data);
+	return len;
+}
+
+static const struct serdev_device_ops yt2_1380_fc_serdev_ops = {
+	.receive_buf = yt2_1380_fc_receive,
+	.write_wakeup = serdev_device_write_wakeup,
+};
+
+static int yt2_1380_fc_serdev_probe(struct serdev_device *serdev)
+{
+	struct device *dev = &serdev->dev;
+	struct yt2_1380_fc *fc;
+	int ret;
+
+	fc = devm_kzalloc(dev, sizeof(*fc), GFP_KERNEL);
+	if (!fc)
+		return -ENOMEM;
+
+	fc->dev = dev;
+	fc->nb.notifier_call = yt2_1380_fc_extcon_evt;
+	INIT_WORK(&fc->work, yt2_1380_fc_worker);
+
+	/*
+	 * Do this first since it may return -EPROBE_DEFER.
+	 * There is no extcon_put(), so there is no need to free this.
+	 */
+	fc->extcon = extcon_get_extcon_dev(YT2_1380_FC_EXTCON_NAME);
+	if (IS_ERR(fc->extcon))
+		return dev_err_probe(dev, PTR_ERR(fc->extcon), "getting extcon\n");
+
+	fc->pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR(fc->pinctrl))
+		return dev_err_probe(dev, PTR_ERR(fc->pinctrl), "getting pinctrl\n");
+
+	/*
+	 * To switch the UART3 pins connected to the USB data lines between
+	 * UART and GPIO modes.
+	 */
+	fc->gpio_state = pinctrl_lookup_state(fc->pinctrl, "uart3_gpio");
+	fc->uart_state = pinctrl_lookup_state(fc->pinctrl, "uart3_uart");
+	if (IS_ERR(fc->gpio_state) || IS_ERR(fc->uart_state))
+		return dev_err_probe(dev, -EINVAL, "getting pinctrl states\n");
+
+	ret = yt2_1380_fc_set_gpio_mode(fc, true);
+	if (ret)
+		return ret;
+
+	fc->uart3_txd = devm_gpiod_get(dev, "uart3_txd", GPIOD_OUT_HIGH);
+	if (IS_ERR(fc->uart3_txd))
+		return dev_err_probe(dev, PTR_ERR(fc->uart3_txd), "getting uart3_txd gpio\n");
+
+	fc->uart3_rxd = devm_gpiod_get(dev, "uart3_rxd", GPIOD_OUT_HIGH);
+	if (IS_ERR(fc->uart3_rxd))
+		return dev_err_probe(dev, PTR_ERR(fc->uart3_rxd), "getting uart3_rxd gpio\n");
+
+	ret = yt2_1380_fc_set_gpio_mode(fc, false);
+	if (ret)
+		return ret;
+
+	ret = devm_serdev_device_open(dev, serdev);
+	if (ret)
+		return dev_err_probe(dev, ret, "opening UART device\n");
+
+	serdev_device_set_baudrate(serdev, 600);
+	serdev_device_set_flow_control(serdev, false);
+	serdev_device_set_drvdata(serdev, fc);
+	serdev_device_set_client_ops(serdev, &yt2_1380_fc_serdev_ops);
+
+	ret = devm_extcon_register_notifier_all(dev, fc->extcon, &fc->nb);
+	if (ret)
+		return dev_err_probe(dev, ret, "registering extcon notifier\n");
+
+	/* In case the extcon already has detected a DCP charger */
+	schedule_work(&fc->work);
+
+	return 0;
+}
+
+struct serdev_device_driver yt2_1380_fc_serdev_driver = {
+	.probe = yt2_1380_fc_serdev_probe,
+	.driver = {
+		.name = KBUILD_MODNAME,
+	},
+};
+
+static const struct pinctrl_map yt2_1380_fc_pinctrl_map[] = {
+	PIN_MAP_MUX_GROUP(YT2_1380_FC_SERDEV_NAME, "uart3_uart",
+			  "INT33FC:00", "uart3_grp", "uart"),
+	PIN_MAP_MUX_GROUP(YT2_1380_FC_SERDEV_NAME, "uart3_gpio",
+			  "INT33FC:00", "uart3_grp_gpio", "gpio"),
+};
+
+static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)
+{
+	struct serdev_device *serdev;
+	struct device *ctrl_dev;
+	int ret;
+
+	/* Register pinctrl mappings for setting the UART3 pins mode */
+	ret = pinctrl_register_mappings(yt2_1380_fc_pinctrl_map,
+					ARRAY_SIZE(yt2_1380_fc_pinctrl_map));
+	if (ret)
+		return ret;
+
+	/* And create the serdev to talk to the charger over the UART3 pins */
+	ctrl_dev = get_serdev_controller("PNP0501", "1", 0, YT2_1380_FC_SERDEV_CTRL);
+	if (IS_ERR(ctrl_dev)) {
+		ret = PTR_ERR(ctrl_dev);
+		goto out_pinctrl_unregister_mappings;
+	}
+
+	serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
+	put_device(ctrl_dev);
+	if (!serdev) {
+		ret = -ENOMEM;
+		goto out_pinctrl_unregister_mappings;
+	}
+
+	ret = serdev_device_add(serdev);
+	if (ret) {
+		dev_err_probe(&pdev->dev, ret, "adding serdev\n");
+		serdev_device_put(serdev);
+		goto out_pinctrl_unregister_mappings;
+	}
+
+	/*
+	 * serdev device <-> driver matching relies on OF or ACPI matches and
+	 * neither is available here, manually bind the driver.
+	 */
+	ret = device_driver_attach(&yt2_1380_fc_serdev_driver.driver, &serdev->dev);
+	if (ret) {
+		/* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */
+		ret = (ret == -EAGAIN) ? -EPROBE_DEFER : ret;
+		dev_err_probe(&pdev->dev, ret, "attaching serdev driver\n");
+		goto out_serdev_device_remove;
+	}
+
+	/* So that yt2_1380_fc_pdev_remove() can remove the serdev */
+	platform_set_drvdata(pdev, serdev);
+	return 0;
+
+out_serdev_device_remove:
+	serdev_device_remove(serdev);
+out_pinctrl_unregister_mappings:
+	pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map);
+	return ret;
+}
+
+static void yt2_1380_fc_pdev_remove(struct platform_device *pdev)
+{
+	struct serdev_device *serdev = platform_get_drvdata(pdev);
+
+	serdev_device_remove(serdev);
+	pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map);
+}
+
+static struct platform_driver yt2_1380_fc_pdev_driver = {
+	.probe = yt2_1380_fc_pdev_probe,
+	.remove_new = yt2_1380_fc_pdev_remove,
+	.driver = {
+		.name = YT2_1380_FC_PDEV_NAME,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+};
+
+static int __init yt2_1380_fc_module_init(void)
+{
+	int ret;
+
+	/*
+	 * serdev driver MUST be registered first because pdev driver calls
+	 * device_driver_attach() on the serdev, serdev-driver pair.
+	 */
+	ret = serdev_device_driver_register(&yt2_1380_fc_serdev_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&yt2_1380_fc_pdev_driver);
+	if (ret)
+		serdev_device_driver_unregister(&yt2_1380_fc_serdev_driver);
+
+	return ret;
+}
+module_init(yt2_1380_fc_module_init);
+
+static void __exit yt2_1380_fc_module_exit(void)
+{
+	platform_driver_unregister(&yt2_1380_fc_pdev_driver);
+	serdev_device_driver_unregister(&yt2_1380_fc_serdev_driver);
+}
+module_exit(yt2_1380_fc_module_exit);
+
+MODULE_ALIAS("platform:" YT2_1380_FC_PDEV_NAME);
+MODULE_DESCRIPTION("Lenovo Yoga Tablet 2 1380 fast charge driver");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_LICENSE("GPL");
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver
  2024-04-06 12:50 ` [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver Hans de Goede
@ 2024-04-06 14:19   ` Andy Shevchenko
  2024-04-22 12:58     ` Hans de Goede
  0 siblings, 1 reply; 6+ messages in thread
From: Andy Shevchenko @ 2024-04-06 14:19 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Ilpo Järvinen, Andy Shevchenko, platform-driver-x86

On Sat, Apr 6, 2024 at 3:53 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> Add a new driver for the custom fast charging protocol found on Lenovo Yoga
> Tablet 2 1380F / 1380L models.

...

> +#include <linux/delay.h>

+ err.h
+ errno.h

> +#include <linux/extcon.h>
> +#include <linux/gpio/consumer.h>

+ mod_devicetable.h

> +#include <linux/module.h>
> +#include <linux/notifier.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/platform_device.h>
> +#include <linux/serdev.h>

+ types.h

> +#include <linux/workqueue.h>
> +#include "serdev_helpers.h"

...

> +#define YT2_1380_FC_PIN_SW_DELAY_US    10000
> +#define YT2_1380_FC_UART_DRAIN_DELAY_US        50000
> +#define YT2_1380_FC_VOLT_SW_DELAY_US   1000000

Hmm... perhaps

  10 * USEC_PER_MSEC
  50 * USEC_PER_MSEC
  1 * USEC_PER_SEC

?

...

> +static bool yt2_1380_fc_dedicated_charger_connected(struct yt2_1380_fc *fc)
> +{
> +       int ret;
> +
> +       ret = extcon_get_state(fc->extcon, EXTCON_CHG_USB_DCP);
> +       return ret > 0;

This and other functions can be shorter by eliminating the ret
variable, but it's up to you.

> +}

...

> +               ret = serdev_device_write_buf(to_serdev_device(fc->dev), "SC", 2);

Hmm... I would replace magic 2 in both cases by strlen("SC") or so.

> +               if (ret != 2) {
> +                       dev_err(fc->dev, "Error %d writing to uart\n", ret);
> +                       return;
> +               }

...

> +static struct platform_driver yt2_1380_fc_pdev_driver = {
> +       .probe = yt2_1380_fc_pdev_probe,
> +       .remove_new = yt2_1380_fc_pdev_remove,
> +       .driver = {
> +               .name = YT2_1380_FC_PDEV_NAME,
> +               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +       },

+ .id_table = ... (see below why)

> +};

> +MODULE_ALIAS("platform:" YT2_1380_FC_PDEV_NAME);

The _standard_ MODULE_ALIAS() is not an excuse to have no ID table.
Just provide an accurate platform device ID table instead.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order
  2024-04-06 12:50 [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede
  2024-04-06 12:50 ` [PATCH 2/3] platform/x86: x86-android-tablets: Add Lenovo Yoga Tablet 2 Pro 1380F/L data Hans de Goede
  2024-04-06 12:50 ` [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver Hans de Goede
@ 2024-04-15 13:43 ` Hans de Goede
  2 siblings, 0 replies; 6+ messages in thread
From: Hans de Goede @ 2024-04-15 13:43 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko; +Cc: platform-driver-x86

Hi All,

I've added patch 1/3 and 2/3 of this series to my review-hans
(soon to be for-next) branch now.

I'll send out a v2 of patch 3/3 to address the review comments.

Regards,

Hans



On 4/6/24 2:50 PM, Hans de Goede wrote:
> Not all subsystems support a device getting removed while there are
> still consumers of the device with a reference to the device.
> 
> One example of this is the regulator subsystem. If a regulator gets
> unregistered while there are still drivers holding a reference
> a WARN() at drivers/regulator/core.c:5829 triggers, e.g.:
> 
>  WARNING: CPU: 1 PID: 1587 at drivers/regulator/core.c:5829 regulator_unregister
>  Hardware name: Intel Corp. VALLEYVIEW C0 PLATFORM/BYT-T FFD8, BIOS BLADE_21.X64.0005.R00.1504101516 FFD8_X64_R_2015_04_10_1516 04/10/2015
>  RIP: 0010:regulator_unregister
>  Call Trace:
>   <TASK>
>   regulator_unregister
>   devres_release_group
>   i2c_device_remove
>   device_release_driver_internal
>   bus_remove_device
>   device_del
>   device_unregister
>   x86_android_tablet_remove
> 
> On the Lenovo Yoga Tablet 2 series the bq24190 charger chip also provides
> a 5V boost converter output for powering USB devices connected to the micro
> USB port, the bq24190-charger driver exports this as a Vbus regulator.
> 
> On the 830 (8") and 1050 ("10") models this regulator is controlled by
> a platform_device and x86_android_tablet_remove() removes platform_device-s
> before i2c_clients so the consumer gets removed first.
> 
> But on the 1380 (13") model there is a lc824206xa micro-USB switch
> connected over I2C and the extcon driver for that controls the regulator.
> The bq24190 i2c-client *must* be registered first, because that creates
> the regulator with the lc824206xa listed as its consumer. If the regulator
> has not been registered yet the lc824206xa driver will end up getting
> a dummy regulator.
> 
> Since in this case both the regulator provider and consumer are I2C
> devices, the only way to ensure that the consumer is unregistered first
> is to unregister the I2C devices in reverse order of in which they were
> created.
> 
> For consistency and to avoid similar problems in the future change
> x86_android_tablet_remove() to unregister all device types in reverse
> order.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
>  drivers/platform/x86/x86-android-tablets/core.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c
> index a3415f1c0b5f..6559bb4ea730 100644
> --- a/drivers/platform/x86/x86-android-tablets/core.c
> +++ b/drivers/platform/x86/x86-android-tablets/core.c
> @@ -278,25 +278,25 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
>  {
>  	int i;
>  
> -	for (i = 0; i < serdev_count; i++) {
> +	for (i = serdev_count - 1; i >= 0; i--) {
>  		if (serdevs[i])
>  			serdev_device_remove(serdevs[i]);
>  	}
>  
>  	kfree(serdevs);
>  
> -	for (i = 0; i < pdev_count; i++)
> +	for (i = pdev_count - 1; i >= 0; i--)
>  		platform_device_unregister(pdevs[i]);
>  
>  	kfree(pdevs);
>  	kfree(buttons);
>  
> -	for (i = 0; i < spi_dev_count; i++)
> +	for (i = spi_dev_count - 1; i >= 0; i--)
>  		spi_unregister_device(spi_devs[i]);
>  
>  	kfree(spi_devs);
>  
> -	for (i = 0; i < i2c_client_count; i++)
> +	for (i = i2c_client_count - 1; i >= 0; i--)
>  		i2c_unregister_device(i2c_clients[i]);
>  
>  	kfree(i2c_clients);


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver
  2024-04-06 14:19   ` Andy Shevchenko
@ 2024-04-22 12:58     ` Hans de Goede
  0 siblings, 0 replies; 6+ messages in thread
From: Hans de Goede @ 2024-04-22 12:58 UTC (permalink / raw)
  To: Andy Shevchenko; +Cc: Ilpo Järvinen, Andy Shevchenko, platform-driver-x86

Hi,

On 4/6/24 4:19 PM, Andy Shevchenko wrote:
> On Sat, Apr 6, 2024 at 3:53 PM Hans de Goede <hdegoede@redhat.com> wrote:
>>
>> Add a new driver for the custom fast charging protocol found on Lenovo Yoga
>> Tablet 2 1380F / 1380L models.
> 
> ...
> 
>> +#include <linux/delay.h>
> 
> + err.h
> + errno.h
> 
>> +#include <linux/extcon.h>
>> +#include <linux/gpio/consumer.h>
> 
> + mod_devicetable.h

This one won't be necessary, see below.

> 
>> +#include <linux/module.h>
>> +#include <linux/notifier.h>
>> +#include <linux/pinctrl/consumer.h>
>> +#include <linux/pinctrl/machine.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/serdev.h>
> 
> + types.h
> 
>> +#include <linux/workqueue.h>
>> +#include "serdev_helpers.h"

Ack others added for v2.

> 
> ...
> 
>> +#define YT2_1380_FC_PIN_SW_DELAY_US    10000
>> +#define YT2_1380_FC_UART_DRAIN_DELAY_US        50000
>> +#define YT2_1380_FC_VOLT_SW_DELAY_US   1000000
> 
> Hmm... perhaps
> 
>   10 * USEC_PER_MSEC
>   50 * USEC_PER_MSEC
>   1 * USEC_PER_SEC
> 
> ?

Ack, changed for v2.

> 
> ...
> 
>> +static bool yt2_1380_fc_dedicated_charger_connected(struct yt2_1380_fc *fc)
>> +{
>> +       int ret;
>> +
>> +       ret = extcon_get_state(fc->extcon, EXTCON_CHG_USB_DCP);
>> +       return ret > 0;
> 
> This and other functions can be shorter by eliminating the ret
> variable, but it's up to you.
> 
>> +}

Ack, changed for v2.

> 
> ...
> 
>> +               ret = serdev_device_write_buf(to_serdev_device(fc->dev), "SC", 2);
> 
> Hmm... I would replace magic 2 in both cases by strlen("SC") or so.

Ack, changed for v2.

> 
>> +               if (ret != 2) {
>> +                       dev_err(fc->dev, "Error %d writing to uart\n", ret);
>> +                       return;
>> +               }
> 
> ...
> 
>> +static struct platform_driver yt2_1380_fc_pdev_driver = {
>> +       .probe = yt2_1380_fc_pdev_probe,
>> +       .remove_new = yt2_1380_fc_pdev_remove,
>> +       .driver = {
>> +               .name = YT2_1380_FC_PDEV_NAME,
>> +               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
>> +       },
> 
> + .id_table = ... (see below why)
> 
>> +};
> 
>> +MODULE_ALIAS("platform:" YT2_1380_FC_PDEV_NAME);
> 
> The _standard_ MODULE_ALIAS() is not an excuse to have no ID table.
> Just provide an accurate platform device ID table instead.

I personally have no strong preference either way (although
the MODULE_ALIAS way seems to be used the most in other code),
but in this specific case adding a platform_device_id table does
not work:

drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c:26:41: warning: initializer-string for array of ‘char’ is too long
   26 | #define YT2_1380_FC_PDEV_NAME           "lenovo-yoga-tab2-pro-1380-fastcharger"
      |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

from mod_devicetable.h:

#define PLATFORM_NAME_SIZE      20
#define PLATFORM_MODULE_PREFIX  "platform:"

struct platform_device_id {
        char name[PLATFORM_NAME_SIZE];
        kernel_ulong_t driver_data;
};

So the driver / pdev name can only by 20 chars, I tried shortening:

"lenovo-yoga-tab2-pro-1380-fastcharger"

to:

"lenovo-yt2-pro-fastcharger"

But that still is too long, making it even shorter will make
the name really cryptic, where as without using
the platform_device_id approach things work fine,
so I'm going to stick with the MODULE_ALIAS() for v2.

Regards,

Hans


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-04-22 12:58 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-04-06 12:50 [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede
2024-04-06 12:50 ` [PATCH 2/3] platform/x86: x86-android-tablets: Add Lenovo Yoga Tablet 2 Pro 1380F/L data Hans de Goede
2024-04-06 12:50 ` [PATCH 3/3] platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver Hans de Goede
2024-04-06 14:19   ` Andy Shevchenko
2024-04-22 12:58     ` Hans de Goede
2024-04-15 13:43 ` [PATCH 1/3] platform/x86: x86-android-tablets: Unregister devices in reverse order Hans de Goede

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox