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