From dc748b66dbfbbfa187044f007d42d9cc01e5ab11 Mon Sep 17 00:00:00 2001 From: Avi Fishman Date: Wed, 20 Jun 2018 09:33:05 +0300 Subject: dt-bindings: usb: new ehci-npcm7xx dt Device Tree documentation for Nuvoton npcm7xx EHCI. Signed-off-by: Avi Fishman Reviewed-by: Rob Herring Reviewed-by: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/npcm7xx-usb.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/npcm7xx-usb.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/npcm7xx-usb.txt b/Documentation/devicetree/bindings/usb/npcm7xx-usb.txt new file mode 100644 index 000000000000..5a0f1f14fbfa --- /dev/null +++ b/Documentation/devicetree/bindings/usb/npcm7xx-usb.txt @@ -0,0 +1,18 @@ +Nuvoton NPCM7XX SoC USB controllers: +----------------------------- + +EHCI: +----- + +Required properties: +- compatible: "nuvoton,npcm750-ehci" +- interrupts: Should contain the EHCI interrupt +- reg: Physical address and length of the register set for the device + +Example: + + ehci1: usb@f0806000 { + compatible = "nuvoton,npcm750-ehci"; + reg = <0xf0806000 0x1000>; + interrupts = <0 61 4>; + }; -- cgit v1.2.3 From 7a846d3c43b0b6d04300be9ba666b102b57a391a Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:19 +0800 Subject: dt-bindings: connector: add properties for typec Add bindings supported by current typec driver, so user can pass all those properties via dt. Reviewed-by: Rob Herring Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- .../bindings/connector/usb-connector.txt | 44 +++++++++++++++ include/dt-bindings/usb/pd.h | 62 ++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 include/dt-bindings/usb/pd.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt index e1463f14af38..8855bfcfd778 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.txt +++ b/Documentation/devicetree/bindings/connector/usb-connector.txt @@ -15,6 +15,33 @@ Optional properties: - type: size of the connector, should be specified in case of USB-A, USB-B non-fullsize connectors: "mini", "micro". +Optional properties for usb-c-connector: +- power-role: should be one of "source", "sink" or "dual"(DRP) if typec + connector has power support. +- try-power-role: preferred power role if "dual"(DRP) can support Try.SNK + or Try.SRC, should be "sink" for Try.SNK or "source" for Try.SRC. +- data-role: should be one of "host", "device", "dual"(DRD) if typec + connector supports USB data. + +Required properties for usb-c-connector with power delivery support: +- source-pdos: An array of u32 with each entry providing supported power + source data object(PDO), the detailed bit definitions of PDO can be found + in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2 + Source_Capabilities Message, the order of each entry(PDO) should follow + the PD spec chapter 6.4.1. Required for power source and power dual role. + User can specify the source PDO array via PDO_FIXED/BATT/VAR() defined in + dt-bindings/usb/pd.h. +- sink-pdos: An array of u32 with each entry providing supported power + sink data object(PDO), the detailed bit definitions of PDO can be found + in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3 + Sink Capabilities Message, the order of each entry(PDO) should follow + the PD spec chapter 6.4.1. Required for power sink and power dual role. + User can specify the sink PDO array via PDO_FIXED/BATT/VAR() defined in + dt-bindings/usb/pd.h. +- op-sink-microwatt: Sink required operating power in microwatt, if source + can't offer the power, Capability Mismatch is set. Required for power + sink and power dual role. + Required nodes: - any data bus to the connector should be modeled using the OF graph bindings specified in bindings/graph.txt, unless the bus is between parent node and @@ -73,3 +100,20 @@ ccic: s2mm005@33 { }; }; }; + +3. USB-C connector attached to a typec port controller(ptn5110), which has +power delivery support and enables drp. + +typec: ptn5110@50 { + ... + usb_con: connector { + compatible = "usb-c-connector"; + label = "USB-C"; + power-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; + }; +}; diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h new file mode 100644 index 000000000000..7b7a92fefa0a --- /dev/null +++ b/include/dt-bindings/usb/pd.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_POWER_DELIVERY_H +#define __DT_POWER_DELIVERY_H + +/* Power delivery Power Data Object definitions */ +#define PDO_TYPE_FIXED 0 +#define PDO_TYPE_BATT 1 +#define PDO_TYPE_VAR 2 +#define PDO_TYPE_APDO 3 + +#define PDO_TYPE_SHIFT 30 +#define PDO_TYPE_MASK 0x3 + +#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT) + +#define PDO_VOLT_MASK 0x3ff +#define PDO_CURR_MASK 0x3ff +#define PDO_PWR_MASK 0x3ff + +#define PDO_FIXED_DUAL_ROLE (1 << 29) /* Power role swap supported */ +#define PDO_FIXED_SUSPEND (1 << 28) /* USB Suspend supported (Source) */ +#define PDO_FIXED_HIGHER_CAP (1 << 28) /* Requires more than vSafe5V (Sink) */ +#define PDO_FIXED_EXTPOWER (1 << 27) /* Externally powered */ +#define PDO_FIXED_USB_COMM (1 << 26) /* USB communications capable */ +#define PDO_FIXED_DATA_SWAP (1 << 25) /* Data role swap supported */ +#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */ + +#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT) +#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT) + +#define PDO_FIXED(mv, ma, flags) \ + (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \ + PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma)) + +#define VSAFE5V 5000 /* mv units */ + +#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */ +#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ + +#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT) +#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT) +#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT) + +#define PDO_BATT(min_mv, max_mv, max_mw) \ + (PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \ + PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw)) + +#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */ +#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */ + +#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT) +#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT) +#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT) + +#define PDO_VAR(min_mv, max_mv, max_ma) \ + (PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \ + PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma)) + + #endif /* __DT_POWER_DELIVERY_H */ -- cgit v1.2.3 From 9aaf7e4348141c7ca7e47a2155e654f32031b397 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:20 +0800 Subject: dt-bindings: usb: add documentation for typec port controller(TCPCI) TCPCI stands for typec port controller interface, its implementation has full typec port control with power delivery support, it's a standard i2c slave with GPIO input as irq interface, detail see spec "Universal Serial Bus Type-C Port Controller Interface Specification Revision 1.0, Version 1.1" Reviewed-by: Rob Herring Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/typec-tcpci.txt | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/typec-tcpci.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/typec-tcpci.txt b/Documentation/devicetree/bindings/usb/typec-tcpci.txt new file mode 100644 index 000000000000..0dd1469e7318 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-tcpci.txt @@ -0,0 +1,49 @@ +TCPCI(Typec port cotroller interface) binding +--------------------------------------------- + +Required properties: +- compatible: should be set one of following: + - "nxp,ptn5110" for NXP USB PD TCPC PHY IC ptn5110. + +- reg: the i2c slave address of typec port controller device. +- interrupt-parent: the phandle to the interrupt controller which provides + the interrupt. +- interrupts: interrupt specification for tcpci alert. + +Required sub-node: +- connector: The "usb-c-connector" attached to the tcpci chip, the bindings + of connector node are specified in + Documentation/devicetree/bindings/connector/usb-connector.txt + +Example: + +ptn5110@50 { + compatible = "nxp,ptn5110"; + reg = <0x50>; + interrupt-parent = <&gpio3>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + usb_con: connector { + compatible = "usb-c-connector"; + label = "USB-C"; + data-role = "dual"; + power-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + usb_con_ss: endpoint { + remote-endpoint = <&usb3_data_ss>; + }; + }; + }; + }; +}; -- cgit v1.2.3 From 8a37d87d72f0c69f837229c04d2fcd7117ea57e7 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:50 +0300 Subject: 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 Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/obsolete/sysfs-class-typec | 48 ++++ Documentation/ABI/testing/sysfs-bus-typec | 51 ++++ Documentation/ABI/testing/sysfs-class-typec | 62 +---- Documentation/driver-api/usb/typec_bus.rst | 136 +++++++++ MAINTAINERS | 11 +- drivers/usb/typec/Makefile | 2 +- drivers/usb/typec/bus.c | 401 +++++++++++++++++++++++++++ drivers/usb/typec/bus.h | 38 +++ drivers/usb/typec/class.c | 366 +++++++++++++++++++----- include/linux/mod_devicetable.h | 15 + include/linux/usb/typec.h | 14 +- include/linux/usb/typec_altmode.h | 160 +++++++++++ scripts/mod/devicetable-offsets.c | 4 + scripts/mod/file2alias.c | 13 + 14 files changed, 1174 insertions(+), 147 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-class-typec create mode 100644 Documentation/ABI/testing/sysfs-bus-typec create mode 100644 Documentation/driver-api/usb/typec_bus.rst create mode 100644 drivers/usb/typec/bus.c create mode 100644 drivers/usb/typec/bus.h create mode 100644 include/linux/usb/typec_altmode.h (limited to 'Documentation') 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 @@ +These files are deprecated and will be removed. The same files are available +under /sys/bus/typec (see Documentation/ABI/testing/sysfs-bus-typec). + +What: /sys/class/typec///svid +Date: April 2017 +Contact: Heikki Krogerus +Description: + The SVID (Standard or Vendor ID) assigned by USB-IF for this + alternate mode. + +What: /sys/class/typec///mode/ +Date: April 2017 +Contact: Heikki Krogerus +Description: + Every supported mode will have its own directory. The name of + a mode will be "mode" (for example mode1), where + is the actual index to the mode VDO returned by Discover Modes + USB power delivery command. + +What: /sys/class/typec///mode/description +Date: April 2017 +Contact: Heikki Krogerus +Description: + Shows description of the mode. The description is optional for + the drivers, just like with the Billboard Devices. + +What: /sys/class/typec///mode/vdo +Date: April 2017 +Contact: Heikki Krogerus +Description: + Shows the VDO in hexadecimal returned by Discover Modes command + for this mode. + +What: /sys/class/typec///mode/active +Date: April 2017 +Contact: Heikki Krogerus +Description: + Shows if the mode is active or not. The attribute can be used + for entering/exiting the mode with partners and cable plugs, and + with the port alternate modes it can be used for disabling + support for specific alternate modes. Entering/exiting modes is + supported as synchronous operation so write(2) to the attribute + does not return until the enter/exit mode operation has + finished. The attribute is notified when the mode is + entered/exited so poll(2) on the attribute wakes up. + Entering/exiting a mode will also generate uevent KOBJ_CHANGE. + + 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 @@ +What: /sys/bus/typec/devices/.../active +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows if the mode is active or not. The attribute can be used + for entering/exiting the mode. Entering/exiting modes is + supported as synchronous operation so write(2) to the attribute + does not return until the enter/exit mode operation has + finished. The attribute is notified when the mode is + entered/exited so poll(2) on the attribute wakes up. + Entering/exiting a mode will also generate uevent KOBJ_CHANGE. + + Valid values are boolean. + +What: /sys/bus/typec/devices/.../description +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows description of the mode. The description is optional for + the drivers, just like with the Billboard Devices. + +What: /sys/bus/typec/devices/.../mode +Date: July 2018 +Contact: Heikki Krogerus +Description: + The index number of the mode returned by Discover Modes USB + Power Delivery command. Depending on the alternate mode, the + mode index may be significant. + + With some alternate modes (SVIDs), the mode index is assigned + for specific functionality in the specification for that + alternate mode. + + With other alternate modes, the mode index values are not + assigned, and can not be therefore used for identification. When + the mode index is not assigned, identifying the alternate mode + must be done with either mode VDO or the description. + +What: /sys/bus/typec/devices/.../svid +Date: July 2018 +Contact: Heikki Krogerus +Description: + The Standard or Vendor ID (SVID) assigned by USB-IF for this + alternate mode. + +What: /sys/bus/typec/devices/.../vdo +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows the VDO in hexadecimal returned by Discover Modes command + 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: available. The value can be polled. -Alternate Mode devices. +USB Type-C port alternate mode devices. -The alternate modes will have Standard or Vendor ID (SVID) assigned by USB-IF. -The ports, partners and cable plugs can have alternate modes. A supported SVID -will consist of a set of modes. Every SVID a port/partner/plug supports will -have a device created for it, and every supported mode for a supported SVID will -have its own directory under that device. Below refers to the device for -the alternate mode. - -What: /sys/class/typec///svid -Date: April 2017 -Contact: Heikki Krogerus -Description: - The SVID (Standard or Vendor ID) assigned by USB-IF for this - alternate mode. - -What: /sys/class/typec///mode/ -Date: April 2017 -Contact: Heikki Krogerus -Description: - Every supported mode will have its own directory. The name of - a mode will be "mode" (for example mode1), where - is the actual index to the mode VDO returned by Discover Modes - USB power delivery command. - -What: /sys/class/typec///mode/description -Date: April 2017 -Contact: Heikki Krogerus -Description: - Shows description of the mode. The description is optional for - the drivers, just like with the Billboard Devices. - -What: /sys/class/typec///mode/vdo -Date: April 2017 -Contact: Heikki Krogerus -Description: - Shows the VDO in hexadecimal returned by Discover Modes command - for this mode. - -What: /sys/class/typec///mode/active -Date: April 2017 -Contact: Heikki Krogerus -Description: - Shows if the mode is active or not. The attribute can be used - for entering/exiting the mode with partners and cable plugs, and - with the port alternate modes it can be used for disabling - support for specific alternate modes. Entering/exiting modes is - supported as synchronous operation so write(2) to the attribute - does not return until the enter/exit mode operation has - finished. The attribute is notified when the mode is - entered/exited so poll(2) on the attribute wakes up. - Entering/exiting a mode will also generate uevent KOBJ_CHANGE. - - Valid values: yes, no - -What: /sys/class/typec///mode/supported_roles +What: /sys/class/typec///supported_roles Date: April 2017 Contact: Heikki Krogerus Description: Space separated list of the supported roles. - This attribute is available for the devices describing the - alternate modes a port supports, and it will not be exposed with - the devices presenting the alternate modes the partners or cable - plugs support. - 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 @@ + +API for USB Type-C Alternate Mode drivers +========================================= + +Introduction +------------ + +Alternate modes require communication with the partner using Vendor Defined +Messages (VDM) as defined in USB Type-C and USB Power Delivery Specifications. +The communication is SVID (Standard or Vendor ID) specific, i.e. specific for +every alternate mode, so every alternate mode will need a custom driver. + +USB Type-C bus allows binding a driver to the discovered partner alternate +modes by using the SVID and the mode number. + +USB Type-C Connector Class provides a device for every alternate mode a port +supports, and separate device for every alternate mode the partner supports. +The drivers for the alternate modes are bound to the partner alternate mode +devices, and the port alternate mode devices must be handled by the port +drivers. + +When a new partner alternate mode device is registered, it is linked to the +alternate mode device of the port that the partner is attached to, that has +matching SVID and mode. Communication between the port driver and alternate mode +driver will happen using the same API. + +The port alternate mode devices are used as a proxy between the partner and the +alternate mode drivers, so the port drivers are only expected to pass the SVID +specific commands from the alternate mode drivers to the partner, and from the +partners to the alternate mode drivers. No direct SVID specific communication is +needed from the port drivers, but the port drivers need to provide the operation +callbacks for the port alternate mode devices, just like the alternate mode +drivers need to provide them for the partner alternate mode devices. + +Usage: +------ + +General +~~~~~~~ + +By default, the alternate mode drivers are responsible for entering the mode. +It is also possible to leave the decision about entering the mode to the user +space (See Documentation/ABI/testing/sysfs-class-typec). Port drivers should not +enter any modes on their own. + +``->vdm`` is the most important callback in the operation callbacks vector. It +will be used to deliver all the SVID specific commands from the partner to the +alternate mode driver, and vice versa in case of port drivers. The drivers send +the SVID specific commands to each other using :c:func:`typec_altmode_vmd()`. + +If the communication with the partner using the SVID specific commands results +in need to reconfigure the pins on the connector, the alternate mode driver +needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver +passes the negotiated SVID specific pin configuration value to the function as +parameter. The bus driver will then configure the mux behind the connector using +that value as the state value for the mux, and also call blocking notification +chain to notify the external drivers about the state of the connector that need +to know it. + +NOTE: The SVID specific pin configuration values must always start from +``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for +the connector: ``TYPEC_STATE_USB`` and ``TYPEC_STATE_SAFE``. These values are +reserved by the bus as the first possible values for the state. When the +alternate mode is entered, the bus will put the connector into +``TYPEC_STATE_SAFE`` before sending Enter or Exit Mode command as defined in USB +Type-C Specification, and also put the connector back to ``TYPEC_STATE_USB`` +after the mode has been exited. + +An example of working definitions for SVID specific pin configurations would +look like this: + +enum { + ALTMODEX_CONF_A = TYPEC_STATE_MODAL, + ALTMODEX_CONF_B, + ... +}; + +Helper macro ``TYPEC_MODAL_STATE()`` can also be used: + +#define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0); +#define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1); + +Notification chain +~~~~~~~~~~~~~~~~~~ + +The drivers for the components that the alternate modes are designed for need to +get details regarding the results of the negotiation with the partner, and the +pin configuration of the connector. In case of DisplayPort alternate mode for +example, the GPU drivers will need to know those details. In case of +Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and +so on. + +The notification chain is designed for this purpose. The drivers can register +notifiers with :c:func:`typec_altmode_register_notifier()`. + +Cable plug alternate modes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The alternate mode drivers are not bound to cable plug alternate mode devices, +only to the partner alternate mode devices. If the alternate mode supports, or +requires, a cable that responds to SOP Prime, and optionally SOP Double Prime +messages, the driver for that alternate mode must request handle to the cable +plug alternate modes using :c:func:`typec_altmode_get_plug()`, and take over +their control. + +Driver API +---------- + +Alternate mode driver registering/unregistering +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_altmode_register_driver typec_altmode_unregister_driver + +Alternate mode driver operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_altmode_enter typec_altmode_exit typec_altmode_attention typec_altmode_vdm typec_altmode_notify + +API for the port drivers +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_match_altmode + +Cable Plug operations +~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_altmode_get_plug typec_altmode_put_plug + +Notifications +~~~~~~~~~~~~~ +.. kernel-doc:: drivers/usb/typec/class.c + :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 S: Maintained F: drivers/usb/typec/mux/pi3usb30532.c -USB TYPEC SUBSYSTEM +USB TYPEC CLASS M: Heikki Krogerus L: linux-usb@vger.kernel.org S: Maintained @@ -14964,6 +14964,15 @@ F: Documentation/driver-api/usb/typec.rst F: drivers/usb/typec/ F: include/linux/usb/typec.h +USB TYPEC BUS FOR ALTERNATE MODES +M: Heikki Krogerus +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-typec +F: Documentation/driver-api/usb/typec_bus.rst +F: drivers/usb/typec/altmodes/ +F: include/linux/usb/typec_altmode.h + USB UHCI DRIVER M: Alan Stern 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 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o +typec-y := class.o mux.o bus.o obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-y += fusb302/ 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 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Bus for USB Type-C Alternate Modes + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus + */ + +#include + +#include "bus.h" + +static inline int typec_altmode_set_mux(struct altmode *alt, u8 state) +{ + return alt->mux ? alt->mux->set(alt->mux, state) : 0; +} + +static int typec_altmode_set_state(struct typec_altmode *adev, int state) +{ + bool is_port = is_typec_port(adev->dev.parent); + struct altmode *port_altmode; + int ret; + + port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; + + ret = typec_altmode_set_mux(port_altmode, state); + if (ret) + return ret; + + blocking_notifier_call_chain(&port_altmode->nh, state, NULL); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* Common API */ + +/** + * typec_altmode_notify - Communication between the OS and alternate mode driver + * @adev: Handle to the alternate mode + * @conf: Alternate mode specific configuration value + * @data: Alternate mode specific data + * + * The primary purpose for this function is to allow the alternate mode drivers + * to tell which pin configuration has been negotiated with the partner. That + * information will then be used for example to configure the muxes. + * Communication to the other direction is also possible, and low level device + * drivers can also send notifications to the alternate mode drivers. The actual + * communication will be specific for every SVID. + */ +int typec_altmode_notify(struct typec_altmode *adev, + unsigned long conf, void *data) +{ + bool is_port = is_typec_port(adev->dev.parent); + struct altmode *altmode; + struct altmode *partner; + int ret; + + if (!adev) + return 0; + + altmode = to_altmode(adev); + + if (!altmode->partner) + return -ENODEV; + + partner = altmode->partner; + + ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf); + if (ret) + return ret; + + blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh, + conf, data); + + if (partner->adev.ops && partner->adev.ops->notify) + return partner->adev.ops->notify(&partner->adev, conf, data); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_altmode_notify); + +/** + * typec_altmode_enter - Enter Mode + * @adev: The alternate mode + * + * The alternate mode drivers use this function to enter mode. The port drivers + * use this to inform the alternate mode drivers that the partner has initiated + * Enter Mode command. + */ +int typec_altmode_enter(struct typec_altmode *adev) +{ + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev = &partner->adev; + int ret; + + if (!adev || adev->active) + return 0; + + if (!pdev->ops || !pdev->ops->enter) + return -EOPNOTSUPP; + + /* Moving to USB Safe State */ + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); + if (ret) + return ret; + + /* Enter Mode */ + return pdev->ops->enter(pdev); +} +EXPORT_SYMBOL_GPL(typec_altmode_enter); + +/** + * typec_altmode_exit - Exit Mode + * @adev: The alternate mode + * + * The partner of @adev has initiated Exit Mode command. + */ +int typec_altmode_exit(struct typec_altmode *adev) +{ + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev = &partner->adev; + int ret; + + if (!adev || !adev->active) + return 0; + + if (!pdev->ops || !pdev->ops->enter) + return -EOPNOTSUPP; + + /* Moving to USB Safe State */ + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); + if (ret) + return ret; + + /* Exit Mode command */ + return pdev->ops->exit(pdev); +} +EXPORT_SYMBOL_GPL(typec_altmode_exit); + +/** + * typec_altmode_attention - Attention command + * @adev: The alternate mode + * @vdo: VDO for the Attention command + * + * Notifies the partner of @adev about Attention command. + */ +void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) +{ + struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; + + if (pdev->ops && pdev->ops->attention) + pdev->ops->attention(pdev, vdo); +} +EXPORT_SYMBOL_GPL(typec_altmode_attention); + +/** + * typec_altmode_vdm - Send Vendor Defined Messages (VDM) to the partner + * @adev: Alternate mode handle + * @header: VDM Header + * @vdo: Array of Vendor Defined Data Objects + * @count: Number of Data Objects + * + * The alternate mode drivers use this function for SVID specific communication + * with the partner. The port drivers use it to deliver the Structured VDMs + * received from the partners to the alternate mode drivers. + */ +int typec_altmode_vdm(struct typec_altmode *adev, + const u32 header, const u32 *vdo, int count) +{ + struct typec_altmode *pdev; + struct altmode *altmode; + + if (!adev) + return 0; + + altmode = to_altmode(adev); + + if (!altmode->partner) + return -ENODEV; + + pdev = &altmode->partner->adev; + + if (!pdev->ops || !pdev->ops->vdm) + return -EOPNOTSUPP; + + return pdev->ops->vdm(pdev, header, vdo, count); +} +EXPORT_SYMBOL_GPL(typec_altmode_vdm); + +const struct typec_altmode * +typec_altmode_get_partner(struct typec_altmode *adev) +{ + return &to_altmode(adev)->partner->adev; +} +EXPORT_SYMBOL_GPL(typec_altmode_get_partner); + +/* -------------------------------------------------------------------------- */ +/* API for the alternate mode drivers */ + +/** + * typec_altmode_get_plug - Find cable plug alternate mode + * @adev: Handle to partner alternate mode + * @index: Cable plug index + * + * Increment reference count for cable plug alternate mode device. Returns + * handle to the cable plug alternate mode, or NULL if none is found. + */ +struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *adev, + enum typec_plug_index index) +{ + struct altmode *port = to_altmode(adev)->partner; + + if (port->plug[index]) { + get_device(&port->plug[index]->adev.dev); + return &port->plug[index]->adev; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(typec_altmode_get_plug); + +/** + * typec_altmode_put_plug - Decrement cable plug alternate mode reference count + * @plug: Handle to the cable plug alternate mode + */ +void typec_altmode_put_plug(struct typec_altmode *plug) +{ + if (plug) + put_device(&plug->dev); +} +EXPORT_SYMBOL_GPL(typec_altmode_put_plug); + +int __typec_altmode_register_driver(struct typec_altmode_driver *drv, + struct module *module) +{ + if (!drv->probe) + return -EINVAL; + + drv->driver.owner = module; + drv->driver.bus = &typec_bus; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__typec_altmode_register_driver); + +void typec_altmode_unregister_driver(struct typec_altmode_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(typec_altmode_unregister_driver); + +/* -------------------------------------------------------------------------- */ +/* API for the port drivers */ + +/** + * typec_match_altmode - Match SVID to an array of alternate modes + * @altmodes: Array of alternate modes + * @n: Number of elements in the array, or -1 for NULL termiated arrays + * @svid: Standard or Vendor ID to match with + * + * Return pointer to an alternate mode with SVID mathing @svid, or NULL when no + * match is found. + */ +struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, + size_t n, u16 svid, u8 mode) +{ + int i; + + for (i = 0; i < n; i++) { + if (!altmodes[i]) + break; + if (altmodes[i]->svid == svid && altmodes[i]->mode == mode) + return altmodes[i]; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(typec_match_altmode); + +/* -------------------------------------------------------------------------- */ + +static ssize_t +description_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_altmode *alt = to_typec_altmode(dev); + + return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); +} +static DEVICE_ATTR_RO(description); + +static struct attribute *typec_attrs[] = { + &dev_attr_description.attr, + NULL +}; +ATTRIBUTE_GROUPS(typec); + +static int typec_match(struct device *dev, struct device_driver *driver) +{ + struct typec_altmode_driver *drv = to_altmode_driver(driver); + struct typec_altmode *altmode = to_typec_altmode(dev); + const struct typec_device_id *id; + + for (id = drv->id_table; id->svid; id++) + if (id->svid == altmode->svid && + (id->mode == TYPEC_ANY_MODE || id->mode == altmode->mode)) + return 1; + return 0; +} + +static int typec_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct typec_altmode *altmode = to_typec_altmode(dev); + + if (add_uevent_var(env, "SVID=%04X", altmode->svid)) + return -ENOMEM; + + if (add_uevent_var(env, "MODE=%u", altmode->mode)) + return -ENOMEM; + + return add_uevent_var(env, "MODALIAS=typec:id%04Xm%02X", + altmode->svid, altmode->mode); +} + +static int typec_altmode_create_links(struct altmode *alt) +{ + struct device *port_dev = &alt->partner->adev.dev; + struct device *dev = &alt->adev.dev; + int err; + + err = sysfs_create_link(&dev->kobj, &port_dev->kobj, "port"); + if (err) + return err; + + err = sysfs_create_link(&port_dev->kobj, &dev->kobj, "partner"); + if (err) + sysfs_remove_link(&dev->kobj, "port"); + + return err; +} + +static void typec_altmode_remove_links(struct altmode *alt) +{ + sysfs_remove_link(&alt->partner->adev.dev.kobj, "partner"); + sysfs_remove_link(&alt->adev.dev.kobj, "port"); +} + +static int typec_probe(struct device *dev) +{ + struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); + struct typec_altmode *adev = to_typec_altmode(dev); + struct altmode *altmode = to_altmode(adev); + int ret; + + /* Fail if the port does not support the alternate mode */ + if (!altmode->partner) + return -ENODEV; + + ret = typec_altmode_create_links(altmode); + if (ret) { + dev_warn(dev, "failed to create symlinks\n"); + return ret; + } + + ret = drv->probe(adev); + if (ret) + typec_altmode_remove_links(altmode); + + return ret; +} + +static int typec_remove(struct device *dev) +{ + struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); + struct typec_altmode *adev = to_typec_altmode(dev); + struct altmode *altmode = to_altmode(adev); + + typec_altmode_remove_links(altmode); + + if (drv->remove) + drv->remove(to_typec_altmode(dev)); + + if (adev->active) { + WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE)); + typec_altmode_update_active(adev, false); + } + + adev->desc = NULL; + adev->ops = NULL; + + return 0; +} + +struct bus_type typec_bus = { + .name = "typec", + .dev_groups = typec_groups, + .match = typec_match, + .uevent = typec_uevent, + .probe = typec_probe, + .remove = typec_remove, +}; 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 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_ALTMODE_H__ +#define __USB_TYPEC_ALTMODE_H__ + +#include +#include + +struct bus_type; + +struct altmode { + unsigned int id; + struct typec_altmode adev; + struct typec_mux *mux; + + enum typec_port_data roles; + + struct attribute *attrs[5]; + char group_name[6]; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct altmode *partner; + struct altmode *plug[2]; + + struct blocking_notifier_head nh; +}; + +#define to_altmode(d) container_of(d, struct altmode, adev) + +extern struct bus_type typec_bus; +extern const struct device_type typec_altmode_dev_type; +extern const struct device_type typec_port_dev_type; + +#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) +#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) + +#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 @@ #include #include #include -#include -#include -struct typec_altmode { - struct device dev; - u16 svid; - u8 mode; - - u32 vdo; - char *desc; - enum typec_port_type roles; - unsigned int active:1; - - struct attribute *attrs[5]; - char group_name[6]; - struct attribute_group group; - const struct attribute_group *groups[2]; -}; +#include "bus.h" struct typec_plug { struct device dev; enum typec_plug_index index; + struct ida mode_ids; }; struct typec_cable { @@ -46,11 +31,13 @@ struct typec_partner { unsigned int usb_pd:1; struct usb_pd_identity *identity; enum typec_accessory accessory; + struct ida mode_ids; }; struct typec_port { unsigned int id; struct device dev; + struct ida mode_ids; int prefer_role; enum typec_data_role data_role; @@ -71,17 +58,14 @@ struct typec_port { #define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) #define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) #define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) -#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev) static const struct device_type typec_partner_dev_type; static const struct device_type typec_cable_dev_type; static const struct device_type typec_plug_dev_type; -static const struct device_type typec_port_dev_type; #define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) #define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) #define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) -#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) static DEFINE_IDA(typec_index_ida); static struct class *typec_class; @@ -163,25 +147,148 @@ static void typec_report_identity(struct device *dev) /* ------------------------------------------------------------------------- */ /* Alternate Modes */ +static int altmode_match(struct device *dev, void *data) +{ + struct typec_altmode *adev = to_typec_altmode(dev); + struct typec_device_id *id = data; + + if (!is_typec_altmode(dev)) + return 0; + + return ((adev->svid == id->svid) && (adev->mode == id->mode)); +} + +static void typec_altmode_set_partner(struct altmode *altmode) +{ + struct typec_altmode *adev = &altmode->adev; + struct typec_device_id id = { adev->svid, adev->mode, }; + struct typec_port *port = typec_altmode2port(adev); + struct altmode *partner; + struct device *dev; + + dev = device_find_child(&port->dev, &id, altmode_match); + if (!dev) + return; + + /* Bind the port alt mode to the partner/plug alt mode. */ + partner = to_altmode(to_typec_altmode(dev)); + altmode->partner = partner; + + /* Bind the partner/plug alt mode to the port alt mode. */ + if (is_typec_plug(adev->dev.parent)) { + struct typec_plug *plug = to_typec_plug(adev->dev.parent); + + partner->plug[plug->index] = altmode; + } else { + partner->partner = altmode; + } +} + +static void typec_altmode_put_partner(struct altmode *altmode) +{ + struct altmode *partner = altmode->partner; + struct typec_altmode *adev; + + if (!partner) + return; + + adev = &partner->adev; + + if (is_typec_plug(adev->dev.parent)) { + struct typec_plug *plug = to_typec_plug(adev->dev.parent); + + partner->plug[plug->index] = NULL; + } else { + partner->partner = NULL; + } + put_device(&adev->dev); +} + +static int __typec_port_match(struct device *dev, const void *name) +{ + return !strcmp((const char *)name, dev_name(dev)); +} + +static void *typec_port_match(struct device_connection *con, int ep, void *data) +{ + return class_find_device(typec_class, NULL, con->endpoint[ep], + __typec_port_match); +} + +struct typec_altmode * +typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, + struct notifier_block *nb) +{ + struct typec_device_id id = { svid, mode, }; + struct device *altmode_dev; + struct device *port_dev; + struct altmode *altmode; + int ret; + + /* Find the port linked to the caller */ + port_dev = device_connection_find_match(dev, NULL, NULL, + typec_port_match); + if (IS_ERR_OR_NULL(port_dev)) + return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV); + + /* Find the altmode with matching svid */ + altmode_dev = device_find_child(port_dev, &id, altmode_match); + + put_device(port_dev); + + if (!altmode_dev) + return ERR_PTR(-ENODEV); + + altmode = to_altmode(to_typec_altmode(altmode_dev)); + + /* Register notifier */ + ret = blocking_notifier_chain_register(&altmode->nh, nb); + if (ret) { + put_device(altmode_dev); + return ERR_PTR(ret); + } + + return &altmode->adev; +} +EXPORT_SYMBOL_GPL(typec_altmode_register_notifier); + +void typec_altmode_unregister_notifier(struct typec_altmode *adev, + struct notifier_block *nb) +{ + struct altmode *altmode = to_altmode(adev); + + blocking_notifier_chain_unregister(&altmode->nh, nb); + put_device(&adev->dev); +} +EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier); + /** * typec_altmode_update_active - Report Enter/Exit mode - * @alt: Handle to the alternate mode + * @adev: Handle to the alternate mode * @active: True when the mode has been entered * * If a partner or cable plug executes Enter/Exit Mode command successfully, the * drivers use this routine to report the updated state of the mode. */ -void typec_altmode_update_active(struct typec_altmode *alt, bool active) +void typec_altmode_update_active(struct typec_altmode *adev, bool active) { char dir[6]; - if (alt->active == active) + if (adev->active == active) return; - alt->active = active; - snprintf(dir, sizeof(dir), "mode%d", alt->mode); - sysfs_notify(&alt->dev.kobj, dir, "active"); - kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE); + if (!is_typec_port(adev->dev.parent)) { + if (!active) + module_put(adev->dev.driver->owner); + else + WARN_ON(!try_module_get(adev->dev.driver->owner)); + } + + adev->active = active; + snprintf(dir, sizeof(dir), "mode%d", adev->mode); + sysfs_notify(&adev->dev.kobj, dir, "active"); + sysfs_notify(&adev->dev.kobj, NULL, "active"); + kobject_uevent(&adev->dev.kobj, KOBJ_CHANGE); } EXPORT_SYMBOL_GPL(typec_altmode_update_active); @@ -208,7 +315,7 @@ EXPORT_SYMBOL_GPL(typec_altmode2port); static ssize_t vdo_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *alt = to_typec_altmode(dev); return sprintf(buf, "0x%08x\n", alt->vdo); } @@ -217,7 +324,7 @@ static DEVICE_ATTR_RO(vdo); static ssize_t description_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *alt = to_typec_altmode(dev); return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); } @@ -226,7 +333,7 @@ static DEVICE_ATTR_RO(description); static ssize_t active_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *alt = to_typec_altmode(dev); return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); } @@ -234,21 +341,37 @@ active_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t active_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct typec_altmode *alt = to_altmode(dev); - struct typec_port *port = typec_altmode2port(alt); - bool activate; + struct typec_altmode *adev = to_typec_altmode(dev); + struct altmode *altmode = to_altmode(adev); + bool enter; int ret; - if (!port->cap->activate_mode) - return -EOPNOTSUPP; - - ret = kstrtobool(buf, &activate); + ret = kstrtobool(buf, &enter); if (ret) return ret; - ret = port->cap->activate_mode(port->cap, alt->mode, activate); - if (ret) - return ret; + if (adev->active == enter) + return size; + + if (is_typec_port(adev->dev.parent)) { + typec_altmode_update_active(adev, enter); + + /* Make sure that the partner exits the mode before disabling */ + if (altmode->partner && !enter && altmode->partner->adev.active) + typec_altmode_exit(&altmode->partner->adev); + } else if (altmode->partner) { + if (enter && !altmode->partner->adev.active) { + dev_warn(dev, "port has the mode disabled\n"); + return -EPERM; + } + } + + /* Note: If there is no driver, the mode will not be entered */ + if (adev->ops && adev->ops->activate) { + ret = adev->ops->activate(adev, enter); + if (ret) + return ret; + } return size; } @@ -258,7 +381,7 @@ static ssize_t supported_roles_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct altmode *alt = to_altmode(to_typec_altmode(dev)); ssize_t ret; switch (alt->roles) { @@ -277,29 +400,72 @@ supported_roles_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(supported_roles); -static void typec_altmode_release(struct device *dev) +static ssize_t +mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *adev = to_typec_altmode(dev); - kfree(alt); + return sprintf(buf, "%u\n", adev->mode); } +static DEVICE_ATTR_RO(mode); -static ssize_t svid_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t +svid_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *adev = to_typec_altmode(dev); - return sprintf(buf, "%04x\n", alt->svid); + return sprintf(buf, "%04x\n", adev->svid); } static DEVICE_ATTR_RO(svid); static struct attribute *typec_altmode_attrs[] = { + &dev_attr_active.attr, + &dev_attr_mode.attr, &dev_attr_svid.attr, + &dev_attr_vdo.attr, NULL }; ATTRIBUTE_GROUPS(typec_altmode); -static const struct device_type typec_altmode_dev_type = { +static int altmode_id_get(struct device *dev) +{ + struct ida *ids; + + if (is_typec_partner(dev)) + ids = &to_typec_partner(dev)->mode_ids; + else if (is_typec_plug(dev)) + ids = &to_typec_plug(dev)->mode_ids; + else + ids = &to_typec_port(dev)->mode_ids; + + return ida_simple_get(ids, 0, 0, GFP_KERNEL); +} + +static void altmode_id_remove(struct device *dev, int id) +{ + struct ida *ids; + + if (is_typec_partner(dev)) + ids = &to_typec_partner(dev)->mode_ids; + else if (is_typec_plug(dev)) + ids = &to_typec_plug(dev)->mode_ids; + else + ids = &to_typec_port(dev)->mode_ids; + + ida_simple_remove(ids, id); +} + +static void typec_altmode_release(struct device *dev) +{ + struct altmode *alt = to_altmode(to_typec_altmode(dev)); + + typec_altmode_put_partner(alt); + + altmode_id_remove(alt->adev.dev.parent, alt->id); + kfree(alt); +} + +const struct device_type typec_altmode_dev_type = { .name = "typec_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release, @@ -309,58 +475,74 @@ static struct typec_altmode * typec_register_altmode(struct device *parent, const struct typec_altmode_desc *desc) { - struct typec_altmode *alt; + unsigned int id = altmode_id_get(parent); + bool is_port = is_typec_port(parent); + struct altmode *alt; int ret; alt = kzalloc(sizeof(*alt), GFP_KERNEL); if (!alt) return ERR_PTR(-ENOMEM); - alt->svid = desc->svid; - alt->mode = desc->mode; - alt->vdo = desc->vdo; + alt->adev.svid = desc->svid; + alt->adev.mode = desc->mode; + alt->adev.vdo = desc->vdo; alt->roles = desc->roles; + alt->id = id; alt->attrs[0] = &dev_attr_vdo.attr; alt->attrs[1] = &dev_attr_description.attr; alt->attrs[2] = &dev_attr_active.attr; - if (is_typec_port(parent)) + if (is_port) { alt->attrs[3] = &dev_attr_supported_roles.attr; + alt->adev.active = true; /* Enabled by default */ + } sprintf(alt->group_name, "mode%d", desc->mode); alt->group.name = alt->group_name; alt->group.attrs = alt->attrs; alt->groups[0] = &alt->group; - alt->dev.parent = parent; - alt->dev.groups = alt->groups; - alt->dev.type = &typec_altmode_dev_type; - dev_set_name(&alt->dev, "%s-%04x:%u", dev_name(parent), - alt->svid, alt->mode); + alt->adev.dev.parent = parent; + alt->adev.dev.groups = alt->groups; + alt->adev.dev.type = &typec_altmode_dev_type; + dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); + + /* Link partners and plugs with the ports */ + if (is_port) + BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh); + else + typec_altmode_set_partner(alt); + + /* The partners are bind to drivers */ + if (is_typec_partner(parent)) + alt->adev.dev.bus = &typec_bus; - ret = device_register(&alt->dev); + ret = device_register(&alt->adev.dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", ret); - put_device(&alt->dev); + put_device(&alt->adev.dev); return ERR_PTR(ret); } - return alt; + return &alt->adev; } /** * typec_unregister_altmode - Unregister Alternate Mode - * @alt: The alternate mode to be unregistered + * @adev: The alternate mode to be unregistered * * Unregister device created with typec_partner_register_altmode(), * typec_plug_register_altmode() or typec_port_register_altmode(). */ -void typec_unregister_altmode(struct typec_altmode *alt) +void typec_unregister_altmode(struct typec_altmode *adev) { - if (!IS_ERR_OR_NULL(alt)) - device_unregister(&alt->dev); + if (IS_ERR_OR_NULL(adev)) + return; + typec_mux_put(to_altmode(adev)->mux); + device_unregister(&adev->dev); } EXPORT_SYMBOL_GPL(typec_unregister_altmode); @@ -398,6 +580,7 @@ static void typec_partner_release(struct device *dev) { struct typec_partner *partner = to_typec_partner(dev); + ida_destroy(&partner->mode_ids); kfree(partner); } @@ -463,6 +646,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, if (!partner) return ERR_PTR(-ENOMEM); + ida_init(&partner->mode_ids); partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; @@ -511,6 +695,7 @@ static void typec_plug_release(struct device *dev) { struct typec_plug *plug = to_typec_plug(dev); + ida_destroy(&plug->mode_ids); kfree(plug); } @@ -563,6 +748,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, sprintf(name, "plug%d", desc->index); + ida_init(&plug->mode_ids); plug->index = desc->index; plug->dev.class = typec_class; plug->dev.parent = &cable->dev; @@ -1083,12 +1269,13 @@ static void typec_release(struct device *dev) struct typec_port *port = to_typec_port(dev); ida_simple_remove(&typec_index_ida, port->id); + ida_destroy(&port->mode_ids); typec_switch_put(port->sw); typec_mux_put(port->mux); kfree(port); } -static const struct device_type typec_port_dev_type = { +const struct device_type typec_port_dev_type = { .name = "typec_port", .groups = typec_groups, .uevent = typec_uevent, @@ -1279,11 +1466,11 @@ EXPORT_SYMBOL_GPL(typec_get_orientation); /** * typec_set_mode - Set mode of operation for USB Type-C connector - * @port: USB Type-C port for the connector - * @mode: Operation mode for the connector + * @port: USB Type-C connector + * @mode: Accessory Mode, USB Operation or Safe State * - * Set mode @mode for @port. This function will configure the muxes needed to - * enter @mode. + * Configure @port for Accessory Mode @mode. This function will configure the + * muxes needed for @mode. */ int typec_set_mode(struct typec_port *port, int mode) { @@ -1297,6 +1484,7 @@ EXPORT_SYMBOL_GPL(typec_set_mode); * typec_port_register_altmode - Register USB Type-C Port Alternate Mode * @port: USB Type-C Port that supports the alternate mode * @desc: Description of the alternate mode + * @drvdata: Private pointer to driver specific info * * This routine is used to register an alternate mode that @port is capable of * supporting. @@ -1307,7 +1495,23 @@ struct typec_altmode * typec_port_register_altmode(struct typec_port *port, const struct typec_altmode_desc *desc) { - return typec_register_altmode(&port->dev, desc); + struct typec_altmode *adev; + struct typec_mux *mux; + char id[10]; + + sprintf(id, "id%04xm%02x", desc->svid, desc->mode); + + mux = typec_mux_get(port->dev.parent, id); + if (IS_ERR(mux)) + return ERR_CAST(mux); + + adev = typec_register_altmode(&port->dev, desc); + if (IS_ERR(adev)) + typec_mux_put(mux); + else + to_altmode(adev)->mux = mux; + + return adev; } EXPORT_SYMBOL_GPL(typec_port_register_altmode); @@ -1381,10 +1585,12 @@ struct typec_port *typec_register_port(struct device *parent, break; } + ida_init(&port->mode_ids); + mutex_init(&port->port_type_lock); + port->id = id; port->cap = cap; port->port_type = cap->type; - mutex_init(&port->port_type_lock); port->prefer_role = cap->prefer_role; port->dev.class = typec_class; @@ -1428,8 +1634,19 @@ EXPORT_SYMBOL_GPL(typec_unregister_port); static int __init typec_init(void) { + int ret; + + ret = bus_register(&typec_bus); + if (ret) + return ret; + typec_class = class_create(THIS_MODULE, "typec"); - return PTR_ERR_OR_ZERO(typec_class); + if (IS_ERR(typec_class)) { + bus_unregister(&typec_bus); + return PTR_ERR(typec_class); + } + + return 0; } subsys_initcall(typec_init); @@ -1437,6 +1654,7 @@ static void __exit typec_exit(void) { class_destroy(typec_class); ida_destroy(&typec_index_ida); + bus_unregister(&typec_bus); } module_exit(typec_exit); 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 { #define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 #define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 +/* USB Type-C Alternate Modes */ + +#define TYPEC_ANY_MODE 0x7 + +/** + * struct typec_device_id - USB Type-C alternate mode identifiers + * @svid: Standard or Vendor ID + * @mode: Mode index + */ +struct typec_device_id { + __u16 svid; + __u8 mode; + kernel_ulong_t driver_data; +}; + #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 @@ #include -/* XXX: Once we have a header for USB Power Delivery, this belongs there */ -#define ALTMODE_MAX_MODES 6 - /* USB Type-C Specification releases */ #define USB_TYPEC_REV_1_0 0x100 /* 1.0 */ #define USB_TYPEC_REV_1_1 0x110 /* 1.1 */ #define USB_TYPEC_REV_1_2 0x120 /* 1.2 */ -struct typec_altmode; struct typec_partner; struct typec_cable; struct typec_plug; struct typec_port; struct fwnode_handle; +struct device; enum typec_port_type { TYPEC_PORT_SRC, @@ -107,7 +104,7 @@ struct typec_altmode_desc { u8 mode; u32 vdo; /* Only used with ports */ - enum typec_port_type roles; + enum typec_port_data roles; }; struct typec_altmode @@ -186,7 +183,6 @@ struct typec_partner_desc { * @dr_set: Set Data Role * @pr_set: Set Power Role * @vconn_set: Set VCONN Role - * @activate_mode: Enter/exit given Alternate Mode * @port_type_set: Set port type * * Static capabilities of a single USB Type-C port. @@ -212,12 +208,8 @@ struct typec_capability { enum typec_role); int (*vconn_set)(const struct typec_capability *, enum typec_role); - - int (*activate_mode)(const struct typec_capability *, - int mode, int activate); int (*port_type_set)(const struct typec_capability *, - enum typec_port_type); - + enum typec_port_type); }; /* 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 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_ALTMODE_H +#define __USB_TYPEC_ALTMODE_H + +#include +#include +#include + +#define MODE_DISCOVERY_MAX 6 + +struct typec_altmode_ops; + +/** + * struct typec_altmode - USB Type-C alternate mode device + * @dev: Driver model's view of this device + * @svid: Standard or Vendor ID (SVID) of the alternate mode + * @mode: Index of the Mode + * @vdo: VDO returned by Discover Modes USB PD command + * @active: Tells has the mode been entered or not + * @desc: Optional human readable description of the mode + * @ops: Operations vector from the driver + */ +struct typec_altmode { + struct device dev; + u16 svid; + int mode; + u32 vdo; + unsigned int active:1; + + char *desc; + const struct typec_altmode_ops *ops; +}; + +#define to_typec_altmode(d) container_of(d, struct typec_altmode, dev) + +static inline void typec_altmode_set_drvdata(struct typec_altmode *altmode, + void *data) +{ + dev_set_drvdata(&altmode->dev, data); +} + +static inline void *typec_altmode_get_drvdata(struct typec_altmode *altmode) +{ + return dev_get_drvdata(&altmode->dev); +} + +/** + * struct typec_altmode_ops - Alternate mode specific operations vector + * @enter: Operations to be executed with Enter Mode Command + * @exit: Operations to be executed with Exit Mode Command + * @attention: Callback for Attention Command + * @vdm: Callback for SVID specific commands + * @notify: Communication channel for platform and the alternate mode + * @activate: User callback for Enter/Exit Mode + */ +struct typec_altmode_ops { + int (*enter)(struct typec_altmode *altmode); + int (*exit)(struct typec_altmode *altmode); + void (*attention)(struct typec_altmode *altmode, u32 vdo); + int (*vdm)(struct typec_altmode *altmode, const u32 hdr, + const u32 *vdo, int cnt); + int (*notify)(struct typec_altmode *altmode, unsigned long conf, + void *data); + int (*activate)(struct typec_altmode *altmode, int activate); +}; + +int typec_altmode_enter(struct typec_altmode *altmode); +int typec_altmode_exit(struct typec_altmode *altmode); +void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); +int typec_altmode_vdm(struct typec_altmode *altmode, + const u32 header, const u32 *vdo, int count); +int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, + void *data); +const struct typec_altmode * +typec_altmode_get_partner(struct typec_altmode *altmode); + +/* + * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C + * Specification. SVID specific connector states are expected to follow and + * start from the value TYPEC_STATE_MODAL. + */ +enum { + TYPEC_STATE_SAFE, /* USB Safe State */ + TYPEC_STATE_USB, /* USB Operation */ + TYPEC_STATE_MODAL, /* Alternate Modes */ +}; + +/* + * For the muxes there is no difference between Accessory Modes and Alternate + * Modes, so the Accessory Modes are supplied with specific modal state values + * here. Unlike with Alternate Modes, where the mux will be linked with the + * alternate mode device, the mux for Accessory Modes will be linked with the + * port device instead. + * + * Port drivers can use TYPEC_MODE_AUDIO and TYPEC_MODE_DEBUG as the mode + * value for typec_set_mode() when accessory modes are supported. + */ +enum { + TYPEC_MODE_AUDIO = TYPEC_STATE_MODAL, /* Audio Accessory */ + TYPEC_MODE_DEBUG, /* Debug Accessory */ +}; + +#define TYPEC_MODAL_STATE(_state_) ((_state_) + TYPEC_STATE_MODAL) + +struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode, + enum typec_plug_index index); +void typec_altmode_put_plug(struct typec_altmode *plug); + +struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, + size_t n, u16 svid, u8 mode); + +struct typec_altmode * +typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, + struct notifier_block *nb); + +void typec_altmode_unregister_notifier(struct typec_altmode *adev, + struct notifier_block *nb); + +/** + * typec_altmode_get_orientation - Get cable plug orientation + * altmode: Handle to the alternate mode + */ +static inline enum typec_orientation +typec_altmode_get_orientation(struct typec_altmode *altmode) +{ + return typec_get_orientation(typec_altmode2port(altmode)); +} + +/** + * struct typec_altmode_driver - USB Type-C alternate mode device driver + * @id_table: Null terminated array of SVIDs + * @probe: Callback for device binding + * @remove: Callback for device unbinding + * @driver: Device driver model driver + * + * These drivers will be bind to the partner alternate mode devices. They will + * handle all SVID specific communication. + */ +struct typec_altmode_driver { + const struct typec_device_id *id_table; + int (*probe)(struct typec_altmode *altmode); + void (*remove)(struct typec_altmode *altmode); + struct device_driver driver; +}; + +#define to_altmode_driver(d) container_of(d, struct typec_altmode_driver, \ + driver) + +#define typec_altmode_register_driver(drv) \ + __typec_altmode_register_driver(drv, THIS_MODULE) +int __typec_altmode_register_driver(struct typec_altmode_driver *drv, + struct module *module); +void typec_altmode_unregister_driver(struct typec_altmode_driver *drv); + +#define module_typec_altmode_driver(__typec_altmode_driver) \ + module_driver(__typec_altmode_driver, typec_altmode_register_driver, \ + typec_altmode_unregister_driver) + +#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) DEVID_FIELD(tb_service_id, protocol_version); DEVID_FIELD(tb_service_id, protocol_revision); + DEVID(typec_device_id); + DEVID_FIELD(typec_device_id, svid); + DEVID_FIELD(typec_device_id, mode); + return 0; } 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) } ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); +/* Looks like: typec:idNmN */ +static int do_typec_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, typec_device_id, svid); + DEF_FIELD(symval, typec_device_id, mode); + + sprintf(alias, "typec:id%04X", svid); + ADD(alias, "m", mode != TYPEC_ANY_MODE, mode); + + return 1; +} +ADD_TO_DEVTABLE("typec", typec_device_id, do_typec_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.2.3 From 0e3bb7d6894d9b6e67d6382bb03a46a1dc989588 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:51 +0300 Subject: usb: typec: Add driver for DisplayPort alternate mode DisplayPort USB Type-C Alt Mode allows DisplayPort displays and adapters to be attached to the USB Type-C ports on the system. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-driver-typec-displayport | 49 ++ drivers/usb/typec/Kconfig | 2 + drivers/usb/typec/Makefile | 1 + drivers/usb/typec/altmodes/Kconfig | 14 + drivers/usb/typec/altmodes/Makefile | 2 + drivers/usb/typec/altmodes/displayport.c | 578 +++++++++++++++++++++ include/linux/usb/typec_dp.h | 95 ++++ 7 files changed, 741 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-typec-displayport create mode 100644 drivers/usb/typec/altmodes/Kconfig create mode 100644 drivers/usb/typec/altmodes/Makefile create mode 100644 drivers/usb/typec/altmodes/displayport.c create mode 100644 include/linux/usb/typec_dp.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-typec-displayport b/Documentation/ABI/testing/sysfs-driver-typec-displayport new file mode 100644 index 000000000000..231471ad0d4b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-typec-displayport @@ -0,0 +1,49 @@ +What: /sys/bus/typec/devices/.../displayport/configuration +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows the current DisplayPort configuration for the connector. + Valid values are USB, source and sink. Source means DisplayPort + source, and sink means DisplayPort sink. + + All supported configurations are listed as space separated list + with the active one wrapped in square brackets. + + Source example: + + USB [source] sink + + The configuration can be changed by writing to the file + + Note. USB configuration does not equal to Exit Mode. It is + separate configuration defined in VESA DisplayPort Alt Mode on + USB Type-C Standard. Functionally it equals to the situation + where the mode has been exited (to exit the mode, see + Documentation/ABI/testing/sysfs-bus-typec, and use file + /sys/bus/typec/devices/.../active). + +What: /sys/bus/typec/devices/.../displayport/pin_assignment +Date: July 2018 +Contact: Heikki Krogerus +Description: + VESA DisplayPort Alt Mode on USB Type-C Standard defines six + different pin assignments for USB Type-C connector that are + labeled A, B, C, D, E, and F. The supported pin assignments are + listed as space separated list with the active one wrapped in + square brackets. + + Example: + + C [D] + + Pin assignment can be changed by writing to the file. It is + possible to set pin assignment before configuration has been + set, but the assignment will not be active before the + connector is actually configured. + + Note. As of VESA DisplayPort Alt Mode on USB Type-C Standard + version 1.0b, pin assignments A, B, and F are deprecated. Only + pin assignment D can now carry simultaneously one channel of + USB SuperSpeed protocol. From user perspective pin assignments C + and E are equal, where all channels on the connector are used + for carrying DisplayPort protocol (allowing higher resolutions). diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index ee808903983f..00878c386dd0 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -104,4 +104,6 @@ config TYPEC_TPS6598X source "drivers/usb/typec/mux/Kconfig" +source "drivers/usb/typec/altmodes/Kconfig" + endif # TYPEC diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 335ee06748fc..45b0aef428a8 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o typec-y := class.o mux.o bus.o +obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-y += fusb302/ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig new file mode 100644 index 000000000000..efef2a64bc51 --- /dev/null +++ b/drivers/usb/typec/altmodes/Kconfig @@ -0,0 +1,14 @@ + +menu "USB Type-C Alternate Mode drivers" + +config TYPEC_DP_ALTMODE + tristate "DisplayPort Alternate Mode driver" + help + DisplayPort USB Type-C Alternate Mode allows DisplayPort + displays and adapters to be attached to the USB Type-C + connectors on the system. + + To compile this driver as a module, choose M here: the + module will be called typec_displayport. + +endmenu diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile new file mode 100644 index 000000000000..5caf094ef71a --- /dev/null +++ b/drivers/usb/typec/altmodes/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o +typec_displayport-y := displayport.o diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c new file mode 100644 index 000000000000..ef12b15bd484 --- /dev/null +++ b/drivers/usb/typec/altmodes/displayport.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * USB Typec-C DisplayPort Alternate Mode driver + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus + * + * DisplayPort is trademark of VESA (www.vesa.org) + */ + +#include +#include +#include +#include +#include + +#define DP_HEADER(cmd) (VDO(USB_TYPEC_DP_SID, 1, cmd) | \ + VDO_OPOS(USB_TYPEC_DP_MODE)) + +enum { + DP_CONF_USB, + DP_CONF_DFP_D, + DP_CONF_UFP_D, + DP_CONF_DUAL_D, +}; + +/* Helper for setting/getting the pin assignement value to the configuration */ +#define DP_CONF_SET_PIN_ASSIGN(_a_) ((_a_) << 8) +#define DP_CONF_GET_PIN_ASSIGN(_conf_) (((_conf_) & GENMASK(15, 8)) >> 8) + +/* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */ +#define DP_PIN_ASSIGN_GEN2_BR_MASK (BIT(DP_PIN_ASSIGN_A) | \ + BIT(DP_PIN_ASSIGN_B)) + +/* Pin assignments that use DP v1.3 signaling to carry DP protocol */ +#define DP_PIN_ASSIGN_DP_BR_MASK (BIT(DP_PIN_ASSIGN_C) | \ + BIT(DP_PIN_ASSIGN_D) | \ + BIT(DP_PIN_ASSIGN_E) | \ + BIT(DP_PIN_ASSIGN_F)) + +/* DP only pin assignments */ +#define DP_PIN_ASSIGN_DP_ONLY_MASK (BIT(DP_PIN_ASSIGN_A) | \ + BIT(DP_PIN_ASSIGN_C) | \ + BIT(DP_PIN_ASSIGN_E)) + +/* Pin assignments where one channel is for USB */ +#define DP_PIN_ASSIGN_MULTI_FUNC_MASK (BIT(DP_PIN_ASSIGN_B) | \ + BIT(DP_PIN_ASSIGN_D) | \ + BIT(DP_PIN_ASSIGN_F)) + +enum dp_state { + DP_STATE_IDLE, + DP_STATE_ENTER, + DP_STATE_UPDATE, + DP_STATE_CONFIGURE, + DP_STATE_EXIT, +}; + +struct dp_altmode { + struct typec_displayport_data data; + + enum dp_state state; + + struct mutex lock; /* device lock */ + struct work_struct work; + struct typec_altmode *alt; + const struct typec_altmode *port; +}; + +static int dp_altmode_notify(struct dp_altmode *dp) +{ + u8 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); + + return typec_altmode_notify(dp->alt, TYPEC_MODAL_STATE(state), + &dp->data); +} + +static int dp_altmode_configure(struct dp_altmode *dp, u8 con) +{ + u32 conf = DP_CONF_SIGNALING_DP; /* Only DP signaling supported */ + u8 pin_assign = 0; + + switch (con) { + case DP_STATUS_CON_DISABLED: + return 0; + case DP_STATUS_CON_DFP_D: + conf |= DP_CONF_UFP_U_AS_DFP_D; + pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) & + DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo); + break; + case DP_STATUS_CON_UFP_D: + case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */ + conf |= DP_CONF_UFP_U_AS_UFP_D; + pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo) & + DP_CAP_UFP_D_PIN_ASSIGN(dp->port->vdo); + break; + default: + break; + } + + /* Determining the initial pin assignment. */ + if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) { + /* Is USB together with DP preferred */ + if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC && + pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK) + pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK; + else + pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK; + + if (!pin_assign) + return -EINVAL; + + conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign); + } + + dp->data.conf = conf; + + return 0; +} + +static int dp_altmode_status_update(struct dp_altmode *dp) +{ + bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); + u8 con = DP_STATUS_CONNECTION(dp->data.status); + int ret = 0; + + if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) { + dp->data.conf = 0; + dp->state = DP_STATE_CONFIGURE; + } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) { + dp->state = DP_STATE_EXIT; + } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) { + ret = dp_altmode_configure(dp, con); + if (!ret) + dp->state = DP_STATE_CONFIGURE; + } + + return ret; +} + +static int dp_altmode_configured(struct dp_altmode *dp) +{ + int ret; + + sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); + + if (!dp->data.conf) + return typec_altmode_notify(dp->alt, TYPEC_STATE_USB, + &dp->data); + + ret = dp_altmode_notify(dp); + if (ret) + return ret; + + sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); + + return 0; +} + +static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) +{ + u32 header = DP_HEADER(DP_CMD_CONFIGURE); + int ret; + + ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data); + if (ret) { + dev_err(&dp->alt->dev, + "unable to put to connector to safe mode\n"); + return ret; + } + + ret = typec_altmode_vdm(dp->alt, header, &conf, 2); + if (ret) { + if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) + dp_altmode_notify(dp); + else + typec_altmode_notify(dp->alt, TYPEC_STATE_USB, + &dp->data); + } + + return ret; +} + +static void dp_altmode_work(struct work_struct *work) +{ + struct dp_altmode *dp = container_of(work, struct dp_altmode, work); + u32 header; + u32 vdo; + int ret; + + mutex_lock(&dp->lock); + + switch (dp->state) { + case DP_STATE_ENTER: + ret = typec_altmode_enter(dp->alt); + if (ret) + dev_err(&dp->alt->dev, "failed to enter mode\n"); + break; + case DP_STATE_UPDATE: + header = DP_HEADER(DP_CMD_STATUS_UPDATE); + vdo = 1; + ret = typec_altmode_vdm(dp->alt, header, &vdo, 2); + if (ret) + dev_err(&dp->alt->dev, + "unable to send Status Update command (%d)\n", + ret); + break; + case DP_STATE_CONFIGURE: + ret = dp_altmode_configure_vdm(dp, dp->data.conf); + if (ret) + dev_err(&dp->alt->dev, + "unable to send Configure command (%d)\n", ret); + break; + case DP_STATE_EXIT: + if (typec_altmode_exit(dp->alt)) + dev_err(&dp->alt->dev, "Exit Mode Failed!\n"); + break; + default: + break; + } + + dp->state = DP_STATE_IDLE; + + mutex_unlock(&dp->lock); +} + +static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo) +{ + struct dp_altmode *dp = typec_altmode_get_drvdata(alt); + u8 old_state; + + mutex_lock(&dp->lock); + + old_state = dp->state; + dp->data.status = vdo; + + if (old_state != DP_STATE_IDLE) + dev_warn(&alt->dev, "ATTENTION while processing state %d\n", + old_state); + + if (dp_altmode_status_update(dp)) + dev_warn(&alt->dev, "%s: status update failed\n", __func__); + + if (dp_altmode_notify(dp)) + dev_err(&alt->dev, "%s: notification failed\n", __func__); + + if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE) + schedule_work(&dp->work); + + mutex_unlock(&dp->lock); +} + +static int dp_altmode_vdm(struct typec_altmode *alt, + const u32 hdr, const u32 *vdo, int count) +{ + struct dp_altmode *dp = typec_altmode_get_drvdata(alt); + int cmd_type = PD_VDO_CMDT(hdr); + int cmd = PD_VDO_CMD(hdr); + int ret = 0; + + mutex_lock(&dp->lock); + + if (dp->state != DP_STATE_IDLE) { + ret = -EBUSY; + goto err_unlock; + } + + switch (cmd_type) { + case CMDT_RSP_ACK: + switch (cmd) { + case CMD_ENTER_MODE: + dp->state = DP_STATE_UPDATE; + break; + case CMD_EXIT_MODE: + dp->data.status = 0; + dp->data.conf = 0; + break; + case DP_CMD_STATUS_UPDATE: + dp->data.status = *vdo; + ret = dp_altmode_status_update(dp); + break; + case DP_CMD_CONFIGURE: + ret = dp_altmode_configured(dp); + break; + default: + break; + } + break; + case CMDT_RSP_NAK: + switch (cmd) { + case DP_CMD_CONFIGURE: + dp->data.conf = 0; + ret = dp_altmode_configured(dp); + break; + default: + break; + } + break; + default: + break; + } + + if (dp->state != DP_STATE_IDLE) + schedule_work(&dp->work); + +err_unlock: + mutex_unlock(&dp->lock); + return ret; +} + +static int dp_altmode_activate(struct typec_altmode *alt, int activate) +{ + return activate ? typec_altmode_enter(alt) : typec_altmode_exit(alt); +} + +static const struct typec_altmode_ops dp_altmode_ops = { + .attention = dp_altmode_attention, + .vdm = dp_altmode_vdm, + .activate = dp_altmode_activate, +}; + +static const char * const configurations[] = { + [DP_CONF_USB] = "USB", + [DP_CONF_DFP_D] = "source", + [DP_CONF_UFP_D] = "sink", +}; + +static ssize_t +configuration_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + u32 conf; + u32 cap; + int con; + int ret; + + con = sysfs_match_string(configurations, buf); + if (con < 0) + return con; + + mutex_lock(&dp->lock); + + if (dp->state != DP_STATE_IDLE) { + ret = -EBUSY; + goto err_unlock; + } + + cap = DP_CAP_CAPABILITY(dp->alt->vdo); + + if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) || + (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) + return -EINVAL; + + conf = dp->data.conf & ~DP_CONF_DUAL_D; + conf |= con; + + if (dp->alt->active) { + ret = dp_altmode_configure_vdm(dp, conf); + if (ret) + goto err_unlock; + } + + dp->data.conf = conf; + +err_unlock: + mutex_unlock(&dp->lock); + + return ret ? ret : size; +} + +static ssize_t configuration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + int len; + u8 cap; + u8 cur; + int i; + + mutex_lock(&dp->lock); + + cap = DP_CAP_CAPABILITY(dp->alt->vdo); + cur = DP_CONF_CURRENTLY(dp->data.conf); + + len = sprintf(buf, "%s ", cur ? "USB" : "[USB]"); + + for (i = 1; i < ARRAY_SIZE(configurations); i++) { + if (i == cur) + len += sprintf(buf + len, "[%s] ", configurations[i]); + else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) || + (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D)) + len += sprintf(buf + len, "%s ", configurations[i]); + } + + mutex_unlock(&dp->lock); + + buf[len - 1] = '\n'; + return len; +} +static DEVICE_ATTR_RW(configuration); + +static const char * const pin_assignments[] = { + [DP_PIN_ASSIGN_A] = "A", + [DP_PIN_ASSIGN_B] = "B", + [DP_PIN_ASSIGN_C] = "C", + [DP_PIN_ASSIGN_D] = "D", + [DP_PIN_ASSIGN_E] = "E", + [DP_PIN_ASSIGN_F] = "F", +}; + +static ssize_t +pin_assignment_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + u8 assignments; + u32 conf; + int ret; + + ret = sysfs_match_string(pin_assignments, buf); + if (ret < 0) + return ret; + + conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret)); + ret = 0; + + mutex_lock(&dp->lock); + + if (conf & dp->data.conf) + goto out_unlock; + + if (dp->state != DP_STATE_IDLE) { + ret = -EBUSY; + goto out_unlock; + } + + if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D) + assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo); + else + assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo); + + if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) { + ret = -EINVAL; + goto out_unlock; + } + + conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK; + + /* Only send Configure command if a configuration has been set */ + if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) { + ret = dp_altmode_configure_vdm(dp, conf); + if (ret) + goto out_unlock; + } + + dp->data.conf = conf; + +out_unlock: + mutex_unlock(&dp->lock); + + return ret ? ret : size; +} + +static ssize_t pin_assignment_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + u8 assignments; + int len = 0; + u8 cur; + int i; + + mutex_lock(&dp->lock); + + cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); + + if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D) + assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo); + else + assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo); + + for (i = 0; assignments; assignments >>= 1, i++) { + if (assignments & 1) { + if (i == cur) + len += sprintf(buf + len, "[%s] ", + pin_assignments[i]); + else + len += sprintf(buf + len, "%s ", + pin_assignments[i]); + } + } + + mutex_unlock(&dp->lock); + + buf[len - 1] = '\n'; + return len; +} +static DEVICE_ATTR_RW(pin_assignment); + +static struct attribute *dp_altmode_attrs[] = { + &dev_attr_configuration.attr, + &dev_attr_pin_assignment.attr, + NULL +}; + +static const struct attribute_group dp_altmode_group = { + .name = "displayport", + .attrs = dp_altmode_attrs, +}; + +static int dp_altmode_probe(struct typec_altmode *alt) +{ + const struct typec_altmode *port = typec_altmode_get_partner(alt); + struct dp_altmode *dp; + int ret; + + /* FIXME: Port can only be DFP_U. */ + + /* Make sure we have compatiple pin configurations */ + if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) & + DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) && + !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) & + DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) + return -ENODEV; + + ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group); + if (ret) + return ret; + + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + INIT_WORK(&dp->work, dp_altmode_work); + mutex_init(&dp->lock); + dp->port = port; + dp->alt = alt; + + alt->desc = "DisplayPort"; + alt->ops = &dp_altmode_ops; + + typec_altmode_set_drvdata(alt, dp); + + dp->state = DP_STATE_ENTER; + schedule_work(&dp->work); + + return 0; +} + +static void dp_altmode_remove(struct typec_altmode *alt) +{ + struct dp_altmode *dp = typec_altmode_get_drvdata(alt); + + sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); + cancel_work_sync(&dp->work); +} + +static const struct typec_device_id dp_typec_id[] = { + { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE }, + { }, +}; +MODULE_DEVICE_TABLE(typec, dp_typec_id); + +static struct typec_altmode_driver dp_altmode_driver = { + .id_table = dp_typec_id, + .probe = dp_altmode_probe, + .remove = dp_altmode_remove, + .driver = { + .name = "typec_displayport", + .owner = THIS_MODULE, + }, +}; +module_typec_altmode_driver(dp_altmode_driver); + +MODULE_AUTHOR("Heikki Krogerus "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DisplayPort Alternate Mode"); diff --git a/include/linux/usb/typec_dp.h b/include/linux/usb/typec_dp.h new file mode 100644 index 000000000000..55ae781d60a9 --- /dev/null +++ b/include/linux/usb/typec_dp.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __USB_TYPEC_DP_H +#define __USB_TYPEC_DP_H + +#include + +#define USB_TYPEC_DP_SID 0xff01 +#define USB_TYPEC_DP_MODE 1 + +/* + * Connector states matching the pin assignments in DisplayPort Alt Mode + * Specification. + * + * These values are meant primarily to be used by the mux drivers, but they are + * also used as the "value" part in the alternate mode notification chain, so + * receivers of those notifications will always see them. + * + * Note. DisplayPort USB Type-C Alt Mode Specification version 1.0b deprecated + * pin assignments A, B and F, but they are still defined here for legacy + * purposes. + */ +enum { + TYPEC_DP_STATE_A = TYPEC_STATE_MODAL, /* Not supported after v1.0b */ + TYPEC_DP_STATE_B, /* Not supported after v1.0b */ + TYPEC_DP_STATE_C, + TYPEC_DP_STATE_D, + TYPEC_DP_STATE_E, + TYPEC_DP_STATE_F, /* Not supported after v1.0b */ +}; + +/* + * struct typec_displayport_data - DisplayPort Alt Mode specific data + * @status: Status Update command VDO content + * @conf: Configure command VDO content + * + * This structure is delivered as the data part with the notifications. It + * contains the VDOs from the two DisplayPort Type-C alternate mode specific + * commands: Status Update and Configure. + * + * @status will show for example the status of the HPD signal. + */ +struct typec_displayport_data { + u32 status; + u32 conf; +}; + +enum { + DP_PIN_ASSIGN_A, /* Not supported after v1.0b */ + DP_PIN_ASSIGN_B, /* Not supported after v1.0b */ + DP_PIN_ASSIGN_C, + DP_PIN_ASSIGN_D, + DP_PIN_ASSIGN_E, + DP_PIN_ASSIGN_F, /* Not supported after v1.0b */ +}; + +/* DisplayPort alt mode specific commands */ +#define DP_CMD_STATUS_UPDATE VDO_CMD_VENDOR(0) +#define DP_CMD_CONFIGURE VDO_CMD_VENDOR(1) + +/* DisplayPort Capabilities VDO bits (returned with Discover Modes) */ +#define DP_CAP_CAPABILITY(_cap_) ((_cap_) & 3) +#define DP_CAP_UFP_D 1 +#define DP_CAP_DFP_D 2 +#define DP_CAP_DFP_D_AND_UFP_D 3 +#define DP_CAP_DP_SIGNALING BIT(2) /* Always set */ +#define DP_CAP_GEN2 BIT(3) /* Reserved after v1.0b */ +#define DP_CAP_RECEPTACLE BIT(6) +#define DP_CAP_USB BIT(7) +#define DP_CAP_DFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(15, 8)) >> 8) +#define DP_CAP_UFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(23, 16)) >> 16) + +/* DisplayPort Status Update VDO bits */ +#define DP_STATUS_CONNECTION(_status_) ((_status_) & 3) +#define DP_STATUS_CON_DISABLED 0 +#define DP_STATUS_CON_DFP_D 1 +#define DP_STATUS_CON_UFP_D 2 +#define DP_STATUS_CON_BOTH 3 +#define DP_STATUS_POWER_LOW BIT(2) +#define DP_STATUS_ENABLED BIT(3) +#define DP_STATUS_PREFER_MULTI_FUNC BIT(4) +#define DP_STATUS_SWITCH_TO_USB BIT(5) +#define DP_STATUS_EXIT_DP_MODE BIT(6) +#define DP_STATUS_HPD_STATE BIT(7) /* 0 = HPD_Low, 1 = HPD_High */ +#define DP_STATUS_IRQ_HPD BIT(8) + +/* DisplayPort Configurations VDO bits */ +#define DP_CONF_CURRENTLY(_conf_) ((_conf_) & 3) +#define DP_CONF_UFP_U_AS_DFP_D BIT(0) +#define DP_CONF_UFP_U_AS_UFP_D BIT(1) +#define DP_CONF_SIGNALING_DP BIT(2) +#define DP_CONF_SIGNALING_GEN_2 BIT(3) /* Reserved after v1.0b */ +#define DP_CONF_PIN_ASSIGNEMENT_SHIFT 8 +#define DP_CONF_PIN_ASSIGNEMENT_MASK GENMASK(15, 8) + +#endif /* __USB_TYPEC_DP_H */ -- cgit v1.2.3 From 8ccba47c699f82cb7b5f6bf24ffe6cd1d5c53381 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 29 Jun 2018 10:20:28 +0800 Subject: dt-bindings: phy-mtk-tphy: add optional properties for u2phy Add some optional properties for eye diagram test and BC12 of u2phy Signed-off-by: Chunfeng Yun Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt b/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt index 0d34b2b4a6b7..a5f7a4f0dbc1 100644 --- a/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt +++ b/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt @@ -47,6 +47,12 @@ Required properties (port (child) node): - PHY_TYPE_PCIE - PHY_TYPE_SATA +Optional properties (PHY_TYPE_USB2 port (child) node): +- mediatek,eye-src : u32, the value of slew rate calibrate +- mediatek,eye-vrt : u32, the selection of VRT reference voltage +- mediatek,eye-term : u32, the selection of HS_TX TERM reference voltage +- mediatek,bc12 : bool, enable BC12 of u2phy if support it + Example: u3phy: usb-phy@11290000 { -- cgit v1.2.3 From 95fb21253ea56d330053bafcf3cb502171551253 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Fri, 6 Jul 2018 17:18:32 -0700 Subject: dt-bindings: phy: Add binding doc for Stingray PCIe PHY Add binding document for Stingray PCIe PHYs for both PAXB and PAXC based root complex Signed-off-by: Ray Jui Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/brcm,sr-pcie-phy.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt b/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt new file mode 100644 index 000000000000..e8d82286beb9 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,sr-pcie-phy.txt @@ -0,0 +1,41 @@ +Broadcom Stingray PCIe PHY + +Required properties: +- compatible: must be "brcm,sr-pcie-phy" +- reg: base address and length of the PCIe SS register space +- brcm,sr-cdru: phandle to the CDRU syscon node +- brcm,sr-mhb: phandle to the MHB syscon node +- #phy-cells: Must be 1, denotes the PHY index + +For PAXB based root complex, one can have a configuration of up to 8 PHYs +PHY index goes from 0 to 7 + +For the internal PAXC based root complex, PHY index is always 8 + +Example: + mhb: syscon@60401000 { + compatible = "brcm,sr-mhb", "syscon"; + reg = <0 0x60401000 0 0x38c>; + }; + + cdru: syscon@6641d000 { + compatible = "brcm,sr-cdru", "syscon"; + reg = <0 0x6641d000 0 0x400>; + }; + + pcie_phy: phy@40000000 { + compatible = "brcm,sr-pcie-phy"; + reg = <0 0x40000000 0 0x800>; + brcm,sr-cdru = <&cdru>; + brcm,sr-mhb = <&mhb>; + #phy-cells = <1>; + }; + + /* users of the PCIe PHY */ + + pcie0: pcie@48000000 { + ... + ... + phys = <&pcie_phy 0>; + phy-names = "pcie-phy"; + }; -- cgit v1.2.3 From 8b1087fa3a272c962836ea0e9a8bba14b0ee0ee5 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 6 Jul 2018 16:31:42 -0700 Subject: phy: qcom-qmp: Fix dts bindings to reflect reality A few patches have landed for the qcom-qmp PHY that affect how you would write a device tree node. ...yet the bindings weren't updated. Let's remedy the situation and make the bindings refelect reality. Fixes: efb05a50c956 ("phy: qcom-qmp: Add support for QMP V3 USB3 PHY") Fixes: ac0d239936bd ("phy: qcom-qmp: Add support for runtime PM") Signed-off-by: Douglas Anderson Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt index 266a1bb8bb6e..0c7629e88bf3 100644 --- a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt @@ -12,7 +12,14 @@ Required properties: "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845, "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845. - - reg: offset and length of register set for PHY's common serdes block. + - reg: + - For "qcom,sdm845-qmp-usb3-phy": + - index 0: address and length of register set for PHY's common serdes + block. + - named register "dp_com" (using reg-names): address and length of the + DP_COM control block. + - For all others: + - offset and length of register set for PHY's common serdes block. - #clock-cells: must be 1 - Phy pll outputs a bunch of clocks for Tx, Rx and Pipe @@ -60,7 +67,10 @@ Required nodes: Required properties for child node: - reg: list of offset and length pairs of register sets for PHY blocks - - tx, rx and pcs. + - index 0: tx + - index 1: rx + - index 2: pcs + - index 3: pcs_misc (optional) - #phy-cells: must be 0 -- cgit v1.2.3 From 4fa88cd3370ed33119863747a4db7f5e3f1dc308 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 10 Jun 2018 21:22:46 +0300 Subject: dt-bindings: phy: Renesas R-Car Gen3 PCIe PHY bindings This PHY is still mostly undocumented -- the only documented registers exist on R-Car V3H (R8A77980) SoC. Add the corresponding device tree bindings. Signed-off-by: Sergei Shtylyov Reviewed-by: Simon Horman Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/rcar-gen3-phy-pcie.txt | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt new file mode 100644 index 000000000000..63853b35e083 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt @@ -0,0 +1,24 @@ +* Renesas R-Car generation 3 PCIe PHY + +This file provides information on what the device node for the R-Car +generation 3 PCIe PHY contains. + +Required properties: +- compatible: "renesas,r8a77980-pcie-phy" if the device is a part of the + R8A77980 SoC. +- reg: offset and length of the register block. +- clocks: clock phandle and specifier pair. +- power-domains: power domain phandle and specifier pair. +- resets: reset phandle and specifier pair. +- #phy-cells: see phy-bindings.txt in the same directory, must be <0>. + +Example (R-Car V3H): + + pcie-phy@e65d0000 { + compatible = "renesas,r8a77980-pcie-phy"; + reg = <0 0xe65d0000 0 0x8000>; + #phy-cells = <0>; + clocks = <&cpg CPG_MOD 319>; + power-domains = <&sysc 32>; + resets = <&cpg 319>; + }; -- cgit v1.2.3 From 3738c506657f34adbfc860df729b9e38b1567ed4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 5 Jul 2018 12:38:33 +0200 Subject: USB: serial: clean up kl5kusb105 documentation Remove references to long-gone kl5kusb105 module parameters in the usb-serial documentation. Signed-off-by: Johan Hovold --- Documentation/usb/usb-serial.txt | 9 --------- 1 file changed, 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt index 349f3104fa4f..ab100d6ee436 100644 --- a/Documentation/usb/usb-serial.txt +++ b/Documentation/usb/usb-serial.txt @@ -418,15 +418,6 @@ Current status: why it is wise to cut down on the rate used is wise for large transfers until this is settled. -Options supported: - If this driver is compiled as a module you can pass the following - options to it: - debug - extra verbose debugging info - (default: 0; nonzero enables) - use_lowlatency - use low_latency flag to speed up tty layer - when reading from the device. - (default: 0; nonzero enables) - See http://www.uuhaus.de/linux/palmconnect.html for up-to-date information on this driver. -- cgit v1.2.3 From 4eb44f69e77141992e305d9e75e021b196071cdd Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Jul 2018 19:51:47 +0900 Subject: dt-bindings: usb-xhci: Add r8a77990 support This patch adds r8a77990 xhci support. The driver will use the fallback compatible string "renesas,rcar-gen3-xhci". Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-xhci.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index bd1dd316fb23..ac4cd0d6195a 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -14,6 +14,7 @@ Required properties: - "renesas,xhci-r8a7795" for r8a7795 SoC - "renesas,xhci-r8a7796" for r8a7796 SoC - "renesas,xhci-r8a77965" for r8a77965 SoC + - "renesas,xhci-r8a77990" for r8a77990 SoC - "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 or RZ/G1 compatible device - "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 compatible device -- cgit v1.2.3 From 5a78671309e029d8e3427a5b9ec440c0d9ba9bde Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 6 Jun 2018 15:42:28 +0900 Subject: dt-bindings: rcar-gen3-phy-usb2: Add bindings for r8a77990 This patch adds suuport for r8a77990 (R-Car E3). Signed-off-by: Yoshihiro Shimoda Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt index dbd137c079e2..fb4a204da2bf 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt @@ -10,6 +10,8 @@ Required properties: SoC. "renesas,usb2-phy-r8a77965" if the device is a part of an R8A77965 SoC. + "renesas,usb2-phy-r8a77990" if the device is a part of an + R8A77990 SoC. "renesas,usb2-phy-r8a77995" if the device is a part of an R8A77995 SoC. "renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device. -- cgit v1.2.3 From d7af78b9245545908c90bce2206f200a4250b5db Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Thu, 24 May 2018 17:16:12 +0100 Subject: usb: gadget: uvc: Expose configuration name through video node When utilising multiple instantiations of a UVC gadget on a composite device, there is no clear method to link a particular configuration to its respective video node. Provide a means for identifying the correct video node by exposing the name of the function configuration through sysfs. Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham Signed-off-by: Felipe Balbi --- Documentation/ABI/testing/configfs-usb-gadget-uvc | 5 +++++ drivers/usb/gadget/function/f_uvc.c | 24 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 1ba0d0fda9c0..9281e2aa38df 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -263,3 +263,8 @@ Description: Specific streaming header descriptors is connected bmInfo - capabilities of this video streaming interface + +What: /sys/class/udc/udc.name/device/gadget/video4linux/video.name/function_name +Date: May 2018 +KernelVersion: 4.19 +Description: UVC configfs function instance name diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 1d2feac5c532..d8ce7868fe22 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -413,10 +413,21 @@ uvc_function_disconnect(struct uvc_device *uvc) * USB probe and disconnect */ +static ssize_t function_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uvc_device *uvc = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", uvc->func.fi->group.cg_item.ci_name); +} + +static DEVICE_ATTR_RO(function_name); + static int uvc_register_video(struct uvc_device *uvc) { struct usb_composite_dev *cdev = uvc->func.config->cdev; + int ret; /* TODO reference counting. */ uvc->vdev.v4l2_dev = &uvc->v4l2_dev; @@ -429,7 +440,17 @@ uvc_register_video(struct uvc_device *uvc) video_set_drvdata(&uvc->vdev, uvc); - return video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + return ret; + + ret = device_create_file(&uvc->vdev.dev, &dev_attr_function_name); + if (ret < 0) { + video_unregister_device(&uvc->vdev); + return ret; + } + + return 0; } #define UVC_COPY_DESCRIPTOR(mem, dst, desc) \ @@ -867,6 +888,7 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) INFO(cdev, "%s\n", __func__); + device_remove_file(&uvc->vdev.dev, &dev_attr_function_name); video_unregister_device(&uvc->vdev); v4l2_device_unregister(&uvc->v4l2_dev); -- cgit v1.2.3 From 262c25d68e39e0f7e7619f715752c12e167644c3 Mon Sep 17 00:00:00 2001 From: Pengbo Mu Date: Mon, 23 Jul 2018 18:32:38 +0800 Subject: arm64: dts: dwc3: description of incr burst type Add description of 'snps,incr-burst-type-adjustment' to binding so that configuring devicetree. Reviewed-by: Rob Herring Signed-off-by: Ran Wang Signed-off-by: Pengbo Mu Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc3.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 7f13ebef06cb..3e4c38b806ac 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -96,6 +96,11 @@ Optional properties: enable periodic ESS TX threshold. - tx-fifo-resize: determines if the FIFO *has* to be reallocated. + - snps,incr-burst-type-adjustment: Value for INCR burst type of GSBUSCFG0 + register, undefined length INCR burst type enable and INCRx type. + When just one value, which means INCRX burst mode enabled. When + more than one value, which means undefined length INCR burst type + enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256. - in addition all properties from usb-xhci.txt from the current directory are supported as well @@ -108,4 +113,5 @@ dwc3@4a030000 { reg = <0x4a030000 0xcfff>; interrupts = <0 92 4> usb-phy = <&usb2_phy>, <&usb3,phy>; + snps,incr-burst-type-adjustment = <1>, <4>, <8>, <16>; }; -- cgit v1.2.3