summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Ciubotariu <aciubotariu@google.com>2023-09-18 19:04:58 -0700
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-09-28 23:10:15 +0000
commita7d8f40bd0261821c800374d6848cf6041f37c31 (patch)
tree27cedd0b57d60e168d865ea8f3740a8689e856b3
parent1747eb03698067eea4b39abdd17df598141c6c2d (diff)
downloadmsm-a7d8f40bd0261821c800374d6848cf6041f37c31.tar.gz
Add USB extcon shim module
The shim module sits in between a USB extcon (e.g. PMIC module) and the destination for the extcon event (i.e. a USB phy module). It can forward or prevent the event from making it to the destination, thereby decoupling the modules. For the shim module to be in the middle, use the devicetree. Set the "extcon" property to the USB extcon's phandle, and replace the USB extcon phandle in the destination's dt node with the shim module's phandle. Bug: 298708751 Signed-off-by: Andrei Ciubotariu <aciubotariu@google.com> (cherry picked from https://partner-android-review.googlesource.com/q/commit:befc01d824fdcc01ba31477b62c2b96b3c4a0e19) Merged-In: I8ff0b3f125b208d00db2d681912da79384191355 Change-Id: I8ff0b3f125b208d00db2d681912da79384191355
-rw-r--r--build.config.sw51001
-rw-r--r--usb_shim/Kbuild1
-rw-r--r--usb_shim/Makefile8
-rw-r--r--usb_shim/google-extcon-usb-shim.c256
4 files changed, 266 insertions, 0 deletions
diff --git a/build.config.sw5100 b/build.config.sw5100
index 11227e4..29edadc 100644
--- a/build.config.sw5100
+++ b/build.config.sw5100
@@ -65,6 +65,7 @@ private/msm-google-modules/securemsm
private/msm-google-modules/video
private/msm-google-modules/wlan/platform
private/msm-google-modules/wlan/qcacld-3.0
+private/google-modules/soc/msm/usb_shim
"
if false; then
diff --git a/usb_shim/Kbuild b/usb_shim/Kbuild
new file mode 100644
index 0000000..f38b1be
--- /dev/null
+++ b/usb_shim/Kbuild
@@ -0,0 +1 @@
+obj-m += google-extcon-usb-shim.o
diff --git a/usb_shim/Makefile b/usb_shim/Makefile
new file mode 100644
index 0000000..d469c5b
--- /dev/null
+++ b/usb_shim/Makefile
@@ -0,0 +1,8 @@
+all:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+
+modules_install:
+ $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
+
+clean:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M) clean
diff --git a/usb_shim/google-extcon-usb-shim.c b/usb_shim/google-extcon-usb-shim.c
new file mode 100644
index 0000000..1cb3301
--- /dev/null
+++ b/usb_shim/google-extcon-usb-shim.c
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2023 Google LLC */
+
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+static const unsigned int supported_cables[] = {
+ EXTCON_USB,
+ EXTCON_NONE /* sentinel */
+};
+
+struct extcon_usb_shim {
+ struct mutex lock;
+ struct device *dev;
+ struct extcon_dev *extcon;
+
+ /* The "supplier" is the extcon device we receive state updates from.
+ * i.e. the "real" extcon device.
+ */
+ struct notifier_block supplier_notifier;
+ bool supplier_connected;
+
+ /* Whether the USB phy is allowed to be enabled */
+ bool usb_allowed;
+ bool force_on;
+};
+
+static void update(struct extcon_usb_shim *shim, bool *state, bool newval)
+{
+ union extcon_property_value prop;
+ bool usb_on;
+
+ mutex_lock(&shim->lock);
+ if (state)
+ *state = newval;
+
+ usb_on = shim->supplier_connected && shim->usb_allowed;
+ usb_on = usb_on || shim->force_on;
+
+ if (usb_on) {
+ prop.intval = false;
+ extcon_set_property(shim->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS, prop);
+ }
+
+ extcon_set_state_sync(shim->extcon, EXTCON_USB, usb_on);
+
+ mutex_unlock(&shim->lock);
+}
+
+static int supplier_change(struct notifier_block *notifier,
+ unsigned long supplier_connected, void *unused)
+{
+ struct extcon_usb_shim *shim = container_of(notifier,
+ struct extcon_usb_shim,
+ supplier_notifier);
+
+ update(shim, &shim->supplier_connected, supplier_connected);
+
+ return NOTIFY_DONE;
+}
+
+static ssize_t allowed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct extcon_usb_shim *shim = platform_get_drvdata(pdev);
+ bool allowed;
+ int ret;
+
+ ret = kstrtobool(buf, &allowed);
+ if (ret < 0) {
+ return ret;
+ }
+
+ update(shim, &shim->usb_allowed, allowed);
+
+ return count;
+}
+
+static ssize_t allowed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct extcon_usb_shim *shim = platform_get_drvdata(pdev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", shim->usb_allowed);
+}
+
+static const DEVICE_ATTR_RW(allowed);
+
+static int supplier_notification_setup(struct extcon_usb_shim *shim)
+{
+ struct device *dev = shim->dev;
+ struct device_node *node = dev->of_node;
+ struct extcon_dev *supplier;
+ int num_extcon;
+ int ret;
+
+ num_extcon = of_count_phandle_with_args(node, "extcon", NULL);
+ if (num_extcon < 0) {
+ dev_err(dev, "Extcon count failed\n");
+ return -ENODEV;
+ }
+
+ if (num_extcon != 1) {
+ dev_err(dev, "Only one extcon allowed, %d provided\n",
+ num_extcon);
+ return -EINVAL;
+ }
+
+ supplier = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(supplier)) {
+ return PTR_ERR(supplier);
+ }
+
+ shim->supplier_connected = extcon_get_state(supplier, EXTCON_USB);
+
+ shim->supplier_notifier.notifier_call = supplier_change;
+ ret = devm_extcon_register_notifier(shim->dev, supplier, EXTCON_USB,
+ &shim->supplier_notifier);
+ if (ret < 0)
+ dev_err(dev,
+ "Failed to register notifier for supplier: %d\n", ret);
+
+ return ret;
+}
+
+static int setup_own_extcon(struct extcon_usb_shim *shim)
+{
+ struct device *dev = shim->dev;
+ int ret = 0;
+
+ shim->extcon = devm_extcon_dev_allocate(dev, supported_cables);
+ if (IS_ERR(shim->extcon)) {
+ dev_err(dev, "Failed to allocate extcon device: %d\n", ret);
+ return PTR_ERR(shim->extcon);
+ }
+
+ ret = devm_extcon_dev_register(dev, shim->extcon);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register extcon device: %d\n", ret);
+ return ret;
+ }
+
+ ret = extcon_set_property_capability(shim->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ if (ret < 0)
+ dev_err(dev, "Failed to set property capability: %d\n", ret);
+
+ return ret;
+}
+
+static int setup_sysfs(struct extcon_usb_shim *shim)
+{
+ struct device *dev = shim->dev;
+ int ret;
+
+ ret = device_create_file(shim->dev, &dev_attr_allowed);
+ if (ret < 0) {
+ dev_err(dev, "Failed to create %s: %d\n",
+ dev_attr_allowed.attr.name, ret);
+
+ /* Nonfatal, but no gating will happen */
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void teardown_sysfs(struct extcon_usb_shim *shim)
+{
+ device_remove_file(shim->dev, &dev_attr_allowed);
+}
+
+static int extcon_usb_shim_probe(struct platform_device *pdev)
+{
+ struct extcon_usb_shim *shim;
+ int ret = 0;
+
+
+ shim = devm_kzalloc(&pdev->dev, sizeof(*shim), GFP_KERNEL);
+ if (!shim)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, shim);
+ shim->dev = &pdev->dev;
+
+ mutex_init(&shim->lock);
+
+ ret = setup_own_extcon(shim);
+ if (ret < 0) {
+ dev_err(shim->dev, "Own extcon setup failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = supplier_notification_setup(shim);
+ if (ret < 0) {
+ /* Spoof the connection.
+ * USB phy will turn on and facilitate debug.
+ */
+ dev_err(shim->dev,
+ "Supplier notification setup failed: %d\n", ret);
+ dev_err(shim->dev, "Extcon will be forced on\n");
+ shim->force_on = true;
+ }
+
+ ret = setup_sysfs(shim);
+ if (ret < 0) {
+ dev_err(shim->dev, "Failed to set up sysfs: %d\n", ret);
+ return ret;
+ }
+
+ shim->usb_allowed = true;
+ update(shim, NULL, false);
+
+ return 0;
+}
+
+static int extcon_usb_shim_remove(struct platform_device *pdev)
+{
+ struct extcon_usb_shim *shim = platform_get_drvdata(pdev);
+
+ teardown_sysfs(shim);
+ mutex_destroy(&shim->lock);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "google,extcon-usb-shim" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, match_table);
+
+static struct platform_driver extcon_usb_shim = {
+ .driver = {
+ .name = "extcon-usb-shim",
+ .of_match_table = of_match_ptr(match_table),
+ .pm = NULL,
+ },
+ .probe = extcon_usb_shim_probe,
+ .remove = extcon_usb_shim_remove,
+};
+module_platform_driver(extcon_usb_shim);
+
+MODULE_DESCRIPTION("USB extcon shim");
+MODULE_LICENSE("GPL v2");