Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * USB Type-C Multiplexer/DeMultiplexer Switch support
0004  *
0005  * Copyright (C) 2018 Intel Corporation
0006  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
0007  *         Hans de Goede <hdegoede@redhat.com>
0008  */
0009 
0010 #include <linux/device.h>
0011 #include <linux/list.h>
0012 #include <linux/module.h>
0013 #include <linux/mutex.h>
0014 #include <linux/property.h>
0015 #include <linux/slab.h>
0016 
0017 #include "class.h"
0018 #include "mux.h"
0019 
0020 #define TYPEC_MUX_MAX_DEVS  3
0021 
0022 struct typec_switch {
0023     struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
0024     unsigned int num_sw_devs;
0025 };
0026 
0027 static int switch_fwnode_match(struct device *dev, const void *fwnode)
0028 {
0029     if (!is_typec_switch_dev(dev))
0030         return 0;
0031 
0032     return dev_fwnode(dev) == fwnode;
0033 }
0034 
0035 static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
0036                 void *data)
0037 {
0038     struct device *dev;
0039 
0040     /*
0041      * Device graph (OF graph) does not give any means to identify the
0042      * device type or the device class of the remote port parent that @fwnode
0043      * represents, so in order to identify the type or the class of @fwnode
0044      * an additional device property is needed. With typec switches the
0045      * property is named "orientation-switch" (@id). The value of the device
0046      * property is ignored.
0047      */
0048     if (id && !fwnode_property_present(fwnode, id))
0049         return NULL;
0050 
0051     /*
0052      * At this point we are sure that @fwnode is a typec switch in all
0053      * cases. If the switch hasn't yet been registered for some reason, the
0054      * function "defers probe" for now.
0055      */
0056     dev = class_find_device(&typec_mux_class, NULL, fwnode,
0057                 switch_fwnode_match);
0058 
0059     return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER);
0060 }
0061 
0062 /**
0063  * fwnode_typec_switch_get - Find USB Type-C orientation switch
0064  * @fwnode: The caller device node
0065  *
0066  * Finds a switch linked with @dev. Returns a reference to the switch on
0067  * success, NULL if no matching connection was found, or
0068  * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
0069  * has not been enumerated yet.
0070  */
0071 struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
0072 {
0073     struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
0074     struct typec_switch *sw;
0075     int count;
0076     int err;
0077     int i;
0078 
0079     sw = kzalloc(sizeof(*sw), GFP_KERNEL);
0080     if (!sw)
0081         return ERR_PTR(-ENOMEM);
0082 
0083     count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL,
0084                            typec_switch_match,
0085                            (void **)sw_devs,
0086                            ARRAY_SIZE(sw_devs));
0087     if (count <= 0) {
0088         kfree(sw);
0089         return NULL;
0090     }
0091 
0092     for (i = 0; i < count; i++) {
0093         if (IS_ERR(sw_devs[i])) {
0094             err = PTR_ERR(sw_devs[i]);
0095             goto put_sw_devs;
0096         }
0097     }
0098 
0099     for (i = 0; i < count; i++) {
0100         WARN_ON(!try_module_get(sw_devs[i]->dev.parent->driver->owner));
0101         sw->sw_devs[i] = sw_devs[i];
0102     }
0103 
0104     sw->num_sw_devs = count;
0105 
0106     return sw;
0107 
0108 put_sw_devs:
0109     for (i = 0; i < count; i++) {
0110         if (!IS_ERR(sw_devs[i]))
0111             put_device(&sw_devs[i]->dev);
0112     }
0113 
0114     kfree(sw);
0115 
0116     return ERR_PTR(err);
0117 }
0118 EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
0119 
0120 /**
0121  * typec_switch_put - Release USB Type-C orientation switch
0122  * @sw: USB Type-C orientation switch
0123  *
0124  * Decrement reference count for @sw.
0125  */
0126 void typec_switch_put(struct typec_switch *sw)
0127 {
0128     struct typec_switch_dev *sw_dev;
0129     unsigned int i;
0130 
0131     if (IS_ERR_OR_NULL(sw))
0132         return;
0133 
0134     for (i = 0; i < sw->num_sw_devs; i++) {
0135         sw_dev = sw->sw_devs[i];
0136 
0137         module_put(sw_dev->dev.parent->driver->owner);
0138         put_device(&sw_dev->dev);
0139     }
0140     kfree(sw);
0141 }
0142 EXPORT_SYMBOL_GPL(typec_switch_put);
0143 
0144 static void typec_switch_release(struct device *dev)
0145 {
0146     kfree(to_typec_switch_dev(dev));
0147 }
0148 
0149 const struct device_type typec_switch_dev_type = {
0150     .name = "orientation_switch",
0151     .release = typec_switch_release,
0152 };
0153 
0154 /**
0155  * typec_switch_register - Register USB Type-C orientation switch
0156  * @parent: Parent device
0157  * @desc: Orientation switch description
0158  *
0159  * This function registers a switch that can be used for routing the correct
0160  * data pairs depending on the cable plug orientation from the USB Type-C
0161  * connector to the USB controllers. USB Type-C plugs can be inserted
0162  * right-side-up or upside-down.
0163  */
0164 struct typec_switch_dev *
0165 typec_switch_register(struct device *parent,
0166               const struct typec_switch_desc *desc)
0167 {
0168     struct typec_switch_dev *sw_dev;
0169     int ret;
0170 
0171     if (!desc || !desc->set)
0172         return ERR_PTR(-EINVAL);
0173 
0174     sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL);
0175     if (!sw_dev)
0176         return ERR_PTR(-ENOMEM);
0177 
0178     sw_dev->set = desc->set;
0179 
0180     device_initialize(&sw_dev->dev);
0181     sw_dev->dev.parent = parent;
0182     sw_dev->dev.fwnode = desc->fwnode;
0183     sw_dev->dev.class = &typec_mux_class;
0184     sw_dev->dev.type = &typec_switch_dev_type;
0185     sw_dev->dev.driver_data = desc->drvdata;
0186     ret = dev_set_name(&sw_dev->dev, "%s-switch", desc->name ? desc->name : dev_name(parent));
0187     if (ret) {
0188         put_device(&sw_dev->dev);
0189         return ERR_PTR(ret);
0190     }
0191 
0192     ret = device_add(&sw_dev->dev);
0193     if (ret) {
0194         dev_err(parent, "failed to register switch (%d)\n", ret);
0195         put_device(&sw_dev->dev);
0196         return ERR_PTR(ret);
0197     }
0198 
0199     return sw_dev;
0200 }
0201 EXPORT_SYMBOL_GPL(typec_switch_register);
0202 
0203 int typec_switch_set(struct typec_switch *sw,
0204              enum typec_orientation orientation)
0205 {
0206     struct typec_switch_dev *sw_dev;
0207     unsigned int i;
0208     int ret;
0209 
0210     if (IS_ERR_OR_NULL(sw))
0211         return 0;
0212 
0213     for (i = 0; i < sw->num_sw_devs; i++) {
0214         sw_dev = sw->sw_devs[i];
0215 
0216         ret = sw_dev->set(sw_dev, orientation);
0217         if (ret)
0218             return ret;
0219     }
0220 
0221     return 0;
0222 }
0223 EXPORT_SYMBOL_GPL(typec_switch_set);
0224 
0225 /**
0226  * typec_switch_unregister - Unregister USB Type-C orientation switch
0227  * @sw_dev: USB Type-C orientation switch
0228  *
0229  * Unregister switch that was registered with typec_switch_register().
0230  */
0231 void typec_switch_unregister(struct typec_switch_dev *sw_dev)
0232 {
0233     if (!IS_ERR_OR_NULL(sw_dev))
0234         device_unregister(&sw_dev->dev);
0235 }
0236 EXPORT_SYMBOL_GPL(typec_switch_unregister);
0237 
0238 void typec_switch_set_drvdata(struct typec_switch_dev *sw_dev, void *data)
0239 {
0240     dev_set_drvdata(&sw_dev->dev, data);
0241 }
0242 EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
0243 
0244 void *typec_switch_get_drvdata(struct typec_switch_dev *sw_dev)
0245 {
0246     return dev_get_drvdata(&sw_dev->dev);
0247 }
0248 EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
0249 
0250 /* ------------------------------------------------------------------------- */
0251 
0252 struct typec_mux {
0253     struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
0254     unsigned int num_mux_devs;
0255 };
0256 
0257 static int mux_fwnode_match(struct device *dev, const void *fwnode)
0258 {
0259     if (!is_typec_mux_dev(dev))
0260         return 0;
0261 
0262     return dev_fwnode(dev) == fwnode;
0263 }
0264 
0265 static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id,
0266                  void *data)
0267 {
0268     const struct typec_altmode_desc *desc = data;
0269     struct device *dev;
0270     bool match;
0271     int nval;
0272     u16 *val;
0273     int ret;
0274     int i;
0275 
0276     /*
0277      * Check has the identifier already been "consumed". If it
0278      * has, no need to do any extra connection identification.
0279      */
0280     match = !id;
0281     if (match)
0282         goto find_mux;
0283 
0284     if (!desc) {
0285         /*
0286          * Accessory Mode muxes & muxes which explicitly specify
0287          * the required identifier can avoid SVID matching.
0288          */
0289         match = fwnode_property_present(fwnode, "accessory") ||
0290             fwnode_property_present(fwnode, id);
0291         if (match)
0292             goto find_mux;
0293         return NULL;
0294     }
0295 
0296     /* Alternate Mode muxes */
0297     nval = fwnode_property_count_u16(fwnode, "svid");
0298     if (nval <= 0)
0299         return NULL;
0300 
0301     val = kcalloc(nval, sizeof(*val), GFP_KERNEL);
0302     if (!val)
0303         return ERR_PTR(-ENOMEM);
0304 
0305     ret = fwnode_property_read_u16_array(fwnode, "svid", val, nval);
0306     if (ret < 0) {
0307         kfree(val);
0308         return ERR_PTR(ret);
0309     }
0310 
0311     for (i = 0; i < nval; i++) {
0312         match = val[i] == desc->svid;
0313         if (match) {
0314             kfree(val);
0315             goto find_mux;
0316         }
0317     }
0318     kfree(val);
0319     return NULL;
0320 
0321 find_mux:
0322     dev = class_find_device(&typec_mux_class, NULL, fwnode,
0323                 mux_fwnode_match);
0324 
0325     return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
0326 }
0327 
0328 /**
0329  * fwnode_typec_mux_get - Find USB Type-C Multiplexer
0330  * @fwnode: The caller device node
0331  * @desc: Alt Mode description
0332  *
0333  * Finds a mux linked to the caller. This function is primarily meant for the
0334  * Type-C drivers. Returns a reference to the mux on success, NULL if no
0335  * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
0336  * was found but the mux has not been enumerated yet.
0337  */
0338 struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
0339                        const struct typec_altmode_desc *desc)
0340 {
0341     struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
0342     struct typec_mux *mux;
0343     int count;
0344     int err;
0345     int i;
0346 
0347     mux = kzalloc(sizeof(*mux), GFP_KERNEL);
0348     if (!mux)
0349         return ERR_PTR(-ENOMEM);
0350 
0351     count = fwnode_connection_find_matches(fwnode, "mode-switch",
0352                            (void *)desc, typec_mux_match,
0353                            (void **)mux_devs,
0354                            ARRAY_SIZE(mux_devs));
0355     if (count <= 0) {
0356         kfree(mux);
0357         return NULL;
0358     }
0359 
0360     for (i = 0; i < count; i++) {
0361         if (IS_ERR(mux_devs[i])) {
0362             err = PTR_ERR(mux_devs[i]);
0363             goto put_mux_devs;
0364         }
0365     }
0366 
0367     for (i = 0; i < count; i++) {
0368         WARN_ON(!try_module_get(mux_devs[i]->dev.parent->driver->owner));
0369         mux->mux_devs[i] = mux_devs[i];
0370     }
0371 
0372     mux->num_mux_devs = count;
0373 
0374     return mux;
0375 
0376 put_mux_devs:
0377     for (i = 0; i < count; i++) {
0378         if (!IS_ERR(mux_devs[i]))
0379             put_device(&mux_devs[i]->dev);
0380     }
0381 
0382     kfree(mux);
0383 
0384     return ERR_PTR(err);
0385 }
0386 EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
0387 
0388 /**
0389  * typec_mux_put - Release handle to a Multiplexer
0390  * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
0391  *
0392  * Decrements reference count for @mux.
0393  */
0394 void typec_mux_put(struct typec_mux *mux)
0395 {
0396     struct typec_mux_dev *mux_dev;
0397     unsigned int i;
0398 
0399     if (IS_ERR_OR_NULL(mux))
0400         return;
0401 
0402     for (i = 0; i < mux->num_mux_devs; i++) {
0403         mux_dev = mux->mux_devs[i];
0404         module_put(mux_dev->dev.parent->driver->owner);
0405         put_device(&mux_dev->dev);
0406     }
0407     kfree(mux);
0408 }
0409 EXPORT_SYMBOL_GPL(typec_mux_put);
0410 
0411 int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
0412 {
0413     struct typec_mux_dev *mux_dev;
0414     unsigned int i;
0415     int ret;
0416 
0417     if (IS_ERR_OR_NULL(mux))
0418         return 0;
0419 
0420     for (i = 0; i < mux->num_mux_devs; i++) {
0421         mux_dev = mux->mux_devs[i];
0422 
0423         ret = mux_dev->set(mux_dev, state);
0424         if (ret)
0425             return ret;
0426     }
0427 
0428     return 0;
0429 }
0430 EXPORT_SYMBOL_GPL(typec_mux_set);
0431 
0432 static void typec_mux_release(struct device *dev)
0433 {
0434     kfree(to_typec_mux_dev(dev));
0435 }
0436 
0437 const struct device_type typec_mux_dev_type = {
0438     .name = "mode_switch",
0439     .release = typec_mux_release,
0440 };
0441 
0442 /**
0443  * typec_mux_register - Register Multiplexer routing USB Type-C pins
0444  * @parent: Parent device
0445  * @desc: Multiplexer description
0446  *
0447  * USB Type-C connectors can be used for alternate modes of operation besides
0448  * USB when Accessory/Alternate Modes are supported. With some of those modes,
0449  * the pins on the connector need to be reconfigured. This function registers
0450  * multiplexer switches routing the pins on the connector.
0451  */
0452 struct typec_mux_dev *
0453 typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
0454 {
0455     struct typec_mux_dev *mux_dev;
0456     int ret;
0457 
0458     if (!desc || !desc->set)
0459         return ERR_PTR(-EINVAL);
0460 
0461     mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
0462     if (!mux_dev)
0463         return ERR_PTR(-ENOMEM);
0464 
0465     mux_dev->set = desc->set;
0466 
0467     device_initialize(&mux_dev->dev);
0468     mux_dev->dev.parent = parent;
0469     mux_dev->dev.fwnode = desc->fwnode;
0470     mux_dev->dev.class = &typec_mux_class;
0471     mux_dev->dev.type = &typec_mux_dev_type;
0472     mux_dev->dev.driver_data = desc->drvdata;
0473     ret = dev_set_name(&mux_dev->dev, "%s-mux", desc->name ? desc->name : dev_name(parent));
0474     if (ret) {
0475         put_device(&mux_dev->dev);
0476         return ERR_PTR(ret);
0477     }
0478 
0479     ret = device_add(&mux_dev->dev);
0480     if (ret) {
0481         dev_err(parent, "failed to register mux (%d)\n", ret);
0482         put_device(&mux_dev->dev);
0483         return ERR_PTR(ret);
0484     }
0485 
0486     return mux_dev;
0487 }
0488 EXPORT_SYMBOL_GPL(typec_mux_register);
0489 
0490 /**
0491  * typec_mux_unregister - Unregister Multiplexer Switch
0492  * @mux_dev: USB Type-C Connector Multiplexer/DeMultiplexer
0493  *
0494  * Unregister mux that was registered with typec_mux_register().
0495  */
0496 void typec_mux_unregister(struct typec_mux_dev *mux_dev)
0497 {
0498     if (!IS_ERR_OR_NULL(mux_dev))
0499         device_unregister(&mux_dev->dev);
0500 }
0501 EXPORT_SYMBOL_GPL(typec_mux_unregister);
0502 
0503 void typec_mux_set_drvdata(struct typec_mux_dev *mux_dev, void *data)
0504 {
0505     dev_set_drvdata(&mux_dev->dev, data);
0506 }
0507 EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
0508 
0509 void *typec_mux_get_drvdata(struct typec_mux_dev *mux_dev)
0510 {
0511     return dev_get_drvdata(&mux_dev->dev);
0512 }
0513 EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
0514 
0515 struct class typec_mux_class = {
0516     .name = "typec_mux",
0517     .owner = THIS_MODULE,
0518 };