mux.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. #include <linux/device.h>
  10. #include <linux/list.h>
  11. #include <linux/module.h>
  12. #include <linux/mutex.h>
  13. #include <linux/property.h>
  14. #include <linux/slab.h>
  15. #include <linux/usb/typec_mux.h>
  16. #include "bus.h"
  17. static int name_match(struct device *dev, const void *name)
  18. {
  19. return !strcmp((const char *)name, dev_name(dev));
  20. }
  21. static bool dev_name_ends_with(struct device *dev, const char *suffix)
  22. {
  23. const char *name = dev_name(dev);
  24. const int name_len = strlen(name);
  25. const int suffix_len = strlen(suffix);
  26. if (suffix_len > name_len)
  27. return false;
  28. return strcmp(name + (name_len - suffix_len), suffix) == 0;
  29. }
  30. static int switch_fwnode_match(struct device *dev, const void *fwnode)
  31. {
  32. return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-switch");
  33. }
  34. static void *typec_switch_match(struct device_connection *con, int ep,
  35. void *data)
  36. {
  37. struct device *dev;
  38. if (con->fwnode) {
  39. if (con->id && !fwnode_property_present(con->fwnode, con->id))
  40. return NULL;
  41. dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
  42. switch_fwnode_match);
  43. } else {
  44. dev = class_find_device(&typec_mux_class, NULL,
  45. con->endpoint[ep], name_match);
  46. }
  47. return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
  48. }
  49. /**
  50. * typec_switch_get - Find USB Type-C orientation switch
  51. * @dev: The caller device
  52. *
  53. * Finds a switch linked with @dev. Returns a reference to the switch on
  54. * success, NULL if no matching connection was found, or
  55. * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
  56. * has not been enumerated yet.
  57. */
  58. struct typec_switch *typec_switch_get(struct device *dev)
  59. {
  60. struct typec_switch *sw;
  61. sw = device_connection_find_match(dev, "orientation-switch", NULL,
  62. typec_switch_match);
  63. if (!IS_ERR_OR_NULL(sw))
  64. WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
  65. return sw;
  66. }
  67. EXPORT_SYMBOL_GPL(typec_switch_get);
  68. /**
  69. * typec_put_switch - Release USB Type-C orientation switch
  70. * @sw: USB Type-C orientation switch
  71. *
  72. * Decrement reference count for @sw.
  73. */
  74. void typec_switch_put(struct typec_switch *sw)
  75. {
  76. if (!IS_ERR_OR_NULL(sw)) {
  77. module_put(sw->dev.parent->driver->owner);
  78. put_device(&sw->dev);
  79. }
  80. }
  81. EXPORT_SYMBOL_GPL(typec_switch_put);
  82. static void typec_switch_release(struct device *dev)
  83. {
  84. kfree(to_typec_switch(dev));
  85. }
  86. static const struct device_type typec_switch_dev_type = {
  87. .name = "orientation_switch",
  88. .release = typec_switch_release,
  89. };
  90. /**
  91. * typec_switch_register - Register USB Type-C orientation switch
  92. * @parent: Parent device
  93. * @desc: Orientation switch description
  94. *
  95. * This function registers a switch that can be used for routing the correct
  96. * data pairs depending on the cable plug orientation from the USB Type-C
  97. * connector to the USB controllers. USB Type-C plugs can be inserted
  98. * right-side-up or upside-down.
  99. */
  100. struct typec_switch *
  101. typec_switch_register(struct device *parent,
  102. const struct typec_switch_desc *desc)
  103. {
  104. struct typec_switch *sw;
  105. int ret;
  106. if (!desc || !desc->set)
  107. return ERR_PTR(-EINVAL);
  108. sw = kzalloc(sizeof(*sw), GFP_KERNEL);
  109. if (!sw)
  110. return ERR_PTR(-ENOMEM);
  111. sw->set = desc->set;
  112. device_initialize(&sw->dev);
  113. sw->dev.parent = parent;
  114. sw->dev.fwnode = desc->fwnode;
  115. sw->dev.class = &typec_mux_class;
  116. sw->dev.type = &typec_switch_dev_type;
  117. sw->dev.driver_data = desc->drvdata;
  118. dev_set_name(&sw->dev, "%s-switch", dev_name(parent));
  119. ret = device_add(&sw->dev);
  120. if (ret) {
  121. dev_err(parent, "failed to register switch (%d)\n", ret);
  122. put_device(&sw->dev);
  123. return ERR_PTR(ret);
  124. }
  125. return sw;
  126. }
  127. EXPORT_SYMBOL_GPL(typec_switch_register);
  128. /**
  129. * typec_switch_unregister - Unregister USB Type-C orientation switch
  130. * @sw: USB Type-C orientation switch
  131. *
  132. * Unregister switch that was registered with typec_switch_register().
  133. */
  134. void typec_switch_unregister(struct typec_switch *sw)
  135. {
  136. if (!IS_ERR_OR_NULL(sw))
  137. device_unregister(&sw->dev);
  138. }
  139. EXPORT_SYMBOL_GPL(typec_switch_unregister);
  140. void typec_switch_set_drvdata(struct typec_switch *sw, void *data)
  141. {
  142. dev_set_drvdata(&sw->dev, data);
  143. }
  144. EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
  145. void *typec_switch_get_drvdata(struct typec_switch *sw)
  146. {
  147. return dev_get_drvdata(&sw->dev);
  148. }
  149. EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
  150. /* ------------------------------------------------------------------------- */
  151. static int mux_fwnode_match(struct device *dev, const void *fwnode)
  152. {
  153. return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-mux");
  154. }
  155. static void *typec_mux_match(struct device_connection *con, int ep, void *data)
  156. {
  157. const struct typec_altmode_desc *desc = data;
  158. struct device *dev;
  159. bool match;
  160. int nval;
  161. u16 *val;
  162. int i;
  163. if (!con->fwnode) {
  164. dev = class_find_device(&typec_mux_class, NULL,
  165. con->endpoint[ep], name_match);
  166. return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
  167. }
  168. /*
  169. * Check has the identifier already been "consumed". If it
  170. * has, no need to do any extra connection identification.
  171. */
  172. match = !con->id;
  173. if (match)
  174. goto find_mux;
  175. /* Accessory Mode muxes */
  176. if (!desc) {
  177. match = fwnode_property_present(con->fwnode, "accessory");
  178. if (match)
  179. goto find_mux;
  180. return NULL;
  181. }
  182. /* Alternate Mode muxes */
  183. nval = fwnode_property_count_u16(con->fwnode, "svid");
  184. if (nval <= 0)
  185. return NULL;
  186. val = kcalloc(nval, sizeof(*val), GFP_KERNEL);
  187. if (!val)
  188. return ERR_PTR(-ENOMEM);
  189. nval = fwnode_property_read_u16_array(con->fwnode, "svid", val, nval);
  190. if (nval < 0) {
  191. kfree(val);
  192. return ERR_PTR(nval);
  193. }
  194. for (i = 0; i < nval; i++) {
  195. match = val[i] == desc->svid;
  196. if (match) {
  197. kfree(val);
  198. goto find_mux;
  199. }
  200. }
  201. kfree(val);
  202. return NULL;
  203. find_mux:
  204. dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
  205. mux_fwnode_match);
  206. return dev ? to_typec_mux(dev) : ERR_PTR(-EPROBE_DEFER);
  207. }
  208. /**
  209. * typec_mux_get - Find USB Type-C Multiplexer
  210. * @dev: The caller device
  211. * @desc: Alt Mode description
  212. *
  213. * Finds a mux linked to the caller. This function is primarily meant for the
  214. * Type-C drivers. Returns a reference to the mux on success, NULL if no
  215. * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
  216. * was found but the mux has not been enumerated yet.
  217. */
  218. struct typec_mux *typec_mux_get(struct device *dev,
  219. const struct typec_altmode_desc *desc)
  220. {
  221. struct typec_mux *mux;
  222. mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
  223. typec_mux_match);
  224. if (!IS_ERR_OR_NULL(mux))
  225. WARN_ON(!try_module_get(mux->dev.parent->driver->owner));
  226. return mux;
  227. }
  228. EXPORT_SYMBOL_GPL(typec_mux_get);
  229. /**
  230. * typec_mux_put - Release handle to a Multiplexer
  231. * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
  232. *
  233. * Decrements reference count for @mux.
  234. */
  235. void typec_mux_put(struct typec_mux *mux)
  236. {
  237. if (!IS_ERR_OR_NULL(mux)) {
  238. module_put(mux->dev.parent->driver->owner);
  239. put_device(&mux->dev);
  240. }
  241. }
  242. EXPORT_SYMBOL_GPL(typec_mux_put);
  243. static void typec_mux_release(struct device *dev)
  244. {
  245. kfree(to_typec_mux(dev));
  246. }
  247. static const struct device_type typec_mux_dev_type = {
  248. .name = "mode_switch",
  249. .release = typec_mux_release,
  250. };
  251. /**
  252. * typec_mux_register - Register Multiplexer routing USB Type-C pins
  253. * @parent: Parent device
  254. * @desc: Multiplexer description
  255. *
  256. * USB Type-C connectors can be used for alternate modes of operation besides
  257. * USB when Accessory/Alternate Modes are supported. With some of those modes,
  258. * the pins on the connector need to be reconfigured. This function registers
  259. * multiplexer switches routing the pins on the connector.
  260. */
  261. struct typec_mux *
  262. typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
  263. {
  264. struct typec_mux *mux;
  265. int ret;
  266. if (!desc || !desc->set)
  267. return ERR_PTR(-EINVAL);
  268. mux = kzalloc(sizeof(*mux), GFP_KERNEL);
  269. if (!mux)
  270. return ERR_PTR(-ENOMEM);
  271. mux->set = desc->set;
  272. device_initialize(&mux->dev);
  273. mux->dev.parent = parent;
  274. mux->dev.fwnode = desc->fwnode;
  275. mux->dev.class = &typec_mux_class;
  276. mux->dev.type = &typec_mux_dev_type;
  277. mux->dev.driver_data = desc->drvdata;
  278. dev_set_name(&mux->dev, "%s-mux", dev_name(parent));
  279. ret = device_add(&mux->dev);
  280. if (ret) {
  281. dev_err(parent, "failed to register mux (%d)\n", ret);
  282. put_device(&mux->dev);
  283. return ERR_PTR(ret);
  284. }
  285. return mux;
  286. }
  287. EXPORT_SYMBOL_GPL(typec_mux_register);
  288. /**
  289. * typec_mux_unregister - Unregister Multiplexer Switch
  290. * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
  291. *
  292. * Unregister mux that was registered with typec_mux_register().
  293. */
  294. void typec_mux_unregister(struct typec_mux *mux)
  295. {
  296. if (!IS_ERR_OR_NULL(mux))
  297. device_unregister(&mux->dev);
  298. }
  299. EXPORT_SYMBOL_GPL(typec_mux_unregister);
  300. void typec_mux_set_drvdata(struct typec_mux *mux, void *data)
  301. {
  302. dev_set_drvdata(&mux->dev, data);
  303. }
  304. EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
  305. void *typec_mux_get_drvdata(struct typec_mux *mux)
  306. {
  307. return dev_get_drvdata(&mux->dev);
  308. }
  309. EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
  310. struct class typec_mux_class = {
  311. .name = "typec_mux",
  312. .owner = THIS_MODULE,
  313. };