mux.c (12156B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * USB Type-C Multiplexer/DeMultiplexer Switch support 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 * Hans de Goede <hdegoede@redhat.com> 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 "mux.h" 19 20#define TYPEC_MUX_MAX_DEVS 3 21 22struct typec_switch { 23 struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS]; 24 unsigned int num_sw_devs; 25}; 26 27static int switch_fwnode_match(struct device *dev, const void *fwnode) 28{ 29 if (!is_typec_switch_dev(dev)) 30 return 0; 31 32 return dev_fwnode(dev) == fwnode; 33} 34 35static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id, 36 void *data) 37{ 38 struct device *dev; 39 40 /* 41 * Device graph (OF graph) does not give any means to identify the 42 * device type or the device class of the remote port parent that @fwnode 43 * represents, so in order to identify the type or the class of @fwnode 44 * an additional device property is needed. With typec switches the 45 * property is named "orientation-switch" (@id). The value of the device 46 * property is ignored. 47 */ 48 if (id && !fwnode_property_present(fwnode, id)) 49 return NULL; 50 51 /* 52 * At this point we are sure that @fwnode is a typec switch in all 53 * cases. If the switch hasn't yet been registered for some reason, the 54 * function "defers probe" for now. 55 */ 56 dev = class_find_device(&typec_mux_class, NULL, fwnode, 57 switch_fwnode_match); 58 59 return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); 60} 61 62/** 63 * fwnode_typec_switch_get - Find USB Type-C orientation switch 64 * @fwnode: The caller device node 65 * 66 * Finds a switch linked with @dev. Returns a reference to the switch on 67 * success, NULL if no matching connection was found, or 68 * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch 69 * has not been enumerated yet. 70 */ 71struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) 72{ 73 struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS]; 74 struct typec_switch *sw; 75 int count; 76 int err; 77 int i; 78 79 sw = kzalloc(sizeof(*sw), GFP_KERNEL); 80 if (!sw) 81 return ERR_PTR(-ENOMEM); 82 83 count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL, 84 typec_switch_match, 85 (void **)sw_devs, 86 ARRAY_SIZE(sw_devs)); 87 if (count <= 0) { 88 kfree(sw); 89 return NULL; 90 } 91 92 for (i = 0; i < count; i++) { 93 if (IS_ERR(sw_devs[i])) { 94 err = PTR_ERR(sw_devs[i]); 95 goto put_sw_devs; 96 } 97 } 98 99 for (i = 0; i < count; i++) { 100 WARN_ON(!try_module_get(sw_devs[i]->dev.parent->driver->owner)); 101 sw->sw_devs[i] = sw_devs[i]; 102 } 103 104 sw->num_sw_devs = count; 105 106 return sw; 107 108put_sw_devs: 109 for (i = 0; i < count; i++) { 110 if (!IS_ERR(sw_devs[i])) 111 put_device(&sw_devs[i]->dev); 112 } 113 114 kfree(sw); 115 116 return ERR_PTR(err); 117} 118EXPORT_SYMBOL_GPL(fwnode_typec_switch_get); 119 120/** 121 * typec_switch_put - Release USB Type-C orientation switch 122 * @sw: USB Type-C orientation switch 123 * 124 * Decrement reference count for @sw. 125 */ 126void typec_switch_put(struct typec_switch *sw) 127{ 128 struct typec_switch_dev *sw_dev; 129 unsigned int i; 130 131 if (IS_ERR_OR_NULL(sw)) 132 return; 133 134 for (i = 0; i < sw->num_sw_devs; i++) { 135 sw_dev = sw->sw_devs[i]; 136 137 module_put(sw_dev->dev.parent->driver->owner); 138 put_device(&sw_dev->dev); 139 } 140 kfree(sw); 141} 142EXPORT_SYMBOL_GPL(typec_switch_put); 143 144static void typec_switch_release(struct device *dev) 145{ 146 kfree(to_typec_switch_dev(dev)); 147} 148 149const struct device_type typec_switch_dev_type = { 150 .name = "orientation_switch", 151 .release = typec_switch_release, 152}; 153 154/** 155 * typec_switch_register - Register USB Type-C orientation switch 156 * @parent: Parent device 157 * @desc: Orientation switch description 158 * 159 * This function registers a switch that can be used for routing the correct 160 * data pairs depending on the cable plug orientation from the USB Type-C 161 * connector to the USB controllers. USB Type-C plugs can be inserted 162 * right-side-up or upside-down. 163 */ 164struct typec_switch_dev * 165typec_switch_register(struct device *parent, 166 const struct typec_switch_desc *desc) 167{ 168 struct typec_switch_dev *sw_dev; 169 int ret; 170 171 if (!desc || !desc->set) 172 return ERR_PTR(-EINVAL); 173 174 sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL); 175 if (!sw_dev) 176 return ERR_PTR(-ENOMEM); 177 178 sw_dev->set = desc->set; 179 180 device_initialize(&sw_dev->dev); 181 sw_dev->dev.parent = parent; 182 sw_dev->dev.fwnode = desc->fwnode; 183 sw_dev->dev.class = &typec_mux_class; 184 sw_dev->dev.type = &typec_switch_dev_type; 185 sw_dev->dev.driver_data = desc->drvdata; 186 ret = dev_set_name(&sw_dev->dev, "%s-switch", desc->name ? desc->name : dev_name(parent)); 187 if (ret) { 188 put_device(&sw_dev->dev); 189 return ERR_PTR(ret); 190 } 191 192 ret = device_add(&sw_dev->dev); 193 if (ret) { 194 dev_err(parent, "failed to register switch (%d)\n", ret); 195 put_device(&sw_dev->dev); 196 return ERR_PTR(ret); 197 } 198 199 return sw_dev; 200} 201EXPORT_SYMBOL_GPL(typec_switch_register); 202 203int typec_switch_set(struct typec_switch *sw, 204 enum typec_orientation orientation) 205{ 206 struct typec_switch_dev *sw_dev; 207 unsigned int i; 208 int ret; 209 210 if (IS_ERR_OR_NULL(sw)) 211 return 0; 212 213 for (i = 0; i < sw->num_sw_devs; i++) { 214 sw_dev = sw->sw_devs[i]; 215 216 ret = sw_dev->set(sw_dev, orientation); 217 if (ret) 218 return ret; 219 } 220 221 return 0; 222} 223EXPORT_SYMBOL_GPL(typec_switch_set); 224 225/** 226 * typec_switch_unregister - Unregister USB Type-C orientation switch 227 * @sw_dev: USB Type-C orientation switch 228 * 229 * Unregister switch that was registered with typec_switch_register(). 230 */ 231void typec_switch_unregister(struct typec_switch_dev *sw_dev) 232{ 233 if (!IS_ERR_OR_NULL(sw_dev)) 234 device_unregister(&sw_dev->dev); 235} 236EXPORT_SYMBOL_GPL(typec_switch_unregister); 237 238void typec_switch_set_drvdata(struct typec_switch_dev *sw_dev, void *data) 239{ 240 dev_set_drvdata(&sw_dev->dev, data); 241} 242EXPORT_SYMBOL_GPL(typec_switch_set_drvdata); 243 244void *typec_switch_get_drvdata(struct typec_switch_dev *sw_dev) 245{ 246 return dev_get_drvdata(&sw_dev->dev); 247} 248EXPORT_SYMBOL_GPL(typec_switch_get_drvdata); 249 250/* ------------------------------------------------------------------------- */ 251 252struct typec_mux { 253 struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS]; 254 unsigned int num_mux_devs; 255}; 256 257static int mux_fwnode_match(struct device *dev, const void *fwnode) 258{ 259 if (!is_typec_mux_dev(dev)) 260 return 0; 261 262 return dev_fwnode(dev) == fwnode; 263} 264 265static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id, 266 void *data) 267{ 268 const struct typec_altmode_desc *desc = data; 269 struct device *dev; 270 bool match; 271 int nval; 272 u16 *val; 273 int ret; 274 int i; 275 276 /* 277 * Check has the identifier already been "consumed". If it 278 * has, no need to do any extra connection identification. 279 */ 280 match = !id; 281 if (match) 282 goto find_mux; 283 284 /* Accessory Mode muxes */ 285 if (!desc) { 286 match = fwnode_property_present(fwnode, "accessory"); 287 if (match) 288 goto find_mux; 289 return NULL; 290 } 291 292 /* Alternate Mode muxes */ 293 nval = fwnode_property_count_u16(fwnode, "svid"); 294 if (nval <= 0) 295 return NULL; 296 297 val = kcalloc(nval, sizeof(*val), GFP_KERNEL); 298 if (!val) 299 return ERR_PTR(-ENOMEM); 300 301 ret = fwnode_property_read_u16_array(fwnode, "svid", val, nval); 302 if (ret < 0) { 303 kfree(val); 304 return ERR_PTR(ret); 305 } 306 307 for (i = 0; i < nval; i++) { 308 match = val[i] == desc->svid; 309 if (match) { 310 kfree(val); 311 goto find_mux; 312 } 313 } 314 kfree(val); 315 return NULL; 316 317find_mux: 318 dev = class_find_device(&typec_mux_class, NULL, fwnode, 319 mux_fwnode_match); 320 321 return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); 322} 323 324/** 325 * fwnode_typec_mux_get - Find USB Type-C Multiplexer 326 * @fwnode: The caller device node 327 * @desc: Alt Mode description 328 * 329 * Finds a mux linked to the caller. This function is primarily meant for the 330 * Type-C drivers. Returns a reference to the mux on success, NULL if no 331 * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection 332 * was found but the mux has not been enumerated yet. 333 */ 334struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, 335 const struct typec_altmode_desc *desc) 336{ 337 struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS]; 338 struct typec_mux *mux; 339 int count; 340 int err; 341 int i; 342 343 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 344 if (!mux) 345 return ERR_PTR(-ENOMEM); 346 347 count = fwnode_connection_find_matches(fwnode, "mode-switch", 348 (void *)desc, typec_mux_match, 349 (void **)mux_devs, 350 ARRAY_SIZE(mux_devs)); 351 if (count <= 0) { 352 kfree(mux); 353 return NULL; 354 } 355 356 for (i = 0; i < count; i++) { 357 if (IS_ERR(mux_devs[i])) { 358 err = PTR_ERR(mux_devs[i]); 359 goto put_mux_devs; 360 } 361 } 362 363 for (i = 0; i < count; i++) { 364 WARN_ON(!try_module_get(mux_devs[i]->dev.parent->driver->owner)); 365 mux->mux_devs[i] = mux_devs[i]; 366 } 367 368 mux->num_mux_devs = count; 369 370 return mux; 371 372put_mux_devs: 373 for (i = 0; i < count; i++) { 374 if (!IS_ERR(mux_devs[i])) 375 put_device(&mux_devs[i]->dev); 376 } 377 378 kfree(mux); 379 380 return ERR_PTR(err); 381} 382EXPORT_SYMBOL_GPL(fwnode_typec_mux_get); 383 384/** 385 * typec_mux_put - Release handle to a Multiplexer 386 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer 387 * 388 * Decrements reference count for @mux. 389 */ 390void typec_mux_put(struct typec_mux *mux) 391{ 392 struct typec_mux_dev *mux_dev; 393 unsigned int i; 394 395 if (IS_ERR_OR_NULL(mux)) 396 return; 397 398 for (i = 0; i < mux->num_mux_devs; i++) { 399 mux_dev = mux->mux_devs[i]; 400 module_put(mux_dev->dev.parent->driver->owner); 401 put_device(&mux_dev->dev); 402 } 403 kfree(mux); 404} 405EXPORT_SYMBOL_GPL(typec_mux_put); 406 407int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state) 408{ 409 struct typec_mux_dev *mux_dev; 410 unsigned int i; 411 int ret; 412 413 if (IS_ERR_OR_NULL(mux)) 414 return 0; 415 416 for (i = 0; i < mux->num_mux_devs; i++) { 417 mux_dev = mux->mux_devs[i]; 418 419 ret = mux_dev->set(mux_dev, state); 420 if (ret) 421 return ret; 422 } 423 424 return 0; 425} 426EXPORT_SYMBOL_GPL(typec_mux_set); 427 428static void typec_mux_release(struct device *dev) 429{ 430 kfree(to_typec_mux_dev(dev)); 431} 432 433const struct device_type typec_mux_dev_type = { 434 .name = "mode_switch", 435 .release = typec_mux_release, 436}; 437 438/** 439 * typec_mux_register - Register Multiplexer routing USB Type-C pins 440 * @parent: Parent device 441 * @desc: Multiplexer description 442 * 443 * USB Type-C connectors can be used for alternate modes of operation besides 444 * USB when Accessory/Alternate Modes are supported. With some of those modes, 445 * the pins on the connector need to be reconfigured. This function registers 446 * multiplexer switches routing the pins on the connector. 447 */ 448struct typec_mux_dev * 449typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) 450{ 451 struct typec_mux_dev *mux_dev; 452 int ret; 453 454 if (!desc || !desc->set) 455 return ERR_PTR(-EINVAL); 456 457 mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL); 458 if (!mux_dev) 459 return ERR_PTR(-ENOMEM); 460 461 mux_dev->set = desc->set; 462 463 device_initialize(&mux_dev->dev); 464 mux_dev->dev.parent = parent; 465 mux_dev->dev.fwnode = desc->fwnode; 466 mux_dev->dev.class = &typec_mux_class; 467 mux_dev->dev.type = &typec_mux_dev_type; 468 mux_dev->dev.driver_data = desc->drvdata; 469 ret = dev_set_name(&mux_dev->dev, "%s-mux", desc->name ? desc->name : dev_name(parent)); 470 if (ret) { 471 put_device(&mux_dev->dev); 472 return ERR_PTR(ret); 473 } 474 475 ret = device_add(&mux_dev->dev); 476 if (ret) { 477 dev_err(parent, "failed to register mux (%d)\n", ret); 478 put_device(&mux_dev->dev); 479 return ERR_PTR(ret); 480 } 481 482 return mux_dev; 483} 484EXPORT_SYMBOL_GPL(typec_mux_register); 485 486/** 487 * typec_mux_unregister - Unregister Multiplexer Switch 488 * @mux_dev: USB Type-C Connector Multiplexer/DeMultiplexer 489 * 490 * Unregister mux that was registered with typec_mux_register(). 491 */ 492void typec_mux_unregister(struct typec_mux_dev *mux_dev) 493{ 494 if (!IS_ERR_OR_NULL(mux_dev)) 495 device_unregister(&mux_dev->dev); 496} 497EXPORT_SYMBOL_GPL(typec_mux_unregister); 498 499void typec_mux_set_drvdata(struct typec_mux_dev *mux_dev, void *data) 500{ 501 dev_set_drvdata(&mux_dev->dev, data); 502} 503EXPORT_SYMBOL_GPL(typec_mux_set_drvdata); 504 505void *typec_mux_get_drvdata(struct typec_mux_dev *mux_dev) 506{ 507 return dev_get_drvdata(&mux_dev->dev); 508} 509EXPORT_SYMBOL_GPL(typec_mux_get_drvdata); 510 511struct class typec_mux_class = { 512 .name = "typec_mux", 513 .owner = THIS_MODULE, 514};