Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: Add support for retimers

Introduce a retimer device class and associated functions that register
and use retimer "switch" devices. These operate in a manner similar to
the "mode-switch" and help configure retimers that exist between the
Type-C connector and host controller(s).

Type C ports can be linked to retimers using firmware node device
references (again, in a manner similar to "mode-switch").

There are no new sysfs files being created; there is the new retimer
class directory, but there are no class-specific files being created
there.

Signed-off-by: Prashant Malani <pmalani@chromium.org>
Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20220711072333.2064341-2-pmalani@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Prashant Malani and committed by
Greg Kroah-Hartman
ddaf8d96 69bb3520

+238 -2
+1 -1
drivers/usb/typec/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 obj-$(CONFIG_TYPEC) += typec.o 3 - typec-y := class.o mux.o bus.o pd.o 3 + typec-y := class.o mux.o bus.o pd.o retimer.o 4 4 typec-$(CONFIG_ACPI) += port-mapper.o 5 5 obj-$(CONFIG_TYPEC) += altmodes/ 6 6 obj-$(CONFIG_TYPEC_TCPM) += tcpm/
+8 -1
drivers/usb/typec/class.c
··· 2299 2299 if (ret) 2300 2300 goto err_unregister_bus; 2301 2301 2302 - ret = class_register(&typec_class); 2302 + ret = class_register(&retimer_class); 2303 2303 if (ret) 2304 2304 goto err_unregister_mux_class; 2305 + 2306 + ret = class_register(&typec_class); 2307 + if (ret) 2308 + goto err_unregister_retimer_class; 2305 2309 2306 2310 ret = usb_power_delivery_init(); 2307 2311 if (ret) ··· 2315 2311 2316 2312 err_unregister_class: 2317 2313 class_unregister(&typec_class); 2314 + 2315 + err_unregister_retimer_class: 2316 + class_unregister(&retimer_class); 2318 2317 2319 2318 err_unregister_mux_class: 2320 2319 class_unregister(&typec_mux_class);
+1
drivers/usb/typec/class.h
··· 76 76 #define is_typec_port(dev) ((dev)->type == &typec_port_dev_type) 77 77 78 78 extern struct class typec_mux_class; 79 + extern struct class retimer_class; 79 80 extern struct class typec_class; 80 81 81 82 #if defined(CONFIG_ACPI)
+168
drivers/usb/typec/retimer.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2022 Google LLC 4 + * 5 + * USB Type-C Retimer support. 6 + * Author: Prashant Malani <pmalani@chromium.org> 7 + * 8 + */ 9 + 10 + #include <linux/device.h> 11 + #include <linux/list.h> 12 + #include <linux/module.h> 13 + #include <linux/mutex.h> 14 + #include <linux/property.h> 15 + #include <linux/slab.h> 16 + 17 + #include "class.h" 18 + #include "retimer.h" 19 + 20 + static bool dev_name_ends_with(struct device *dev, const char *suffix) 21 + { 22 + const char *name = dev_name(dev); 23 + const int name_len = strlen(name); 24 + const int suffix_len = strlen(suffix); 25 + 26 + if (suffix_len > name_len) 27 + return false; 28 + 29 + return strcmp(name + (name_len - suffix_len), suffix) == 0; 30 + } 31 + 32 + static int retimer_fwnode_match(struct device *dev, const void *fwnode) 33 + { 34 + return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-retimer"); 35 + } 36 + 37 + static void *typec_retimer_match(struct fwnode_handle *fwnode, const char *id, void *data) 38 + { 39 + struct device *dev = class_find_device(&retimer_class, NULL, fwnode, 40 + retimer_fwnode_match); 41 + 42 + return dev ? to_typec_retimer(dev) : ERR_PTR(-EPROBE_DEFER); 43 + } 44 + 45 + /** 46 + * fwnode_typec_retimer_get - Find USB Type-C retimer. 47 + * @fwnode: The caller device node. 48 + * 49 + * Finds a retimer linked to the caller. This function is primarily meant for the 50 + * Type-C drivers. Returns a reference to the retimer on success, NULL if no 51 + * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection 52 + * was found but the retimer has not been enumerated yet. 53 + */ 54 + struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode) 55 + { 56 + struct typec_retimer *retimer; 57 + 58 + retimer = fwnode_connection_find_match(fwnode, "retimer-switch", NULL, typec_retimer_match); 59 + if (!IS_ERR_OR_NULL(retimer)) 60 + WARN_ON(!try_module_get(retimer->dev.parent->driver->owner)); 61 + 62 + return retimer; 63 + } 64 + EXPORT_SYMBOL_GPL(fwnode_typec_retimer_get); 65 + 66 + /** 67 + * typec_retimer_put - Release handle to a retimer. 68 + * @retimer: USB Type-C Connector Retimer. 69 + * 70 + * Decrements reference count for @retimer. 71 + */ 72 + void typec_retimer_put(struct typec_retimer *retimer) 73 + { 74 + if (!IS_ERR_OR_NULL(retimer)) { 75 + module_put(retimer->dev.parent->driver->owner); 76 + put_device(&retimer->dev); 77 + } 78 + } 79 + EXPORT_SYMBOL_GPL(typec_retimer_put); 80 + 81 + int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state) 82 + { 83 + if (IS_ERR_OR_NULL(retimer)) 84 + return 0; 85 + 86 + return retimer->set(retimer, state); 87 + } 88 + EXPORT_SYMBOL_GPL(typec_retimer_set); 89 + 90 + static void typec_retimer_release(struct device *dev) 91 + { 92 + kfree(to_typec_retimer(dev)); 93 + } 94 + 95 + static const struct device_type typec_retimer_dev_type = { 96 + .name = "typec_retimer", 97 + .release = typec_retimer_release, 98 + }; 99 + 100 + /** 101 + * typec_retimer_register - Register a retimer device. 102 + * @parent: Parent device. 103 + * @desc: Retimer description. 104 + * 105 + * Some USB Type-C connectors have their physical lines routed through retimers before they 106 + * reach muxes or host controllers. In some cases (for example: using alternate modes) 107 + * these retimers need to be reconfigured appropriately. This function registers retimer 108 + * switches which route and potentially modify the signals on the Type C physical lines 109 + * enroute to the host controllers. 110 + */ 111 + struct typec_retimer * 112 + typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc) 113 + { 114 + struct typec_retimer *retimer; 115 + int ret; 116 + 117 + if (!desc || !desc->set) 118 + return ERR_PTR(-EINVAL); 119 + 120 + retimer = kzalloc(sizeof(*retimer), GFP_KERNEL); 121 + if (!retimer) 122 + return ERR_PTR(-ENOMEM); 123 + 124 + retimer->set = desc->set; 125 + 126 + device_initialize(&retimer->dev); 127 + retimer->dev.parent = parent; 128 + retimer->dev.fwnode = desc->fwnode; 129 + retimer->dev.class = &retimer_class; 130 + retimer->dev.type = &typec_retimer_dev_type; 131 + retimer->dev.driver_data = desc->drvdata; 132 + dev_set_name(&retimer->dev, "%s-retimer", 133 + desc->name ? desc->name : dev_name(parent)); 134 + 135 + ret = device_add(&retimer->dev); 136 + if (ret) { 137 + dev_err(parent, "failed to register retimer (%d)\n", ret); 138 + put_device(&retimer->dev); 139 + return ERR_PTR(ret); 140 + } 141 + 142 + return retimer; 143 + } 144 + EXPORT_SYMBOL_GPL(typec_retimer_register); 145 + 146 + /** 147 + * typec_retimer_unregister - Unregister retimer device. 148 + * @retimer: USB Type-C Connector retimer. 149 + * 150 + * Unregister retimer that was registered with typec_retimer_register(). 151 + */ 152 + void typec_retimer_unregister(struct typec_retimer *retimer) 153 + { 154 + if (!IS_ERR_OR_NULL(retimer)) 155 + device_unregister(&retimer->dev); 156 + } 157 + EXPORT_SYMBOL_GPL(typec_retimer_unregister); 158 + 159 + void *typec_retimer_get_drvdata(struct typec_retimer *retimer) 160 + { 161 + return dev_get_drvdata(&retimer->dev); 162 + } 163 + EXPORT_SYMBOL_GPL(typec_retimer_get_drvdata); 164 + 165 + struct class retimer_class = { 166 + .name = "retimer", 167 + .owner = THIS_MODULE, 168 + };
+15
drivers/usb/typec/retimer.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __USB_TYPEC_RETIMER__ 4 + #define __USB_TYPEC_RETIMER__ 5 + 6 + #include <linux/usb/typec_retimer.h> 7 + 8 + struct typec_retimer { 9 + struct device dev; 10 + typec_retimer_set_fn_t set; 11 + }; 12 + 13 + #define to_typec_retimer(_dev_) container_of(_dev_, struct typec_retimer, dev) 14 + 15 + #endif /* __USB_TYPEC_RETIMER__ */
+45
include/linux/usb/typec_retimer.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __USB_TYPEC_RETIMER 4 + #define __USB_TYPEC_RETIMER 5 + 6 + #include <linux/property.h> 7 + #include <linux/usb/typec.h> 8 + 9 + struct device; 10 + struct typec_retimer; 11 + struct typec_altmode; 12 + struct fwnode_handle; 13 + 14 + struct typec_retimer_state { 15 + struct typec_altmode *alt; 16 + unsigned long mode; 17 + void *data; 18 + }; 19 + 20 + typedef int (*typec_retimer_set_fn_t)(struct typec_retimer *retimer, 21 + struct typec_retimer_state *state); 22 + 23 + struct typec_retimer_desc { 24 + struct fwnode_handle *fwnode; 25 + typec_retimer_set_fn_t set; 26 + const char *name; 27 + void *drvdata; 28 + }; 29 + 30 + struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode); 31 + void typec_retimer_put(struct typec_retimer *retimer); 32 + int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state); 33 + 34 + static inline struct typec_retimer *typec_retimer_get(struct device *dev) 35 + { 36 + return fwnode_typec_retimer_get(dev_fwnode(dev)); 37 + } 38 + 39 + struct typec_retimer * 40 + typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc); 41 + void typec_retimer_unregister(struct typec_retimer *retimer); 42 + 43 + void *typec_retimer_get_drvdata(struct typec_retimer *retimer); 44 + 45 + #endif /* __USB_TYPEC_RETIMER */