summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2021-12-30 02:14:26 +0300
committerHans de Goede <hdegoede@redhat.com>2022-01-05 13:31:19 +0300
commitc2138b25d5a42e527588bd26cb2409352d8c0743 (patch)
tree27d58121c45b87582f2291c53096330c31e67228 /drivers/platform
parent5eba0141206ea521bbcfcf5067c174e825e943dd (diff)
downloadlinux-c2138b25d5a42e527588bd26cb2409352d8c0743.tar.xz
platform/x86: x86-android-tablets: Add support for instantiating serdevs
Add support for instantiating serdevs, this is necessary on some boards where the serdev info in the DSDT has issues. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Link: https://lore.kernel.org/r/20211229231431.437982-8-hdegoede@redhat.com
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig2
-rw-r--r--drivers/platform/x86/x86-android-tablets.c101
2 files changed, 102 insertions, 1 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b9a73df1820f..833abec54644 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1006,7 +1006,7 @@ config TOUCHSCREEN_DMI
config X86_ANDROID_TABLETS
tristate "X86 Android tablet support"
- depends on I2C && ACPI && GPIOLIB
+ depends on I2C && SERIAL_DEV_BUS && ACPI && GPIOLIB
help
X86 tablets which ship with Android as (part of) the factory image
typically have various problems with their DSDTs. The factory kernels
diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c
index 4bcad05d4039..5267e57c4fea 100644
--- a/drivers/platform/x86/x86-android-tablets.c
+++ b/drivers/platform/x86/x86-android-tablets.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/serdev.h>
#include <linux/string.h>
/* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */
#include "../../gpio/gpiolib.h"
@@ -127,11 +128,26 @@ struct x86_i2c_client_info {
struct x86_acpi_irq_data irq_data;
};
+struct x86_serdev_info {
+ const char *ctrl_hid;
+ const char *ctrl_uid;
+ const char *ctrl_devname;
+ /*
+ * ATM the serdev core only supports of or ACPI matching; and sofar all
+ * Android x86 tablets DSDTs have usable serdev nodes, but sometimes
+ * under the wrong controller. So we just tie the existing serdev ACPI
+ * node to the right controller.
+ */
+ const char *serdev_hid;
+};
+
struct x86_dev_info {
const struct x86_i2c_client_info *i2c_client_info;
const struct platform_device_info *pdev_info;
+ const struct x86_serdev_info *serdev_info;
int i2c_client_count;
int pdev_count;
+ int serdev_count;
};
/*
@@ -273,8 +289,10 @@ MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids);
static int i2c_client_count;
static int pdev_count;
+static int serdev_count;
static struct i2c_client **i2c_clients;
static struct platform_device **pdevs;
+static struct serdev_device **serdevs;
static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info,
int idx)
@@ -310,10 +328,78 @@ static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info
return 0;
}
+static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx)
+{
+ struct acpi_device *ctrl_adev, *serdev_adev;
+ struct serdev_device *serdev;
+ struct device *ctrl_dev;
+ int ret = -ENODEV;
+
+ ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1);
+ if (!ctrl_adev) {
+ pr_err("error could not get %s/%s ctrl adev\n",
+ info->ctrl_hid, info->ctrl_uid);
+ return -ENODEV;
+ }
+
+ serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1);
+ if (!serdev_adev) {
+ pr_err("error could not get %s serdev adev\n", info->serdev_hid);
+ goto put_ctrl_adev;
+ }
+
+ /* get_first_physical_node() returns a weak ref, no need to put() it */
+ ctrl_dev = acpi_get_first_physical_node(ctrl_adev);
+ if (!ctrl_dev) {
+ pr_err("error could not get %s/%s ctrl physical dev\n",
+ info->ctrl_hid, info->ctrl_uid);
+ goto put_serdev_adev;
+ }
+
+ /* ctrl_dev now points to the controller's parent, get the controller */
+ ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname);
+ if (!ctrl_dev) {
+ pr_err("error could not get %s/%s %s ctrl dev\n",
+ info->ctrl_hid, info->ctrl_uid, info->ctrl_devname);
+ goto put_serdev_adev;
+ }
+
+ serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
+ if (!serdev) {
+ ret = -ENOMEM;
+ goto put_serdev_adev;
+ }
+
+ ACPI_COMPANION_SET(&serdev->dev, serdev_adev);
+ acpi_device_set_enumerated(serdev_adev);
+
+ ret = serdev_device_add(serdev);
+ if (ret) {
+ dev_err(&serdev->dev, "error %d adding serdev\n", ret);
+ serdev_device_put(serdev);
+ goto put_serdev_adev;
+ }
+
+ serdevs[idx] = serdev;
+
+put_serdev_adev:
+ acpi_dev_put(serdev_adev);
+put_ctrl_adev:
+ acpi_dev_put(ctrl_adev);
+ return ret;
+}
+
static void x86_android_tablet_cleanup(void)
{
int i;
+ for (i = 0; i < serdev_count; i++) {
+ if (serdevs[i])
+ serdev_device_remove(serdevs[i]);
+ }
+
+ kfree(serdevs);
+
for (i = 0; i < pdev_count; i++)
platform_device_unregister(pdevs[i]);
@@ -365,6 +451,21 @@ static __init int x86_android_tablet_init(void)
}
}
+ serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL);
+ if (!serdevs) {
+ x86_android_tablet_cleanup();
+ return -ENOMEM;
+ }
+
+ serdev_count = dev_info->serdev_count;
+ for (i = 0; i < serdev_count; i++) {
+ ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i);
+ if (ret < 0) {
+ x86_android_tablet_cleanup();
+ return ret;
+ }
+ }
+
return 0;
}