summaryrefslogtreecommitdiffstats
path: root/drivers/usb/typec/class.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/typec/class.c')
-rw-r--r--drivers/usb/typec/class.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
new file mode 100644
index 0000000000..7f498550f8
--- /dev/null
+++ b/drivers/usb/typec/class.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <module.h>
+#include <driver.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
+#include <param.h>
+
+enum typec_param_accessory {
+ TYPEC_PARAM_ACCESSORY_NONE,
+ TYPEC_PARAM_ACCESSORY_AUDIO,
+ TYPEC_PARAM_ACCESSORY_DEBUG,
+};
+
+struct typec_port {
+ struct device dev;
+ const struct typec_operations *ops;
+ int pwr_role; /* enum typec_role */
+ int usb_role; /* enum usb_role role */
+ int accessory; /* enum typec_param_accessory */
+};
+
+/**
+ * typec_set_pwr_role - Report power role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report power role changes.
+ */
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
+{
+ port->pwr_role = role;
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_role);
+
+static inline enum typec_param_accessory typec_mode_to_accessory(int mode)
+{
+ switch (mode) {
+ case TYPEC_MODE_AUDIO:
+ return TYPEC_PARAM_ACCESSORY_AUDIO;
+ case TYPEC_MODE_DEBUG:
+ return TYPEC_PARAM_ACCESSORY_DEBUG;
+ default:
+ return TYPEC_PARAM_ACCESSORY_NONE;
+ }
+}
+
+/**
+ * typec_set_mode - Set mode of operation for USB Type-C connector
+ * @port: USB Type-C connector
+ * @mode: Accessory Mode, USB Operation or Safe State
+ *
+ * Configure @port for Accessory Mode @mode. This function will configure the
+ * muxes needed for @mode.
+ */
+int typec_set_mode(struct typec_port *port, int mode)
+{
+ port->accessory = typec_mode_to_accessory(mode);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_mode);
+
+/**
+ * typec_set_role - Set USB role for a Type-C connector
+ * @port: USB Type-C connector
+ * @role: USB role to be switched to
+ *
+ * Set USB role @role for @sw. This is equivalent to Linux
+ * usb_role_switch_set_role();
+ */
+int typec_set_role(struct typec_port *port, enum usb_role role)
+{
+ port->usb_role = role;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_role);
+
+/**
+ * typec_get_drvdata - Return private driver data pointer
+ * @port: USB Type-C port
+ */
+void *typec_get_drvdata(struct typec_port *port)
+{
+ return port->dev.priv;
+}
+EXPORT_SYMBOL_GPL(typec_get_drvdata);
+
+static int typec_register_port_dev(struct typec_port *port, const char *name, int id)
+{
+ port->dev.id = id;
+ dev_set_name(&port->dev, name);
+
+ return register_device(&port->dev);
+}
+
+static const char * const usb_role_names[] = {
+ [USB_ROLE_NONE] = "none",
+ [USB_ROLE_HOST] = "host",
+ [USB_ROLE_DEVICE] = "device",
+};
+
+static const char * const pwr_role_names[] = {
+ [TYPEC_SINK] = "sink",
+ [TYPEC_SOURCE] = "source",
+};
+
+static const char * const accessory_names[] = {
+ [TYPEC_PARAM_ACCESSORY_NONE] = "none",
+ [TYPEC_PARAM_ACCESSORY_AUDIO] = "audio", /* analog */
+ [TYPEC_PARAM_ACCESSORY_DEBUG] = "debug",
+};
+
+static int typec_param_update(struct param_d *p, void *priv)
+{
+ struct typec_port *port = priv;
+
+ return port->ops->poll(port);
+}
+
+/**
+ * typec_register_port - Register a USB Type-C Port
+ * @parent: Parent device
+ * @cap: Description of the port
+ *
+ * Registers a device for USB Type-C Port described in @cap.
+ *
+ * Returns handle to the port on success or ERR_PTR on failure.
+ */
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap)
+{
+ struct typec_port *port;
+ struct device *dev;
+ const char *alias;
+ int ret;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ port->ops = cap->ops;
+ dev = &port->dev;
+ dev->parent = parent;
+ dev->of_node = cap->of_node;
+ dev->priv = cap->driver_data;
+
+ alias = dev->of_node ? of_alias_get(dev->of_node) : NULL;
+ if (alias)
+ ret = typec_register_port_dev(port, alias, DEVICE_ID_SINGLE);
+ if (!alias || ret)
+ ret = typec_register_port_dev(port, "typec", DEVICE_ID_DYNAMIC);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ of_platform_device_dummy_drv(dev);
+ if (dev->of_node)
+ dev->of_node->dev = dev;
+
+ dev_add_param_enum(dev, "usb_role", param_set_readonly, typec_param_update,
+ &port->usb_role, usb_role_names,
+ ARRAY_SIZE(usb_role_names), port);
+ dev_add_param_enum(dev, "pwr_role", param_set_readonly, typec_param_update,
+ &port->pwr_role, pwr_role_names,
+ ARRAY_SIZE(pwr_role_names), port);
+ dev_add_param_enum(dev, "accessory", param_set_readonly, typec_param_update,
+ &port->accessory, accessory_names,
+ ARRAY_SIZE(accessory_names), port);
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(typec_register_port);