diff options
| author | Heikki Krogerus <heikki.krogerus@linux.intel.com> | 2018-06-27 11:19:50 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-02 11:42:36 -0400 |
| commit | 8a37d87d72f0c69f837229c04d2fcd7117ea57e7 (patch) | |
| tree | 43cf3111a964678d08fe6638e4459e1c2db50b55 | |
| parent | 4ab8c18d4d67321cc7b660559de17511d4fc0237 (diff) | |
usb: typec: Bus type for alternate modes
Introducing a simple bus for the alternate modes. Bus allows
binding drivers to the discovered alternate modes the
partners support.
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Tested-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | Documentation/ABI/obsolete/sysfs-class-typec | 48 | ||||
| -rw-r--r-- | Documentation/ABI/testing/sysfs-bus-typec | 51 | ||||
| -rw-r--r-- | Documentation/ABI/testing/sysfs-class-typec | 62 | ||||
| -rw-r--r-- | Documentation/driver-api/usb/typec_bus.rst | 136 | ||||
| -rw-r--r-- | MAINTAINERS | 11 | ||||
| -rw-r--r-- | drivers/usb/typec/Makefile | 2 | ||||
| -rw-r--r-- | drivers/usb/typec/bus.c | 401 | ||||
| -rw-r--r-- | drivers/usb/typec/bus.h | 38 | ||||
| -rw-r--r-- | drivers/usb/typec/class.c | 366 | ||||
| -rw-r--r-- | include/linux/mod_devicetable.h | 15 | ||||
| -rw-r--r-- | include/linux/usb/typec.h | 14 | ||||
| -rw-r--r-- | include/linux/usb/typec_altmode.h | 160 | ||||
| -rw-r--r-- | scripts/mod/devicetable-offsets.c | 4 | ||||
| -rw-r--r-- | scripts/mod/file2alias.c | 13 |
14 files changed, 1174 insertions, 147 deletions
diff --git a/Documentation/ABI/obsolete/sysfs-class-typec b/Documentation/ABI/obsolete/sysfs-class-typec new file mode 100644 index 000000000000..32623514ee87 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-class-typec | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | These files are deprecated and will be removed. The same files are available | ||
| 2 | under /sys/bus/typec (see Documentation/ABI/testing/sysfs-bus-typec). | ||
| 3 | |||
| 4 | What: /sys/class/typec/<port|partner|cable>/<dev>/svid | ||
| 5 | Date: April 2017 | ||
| 6 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 7 | Description: | ||
| 8 | The SVID (Standard or Vendor ID) assigned by USB-IF for this | ||
| 9 | alternate mode. | ||
| 10 | |||
| 11 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/ | ||
| 12 | Date: April 2017 | ||
| 13 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 14 | Description: | ||
| 15 | Every supported mode will have its own directory. The name of | ||
| 16 | a mode will be "mode<index>" (for example mode1), where <index> | ||
| 17 | is the actual index to the mode VDO returned by Discover Modes | ||
| 18 | USB power delivery command. | ||
| 19 | |||
| 20 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/description | ||
| 21 | Date: April 2017 | ||
| 22 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 23 | Description: | ||
| 24 | Shows description of the mode. The description is optional for | ||
| 25 | the drivers, just like with the Billboard Devices. | ||
| 26 | |||
| 27 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/vdo | ||
| 28 | Date: April 2017 | ||
| 29 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 30 | Description: | ||
| 31 | Shows the VDO in hexadecimal returned by Discover Modes command | ||
| 32 | for this mode. | ||
| 33 | |||
| 34 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/active | ||
| 35 | Date: April 2017 | ||
| 36 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 37 | Description: | ||
| 38 | Shows if the mode is active or not. The attribute can be used | ||
| 39 | for entering/exiting the mode with partners and cable plugs, and | ||
| 40 | with the port alternate modes it can be used for disabling | ||
| 41 | support for specific alternate modes. Entering/exiting modes is | ||
| 42 | supported as synchronous operation so write(2) to the attribute | ||
| 43 | does not return until the enter/exit mode operation has | ||
| 44 | finished. The attribute is notified when the mode is | ||
| 45 | entered/exited so poll(2) on the attribute wakes up. | ||
| 46 | Entering/exiting a mode will also generate uevent KOBJ_CHANGE. | ||
| 47 | |||
| 48 | Valid values: yes, no | ||
diff --git a/Documentation/ABI/testing/sysfs-bus-typec b/Documentation/ABI/testing/sysfs-bus-typec new file mode 100644 index 000000000000..205d9c91e2e1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-typec | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | What: /sys/bus/typec/devices/.../active | ||
| 2 | Date: July 2018 | ||
| 3 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 4 | Description: | ||
| 5 | Shows if the mode is active or not. The attribute can be used | ||
| 6 | for entering/exiting the mode. Entering/exiting modes is | ||
| 7 | supported as synchronous operation so write(2) to the attribute | ||
| 8 | does not return until the enter/exit mode operation has | ||
| 9 | finished. The attribute is notified when the mode is | ||
| 10 | entered/exited so poll(2) on the attribute wakes up. | ||
| 11 | Entering/exiting a mode will also generate uevent KOBJ_CHANGE. | ||
| 12 | |||
| 13 | Valid values are boolean. | ||
| 14 | |||
| 15 | What: /sys/bus/typec/devices/.../description | ||
| 16 | Date: July 2018 | ||
| 17 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 18 | Description: | ||
| 19 | Shows description of the mode. The description is optional for | ||
| 20 | the drivers, just like with the Billboard Devices. | ||
| 21 | |||
| 22 | What: /sys/bus/typec/devices/.../mode | ||
| 23 | Date: July 2018 | ||
| 24 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 25 | Description: | ||
| 26 | The index number of the mode returned by Discover Modes USB | ||
| 27 | Power Delivery command. Depending on the alternate mode, the | ||
| 28 | mode index may be significant. | ||
| 29 | |||
| 30 | With some alternate modes (SVIDs), the mode index is assigned | ||
| 31 | for specific functionality in the specification for that | ||
| 32 | alternate mode. | ||
| 33 | |||
| 34 | With other alternate modes, the mode index values are not | ||
| 35 | assigned, and can not be therefore used for identification. When | ||
| 36 | the mode index is not assigned, identifying the alternate mode | ||
| 37 | must be done with either mode VDO or the description. | ||
| 38 | |||
| 39 | What: /sys/bus/typec/devices/.../svid | ||
| 40 | Date: July 2018 | ||
| 41 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 42 | Description: | ||
| 43 | The Standard or Vendor ID (SVID) assigned by USB-IF for this | ||
| 44 | alternate mode. | ||
| 45 | |||
| 46 | What: /sys/bus/typec/devices/.../vdo | ||
| 47 | Date: July 2018 | ||
| 48 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 49 | Description: | ||
| 50 | Shows the VDO in hexadecimal returned by Discover Modes command | ||
| 51 | for this mode. | ||
diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 5be552e255e9..d7647b258c3c 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec | |||
| @@ -222,70 +222,12 @@ Description: | |||
| 222 | available. The value can be polled. | 222 | available. The value can be polled. |
| 223 | 223 | ||
| 224 | 224 | ||
| 225 | Alternate Mode devices. | 225 | USB Type-C port alternate mode devices. |
| 226 | 226 | ||
| 227 | The alternate modes will have Standard or Vendor ID (SVID) assigned by USB-IF. | 227 | What: /sys/class/typec/<port>/<alt mode>/supported_roles |
| 228 | The ports, partners and cable plugs can have alternate modes. A supported SVID | ||
| 229 | will consist of a set of modes. Every SVID a port/partner/plug supports will | ||
| 230 | have a device created for it, and every supported mode for a supported SVID will | ||
| 231 | have its own directory under that device. Below <dev> refers to the device for | ||
| 232 | the alternate mode. | ||
| 233 | |||
| 234 | What: /sys/class/typec/<port|partner|cable>/<dev>/svid | ||
| 235 | Date: April 2017 | ||
| 236 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 237 | Description: | ||
| 238 | The SVID (Standard or Vendor ID) assigned by USB-IF for this | ||
| 239 | alternate mode. | ||
| 240 | |||
| 241 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/ | ||
| 242 | Date: April 2017 | ||
| 243 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 244 | Description: | ||
| 245 | Every supported mode will have its own directory. The name of | ||
| 246 | a mode will be "mode<index>" (for example mode1), where <index> | ||
| 247 | is the actual index to the mode VDO returned by Discover Modes | ||
| 248 | USB power delivery command. | ||
| 249 | |||
| 250 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/description | ||
| 251 | Date: April 2017 | ||
| 252 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 253 | Description: | ||
| 254 | Shows description of the mode. The description is optional for | ||
| 255 | the drivers, just like with the Billboard Devices. | ||
| 256 | |||
| 257 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/vdo | ||
| 258 | Date: April 2017 | ||
| 259 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 260 | Description: | ||
| 261 | Shows the VDO in hexadecimal returned by Discover Modes command | ||
| 262 | for this mode. | ||
| 263 | |||
| 264 | What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/active | ||
| 265 | Date: April 2017 | ||
| 266 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 267 | Description: | ||
| 268 | Shows if the mode is active or not. The attribute can be used | ||
| 269 | for entering/exiting the mode with partners and cable plugs, and | ||
| 270 | with the port alternate modes it can be used for disabling | ||
| 271 | support for specific alternate modes. Entering/exiting modes is | ||
| 272 | supported as synchronous operation so write(2) to the attribute | ||
| 273 | does not return until the enter/exit mode operation has | ||
| 274 | finished. The attribute is notified when the mode is | ||
| 275 | entered/exited so poll(2) on the attribute wakes up. | ||
| 276 | Entering/exiting a mode will also generate uevent KOBJ_CHANGE. | ||
| 277 | |||
| 278 | Valid values: yes, no | ||
| 279 | |||
| 280 | What: /sys/class/typec/<port>/<dev>/mode<index>/supported_roles | ||
| 281 | Date: April 2017 | 228 | Date: April 2017 |
| 282 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | 229 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| 283 | Description: | 230 | Description: |
| 284 | Space separated list of the supported roles. | 231 | Space separated list of the supported roles. |
| 285 | 232 | ||
| 286 | This attribute is available for the devices describing the | ||
| 287 | alternate modes a port supports, and it will not be exposed with | ||
| 288 | the devices presenting the alternate modes the partners or cable | ||
| 289 | plugs support. | ||
| 290 | |||
| 291 | Valid values: source, sink | 233 | Valid values: source, sink |
diff --git a/Documentation/driver-api/usb/typec_bus.rst b/Documentation/driver-api/usb/typec_bus.rst new file mode 100644 index 000000000000..d5eec1715b5b --- /dev/null +++ b/Documentation/driver-api/usb/typec_bus.rst | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | |||
| 2 | API for USB Type-C Alternate Mode drivers | ||
| 3 | ========================================= | ||
| 4 | |||
| 5 | Introduction | ||
| 6 | ------------ | ||
| 7 | |||
| 8 | Alternate modes require communication with the partner using Vendor Defined | ||
| 9 | Messages (VDM) as defined in USB Type-C and USB Power Delivery Specifications. | ||
| 10 | The communication is SVID (Standard or Vendor ID) specific, i.e. specific for | ||
| 11 | every alternate mode, so every alternate mode will need a custom driver. | ||
| 12 | |||
| 13 | USB Type-C bus allows binding a driver to the discovered partner alternate | ||
| 14 | modes by using the SVID and the mode number. | ||
| 15 | |||
| 16 | USB Type-C Connector Class provides a device for every alternate mode a port | ||
| 17 | supports, and separate device for every alternate mode the partner supports. | ||
| 18 | The drivers for the alternate modes are bound to the partner alternate mode | ||
| 19 | devices, and the port alternate mode devices must be handled by the port | ||
| 20 | drivers. | ||
| 21 | |||
| 22 | When a new partner alternate mode device is registered, it is linked to the | ||
| 23 | alternate mode device of the port that the partner is attached to, that has | ||
| 24 | matching SVID and mode. Communication between the port driver and alternate mode | ||
| 25 | driver will happen using the same API. | ||
| 26 | |||
| 27 | The port alternate mode devices are used as a proxy between the partner and the | ||
| 28 | alternate mode drivers, so the port drivers are only expected to pass the SVID | ||
| 29 | specific commands from the alternate mode drivers to the partner, and from the | ||
| 30 | partners to the alternate mode drivers. No direct SVID specific communication is | ||
| 31 | needed from the port drivers, but the port drivers need to provide the operation | ||
| 32 | callbacks for the port alternate mode devices, just like the alternate mode | ||
| 33 | drivers need to provide them for the partner alternate mode devices. | ||
| 34 | |||
| 35 | Usage: | ||
| 36 | ------ | ||
| 37 | |||
| 38 | General | ||
| 39 | ~~~~~~~ | ||
| 40 | |||
| 41 | By default, the alternate mode drivers are responsible for entering the mode. | ||
| 42 | It is also possible to leave the decision about entering the mode to the user | ||
| 43 | space (See Documentation/ABI/testing/sysfs-class-typec). Port drivers should not | ||
| 44 | enter any modes on their own. | ||
| 45 | |||
| 46 | ``->vdm`` is the most important callback in the operation callbacks vector. It | ||
| 47 | will be used to deliver all the SVID specific commands from the partner to the | ||
| 48 | alternate mode driver, and vice versa in case of port drivers. The drivers send | ||
| 49 | the SVID specific commands to each other using :c:func:`typec_altmode_vmd()`. | ||
| 50 | |||
| 51 | If the communication with the partner using the SVID specific commands results | ||
| 52 | in need to reconfigure the pins on the connector, the alternate mode driver | ||
| 53 | needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver | ||
| 54 | passes the negotiated SVID specific pin configuration value to the function as | ||
| 55 | parameter. The bus driver will then configure the mux behind the connector using | ||
| 56 | that value as the state value for the mux, and also call blocking notification | ||
| 57 | chain to notify the external drivers about the state of the connector that need | ||
| 58 | to know it. | ||
| 59 | |||
| 60 | NOTE: The SVID specific pin configuration values must always start from | ||
| 61 | ``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for | ||
| 62 | the connector: ``TYPEC_STATE_USB`` and ``TYPEC_STATE_SAFE``. These values are | ||
| 63 | reserved by the bus as the first possible values for the state. When the | ||
| 64 | alternate mode is entered, the bus will put the connector into | ||
| 65 | ``TYPEC_STATE_SAFE`` before sending Enter or Exit Mode command as defined in USB | ||
| 66 | Type-C Specification, and also put the connector back to ``TYPEC_STATE_USB`` | ||
| 67 | after the mode has been exited. | ||
| 68 | |||
| 69 | An example of working definitions for SVID specific pin configurations would | ||
| 70 | look like this: | ||
| 71 | |||
| 72 | enum { | ||
| 73 | ALTMODEX_CONF_A = TYPEC_STATE_MODAL, | ||
| 74 | ALTMODEX_CONF_B, | ||
| 75 | ... | ||
| 76 | }; | ||
| 77 | |||
| 78 | Helper macro ``TYPEC_MODAL_STATE()`` can also be used: | ||
| 79 | |||
| 80 | #define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0); | ||
| 81 | #define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1); | ||
| 82 | |||
| 83 | Notification chain | ||
| 84 | ~~~~~~~~~~~~~~~~~~ | ||
| 85 | |||
| 86 | The drivers for the components that the alternate modes are designed for need to | ||
| 87 | get details regarding the results of the negotiation with the partner, and the | ||
| 88 | pin configuration of the connector. In case of DisplayPort alternate mode for | ||
| 89 | example, the GPU drivers will need to know those details. In case of | ||
| 90 | Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and | ||
| 91 | so on. | ||
| 92 | |||
| 93 | The notification chain is designed for this purpose. The drivers can register | ||
| 94 | notifiers with :c:func:`typec_altmode_register_notifier()`. | ||
| 95 | |||
| 96 | Cable plug alternate modes | ||
| 97 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 98 | |||
| 99 | The alternate mode drivers are not bound to cable plug alternate mode devices, | ||
| 100 | only to the partner alternate mode devices. If the alternate mode supports, or | ||
| 101 | requires, a cable that responds to SOP Prime, and optionally SOP Double Prime | ||
| 102 | messages, the driver for that alternate mode must request handle to the cable | ||
| 103 | plug alternate modes using :c:func:`typec_altmode_get_plug()`, and take over | ||
| 104 | their control. | ||
| 105 | |||
| 106 | Driver API | ||
| 107 | ---------- | ||
| 108 | |||
| 109 | Alternate mode driver registering/unregistering | ||
| 110 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 111 | |||
| 112 | .. kernel-doc:: drivers/usb/typec/bus.c | ||
| 113 | :functions: typec_altmode_register_driver typec_altmode_unregister_driver | ||
| 114 | |||
| 115 | Alternate mode driver operations | ||
| 116 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 117 | |||
| 118 | .. kernel-doc:: drivers/usb/typec/bus.c | ||
| 119 | :functions: typec_altmode_enter typec_altmode_exit typec_altmode_attention typec_altmode_vdm typec_altmode_notify | ||
| 120 | |||
| 121 | API for the port drivers | ||
| 122 | ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 123 | |||
| 124 | .. kernel-doc:: drivers/usb/typec/bus.c | ||
| 125 | :functions: typec_match_altmode | ||
| 126 | |||
| 127 | Cable Plug operations | ||
| 128 | ~~~~~~~~~~~~~~~~~~~~~ | ||
| 129 | |||
| 130 | .. kernel-doc:: drivers/usb/typec/bus.c | ||
| 131 | :functions: typec_altmode_get_plug typec_altmode_put_plug | ||
| 132 | |||
| 133 | Notifications | ||
| 134 | ~~~~~~~~~~~~~ | ||
| 135 | .. kernel-doc:: drivers/usb/typec/class.c | ||
| 136 | :functions: typec_altmode_register_notifier typec_altmode_unregister_notifier | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 07d1576fc766..f35f39f2072e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -14955,7 +14955,7 @@ L: linux-usb@vger.kernel.org | |||
| 14955 | S: Maintained | 14955 | S: Maintained |
| 14956 | F: drivers/usb/typec/mux/pi3usb30532.c | 14956 | F: drivers/usb/typec/mux/pi3usb30532.c |
| 14957 | 14957 | ||
| 14958 | USB TYPEC SUBSYSTEM | 14958 | USB TYPEC CLASS |
| 14959 | M: Heikki Krogerus <heikki.krogerus@linux.intel.com> | 14959 | M: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| 14960 | L: linux-usb@vger.kernel.org | 14960 | L: linux-usb@vger.kernel.org |
| 14961 | S: Maintained | 14961 | S: Maintained |
| @@ -14964,6 +14964,15 @@ F: Documentation/driver-api/usb/typec.rst | |||
| 14964 | F: drivers/usb/typec/ | 14964 | F: drivers/usb/typec/ |
| 14965 | F: include/linux/usb/typec.h | 14965 | F: include/linux/usb/typec.h |
| 14966 | 14966 | ||
| 14967 | USB TYPEC BUS FOR ALTERNATE MODES | ||
| 14968 | M: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 14969 | L: linux-usb@vger.kernel.org | ||
| 14970 | S: Maintained | ||
| 14971 | F: Documentation/ABI/testing/sysfs-bus-typec | ||
| 14972 | F: Documentation/driver-api/usb/typec_bus.rst | ||
| 14973 | F: drivers/usb/typec/altmodes/ | ||
| 14974 | F: include/linux/usb/typec_altmode.h | ||
| 14975 | |||
| 14967 | USB UHCI DRIVER | 14976 | USB UHCI DRIVER |
| 14968 | M: Alan Stern <stern@rowland.harvard.edu> | 14977 | M: Alan Stern <stern@rowland.harvard.edu> |
| 14969 | L: linux-usb@vger.kernel.org | 14978 | L: linux-usb@vger.kernel.org |
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 46f86ee134a2..335ee06748fc 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | obj-$(CONFIG_TYPEC) += typec.o | 2 | obj-$(CONFIG_TYPEC) += typec.o |
| 3 | typec-y := class.o mux.o | 3 | typec-y := class.o mux.o bus.o |
| 4 | obj-$(CONFIG_TYPEC_TCPM) += tcpm.o | 4 | obj-$(CONFIG_TYPEC_TCPM) += tcpm.o |
| 5 | obj-y += fusb302/ | 5 | obj-y += fusb302/ |
| 6 | obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o | 6 | obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o |
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c new file mode 100644 index 000000000000..999d7904172a --- /dev/null +++ b/drivers/usb/typec/bus.c | |||
| @@ -0,0 +1,401 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /** | ||
| 3 | * Bus for USB Type-C Alternate Modes | ||
| 4 | * | ||
| 5 | * Copyright (C) 2018 Intel Corporation | ||
| 6 | * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/usb/pd_vdo.h> | ||
| 10 | |||
| 11 | #include "bus.h" | ||
| 12 | |||
| 13 | static inline int typec_altmode_set_mux(struct altmode *alt, u8 state) | ||
| 14 | { | ||
| 15 | return alt->mux ? alt->mux->set(alt->mux, state) : 0; | ||
| 16 | } | ||
| 17 | |||
| 18 | static int typec_altmode_set_state(struct typec_altmode *adev, int state) | ||
| 19 | { | ||
| 20 | bool is_port = is_typec_port(adev->dev.parent); | ||
| 21 | struct altmode *port_altmode; | ||
| 22 | int ret; | ||
| 23 | |||
| 24 | port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; | ||
| 25 | |||
| 26 | ret = typec_altmode_set_mux(port_altmode, state); | ||
| 27 | if (ret) | ||
| 28 | return ret; | ||
| 29 | |||
| 30 | blocking_notifier_call_chain(&port_altmode->nh, state, NULL); | ||
| 31 | |||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | /* -------------------------------------------------------------------------- */ | ||
| 36 | /* Common API */ | ||
| 37 | |||
| 38 | /** | ||
| 39 | * typec_altmode_notify - Communication between the OS and alternate mode driver | ||
| 40 | * @adev: Handle to the alternate mode | ||
| 41 | * @conf: Alternate mode specific configuration value | ||
| 42 | * @data: Alternate mode specific data | ||
| 43 | * | ||
| 44 | * The primary purpose for this function is to allow the alternate mode drivers | ||
| 45 | * to tell which pin configuration has been negotiated with the partner. That | ||
| 46 | * information will then be used for example to configure the muxes. | ||
| 47 | * Communication to the other direction is also possible, and low level device | ||
| 48 | * drivers can also send notifications to the alternate mode drivers. The actual | ||
| 49 | * communication will be specific for every SVID. | ||
| 50 | */ | ||
| 51 | int typec_altmode_notify(struct typec_altmode *adev, | ||
| 52 | unsigned long conf, void *data) | ||
| 53 | { | ||
| 54 | bool is_port = is_typec_port(adev->dev.parent); | ||
| 55 | struct altmode *altmode; | ||
| 56 | struct altmode *partner; | ||
| 57 | int ret; | ||
| 58 | |||
| 59 | if (!adev) | ||
| 60 | return 0; | ||
| 61 | |||
| 62 | altmode = to_altmode(adev); | ||
| 63 | |||
| 64 | if (!altmode->partner) | ||
| 65 | return -ENODEV; | ||
| 66 | |||
| 67 | partner = altmode->partner; | ||
| 68 | |||
| 69 | ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf); | ||
| 70 | if (ret) | ||
| 71 | return ret; | ||
| 72 | |||
| 73 | blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh, | ||
| 74 | conf, data); | ||
| 75 | |||
| 76 | if (partner->adev.ops && partner->adev.ops->notify) | ||
| 77 | return partner->adev.ops->notify(&partner->adev, conf, data); | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | EXPORT_SYMBOL_GPL(typec_altmode_notify); | ||
| 82 | |||
| 83 | /** | ||
| 84 | * typec_altmode_enter - Enter Mode | ||
| 85 | * @adev: The alternate mode | ||
| 86 | * | ||
| 87 | * The alternate mode drivers use this function to enter mode. The port drivers | ||
| 88 | * use this to inform the alternate mode drivers that the partner has initiated | ||
| 89 | * Enter Mode command. | ||
| 90 | */ | ||
| 91 | int typec_altmode_enter(struct typec_altmode *adev) | ||
| 92 | { | ||
| 93 | struct altmode *partner = to_altmode(adev)->partner; | ||
| 94 | struct typec_altmode *pdev = &partner->adev; | ||
| 95 | int ret; | ||
| 96 | |||
| 97 | if (!adev || adev->active) | ||
| 98 | return 0; | ||
| 99 | |||
| 100 | if (!pdev->ops || !pdev->ops->enter) | ||
| 101 | return -EOPNOTSUPP; | ||
| 102 | |||
| 103 | /* Moving to USB Safe State */ | ||
| 104 | ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); | ||
| 105 | if (ret) | ||
| 106 | return ret; | ||
| 107 | |||
| 108 | /* Enter Mode */ | ||
| 109 | return pdev->ops->enter(pdev); | ||
| 110 | } | ||
| 111 | EXPORT_SYMBOL_GPL(typec_altmode_enter); | ||
| 112 | |||
| 113 | /** | ||
| 114 | * typec_altmode_exit - Exit Mode | ||
| 115 | * @adev: The alternate mode | ||
| 116 | * | ||
| 117 | * The partner of @adev has initiated Exit Mode command. | ||
| 118 | */ | ||
| 119 | int typec_altmode_exit(struct typec_altmode *adev) | ||
| 120 | { | ||
| 121 | struct altmode *partner = to_altmode(adev)->partner; | ||
| 122 | struct typec_altmode *pdev = &partner->adev; | ||
| 123 | int ret; | ||
| 124 | |||
| 125 | if (!adev || !adev->active) | ||
| 126 | return 0; | ||
| 127 | |||
| 128 | if (!pdev->ops || !pdev->ops->enter) | ||
| 129 | return -EOPNOTSUPP; | ||
| 130 | |||
| 131 | /* Moving to USB Safe State */ | ||
| 132 | ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); | ||
| 133 | if (ret) | ||
| 134 | return ret; | ||
| 135 | |||
| 136 | /* Exit Mode command */ | ||
| 137 | return pdev->ops->exit(pdev); | ||
| 138 | } | ||
| 139 | EXPORT_SYMBOL_GPL(typec_altmode_exit); | ||
| 140 | |||
| 141 | /** | ||
| 142 | * typec_altmode_attention - Attention command | ||
| 143 | * @adev: The alternate mode | ||
| 144 | * @vdo: VDO for the Attention command | ||
| 145 | * | ||
| 146 | * Notifies the partner of @adev about Attention command. | ||
| 147 | */ | ||
| 148 | void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) | ||
| 149 | { | ||
| 150 | struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; | ||
| 151 | |||
| 152 | if (pdev->ops && pdev->ops->attention) | ||
| 153 | pdev->ops->attention(pdev, vdo); | ||
| 154 | } | ||
| 155 | EXPORT_SYMBOL_GPL(typec_altmode_attention); | ||
| 156 | |||
| 157 | /** | ||
| 158 | * typec_altmode_vdm - Send Vendor Defined Messages (VDM) to the partner | ||
| 159 | * @adev: Alternate mode handle | ||
| 160 | * @header: VDM Header | ||
| 161 | * @vdo: Array of Vendor Defined Data Objects | ||
| 162 | * @count: Number of Data Objects | ||
| 163 | * | ||
| 164 | * The alternate mode drivers use this function for SVID specific communication | ||
| 165 | * with the partner. The port drivers use it to deliver the Structured VDMs | ||
| 166 | * received from the partners to the alternate mode drivers. | ||
| 167 | */ | ||
| 168 | int typec_altmode_vdm(struct typec_altmode *adev, | ||
| 169 | const u32 header, const u32 *vdo, int count) | ||
| 170 | { | ||
| 171 | struct typec_altmode *pdev; | ||
| 172 | struct altmode *altmode; | ||
| 173 | |||
| 174 | if (!adev) | ||
| 175 | return 0; | ||
| 176 | |||
| 177 | altmode = to_altmode(adev); | ||
| 178 | |||
| 179 | if (!altmode->partner) | ||
| 180 | return -ENODEV; | ||
| 181 | |||
| 182 | pdev = &altmode->partner->adev; | ||
| 183 | |||
| 184 | if (!pdev->ops || !pdev->ops->vdm) | ||
| 185 | return -EOPNOTSUPP; | ||
| 186 | |||
| 187 | return pdev->ops->vdm(pdev, header, vdo, count); | ||
| 188 | } | ||
| 189 | EXPORT_SYMBOL_GPL(typec_altmode_vdm); | ||
| 190 | |||
| 191 | const struct typec_altmode * | ||
| 192 | typec_altmode_get_partner(struct typec_altmode *adev) | ||
| 193 | { | ||
| 194 | return &to_altmode(adev)->partner->adev; | ||
| 195 | } | ||
| 196 | EXPORT_SYMBOL_GPL(typec_altmode_get_partner); | ||
| 197 | |||
| 198 | /* -------------------------------------------------------------------------- */ | ||
| 199 | /* API for the alternate mode drivers */ | ||
| 200 | |||
| 201 | /** | ||
| 202 | * typec_altmode_get_plug - Find cable plug alternate mode | ||
| 203 | * @adev: Handle to partner alternate mode | ||
| 204 | * @index: Cable plug index | ||
| 205 | * | ||
| 206 | * Increment reference count for cable plug alternate mode device. Returns | ||
| 207 | * handle to the cable plug alternate mode, or NULL if none is found. | ||
| 208 | */ | ||
| 209 | struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *adev, | ||
| 210 | enum typec_plug_index index) | ||
| 211 | { | ||
| 212 | struct altmode *port = to_altmode(adev)->partner; | ||
| 213 | |||
| 214 | if (port->plug[index]) { | ||
| 215 | get_device(&port->plug[index]->adev.dev); | ||
| 216 | return &port->plug[index]->adev; | ||
| 217 | } | ||
| 218 | |||
| 219 | return NULL; | ||
| 220 | } | ||
| 221 | EXPORT_SYMBOL_GPL(typec_altmode_get_plug); | ||
| 222 | |||
| 223 | /** | ||
| 224 | * typec_altmode_put_plug - Decrement cable plug alternate mode reference count | ||
| 225 | * @plug: Handle to the cable plug alternate mode | ||
| 226 | */ | ||
| 227 | void typec_altmode_put_plug(struct typec_altmode *plug) | ||
| 228 | { | ||
| 229 | if (plug) | ||
| 230 | put_device(&plug->dev); | ||
| 231 | } | ||
| 232 | EXPORT_SYMBOL_GPL(typec_altmode_put_plug); | ||
| 233 | |||
| 234 | int __typec_altmode_register_driver(struct typec_altmode_driver *drv, | ||
| 235 | struct module *module) | ||
| 236 | { | ||
| 237 | if (!drv->probe) | ||
| 238 | return -EINVAL; | ||
| 239 | |||
| 240 | drv->driver.owner = module; | ||
| 241 | drv->driver.bus = &typec_bus; | ||
| 242 | |||
| 243 | return driver_register(&drv->driver); | ||
| 244 | } | ||
| 245 | EXPORT_SYMBOL_GPL(__typec_altmode_register_driver); | ||
| 246 | |||
| 247 | void typec_altmode_unregister_driver(struct typec_altmode_driver *drv) | ||
| 248 | { | ||
| 249 | driver_unregister(&drv->driver); | ||
| 250 | } | ||
| 251 | EXPORT_SYMBOL_GPL(typec_altmode_unregister_driver); | ||
| 252 | |||
| 253 | /* -------------------------------------------------------------------------- */ | ||
| 254 | /* API for the port drivers */ | ||
| 255 | |||
| 256 | /** | ||
| 257 | * typec_match_altmode - Match SVID to an array of alternate modes | ||
| 258 | * @altmodes: Array of alternate modes | ||
| 259 | * @n: Number of elements in the array, or -1 for NULL termiated arrays | ||
| 260 | * @svid: Standard or Vendor ID to match with | ||
| 261 | * | ||
| 262 | * Return pointer to an alternate mode with SVID mathing @svid, or NULL when no | ||
| 263 | * match is found. | ||
| 264 | */ | ||
| 265 | struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, | ||
| 266 | size_t n, u16 svid, u8 mode) | ||
| 267 | { | ||
| 268 | int i; | ||
| 269 | |||
| 270 | for (i = 0; i < n; i++) { | ||
| 271 | if (!altmodes[i]) | ||
| 272 | break; | ||
| 273 | if (altmodes[i]->svid == svid && altmodes[i]->mode == mode) | ||
| 274 | return altmodes[i]; | ||
| 275 | } | ||
| 276 | |||
| 277 | return NULL; | ||
| 278 | } | ||
| 279 | EXPORT_SYMBOL_GPL(typec_match_altmode); | ||
| 280 | |||
| 281 | /* -------------------------------------------------------------------------- */ | ||
| 282 | |||
| 283 | static ssize_t | ||
| 284 | description_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 285 | { | ||
| 286 | struct typec_altmode *alt = to_typec_altmode(dev); | ||
| 287 | |||
| 288 | return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); | ||
| 289 | } | ||
| 290 | static DEVICE_ATTR_RO(description); | ||
| 291 | |||
| 292 | static struct attribute *typec_attrs[] = { | ||
| 293 | &dev_attr_description.attr, | ||
| 294 | NULL | ||
| 295 | }; | ||
| 296 | ATTRIBUTE_GROUPS(typec); | ||
| 297 | |||
| 298 | static int typec_match(struct device *dev, struct device_driver *driver) | ||
| 299 | { | ||
| 300 | struct typec_altmode_driver *drv = to_altmode_driver(driver); | ||
| 301 | struct typec_altmode *altmode = to_typec_altmode(dev); | ||
| 302 | const struct typec_device_id *id; | ||
| 303 | |||
| 304 | for (id = drv->id_table; id->svid; id++) | ||
| 305 | if (id->svid == altmode->svid && | ||
| 306 | (id->mode == TYPEC_ANY_MODE || id->mode == altmode->mode)) | ||
| 307 | return 1; | ||
| 308 | return 0; | ||
| 309 | } | ||
| 310 | |||
| 311 | static int typec_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
| 312 | { | ||
| 313 | struct typec_altmode *altmode = to_typec_altmode(dev); | ||
| 314 | |||
| 315 | if (add_uevent_var(env, "SVID=%04X", altmode->svid)) | ||
| 316 | return -ENOMEM; | ||
| 317 | |||
| 318 | if (add_uevent_var(env, "MODE=%u", altmode->mode)) | ||
| 319 | return -ENOMEM; | ||
| 320 | |||
| 321 | return add_uevent_var(env, "MODALIAS=typec:id%04Xm%02X", | ||
| 322 | altmode->svid, altmode->mode); | ||
| 323 | } | ||
| 324 | |||
| 325 | static int typec_altmode_create_links(struct altmode *alt) | ||
| 326 | { | ||
| 327 | struct device *port_dev = &alt->partner->adev.dev; | ||
| 328 | struct device *dev = &alt->adev.dev; | ||
| 329 | int err; | ||
| 330 | |||
| 331 | err = sysfs_create_link(&dev->kobj, &port_dev->kobj, "port"); | ||
| 332 | if (err) | ||
| 333 | return err; | ||
| 334 | |||
| 335 | err = sysfs_create_link(&port_dev->kobj, &dev->kobj, "partner"); | ||
| 336 | if (err) | ||
| 337 | sysfs_remove_link(&dev->kobj, "port"); | ||
| 338 | |||
| 339 | return err; | ||
| 340 | } | ||
| 341 | |||
| 342 | static void typec_altmode_remove_links(struct altmode *alt) | ||
| 343 | { | ||
| 344 | sysfs_remove_link(&alt->partner->adev.dev.kobj, "partner"); | ||
| 345 | sysfs_remove_link(&alt->adev.dev.kobj, "port"); | ||
| 346 | } | ||
| 347 | |||
| 348 | static int typec_probe(struct device *dev) | ||
| 349 | { | ||
| 350 | struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); | ||
| 351 | struct typec_altmode *adev = to_typec_altmode(dev); | ||
| 352 | struct altmode *altmode = to_altmode(adev); | ||
| 353 | int ret; | ||
| 354 | |||
| 355 | /* Fail if the port does not support the alternate mode */ | ||
| 356 | if (!altmode->partner) | ||
| 357 | return -ENODEV; | ||
| 358 | |||
| 359 | ret = typec_altmode_create_links(altmode); | ||
| 360 | if (ret) { | ||
| 361 | dev_warn(dev, "failed to create symlinks\n"); | ||
| 362 | return ret; | ||
| 363 | } | ||
| 364 | |||
| 365 | ret = drv->probe(adev); | ||
| 366 | if (ret) | ||
| 367 | typec_altmode_remove_links(altmode); | ||
| 368 | |||
| 369 | return ret; | ||
| 370 | } | ||
| 371 | |||
| 372 | static int typec_remove(struct device *dev) | ||
| 373 | { | ||
| 374 | struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); | ||
| 375 | struct typec_altmode *adev = to_typec_altmode(dev); | ||
| 376 | struct altmode *altmode = to_altmode(adev); | ||
| 377 | |||
| 378 | typec_altmode_remove_links(altmode); | ||
| 379 | |||
| 380 | if (drv->remove) | ||
| 381 | drv->remove(to_typec_altmode(dev)); | ||
| 382 | |||
| 383 | if (adev->active) { | ||
| 384 | WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE)); | ||
| 385 | typec_altmode_update_active(adev, false); | ||
| 386 | } | ||
| 387 | |||
| 388 | adev->desc = NULL; | ||
| 389 | adev->ops = NULL; | ||
| 390 | |||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | struct bus_type typec_bus = { | ||
| 395 | .name = "typec", | ||
| 396 | .dev_groups = typec_groups, | ||
| 397 | .match = typec_match, | ||
| 398 | .uevent = typec_uevent, | ||
| 399 | .probe = typec_probe, | ||
| 400 | .remove = typec_remove, | ||
| 401 | }; | ||
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h new file mode 100644 index 000000000000..62aaf8b56bde --- /dev/null +++ b/drivers/usb/typec/bus.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | |||
| 3 | #ifndef __USB_TYPEC_ALTMODE_H__ | ||
| 4 | #define __USB_TYPEC_ALTMODE_H__ | ||
| 5 | |||
| 6 | #include <linux/usb/typec_altmode.h> | ||
| 7 | #include <linux/usb/typec_mux.h> | ||
| 8 | |||
| 9 | struct bus_type; | ||
| 10 | |||
| 11 | struct altmode { | ||
| 12 | unsigned int id; | ||
| 13 | struct typec_altmode adev; | ||
| 14 | struct typec_mux *mux; | ||
| 15 | |||
| 16 | enum typec_port_data roles; | ||
| 17 | |||
| 18 | struct attribute *attrs[5]; | ||
| 19 | char group_name[6]; | ||
| 20 | struct attribute_group group; | ||
| 21 | const struct attribute_group *groups[2]; | ||
| 22 | |||
| 23 | struct altmode *partner; | ||
| 24 | struct altmode *plug[2]; | ||
| 25 | |||
| 26 | struct blocking_notifier_head nh; | ||
| 27 | }; | ||
| 28 | |||
| 29 | #define to_altmode(d) container_of(d, struct altmode, adev) | ||
| 30 | |||
| 31 | extern struct bus_type typec_bus; | ||
| 32 | extern const struct device_type typec_altmode_dev_type; | ||
| 33 | extern const struct device_type typec_port_dev_type; | ||
| 34 | |||
| 35 | #define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) | ||
| 36 | #define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) | ||
| 37 | |||
| 38 | #endif /* __USB_TYPEC_ALTMODE_H__ */ | ||
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 96dc9c4f73f0..c202975f8097 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c | |||
| @@ -10,28 +10,13 @@ | |||
| 10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
| 11 | #include <linux/mutex.h> | 11 | #include <linux/mutex.h> |
| 12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
| 13 | #include <linux/usb/typec.h> | ||
| 14 | #include <linux/usb/typec_mux.h> | ||
| 15 | 13 | ||
| 16 | struct typec_altmode { | 14 | #include "bus.h" |
| 17 | struct device dev; | ||
| 18 | u16 svid; | ||
| 19 | u8 mode; | ||
| 20 | |||
| 21 | u32 vdo; | ||
| 22 | char *desc; | ||
| 23 | enum typec_port_type roles; | ||
| 24 | unsigned int active:1; | ||
| 25 | |||
| 26 | struct attribute *attrs[5]; | ||
| 27 | char group_name[6]; | ||
| 28 | struct attribute_group group; | ||
| 29 | const struct attribute_group *groups[2]; | ||
| 30 | }; | ||
| 31 | 15 | ||
| 32 | struct typec_plug { | 16 | struct typec_plug { |
| 33 | struct device dev; | 17 | struct device dev; |
| 34 | enum typec_plug_index index; | 18 | enum typec_plug_index index; |
| 19 | struct ida mode_ids; | ||
| 35 | }; | 20 | }; |
| 36 | 21 | ||
| 37 | struct typec_cable { | 22 | struct typec_cable { |
| @@ -46,11 +31,13 @@ struct typec_partner { | |||
| 46 | unsigned int usb_pd:1; | 31 | unsigned int usb_pd:1; |
| 47 | struct usb_pd_identity *identity; | 32 | struct usb_pd_identity *identity; |
| 48 | enum typec_accessory accessory; | 33 | enum typec_accessory accessory; |
| 34 | struct ida mode_ids; | ||
| 49 | }; | 35 | }; |
| 50 | 36 | ||
| 51 | struct typec_port { | 37 | struct typec_port { |
| 52 | unsigned int id; | 38 | unsigned int id; |
| 53 | struct device dev; | 39 | struct device dev; |
| 40 | struct ida mode_ids; | ||
| 54 | 41 | ||
| 55 | int prefer_role; | 42 | int prefer_role; |
| 56 | enum typec_data_role data_role; | 43 | enum typec_data_role data_role; |
| @@ -71,17 +58,14 @@ struct typec_port { | |||
| 71 | #define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) | 58 | #define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) |
| 72 | #define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) | 59 | #define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) |
| 73 | #define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) | 60 | #define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) |
| 74 | #define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev) | ||
| 75 | 61 | ||
| 76 | static const struct device_type typec_partner_dev_type; | 62 | static const struct device_type typec_partner_dev_type; |
| 77 | static const struct device_type typec_cable_dev_type; | 63 | static const struct device_type typec_cable_dev_type; |
| 78 | static const struct device_type typec_plug_dev_type; | 64 | static const struct device_type typec_plug_dev_type; |
| 79 | static const struct device_type typec_port_dev_type; | ||
| 80 | 65 | ||
| 81 | #define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) | 66 | #define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) |
| 82 | #define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) | 67 | #define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) |
| 83 | #define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) | 68 | #define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) |
| 84 | #define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) | ||
| 85 | 69 | ||
| 86 | static DEFINE_IDA(typec_index_ida); | 70 | static DEFINE_IDA(typec_index_ida); |
| 87 | static struct class *typec_class; | 71 | static struct class *typec_class; |
| @@ -163,25 +147,148 @@ static void typec_report_identity(struct device *dev) | |||
| 163 | /* ------------------------------------------------------------------------- */ | 147 | /* ------------------------------------------------------------------------- */ |
| 164 | /* Alternate Modes */ | 148 | /* Alternate Modes */ |
| 165 | 149 | ||
| 150 | static int altmode_match(struct device *dev, void *data) | ||
| 151 | { | ||
| 152 | struct typec_altmode *adev = to_typec_altmode(dev); | ||
| 153 | struct typec_device_id *id = data; | ||
| 154 | |||
| 155 | if (!is_typec_altmode(dev)) | ||
| 156 | return 0; | ||
| 157 | |||
| 158 | return ((adev->svid == id->svid) && (adev->mode == id->mode)); | ||
| 159 | } | ||
| 160 | |||
| 161 | static void typec_altmode_set_partner(struct altmode *altmode) | ||
| 162 | { | ||
| 163 | struct typec_altmode *adev = &altmode->adev; | ||
| 164 | struct typec_device_id id = { adev->svid, adev->mode, }; | ||
| 165 | struct typec_port *port = typec_altmode2port(adev); | ||
| 166 | struct altmode *partner; | ||
| 167 | struct device *dev; | ||
| 168 | |||
| 169 | dev = device_find_child(&port->dev, &id, altmode_match); | ||
| 170 | if (!dev) | ||
| 171 | return; | ||
| 172 | |||
| 173 | /* Bind the port alt mode to the partner/plug alt mode. */ | ||
| 174 | partner = to_altmode(to_typec_altmode(dev)); | ||
| 175 | altmode->partner = partner; | ||
| 176 | |||
| 177 | /* Bind the partner/plug alt mode to the port alt mode. */ | ||
| 178 | if (is_typec_plug(adev->dev.parent)) { | ||
| 179 | struct typec_plug *plug = to_typec_plug(adev->dev.parent); | ||
| 180 | |||
| 181 | partner->plug[plug->index] = altmode; | ||
| 182 | } else { | ||
| 183 | partner->partner = altmode; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | static void typec_altmode_put_partner(struct altmode *altmode) | ||
| 188 | { | ||
| 189 | struct altmode *partner = altmode->partner; | ||
| 190 | struct typec_altmode *adev; | ||
| 191 | |||
| 192 | if (!partner) | ||
| 193 | return; | ||
| 194 | |||
| 195 | adev = &partner->adev; | ||
| 196 | |||
| 197 | if (is_typec_plug(adev->dev.parent)) { | ||
| 198 | struct typec_plug *plug = to_typec_plug(adev->dev.parent); | ||
| 199 | |||
| 200 | partner->plug[plug->index] = NULL; | ||
| 201 | } else { | ||
| 202 | partner->partner = NULL; | ||
| 203 | } | ||
| 204 | put_device(&adev->dev); | ||
| 205 | } | ||
| 206 | |||
| 207 | static int __typec_port_match(struct device *dev, const void *name) | ||
| 208 | { | ||
| 209 | return !strcmp((const char *)name, dev_name(dev)); | ||
| 210 | } | ||
| 211 | |||
| 212 | static void *typec_port_match(struct device_connection *con, int ep, void *data) | ||
| 213 | { | ||
| 214 | return class_find_device(typec_class, NULL, con->endpoint[ep], | ||
| 215 | __typec_port_match); | ||
| 216 | } | ||
| 217 | |||
| 218 | struct typec_altmode * | ||
| 219 | typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, | ||
| 220 | struct notifier_block *nb) | ||
| 221 | { | ||
| 222 | struct typec_device_id id = { svid, mode, }; | ||
| 223 | struct device *altmode_dev; | ||
| 224 | struct device *port_dev; | ||
| 225 | struct altmode *altmode; | ||
| 226 | int ret; | ||
| 227 | |||
| 228 | /* Find the port linked to the caller */ | ||
| 229 | port_dev = device_connection_find_match(dev, NULL, NULL, | ||
| 230 | typec_port_match); | ||
| 231 | if (IS_ERR_OR_NULL(port_dev)) | ||
| 232 | return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV); | ||
| 233 | |||
| 234 | /* Find the altmode with matching svid */ | ||
| 235 | altmode_dev = device_find_child(port_dev, &id, altmode_match); | ||
| 236 | |||
| 237 | put_device(port_dev); | ||
| 238 | |||
| 239 | if (!altmode_dev) | ||
| 240 | return ERR_PTR(-ENODEV); | ||
| 241 | |||
| 242 | altmode = to_altmode(to_typec_altmode(altmode_dev)); | ||
| 243 | |||
| 244 | /* Register notifier */ | ||
| 245 | ret = blocking_notifier_chain_register(&altmode->nh, nb); | ||
| 246 | if (ret) { | ||
| 247 | put_device(altmode_dev); | ||
| 248 | return ERR_PTR(ret); | ||
| 249 | } | ||
| 250 | |||
| 251 | return &altmode->adev; | ||
| 252 | } | ||
| 253 | EXPORT_SYMBOL_GPL(typec_altmode_register_notifier); | ||
| 254 | |||
| 255 | void typec_altmode_unregister_notifier(struct typec_altmode *adev, | ||
| 256 | struct notifier_block *nb) | ||
| 257 | { | ||
| 258 | struct altmode *altmode = to_altmode(adev); | ||
| 259 | |||
| 260 | blocking_notifier_chain_unregister(&altmode->nh, nb); | ||
| 261 | put_device(&adev->dev); | ||
| 262 | } | ||
| 263 | EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier); | ||
| 264 | |||
| 166 | /** | 265 | /** |
| 167 | * typec_altmode_update_active - Report Enter/Exit mode | 266 | * typec_altmode_update_active - Report Enter/Exit mode |
| 168 | * @alt: Handle to the alternate mode | 267 | * @adev: Handle to the alternate mode |
| 169 | * @active: True when the mode has been entered | 268 | * @active: True when the mode has been entered |
| 170 | * | 269 | * |
| 171 | * If a partner or cable plug executes Enter/Exit Mode command successfully, the | 270 | * If a partner or cable plug executes Enter/Exit Mode command successfully, the |
| 172 | * drivers use this routine to report the updated state of the mode. | 271 | * drivers use this routine to report the updated state of the mode. |
| 173 | */ | 272 | */ |
| 174 | void typec_altmode_update_active(struct typec_altmode *alt, bool active) | 273 | void typec_altmode_update_active(struct typec_altmode *adev, bool active) |
| 175 | { | 274 | { |
| 176 | char dir[6]; | 275 | char dir[6]; |
| 177 | 276 | ||
| 178 | if (alt->active == active) | 277 | if (adev->active == active) |
| 179 | return; | 278 | return; |
| 180 | 279 | ||
| 181 | alt->active = active; | 280 | if (!is_typec_port(adev->dev.parent)) { |
| 182 | snprintf(dir, sizeof(dir), "mode%d", alt->mode); | 281 | if (!active) |
| 183 | sysfs_notify(&alt->dev.kobj, dir, "active"); | 282 | module_put(adev->dev.driver->owner); |
| 184 | kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE); | 283 | else |
| 284 | WARN_ON(!try_module_get(adev->dev.driver->owner)); | ||
| 285 | } | ||
| 286 | |||
| 287 | adev->active = active; | ||
| 288 | snprintf(dir, sizeof(dir), "mode%d", adev->mode); | ||
| 289 | sysfs_notify(&adev->dev.kobj, dir, "active"); | ||
| 290 | sysfs_notify(&adev->dev.kobj, NULL, "active"); | ||
| 291 | kobject_uevent(&adev->dev.kobj, KOBJ_CHANGE); | ||
| 185 | } | 292 | } |
| 186 | EXPORT_SYMBOL_GPL(typec_altmode_update_active); | 293 | EXPORT_SYMBOL_GPL(typec_altmode_update_active); |
| 187 | 294 | ||
| @@ -208,7 +315,7 @@ EXPORT_SYMBOL_GPL(typec_altmode2port); | |||
| 208 | static ssize_t | 315 | static ssize_t |
| 209 | vdo_show(struct device *dev, struct device_attribute *attr, char *buf) | 316 | vdo_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 210 | { | 317 | { |
| 211 | struct typec_altmode *alt = to_altmode(dev); | 318 | struct typec_altmode *alt = to_typec_altmode(dev); |
| 212 | 319 | ||
| 213 | return sprintf(buf, "0x%08x\n", alt->vdo); | 320 | return sprintf(buf, "0x%08x\n", alt->vdo); |
| 214 | } | 321 | } |
| @@ -217,7 +324,7 @@ static DEVICE_ATTR_RO(vdo); | |||
| 217 | static ssize_t | 324 | static ssize_t |
| 218 | description_show(struct device *dev, struct device_attribute *attr, char *buf) | 325 | description_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 219 | { | 326 | { |
| 220 | struct typec_altmode *alt = to_altmode(dev); | 327 | struct typec_altmode *alt = to_typec_altmode(dev); |
| 221 | 328 | ||
| 222 | return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); | 329 | return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); |
| 223 | } | 330 | } |
| @@ -226,7 +333,7 @@ static DEVICE_ATTR_RO(description); | |||
| 226 | static ssize_t | 333 | static ssize_t |
| 227 | active_show(struct device *dev, struct device_attribute *attr, char *buf) | 334 | active_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 228 | { | 335 | { |
| 229 | struct typec_altmode *alt = to_altmode(dev); | 336 | struct typec_altmode *alt = to_typec_altmode(dev); |
| 230 | 337 | ||
| 231 | return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); | 338 | return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); |
| 232 | } | 339 | } |
| @@ -234,21 +341,37 @@ active_show(struct device *dev, struct device_attribute *attr, char *buf) | |||
| 234 | static ssize_t active_store(struct device *dev, struct device_attribute *attr, | 341 | static ssize_t active_store(struct device *dev, struct device_attribute *attr, |
| 235 | const char *buf, size_t size) | 342 | const char *buf, size_t size) |
| 236 | { | 343 | { |
| 237 | struct typec_altmode *alt = to_altmode(dev); | 344 | struct typec_altmode *adev = to_typec_altmode(dev); |
| 238 | struct typec_port *port = typec_altmode2port(alt); | 345 | struct altmode *altmode = to_altmode(adev); |
| 239 | bool activate; | 346 | bool enter; |
| 240 | int ret; | 347 | int ret; |
| 241 | 348 | ||
| 242 | if (!port->cap->activate_mode) | 349 | ret = kstrtobool(buf, &enter); |
| 243 | return -EOPNOTSUPP; | ||
| 244 | |||
| 245 | ret = kstrtobool(buf, &activate); | ||
| 246 | if (ret) | 350 | if (ret) |
| 247 | return ret; | 351 | return ret; |
| 248 | 352 | ||
| 249 | ret = port->cap->activate_mode(port->cap, alt->mode, activate); | 353 | if (adev->active == enter) |
| 250 | if (ret) | 354 | return size; |
| 251 | return ret; | 355 | |
| 356 | if (is_typec_port(adev->dev.parent)) { | ||
| 357 | typec_altmode_update_active(adev, enter); | ||
| 358 | |||
| 359 | /* Make sure that the partner exits the mode before disabling */ | ||
| 360 | if (altmode->partner && !enter && altmode->partner->adev.active) | ||
| 361 | typec_altmode_exit(&altmode->partner->adev); | ||
| 362 | } else if (altmode->partner) { | ||
| 363 | if (enter && !altmode->partner->adev.active) { | ||
| 364 | dev_warn(dev, "port has the mode disabled\n"); | ||
| 365 | return -EPERM; | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | /* Note: If there is no driver, the mode will not be entered */ | ||
| 370 | if (adev->ops && adev->ops->activate) { | ||
| 371 | ret = adev->ops->activate(adev, enter); | ||
| 372 | if (ret) | ||
| 373 | return ret; | ||
| 374 | } | ||
| 252 | 375 | ||
| 253 | return size; | 376 | return size; |
| 254 | } | 377 | } |
| @@ -258,7 +381,7 @@ static ssize_t | |||
| 258 | supported_roles_show(struct device *dev, struct device_attribute *attr, | 381 | supported_roles_show(struct device *dev, struct device_attribute *attr, |
| 259 | char *buf) | 382 | char *buf) |
| 260 | { | 383 | { |
| 261 | struct typec_altmode *alt = to_altmode(dev); | 384 | struct altmode *alt = to_altmode(to_typec_altmode(dev)); |
| 262 | ssize_t ret; | 385 | ssize_t ret; |
| 263 | 386 | ||
| 264 | switch (alt->roles) { | 387 | switch (alt->roles) { |
| @@ -277,29 +400,72 @@ supported_roles_show(struct device *dev, struct device_attribute *attr, | |||
| 277 | } | 400 | } |
| 278 | static DEVICE_ATTR_RO(supported_roles); | 401 | static DEVICE_ATTR_RO(supported_roles); |
| 279 | 402 | ||
| 280 | static void typec_altmode_release(struct device *dev) | 403 | static ssize_t |
| 404 | mode_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 281 | { | 405 | { |
| 282 | struct typec_altmode *alt = to_altmode(dev); | 406 | struct typec_altmode *adev = to_typec_altmode(dev); |
| 283 | 407 | ||
| 284 | kfree(alt); | 408 | return sprintf(buf, "%u\n", adev->mode); |
| 285 | } | 409 | } |
| 410 | static DEVICE_ATTR_RO(mode); | ||
| 286 | 411 | ||
| 287 | static ssize_t svid_show(struct device *dev, struct device_attribute *attr, | 412 | static ssize_t |
| 288 | char *buf) | 413 | svid_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 289 | { | 414 | { |
| 290 | struct typec_altmode *alt = to_altmode(dev); | 415 | struct typec_altmode *adev = to_typec_altmode(dev); |
| 291 | 416 | ||
| 292 | return sprintf(buf, "%04x\n", alt->svid); | 417 | return sprintf(buf, "%04x\n", adev->svid); |
| 293 | } | 418 | } |
| 294 | static DEVICE_ATTR_RO(svid); | 419 | static DEVICE_ATTR_RO(svid); |
| 295 | 420 | ||
| 296 | static struct attribute *typec_altmode_attrs[] = { | 421 | static struct attribute *typec_altmode_attrs[] = { |
| 422 | &dev_attr_active.attr, | ||
| 423 | &dev_attr_mode.attr, | ||
| 297 | &dev_attr_svid.attr, | 424 | &dev_attr_svid.attr, |
| 425 | &dev_attr_vdo.attr, | ||
| 298 | NULL | 426 | NULL |
| 299 | }; | 427 | }; |
| 300 | ATTRIBUTE_GROUPS(typec_altmode); | 428 | ATTRIBUTE_GROUPS(typec_altmode); |
| 301 | 429 | ||
| 302 | static const struct device_type typec_altmode_dev_type = { | 430 | static int altmode_id_get(struct device *dev) |
| 431 | { | ||
| 432 | struct ida *ids; | ||
| 433 | |||
| 434 | if (is_typec_partner(dev)) | ||
| 435 | ids = &to_typec_partner(dev)->mode_ids; | ||
| 436 | else if (is_typec_plug(dev)) | ||
| 437 | ids = &to_typec_plug(dev)->mode_ids; | ||
| 438 | else | ||
| 439 | ids = &to_typec_port(dev)->mode_ids; | ||
| 440 | |||
| 441 | return ida_simple_get(ids, 0, 0, GFP_KERNEL); | ||
| 442 | } | ||
| 443 | |||
| 444 | static void altmode_id_remove(struct device *dev, int id) | ||
| 445 | { | ||
| 446 | struct ida *ids; | ||
| 447 | |||
| 448 | if (is_typec_partner(dev)) | ||
| 449 | ids = &to_typec_partner(dev)->mode_ids; | ||
| 450 | else if (is_typec_plug(dev)) | ||
| 451 | ids = &to_typec_plug(dev)->mode_ids; | ||
| 452 | else | ||
| 453 | ids = &to_typec_port(dev)->mode_ids; | ||
| 454 | |||
| 455 | ida_simple_remove(ids, id); | ||
| 456 | } | ||
| 457 | |||
| 458 | static void typec_altmode_release(struct device *dev) | ||
| 459 | { | ||
| 460 | struct altmode *alt = to_altmode(to_typec_altmode(dev)); | ||
| 461 | |||
| 462 | typec_altmode_put_partner(alt); | ||
| 463 | |||
| 464 | altmode_id_remove(alt->adev.dev.parent, alt->id); | ||
| 465 | kfree(alt); | ||
| 466 | } | ||
| 467 | |||
| 468 | const struct device_type typec_altmode_dev_type = { | ||
| 303 | .name = "typec_alternate_mode", | 469 | .name = "typec_alternate_mode", |
| 304 | .groups = typec_altmode_groups, | 470 | .groups = typec_altmode_groups, |
| 305 | .release = typec_altmode_release, | 471 | .release = typec_altmode_release, |
| @@ -309,58 +475,74 @@ static struct typec_altmode * | |||
| 309 | typec_register_altmode(struct device *parent, | 475 | typec_register_altmode(struct device *parent, |
| 310 | const struct typec_altmode_desc *desc) | 476 | const struct typec_altmode_desc *desc) |
| 311 | { | 477 | { |
| 312 | struct typec_altmode *alt; | 478 | unsigned int id = altmode_id_get(parent); |
| 479 | bool is_port = is_typec_port(parent); | ||
| 480 | struct altmode *alt; | ||
| 313 | int ret; | 481 | int ret; |
| 314 | 482 | ||
| 315 | alt = kzalloc(sizeof(*alt), GFP_KERNEL); | 483 | alt = kzalloc(sizeof(*alt), GFP_KERNEL); |
| 316 | if (!alt) | 484 | if (!alt) |
| 317 | return ERR_PTR(-ENOMEM); | 485 | return ERR_PTR(-ENOMEM); |
| 318 | 486 | ||
| 319 | alt->svid = desc->svid; | 487 | alt->adev.svid = desc->svid; |
| 320 | alt->mode = desc->mode; | 488 | alt->adev.mode = desc->mode; |
| 321 | alt->vdo = desc->vdo; | 489 | alt->adev.vdo = desc->vdo; |
| 322 | alt->roles = desc->roles; | 490 | alt->roles = desc->roles; |
| 491 | alt->id = id; | ||
| 323 | 492 | ||
| 324 | alt->attrs[0] = &dev_attr_vdo.attr; | 493 | alt->attrs[0] = &dev_attr_vdo.attr; |
| 325 | alt->attrs[1] = &dev_attr_description.attr; | 494 | alt->attrs[1] = &dev_attr_description.attr; |
| 326 | alt->attrs[2] = &dev_attr_active.attr; | 495 | alt->attrs[2] = &dev_attr_active.attr; |
| 327 | 496 | ||
| 328 | if (is_typec_port(parent)) | 497 | if (is_port) { |
| 329 | alt->attrs[3] = &dev_attr_supported_roles.attr; | 498 | alt->attrs[3] = &dev_attr_supported_roles.attr; |
| 499 | alt->adev.active = true; /* Enabled by default */ | ||
| 500 | } | ||
| 330 | 501 | ||
| 331 | sprintf(alt->group_name, "mode%d", desc->mode); | 502 | sprintf(alt->group_name, "mode%d", desc->mode); |
| 332 | alt->group.name = alt->group_name; | 503 | alt->group.name = alt->group_name; |
| 333 | alt->group.attrs = alt->attrs; | 504 | alt->group.attrs = alt->attrs; |
| 334 | alt->groups[0] = &alt->group; | 505 | alt->groups[0] = &alt->group; |
| 335 | 506 | ||
| 336 | alt->dev.parent = parent; | 507 | alt->adev.dev.parent = parent; |
| 337 | alt->dev.groups = alt->groups; | 508 | alt->adev.dev.groups = alt->groups; |
| 338 | alt->dev.type = &typec_altmode_dev_type; | 509 | alt->adev.dev.type = &typec_altmode_dev_type; |
| 339 | dev_set_name(&alt->dev, "%s-%04x:%u", dev_name(parent), | 510 | dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); |
| 340 | alt->svid, alt->mode); | 511 | |
| 512 | /* Link partners and plugs with the ports */ | ||
| 513 | if (is_port) | ||
| 514 | BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh); | ||
| 515 | else | ||
| 516 | typec_altmode_set_partner(alt); | ||
| 517 | |||
| 518 | /* The partners are bind to drivers */ | ||
| 519 | if (is_typec_partner(parent)) | ||
| 520 | alt->adev.dev.bus = &typec_bus; | ||
| 341 | 521 | ||
| 342 | ret = device_register(&alt->dev); | 522 | ret = device_register(&alt->adev.dev); |
| 343 | if (ret) { | 523 | if (ret) { |
| 344 | dev_err(parent, "failed to register alternate mode (%d)\n", | 524 | dev_err(parent, "failed to register alternate mode (%d)\n", |
| 345 | ret); | 525 | ret); |
| 346 | put_device(&alt->dev); | 526 | put_device(&alt->adev.dev); |
| 347 | return ERR_PTR(ret); | 527 | return ERR_PTR(ret); |
| 348 | } | 528 | } |
| 349 | 529 | ||
| 350 | return alt; | 530 | return &alt->adev; |
| 351 | } | 531 | } |
| 352 | 532 | ||
| 353 | /** | 533 | /** |
| 354 | * typec_unregister_altmode - Unregister Alternate Mode | 534 | * typec_unregister_altmode - Unregister Alternate Mode |
| 355 | * @alt: The alternate mode to be unregistered | 535 | * @adev: The alternate mode to be unregistered |
| 356 | * | 536 | * |
| 357 | * Unregister device created with typec_partner_register_altmode(), | 537 | * Unregister device created with typec_partner_register_altmode(), |
| 358 | * typec_plug_register_altmode() or typec_port_register_altmode(). | 538 | * typec_plug_register_altmode() or typec_port_register_altmode(). |
| 359 | */ | 539 | */ |
| 360 | void typec_unregister_altmode(struct typec_altmode *alt) | 540 | void typec_unregister_altmode(struct typec_altmode *adev) |
| 361 | { | 541 | { |
| 362 | if (!IS_ERR_OR_NULL(alt)) | 542 | if (IS_ERR_OR_NULL(adev)) |
| 363 | device_unregister(&alt->dev); | 543 | return; |
| 544 | typec_mux_put(to_altmode(adev)->mux); | ||
| 545 | device_unregister(&adev->dev); | ||
| 364 | } | 546 | } |
| 365 | EXPORT_SYMBOL_GPL(typec_unregister_altmode); | 547 | EXPORT_SYMBOL_GPL(typec_unregister_altmode); |
| 366 | 548 | ||
| @@ -398,6 +580,7 @@ static void typec_partner_release(struct device *dev) | |||
| 398 | { | 580 | { |
| 399 | struct typec_partner *partner = to_typec_partner(dev); | 581 | struct typec_partner *partner = to_typec_partner(dev); |
| 400 | 582 | ||
| 583 | ida_destroy(&partner->mode_ids); | ||
| 401 | kfree(partner); | 584 | kfree(partner); |
| 402 | } | 585 | } |
| 403 | 586 | ||
| @@ -463,6 +646,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, | |||
| 463 | if (!partner) | 646 | if (!partner) |
| 464 | return ERR_PTR(-ENOMEM); | 647 | return ERR_PTR(-ENOMEM); |
| 465 | 648 | ||
| 649 | ida_init(&partner->mode_ids); | ||
| 466 | partner->usb_pd = desc->usb_pd; | 650 | partner->usb_pd = desc->usb_pd; |
| 467 | partner->accessory = desc->accessory; | 651 | partner->accessory = desc->accessory; |
| 468 | 652 | ||
| @@ -511,6 +695,7 @@ static void typec_plug_release(struct device *dev) | |||
| 511 | { | 695 | { |
| 512 | struct typec_plug *plug = to_typec_plug(dev); | 696 | struct typec_plug *plug = to_typec_plug(dev); |
| 513 | 697 | ||
| 698 | ida_destroy(&plug->mode_ids); | ||
| 514 | kfree(plug); | 699 | kfree(plug); |
| 515 | } | 700 | } |
| 516 | 701 | ||
| @@ -563,6 +748,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, | |||
| 563 | 748 | ||
| 564 | sprintf(name, "plug%d", desc->index); | 749 | sprintf(name, "plug%d", desc->index); |
| 565 | 750 | ||
| 751 | ida_init(&plug->mode_ids); | ||
| 566 | plug->index = desc->index; | 752 | plug->index = desc->index; |
| 567 | plug->dev.class = typec_class; | 753 | plug->dev.class = typec_class; |
| 568 | plug->dev.parent = &cable->dev; | 754 | plug->dev.parent = &cable->dev; |
| @@ -1083,12 +1269,13 @@ static void typec_release(struct device *dev) | |||
| 1083 | struct typec_port *port = to_typec_port(dev); | 1269 | struct typec_port *port = to_typec_port(dev); |
| 1084 | 1270 | ||
| 1085 | ida_simple_remove(&typec_index_ida, port->id); | 1271 | ida_simple_remove(&typec_index_ida, port->id); |
| 1272 | ida_destroy(&port->mode_ids); | ||
| 1086 | typec_switch_put(port->sw); | 1273 | typec_switch_put(port->sw); |
| 1087 | typec_mux_put(port->mux); | 1274 | typec_mux_put(port->mux); |
| 1088 | kfree(port); | 1275 | kfree(port); |
| 1089 | } | 1276 | } |
| 1090 | 1277 | ||
| 1091 | static const struct device_type typec_port_dev_type = { | 1278 | const struct device_type typec_port_dev_type = { |
| 1092 | .name = "typec_port", | 1279 | .name = "typec_port", |
| 1093 | .groups = typec_groups, | 1280 | .groups = typec_groups, |
| 1094 | .uevent = typec_uevent, | 1281 | .uevent = typec_uevent, |
| @@ -1279,11 +1466,11 @@ EXPORT_SYMBOL_GPL(typec_get_orientation); | |||
| 1279 | 1466 | ||
| 1280 | /** | 1467 | /** |
| 1281 | * typec_set_mode - Set mode of operation for USB Type-C connector | 1468 | * typec_set_mode - Set mode of operation for USB Type-C connector |
| 1282 | * @port: USB Type-C port for the connector | 1469 | * @port: USB Type-C connector |
| 1283 | * @mode: Operation mode for the connector | 1470 | * @mode: Accessory Mode, USB Operation or Safe State |
| 1284 | * | 1471 | * |
| 1285 | * Set mode @mode for @port. This function will configure the muxes needed to | 1472 | * Configure @port for Accessory Mode @mode. This function will configure the |
| 1286 | * enter @mode. | 1473 | * muxes needed for @mode. |
| 1287 | */ | 1474 | */ |
| 1288 | int typec_set_mode(struct typec_port *port, int mode) | 1475 | int typec_set_mode(struct typec_port *port, int mode) |
| 1289 | { | 1476 | { |
| @@ -1297,6 +1484,7 @@ EXPORT_SYMBOL_GPL(typec_set_mode); | |||
| 1297 | * typec_port_register_altmode - Register USB Type-C Port Alternate Mode | 1484 | * typec_port_register_altmode - Register USB Type-C Port Alternate Mode |
| 1298 | * @port: USB Type-C Port that supports the alternate mode | 1485 | * @port: USB Type-C Port that supports the alternate mode |
| 1299 | * @desc: Description of the alternate mode | 1486 | * @desc: Description of the alternate mode |
| 1487 | * @drvdata: Private pointer to driver specific info | ||
| 1300 | * | 1488 | * |
| 1301 | * This routine is used to register an alternate mode that @port is capable of | 1489 | * This routine is used to register an alternate mode that @port is capable of |
| 1302 | * supporting. | 1490 | * supporting. |
| @@ -1307,7 +1495,23 @@ struct typec_altmode * | |||
| 1307 | typec_port_register_altmode(struct typec_port *port, | 1495 | typec_port_register_altmode(struct typec_port *port, |
| 1308 | const struct typec_altmode_desc *desc) | 1496 | const struct typec_altmode_desc *desc) |
| 1309 | { | 1497 | { |
| 1310 | return typec_register_altmode(&port->dev, desc); | 1498 | struct typec_altmode *adev; |
| 1499 | struct typec_mux *mux; | ||
| 1500 | char id[10]; | ||
| 1501 | |||
| 1502 | sprintf(id, "id%04xm%02x", desc->svid, desc->mode); | ||
| 1503 | |||
| 1504 | mux = typec_mux_get(port->dev.parent, id); | ||
| 1505 | if (IS_ERR(mux)) | ||
| 1506 | return ERR_CAST(mux); | ||
| 1507 | |||
| 1508 | adev = typec_register_altmode(&port->dev, desc); | ||
| 1509 | if (IS_ERR(adev)) | ||
| 1510 | typec_mux_put(mux); | ||
| 1511 | else | ||
| 1512 | to_altmode(adev)->mux = mux; | ||
| 1513 | |||
| 1514 | return adev; | ||
| 1311 | } | 1515 | } |
| 1312 | EXPORT_SYMBOL_GPL(typec_port_register_altmode); | 1516 | EXPORT_SYMBOL_GPL(typec_port_register_altmode); |
| 1313 | 1517 | ||
| @@ -1381,10 +1585,12 @@ struct typec_port *typec_register_port(struct device *parent, | |||
| 1381 | break; | 1585 | break; |
| 1382 | } | 1586 | } |
| 1383 | 1587 | ||
| 1588 | ida_init(&port->mode_ids); | ||
| 1589 | mutex_init(&port->port_type_lock); | ||
| 1590 | |||
| 1384 | port->id = id; | 1591 | port->id = id; |
| 1385 | port->cap = cap; | 1592 | port->cap = cap; |
| 1386 | port->port_type = cap->type; | 1593 | port->port_type = cap->type; |
| 1387 | mutex_init(&port->port_type_lock); | ||
| 1388 | port->prefer_role = cap->prefer_role; | 1594 | port->prefer_role = cap->prefer_role; |
| 1389 | 1595 | ||
| 1390 | port->dev.class = typec_class; | 1596 | port->dev.class = typec_class; |
| @@ -1428,8 +1634,19 @@ EXPORT_SYMBOL_GPL(typec_unregister_port); | |||
| 1428 | 1634 | ||
| 1429 | static int __init typec_init(void) | 1635 | static int __init typec_init(void) |
| 1430 | { | 1636 | { |
| 1637 | int ret; | ||
| 1638 | |||
| 1639 | ret = bus_register(&typec_bus); | ||
| 1640 | if (ret) | ||
| 1641 | return ret; | ||
| 1642 | |||
| 1431 | typec_class = class_create(THIS_MODULE, "typec"); | 1643 | typec_class = class_create(THIS_MODULE, "typec"); |
| 1432 | return PTR_ERR_OR_ZERO(typec_class); | 1644 | if (IS_ERR(typec_class)) { |
| 1645 | bus_unregister(&typec_bus); | ||
| 1646 | return PTR_ERR(typec_class); | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | return 0; | ||
| 1433 | } | 1650 | } |
| 1434 | subsys_initcall(typec_init); | 1651 | subsys_initcall(typec_init); |
| 1435 | 1652 | ||
| @@ -1437,6 +1654,7 @@ static void __exit typec_exit(void) | |||
| 1437 | { | 1654 | { |
| 1438 | class_destroy(typec_class); | 1655 | class_destroy(typec_class); |
| 1439 | ida_destroy(&typec_index_ida); | 1656 | ida_destroy(&typec_index_ida); |
| 1657 | bus_unregister(&typec_bus); | ||
| 1440 | } | 1658 | } |
| 1441 | module_exit(typec_exit); | 1659 | module_exit(typec_exit); |
| 1442 | 1660 | ||
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 96a71a648eed..1298a7daa57d 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h | |||
| @@ -746,4 +746,19 @@ struct tb_service_id { | |||
| 746 | #define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 | 746 | #define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 |
| 747 | #define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 | 747 | #define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 |
| 748 | 748 | ||
| 749 | /* USB Type-C Alternate Modes */ | ||
| 750 | |||
| 751 | #define TYPEC_ANY_MODE 0x7 | ||
| 752 | |||
| 753 | /** | ||
| 754 | * struct typec_device_id - USB Type-C alternate mode identifiers | ||
| 755 | * @svid: Standard or Vendor ID | ||
| 756 | * @mode: Mode index | ||
| 757 | */ | ||
| 758 | struct typec_device_id { | ||
| 759 | __u16 svid; | ||
| 760 | __u8 mode; | ||
| 761 | kernel_ulong_t driver_data; | ||
| 762 | }; | ||
| 763 | |||
| 749 | #endif /* LINUX_MOD_DEVICETABLE_H */ | 764 | #endif /* LINUX_MOD_DEVICETABLE_H */ |
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 2dcb1683075f..7df4ecabc78a 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h | |||
| @@ -5,21 +5,18 @@ | |||
| 5 | 5 | ||
| 6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
| 7 | 7 | ||
| 8 | /* XXX: Once we have a header for USB Power Delivery, this belongs there */ | ||
| 9 | #define ALTMODE_MAX_MODES 6 | ||
| 10 | |||
| 11 | /* USB Type-C Specification releases */ | 8 | /* USB Type-C Specification releases */ |
| 12 | #define USB_TYPEC_REV_1_0 0x100 /* 1.0 */ | 9 | #define USB_TYPEC_REV_1_0 0x100 /* 1.0 */ |
| 13 | #define USB_TYPEC_REV_1_1 0x110 /* 1.1 */ | 10 | #define USB_TYPEC_REV_1_1 0x110 /* 1.1 */ |
| 14 | #define USB_TYPEC_REV_1_2 0x120 /* 1.2 */ | 11 | #define USB_TYPEC_REV_1_2 0x120 /* 1.2 */ |
| 15 | 12 | ||
| 16 | struct typec_altmode; | ||
| 17 | struct typec_partner; | 13 | struct typec_partner; |
| 18 | struct typec_cable; | 14 | struct typec_cable; |
| 19 | struct typec_plug; | 15 | struct typec_plug; |
| 20 | struct typec_port; | 16 | struct typec_port; |
| 21 | 17 | ||
| 22 | struct fwnode_handle; | 18 | struct fwnode_handle; |
| 19 | struct device; | ||
| 23 | 20 | ||
| 24 | enum typec_port_type { | 21 | enum typec_port_type { |
| 25 | TYPEC_PORT_SRC, | 22 | TYPEC_PORT_SRC, |
| @@ -107,7 +104,7 @@ struct typec_altmode_desc { | |||
| 107 | u8 mode; | 104 | u8 mode; |
| 108 | u32 vdo; | 105 | u32 vdo; |
| 109 | /* Only used with ports */ | 106 | /* Only used with ports */ |
| 110 | enum typec_port_type roles; | 107 | enum typec_port_data roles; |
| 111 | }; | 108 | }; |
| 112 | 109 | ||
| 113 | struct typec_altmode | 110 | struct typec_altmode |
| @@ -186,7 +183,6 @@ struct typec_partner_desc { | |||
| 186 | * @dr_set: Set Data Role | 183 | * @dr_set: Set Data Role |
| 187 | * @pr_set: Set Power Role | 184 | * @pr_set: Set Power Role |
| 188 | * @vconn_set: Set VCONN Role | 185 | * @vconn_set: Set VCONN Role |
| 189 | * @activate_mode: Enter/exit given Alternate Mode | ||
| 190 | * @port_type_set: Set port type | 186 | * @port_type_set: Set port type |
| 191 | * | 187 | * |
| 192 | * Static capabilities of a single USB Type-C port. | 188 | * Static capabilities of a single USB Type-C port. |
| @@ -212,12 +208,8 @@ struct typec_capability { | |||
| 212 | enum typec_role); | 208 | enum typec_role); |
| 213 | int (*vconn_set)(const struct typec_capability *, | 209 | int (*vconn_set)(const struct typec_capability *, |
| 214 | enum typec_role); | 210 | enum typec_role); |
| 215 | |||
| 216 | int (*activate_mode)(const struct typec_capability *, | ||
| 217 | int mode, int activate); | ||
| 218 | int (*port_type_set)(const struct typec_capability *, | 211 | int (*port_type_set)(const struct typec_capability *, |
| 219 | enum typec_port_type); | 212 | enum typec_port_type); |
| 220 | |||
| 221 | }; | 213 | }; |
| 222 | 214 | ||
| 223 | /* Specific to try_role(). Indicates the user want's to clear the preference. */ | 215 | /* Specific to try_role(). Indicates the user want's to clear the preference. */ |
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h new file mode 100644 index 000000000000..9a88c74a1d0d --- /dev/null +++ b/include/linux/usb/typec_altmode.h | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | |||
| 3 | #ifndef __USB_TYPEC_ALTMODE_H | ||
| 4 | #define __USB_TYPEC_ALTMODE_H | ||
| 5 | |||
| 6 | #include <linux/mod_devicetable.h> | ||
| 7 | #include <linux/usb/typec.h> | ||
| 8 | #include <linux/device.h> | ||
| 9 | |||
| 10 | #define MODE_DISCOVERY_MAX 6 | ||
| 11 | |||
| 12 | struct typec_altmode_ops; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * struct typec_altmode - USB Type-C alternate mode device | ||
| 16 | * @dev: Driver model's view of this device | ||
| 17 | * @svid: Standard or Vendor ID (SVID) of the alternate mode | ||
| 18 | * @mode: Index of the Mode | ||
| 19 | * @vdo: VDO returned by Discover Modes USB PD command | ||
| 20 | * @active: Tells has the mode been entered or not | ||
| 21 | * @desc: Optional human readable description of the mode | ||
| 22 | * @ops: Operations vector from the driver | ||
| 23 | */ | ||
| 24 | struct typec_altmode { | ||
| 25 | struct device dev; | ||
| 26 | u16 svid; | ||
| 27 | int mode; | ||
| 28 | u32 vdo; | ||
| 29 | unsigned int active:1; | ||
| 30 | |||
| 31 | char *desc; | ||
| 32 | const struct typec_altmode_ops *ops; | ||
| 33 | }; | ||
| 34 | |||
| 35 | #define to_typec_altmode(d) container_of(d, struct typec_altmode, dev) | ||
| 36 | |||
| 37 | static inline void typec_altmode_set_drvdata(struct typec_altmode *altmode, | ||
| 38 | void *data) | ||
| 39 | { | ||
| 40 | dev_set_drvdata(&altmode->dev, data); | ||
| 41 | } | ||
| 42 | |||
| 43 | static inline void *typec_altmode_get_drvdata(struct typec_altmode *altmode) | ||
| 44 | { | ||
| 45 | return dev_get_drvdata(&altmode->dev); | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 49 | * struct typec_altmode_ops - Alternate mode specific operations vector | ||
| 50 | * @enter: Operations to be executed with Enter Mode Command | ||
| 51 | * @exit: Operations to be executed with Exit Mode Command | ||
| 52 | * @attention: Callback for Attention Command | ||
| 53 | * @vdm: Callback for SVID specific commands | ||
| 54 | * @notify: Communication channel for platform and the alternate mode | ||
| 55 | * @activate: User callback for Enter/Exit Mode | ||
| 56 | */ | ||
| 57 | struct typec_altmode_ops { | ||
| 58 | int (*enter)(struct typec_altmode *altmode); | ||
| 59 | int (*exit)(struct typec_altmode *altmode); | ||
| 60 | void (*attention)(struct typec_altmode *altmode, u32 vdo); | ||
| 61 | int (*vdm)(struct typec_altmode *altmode, const u32 hdr, | ||
| 62 | const u32 *vdo, int cnt); | ||
| 63 | int (*notify)(struct typec_altmode *altmode, unsigned long conf, | ||
| 64 | void *data); | ||
| 65 | int (*activate)(struct typec_altmode *altmode, int activate); | ||
| 66 | }; | ||
| 67 | |||
| 68 | int typec_altmode_enter(struct typec_altmode *altmode); | ||
| 69 | int typec_altmode_exit(struct typec_altmode *altmode); | ||
| 70 | void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); | ||
| 71 | int typec_altmode_vdm(struct typec_altmode *altmode, | ||
| 72 | const u32 header, const u32 *vdo, int count); | ||
| 73 | int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, | ||
| 74 | void *data); | ||
| 75 | const struct typec_altmode * | ||
| 76 | typec_altmode_get_partner(struct typec_altmode *altmode); | ||
| 77 | |||
| 78 | /* | ||
| 79 | * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C | ||
| 80 | * Specification. SVID specific connector states are expected to follow and | ||
| 81 | * start from the value TYPEC_STATE_MODAL. | ||
| 82 | */ | ||
| 83 | enum { | ||
| 84 | TYPEC_STATE_SAFE, /* USB Safe State */ | ||
| 85 | TYPEC_STATE_USB, /* USB Operation */ | ||
| 86 | TYPEC_STATE_MODAL, /* Alternate Modes */ | ||
| 87 | }; | ||
| 88 | |||
| 89 | /* | ||
| 90 | * For the muxes there is no difference between Accessory Modes and Alternate | ||
| 91 | * Modes, so the Accessory Modes are supplied with specific modal state values | ||
| 92 | * here. Unlike with Alternate Modes, where the mux will be linked with the | ||
| 93 | * alternate mode device, the mux for Accessory Modes will be linked with the | ||
| 94 | * port device instead. | ||
| 95 | * | ||
| 96 | * Port drivers can use TYPEC_MODE_AUDIO and TYPEC_MODE_DEBUG as the mode | ||
| 97 | * value for typec_set_mode() when accessory modes are supported. | ||
| 98 | */ | ||
| 99 | enum { | ||
| 100 | TYPEC_MODE_AUDIO = TYPEC_STATE_MODAL, /* Audio Accessory */ | ||
| 101 | TYPEC_MODE_DEBUG, /* Debug Accessory */ | ||
| 102 | }; | ||
| 103 | |||
| 104 | #define TYPEC_MODAL_STATE(_state_) ((_state_) + TYPEC_STATE_MODAL) | ||
| 105 | |||
| 106 | struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode, | ||
| 107 | enum typec_plug_index index); | ||
| 108 | void typec_altmode_put_plug(struct typec_altmode *plug); | ||
| 109 | |||
| 110 | struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, | ||
| 111 | size_t n, u16 svid, u8 mode); | ||
| 112 | |||
| 113 | struct typec_altmode * | ||
| 114 | typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, | ||
| 115 | struct notifier_block *nb); | ||
| 116 | |||
| 117 | void typec_altmode_unregister_notifier(struct typec_altmode *adev, | ||
| 118 | struct notifier_block *nb); | ||
| 119 | |||
| 120 | /** | ||
| 121 | * typec_altmode_get_orientation - Get cable plug orientation | ||
| 122 | * altmode: Handle to the alternate mode | ||
| 123 | */ | ||
| 124 | static inline enum typec_orientation | ||
| 125 | typec_altmode_get_orientation(struct typec_altmode *altmode) | ||
| 126 | { | ||
| 127 | return typec_get_orientation(typec_altmode2port(altmode)); | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * struct typec_altmode_driver - USB Type-C alternate mode device driver | ||
| 132 | * @id_table: Null terminated array of SVIDs | ||
| 133 | * @probe: Callback for device binding | ||
| 134 | * @remove: Callback for device unbinding | ||
| 135 | * @driver: Device driver model driver | ||
| 136 | * | ||
| 137 | * These drivers will be bind to the partner alternate mode devices. They will | ||
| 138 | * handle all SVID specific communication. | ||
| 139 | */ | ||
| 140 | struct typec_altmode_driver { | ||
| 141 | const struct typec_device_id *id_table; | ||
| 142 | int (*probe)(struct typec_altmode *altmode); | ||
| 143 | void (*remove)(struct typec_altmode *altmode); | ||
| 144 | struct device_driver driver; | ||
| 145 | }; | ||
| 146 | |||
| 147 | #define to_altmode_driver(d) container_of(d, struct typec_altmode_driver, \ | ||
| 148 | driver) | ||
| 149 | |||
| 150 | #define typec_altmode_register_driver(drv) \ | ||
| 151 | __typec_altmode_register_driver(drv, THIS_MODULE) | ||
| 152 | int __typec_altmode_register_driver(struct typec_altmode_driver *drv, | ||
| 153 | struct module *module); | ||
| 154 | void typec_altmode_unregister_driver(struct typec_altmode_driver *drv); | ||
| 155 | |||
| 156 | #define module_typec_altmode_driver(__typec_altmode_driver) \ | ||
| 157 | module_driver(__typec_altmode_driver, typec_altmode_register_driver, \ | ||
| 158 | typec_altmode_unregister_driver) | ||
| 159 | |||
| 160 | #endif /* __USB_TYPEC_ALTMODE_H */ | ||
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 6667f7b491d6..293004499b4d 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c | |||
| @@ -221,5 +221,9 @@ int main(void) | |||
| 221 | DEVID_FIELD(tb_service_id, protocol_version); | 221 | DEVID_FIELD(tb_service_id, protocol_version); |
| 222 | DEVID_FIELD(tb_service_id, protocol_revision); | 222 | DEVID_FIELD(tb_service_id, protocol_revision); |
| 223 | 223 | ||
| 224 | DEVID(typec_device_id); | ||
| 225 | DEVID_FIELD(typec_device_id, svid); | ||
| 226 | DEVID_FIELD(typec_device_id, mode); | ||
| 227 | |||
| 224 | return 0; | 228 | return 0; |
| 225 | } | 229 | } |
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 52fd54a8fe39..7be43697ff84 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c | |||
| @@ -1352,6 +1352,19 @@ static int do_tbsvc_entry(const char *filename, void *symval, char *alias) | |||
| 1352 | } | 1352 | } |
| 1353 | ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); | 1353 | ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); |
| 1354 | 1354 | ||
| 1355 | /* Looks like: typec:idNmN */ | ||
| 1356 | static int do_typec_entry(const char *filename, void *symval, char *alias) | ||
| 1357 | { | ||
| 1358 | DEF_FIELD(symval, typec_device_id, svid); | ||
| 1359 | DEF_FIELD(symval, typec_device_id, mode); | ||
| 1360 | |||
| 1361 | sprintf(alias, "typec:id%04X", svid); | ||
| 1362 | ADD(alias, "m", mode != TYPEC_ANY_MODE, mode); | ||
| 1363 | |||
| 1364 | return 1; | ||
| 1365 | } | ||
| 1366 | ADD_TO_DEVTABLE("typec", typec_device_id, do_typec_entry); | ||
| 1367 | |||
| 1355 | /* Does namelen bytes of name exactly match the symbol? */ | 1368 | /* Does namelen bytes of name exactly match the symbol? */ |
| 1356 | static bool sym_is(const char *name, unsigned namelen, const char *symbol) | 1369 | static bool sym_is(const char *name, unsigned namelen, const char *symbol) |
| 1357 | { | 1370 | { |
