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