Commit 2c40c1c6 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'usb-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB/Thunderbolt updates from Greg KH:
 "Here is the "big" set of USB and Thunderbolt changes for 6.7-rc1.
  Nothing really major in here, just lots of constant development for
  new hardware. Included in here are:

   - Thunderbolt (i.e. USB4) fixes for reported issues and support for
     new hardware types and devices

   - USB typec additions of new drivers and cleanups for some existing
     ones

   - xhci cleanups and expanded tracing support and some platform
     specific updates

   - USB "La Jolla Cove Adapter (LJCA)" support added, and the gpio,
     spi, and i2c drivers for that type of device (all acked by the
     respective subsystem maintainers.)

   - lots of USB gadget driver updates and cleanups

   - new USB dwc3 platforms supported, as well as other dwc3 fixes and
     cleanups

   - USB chipidea driver updates

   - other smaller driver cleanups and additions, full details in the
     shortlog

  All of these have been in the linux-next tree for a while with no
  reported problems"

* tag 'usb-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (167 commits)
  usb: gadget: uvc: Add missing initialization of ssp config descriptor
  usb: storage: set 1.50 as the lower bcdDevice for older "Super Top" compatibility
  usb: raw-gadget: report suspend, resume, reset, and disconnect events
  usb: raw-gadget: don't disable device if usb_ep_queue fails
  usb: raw-gadget: properly handle interrupted requests
  usb:cdnsp: remove TRB_FLUSH_ENDPOINT command
  usb: gadget: aspeed_udc: Convert to platform remove callback returning void
  dt-bindings: usb: fsa4480: Add compatible for OCP96011
  usb: typec: fsa4480: Add support to swap SBU orientation
  dt-bindings: usb: fsa4480: Add data-lanes property to endpoint
  usb: typec: tcpm: Fix NULL pointer dereference in tcpm_pd_svdm()
  Revert "dt-bindings: usb: Add bindings for multiport properties on DWC3 controller"
  Revert "dt-bindings: usb: qcom,dwc3: Add bindings for SC8280 Multiport"
  thunderbolt: Fix one kernel-doc comment
  usb: gadget: f_ncm: Always set current gadget in ncm_bind()
  usb: core: Remove duplicated check in usb_hub_create_port_device
  usb: typec: tcpm: Add additional checks for contaminant
  arm64: dts: rockchip: rk3588s: Add USB3 host controller
  usb: dwc3: add optional PHY interface clocks
  dt-bindings: usb: add rk3588 compatible to rockchip,dwc3
  ...
parents 1f24458a c70793fb
......@@ -35,4 +35,6 @@ Description:
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
c_terminal_type code of the capture terminal type
p_terminal_type code of the playback terminal type
===================== =======================================
......@@ -313,6 +313,15 @@ Description:
Inter-Chip SSIC devices support asymmetric lanes up to 4 lanes per
direction. Devices before USB 3.2 are single lane (tx_lanes = 1)
What: /sys/bus/usb/devices/.../typec
Date: November 2023
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Symlink to the USB Type-C partner device. USB Type-C partner
represents the component that communicates over the
Configuration Channel (CC signal on USB Type-C connectors and
cables) with the local port.
What: /sys/bus/usb/devices/usbX/bAlternateSetting
Description:
The current interface alternate setting number, in decimal.
......
......@@ -124,6 +124,13 @@ Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
The voltage the supply supports in millivolts.
What: /sys/class/usb_power_delivery/.../source-capabilities/<position>:fixed_supply/peak_current
Date: October 2023
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
This file shows the value of the Fixed Power Source Peak Current
Capability field.
What: /sys/class/usb_power_delivery/.../source-capabilities/<position>:fixed_supply/maximum_current
Date: May 2022
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
......
......@@ -20,6 +20,7 @@ properties:
- qcom,qcm2290-qmp-usb3-phy
- qcom,sa8775p-qmp-usb3-uni-phy
- qcom,sc8280xp-qmp-usb3-uni-phy
- qcom,sdx75-qmp-usb3-uni-phy
- qcom,sm6115-qmp-usb3-phy
reg:
......@@ -75,6 +76,7 @@ allOf:
contains:
enum:
- qcom,ipq9574-qmp-usb3-phy
- qcom,sdx75-qmp-usb3-uni-phy
then:
properties:
clock-names:
......
......@@ -14,7 +14,12 @@ description:
properties:
compatible:
const: qcom,sm8550-snps-eusb2-phy
oneOf:
- items:
- enum:
- qcom,sdx75-snps-eusb2-phy
- const: qcom,sm8550-snps-eusb2-phy
- const: qcom,sm8550-snps-eusb2-phy
reg:
maxItems: 1
......
......@@ -35,6 +35,12 @@ properties:
'#size-cells':
const: 0
orientation-gpios:
description: Array of input gpios for the Type-C connector orientation indication.
The GPIO indication is used to detect the orientation of the Type-C connector.
The array should contain a gpio entry for each PMIC Glink connector, in reg order.
It is defined that GPIO active level means "CC2" or Reversed/Flipped orientation.
patternProperties:
'^connector@\d$':
$ref: /schemas/connector/usb-connector.yaml#
......@@ -44,6 +50,19 @@ patternProperties:
required:
- compatible
allOf:
- if:
not:
properties:
compatible:
contains:
enum:
- qcom,sm8450-pmic-glink
- qcom,sm8550-pmic-glink
then:
properties:
orientation-gpios: false
additionalProperties: false
examples:
......
......@@ -15,7 +15,9 @@ properties:
oneOf:
- enum:
- chipidea,usb2
- fsl,imx27-usb
- lsi,zevio-usb
- nuvoton,npcm750-udc
- nvidia,tegra20-ehci
- nvidia,tegra20-udc
- nvidia,tegra30-ehci
......@@ -66,6 +68,10 @@ properties:
- items:
- const: xlnx,zynq-usb-2.20a
- const: chipidea,usb2
- items:
- enum:
- nuvoton,npcm845-udc
- const: nuvoton,npcm750-udc
reg:
minItems: 1
......@@ -388,6 +394,7 @@ allOf:
enum:
- chipidea,usb2
- lsi,zevio-usb
- nuvoton,npcm750-udc
- nvidia,tegra20-udc
- nvidia,tegra30-udc
- nvidia,tegra114-udc
......
......@@ -11,8 +11,12 @@ maintainers:
properties:
compatible:
enum:
- fcs,fsa4480
oneOf:
- const: fcs,fsa4480
- items:
- enum:
- ocs,ocp96011
- const: fcs,fsa4480
reg:
maxItems: 1
......@@ -32,10 +36,43 @@ properties:
type: boolean
port:
$ref: /schemas/graph.yaml#/properties/port
$ref: /schemas/graph.yaml#/$defs/port-base
description:
A port node to link the FSA4480 to a TypeC controller for the purpose of
handling altmode muxing and orientation switching.
unevaluatedProperties: false
properties:
endpoint:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
unevaluatedProperties: false
properties:
data-lanes:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
Specifies how the AUX+/- lines are connected to SBU1/2.
oneOf:
- items:
- const: 0
- const: 1
description: |
Default AUX/SBU layout (FSA4480)
- AUX+ connected to SBU2
- AUX- connected to SBU1
Default AUX/SBU layout (OCP96011)
- AUX+ connected to SBU1
- AUX- connected to SBU2
- items:
- const: 1
- const: 0
description: |
Swapped AUX/SBU layout (FSA4480)
- AUX+ connected to SBU1
- AUX- connected to SBU2
Swapped AUX/SBU layout (OCP96011)
- AUX+ connected to SBU2
- AUX- connected to SBU1
required:
- compatible
......
......@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/usb/genesys,gl850g.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Genesys Logic GL850G USB 2.0 hub controller
title: Genesys Logic USB hub controller
maintainers:
- Icenowy Zheng <uwu@icenowy.me>
......@@ -18,6 +18,7 @@ properties:
- usb5e3,608
- usb5e3,610
- usb5e3,620
- usb5e3,626
reg: true
......
......@@ -19,6 +19,7 @@ properties:
compatible:
items:
- enum:
- nxp,cbdtu02043
- onnn,fsusb43l10x
- pericom,pi3usb102
- const: gpio-sbu-mux
......@@ -50,7 +51,6 @@ required:
- compatible
- enable-gpios
- select-gpios
- mode-switch
- orientation-switch
- port
......
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/nxp,ptn36502.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PTN36502 Type-C USB 3.1 Gen 1 and DisplayPort v1.2 combo redriver
maintainers:
- Luca Weiss <luca.weiss@fairphone.com>
properties:
compatible:
enum:
- nxp,ptn36502
reg:
maxItems: 1
vdd18-supply:
description: Power supply for VDD18 pin
retimer-switch:
description: Flag the port as possible handle of SuperSpeed signals retiming
type: boolean
orientation-switch:
description: Flag the port as possible handler of orientation switching
type: boolean
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Super Speed (SS) Output endpoint to the Type-C connector
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Super Speed (SS) Input endpoint from the Super-Speed PHY
port@2:
$ref: /schemas/graph.yaml#/properties/port
description:
Sideband Use (SBU) AUX lines endpoint to the Type-C connector for the purpose of
handling altmode muxing and orientation switching.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
typec-mux@1a {
compatible = "nxp,ptn36502";
reg = <0x1a>;
vdd18-supply = <&usb_redrive_1v8>;
retimer-switch;
orientation-switch;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
usb_con_ss: endpoint {
remote-endpoint = <&typec_con_ss>;
};
};
port@1 {
reg = <1>;
phy_con_ss: endpoint {
remote-endpoint = <&usb_phy_ss>;
};
};
port@2 {
reg = <2>;
usb_con_sbu: endpoint {
remote-endpoint = <&typec_dp_aux>;
};
};
};
};
};
...
......@@ -14,6 +14,7 @@ properties:
items:
- enum:
- qcom,ipq4019-dwc3
- qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,ipq6018-dwc3
- qcom,ipq8064-dwc3
......@@ -34,6 +35,7 @@ properties:
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
- qcom,sdx65-dwc3
- qcom,sdx75-dwc3
- qcom,sm4250-dwc3
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3
......@@ -180,6 +182,8 @@ allOf:
- qcom,sdm670-dwc3
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
- qcom,sdx65-dwc3
- qcom,sdx75-dwc3
- qcom,sm6350-dwc3
then:
properties:
......@@ -238,6 +242,7 @@ allOf:
compatible:
contains:
enum:
- qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,msm8994-dwc3
- qcom,qcs404-dwc3
......@@ -363,6 +368,7 @@ allOf:
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
- qcom,sdx65-dwc3
- qcom,sdx75-dwc3
- qcom,sm4250-dwc3
- qcom,sm6125-dwc3
- qcom,sm6350-dwc3
......@@ -411,6 +417,7 @@ allOf:
compatible:
contains:
enum:
- qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,sdm660-dwc3
then:
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2023 Realtek Semiconductor Corporation
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/realtek,rtd-dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek DWC3 USB SoC Controller Glue
maintainers:
- Stanley Chang <stanley_chang@realtek.com>
description:
The Realtek DHC SoC embeds a DWC3 USB IP Core configured for USB 2.0
and USB 3.0 in host or dual-role mode.
properties:
compatible:
items:
- enum:
- realtek,rtd1295-dwc3
- realtek,rtd1315e-dwc3
- realtek,rtd1319-dwc3
- realtek,rtd1319d-dwc3
- realtek,rtd1395-dwc3
- realtek,rtd1619-dwc3
- realtek,rtd1619b-dwc3
- const: realtek,rtd-dwc3
reg:
items:
- description: Address and length of register set for wrapper of dwc3 core.
- description: Address and length of register set for pm control.
'#address-cells':
const: 1
'#size-cells':
const: 1
ranges: true
patternProperties:
"^usb@[0-9a-f]+$":
$ref: snps,dwc3.yaml#
description: Required child node
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
- ranges
additionalProperties: false
examples:
- |
usb@98013e00 {
compatible = "realtek,rtd1319d-dwc3", "realtek,rtd-dwc3";
reg = <0x98013e00 0x140>, <0x98013f60 0x4>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
usb@98050000 {
compatible = "snps,dwc3";
reg = <0x98050000 0x9000>;
interrupts = <0 94 4>;
phys = <&usb2phy &usb3phy>;
phy-names = "usb2-phy", "usb3-phy";
dr_mode = "otg";
usb-role-switch;
role-switch-default-mode = "host";
snps,dis_u2_susphy_quirk;
snps,parkmode-disable-ss-quirk;
snps,parkmode-disable-hs-quirk;
maximum-speed = "high-speed";
};
};
......@@ -20,9 +20,6 @@ description:
Type-C PHY
Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
allOf:
- $ref: snps,dwc3.yaml#
select:
properties:
compatible:
......@@ -30,6 +27,7 @@ select:
enum:
- rockchip,rk3328-dwc3
- rockchip,rk3568-dwc3
- rockchip,rk3588-dwc3
required:
- compatible
......@@ -39,6 +37,7 @@ properties:
- enum:
- rockchip,rk3328-dwc3
- rockchip,rk3568-dwc3
- rockchip,rk3588-dwc3
- const: snps,dwc3
reg:
......@@ -58,7 +57,9 @@ properties:
Master/Core clock, must to be >= 62.5 MHz for SS
operation and >= 30MHz for HS operation
- description:
Controller grf clock
Controller grf clock OR UTMI clock
- description:
PIPE clock
clock-names:
minItems: 3
......@@ -66,7 +67,10 @@ properties:
- const: ref_clk
- const: suspend_clk
- const: bus_clk
- const: grf_clk
- enum:
- grf_clk
- utmi
- const: pipe
power-domains:
maxItems: 1
......@@ -86,6 +90,52 @@ required:
- clocks
- clock-names
allOf:
- $ref: snps,dwc3.yaml#
- if:
properties:
compatible:
contains:
const: rockchip,rk3328-dwc3
then:
properties:
clocks:
minItems: 3
maxItems: 4
clock-names:
minItems: 3
items:
- const: ref_clk
- const: suspend_clk
- const: bus_clk
- const: grf_clk
- if:
properties:
compatible:
contains:
const: rockchip,rk3568-dwc3
then:
properties:
clocks:
maxItems: 3
clock-names:
maxItems: 3
- if:
properties:
compatible:
contains:
const: rockchip,rk3588-dwc3
then:
properties:
clock-names:
minItems: 3
items:
- const: ref_clk
- const: suspend_clk
- const: bus_clk
- const: utmi
- const: pipe
examples:
- |
#include <dt-bindings/clock/rk3328-cru.h>
......
......@@ -310,6 +310,62 @@ properties:
maximum: 62
deprecated: true
snps,rx-thr-num-pkt:
description:
USB RX packet threshold count. In host mode, this field specifies
the space that must be available in the RX FIFO before the core can
start the corresponding USB RX transaction (burst).
In device mode, this field specifies the space that must be
available in the RX FIFO before the core can send ERDY for a
flow-controlled endpoint. It is only used for SuperSpeed.
The valid values for this field are from 1 to 15. (DWC3 SuperSpeed
USB 3.0 Controller Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 15
snps,rx-max-burst:
description:
Max USB RX burst size. In host mode, this field specifies the
Maximum Bulk IN burst the DWC_usb3 core can perform. When the system
bus is slower than the USB, RX FIFO can overrun during a long burst.
You can program a smaller value to this field to limit the RX burst
size that the core can perform. It only applies to SS Bulk,
Isochronous, and Interrupt IN endpoints in the host mode.
In device mode, this field specifies the NUMP value that is sent in
ERDY for an OUT endpoint.
The valid values for this field are from 1 to 16. (DWC3 SuperSpeed
USB 3.0 Controller Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-thr-num-pkt:
description:
USB TX packet threshold count. This field specifies the number of
packets that must be in the TXFIFO before the core can start
transmission for the corresponding USB transaction (burst).
This count is valid in both host and device modes. It is only used
for SuperSpeed operation.
Valid values are from 1 to 15. (DWC3 SuperSpeed USB 3.0 Controller
Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 15
snps,tx-max-burst:
description:
Max USB TX burst size. When the system bus is slower than the USB,
TX FIFO can underrun during a long burst. Program a smaller value
to this field to limit the TX burst size that the core can execute.
In Host mode, it only applies to SS Bulk, Isochronous, and Interrupt
OUT endpoints. This value is not used in device mode.
Valid values are from 1 to 16. (DWC3 SuperSpeed USB 3.0 Controller
Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,rx-thr-num-pkt-prd:
description:
Periodic ESS RX packet threshold count (host mode only). Set this and
......
......@@ -20,8 +20,23 @@ properties:
enum:
- ti,tps6598x
- apple,cd321x
- ti,tps25750
reg:
maxItems: 1
minItems: 1
items:
- description: main PD controller address
- description: |
I2C slave address field in PBMs input data
which is used as the device address when writing the
patch for TPS25750.
The patch address can be any value except 0x00, 0x20,
0x21, 0x22, and 0x23
reg-names:
items:
- const: main
- const: patch-address
wakeup-source: true
......@@ -35,10 +50,42 @@ properties:
connector:
$ref: /schemas/connector/usb-connector.yaml#
firmware-name:
description: |
Should contain the name of the default patch binary
file located on the firmware search path which is
used to switch the controller into APP mode.
This is used when tps25750 doesn't have an EEPROM
connected to it.
maxItems: 1
required:
- compatible
- reg
allOf:
- if:
properties:
compatible:
contains:
const: ti,tps25750
then:
properties:
reg:
maxItems: 2
connector:
required:
- data-role
required:
- connector
- reg-names
else:
properties:
reg:
maxItems: 1
additionalProperties: false
examples:
......@@ -71,4 +118,36 @@ examples:
};
};
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
typec@21 {
compatible = "ti,tps25750";
reg = <0x21>, <0x0f>;
reg-names = "main", "patch-address";
interrupt-parent = <&msmgpio>;
interrupts = <100 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "irq";
firmware-name = "tps25750.bin";
pinctrl-names = "default";
pinctrl-0 = <&typec_pins>;
typec_con0: connector {
compatible = "usb-c-connector";
label = "USB-C";
data-role = "dual";
port {
typec_ep0: endpoint {
remote-endpoint = <&otg_ep>;
};
};
};
};
};
...
......@@ -37,7 +37,6 @@ properties:
required:
- compatible
- reg
- reset-gpios
- vdd-supply
- peer-hub
......
......@@ -93,44 +93,18 @@ DMA address space of the device. However, most buffers passed to your
driver can safely be used with such DMA mapping. (See the first section
of Documentation/core-api/dma-api-howto.rst, titled "What memory is DMA-able?")
- When you're using scatterlists, you can map everything at once. On some
systems, this kicks in an IOMMU and turns the scatterlists into single
DMA transactions::
- When you have the scatterlists which have been mapped for the USB controller,
you could use the new ``usb_sg_*()`` calls, which would turn scatterlist
into URBs::
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int nents);
int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
unsigned pipe, unsigned period, struct scatterlist *sg,
int nents, size_t length, gfp_t mem_flags);
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
void usb_sg_wait(struct usb_sg_request *io);
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
void usb_sg_cancel(struct usb_sg_request *io);
It's probably easier to use the new ``usb_sg_*()`` calls, which do the DMA
mapping and apply other tweaks to make scatterlist i/o be fast.
- Some drivers may prefer to work with the model that they're mapping large
buffers, synchronizing their safe re-use. (If there's no re-use, then let
usbcore do the map/unmap.) Large periodic transfers make good examples
here, since it's cheaper to just synchronize the buffer than to unmap it
each time an urb completes and then re-map it on during resubmission.
These calls all work with initialized urbs: ``urb->dev``, ``urb->pipe``,
``urb->transfer_buffer``, and ``urb->transfer_buffer_length`` must all be
valid when these calls are used (``urb->setup_packet`` must be valid too
if urb is a control request)::
struct urb *usb_buffer_map (struct urb *urb);
void usb_buffer_dmasync (struct urb *urb);
void usb_buffer_unmap (struct urb *urb);
The calls manage ``urb->transfer_dma`` for you, and set
``URB_NO_TRANSFER_DMA_MAP`` so that usbcore won't map or unmap the buffer.
They cannot be used for setup_packet buffers in control requests.
Note that several of those interfaces are currently commented out, since
they don't have current users. See the source code. Other than the dmasync
calls (where the underlying DMA primitives have changed), most of them can
easily be commented back in if you want to use them.
When the USB controller doesn't support DMA, the ``usb_sg_init()`` would try
to submit URBs in PIO way as long as the page in scatterlists is not in the
Highmem, which could be very rare in modern architectures.
......@@ -755,6 +755,8 @@ The uac2 function provides these attributes in its function directory:
req_number the number of pre-allocated request for both capture
and playback
function_name name of the interface
c_terminal_type code of the capture terminal type
p_terminal_type code of the playback terminal type
================ ====================================================
The attributes have sane default values.
......
......@@ -59,6 +59,7 @@ pmic-glink {
compatible = "qcom,sm8550-pmic-glink", "qcom,pmic-glink";
#address-cells = <1>;
#size-cells = <0>;
orientation-gpios = <&tlmm 11 GPIO_ACTIVE_HIGH>;
connector@0 {
compatible = "usb-c-connector";
......
......@@ -77,6 +77,7 @@ pmic-glink {
compatible = "qcom,sm8550-pmic-glink", "qcom,pmic-glink";
#address-cells = <1>;
#size-cells = <0>;
orientation-gpios = <&tlmm 11 GPIO_ACTIVE_HIGH>;
connector@0 {
compatible = "usb-c-connector";
......
......@@ -443,6 +443,27 @@ usb_host1_ohci: usb@fc8c0000 {
status = "disabled";
};
usb_host2_xhci: usb@fcd00000 {
compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
reg = <0x0 0xfcd00000 0x0 0x400000>;
interrupts = <GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&cru REF_CLK_USB3OTG2>, <&cru SUSPEND_CLK_USB3OTG2>,
<&cru ACLK_USB3OTG2>, <&cru CLK_UTMI_OTG2>,
<&cru CLK_PIPEPHY2_PIPE_U3_G>;
clock-names = "ref_clk", "suspend_clk", "bus_clk", "utmi", "pipe";
dr_mode = "host";
phys = <&combphy2_psu PHY_TYPE_USB3>;
phy-names = "usb3-phy";
phy_type = "utmi_wide";
resets = <&cru SRST_A_USB3OTG2>;
snps,dis_enblslpm_quirk;
snps,dis-u2-freeclk-exists-quirk;
snps,dis-del-phy-power-chg-quirk;
snps,dis-tx-ipgap-linecheck-quirk;
snps,dis_rxdet_inp3_quirk;
status = "disabled";
};
pmu1grf: syscon@fd58a000 {
compatible = "rockchip,rk3588-pmugrf", "syscon", "simple-mfd";
reg = <0x0 0xfd58a000 0x0 0x10000>;
......
......@@ -1312,9 +1312,9 @@ config GPIO_KEMPLD
config GPIO_LJCA
tristate "INTEL La Jolla Cove Adapter GPIO support"
depends on MFD_LJCA
depends on USB_LJCA
select GPIOLIB_IRQCHIP
default MFD_LJCA
default USB_LJCA
help
Select this option to enable GPIO driver for the INTEL
La Jolla Cove Adapter (LJCA) board.
......
This diff is collapsed.
......@@ -1264,6 +1264,17 @@ config I2C_DLN2
This driver can also be built as a module. If so, the module
will be called i2c-dln2.
config I2C_LJCA
tristate "I2C functionality of Intel La Jolla Cove Adapter"
depends on USB_LJCA
default USB_LJCA
help
If you say yes to this option, I2C functionality support of Intel
La Jolla Cove Adapter (LJCA) will be included.
This driver can also be built as a module. If so, the module
will be called i2c-ljca.
config I2C_CP2615
tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
depends on USB
......
......@@ -133,6 +133,7 @@ obj-$(CONFIG_I2C_GXP) += i2c-gxp.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o
obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel La Jolla Cove Adapter USB-I2C driver
*
* Copyright (c) 2023, Intel Corporation.
*/
#include <linux/acpi.h>
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/usb/ljca.h>
/* I2C init flags */
#define LJCA_I2C_INIT_FLAG_MODE BIT(0)
#define LJCA_I2C_INIT_FLAG_MODE_POLLING FIELD_PREP(LJCA_I2C_INIT_FLAG_MODE, 0)
#define LJCA_I2C_INIT_FLAG_MODE_INTERRUPT FIELD_PREP(LJCA_I2C_INIT_FLAG_MODE, 1)
#define LJCA_I2C_INIT_FLAG_ADDR_16BIT BIT(0)
#define LJCA_I2C_INIT_FLAG_FREQ GENMASK(2, 1)
#define LJCA_I2C_INIT_FLAG_FREQ_100K FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 0)
#define LJCA_I2C_INIT_FLAG_FREQ_400K FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 1)
#define LJCA_I2C_INIT_FLAG_FREQ_1M FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 2)
#define LJCA_I2C_BUF_SIZE 60u
#define LJCA_I2C_MAX_XFER_SIZE (LJCA_I2C_BUF_SIZE - sizeof(struct ljca_i2c_rw_packet))
/* I2C commands */
enum ljca_i2c_cmd {
LJCA_I2C_INIT = 1,
LJCA_I2C_XFER,
LJCA_I2C_START,
LJCA_I2C_STOP,
LJCA_I2C_READ,
LJCA_I2C_WRITE,
};
enum ljca_xfer_type {
LJCA_I2C_WRITE_XFER_TYPE,
LJCA_I2C_READ_XFER_TYPE,
};
/* I2C raw commands: Init/Start/Read/Write/Stop */
struct ljca_i2c_rw_packet {
u8 id;
__le16 len;
u8 data[] __counted_by(len);
} __packed;
struct ljca_i2c_dev {
struct ljca_client *ljca;
struct ljca_i2c_info *i2c_info;
struct i2c_adapter adap;
u8 obuf[LJCA_I2C_BUF_SIZE];
u8 ibuf[LJCA_I2C_BUF_SIZE];
};
static int ljca_i2c_init(struct ljca_i2c_dev *ljca_i2c, u8 id)
{
struct ljca_i2c_rw_packet *w_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
int ret;
w_packet->id = id;
w_packet->len = cpu_to_le16(sizeof(*w_packet->data));
w_packet->data[0] = LJCA_I2C_INIT_FLAG_FREQ_400K;
ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_INIT, (u8 *)w_packet,
struct_size(w_packet, data, 1), NULL, 0);
return ret < 0 ? ret : 0;
}
static int ljca_i2c_start(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr,
enum ljca_xfer_type type)
{
struct ljca_i2c_rw_packet *w_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
struct ljca_i2c_rw_packet *r_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
s16 rp_len;
int ret;
w_packet->id = ljca_i2c->i2c_info->id;
w_packet->len = cpu_to_le16(sizeof(*w_packet->data));
w_packet->data[0] = (slave_addr << 1) | type;
ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_START, (u8 *)w_packet,
struct_size(w_packet, data, 1), (u8 *)r_packet,
LJCA_I2C_BUF_SIZE);
if (ret < 0 || ret < sizeof(*r_packet))
return ret < 0 ? ret : -EIO;
rp_len = le16_to_cpu(r_packet->len);
if (rp_len < 0 || r_packet->id != w_packet->id) {
dev_dbg(&ljca_i2c->adap.dev,
"i2c start failed len: %d id: %d %d\n",
rp_len, r_packet->id, w_packet->id);
return -EIO;
}
return 0;
}
static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr)
{
struct ljca_i2c_rw_packet *w_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
struct ljca_i2c_rw_packet *r_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
s16 rp_len;
int ret;
w_packet->id = ljca_i2c->i2c_info->id;
w_packet->len = cpu_to_le16(sizeof(*w_packet->data));
w_packet->data[0] = 0;
ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_STOP, (u8 *)w_packet,
struct_size(w_packet, data, 1), (u8 *)r_packet,
LJCA_I2C_BUF_SIZE);
if (ret < 0 || ret < sizeof(*r_packet)) {
dev_dbg(&ljca_i2c->adap.dev,
"i2c stop failed ret: %d id: %d\n",
ret, w_packet->id);
return;
}
rp_len = le16_to_cpu(r_packet->len);
if (rp_len < 0 || r_packet->id != w_packet->id)
dev_dbg(&ljca_i2c->adap.dev,
"i2c stop failed len: %d id: %d %d\n",
rp_len, r_packet->id, w_packet->id);
}
static int ljca_i2c_pure_read(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len)
{
struct ljca_i2c_rw_packet *w_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
struct ljca_i2c_rw_packet *r_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
s16 rp_len;
int ret;
w_packet->id = ljca_i2c->i2c_info->id;
w_packet->len = cpu_to_le16(len);
w_packet->data[0] = 0;
ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_READ, (u8 *)w_packet,
struct_size(w_packet, data, 1), (u8 *)r_packet,
LJCA_I2C_BUF_SIZE);
if (ret < 0 || ret < sizeof(*r_packet))
return ret < 0 ? ret : -EIO;
rp_len = le16_to_cpu(r_packet->len);
if (rp_len != len || r_packet->id != w_packet->id) {
dev_dbg(&ljca_i2c->adap.dev,
"i2c raw read failed len: %d id: %d %d\n",
rp_len, r_packet->id, w_packet->id);
return -EIO;
}
memcpy(data, r_packet->data, len);
return 0;
}
static int ljca_i2c_read(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr, u8 *data,
u8 len)
{
int ret;
ret = ljca_i2c_start(ljca_i2c, slave_addr, LJCA_I2C_READ_XFER_TYPE);
if (!ret)
ret = ljca_i2c_pure_read(ljca_i2c, data, len);
ljca_i2c_stop(ljca_i2c, slave_addr);
return ret;
}
static int ljca_i2c_pure_write(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len)
{
struct ljca_i2c_rw_packet *w_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
struct ljca_i2c_rw_packet *r_packet =
(struct ljca_i2c_rw_packet *)ljca_i2c->ibuf;
s16 rplen;
int ret;
w_packet->id = ljca_i2c->i2c_info->id;
w_packet->len = cpu_to_le16(len);
memcpy(w_packet->data, data, len);
ret = ljca_transfer(ljca_i2c->ljca, LJCA_I2C_WRITE, (u8 *)w_packet,
struct_size(w_packet, data, len), (u8 *)r_packet,
LJCA_I2C_BUF_SIZE);
if (ret < 0 || ret < sizeof(*r_packet))
return ret < 0 ? ret : -EIO;
rplen = le16_to_cpu(r_packet->len);
if (rplen != len || r_packet->id != w_packet->id) {
dev_dbg(&ljca_i2c->adap.dev,
"i2c write failed len: %d id: %d/%d\n",
rplen, r_packet->id, w_packet->id);
return -EIO;
}
return 0;
}
static int ljca_i2c_write(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr,
u8 *data, u8 len)
{
int ret;
ret = ljca_i2c_start(ljca_i2c, slave_addr, LJCA_I2C_WRITE_XFER_TYPE);
if (!ret)
ret = ljca_i2c_pure_write(ljca_i2c, data, len);
ljca_i2c_stop(ljca_i2c, slave_addr);
return ret;
}
static int ljca_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
int num)
{
struct ljca_i2c_dev *ljca_i2c;
struct i2c_msg *cur_msg;
int i, ret;
ljca_i2c = i2c_get_adapdata(adapter);
if (!ljca_i2c)
return -EINVAL;
for (i = 0; i < num; i++) {
cur_msg = &msg[i];
if (cur_msg->flags & I2C_M_RD)
ret = ljca_i2c_read(ljca_i2c, cur_msg->addr,
cur_msg->buf, cur_msg->len);
else
ret = ljca_i2c_write(ljca_i2c, cur_msg->addr,
cur_msg->buf, cur_msg->len);
if (ret)
return ret;
}
return num;
}
static u32 ljca_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_adapter_quirks ljca_i2c_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
.max_read_len = LJCA_I2C_MAX_XFER_SIZE,
.max_write_len = LJCA_I2C_MAX_XFER_SIZE,
};
static const struct i2c_algorithm ljca_i2c_algo = {
.master_xfer = ljca_i2c_xfer,
.functionality = ljca_i2c_func,
};
static int ljca_i2c_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *aux_dev_id)
{
struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev);
struct ljca_i2c_dev *ljca_i2c;
int ret;
ljca_i2c = devm_kzalloc(&auxdev->dev, sizeof(*ljca_i2c), GFP_KERNEL);
if (!ljca_i2c)
return -ENOMEM;
ljca_i2c->ljca = ljca;
ljca_i2c->i2c_info = dev_get_platdata(&auxdev->dev);
ljca_i2c->adap.owner = THIS_MODULE;
ljca_i2c->adap.class = I2C_CLASS_HWMON;
ljca_i2c->adap.algo = &ljca_i2c_algo;
ljca_i2c->adap.quirks = &ljca_i2c_quirks;
ljca_i2c->adap.dev.parent = &auxdev->dev;
snprintf(ljca_i2c->adap.name, sizeof(ljca_i2c->adap.name), "%s-%s-%d",
dev_name(&auxdev->dev), dev_name(auxdev->dev.parent),
ljca_i2c->i2c_info->id);
device_set_node(&ljca_i2c->adap.dev, dev_fwnode(&auxdev->dev));
i2c_set_adapdata(&ljca_i2c->adap, ljca_i2c);
auxiliary_set_drvdata(auxdev, ljca_i2c);
ret = ljca_i2c_init(ljca_i2c, ljca_i2c->i2c_info->id);
if (ret)
return dev_err_probe(&auxdev->dev, -EIO,
"i2c init failed id: %d\n",
ljca_i2c->i2c_info->id);
ret = devm_i2c_add_adapter(&auxdev->dev, &ljca_i2c->adap);
if (ret)
return ret;
if (has_acpi_companion(&ljca_i2c->adap.dev))
acpi_dev_clear_dependencies(ACPI_COMPANION(&ljca_i2c->adap.dev));
return 0;
}
static void ljca_i2c_remove(struct auxiliary_device *auxdev)
{
struct ljca_i2c_dev *ljca_i2c = auxiliary_get_drvdata(auxdev);
i2c_del_adapter(&ljca_i2c->adap);
}
static const struct auxiliary_device_id ljca_i2c_id_table[] = {
{ "usb_ljca.ljca-i2c", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(auxiliary, ljca_i2c_id_table);
static struct auxiliary_driver ljca_i2c_driver = {
.probe = ljca_i2c_probe,
.remove = ljca_i2c_remove,
.id_table = ljca_i2c_id_table,
};
module_auxiliary_driver(ljca_i2c_driver);
MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-I2C driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(LJCA);
......@@ -492,6 +492,8 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
{
struct cros_typec_port *port = typec->ports[port_num];
struct typec_displayport_data dp_data;
u32 cable_tbt_vdo;
u32 cable_dp_vdo;
int ret;
if (typec->pd_ctrl_ver < 2) {
......@@ -524,6 +526,32 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
port->state.data = &dp_data;
port->state.mode = TYPEC_MODAL_STATE(ffs(pd_ctrl->dp_mode));
/* Get cable VDO for cables with DPSID to check DPAM2.1 is supported */
cable_dp_vdo = cros_typec_get_cable_vdo(port, USB_TYPEC_DP_SID);
/**
* Get cable VDO for thunderbolt cables and cables with DPSID but does not
* support DPAM2.1.
*/
cable_tbt_vdo = cros_typec_get_cable_vdo(port, USB_TYPEC_TBT_SID);
if (cable_dp_vdo & DP_CAP_DPAM_VERSION) {
dp_data.conf |= cable_dp_vdo;
} else if (cable_tbt_vdo) {
dp_data.conf |= TBT_CABLE_SPEED(cable_tbt_vdo) << DP_CONF_SIGNALLING_SHIFT;
/* Cable Type */
if (cable_tbt_vdo & TBT_CABLE_OPTICAL)
dp_data.conf |= DP_CONF_CABLE_TYPE_OPTICAL << DP_CONF_CABLE_TYPE_SHIFT;
else if (cable_tbt_vdo & TBT_CABLE_RETIMER)
dp_data.conf |= DP_CONF_CABLE_TYPE_RE_TIMER << DP_CONF_CABLE_TYPE_SHIFT;
else if (cable_tbt_vdo & TBT_CABLE_ACTIVE_PASSIVE)
dp_data.conf |= DP_CONF_CABLE_TYPE_RE_DRIVER << DP_CONF_CABLE_TYPE_SHIFT;
} else if (PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_PCABLE) {
dp_data.conf |= VDO_TYPEC_CABLE_SPEED(port->c_identity.vdo[0]) <<
DP_CONF_SIGNALLING_SHIFT;
}
ret = cros_typec_retimer_set(port->retimer, port->state);
if (!ret)
ret = typec_mux_set(port->mux, &port->state);
......
......@@ -237,7 +237,7 @@ static int tps65217_charger_probe(struct platform_device *pdev)
for (i = 0; i < NUM_CHARGER_IRQS; i++) {
ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,
tps65217_charger_irq,
IRQF_ONESHOT, "tps65217-charger",
IRQF_SHARED, "tps65217-charger",
charger);
if (ret) {
dev_err(charger->dev,
......
......@@ -616,6 +616,17 @@ config SPI_FSL_ESPI
From MPC8536, 85xx platform uses the controller, and all P10xx,
P20xx, P30xx,P40xx, P50xx uses this controller.
config SPI_LJCA
tristate "Intel La Jolla Cove Adapter SPI support"
depends on USB_LJCA
default USB_LJCA
help
Select this option to enable SPI driver for the Intel
La Jolla Cove Adapter (LJCA) board.
This driver can also be built as a module. If so, the module
will be called spi-ljca.
config SPI_MESON_SPICC
tristate "Amlogic Meson SPICC controller"
depends on COMMON_CLK
......
......@@ -71,6 +71,7 @@ obj-$(CONFIG_SPI_INTEL_PCI) += spi-intel-pci.o
obj-$(CONFIG_SPI_INTEL_PLATFORM) += spi-intel-platform.o
obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
obj-$(CONFIG_SPI_LJCA) += spi-ljca.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
obj-$(CONFIG_SPI_LOONGSON_CORE) += spi-loongson-core.o
obj-$(CONFIG_SPI_LOONGSON_PCI) += spi-loongson-pci.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel La Jolla Cove Adapter USB-SPI driver
*
* Copyright (c) 2023, Intel Corporation.
*/
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/usb/ljca.h>
#define LJCA_SPI_BUS_MAX_HZ 48000000
#define LJCA_SPI_BUF_SIZE 60u
#define LJCA_SPI_MAX_XFER_SIZE \
(LJCA_SPI_BUF_SIZE - sizeof(struct ljca_spi_xfer_packet))
#define LJCA_SPI_CLK_MODE_POLARITY BIT(0)
#define LJCA_SPI_CLK_MODE_PHASE BIT(1)
#define LJCA_SPI_XFER_INDICATOR_ID GENMASK(5, 0)
#define LJCA_SPI_XFER_INDICATOR_CMPL BIT(6)
#define LJCA_SPI_XFER_INDICATOR_INDEX BIT(7)
/* SPI commands */
enum ljca_spi_cmd {
LJCA_SPI_INIT = 1,
LJCA_SPI_READ,
LJCA_SPI_WRITE,
LJCA_SPI_WRITEREAD,
LJCA_SPI_DEINIT,
};
enum {
LJCA_SPI_BUS_SPEED_24M,
LJCA_SPI_BUS_SPEED_12M,
LJCA_SPI_BUS_SPEED_8M,
LJCA_SPI_BUS_SPEED_6M,
LJCA_SPI_BUS_SPEED_4_8M, /*4.8MHz*/
LJCA_SPI_BUS_SPEED_MIN = LJCA_SPI_BUS_SPEED_4_8M,
};
enum {
LJCA_SPI_CLOCK_LOW_POLARITY,
LJCA_SPI_CLOCK_HIGH_POLARITY,
};
enum {
LJCA_SPI_CLOCK_FIRST_PHASE,
LJCA_SPI_CLOCK_SECOND_PHASE,
};
struct ljca_spi_init_packet {
u8 index;
u8 speed;
u8 mode;
} __packed;
struct ljca_spi_xfer_packet {
u8 indicator;
u8 len;
u8 data[] __counted_by(len);
} __packed;
struct ljca_spi_dev {
struct ljca_client *ljca;
struct spi_controller *controller;
struct ljca_spi_info *spi_info;
u8 speed;
u8 mode;
u8 obuf[LJCA_SPI_BUF_SIZE];
u8 ibuf[LJCA_SPI_BUF_SIZE];
};
static int ljca_spi_read_write(struct ljca_spi_dev *ljca_spi, const u8 *w_data,
u8 *r_data, int len, int id, int complete,
int cmd)
{
struct ljca_spi_xfer_packet *w_packet =
(struct ljca_spi_xfer_packet *)ljca_spi->obuf;
struct ljca_spi_xfer_packet *r_packet =
(struct ljca_spi_xfer_packet *)ljca_spi->ibuf;
int ret;
w_packet->indicator = FIELD_PREP(LJCA_SPI_XFER_INDICATOR_ID, id) |
FIELD_PREP(LJCA_SPI_XFER_INDICATOR_CMPL, complete) |
FIELD_PREP(LJCA_SPI_XFER_INDICATOR_INDEX,
ljca_spi->spi_info->id);
if (cmd == LJCA_SPI_READ) {
w_packet->len = sizeof(u16);
*(__le16 *)&w_packet->data[0] = cpu_to_le16(len);
} else {
w_packet->len = len;
memcpy(w_packet->data, w_data, len);
}
ret = ljca_transfer(ljca_spi->ljca, cmd, (u8 *)w_packet,
struct_size(w_packet, data, w_packet->len),
(u8 *)r_packet, LJCA_SPI_BUF_SIZE);
if (ret < 0)
return ret;
else if (ret < sizeof(*r_packet) || r_packet->len <= 0)
return -EIO;
if (r_data)
memcpy(r_data, r_packet->data, r_packet->len);
return 0;
}
static int ljca_spi_init(struct ljca_spi_dev *ljca_spi, u8 div, u8 mode)
{
struct ljca_spi_init_packet w_packet = {};
int ret;
if (ljca_spi->mode == mode && ljca_spi->speed == div)
return 0;
w_packet.index = ljca_spi->spi_info->id;
w_packet.speed = div;
w_packet.mode = FIELD_PREP(LJCA_SPI_CLK_MODE_POLARITY,
(mode & SPI_CPOL) ? LJCA_SPI_CLOCK_HIGH_POLARITY :
LJCA_SPI_CLOCK_LOW_POLARITY) |
FIELD_PREP(LJCA_SPI_CLK_MODE_PHASE,
(mode & SPI_CPHA) ? LJCA_SPI_CLOCK_SECOND_PHASE :
LJCA_SPI_CLOCK_FIRST_PHASE);
ret = ljca_transfer(ljca_spi->ljca, LJCA_SPI_INIT, (u8 *)&w_packet,
sizeof(w_packet), NULL, 0);
if (ret < 0)
return ret;
ljca_spi->mode = mode;
ljca_spi->speed = div;
return 0;
}
static int ljca_spi_deinit(struct ljca_spi_dev *ljca_spi)
{
struct ljca_spi_init_packet w_packet = {};
int ret;
w_packet.index = ljca_spi->spi_info->id;
ret = ljca_transfer(ljca_spi->ljca, LJCA_SPI_DEINIT, (u8 *)&w_packet,
sizeof(w_packet), NULL, 0);
return ret < 0 ? ret : 0;
}
static inline int ljca_spi_transfer(struct ljca_spi_dev *ljca_spi,
const u8 *tx_data, u8 *rx_data, u16 len)
{
int complete, cur_len;
int remaining = len;
int cmd, ret, i;
int offset = 0;
if (tx_data && rx_data)
cmd = LJCA_SPI_WRITEREAD;
else if (tx_data)
cmd = LJCA_SPI_WRITE;
else if (rx_data)
cmd = LJCA_SPI_READ;
else
return -EINVAL;
for (i = 0; remaining > 0; i++) {
cur_len = min_t(unsigned int, remaining, LJCA_SPI_MAX_XFER_SIZE);
complete = (cur_len == remaining);
ret = ljca_spi_read_write(ljca_spi,
tx_data ? tx_data + offset : NULL,
rx_data ? rx_data + offset : NULL,
cur_len, i, complete, cmd);
if (ret)
return ret;
offset += cur_len;
remaining -= cur_len;
}
return 0;
}
static int ljca_spi_transfer_one(struct spi_controller *controller,
struct spi_device *spi,
struct spi_transfer *xfer)
{
u8 div = DIV_ROUND_UP(controller->max_speed_hz, xfer->speed_hz) / 2 - 1;
struct ljca_spi_dev *ljca_spi = spi_controller_get_devdata(controller);
int ret;
div = min_t(u8, LJCA_SPI_BUS_SPEED_MIN, div);
ret = ljca_spi_init(ljca_spi, div, spi->mode);
if (ret) {
dev_err(&ljca_spi->ljca->auxdev.dev,
"cannot initialize transfer ret %d\n", ret);
return ret;
}
ret = ljca_spi_transfer(ljca_spi, xfer->tx_buf, xfer->rx_buf, xfer->len);
if (ret)
dev_err(&ljca_spi->ljca->auxdev.dev,
"transfer failed len: %d\n", xfer->len);
return ret;
}
static int ljca_spi_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *aux_dev_id)
{
struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev);
struct spi_controller *controller;
struct ljca_spi_dev *ljca_spi;
int ret;
controller = devm_spi_alloc_master(&auxdev->dev, sizeof(*ljca_spi));
if (!controller)
return -ENOMEM;
ljca_spi = spi_controller_get_devdata(controller);
ljca_spi->ljca = ljca;
ljca_spi->spi_info = dev_get_platdata(&auxdev->dev);
ljca_spi->controller = controller;
controller->bus_num = -1;
controller->mode_bits = SPI_CPHA | SPI_CPOL;
controller->transfer_one = ljca_spi_transfer_one;
controller->auto_runtime_pm = false;
controller->max_speed_hz = LJCA_SPI_BUS_MAX_HZ;
device_set_node(&ljca_spi->controller->dev, dev_fwnode(&auxdev->dev));
auxiliary_set_drvdata(auxdev, controller);
ret = spi_register_controller(controller);
if (ret)
dev_err(&auxdev->dev, "Failed to register controller\n");
return ret;
}
static void ljca_spi_dev_remove(struct auxiliary_device *auxdev)
{
struct spi_controller *controller = auxiliary_get_drvdata(auxdev);
struct ljca_spi_dev *ljca_spi = spi_controller_get_devdata(controller);
spi_unregister_controller(controller);
ljca_spi_deinit(ljca_spi);
}
static int ljca_spi_dev_suspend(struct device *dev)
{
struct spi_controller *controller = dev_get_drvdata(dev);
return spi_controller_suspend(controller);
}
static int ljca_spi_dev_resume(struct device *dev)
{
struct spi_controller *controller = dev_get_drvdata(dev);
return spi_controller_resume(controller);
}
static const struct dev_pm_ops ljca_spi_pm = {
SYSTEM_SLEEP_PM_OPS(ljca_spi_dev_suspend, ljca_spi_dev_resume)
};
static const struct auxiliary_device_id ljca_spi_id_table[] = {
{ "usb_ljca.ljca-spi", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(auxiliary, ljca_spi_id_table);
static struct auxiliary_driver ljca_spi_driver = {
.driver.pm = &ljca_spi_pm,
.probe = ljca_spi_probe,
.remove = ljca_spi_dev_remove,
.id_table = ljca_spi_id_table,
};
module_auxiliary_driver(ljca_spi_driver);
MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-SPI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(LJCA);
......@@ -174,6 +174,28 @@ bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx)
return !!(tb_port_clx(port) & clx);
}
/**
* tb_switch_clx_is_supported() - Is CLx supported on this type of router
* @sw: The router to check CLx support for
*/
static bool tb_switch_clx_is_supported(const struct tb_switch *sw)
{
if (!clx_enabled)
return false;
if (sw->quirks & QUIRK_NO_CLX)
return false;
/*
* CLx is not enabled and validated on Intel USB4 platforms
* before Alder Lake.
*/
if (tb_switch_is_tiger_lake(sw))
return false;
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
}
/**
* tb_switch_clx_init() - Initialize router CL states
* @sw: Router
......@@ -273,28 +295,6 @@ static int tb_switch_mask_clx_objections(struct tb_switch *sw)
sw->cap_lp + offset, ARRAY_SIZE(val));
}
/**
* tb_switch_clx_is_supported() - Is CLx supported on this type of router
* @sw: The router to check CLx support for
*/
bool tb_switch_clx_is_supported(const struct tb_switch *sw)
{
if (!clx_enabled)
return false;
if (sw->quirks & QUIRK_NO_CLX)
return false;
/*
* CLx is not enabled and validated on Intel USB4 platforms
* before Alder Lake.
*/
if (tb_switch_is_tiger_lake(sw))
return false;
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
}
static bool validate_mask(unsigned int clx)
{
/* Previous states need to be enabled */
......@@ -405,6 +405,9 @@ int tb_switch_clx_disable(struct tb_switch *sw)
if (!clx)
return 0;
if (sw->is_unplugged)
return clx;
up = tb_upstream_port(sw);
down = tb_switch_downstream_port(sw);
......
......@@ -101,7 +101,7 @@ struct dma_test {
unsigned int packets_sent;
unsigned int packets_received;
unsigned int link_speed;
unsigned int link_width;
enum tb_link_width link_width;
unsigned int crc_errors;
unsigned int buffer_overflow_errors;
enum dma_test_result result;
......@@ -465,9 +465,9 @@ DMA_TEST_DEBUGFS_ATTR(packets_to_send, packets_to_send_get,
static int dma_test_set_bonding(struct dma_test *dt)
{
switch (dt->link_width) {
case 2:
case TB_LINK_WIDTH_DUAL:
return tb_xdomain_lane_bonding_enable(dt->xd);
case 1:
case TB_LINK_WIDTH_SINGLE:
tb_xdomain_lane_bonding_disable(dt->xd);
fallthrough;
default:
......@@ -490,12 +490,8 @@ static void dma_test_check_errors(struct dma_test *dt, int ret)
if (!dt->error_code) {
if (dt->link_speed && dt->xd->link_speed != dt->link_speed) {
dt->error_code = DMA_TEST_SPEED_ERROR;
} else if (dt->link_width) {
const struct tb_xdomain *xd = dt->xd;
if ((dt->link_width == 1 && xd->link_width != TB_LINK_WIDTH_SINGLE) ||
(dt->link_width == 2 && xd->link_width < TB_LINK_WIDTH_DUAL))
dt->error_code = DMA_TEST_WIDTH_ERROR;
} else if (dt->link_width && dt->link_width != dt->xd->link_width) {
dt->error_code = DMA_TEST_WIDTH_ERROR;
} else if (dt->packets_to_send != dt->packets_sent ||
dt->packets_to_receive != dt->packets_received ||
dt->crc_errors || dt->buffer_overflow_errors) {
......
......@@ -19,9 +19,9 @@ static void tb_dump_hop(const struct tb_path_hop *hop, const struct tb_regs_hop
tb_port_dbg(port, " In HopID: %d => Out port: %d Out HopID: %d\n",
hop->in_hop_index, regs->out_port, regs->next_hop);
tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n",
regs->weight, regs->priority,
regs->initial_credits, regs->drop_packages);
tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d PM: %d\n",
regs->weight, regs->priority, regs->initial_credits,
regs->drop_packages, regs->pmps);
tb_port_dbg(port, " Counter enabled: %d Counter index: %d\n",
regs->counter_enable, regs->counter);
tb_port_dbg(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n",
......@@ -535,6 +535,7 @@ int tb_path_activate(struct tb_path *path)
hop.next_hop = path->hops[i].next_hop_index;
hop.out_port = path->hops[i].out_port->port;
hop.initial_credits = path->hops[i].initial_credits;
hop.pmps = path->hops[i].pm_support;
hop.unknown1 = 0;
hop.enable = 1;
......
......@@ -31,6 +31,9 @@ static void quirk_usb3_maximum_bandwidth(struct tb_switch *sw)
{
struct tb_port *port;
if (tb_switch_is_icm(sw))
return;
tb_switch_for_each_port(sw, port) {
if (!tb_port_is_usb3_down(port))
continue;
......
......@@ -94,6 +94,7 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)
goto err_nvm;
rt->nvm = nvm;
dev_dbg(&rt->dev, "NVM version %x.%x\n", nvm->major, nvm->minor);
return 0;
err_nvm:
......
This diff is collapsed.
This diff is collapsed.
......@@ -162,11 +162,6 @@ struct tb_switch_tmu {
* switches) you need to have domain lock held.
*
* In USB4 terminology this structure represents a router.
*
* Note @link_width is not the same as whether link is bonded or not.
* For Gen 4 links the link is also bonded when it is asymmetric. The
* correct way to find out whether the link is bonded or not is to look
* @bonded field of the upstream port.
*/
struct tb_switch {
struct device dev;
......@@ -348,6 +343,7 @@ struct tb_retimer {
* the path
* @nfc_credits: Number of non-flow controlled buffers allocated for the
* @in_port.
* @pm_support: Set path PM packet support bit to 1 (for USB4 v2 routers)
*
* Hop configuration is always done on the IN port of a switch.
* in_port and out_port have to be on the same switch. Packets arriving on
......@@ -368,6 +364,7 @@ struct tb_path_hop {
int next_hop_index;
unsigned int initial_credits;
unsigned int nfc_credits;
bool pm_support;
};
/**
......@@ -864,6 +861,15 @@ static inline struct tb_port *tb_switch_downstream_port(struct tb_switch *sw)
return tb_port_at(tb_route(sw), tb_switch_parent(sw));
}
/**
* tb_switch_depth() - Returns depth of the connected router
* @sw: Router
*/
static inline int tb_switch_depth(const struct tb_switch *sw)
{
return sw->config.depth;
}
static inline bool tb_switch_is_light_ridge(const struct tb_switch *sw)
{
return sw->config.vendor_id == PCI_VENDOR_ID_INTEL &&
......@@ -956,8 +962,7 @@ static inline bool tb_switch_is_icm(const struct tb_switch *sw)
return !sw->config.enabled;
}
int tb_switch_lane_bonding_enable(struct tb_switch *sw);
void tb_switch_lane_bonding_disable(struct tb_switch *sw);
int tb_switch_set_link_width(struct tb_switch *sw, enum tb_link_width width);
int tb_switch_configure_link(struct tb_switch *sw);
void tb_switch_unconfigure_link(struct tb_switch *sw);
......@@ -1001,7 +1006,6 @@ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw)
bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx);
int tb_switch_clx_init(struct tb_switch *sw);
bool tb_switch_clx_is_supported(const struct tb_switch *sw);
int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx);
int tb_switch_clx_disable(struct tb_switch *sw);
......@@ -1040,6 +1044,21 @@ void tb_port_release_out_hopid(struct tb_port *port, int hopid);
struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
struct tb_port *prev);
/**
* tb_port_path_direction_downstream() - Checks if path directed downstream
* @src: Source adapter
* @dst: Destination adapter
*
* Returns %true only if the specified path from source adapter (@src)
* to destination adapter (@dst) is directed downstream.
*/
static inline bool
tb_port_path_direction_downstream(const struct tb_port *src,
const struct tb_port *dst)
{
return src->sw->config.depth < dst->sw->config.depth;
}
static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
{
return tb_port_is_null(port) && port->sw->credit_allocation;
......@@ -1057,12 +1076,29 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
for ((p) = tb_next_port_on_path((src), (dst), NULL); (p); \
(p) = tb_next_port_on_path((src), (dst), (p)))
/**
* tb_for_each_upstream_port_on_path() - Iterate over each upstreamm port on path
* @src: Source port
* @dst: Destination port
* @p: Port used as iterator
*
* Walks over each upstream lane adapter on path from @src to @dst.
*/
#define tb_for_each_upstream_port_on_path(src, dst, p) \
for ((p) = tb_next_port_on_path((src), (dst), NULL); (p); \
(p) = tb_next_port_on_path((src), (dst), (p))) \
if (!tb_port_is_null((p)) || !tb_is_upstream_port((p))) {\
continue; \
} else
int tb_port_get_link_speed(struct tb_port *port);
int tb_port_get_link_generation(struct tb_port *port);
int tb_port_get_link_width(struct tb_port *port);
bool tb_port_width_supported(struct tb_port *port, unsigned int width);
int tb_port_set_link_width(struct tb_port *port, enum tb_link_width width);
int tb_port_lane_bonding_enable(struct tb_port *port);
void tb_port_lane_bonding_disable(struct tb_port *port);
int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask,
int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width,
int timeout_msec);
int tb_port_update_credits(struct tb_port *port);
......@@ -1256,6 +1292,11 @@ int usb4_port_router_online(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port);
bool usb4_port_clx_supported(struct tb_port *port);
int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
bool usb4_port_asym_supported(struct tb_port *port);
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width);
int usb4_port_asym_start(struct tb_port *port);
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
unsigned int ber_level, bool timing, bool right_high,
u32 *results);
......@@ -1283,7 +1324,6 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index,
unsigned int address, void *buf, size_t size);
int usb4_usb3_port_max_link_rate(struct tb_port *port);
int usb4_usb3_port_actual_link_rate(struct tb_port *port);
int usb4_usb3_port_allocated_bandwidth(struct tb_port *port, int *upstream_bw,
int *downstream_bw);
int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw,
......
......@@ -346,10 +346,14 @@ struct tb_regs_port_header {
#define LANE_ADP_CS_1 0x01
#define LANE_ADP_CS_1_TARGET_SPEED_MASK GENMASK(3, 0)
#define LANE_ADP_CS_1_TARGET_SPEED_GEN3 0xc
#define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(9, 4)
#define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(5, 4)
#define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT 4
#define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE 0x1
#define LANE_ADP_CS_1_TARGET_WIDTH_DUAL 0x3
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK GENMASK(7, 6)
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_TX 0x1
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_RX 0x2
#define LANE_ADP_CS_1_TARGET_WIDTH_ASYM_DUAL 0x0
#define LANE_ADP_CS_1_CL0S_ENABLE BIT(10)
#define LANE_ADP_CS_1_CL1_ENABLE BIT(11)
#define LANE_ADP_CS_1_CL2_ENABLE BIT(12)
......@@ -382,12 +386,15 @@ struct tb_regs_port_header {
#define PORT_CS_18_WOCS BIT(16)
#define PORT_CS_18_WODS BIT(17)
#define PORT_CS_18_WOU4S BIT(18)
#define PORT_CS_18_CSA BIT(22)
#define PORT_CS_18_TIP BIT(24)
#define PORT_CS_19 0x13
#define PORT_CS_19_PC BIT(3)
#define PORT_CS_19_PID BIT(4)
#define PORT_CS_19_WOC BIT(16)
#define PORT_CS_19_WOD BIT(17)
#define PORT_CS_19_WOU4 BIT(18)
#define PORT_CS_19_START_ASYM BIT(24)
/* Display Port adapter registers */
#define ADP_DP_CS_0 0x00
......@@ -400,7 +407,7 @@ struct tb_regs_port_header {
#define ADP_DP_CS_1_AUX_RX_HOPID_SHIFT 11
#define ADP_DP_CS_2 0x02
#define ADP_DP_CS_2_NRD_MLC_MASK GENMASK(2, 0)
#define ADP_DP_CS_2_HDP BIT(6)
#define ADP_DP_CS_2_HPD BIT(6)
#define ADP_DP_CS_2_NRD_MLR_MASK GENMASK(9, 7)
#define ADP_DP_CS_2_NRD_MLR_SHIFT 7
#define ADP_DP_CS_2_CA BIT(10)
......@@ -417,7 +424,7 @@ struct tb_regs_port_header {
#define ADP_DP_CS_2_ESTIMATED_BW_MASK GENMASK(31, 24)
#define ADP_DP_CS_2_ESTIMATED_BW_SHIFT 24
#define ADP_DP_CS_3 0x03
#define ADP_DP_CS_3_HDPC BIT(9)
#define ADP_DP_CS_3_HPDC BIT(9)
#define DP_LOCAL_CAP 0x04
#define DP_REMOTE_CAP 0x05
/* For DP IN adapter */
......@@ -484,9 +491,6 @@ struct tb_regs_port_header {
#define ADP_USB3_CS_3 0x03
#define ADP_USB3_CS_3_SCALE_MASK GENMASK(5, 0)
#define ADP_USB3_CS_4 0x04
#define ADP_USB3_CS_4_ALR_MASK GENMASK(6, 0)
#define ADP_USB3_CS_4_ALR_20G 0x1
#define ADP_USB3_CS_4_ULV BIT(7)
#define ADP_USB3_CS_4_MSLR_MASK GENMASK(18, 12)
#define ADP_USB3_CS_4_MSLR_SHIFT 12
#define ADP_USB3_CS_4_MSLR_20G 0x1
......@@ -499,7 +503,8 @@ struct tb_regs_hop {
* out_port (on the incoming port of the next switch)
*/
u32 out_port:6; /* next port of the path (on the same switch) */
u32 initial_credits:8;
u32 initial_credits:7;
u32 pmps:1;
u32 unknown1:6; /* set to zero */
bool enable:1;
......
This diff is collapsed.
......@@ -80,6 +80,8 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
bool alloc_hopid);
struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
struct tb_port *down);
bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up,
int *reserved_down);
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
bool alloc_hopid);
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
......@@ -137,5 +139,27 @@ static inline bool tb_tunnel_is_usb3(const struct tb_tunnel *tunnel)
return tunnel->type == TB_TUNNEL_USB3;
}
#endif
const char *tb_tunnel_type_name(const struct tb_tunnel *tunnel);
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
do { \
struct tb_tunnel *__tunnel = (tunnel); \
level(__tunnel->tb, "%llx:%u <-> %llx:%u (%s): " fmt, \
tb_route(__tunnel->src_port->sw), \
__tunnel->src_port->port, \
tb_route(__tunnel->dst_port->sw), \
__tunnel->dst_port->port, \
tb_tunnel_type_name(__tunnel), \
## arg); \
} while (0)
#define tb_tunnel_WARN(tunnel, fmt, arg...) \
__TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg)
#define tb_tunnel_warn(tunnel, fmt, arg...) \
__TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg)
#define tb_tunnel_info(tunnel, fmt, arg...) \
__TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
#define tb_tunnel_dbg(tunnel, fmt, arg...) \
__TB_TUNNEL_PRINT(tb_dbg, tunnel, fmt, ##arg)
#endif
......@@ -1454,6 +1454,112 @@ bool usb4_port_clx_supported(struct tb_port *port)
return !!(val & PORT_CS_18_CPS);
}
/**
* usb4_port_asym_supported() - If the port supports asymmetric link
* @port: USB4 port
*
* Checks if the port and the cable supports asymmetric link and returns
* %true in that case.
*/
bool usb4_port_asym_supported(struct tb_port *port)
{
u32 val;
if (!port->cap_usb4)
return false;
if (tb_port_read(port, &val, TB_CFG_PORT, port->cap_usb4 + PORT_CS_18, 1))
return false;
return !!(val & PORT_CS_18_CSA);
}
/**
* usb4_port_asym_set_link_width() - Set link width to asymmetric or symmetric
* @port: USB4 port
* @width: Asymmetric width to configure
*
* Sets USB4 port link width to @width. Can be called for widths where
* usb4_port_asym_width_supported() returned @true.
*/
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width)
{
u32 val;
int ret;
if (!port->cap_phy)
return -EINVAL;
ret = tb_port_read(port, &val, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1);
if (ret)
return ret;
val &= ~LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK;
switch (width) {
case TB_LINK_WIDTH_DUAL:
val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK,
LANE_ADP_CS_1_TARGET_WIDTH_ASYM_DUAL);
break;
case TB_LINK_WIDTH_ASYM_TX:
val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK,
LANE_ADP_CS_1_TARGET_WIDTH_ASYM_TX);
break;
case TB_LINK_WIDTH_ASYM_RX:
val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK,
LANE_ADP_CS_1_TARGET_WIDTH_ASYM_RX);
break;
default:
return -EINVAL;
}
return tb_port_write(port, &val, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1);
}
/**
* usb4_port_asym_start() - Start symmetry change and wait for completion
* @port: USB4 port
*
* Start symmetry change of the link to asymmetric or symmetric
* (according to what was previously set in tb_port_set_link_width().
* Wait for completion of the change.
*
* Returns %0 in case of success, %-ETIMEDOUT if case of timeout or
* a negative errno in case of a failure.
*/
int usb4_port_asym_start(struct tb_port *port)
{
int ret;
u32 val;
ret = tb_port_read(port, &val, TB_CFG_PORT,
port->cap_usb4 + PORT_CS_19, 1);
if (ret)
return ret;
val &= ~PORT_CS_19_START_ASYM;
val |= FIELD_PREP(PORT_CS_19_START_ASYM, 1);
ret = tb_port_write(port, &val, TB_CFG_PORT,
port->cap_usb4 + PORT_CS_19, 1);
if (ret)
return ret;
/*
* Wait for PORT_CS_19_START_ASYM to be 0. This means the USB4
* port started the symmetry transition.
*/
ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_19,
PORT_CS_19_START_ASYM, 0, 1000);
if (ret)
return ret;
/* Then wait for the transtion to be completed */
return usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_18,
PORT_CS_18_TIP, 0, 5000);
}
/**
* usb4_port_margining_caps() - Read USB4 port marginig capabilities
* @port: USB4 port
......@@ -1946,35 +2052,6 @@ int usb4_usb3_port_max_link_rate(struct tb_port *port)
return usb4_usb3_port_max_bandwidth(port, ret);
}
/**
* usb4_usb3_port_actual_link_rate() - Established USB3 link rate
* @port: USB3 adapter port
*
* Return actual established link rate of a USB3 adapter in Mb/s. If the
* link is not up returns %0 and negative errno in case of failure.
*/
int usb4_usb3_port_actual_link_rate(struct tb_port *port)
{
int ret, lr;
u32 val;
if (!tb_port_is_usb3_down(port) && !tb_port_is_usb3_up(port))
return -EINVAL;
ret = tb_port_read(port, &val, TB_CFG_PORT,
port->cap_adap + ADP_USB3_CS_4, 1);
if (ret)
return ret;
if (!(val & ADP_USB3_CS_4_ULV))
return 0;
lr = val & ADP_USB3_CS_4_ALR_MASK;
ret = lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000;
return usb4_usb3_port_max_bandwidth(port, ret);
}
static int usb4_usb3_port_cm_request(struct tb_port *port, bool request)
{
int ret;
......
......@@ -91,6 +91,16 @@ config USB_PCI
If you have such a device you may say N here and PCI related code
will not be built in the USB driver.
config USB_PCI_AMD
bool "AMD PCI USB host support"
depends on USB_PCI && HAS_IOPORT
default X86 || MACH_LOONGSON64 || PPC_PASEMI
help
Enable workarounds for USB implementation quirks in SB600/SB700/SB800
and later south bridge implementations. These are common on x86 PCs
with AMD CPUs but rarely used elsewhere, with the exception of a few
powerpc and mips desktop machines.
if USB
source "drivers/usb/core/Kconfig"
......
......@@ -109,7 +109,6 @@ int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
void c67x00_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
void c67x00_hcd_msg_received(struct c67x00_sie *sie, u16 msg);
void c67x00_sched_kick(struct c67x00_hcd *c67x00);
int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00);
void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00);
......
......@@ -131,8 +131,6 @@ static inline const char *cdnsp_trb_type_string(u8 type)
return "Endpoint Not ready";
case TRB_HALT_ENDPOINT:
return "Halt Endpoint";
case TRB_FLUSH_ENDPOINT:
return "FLush Endpoint";
default:
return "UNKNOWN";
}
......@@ -328,7 +326,6 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
break;
case TRB_RESET_EP:
case TRB_HALT_ENDPOINT:
case TRB_FLUSH_ENDPOINT:
ret = snprintf(str, size,
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type),
......
......@@ -1024,10 +1024,8 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep)
pep->ep_state |= EP_DIS_IN_RROGRESS;
/* Endpoint was unconfigured by Reset Device command. */
if (!(pep->ep_state & EP_UNCONFIGURED)) {
if (!(pep->ep_state & EP_UNCONFIGURED))
cdnsp_cmd_stop_ep(pdev, pep);
cdnsp_cmd_flush_ep(pdev, pep);
}
/* Remove all queued USB requests. */
while (!list_empty(&pep->pending_list)) {
......@@ -1424,8 +1422,6 @@ static void cdnsp_stop(struct cdnsp_device *pdev)
{
u32 temp;
cdnsp_cmd_flush_ep(pdev, &pdev->eps[0]);
/* Remove internally queued request for ep0. */
if (!list_empty(&pdev->eps[0].pending_list)) {
struct cdnsp_request *req;
......
......@@ -1128,8 +1128,6 @@ union cdnsp_trb {
#define TRB_HALT_ENDPOINT 54
/* Doorbell Overflow Event. */
#define TRB_DRB_OVERFLOW 57
/* Flush Endpoint Command. */
#define TRB_FLUSH_ENDPOINT 58
#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
......@@ -1539,8 +1537,6 @@ void cdnsp_queue_configure_endpoint(struct cdnsp_device *pdev,
void cdnsp_queue_reset_ep(struct cdnsp_device *pdev, unsigned int ep_index);
void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev,
unsigned int ep_index);
void cdnsp_queue_flush_endpoint(struct cdnsp_device *pdev,
unsigned int ep_index);
void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num);
void cdnsp_queue_reset_device(struct cdnsp_device *pdev);
void cdnsp_queue_new_dequeue_state(struct cdnsp_device *pdev,
......@@ -1574,7 +1570,6 @@ void cdnsp_irq_reset(struct cdnsp_device *pdev);
int cdnsp_halt_endpoint(struct cdnsp_device *pdev,
struct cdnsp_ep *pep, int value);
int cdnsp_cmd_stop_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep);
int cdnsp_cmd_flush_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep);
void cdnsp_setup_analyze(struct cdnsp_device *pdev);
int cdnsp_status_stage(struct cdnsp_device *pdev);
int cdnsp_reset_device(struct cdnsp_device *pdev);
......
......@@ -2123,19 +2123,6 @@ int cdnsp_cmd_stop_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep)
return ret;
}
int cdnsp_cmd_flush_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep)
{
int ret;
cdnsp_queue_flush_endpoint(pdev, pep->idx);
cdnsp_ring_cmd_db(pdev);
ret = cdnsp_wait_for_cmd_compl(pdev);
trace_cdnsp_handle_cmd_flush_ep(pep->out_ctx);
return ret;
}
/*
* The transfer burst count field of the isochronous TRB defines the number of
* bursts that are required to move all packets in this TD. Only SuperSpeed
......@@ -2465,17 +2452,6 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index)
EP_ID_FOR_TRB(ep_index));
}
/*
* Queue a flush endpoint request on the command ring.
*/
void cdnsp_queue_flush_endpoint(struct cdnsp_device *pdev,
unsigned int ep_index)
{
cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_FLUSH_ENDPOINT) |
SLOT_ID_FOR_TRB(pdev->slot_id) |
EP_ID_FOR_TRB(ep_index));
}
void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num)
{
u32 lo, mid;
......
......@@ -43,6 +43,10 @@ config USB_CHIPIDEA_MSM
tristate "Enable MSM hsusb glue driver" if EXPERT
default USB_CHIPIDEA
config USB_CHIPIDEA_NPCM
tristate "Enable NPCM hsusb glue driver" if EXPERT
default USB_CHIPIDEA
config USB_CHIPIDEA_IMX
tristate "Enable i.MX USB glue driver" if EXPERT
depends on OF
......
......@@ -13,6 +13,7 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
obj-$(CONFIG_USB_CHIPIDEA_GENERIC) += ci_hdrc_usb2.o
obj-$(CONFIG_USB_CHIPIDEA_MSM) += ci_hdrc_msm.o
obj-$(CONFIG_USB_CHIPIDEA_NPCM) += ci_hdrc_npcm.o
obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
obj-$(CONFIG_USB_CHIPIDEA_IMX) += usbmisc_imx.o ci_hdrc_imx.o
obj-$(CONFIG_USB_CHIPIDEA_TEGRA) += ci_hdrc_tegra.o
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2023 Nuvoton Technology corporation.
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/usb/chipidea.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/reset-controller.h>
#include <linux/of.h>
#include "ci.h"
struct npcm_udc_data {
struct platform_device *ci;
struct clk *core_clk;
struct ci_hdrc_platform_data pdata;
};
static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
{
struct device *dev = ci->dev->parent;
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
/* clear all mode bits */
hw_write(ci, OP_USBMODE, 0xffffffff, 0x0);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event (%d)\n",event);
break;
}
return 0;
}
static int npcm_udc_probe(struct platform_device *pdev)
{
int ret;
struct npcm_udc_data *ci;
struct platform_device *plat_ci;
struct device *dev = &pdev->dev;
ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
if (!ci)
return -ENOMEM;
platform_set_drvdata(pdev, ci);
ci->core_clk = devm_clk_get_optional(dev, NULL);
if (IS_ERR(ci->core_clk))
return PTR_ERR(ci->core_clk);
ret = clk_prepare_enable(ci->core_clk);
if (ret)
return dev_err_probe(dev, ret, "failed to enable the clock: %d\n", ret);
ci->pdata.name = dev_name(dev);
ci->pdata.capoffset = DEF_CAPOFFSET;
ci->pdata.flags = CI_HDRC_REQUIRES_ALIGNED_DMA |
CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS;
ci->pdata.phy_mode = USBPHY_INTERFACE_MODE_UTMI;
ci->pdata.notify_event = npcm_udc_notify_event;
plat_ci = ci_hdrc_add_device(dev, pdev->resource, pdev->num_resources,
&ci->pdata);
if (IS_ERR(plat_ci)) {
ret = PTR_ERR(plat_ci);
dev_err(dev, "failed to register HDRC NPCM device: %d\n", ret);
goto clk_err;
}
pm_runtime_no_callbacks(dev);
pm_runtime_enable(dev);
return 0;
clk_err:
clk_disable_unprepare(ci->core_clk);
return ret;
}
static int npcm_udc_remove(struct platform_device *pdev)
{
struct npcm_udc_data *ci = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
ci_hdrc_remove_device(ci->ci);
clk_disable_unprepare(ci->core_clk);
return 0;
}
static const struct of_device_id npcm_udc_dt_match[] = {
{ .compatible = "nuvoton,npcm750-udc", },
{ .compatible = "nuvoton,npcm845-udc", },
{ }
};
MODULE_DEVICE_TABLE(of, npcm_udc_dt_match);
static struct platform_driver npcm_udc_driver = {
.probe = npcm_udc_probe,
.remove = npcm_udc_remove,
.driver = {
.name = "npcm_udc",
.of_match_table = npcm_udc_dt_match,
},
};
module_platform_driver(npcm_udc_driver);
MODULE_DESCRIPTION("NPCM USB device controller driver");
MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
MODULE_LICENSE("GPL v2");
......@@ -293,14 +293,12 @@ static int tegra_usb_probe(struct platform_device *pdev)
usb->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(usb->phy))
return dev_err_probe(&pdev->dev, PTR_ERR(usb->phy),
"failed to get PHY\n");
"failed to get PHY");
usb->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(usb->clk)) {
err = PTR_ERR(usb->clk);
dev_err(&pdev->dev, "failed to get clock: %d\n", err);
return err;
}
if (IS_ERR(usb->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(usb->clk),
"failed to get clock");
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
......@@ -316,7 +314,7 @@ static int tegra_usb_probe(struct platform_device *pdev)
err = tegra_usb_reset_controller(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "failed to reset controller: %d\n", err);
dev_err_probe(&pdev->dev, err, "failed to reset controller");
goto fail_power_off;
}
......@@ -347,8 +345,8 @@ static int tegra_usb_probe(struct platform_device *pdev)
usb->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
pdev->num_resources, &usb->data);
if (IS_ERR(usb->dev)) {
err = PTR_ERR(usb->dev);
dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err);
err = dev_err_probe(&pdev->dev, PTR_ERR(usb->dev),
"failed to add HDRC device");
goto phy_shutdown;
}
......
......@@ -9,9 +9,9 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/usb/chipidea.h>
#include <linux/usb/hcd.h>
#include <linux/usb/ulpi.h>
......@@ -51,8 +51,8 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct ci_hdrc_usb2_priv *priv;
struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
const struct ci_hdrc_platform_data *data;
int ret;
const struct of_device_id *match;
if (!ci_pdata) {
ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL);
......@@ -61,11 +61,10 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
*ci_pdata = ci_default_pdata; /* struct copy */
}
match = of_match_device(ci_hdrc_usb2_of_match, &pdev->dev);
if (match && match->data) {
data = device_get_match_data(&pdev->dev);
if (data)
/* struct copy */
*ci_pdata = *(struct ci_hdrc_platform_data *)match->data;
}
*ci_pdata = *data;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
......@@ -120,7 +119,7 @@ static struct platform_driver ci_hdrc_usb2_driver = {
.remove_new = ci_hdrc_usb2_remove,
.driver = {
.name = "chipidea-usb2",
.of_match_table = of_match_ptr(ci_hdrc_usb2_of_match),
.of_match_table = ci_hdrc_usb2_of_match,
},
};
module_platform_driver(ci_hdrc_usb2_driver);
......
This diff is collapsed.
......@@ -130,8 +130,11 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
if (!ci->is_otg)
if (!ci->is_otg) {
if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS)
usb_gadget_vbus_connect(&ci->gadget);
return;
}
if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget);
......
......@@ -206,8 +206,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
goto free_irq_vectors;
}
hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
hcd->amd_resume_bug = usb_hcd_amd_resume_bug(dev, driver);
if (driver->flags & HCD_MEMORY) {
/* EHCI, OHCI */
......
......@@ -2274,6 +2274,8 @@ void usb_disconnect(struct usb_device **pdev)
*/
if (!test_and_set_bit(port1, hub->child_usage_bits))
pm_runtime_get_sync(&port_dev->dev);
typec_deattach(port_dev->connector, &udev->dev);
}
usb_remove_ep_devs(&udev->ep0);
......@@ -2620,6 +2622,8 @@ int usb_new_device(struct usb_device *udev)
if (!test_and_set_bit(port1, hub->child_usage_bits))
pm_runtime_get_sync(&port_dev->dev);
typec_attach(port_dev->connector, &udev->dev);
}
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
......
This diff is collapsed.
This diff is collapsed.
......@@ -4769,8 +4769,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
if (qh_allocated && qh->channel && qh->channel->qh == qh)
qh->channel->qh = NULL;
fail2:
spin_unlock_irqrestore(&hsotg->lock, flags);
urb->hcpriv = NULL;
spin_unlock_irqrestore(&hsotg->lock, flags);
kfree(qtd);
fail1:
if (qh_allocated) {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment