aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>2018-06-27 11:19:50 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-07-02 11:42:36 -0400
commit8a37d87d72f0c69f837229c04d2fcd7117ea57e7 (patch)
tree43cf3111a964678d08fe6638e4459e1c2db50b55
parent4ab8c18d4d67321cc7b660559de17511d4fc0237 (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-typec48
-rw-r--r--Documentation/ABI/testing/sysfs-bus-typec51
-rw-r--r--Documentation/ABI/testing/sysfs-class-typec62
-rw-r--r--Documentation/driver-api/usb/typec_bus.rst136
-rw-r--r--MAINTAINERS11
-rw-r--r--drivers/usb/typec/Makefile2
-rw-r--r--drivers/usb/typec/bus.c401
-rw-r--r--drivers/usb/typec/bus.h38
-rw-r--r--drivers/usb/typec/class.c366
-rw-r--r--include/linux/mod_devicetable.h15
-rw-r--r--include/linux/usb/typec.h14
-rw-r--r--include/linux/usb/typec_altmode.h160
-rw-r--r--scripts/mod/devicetable-offsets.c4
-rw-r--r--scripts/mod/file2alias.c13
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 @@
1These files are deprecated and will be removed. The same files are available
2under /sys/bus/typec (see Documentation/ABI/testing/sysfs-bus-typec).
3
4What: /sys/class/typec/<port|partner|cable>/<dev>/svid
5Date: April 2017
6Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7Description:
8 The SVID (Standard or Vendor ID) assigned by USB-IF for this
9 alternate mode.
10
11What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/
12Date: April 2017
13Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
14Description:
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
20What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/description
21Date: April 2017
22Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
23Description:
24 Shows description of the mode. The description is optional for
25 the drivers, just like with the Billboard Devices.
26
27What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/vdo
28Date: April 2017
29Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
30Description:
31 Shows the VDO in hexadecimal returned by Discover Modes command
32 for this mode.
33
34What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/active
35Date: April 2017
36Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
37Description:
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 @@
1What: /sys/bus/typec/devices/.../active
2Date: July 2018
3Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
4Description:
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
15What: /sys/bus/typec/devices/.../description
16Date: July 2018
17Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
18Description:
19 Shows description of the mode. The description is optional for
20 the drivers, just like with the Billboard Devices.
21
22What: /sys/bus/typec/devices/.../mode
23Date: July 2018
24Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
25Description:
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
39What: /sys/bus/typec/devices/.../svid
40Date: July 2018
41Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
42Description:
43 The Standard or Vendor ID (SVID) assigned by USB-IF for this
44 alternate mode.
45
46What: /sys/bus/typec/devices/.../vdo
47Date: July 2018
48Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
49Description:
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
225Alternate Mode devices. 225USB Type-C port alternate mode devices.
226 226
227The alternate modes will have Standard or Vendor ID (SVID) assigned by USB-IF. 227What: /sys/class/typec/<port>/<alt mode>/supported_roles
228The ports, partners and cable plugs can have alternate modes. A supported SVID
229will consist of a set of modes. Every SVID a port/partner/plug supports will
230have a device created for it, and every supported mode for a supported SVID will
231have its own directory under that device. Below <dev> refers to the device for
232the alternate mode.
233
234What: /sys/class/typec/<port|partner|cable>/<dev>/svid
235Date: April 2017
236Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
237Description:
238 The SVID (Standard or Vendor ID) assigned by USB-IF for this
239 alternate mode.
240
241What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/
242Date: April 2017
243Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
244Description:
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
250What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/description
251Date: April 2017
252Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
253Description:
254 Shows description of the mode. The description is optional for
255 the drivers, just like with the Billboard Devices.
256
257What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/vdo
258Date: April 2017
259Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
260Description:
261 Shows the VDO in hexadecimal returned by Discover Modes command
262 for this mode.
263
264What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/active
265Date: April 2017
266Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
267Description:
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
280What: /sys/class/typec/<port>/<dev>/mode<index>/supported_roles
281Date: April 2017 228Date: April 2017
282Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 229Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
283Description: 230Description:
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
2API for USB Type-C Alternate Mode drivers
3=========================================
4
5Introduction
6------------
7
8Alternate modes require communication with the partner using Vendor Defined
9Messages (VDM) as defined in USB Type-C and USB Power Delivery Specifications.
10The communication is SVID (Standard or Vendor ID) specific, i.e. specific for
11every alternate mode, so every alternate mode will need a custom driver.
12
13USB Type-C bus allows binding a driver to the discovered partner alternate
14modes by using the SVID and the mode number.
15
16USB Type-C Connector Class provides a device for every alternate mode a port
17supports, and separate device for every alternate mode the partner supports.
18The drivers for the alternate modes are bound to the partner alternate mode
19devices, and the port alternate mode devices must be handled by the port
20drivers.
21
22When a new partner alternate mode device is registered, it is linked to the
23alternate mode device of the port that the partner is attached to, that has
24matching SVID and mode. Communication between the port driver and alternate mode
25driver will happen using the same API.
26
27The port alternate mode devices are used as a proxy between the partner and the
28alternate mode drivers, so the port drivers are only expected to pass the SVID
29specific commands from the alternate mode drivers to the partner, and from the
30partners to the alternate mode drivers. No direct SVID specific communication is
31needed from the port drivers, but the port drivers need to provide the operation
32callbacks for the port alternate mode devices, just like the alternate mode
33drivers need to provide them for the partner alternate mode devices.
34
35Usage:
36------
37
38General
39~~~~~~~
40
41By default, the alternate mode drivers are responsible for entering the mode.
42It is also possible to leave the decision about entering the mode to the user
43space (See Documentation/ABI/testing/sysfs-class-typec). Port drivers should not
44enter any modes on their own.
45
46``->vdm`` is the most important callback in the operation callbacks vector. It
47will be used to deliver all the SVID specific commands from the partner to the
48alternate mode driver, and vice versa in case of port drivers. The drivers send
49the SVID specific commands to each other using :c:func:`typec_altmode_vmd()`.
50
51If the communication with the partner using the SVID specific commands results
52in need to reconfigure the pins on the connector, the alternate mode driver
53needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver
54passes the negotiated SVID specific pin configuration value to the function as
55parameter. The bus driver will then configure the mux behind the connector using
56that value as the state value for the mux, and also call blocking notification
57chain to notify the external drivers about the state of the connector that need
58to know it.
59
60NOTE: The SVID specific pin configuration values must always start from
61``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for
62the connector: ``TYPEC_STATE_USB`` and ``TYPEC_STATE_SAFE``. These values are
63reserved by the bus as the first possible values for the state. When the
64alternate 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
66Type-C Specification, and also put the connector back to ``TYPEC_STATE_USB``
67after the mode has been exited.
68
69An example of working definitions for SVID specific pin configurations would
70look like this:
71
72enum {
73 ALTMODEX_CONF_A = TYPEC_STATE_MODAL,
74 ALTMODEX_CONF_B,
75 ...
76};
77
78Helper 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
83Notification chain
84~~~~~~~~~~~~~~~~~~
85
86The drivers for the components that the alternate modes are designed for need to
87get details regarding the results of the negotiation with the partner, and the
88pin configuration of the connector. In case of DisplayPort alternate mode for
89example, the GPU drivers will need to know those details. In case of
90Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and
91so on.
92
93The notification chain is designed for this purpose. The drivers can register
94notifiers with :c:func:`typec_altmode_register_notifier()`.
95
96Cable plug alternate modes
97~~~~~~~~~~~~~~~~~~~~~~~~~~
98
99The alternate mode drivers are not bound to cable plug alternate mode devices,
100only to the partner alternate mode devices. If the alternate mode supports, or
101requires, a cable that responds to SOP Prime, and optionally SOP Double Prime
102messages, the driver for that alternate mode must request handle to the cable
103plug alternate modes using :c:func:`typec_altmode_get_plug()`, and take over
104their control.
105
106Driver API
107----------
108
109Alternate 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
115Alternate 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
121API for the port drivers
122~~~~~~~~~~~~~~~~~~~~~~~~
123
124.. kernel-doc:: drivers/usb/typec/bus.c
125 :functions: typec_match_altmode
126
127Cable Plug operations
128~~~~~~~~~~~~~~~~~~~~~
129
130.. kernel-doc:: drivers/usb/typec/bus.c
131 :functions: typec_altmode_get_plug typec_altmode_put_plug
132
133Notifications
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
14955S: Maintained 14955S: Maintained
14956F: drivers/usb/typec/mux/pi3usb30532.c 14956F: drivers/usb/typec/mux/pi3usb30532.c
14957 14957
14958USB TYPEC SUBSYSTEM 14958USB TYPEC CLASS
14959M: Heikki Krogerus <heikki.krogerus@linux.intel.com> 14959M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
14960L: linux-usb@vger.kernel.org 14960L: linux-usb@vger.kernel.org
14961S: Maintained 14961S: Maintained
@@ -14964,6 +14964,15 @@ F: Documentation/driver-api/usb/typec.rst
14964F: drivers/usb/typec/ 14964F: drivers/usb/typec/
14965F: include/linux/usb/typec.h 14965F: include/linux/usb/typec.h
14966 14966
14967USB TYPEC BUS FOR ALTERNATE MODES
14968M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
14969L: linux-usb@vger.kernel.org
14970S: Maintained
14971F: Documentation/ABI/testing/sysfs-bus-typec
14972F: Documentation/driver-api/usb/typec_bus.rst
14973F: drivers/usb/typec/altmodes/
14974F: include/linux/usb/typec_altmode.h
14975
14967USB UHCI DRIVER 14976USB UHCI DRIVER
14968M: Alan Stern <stern@rowland.harvard.edu> 14977M: Alan Stern <stern@rowland.harvard.edu>
14969L: linux-usb@vger.kernel.org 14978L: 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
2obj-$(CONFIG_TYPEC) += typec.o 2obj-$(CONFIG_TYPEC) += typec.o
3typec-y := class.o mux.o 3typec-y := class.o mux.o bus.o
4obj-$(CONFIG_TYPEC_TCPM) += tcpm.o 4obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
5obj-y += fusb302/ 5obj-y += fusb302/
6obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o 6obj-$(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
13static 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
18static 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 */
51int 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}
81EXPORT_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 */
91int 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}
111EXPORT_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 */
119int 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}
139EXPORT_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 */
148void 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}
155EXPORT_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 */
168int 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}
189EXPORT_SYMBOL_GPL(typec_altmode_vdm);
190
191const struct typec_altmode *
192typec_altmode_get_partner(struct typec_altmode *adev)
193{
194 return &to_altmode(adev)->partner->adev;
195}
196EXPORT_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 */
209struct 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}
221EXPORT_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 */
227void typec_altmode_put_plug(struct typec_altmode *plug)
228{
229 if (plug)
230 put_device(&plug->dev);
231}
232EXPORT_SYMBOL_GPL(typec_altmode_put_plug);
233
234int __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}
245EXPORT_SYMBOL_GPL(__typec_altmode_register_driver);
246
247void typec_altmode_unregister_driver(struct typec_altmode_driver *drv)
248{
249 driver_unregister(&drv->driver);
250}
251EXPORT_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 */
265struct 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}
279EXPORT_SYMBOL_GPL(typec_match_altmode);
280
281/* -------------------------------------------------------------------------- */
282
283static ssize_t
284description_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}
290static DEVICE_ATTR_RO(description);
291
292static struct attribute *typec_attrs[] = {
293 &dev_attr_description.attr,
294 NULL
295};
296ATTRIBUTE_GROUPS(typec);
297
298static 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
311static 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
325static 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
342static 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
348static 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
372static 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
394struct 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
9struct bus_type;
10
11struct 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
31extern struct bus_type typec_bus;
32extern const struct device_type typec_altmode_dev_type;
33extern 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
16struct 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
32struct typec_plug { 16struct 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
37struct typec_cable { 22struct 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
51struct typec_port { 37struct 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
76static const struct device_type typec_partner_dev_type; 62static const struct device_type typec_partner_dev_type;
77static const struct device_type typec_cable_dev_type; 63static const struct device_type typec_cable_dev_type;
78static const struct device_type typec_plug_dev_type; 64static const struct device_type typec_plug_dev_type;
79static 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
86static DEFINE_IDA(typec_index_ida); 70static DEFINE_IDA(typec_index_ida);
87static struct class *typec_class; 71static 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
150static 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
161static 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
187static 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
207static int __typec_port_match(struct device *dev, const void *name)
208{
209 return !strcmp((const char *)name, dev_name(dev));
210}
211
212static 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
218struct typec_altmode *
219typec_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}
253EXPORT_SYMBOL_GPL(typec_altmode_register_notifier);
254
255void 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}
263EXPORT_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 */
174void typec_altmode_update_active(struct typec_altmode *alt, bool active) 273void 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}
186EXPORT_SYMBOL_GPL(typec_altmode_update_active); 293EXPORT_SYMBOL_GPL(typec_altmode_update_active);
187 294
@@ -208,7 +315,7 @@ EXPORT_SYMBOL_GPL(typec_altmode2port);
208static ssize_t 315static ssize_t
209vdo_show(struct device *dev, struct device_attribute *attr, char *buf) 316vdo_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);
217static ssize_t 324static ssize_t
218description_show(struct device *dev, struct device_attribute *attr, char *buf) 325description_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);
226static ssize_t 333static ssize_t
227active_show(struct device *dev, struct device_attribute *attr, char *buf) 334active_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)
234static ssize_t active_store(struct device *dev, struct device_attribute *attr, 341static 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
258supported_roles_show(struct device *dev, struct device_attribute *attr, 381supported_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}
278static DEVICE_ATTR_RO(supported_roles); 401static DEVICE_ATTR_RO(supported_roles);
279 402
280static void typec_altmode_release(struct device *dev) 403static ssize_t
404mode_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}
410static DEVICE_ATTR_RO(mode);
286 411
287static ssize_t svid_show(struct device *dev, struct device_attribute *attr, 412static ssize_t
288 char *buf) 413svid_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}
294static DEVICE_ATTR_RO(svid); 419static DEVICE_ATTR_RO(svid);
295 420
296static struct attribute *typec_altmode_attrs[] = { 421static 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};
300ATTRIBUTE_GROUPS(typec_altmode); 428ATTRIBUTE_GROUPS(typec_altmode);
301 429
302static const struct device_type typec_altmode_dev_type = { 430static 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
444static 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
458static 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
468const 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 *
309typec_register_altmode(struct device *parent, 475typec_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 */
360void typec_unregister_altmode(struct typec_altmode *alt) 540void 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}
365EXPORT_SYMBOL_GPL(typec_unregister_altmode); 547EXPORT_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
1091static const struct device_type typec_port_dev_type = { 1278const 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 */
1288int typec_set_mode(struct typec_port *port, int mode) 1475int 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 *
1307typec_port_register_altmode(struct typec_port *port, 1495typec_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}
1312EXPORT_SYMBOL_GPL(typec_port_register_altmode); 1516EXPORT_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
1429static int __init typec_init(void) 1635static 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}
1434subsys_initcall(typec_init); 1651subsys_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}
1441module_exit(typec_exit); 1659module_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 */
758struct 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
16struct typec_altmode;
17struct typec_partner; 13struct typec_partner;
18struct typec_cable; 14struct typec_cable;
19struct typec_plug; 15struct typec_plug;
20struct typec_port; 16struct typec_port;
21 17
22struct fwnode_handle; 18struct fwnode_handle;
19struct device;
23 20
24enum typec_port_type { 21enum 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
113struct typec_altmode 110struct 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
12struct 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 */
24struct 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
37static inline void typec_altmode_set_drvdata(struct typec_altmode *altmode,
38 void *data)
39{
40 dev_set_drvdata(&altmode->dev, data);
41}
42
43static 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 */
57struct 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
68int typec_altmode_enter(struct typec_altmode *altmode);
69int typec_altmode_exit(struct typec_altmode *altmode);
70void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
71int typec_altmode_vdm(struct typec_altmode *altmode,
72 const u32 header, const u32 *vdo, int count);
73int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf,
74 void *data);
75const struct typec_altmode *
76typec_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 */
83enum {
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 */
99enum {
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
106struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode,
107 enum typec_plug_index index);
108void typec_altmode_put_plug(struct typec_altmode *plug);
109
110struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes,
111 size_t n, u16 svid, u8 mode);
112
113struct typec_altmode *
114typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode,
115 struct notifier_block *nb);
116
117void 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 */
124static inline enum typec_orientation
125typec_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 */
140struct 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)
152int __typec_altmode_register_driver(struct typec_altmode_driver *drv,
153 struct module *module);
154void 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}
1353ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); 1353ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry);
1354 1354
1355/* Looks like: typec:idNmN */
1356static 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}
1366ADD_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? */
1356static bool sym_is(const char *name, unsigned namelen, const char *symbol) 1369static bool sym_is(const char *name, unsigned namelen, const char *symbol)
1357{ 1370{