linux/drivers/usb/typec/mux.c
<<
>>
Prefs
   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/mutex.h>
  13#include <linux/usb/typec_mux.h>
  14
  15static DEFINE_MUTEX(switch_lock);
  16static DEFINE_MUTEX(mux_lock);
  17static LIST_HEAD(switch_list);
  18static LIST_HEAD(mux_list);
  19
  20static void *typec_switch_match(struct device_connection *con, int ep,
  21                                void *data)
  22{
  23        struct typec_switch *sw;
  24
  25        list_for_each_entry(sw, &switch_list, entry)
  26                if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
  27                        return sw;
  28
  29        /*
  30         * We only get called if a connection was found, tell the caller to
  31         * wait for the switch to show up.
  32         */
  33        return ERR_PTR(-EPROBE_DEFER);
  34}
  35
  36/**
  37 * typec_switch_get - Find USB Type-C orientation switch
  38 * @dev: The caller device
  39 *
  40 * Finds a switch linked with @dev. Returns a reference to the switch on
  41 * success, NULL if no matching connection was found, or
  42 * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
  43 * has not been enumerated yet.
  44 */
  45struct typec_switch *typec_switch_get(struct device *dev)
  46{
  47        struct typec_switch *sw;
  48
  49        mutex_lock(&switch_lock);
  50        sw = device_connection_find_match(dev, "typec-switch", NULL,
  51                                          typec_switch_match);
  52        if (!IS_ERR_OR_NULL(sw))
  53                get_device(sw->dev);
  54        mutex_unlock(&switch_lock);
  55
  56        return sw;
  57}
  58EXPORT_SYMBOL_GPL(typec_switch_get);
  59
  60/**
  61 * typec_put_switch - Release USB Type-C orientation switch
  62 * @sw: USB Type-C orientation switch
  63 *
  64 * Decrement reference count for @sw.
  65 */
  66void typec_switch_put(struct typec_switch *sw)
  67{
  68        if (!IS_ERR_OR_NULL(sw))
  69                put_device(sw->dev);
  70}
  71EXPORT_SYMBOL_GPL(typec_switch_put);
  72
  73/**
  74 * typec_switch_register - Register USB Type-C orientation switch
  75 * @sw: USB Type-C orientation switch
  76 *
  77 * This function registers a switch that can be used for routing the correct
  78 * data pairs depending on the cable plug orientation from the USB Type-C
  79 * connector to the USB controllers. USB Type-C plugs can be inserted
  80 * right-side-up or upside-down.
  81 */
  82int typec_switch_register(struct typec_switch *sw)
  83{
  84        mutex_lock(&switch_lock);
  85        list_add_tail(&sw->entry, &switch_list);
  86        mutex_unlock(&switch_lock);
  87
  88        return 0;
  89}
  90EXPORT_SYMBOL_GPL(typec_switch_register);
  91
  92/**
  93 * typec_switch_unregister - Unregister USB Type-C orientation switch
  94 * @sw: USB Type-C orientation switch
  95 *
  96 * Unregister switch that was registered with typec_switch_register().
  97 */
  98void typec_switch_unregister(struct typec_switch *sw)
  99{
 100        mutex_lock(&switch_lock);
 101        list_del(&sw->entry);
 102        mutex_unlock(&switch_lock);
 103}
 104EXPORT_SYMBOL_GPL(typec_switch_unregister);
 105
 106/* ------------------------------------------------------------------------- */
 107
 108static void *typec_mux_match(struct device_connection *con, int ep, void *data)
 109{
 110        struct typec_mux *mux;
 111
 112        list_for_each_entry(mux, &mux_list, entry)
 113                if (!strcmp(con->endpoint[ep], dev_name(mux->dev)))
 114                        return mux;
 115
 116        /*
 117         * We only get called if a connection was found, tell the caller to
 118         * wait for the switch to show up.
 119         */
 120        return ERR_PTR(-EPROBE_DEFER);
 121}
 122
 123/**
 124 * typec_mux_get - Find USB Type-C Multiplexer
 125 * @dev: The caller device
 126 * @name: Mux identifier
 127 *
 128 * Finds a mux linked to the caller. This function is primarily meant for the
 129 * Type-C drivers. Returns a reference to the mux on success, NULL if no
 130 * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
 131 * was found but the mux has not been enumerated yet.
 132 */
 133struct typec_mux *typec_mux_get(struct device *dev, const char *name)
 134{
 135        struct typec_mux *mux;
 136
 137        mutex_lock(&mux_lock);
 138        mux = device_connection_find_match(dev, name, NULL, typec_mux_match);
 139        if (!IS_ERR_OR_NULL(mux))
 140                get_device(mux->dev);
 141        mutex_unlock(&mux_lock);
 142
 143        return mux;
 144}
 145EXPORT_SYMBOL_GPL(typec_mux_get);
 146
 147/**
 148 * typec_mux_put - Release handle to a Multiplexer
 149 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
 150 *
 151 * Decrements reference count for @mux.
 152 */
 153void typec_mux_put(struct typec_mux *mux)
 154{
 155        if (!IS_ERR_OR_NULL(mux))
 156                put_device(mux->dev);
 157}
 158EXPORT_SYMBOL_GPL(typec_mux_put);
 159
 160/**
 161 * typec_mux_register - Register Multiplexer routing USB Type-C pins
 162 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
 163 *
 164 * USB Type-C connectors can be used for alternate modes of operation besides
 165 * USB when Accessory/Alternate Modes are supported. With some of those modes,
 166 * the pins on the connector need to be reconfigured. This function registers
 167 * multiplexer switches routing the pins on the connector.
 168 */
 169int typec_mux_register(struct typec_mux *mux)
 170{
 171        mutex_lock(&mux_lock);
 172        list_add_tail(&mux->entry, &mux_list);
 173        mutex_unlock(&mux_lock);
 174
 175        return 0;
 176}
 177EXPORT_SYMBOL_GPL(typec_mux_register);
 178
 179/**
 180 * typec_mux_unregister - Unregister Multiplexer Switch
 181 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
 182 *
 183 * Unregister mux that was registered with typec_mux_register().
 184 */
 185void typec_mux_unregister(struct typec_mux *mux)
 186{
 187        mutex_lock(&mux_lock);
 188        list_del(&mux->entry);
 189        mutex_unlock(&mux_lock);
 190}
 191EXPORT_SYMBOL_GPL(typec_mux_unregister);
 192