2014-11-25 00:17:33

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 00/12] Tegra xHCI support

This series adds support for xHCI on NVIDIA Tegra SoCs. This includes:
- patches 1, 2, and 3: minor cleanups for mailbox framework and xHCI,
- patches 4 and 5: adding a driver for the mailbox used to communicate
with the xHCI controller's firmware,
- patches 6 and 7: extending the XUSB pad controller driver to support
the USB PHY types (UTMI, HSIC, and USB3),
- patches 8 and 9: adding a xHCI host-controller driver, and
- patches 10, 11, and 12: updating the relevant DT files.

The mailbox driver (patch 5) has a compile-time dependency on patch 2 and
a run-time dependency on patch 3. Both the PHY (patch 7) and host (patch 9)
drivers have compile-time dependencies on the mailbox driver. The host
driver also has a run-time dependency on patch 1. Because of this, this
entire series should probably go through the Tegra tree. Patches 11 and 12
should probably not be merged until the controller firmware [0] lands in
linux-firmware since they disable the EHCI controllers.

Tested on Venice2, Jetson TK1, and Big with a variety of USB2.0 and
USB3.0 memory sticks and ethernet dongles. This has also been tested,
with additional out-of-tree patches, on a Tegra132-based board.

Based on v3.18-rc6. A branch with the entire series is available at:
https://github.com/abrestic/linux/tree/tegra-xhci-v6

Notes:
- HSIC support is mostly untested and I think there are still some issues
to work out there. I do have a Tegra124 board with a HSIC hub so I'll
try to sort those out later.
- The XUSB padctl driver doesn't play nice with the existing Tegra USB2.0
PHY driver, so all ports should be assigned to the XHCI controller.

Based on work by:
a lot of people, but from what I can tell from the L4T tree [1], the
original authors of the Tegra xHCI driver are:
Ajay Gupta <[email protected]>
Bharath Yadav <[email protected]>

[0] https://patchwork.ozlabs.org/patch/400110/
[1] git://nv-tegra.nvidia.com/linux-3.10.git

Changes from v5:
- Addressed review comments from Jassi and Felipe.

Changes from v4:
- Made USB support optional in padctl driver.
- Made usb3-port a pinconfig property again.
- Cleaned up mbox_request_channel() error handling and allowed it to defer
probing (patch 3).
- Minor xHCI (patch 1) and mailbox framework (patch 2) cleanups suggested
by Thierry.
- Addressed Thierry's review comments.

Changes from v3:
- Fixed USB2.0 flakiness on Jetson-TK1.
- Switched to 32-bit DMA mask for host.
- Addressed Stephen's review comments.

Chagnes from v2:
- Dropped mailbox channel specifier. The mailbox driver allocates virtual
channels backed by the single physical channel.
- Added support for HS_CURR_LEVEL adjustment pinconfig property, which
will be required for the Blaze board.
- Addressed Stephen's review comments.

Changes from v1:
- Converted mailbox driver to use the common mailbox framework.
- Fixed up host driver so that it can now be built and used as a module.
- Addressed Stephen's review comments.
- Misc. cleanups.

Andrew Bresticker (11):
xhci: Set shared HCD's hcd_priv in xhci_gen_setup
mailbox: Make struct mbox_controller's ops field const
of: Add NVIDIA Tegra XUSB mailbox binding
mailbox: Add NVIDIA Tegra XUSB mailbox driver
of: Update Tegra XUSB pad controller binding for USB
pinctrl: tegra-xusb: Add USB PHY support
of: Add NVIDIA Tegra xHCI controller binding
usb: xhci: Add NVIDIA Tegra xHCI host-controller driver
ARM: tegra: jetson-tk1: Add xHCI support
ARM: tegra: Add Tegra124 XUSB mailbox and xHCI controller
ARM: tegra: venice2: Add xHCI support

Benson Leung (1):
mailbox: Fix up error handling in mbox_request_channel()

.../bindings/mailbox/nvidia,tegra124-xusb-mbox.txt | 32 +
.../pinctrl/nvidia,tegra124-xusb-padctl.txt | 63 +-
.../bindings/usb/nvidia,tegra124-xhci.txt | 104 ++
arch/arm/boot/dts/tegra124-jetson-tk1.dts | 46 +-
arch/arm/boot/dts/tegra124-venice2.dts | 79 +-
arch/arm/boot/dts/tegra124.dtsi | 41 +
drivers/mailbox/Kconfig | 3 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/mailbox.c | 11 +-
drivers/mailbox/tegra-xusb-mailbox.c | 278 +++++
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/pinctrl-tegra-xusb.c | 1262 +++++++++++++++++++-
drivers/usb/host/Kconfig | 10 +
drivers/usb/host/Makefile | 1 +
drivers/usb/host/xhci-pci.c | 5 -
drivers/usb/host/xhci-plat.c | 5 -
drivers/usb/host/xhci-tegra.c | 931 +++++++++++++++
drivers/usb/host/xhci.c | 6 +-
include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 +
include/linux/mailbox_controller.h | 2 +-
include/soc/tegra/xusb.h | 50 +
21 files changed, 2852 insertions(+), 87 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra124-xhci.txt
create mode 100644 drivers/mailbox/tegra-xusb-mailbox.c
create mode 100644 drivers/usb/host/xhci-tegra.c
create mode 100644 include/soc/tegra/xusb.h

--
2.1.0.rc2.206.gedb03e5


2014-11-25 00:17:37

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 03/12] mailbox: Fix up error handling in mbox_request_channel()

From: Benson Leung <[email protected]>

mbox_request_channel() currently returns EBUSY in the event the controller
is not present or if of_xlate() fails, but in neither case is EBUSY really
appropriate. Return EPROBE_DEFER if the controller is not yet present
and change of_xlate() to return an ERR_PTR instead of NULL so that the
error can be propagated back to the caller of mbox_request_channel().

Signed-off-by: Benson Leung <[email protected]>
Signed-off-by: Andrew Bresticker <[email protected]>
---
No changes from v5.
New for v5.
---
drivers/mailbox/mailbox.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index afcb430..955c0a7 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -315,7 +315,7 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
return ERR_PTR(-ENODEV);
}

- chan = NULL;
+ chan = ERR_PTR(-EPROBE_DEFER);
list_for_each_entry(mbox, &mbox_cons, node)
if (mbox->dev->of_node == spec.np) {
chan = mbox->of_xlate(mbox, &spec);
@@ -324,7 +324,12 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)

of_node_put(spec.np);

- if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) {
+ if (IS_ERR(chan)) {
+ mutex_unlock(&con_mutex);
+ return chan;
+ }
+
+ if (chan->cl || !try_module_get(mbox->dev->driver->owner)) {
dev_dbg(dev, "%s: mailbox not free\n", __func__);
mutex_unlock(&con_mutex);
return ERR_PTR(-EBUSY);
@@ -387,7 +392,7 @@ of_mbox_index_xlate(struct mbox_controller *mbox,
int ind = sp->args[0];

if (ind >= mbox->num_chans)
- return NULL;
+ return ERR_PTR(-EINVAL);

return &mbox->chans[ind];
}
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:17:42

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 06/12] of: Update Tegra XUSB pad controller binding for USB

Add new bindings used for USB support by the Tegra XUSB pad controller.
This includes additional PHY types, USB-specific pinconfig properties, etc.

Signed-off-by: Andrew Bresticker <[email protected]>
Acked-by: Linus Walleij <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
No changes from v5.
Changes from v4:
- nvidia,usb2-port-num -> nvidia,usb2-port
- Made usb3-port a pinconfig property
- Adjusted property descriptions as suggested by Thierry.
No changes from v3.
Changes from v2:
- Added nvidia,otg-hs-curr-level-offset property.
- Dropped "-otg" from VBUS supplies.
- Added mbox-names property.
- Removed extra whitespace.
Changes from v1:
- Updated to use common mailbox bindings.
- Made USB3 port-to-lane mappins a top-level binding rather than a pinconfig
binding.
- Add #defines for the padctl lanes.
---
.../pinctrl/nvidia,tegra124-xusb-padctl.txt | 63 +++++++++++++++++++---
include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 +++
2 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
index 2f9c0bd..80895d1 100644
--- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
@@ -21,6 +21,15 @@ Required properties:
- padctl
- #phy-cells: Should be 1. The specifier is the index of the PHY to reference.
See <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> for the list of valid values.
+- mboxes: Must contain an entry for the XUSB mailbox channel.
+ See ../mailbox/mailbox.txt for details.
+- mbox-names: Must include the following entries:
+ - xusb
+
+Optional properties:
+-------------------
+- vbus-{0,1,2}-supply: VBUS regulator for the corresponding UTMI pad.
+- vddio-hsic-supply: VDDIO regulator for the HSIC pads.

Lane muxing:
------------
@@ -50,26 +59,46 @@ Optional properties:
pin or group should be assigned to. Valid values for function names are
listed below.
- nvidia,iddq: Enables IDDQ mode of the lane. (0: no, 1: yes)
+- nvidia,usb2-port: USB2 port (0, 1, or 2) to which the lane is mapped.
+- nvidia,usb3-port: USB3 port (0 or 1) to which the lane is mapped.
+- nvidia,hsic-strobe-trim: HSIC strobe trimmer value.
+- nvidia,hsic-rx-strobe-trim: HSIC RX strobe trimmer value. (0 - 7)
+- nvidia,hsic-rx-data-trim: HSIC RX data trimmer value. (0 - 7)
+- nvidia,hsic-tx-rtune-n: HSIC TX RTUNEN value. (0 - 7)
+- nvidia,hsic-tx-rtune-p: HSIC TX RTUNEP value. (0 - 7)
+- nvidia,hsic-tx-slew-n: HSIC TX SLEWN value. (0 - 7)
+- nvidia,hsic-tx-slew-p: HSIC TX SLEWP value. (0 - 7)
+- nvidia,hsic-auto-term: Enables HSIC AUTO_TERM. (0: no, 1: yes)
+- nvidia,otg-hs-curr-level-offset: Offset to be applied to the pad's fused
+ HS_CURR_LEVEL value. (0 - 63)

Note that not all of these properties are valid for all lanes. Lanes can be
-divided into three groups:
+divided into four groups:

- otg-0, otg-1, otg-2:

Valid functions for this group are: "snps", "xusb", "uart", "rsvd".

- The nvidia,iddq property does not apply to this group.
+ Only the nvidia,otg-hs-curr-level-offset property applies.
+
+ - ulpi-0:

- - ulpi-0, hsic-0, hsic-1:
+ Valid functions for this group are: "snps", "xusb".
+
+ - hsic-0, hsic-1:

Valid functions for this group are: "snps", "xusb".

- The nvidia,iddq property does not apply to this group.
+ Only the nvidia,hsic-* properties apply, and only when the function is
+ xusb.

- pcie-0, pcie-1, pcie-2, pcie-3, pcie-4, sata-0:

Valid functions for this group are: "pcie", "usb3", "sata", "rsvd".

+ Only the nvidia,iddq, nvidia,usb2-port, and nvidia,usb3-port properties
+ apply. The nvidia,usb2-port and nvidia,usb3-port properties are required
+ when the function is usb3.

Example:
========
@@ -82,6 +111,8 @@ SoC file extract:
reg = <0x0 0x7009f000 0x0 0x1000>;
resets = <&tegra_car 142>;
reset-names = "padctl";
+ mboxes = <&xusb_mbox>;
+ mbox-names = "xusb";

#phy-cells = <1>;
};
@@ -100,15 +131,35 @@ Board file extract:

...

+ usb@0,70090000 {
+ ...
+
+ phys = <&padctl 5>, <&padctl 6>, <&padctl 7>;
+ phy-names = "utmi-1", "utmi-2", "usb3-0";
+
+ ...
+ }
+
+ ...
+
padctl: padctl@0,7009f000 {
pinctrl-0 = <&padctl_default>;
pinctrl-names = "default";

+ vbus-2-supply = <&vdd_usb3_vbus>;
+
padctl_default: pinmux {
- usb3 {
- nvidia,lanes = "pcie-0", "pcie-1";
+ otg {
+ nvidia,lanes = "otg-1", "otg-2";
+ nvidia,function = "xusb";
+ };
+
+ usb3p0 {
+ nvidia,lanes = "pcie-0";
nvidia,function = "usb3";
nvidia,iddq = <0>;
+ nvidia,usb2-port = <2>;
+ nvidia,usb3-port = <0>;
};

pcie {
diff --git a/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h b/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h
index 914d56d..c83a4d4 100644
--- a/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h
+++ b/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h
@@ -3,5 +3,12 @@

#define TEGRA_XUSB_PADCTL_PCIE 0
#define TEGRA_XUSB_PADCTL_SATA 1
+#define TEGRA_XUSB_PADCTL_USB3_P0 2
+#define TEGRA_XUSB_PADCTL_USB3_P1 3
+#define TEGRA_XUSB_PADCTL_UTMI_P0 4
+#define TEGRA_XUSB_PADCTL_UTMI_P1 5
+#define TEGRA_XUSB_PADCTL_UTMI_P2 6
+#define TEGRA_XUSB_PADCTL_HSIC_P0 7
+#define TEGRA_XUSB_PADCTL_HSIC_P1 8

#endif /* _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H */
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:17:49

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 09/12] usb: xhci: Add NVIDIA Tegra xHCI host-controller driver

Add support for the on-chip xHCI host controller present on Tegra SoCs.
The controller requires external firmware which must be loaded before
using the controller. This driver loads the firmware, starts the
controller, and is able to service host-specific messages sent by
the controller's firmware.

The controller also supports USB device mode as well as powergating
of the SuperSpeed and host-controller logic when not in use, but
support for these is not yet implemented.

Based on work by:
Ajay Gupta <[email protected]>
Bharath Yadav <[email protected]>

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
Changes from v5:
- Added dependency on COMPILE_TEST and MAILBOX.
- Added TODO about powergating support.
Changes from v4:
- Poll for controller to finish booting.
- Addressed review comments from Thierry.
Changes from v3:
- Used 32-bit DMA mask (platforms may have > 32-bit physical address space
and < 64-bit dma_addr_t).
- Moved comment about SET_BW command.
Changes from v2:
- Added filtering out of non-host mailbox messages.
- Removed MODULE_ALIAS.
Changes from v1:
- Updated to use common mailbox API.
- Fixed up so that the driver can be built and used as a module.
- Incorporated review feedback from Stephen.
- Misc. cleanups.
---
drivers/usb/host/Kconfig | 10 +
drivers/usb/host/Makefile | 1 +
drivers/usb/host/xhci-tegra.c | 931 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 942 insertions(+)
create mode 100644 drivers/usb/host/xhci-tegra.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index a3ca137..ddf450e 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -50,6 +50,16 @@ config USB_XHCI_RCAR
Say 'Y' to enable the support for the xHCI host controller
found in Renesas R-Car ARM SoCs.

+config USB_XHCI_TEGRA
+ tristate "xHCI support for NVIDIA Tegra SoCs"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ depends on MAILBOX
+ depends on RESET_CONTROLLER
+ select FW_LOADER
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in NVIDIA Tegra124 and later SoCs.
+
endif # USB_XHCI_HCD

config USB_EHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 348c243..af51487 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_PCI) += pci-quirks.o

obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
+obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o

obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
new file mode 100644
index 0000000..cb177d5
--- /dev/null
+++ b/drivers/usb/host/xhci-tegra.c
@@ -0,0 +1,931 @@
+/*
+ * NVIDIA Tegra xHCI host controller driver
+ *
+ * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <soc/tegra/xusb.h>
+
+#include "xhci.h"
+
+#define TEGRA_XHCI_SS_CLK_HIGH_SPEED 120000000
+#define TEGRA_XHCI_SS_CLK_LOW_SPEED 12000000
+
+/* FPCI CFG registers */
+#define XUSB_CFG_1 0x004
+#define XUSB_IO_SPACE_EN BIT(0)
+#define XUSB_MEM_SPACE_EN BIT(1)
+#define XUSB_BUS_MASTER_EN BIT(2)
+#define XUSB_CFG_4 0x010
+#define XUSB_BASE_ADDR_SHIFT 15
+#define XUSB_BASE_ADDR_MASK 0x1ffff
+#define XUSB_CFG_ARU_C11_CSBRANGE 0x41c
+#define XUSB_CFG_CSB_BASE_ADDR 0x800
+
+/* IPFS registers */
+#define IPFS_XUSB_HOST_CONFIGURATION_0 0x180
+#define IPFS_EN_FPCI BIT(0)
+#define IPFS_XUSB_HOST_INTR_MASK_0 0x188
+#define IPFS_IP_INT_MASK BIT(16)
+#define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc
+
+#define CSB_PAGE_SELECT_MASK 0x7fffff
+#define CSB_PAGE_SELECT_SHIFT 9
+#define CSB_PAGE_OFFSET_MASK 0x1ff
+#define CSB_PAGE_SELECT(addr) ((addr) >> (CSB_PAGE_SELECT_SHIFT) & \
+ CSB_PAGE_SELECT_MASK)
+#define CSB_PAGE_OFFSET(addr) ((addr) & CSB_PAGE_OFFSET_MASK)
+
+/* Falcon CSB registers */
+#define XUSB_FALC_CPUCTL 0x100
+#define CPUCTL_STARTCPU BIT(1)
+#define CPUCTL_STATE_HALTED BIT(4)
+#define CPUCTL_STATE_STOPPED BIT(5)
+#define XUSB_FALC_BOOTVEC 0x104
+#define XUSB_FALC_DMACTL 0x10c
+#define XUSB_FALC_IMFILLRNG1 0x154
+#define IMFILLRNG1_TAG_MASK 0xffff
+#define IMFILLRNG1_TAG_LO_SHIFT 0
+#define IMFILLRNG1_TAG_HI_SHIFT 16
+#define XUSB_FALC_IMFILLCTL 0x158
+
+/* MP CSB registers */
+#define XUSB_CSB_MP_ILOAD_ATTR 0x101a00
+#define XUSB_CSB_MP_ILOAD_BASE_LO 0x101a04
+#define XUSB_CSB_MP_ILOAD_BASE_HI 0x101a08
+#define XUSB_CSB_MP_L2IMEMOP_SIZE 0x101a10
+#define L2IMEMOP_SIZE_SRC_OFFSET_SHIFT 8
+#define L2IMEMOP_SIZE_SRC_OFFSET_MASK 0x3ff
+#define L2IMEMOP_SIZE_SRC_COUNT_SHIFT 24
+#define L2IMEMOP_SIZE_SRC_COUNT_MASK 0xff
+#define XUSB_CSB_MP_L2IMEMOP_TRIG 0x101a14
+#define L2IMEMOP_ACTION_SHIFT 24
+#define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT)
+#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT)
+#define XUSB_CSB_MP_APMAP 0x10181c
+#define APMAP_BOOTPATH BIT(31)
+
+#define IMEM_BLOCK_SIZE 256
+
+struct tegra_xhci_fw_cfgtbl {
+ u32 boot_loadaddr_in_imem;
+ u32 boot_codedfi_offset;
+ u32 boot_codetag;
+ u32 boot_codesize;
+ u32 phys_memaddr;
+ u16 reqphys_memsize;
+ u16 alloc_phys_memsize;
+ u32 rodata_img_offset;
+ u32 rodata_section_start;
+ u32 rodata_section_end;
+ u32 main_fnaddr;
+ u32 fwimg_cksum;
+ u32 fwimg_created_time;
+ u32 imem_resident_start;
+ u32 imem_resident_end;
+ u32 idirect_start;
+ u32 idirect_end;
+ u32 l2_imem_start;
+ u32 l2_imem_end;
+ u32 version_id;
+ u8 init_ddirect;
+ u8 reserved[3];
+ u32 phys_addr_log_buffer;
+ u32 total_log_entries;
+ u32 dequeue_ptr;
+ u32 dummy_var[2];
+ u32 fwimg_len;
+ u8 magic[8];
+ u32 ss_low_power_entry_timeout;
+ u8 num_hsic_port;
+ u8 padding[139]; /* Padding to make 256-bytes cfgtbl */
+};
+
+struct tegra_xhci_soc_config {
+ const char *firmware_file;
+};
+
+#define TEGRA_XHCI_NUM_SUPPLIES 8
+static const char *tegra_xhci_supply_names[TEGRA_XHCI_NUM_SUPPLIES] = {
+ "avddio-pex",
+ "dvddio-pex",
+ "avdd-usb",
+ "avdd-pll-utmip",
+ "avdd-pll-erefe",
+ "avdd-usb-ss-pll",
+ "hvdd-usb-ss",
+ "hvdd-usb-ss-pll-e",
+};
+
+static const struct {
+ const char *name;
+ unsigned int num;
+} tegra_xhci_phy_types[] = {
+ {
+ .name = "usb3",
+ .num = TEGRA_XUSB_USB3_PHYS,
+ }, {
+ .name = "utmi",
+ .num = TEGRA_XUSB_UTMI_PHYS,
+ }, {
+ .name = "hsic",
+ .num = TEGRA_XUSB_HSIC_PHYS,
+ },
+};
+
+struct tegra_xhci_hcd {
+ struct device *dev;
+ struct usb_hcd *hcd;
+
+ int irq;
+
+ void __iomem *fpci_base;
+ void __iomem *ipfs_base;
+
+ const struct tegra_xhci_soc_config *soc_config;
+
+ struct regulator_bulk_data supplies[TEGRA_XHCI_NUM_SUPPLIES];
+
+ struct clk *host_clk;
+ struct clk *falc_clk;
+ struct clk *ss_clk;
+ struct clk *ss_src_clk;
+ struct clk *hs_src_clk;
+ struct clk *fs_src_clk;
+ struct clk *pll_u_480m;
+ struct clk *clk_m;
+ struct clk *pll_e;
+
+ struct reset_control *host_rst;
+ struct reset_control *ss_rst;
+
+ struct phy *phys[TEGRA_XUSB_NUM_USB_PHYS];
+
+ struct work_struct mbox_req_work;
+ struct tegra_xusb_mbox_msg mbox_req;
+ struct mbox_client mbox_client;
+ struct mbox_chan *mbox_chan;
+
+ /* Firmware loading related */
+ void *fw_data;
+ size_t fw_size;
+ dma_addr_t fw_dma_addr;
+ bool fw_loaded;
+};
+
+static struct hc_driver __read_mostly tegra_xhci_hc_driver;
+
+static inline struct tegra_xhci_hcd *
+mbox_work_to_tegra(struct work_struct *work)
+{
+ return container_of(work, struct tegra_xhci_hcd, mbox_req_work);
+}
+
+static inline u32 fpci_readl(struct tegra_xhci_hcd *tegra, u32 addr)
+{
+ return readl(tegra->fpci_base + addr);
+}
+
+static inline void fpci_writel(struct tegra_xhci_hcd *tegra, u32 val, u32 addr)
+{
+ writel(val, tegra->fpci_base + addr);
+}
+
+static inline u32 ipfs_readl(struct tegra_xhci_hcd *tegra, u32 addr)
+{
+ return readl(tegra->ipfs_base + addr);
+}
+
+static inline void ipfs_writel(struct tegra_xhci_hcd *tegra, u32 val, u32 addr)
+{
+ writel(val, tegra->ipfs_base + addr);
+}
+
+static u32 csb_readl(struct tegra_xhci_hcd *tegra, u32 addr)
+{
+ u32 page, offset;
+
+ page = CSB_PAGE_SELECT(addr);
+ offset = CSB_PAGE_OFFSET(addr);
+ fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE);
+ return fpci_readl(tegra, XUSB_CFG_CSB_BASE_ADDR + offset);
+}
+
+static void csb_writel(struct tegra_xhci_hcd *tegra, u32 val, u32 addr)
+{
+ u32 page, offset;
+
+ page = CSB_PAGE_SELECT(addr);
+ offset = CSB_PAGE_OFFSET(addr);
+ fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE);
+ fpci_writel(tegra, val, XUSB_CFG_CSB_BASE_ADDR + offset);
+}
+
+static void tegra_xhci_cfg(struct tegra_xhci_hcd *tegra)
+{
+ u32 reg;
+
+ reg = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0);
+ reg |= IPFS_EN_FPCI;
+ ipfs_writel(tegra, reg, IPFS_XUSB_HOST_CONFIGURATION_0);
+ udelay(10);
+
+ /* Program Bar0 Space */
+ reg = fpci_readl(tegra, XUSB_CFG_4);
+ reg &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
+ reg |= tegra->hcd->rsrc_start & (XUSB_BASE_ADDR_MASK <<
+ XUSB_BASE_ADDR_SHIFT);
+ fpci_writel(tegra, reg, XUSB_CFG_4);
+ usleep_range(100, 200);
+
+ /* Enable Bus Master */
+ reg = fpci_readl(tegra, XUSB_CFG_1);
+ reg |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN;
+ fpci_writel(tegra, reg, XUSB_CFG_1);
+
+ /* Set intr mask to enable intr assertion */
+ reg = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0);
+ reg |= IPFS_IP_INT_MASK;
+ ipfs_writel(tegra, reg, IPFS_XUSB_HOST_INTR_MASK_0);
+
+ /* Set hysteresis */
+ ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
+}
+
+static int tegra_xhci_load_firmware(struct tegra_xhci_hcd *tegra)
+{
+ struct device *dev = tegra->dev;
+ struct tegra_xhci_fw_cfgtbl *cfg_tbl;
+ struct tm fw_tm;
+ u32 val, code_tag_blocks, code_size_blocks;
+ u64 fw_base;
+ time_t fw_time;
+ unsigned long timeout;
+
+ if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
+ dev_info(dev, "Firmware already loaded, Falcon state 0x%x\n",
+ csb_readl(tegra, XUSB_FALC_CPUCTL));
+ return 0;
+ }
+
+ cfg_tbl = (struct tegra_xhci_fw_cfgtbl *)tegra->fw_data;
+
+ /* Program the size of DFI into ILOAD_ATTR. */
+ csb_writel(tegra, tegra->fw_size, XUSB_CSB_MP_ILOAD_ATTR);
+
+ /*
+ * Boot code of the firmware reads the ILOAD_BASE registers
+ * to get to the start of the DFI in system memory.
+ */
+ fw_base = tegra->fw_dma_addr + sizeof(*cfg_tbl);
+ csb_writel(tegra, fw_base, XUSB_CSB_MP_ILOAD_BASE_LO);
+ csb_writel(tegra, fw_base >> 32, XUSB_CSB_MP_ILOAD_BASE_HI);
+
+ /* Set BOOTPATH to 1 in APMAP. */
+ csb_writel(tegra, APMAP_BOOTPATH, XUSB_CSB_MP_APMAP);
+
+ /* Invalidate L2IMEM. */
+ csb_writel(tegra, L2IMEMOP_INVALIDATE_ALL, XUSB_CSB_MP_L2IMEMOP_TRIG);
+
+ /*
+ * Initiate fetch of bootcode from system memory into L2IMEM.
+ * Program bootcode location and size in system memory.
+ */
+ code_tag_blocks = DIV_ROUND_UP(le32_to_cpu(cfg_tbl->boot_codetag),
+ IMEM_BLOCK_SIZE);
+ code_size_blocks = DIV_ROUND_UP(le32_to_cpu(cfg_tbl->boot_codesize),
+ IMEM_BLOCK_SIZE);
+ val = ((code_tag_blocks & L2IMEMOP_SIZE_SRC_OFFSET_MASK) <<
+ L2IMEMOP_SIZE_SRC_OFFSET_SHIFT) |
+ ((code_size_blocks & L2IMEMOP_SIZE_SRC_COUNT_MASK) <<
+ L2IMEMOP_SIZE_SRC_COUNT_SHIFT);
+ csb_writel(tegra, val, XUSB_CSB_MP_L2IMEMOP_SIZE);
+
+ /* Trigger L2IMEM Load operation. */
+ csb_writel(tegra, L2IMEMOP_LOAD_LOCKED_RESULT,
+ XUSB_CSB_MP_L2IMEMOP_TRIG);
+
+ /* Setup Falcon Auto-fill. */
+ csb_writel(tegra, code_size_blocks, XUSB_FALC_IMFILLCTL);
+
+ val = ((code_tag_blocks & IMFILLRNG1_TAG_MASK) <<
+ IMFILLRNG1_TAG_LO_SHIFT) |
+ (((code_size_blocks + code_tag_blocks) & IMFILLRNG1_TAG_MASK) <<
+ IMFILLRNG1_TAG_HI_SHIFT);
+ csb_writel(tegra, val, XUSB_FALC_IMFILLRNG1);
+
+ csb_writel(tegra, 0, XUSB_FALC_DMACTL);
+ msleep(50);
+
+ csb_writel(tegra, le32_to_cpu(cfg_tbl->boot_codetag),
+ XUSB_FALC_BOOTVEC);
+
+ /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */
+ timeout = jiffies + msecs_to_jiffies(5);
+ csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
+ while (time_before(jiffies, timeout)) {
+ if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED)
+ break;
+ usleep_range(100, 200);
+ }
+ if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) {
+ dev_err(dev, "Falcon failed to start, state: %#x\n",
+ csb_readl(tegra, XUSB_FALC_CPUCTL));
+ return -EIO;
+ }
+
+ fw_time = le32_to_cpu(cfg_tbl->fwimg_created_time);
+ time_to_tm(fw_time, 0, &fw_tm);
+ dev_info(dev, "Firmware timestamp: %ld-%02d-%02d %02d:%02d:%02d UTC\n",
+ fw_tm.tm_year + 1900, fw_tm.tm_mon + 1, fw_tm.tm_mday,
+ fw_tm.tm_hour, fw_tm.tm_min, fw_tm.tm_sec);
+
+ return 0;
+}
+
+static int tegra_xhci_set_ss_clk(struct tegra_xhci_hcd *tegra,
+ unsigned long rate)
+{
+ struct clk *clk = tegra->ss_src_clk;
+ unsigned long new_parent_rate, old_parent_rate;
+ int ret, div;
+
+ if (clk_get_rate(clk) == rate)
+ return 0;
+
+ switch (rate) {
+ case TEGRA_XHCI_SS_CLK_HIGH_SPEED:
+ /*
+ * Reparent to PLLU_480M. Set divider first to avoid
+ * overclocking.
+ */
+ old_parent_rate = clk_get_rate(clk_get_parent(clk));
+ new_parent_rate = clk_get_rate(tegra->pll_u_480m);
+ div = new_parent_rate / rate;
+ ret = clk_set_rate(clk, old_parent_rate / div);
+ if (ret)
+ return ret;
+ ret = clk_set_parent(clk, tegra->pll_u_480m);
+ if (ret)
+ return ret;
+ /*
+ * The rate should already be correct, but set it again just
+ * to be sure.
+ */
+ ret = clk_set_rate(clk, rate);
+ if (ret)
+ return ret;
+ break;
+ case TEGRA_XHCI_SS_CLK_LOW_SPEED:
+ /* Reparent to CLK_M */
+ ret = clk_set_parent(clk, tegra->clk_m);
+ if (ret)
+ return ret;
+ ret = clk_set_rate(clk, rate);
+ if (ret)
+ return ret;
+ break;
+ default:
+ dev_err(tegra->dev, "Invalid SS rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (clk_get_rate(clk) != rate) {
+ dev_err(tegra->dev, "SS clock doesn't match requested rate\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_xhci_clk_enable(struct tegra_xhci_hcd *tegra)
+{
+ int ret;
+
+ ret = clk_prepare_enable(tegra->pll_e);
+ if (ret < 0)
+ return ret;
+ ret = clk_prepare_enable(tegra->host_clk);
+ if (ret < 0)
+ goto disable_plle;
+ ret = clk_prepare_enable(tegra->ss_clk);
+ if (ret < 0)
+ goto disable_host;
+ ret = clk_prepare_enable(tegra->falc_clk);
+ if (ret < 0)
+ goto disable_ss;
+ ret = clk_prepare_enable(tegra->fs_src_clk);
+ if (ret < 0)
+ goto disable_falc;
+ ret = clk_prepare_enable(tegra->hs_src_clk);
+ if (ret < 0)
+ goto disable_fs_src;
+ ret = tegra_xhci_set_ss_clk(tegra, TEGRA_XHCI_SS_CLK_HIGH_SPEED);
+ if (ret < 0)
+ goto disable_hs_src;
+
+ return 0;
+
+disable_hs_src:
+ clk_disable_unprepare(tegra->hs_src_clk);
+disable_fs_src:
+ clk_disable_unprepare(tegra->fs_src_clk);
+disable_falc:
+ clk_disable_unprepare(tegra->falc_clk);
+disable_ss:
+ clk_disable_unprepare(tegra->ss_clk);
+disable_host:
+ clk_disable_unprepare(tegra->host_clk);
+disable_plle:
+ clk_disable_unprepare(tegra->pll_e);
+ return ret;
+}
+
+static void tegra_xhci_clk_disable(struct tegra_xhci_hcd *tegra)
+{
+ clk_disable_unprepare(tegra->pll_e);
+ clk_disable_unprepare(tegra->host_clk);
+ clk_disable_unprepare(tegra->ss_clk);
+ clk_disable_unprepare(tegra->falc_clk);
+ clk_disable_unprepare(tegra->fs_src_clk);
+ clk_disable_unprepare(tegra->hs_src_clk);
+}
+
+static int tegra_xhci_phy_enable(struct tegra_xhci_hcd *tegra)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(tegra->phys); i++) {
+ ret = phy_init(tegra->phys[i]);
+ if (ret)
+ goto disable_phy;
+ ret = phy_power_on(tegra->phys[i]);
+ if (ret) {
+ phy_exit(tegra->phys[i]);
+ goto disable_phy;
+ }
+ }
+
+ return 0;
+disable_phy:
+ for (; i > 0; i--) {
+ phy_power_off(tegra->phys[i - 1]);
+ phy_exit(tegra->phys[i - 1]);
+ }
+ return ret;
+}
+
+static void tegra_xhci_phy_disable(struct tegra_xhci_hcd *tegra)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(tegra->phys); i++) {
+ phy_power_off(tegra->phys[i]);
+ phy_exit(tegra->phys[i]);
+ }
+}
+
+static bool is_host_mbox_message(u32 cmd)
+{
+ switch (cmd) {
+ case MBOX_CMD_INC_SSPI_CLOCK:
+ case MBOX_CMD_DEC_SSPI_CLOCK:
+ case MBOX_CMD_INC_FALC_CLOCK:
+ case MBOX_CMD_DEC_FALC_CLOCK:
+ case MBOX_CMD_SET_BW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void tegra_xhci_mbox_work(struct work_struct *work)
+{
+ struct tegra_xhci_hcd *tegra = mbox_work_to_tegra(work);
+ struct tegra_xusb_mbox_msg *msg = &tegra->mbox_req;
+ struct tegra_xusb_mbox_msg resp;
+ int ret;
+
+ resp.cmd = 0;
+ switch (msg->cmd) {
+ case MBOX_CMD_INC_SSPI_CLOCK:
+ case MBOX_CMD_DEC_SSPI_CLOCK:
+ ret = tegra_xhci_set_ss_clk(tegra, msg->data * 1000);
+ resp.data = clk_get_rate(tegra->ss_src_clk) / 1000;
+ if (ret)
+ resp.cmd = MBOX_CMD_NAK;
+ else
+ resp.cmd = MBOX_CMD_ACK;
+ break;
+ case MBOX_CMD_INC_FALC_CLOCK:
+ case MBOX_CMD_DEC_FALC_CLOCK:
+ resp.data = clk_get_rate(tegra->falc_clk) / 1000;
+ if (resp.data != msg->data)
+ resp.cmd = MBOX_CMD_NAK;
+ else
+ resp.cmd = MBOX_CMD_ACK;
+ break;
+ case MBOX_CMD_SET_BW:
+ /*
+ * TODO: Request bandwidth once EMC scaling is supported.
+ * Ignore for now since ACK/NAK is not required for SET_BW
+ * messages.
+ */
+ break;
+ default:
+ break;
+ }
+
+ if (resp.cmd)
+ mbox_send_message(tegra->mbox_chan, &resp);
+}
+
+static void tegra_xhci_mbox_rx(struct mbox_client *cl, void *data)
+{
+ struct tegra_xhci_hcd *tegra = dev_get_drvdata(cl->dev);
+ struct tegra_xusb_mbox_msg *msg = data;
+
+ if (is_host_mbox_message(msg->cmd)) {
+ tegra->mbox_req = *msg;
+ schedule_work(&tegra->mbox_req_work);
+ }
+}
+
+static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ xhci->quirks |= XHCI_PLAT;
+}
+
+static int tegra_xhci_setup(struct usb_hcd *hcd)
+{
+ return xhci_gen_setup(hcd, tegra_xhci_quirks);
+}
+
+static const struct tegra_xhci_soc_config tegra124_soc_config = {
+ .firmware_file = "nvidia/tegra124/xusb.bin",
+};
+MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
+
+static const struct of_device_id tegra_xhci_of_match[] = {
+ { .compatible = "nvidia,tegra124-xhci", .data = &tegra124_soc_config },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_xhci_of_match);
+
+static void tegra_xhci_probe_finish(const struct firmware *fw, void *context)
+{
+ struct tegra_xhci_hcd *tegra = context;
+ struct device *dev = tegra->dev;
+ struct xhci_hcd *xhci = NULL;
+ struct tegra_xhci_fw_cfgtbl *cfg_tbl;
+ struct tegra_xusb_mbox_msg msg;
+ int ret;
+
+ if (!fw)
+ goto put_usb2_hcd;
+
+ /* Load Falcon controller with its firmware. */
+ cfg_tbl = (struct tegra_xhci_fw_cfgtbl *)fw->data;
+ tegra->fw_size = le32_to_cpu(cfg_tbl->fwimg_len);
+ tegra->fw_data = dma_alloc_coherent(dev, tegra->fw_size,
+ &tegra->fw_dma_addr,
+ GFP_KERNEL);
+ if (!tegra->fw_data)
+ goto put_usb2_hcd;
+ memcpy(tegra->fw_data, fw->data, tegra->fw_size);
+
+ ret = tegra_xhci_load_firmware(tegra);
+ if (ret < 0)
+ goto put_usb2_hcd;
+
+ ret = usb_add_hcd(tegra->hcd, tegra->irq, IRQF_SHARED);
+ if (ret < 0)
+ goto put_usb2_hcd;
+ device_wakeup_enable(tegra->hcd->self.controller);
+
+ /*
+ * USB 2.0 roothub is stored in drvdata now. Swap it with the Tegra HCD.
+ */
+ tegra->hcd = dev_get_drvdata(dev);
+ dev_set_drvdata(dev, tegra);
+ xhci = hcd_to_xhci(tegra->hcd);
+ xhci->shared_hcd = usb_create_shared_hcd(&tegra_xhci_hc_driver,
+ dev, dev_name(dev),
+ tegra->hcd);
+ if (!xhci->shared_hcd)
+ goto dealloc_usb2_hcd;
+
+ ret = usb_add_hcd(xhci->shared_hcd, tegra->irq, IRQF_SHARED);
+ if (ret < 0)
+ goto put_usb3_hcd;
+
+ /* Enable firmware messages from controller. */
+ msg.cmd = MBOX_CMD_MSG_ENABLED;
+ msg.data = 0;
+ ret = mbox_send_message(tegra->mbox_chan, &msg);
+ if (ret < 0)
+ goto dealloc_usb3_hcd;
+
+ tegra->fw_loaded = true;
+ release_firmware(fw);
+ return;
+
+ /* Free up as much as we can and wait to be unbound. */
+dealloc_usb3_hcd:
+ usb_remove_hcd(xhci->shared_hcd);
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+dealloc_usb2_hcd:
+ usb_remove_hcd(tegra->hcd);
+ kfree(xhci);
+put_usb2_hcd:
+ usb_put_hcd(tegra->hcd);
+ tegra->hcd = NULL;
+ release_firmware(fw);
+}
+
+static int tegra_xhci_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_xhci_hcd *tegra;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct phy *phy;
+ unsigned int i, j, k;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(struct tegra_xhci_fw_cfgtbl) != 256);
+
+ tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
+ if (!tegra)
+ return -ENOMEM;
+ tegra->dev = &pdev->dev;
+ platform_set_drvdata(pdev, tegra);
+
+ match = of_match_device(tegra_xhci_of_match, &pdev->dev);
+ tegra->soc_config = match->data;
+
+ hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+ tegra->hcd = hcd;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto put_hcd;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tegra->fpci_base)) {
+ ret = PTR_ERR(tegra->fpci_base);
+ goto put_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tegra->ipfs_base)) {
+ ret = PTR_ERR(tegra->ipfs_base);
+ goto put_hcd;
+ }
+
+ tegra->irq = platform_get_irq(pdev, 0);
+ if (tegra->irq < 0) {
+ ret = tegra->irq;
+ goto put_hcd;
+ }
+
+ tegra->host_rst = devm_reset_control_get(&pdev->dev, "xusb_host");
+ if (IS_ERR(tegra->host_rst)) {
+ ret = PTR_ERR(tegra->host_rst);
+ goto put_hcd;
+ }
+ tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss");
+ if (IS_ERR(tegra->ss_rst)) {
+ ret = PTR_ERR(tegra->ss_rst);
+ goto put_hcd;
+ }
+
+ tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host");
+ if (IS_ERR(tegra->host_clk)) {
+ ret = PTR_ERR(tegra->host_clk);
+ goto put_hcd;
+ }
+ tegra->falc_clk = devm_clk_get(&pdev->dev, "xusb_falcon_src");
+ if (IS_ERR(tegra->falc_clk)) {
+ ret = PTR_ERR(tegra->falc_clk);
+ goto put_hcd;
+ }
+ tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss");
+ if (IS_ERR(tegra->ss_clk)) {
+ ret = PTR_ERR(tegra->ss_clk);
+ goto put_hcd;
+ }
+ tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src");
+ if (IS_ERR(tegra->ss_src_clk)) {
+ ret = PTR_ERR(tegra->ss_src_clk);
+ goto put_hcd;
+ }
+ tegra->hs_src_clk = devm_clk_get(&pdev->dev, "xusb_hs_src");
+ if (IS_ERR(tegra->hs_src_clk)) {
+ ret = PTR_ERR(tegra->hs_src_clk);
+ goto put_hcd;
+ }
+ tegra->fs_src_clk = devm_clk_get(&pdev->dev, "xusb_fs_src");
+ if (IS_ERR(tegra->fs_src_clk)) {
+ ret = PTR_ERR(tegra->fs_src_clk);
+ goto put_hcd;
+ }
+ tegra->pll_u_480m = devm_clk_get(&pdev->dev, "pll_u_480m");
+ if (IS_ERR(tegra->pll_u_480m)) {
+ ret = PTR_ERR(tegra->pll_u_480m);
+ goto put_hcd;
+ }
+ tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m");
+ if (IS_ERR(tegra->clk_m)) {
+ ret = PTR_ERR(tegra->clk_m);
+ goto put_hcd;
+ }
+ tegra->pll_e = devm_clk_get(&pdev->dev, "pll_e");
+ if (IS_ERR(tegra->pll_e)) {
+ ret = PTR_ERR(tegra->pll_e);
+ goto put_hcd;
+ }
+ ret = tegra_xhci_clk_enable(tegra);
+ if (ret)
+ goto put_hcd;
+
+ for (i = 0; i < ARRAY_SIZE(tegra->supplies); i++)
+ tegra->supplies[i].supply = tegra_xhci_supply_names[i];
+ ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(tegra->supplies),
+ tegra->supplies);
+ if (ret)
+ goto disable_clk;
+ ret = regulator_bulk_enable(ARRAY_SIZE(tegra->supplies),
+ tegra->supplies);
+ if (ret)
+ goto disable_clk;
+
+ INIT_WORK(&tegra->mbox_req_work, tegra_xhci_mbox_work);
+ tegra->mbox_client.dev = &pdev->dev;
+ tegra->mbox_client.tx_block = true;
+ tegra->mbox_client.tx_tout = 0;
+ tegra->mbox_client.rx_callback = tegra_xhci_mbox_rx;
+ tegra->mbox_chan = mbox_request_channel(&tegra->mbox_client, 0);
+ if (IS_ERR(tegra->mbox_chan)) {
+ ret = PTR_ERR(tegra->mbox_chan);
+ goto disable_regulator;
+ }
+
+ for (i = 0, k = 0; i < ARRAY_SIZE(tegra_xhci_phy_types); i++) {
+ char prop[8];
+
+ for (j = 0; j < tegra_xhci_phy_types[i].num; j++) {
+ snprintf(prop, sizeof(prop), "%s-%d",
+ tegra_xhci_phy_types[i].name, j);
+ phy = devm_phy_optional_get(&pdev->dev, prop);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ goto put_mbox;
+ }
+ tegra->phys[k++] = phy;
+ }
+ }
+
+ /* Setup IPFS access and BAR0 space. */
+ tegra_xhci_cfg(tegra);
+
+ ret = tegra_xhci_phy_enable(tegra);
+ if (ret < 0)
+ goto put_mbox;
+
+ ret = request_firmware_nowait(THIS_MODULE, true,
+ tegra->soc_config->firmware_file,
+ tegra->dev, GFP_KERNEL, tegra,
+ tegra_xhci_probe_finish);
+ if (ret < 0)
+ goto disable_phy;
+
+ return 0;
+
+disable_phy:
+ tegra_xhci_phy_disable(tegra);
+put_mbox:
+ mbox_free_channel(tegra->mbox_chan);
+disable_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies);
+disable_clk:
+ tegra_xhci_clk_disable(tegra);
+put_hcd:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static int tegra_xhci_remove(struct platform_device *pdev)
+{
+ struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = tegra->hcd;
+ struct xhci_hcd *xhci;
+
+ if (tegra->fw_loaded) {
+ xhci = hcd_to_xhci(hcd);
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_put_hcd(xhci->shared_hcd);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ kfree(xhci);
+ } else if (hcd) {
+ /* Unbound after probe(), but before firmware loading. */
+ usb_put_hcd(hcd);
+ }
+
+ if (tegra->fw_data)
+ dma_free_coherent(tegra->dev, tegra->fw_size, tegra->fw_data,
+ tegra->fw_dma_addr);
+
+ cancel_work_sync(&tegra->mbox_req_work);
+ mbox_free_channel(tegra->mbox_chan);
+ tegra_xhci_phy_disable(tegra);
+ regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies);
+ tegra_xhci_clk_disable(tegra);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_xhci_suspend(struct device *dev)
+{
+ struct tegra_xhci_hcd *tegra = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+
+ /* TODO: Powergate controller across suspend/resume. */
+ return xhci_suspend(xhci);
+}
+
+static int tegra_xhci_resume(struct device *dev)
+{
+ struct tegra_xhci_hcd *tegra = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+
+ return xhci_resume(xhci, 0);
+}
+#endif
+
+static const struct dev_pm_ops tegra_xhci_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_xhci_suspend, tegra_xhci_resume)
+};
+
+static struct platform_driver tegra_xhci_driver = {
+ .probe = tegra_xhci_probe,
+ .remove = tegra_xhci_remove,
+ .driver = {
+ .name = "xhci-tegra",
+ .pm = &tegra_xhci_pm_ops,
+ .of_match_table = of_match_ptr(tegra_xhci_of_match),
+ },
+};
+
+static int __init tegra_xhci_init(void)
+{
+ xhci_init_driver(&tegra_xhci_hc_driver, tegra_xhci_setup);
+ return platform_driver_register(&tegra_xhci_driver);
+}
+module_init(tegra_xhci_init);
+
+static void __exit tegra_xhci_exit(void)
+{
+ platform_driver_unregister(&tegra_xhci_driver);
+}
+module_exit(tegra_xhci_exit);
+
+MODULE_AUTHOR("Andrew Bresticker <[email protected]>");
+MODULE_DESCRIPTION("NVIDIA Tegra xHCI host-controller driver");
+MODULE_LICENSE("GPL v2");
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:18:16

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 10/12] ARM: tegra: jetson-tk1: Add xHCI support

Assign USB ports previously owned by the EHCI controllers to the xHCI
controller. There is a mini-PCIe USB port (UTMI port 1) and a USB A
connector (UTMI port 2, USB3 port 0). PCIe lane 0 is used for USB3
port 0.

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
No changes from v5.
Changes from v4:
- Updated regulator and pinconfig properties.
Changes from v3:
- Assigned otg-0 to XUSB to avoid USB2.0 flakiness.
Changes from v2:
- Updated VBUS power supply names.
Changes from v1:
- Updated USB power supplies.
---
arch/arm/boot/dts/tegra124-jetson-tk1.dts | 46 +++++++++++++++++--------------
1 file changed, 26 insertions(+), 20 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
index 51b373f..be93710 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
@@ -1687,15 +1687,40 @@
target-12v-supply = <&vdd_12v0_sata>;
};

+ usb@0,70090000 {
+ status = "okay";
+ phys = <&padctl TEGRA_XUSB_PADCTL_UTMI_P1>, /* mini-PCIe USB */
+ <&padctl TEGRA_XUSB_PADCTL_UTMI_P2>, /* USB A */
+ <&padctl TEGRA_XUSB_PADCTL_USB3_P0>; /* USB A */
+ phy-names = "utmi-1", "utmi-2", "usb3-0";
+ avddio-pex-supply = <&vdd_1v05_run>;
+ dvddio-pex-supply = <&vdd_1v05_run>;
+ avdd-usb-supply = <&vdd_3v3_lp0>;
+ avdd-pll-utmip-supply = <&vddio_1v8>;
+ avdd-pll-erefe-supply = <&avdd_1v05_run>;
+ avdd-usb-ss-pll-supply = <&vdd_1v05_run>;
+ hvdd-usb-ss-supply = <&vdd_3v3_lp0>;
+ hvdd-usb-ss-pll-e-supply = <&vdd_3v3_lp0>;
+ };
+
padctl@0,7009f000 {
pinctrl-0 = <&padctl_default>;
pinctrl-names = "default";

+ vbus-2-supply = <&vdd_usb3_vbus>;
+
padctl_default: pinmux {
+ otg {
+ nvidia,lanes = "otg-0", "otg-1", "otg-2";
+ nvidia,function = "xusb";
+ };
+
usb3 {
- nvidia,lanes = "pcie-0", "pcie-1";
+ nvidia,lanes = "pcie-0";
nvidia,function = "usb3";
nvidia,iddq = <0>;
+ nvidia,usb2-port = <2>;
+ nvidia,usb3-port = <0>;
};

pcie {
@@ -1736,25 +1761,6 @@
};
};

- /* mini-PCIe USB */
- usb@0,7d004000 {
- status = "okay";
- };
-
- usb-phy@0,7d004000 {
- status = "okay";
- };
-
- /* USB A connector */
- usb@0,7d008000 {
- status = "okay";
- };
-
- usb-phy@0,7d008000 {
- status = "okay";
- vbus-supply = <&vdd_usb3_vbus>;
- };
-
clocks {
compatible = "simple-bus";
#address-cells = <1>;
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:18:14

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 11/12] ARM: tegra: Add Tegra124 XUSB mailbox and xHCI controller

Add nodes for the Tegra XUSB mailbox and Tegra xHCI controller and
add the PHY mailbox channel to the XUSB padctl node.

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
No changes from v3/v4/v5.
Changes from v2:
- Dropped channel specifier from mailbox bindings.
- Added mbox-names properties.
Changes from v1:
- Updated to use common mailbox bindings.
- Added remaining clocks/resets.
---
arch/arm/boot/dts/tegra124.dtsi | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)

diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index df2b06b..2e06130 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -591,11 +591,52 @@
status = "disabled";
};

+ usb@0,70090000 {
+ compatible = "nvidia,tegra124-xhci";
+ reg = <0x0 0x70090000 0x0 0x8000>,
+ <0x0 0x70098000 0x0 0x1000>,
+ <0x0 0x70099000 0x0 0x1000>;
+ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_XUSB_HOST>,
+ <&tegra_car TEGRA124_CLK_XUSB_HOST_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_DEV>,
+ <&tegra_car TEGRA124_CLK_XUSB_DEV_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_FALCON_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS_DIV2>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_HS_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_FS_SRC>,
+ <&tegra_car TEGRA124_CLK_PLL_U_480M>,
+ <&tegra_car TEGRA124_CLK_CLK_M>,
+ <&tegra_car TEGRA124_CLK_PLL_E>;
+ clock-names = "xusb_host", "xusb_host_src", "xusb_dev",
+ "xusb_dev_src", "xusb_falcon_src", "xusb_ss",
+ "xusb_ss_div2", "xusb_ss_src", "xusb_hs_src",
+ "xusb_fs_src", "pll_u_480m", "clk_m", "pll_e";
+ resets = <&tegra_car 89>, <&tegra_car 95>, <&tegra_car 156>,
+ <&tegra_car 143>;
+ reset-names = "xusb_host", "xusb_dev", "xusb_ss", "xusb";
+ mboxes = <&xusb_mbox>;
+ mbox-names = "xusb";
+ status = "disabled";
+ };
+
+ xusb_mbox: mailbox@0,70098000 {
+ compatible = "nvidia,tegra124-xusb-mbox";
+ reg = <0x0 0x70098000 0x0 0x1000>;
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+
+ #mbox-cells = <0>;
+ };
+
padctl: padctl@0,7009f000 {
compatible = "nvidia,tegra124-xusb-padctl";
reg = <0x0 0x7009f000 0x0 0x1000>;
resets = <&tegra_car 142>;
reset-names = "padctl";
+ mboxes = <&xusb_mbox>;
+ mbox-names = "xusb";

#phy-cells = <1>;
};
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:18:12

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 12/12] ARM: tegra: venice2: Add xHCI support

Assign ports previously owned by the EHCI controllers to the xHCI
controller. There are two external USB ports (UTMI ports 0/2 and
USB3 ports 0/1) and an internal USB port (UTMI port 1). PCIe lanes
0 and 1 are used by the USB3 ports.

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
No changes from v5.
Changes from v4:
- Updated regulator and pinconfig properties.
No changes from v3.
Changes from v2:
- Updated VBUS power supply names.
Changes from v1:
- Updated USB power supplies.
---
arch/arm/boot/dts/tegra124-venice2.dts | 79 ++++++++++++++++++++++------------
1 file changed, 51 insertions(+), 28 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-venice2.dts b/arch/arm/boot/dts/tegra124-venice2.dts
index 5c3f781..abe59f7 100644
--- a/arch/arm/boot/dts/tegra124-venice2.dts
+++ b/arch/arm/boot/dts/tegra124-venice2.dts
@@ -745,7 +745,7 @@
regulator-always-on;
};

- ldo0 {
+ avdd_1v05_run: ldo0 {
regulator-name = "+1.05V_RUN_AVDD";
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
@@ -887,6 +887,56 @@
status = "okay";
};

+ usb@0,70090000 {
+ status = "okay";
+ phys = <&padctl TEGRA_XUSB_PADCTL_UTMI_P0>, /* 1st USB A */
+ <&padctl TEGRA_XUSB_PADCTL_UTMI_P1>, /* Internal USB */
+ <&padctl TEGRA_XUSB_PADCTL_UTMI_P2>, /* 2nd USB A */
+ <&padctl TEGRA_XUSB_PADCTL_USB3_P0>, /* 1st USB A */
+ <&padctl TEGRA_XUSB_PADCTL_USB3_P1>; /* 2nd USB A */
+ phy-names = "utmi-0", "utmi-1", "utmi-2", "usb3-0", "usb3-1";
+ avddio-pex-supply = <&vdd_1v05_run>;
+ dvddio-pex-supply = <&vdd_1v05_run>;
+ avdd-usb-supply = <&vdd_3v3_lp0>;
+ avdd-pll-utmip-supply = <&vddio_1v8>;
+ avdd-pll-erefe-supply = <&avdd_1v05_run>;
+ avdd-usb-ss-pll-supply = <&vdd_1v05_run>;
+ hvdd-usb-ss-supply = <&vdd_3v3_lp0>;
+ hvdd-usb-ss-pll-e-supply = <&vdd_3v3_lp0>;
+ };
+
+ padctl@0,7009f000 {
+ pinctrl-0 = <&padctl_default>;
+ pinctrl-names = "default";
+
+ vbus-0-supply = <&vdd_usb1_vbus>;
+ vbus-1-supply = <&vdd_run_cam>;
+ vbus-2-supply = <&vdd_usb3_vbus>;
+
+ padctl_default: pinmux {
+ otg {
+ nvidia,lanes = "otg-0", "otg-1", "otg-2";
+ nvidia,function = "xusb";
+ };
+
+ usb3p0 {
+ nvidia,lanes = "pcie-0";
+ nvidia,function = "usb3";
+ nvidia,iddq = <0>;
+ nvidia,usb2-port = <0>;
+ nvidia,usb3-port = <0>;
+ };
+
+ usb3p1 {
+ nvidia,lanes = "pcie-1";
+ nvidia,function = "usb3";
+ nvidia,iddq = <0>;
+ nvidia,usb2-port = <2>;
+ nvidia,usb3-port = <1>;
+ };
+ };
+ };
+
sdhci@0,700b0400 {
cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>;
power-gpios = <&gpio TEGRA_GPIO(R, 0) GPIO_ACTIVE_HIGH>;
@@ -907,33 +957,6 @@
};
};

- usb@0,7d000000 {
- status = "okay";
- };
-
- usb-phy@0,7d000000 {
- status = "okay";
- vbus-supply = <&vdd_usb1_vbus>;
- };
-
- usb@0,7d004000 {
- status = "okay";
- };
-
- usb-phy@0,7d004000 {
- status = "okay";
- vbus-supply = <&vdd_run_cam>;
- };
-
- usb@0,7d008000 {
- status = "okay";
- };
-
- usb-phy@0,7d008000 {
- status = "okay";
- vbus-supply = <&vdd_usb3_vbus>;
- };
-
backlight: backlight {
compatible = "pwm-backlight";

--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:19:40

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 07/12] pinctrl: tegra-xusb: Add USB PHY support

In addition to the PCIe and SATA PHYs, the XUSB pad controller also
supports 3 UTMI, 2 HSIC, and 2 USB3 PHYs. Each USB3 PHY uses a single
PCIe or SATA lane and is mapped to one of the three UTMI ports.

The xHCI controller will also send messages intended for the PHY driver,
so request and listen for messages on the mailbox's PHY channel.

Signed-off-by: Andrew Bresticker <[email protected]>
Acked-by: Linus Walleij <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
No changes from v5.
Changes from v4:
- Disabled USB support on missing mailbox channel instead of failing
to probe.
- Made usb3-port a pinconfig property.
- Addressed review comments from Thierry.
No changes from v3.
Changes from v2:
- Added support for nvidia,otg-hs-curr-level-offset property.
- Moved mailbox request handling to workqueue.
- Added filtering out of non-PHY mailbox messages.
- Dropped "-otg" from VBUS supplies.
Changes from v1:
- Updated to use common mailbox API.
- Added SATA PHY enable sequence for USB3 ports using the SATA lane.
- Made USB3 port-to-lane mappins a top-level binding rather than a pinconfig
binding.
---
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/pinctrl-tegra-xusb.c | 1262 +++++++++++++++++++++++++++++++++-
include/soc/tegra/xusb.h | 7 +
3 files changed, 1254 insertions(+), 16 deletions(-)

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6a66de..b2a96f3 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -160,6 +160,7 @@ config PINCTRL_TEGRA124

config PINCTRL_TEGRA_XUSB
def_bool y if ARCH_TEGRA
+ depends on MAILBOX
select GENERIC_PHY
select PINCONF
select PINMUX
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
index 1631ec9..37ad84e 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -13,23 +13,54 @@

#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#include <linux/workqueue.h>
+
+#include <soc/tegra/fuse.h>
+#include <soc/tegra/xusb.h>

#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>

#include "core.h"
#include "pinctrl-utils.h"

+#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? 15 : 0)
+#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
+#define FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT 13
+#define FUSE_SKU_CALIB_HS_IREF_CAP_MASK 0x3
+#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT 11
+#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK 0x3
+#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
+#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
+
+#define XUSB_PADCTL_USB2_PORT_CAP 0x008
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(x) ((x) * 4)
+#define XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK 0x3
+#define XUSB_PADCTL_USB2_PORT_CAP_DISABLED 0x0
+#define XUSB_PADCTL_USB2_PORT_CAP_HOST 0x1
+#define XUSB_PADCTL_USB2_PORT_CAP_DEVICE 0x2
+#define XUSB_PADCTL_USB2_PORT_CAP_OTG 0x3
+
+#define XUSB_PADCTL_SS_PORT_MAP 0x014
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(x) ((x) * 4)
+#define XUSB_PADCTL_SS_PORT_MAP_PORT_MASK 0x7
+
#define XUSB_PADCTL_ELPG_PROGRAM 0x01c
#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
+#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
+#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(x) \
+ (1 << (17 + (x) * 4))
+#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))

#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
@@ -41,17 +72,136 @@
#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)

+#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(x) (0x058 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT 24
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK 0xff
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT 16
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK 0x3f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT 8
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK 0x3f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT 8
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK 0xffff
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0x7
+
+#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(x) (0x068 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT 24
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK 0x1f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT 16
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK 0x7f
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(x) ((x) < 2 ? 0x078 + (x) * 4 : \
+ 0x0f8 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT 28
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK 0x3
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(x) ((x) < 2 ? 0x090 + (x) * 4 : \
+ 0x11c + (x) * 4)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN (1 << 8)
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(x) ((x) < 2 ? 0x098 + (x) * 4 : \
+ 0x128 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT 24
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK 0x3f
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK 0x1f
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK 0x7f
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT 16
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK 0xff
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z 0x21
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP 0x32
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP 0x33
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z 0x48
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z 0xa1
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x0a0 + (x) * 4)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 20)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 19)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT 14
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK 0x3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT 6
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK 0x3f
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x0ac + (x) * 4)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT 9
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK 0x3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0x7
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0b8
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 12)
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 2
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x3
+
+#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x0c0 + (x) * 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT 12
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT 8
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT 4
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT 0
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK 0x7
+
+#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x0c8 + (x) * 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE (1 << 10)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA (1 << 9)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE (1 << 8)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA (1 << 7)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI (1 << 5)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX (1 << 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX (1 << 3)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX (1 << 2)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN (1 << 0)
+
+#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x0d0 + (x) * 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 4
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0x7
+
+#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x0e0
+#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK 0x1f
+
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT 20
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK 0x3
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)

+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13c
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT 20
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK 0xf
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT 16
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK 0xf
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN (1 << 12)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL (1 << 4)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT 0
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK 0x7
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS (1 << 7)
+
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)

+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14c
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15c
+
struct tegra_xusb_padctl_function {
const char *name;
const char * const *groups;
@@ -72,6 +222,16 @@ struct tegra_xusb_padctl_soc {

const struct tegra_xusb_padctl_lane *lanes;
unsigned int num_lanes;
+
+ u32 rx_wander;
+ u32 rx_eq;
+ u32 cdr_cntl;
+ u32 dfe_cntl;
+ u32 hs_slew;
+ u32 ls_rslew[TEGRA_XUSB_UTMI_PHYS];
+ u32 hs_discon_level;
+ u32 spare_in;
+ unsigned int hsic_port_offset;
};

struct tegra_xusb_padctl_lane {
@@ -86,6 +246,22 @@ struct tegra_xusb_padctl_lane {
unsigned int num_funcs;
};

+struct tegra_xusb_fuse_calibration {
+ u32 hs_curr_level[TEGRA_XUSB_UTMI_PHYS];
+ u32 hs_iref_cap;
+ u32 hs_term_range_adj;
+ u32 hs_squelch_level;
+};
+
+struct tegra_xusb_usb3_port {
+ unsigned int lane;
+ bool context_saved;
+ u32 tap1_val;
+ u32 amp_val;
+ u32 ctle_z_val;
+ u32 ctle_g_val;
+};
+
struct tegra_xusb_padctl {
struct device *dev;
void __iomem *regs;
@@ -93,13 +269,25 @@ struct tegra_xusb_padctl {
struct reset_control *rst;

const struct tegra_xusb_padctl_soc *soc;
+ struct tegra_xusb_fuse_calibration calib;
struct pinctrl_dev *pinctrl;
struct pinctrl_desc desc;

struct phy_provider *provider;
- struct phy *phys[2];
+ struct phy *phys[TEGRA_XUSB_NUM_PHYS];

unsigned int enable;
+
+ struct work_struct mbox_req_work;
+ struct tegra_xusb_mbox_msg mbox_req;
+ struct mbox_client mbox_client;
+ struct mbox_chan *mbox_chan;
+
+ struct tegra_xusb_usb3_port usb3_ports[TEGRA_XUSB_USB3_PHYS];
+ unsigned int utmi_enable;
+ unsigned int hs_curr_level_offset[TEGRA_XUSB_UTMI_PHYS];
+ struct regulator *vbus[TEGRA_XUSB_UTMI_PHYS];
+ struct regulator *vddio_hsic;
};

static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
@@ -114,6 +302,53 @@ static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
return readl(padctl->regs + offset);
}

+static inline struct tegra_xusb_padctl *
+mbox_work_to_padctl(struct work_struct *work)
+{
+ return container_of(work, struct tegra_xusb_padctl, mbox_req_work);
+}
+
+#define PIN_OTG_0 0
+#define PIN_OTG_1 1
+#define PIN_OTG_2 2
+#define PIN_ULPI_0 3
+#define PIN_HSIC_0 4
+#define PIN_HSIC_1 5
+#define PIN_PCIE_0 6
+#define PIN_PCIE_1 7
+#define PIN_PCIE_2 8
+#define PIN_PCIE_3 9
+#define PIN_PCIE_4 10
+#define PIN_SATA_0 11
+
+static inline bool lane_is_otg(unsigned int lane)
+{
+ return lane >= PIN_OTG_0 && lane <= PIN_OTG_2;
+}
+
+static inline bool lane_is_hsic(unsigned int lane)
+{
+ return lane >= PIN_HSIC_0 && lane <= PIN_HSIC_1;
+}
+
+static inline bool lane_is_pcie_or_sata(unsigned int lane)
+{
+ return lane >= PIN_PCIE_0 && lane <= PIN_SATA_0;
+}
+
+static int lane_to_usb3_port(struct tegra_xusb_padctl *padctl,
+ unsigned int lane)
+{
+ unsigned int i;
+
+ for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
+ if (padctl->usb3_ports[i].lane == lane)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int tegra_xusb_padctl_get_groups_count(struct pinctrl_dev *pinctrl)
{
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
@@ -131,6 +366,17 @@ static const char *tegra_xusb_padctl_get_group_name(struct pinctrl_dev *pinctrl,

enum tegra_xusb_padctl_param {
TEGRA_XUSB_PADCTL_IDDQ,
+ TEGRA_XUSB_PADCTL_USB3_PORT,
+ TEGRA_XUSB_PADCTL_USB2_PORT,
+ TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM,
+ TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM,
+ TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM,
+ TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN,
+ TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP,
+ TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN,
+ TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP,
+ TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM,
+ TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET,
};

static const struct tegra_xusb_padctl_property {
@@ -138,6 +384,18 @@ static const struct tegra_xusb_padctl_property {
enum tegra_xusb_padctl_param param;
} properties[] = {
{ "nvidia,iddq", TEGRA_XUSB_PADCTL_IDDQ },
+ { "nvidia,usb3-port", TEGRA_XUSB_PADCTL_USB3_PORT },
+ { "nvidia,usb2-port", TEGRA_XUSB_PADCTL_USB2_PORT },
+ { "nvidia,hsic-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM },
+ { "nvidia,hsic-rx-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM },
+ { "nvidia,hsic-rx-data-trim", TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM },
+ { "nvidia,hsic-tx-rtune-n", TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN },
+ { "nvidia,hsic-tx-rtune-p", TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP },
+ { "nvidia,hsic-tx-rslew-n", TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN },
+ { "nvidia,hsic-tx-rslew-p", TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP },
+ { "nvidia,hsic-auto-term", TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM },
+ { "nvidia,otg-hs-curr-level-offset",
+ TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET },
};

#define TEGRA_XUSB_PADCTL_PACK(param, value) ((param) << 16 | (value))
@@ -322,6 +580,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
const struct tegra_xusb_padctl_lane *lane;
enum tegra_xusb_padctl_param param;
u32 value;
+ int port;

param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(*config);
lane = &padctl->soc->lanes[group];
@@ -338,8 +597,136 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
value = 0;
else
value = 1;
+ break;

- *config = TEGRA_XUSB_PADCTL_PACK(param, value);
+ case TEGRA_XUSB_PADCTL_USB3_PORT:
+ value = lane_to_usb3_port(padctl, group);
+ if (value < 0) {
+ dev_err(padctl->dev,
+ "Pin %d not mapped to USB3 port\n", group);
+ return -EINVAL;
+ }
+ break;
+
+ case TEGRA_XUSB_PADCTL_USB2_PORT:
+ port = lane_to_usb3_port(padctl, group);
+ if (port < 0) {
+ dev_err(padctl->dev,
+ "Pin %d not mapped to USB3 port\n", group);
+ return -EINVAL;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP) >>
+ XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
+ value &= XUSB_PADCTL_SS_PORT_MAP_PORT_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ value = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
+ value &= XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(port)) >>
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(port)) >>
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK;
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+ if (value & XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN)
+ value = 1;
+ else
+ value = 0;
+ break;
+
+ case TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET:
+ if (!lane_is_otg(group)) {
+ dev_err(padctl->dev, "Pin %d is not an OTG pad\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_OTG_0;
+ value = padctl->hs_curr_level_offset[port];
break;

default:
@@ -348,6 +735,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
return -ENOTSUPP;
}

+ *config = TEGRA_XUSB_PADCTL_PACK(param, value);
return 0;
}

@@ -362,6 +750,7 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
unsigned long value;
unsigned int i;
u32 regval;
+ int port;

lane = &padctl->soc->lanes[group];

@@ -385,6 +774,206 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
padctl_writel(padctl, regval, lane->offset);
break;

+ case TEGRA_XUSB_PADCTL_USB3_PORT:
+ if (value >= TEGRA_XUSB_USB3_PHYS) {
+ dev_err(padctl->dev, "Invalid USB3 port: %lu\n",
+ value);
+ return -EINVAL;
+ }
+ if (!lane_is_pcie_or_sata(group)) {
+ dev_err(padctl->dev,
+ "USB3 port not applicable for pin %d\n",
+ group);
+ return -EINVAL;
+ }
+
+ padctl->usb3_ports[value].lane = group;
+ break;
+
+ case TEGRA_XUSB_PADCTL_USB2_PORT:
+ if (value >= TEGRA_XUSB_UTMI_PHYS) {
+ dev_err(padctl->dev, "Invalid USB2 port: %lu\n",
+ value);
+ return -EINVAL;
+ }
+ if (!lane_is_pcie_or_sata(group)) {
+ dev_err(padctl->dev,
+ "USB2 port not applicable for pin %d\n",
+ group);
+ return -EINVAL;
+ }
+ port = lane_to_usb3_port(padctl, group);
+ if (port < 0) {
+ dev_err(padctl->dev,
+ "Pin %d not mapped to USB3 port\n",
+ group);
+ return -EINVAL;
+ }
+
+ regval = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ regval &= ~(XUSB_PADCTL_SS_PORT_MAP_PORT_MASK <<
+ XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port));
+ regval |= value <<
+ XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
+ padctl_writel(padctl, regval, XUSB_PADCTL_SS_PORT_MAP);
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ value &= XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK;
+ padctl_writel(padctl, value,
+ XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL2(port));
+ regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT);
+ regval |= value <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL2(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL2(port));
+ regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT);
+ regval |= value <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL2(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT);
+ regval |= value <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT);
+ regval |= value <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT);
+ regval |= value <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT);
+ regval |= value <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL0(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM:
+ if (!lane_is_hsic(group)) {
+ dev_err(padctl->dev, "Pin %d not an HSIC\n",
+ group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_HSIC_0;
+ regval = padctl_readl(padctl,
+ XUSB_PADCTL_HSIC_PADX_CTL1(port));
+ if (!value)
+ regval &= ~XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
+ else
+ regval |= XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
+ padctl_writel(padctl, regval,
+ XUSB_PADCTL_HSIC_PADX_CTL1(port));
+ break;
+
+ case TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET:
+ if (!lane_is_otg(group)) {
+ dev_err(padctl->dev,
+ "Pin %d is not an OTG pad\n", group);
+ return -EINVAL;
+ }
+
+ port = group - PIN_OTG_0;
+ value &= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK;
+ padctl->hs_curr_level_offset[port] = value;
+ break;
+
default:
dev_err(padctl->dev,
"invalid configuration parameter: %04x\n",
@@ -671,6 +1260,548 @@ static const struct phy_ops sata_phy_ops = {
.owner = THIS_MODULE,
};

+static int usb3_phy_to_port(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ unsigned int i;
+
+ for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
+ if (phy == padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i])
+ return i;
+ }
+ WARN_ON(1);
+
+ return -EINVAL;
+}
+
+static int usb3_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int port = usb3_phy_to_port(phy);
+ unsigned int lane;
+ u32 value, offset;
+
+ if (port < 0)
+ return port;
+
+ lane = padctl->usb3_ports[port].lane;
+ if (!lane_is_pcie_or_sata(lane)) {
+ dev_err(padctl->dev, "USB3 PHY %d mapped to invalid lane: %d\n",
+ port, lane);
+ return -EINVAL;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT));
+ value |= (padctl->soc->rx_wander <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
+ (padctl->soc->cdr_cntl <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) |
+ (padctl->soc->rx_eq <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT);
+ if (padctl->usb3_ports[port].context_saved) {
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
+ value |= (padctl->usb3_ports[port].ctle_g_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (padctl->usb3_ports[port].ctle_z_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
+ }
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
+
+ value = padctl->soc->dfe_cntl;
+ if (padctl->usb3_ports[port].context_saved) {
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
+ value |= (padctl->usb3_ports[port].tap1_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (padctl->usb3_ports[port].amp_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
+ }
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
+
+ offset = (lane == PIN_SATA_0) ?
+ XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 :
+ XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane - PIN_PCIE_0);
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT);
+ value |= padctl->soc->spare_in <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ offset = (lane == PIN_SATA_0) ?
+ XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 :
+ XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane - PIN_PCIE_0);
+ value = padctl_readl(padctl, offset);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN;
+ padctl_writel(padctl, value, offset);
+
+ /* Enable SATA PHY when SATA lane is used */
+ if (lane == PIN_SATA_0) {
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~(XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT);
+ value |= 0x2 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
+ value &= ~((XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
+ (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
+ (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN);
+ value |= (0x7 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
+ (0x8 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
+ (0x8 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ return 0;
+}
+
+static int usb3_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int port = usb3_phy_to_port(phy);
+ u32 value;
+
+ if (port < 0)
+ return port;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(250, 350);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ return 0;
+}
+
+static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
+ unsigned int port)
+{
+ unsigned int lane = padctl->usb3_ports[port].lane;
+ u32 value, offset;
+
+ if (port >= TEGRA_XUSB_USB3_PHYS)
+ return -EINVAL;
+
+ padctl->usb3_ports[port].context_saved = true;
+
+ offset = (lane == PIN_SATA_0) ?
+ XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 :
+ XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane - PIN_PCIE_0);
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ padctl->usb3_ports[port].tap1_val = value &
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ padctl->usb3_ports[port].amp_val = value &
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
+ value |= (padctl->usb3_ports[port].tap1_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (padctl->usb3_ports[port].amp_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ padctl->usb3_ports[port].ctle_g_val = value &
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ padctl->usb3_ports[port].ctle_z_val = value &
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
+ value |= (padctl->usb3_ports[port].ctle_g_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (padctl->usb3_ports[port].ctle_z_val <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
+
+ return 0;
+}
+
+static const struct phy_ops usb3_phy_ops = {
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = usb3_phy_power_on,
+ .power_off = usb3_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int utmi_phy_to_port(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ unsigned int i;
+
+ for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
+ if (phy == padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i])
+ return i;
+ }
+ WARN_ON(1);
+
+ return -EINVAL;
+}
+
+static int utmi_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int port = utmi_phy_to_port(phy);
+ int err;
+ u32 value;
+
+ if (port < 0)
+ return port;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT));
+ value |= (padctl->calib.hs_squelch_level <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
+ (padctl->soc->hs_discon_level <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ value &= ~(XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK <<
+ XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(port));
+ value |= XUSB_PADCTL_USB2_PORT_CAP_HOST <<
+ XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port));
+ value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT) |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
+ value |= (padctl->calib.hs_curr_level[port] +
+ padctl->hs_curr_level_offset[port]) <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
+ value |= padctl->soc->hs_slew <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT;
+ value |= padctl->soc->ls_rslew[port] <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
+ value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT) |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP);
+ value |= (padctl->calib.hs_term_range_adj <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
+ (padctl->calib.hs_iref_cap <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
+
+ err = regulator_enable(padctl->vbus[port]);
+ if (err)
+ return err;
+
+ mutex_lock(&padctl->lock);
+
+ if (padctl->utmi_enable++ > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+out:
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int utmi_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int port = utmi_phy_to_port(phy);
+ u32 value;
+
+ if (port < 0)
+ return port;
+
+ regulator_disable(padctl->vbus[port]);
+
+ mutex_lock(&padctl->lock);
+
+ if (WARN_ON(padctl->utmi_enable == 0))
+ goto out;
+
+ if (--padctl->utmi_enable > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+out:
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static const struct phy_ops utmi_phy_ops = {
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = utmi_phy_power_on,
+ .power_off = utmi_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int hsic_phy_to_port(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ unsigned int i;
+
+ for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
+ if (phy == padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i])
+ return i;
+ }
+ WARN_ON(1);
+
+ return -EINVAL;
+}
+
+static int hsic_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int port = hsic_phy_to_port(phy);
+ int err;
+ u32 value;
+
+ if (port < 0)
+ return port;
+
+ err = regulator_enable(padctl->vddio_hsic);
+ if (err)
+ return err;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX);
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+
+ return 0;
+}
+
+static int hsic_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int port = hsic_phy_to_port(phy);
+ u32 value;
+
+ if (port < 0)
+ return port;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+
+ regulator_disable(padctl->vddio_hsic);
+
+ return 0;
+}
+
+static void hsic_phy_set_idle(struct tegra_xusb_padctl *padctl,
+ unsigned int port, bool idle)
+{
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+ if (idle)
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
+ else
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE);
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+}
+
+static const struct phy_ops hsic_phy_ops = {
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = hsic_phy_power_on,
+ .power_off = hsic_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static void tegra_xusb_phy_mbox_work(struct work_struct *work)
+{
+ struct tegra_xusb_padctl *padctl = mbox_work_to_padctl(work);
+ struct tegra_xusb_mbox_msg *msg = &padctl->mbox_req;
+ struct tegra_xusb_mbox_msg resp;
+ unsigned int i;
+ u32 ports;
+
+ resp.cmd = 0;
+ switch (msg->cmd) {
+ case MBOX_CMD_SAVE_DFE_CTLE_CTX:
+ resp.data = msg->data;
+ if (usb3_phy_save_context(padctl, msg->data) < 0)
+ resp.cmd = MBOX_CMD_NAK;
+ else
+ resp.cmd = MBOX_CMD_ACK;
+ break;
+ case MBOX_CMD_START_HSIC_IDLE:
+ case MBOX_CMD_STOP_HSIC_IDLE:
+ ports = msg->data >> (padctl->soc->hsic_port_offset + 1);
+ resp.data = msg->data;
+ resp.cmd = MBOX_CMD_ACK;
+ for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
+ if (!(ports & BIT(i)))
+ continue;
+ if (msg->cmd == MBOX_CMD_START_HSIC_IDLE)
+ hsic_phy_set_idle(padctl, i, true);
+ else
+ hsic_phy_set_idle(padctl, i, false);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (resp.cmd)
+ mbox_send_message(padctl->mbox_chan, &resp);
+}
+
+static bool is_phy_mbox_message(u32 cmd)
+{
+ switch (cmd) {
+ case MBOX_CMD_SAVE_DFE_CTLE_CTX:
+ case MBOX_CMD_START_HSIC_IDLE:
+ case MBOX_CMD_STOP_HSIC_IDLE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void tegra_xusb_phy_mbox_rx(struct mbox_client *cl, void *data)
+{
+ struct tegra_xusb_padctl *padctl = dev_get_drvdata(cl->dev);
+ struct tegra_xusb_mbox_msg *msg = data;
+
+ if (is_phy_mbox_message(msg->cmd)) {
+ padctl->mbox_req = *msg;
+ schedule_work(&padctl->mbox_req_work);
+ }
+}
+
static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
struct of_phandle_args *args)
{
@@ -680,25 +1811,12 @@ static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
if (args->args_count <= 0)
return ERR_PTR(-EINVAL);

- if (index >= ARRAY_SIZE(padctl->phys))
+ if (index >= ARRAY_SIZE(padctl->phys) && !padctl->phys[index])
return ERR_PTR(-EINVAL);

return padctl->phys[index];
}

-#define PIN_OTG_0 0
-#define PIN_OTG_1 1
-#define PIN_OTG_2 2
-#define PIN_ULPI_0 3
-#define PIN_HSIC_0 4
-#define PIN_HSIC_1 5
-#define PIN_PCIE_0 6
-#define PIN_PCIE_1 7
-#define PIN_PCIE_2 8
-#define PIN_PCIE_3 9
-#define PIN_PCIE_4 10
-#define PIN_SATA_0 11
-
static const struct pinctrl_pin_desc tegra124_pins[] = {
PINCTRL_PIN(PIN_OTG_0, "otg-0"),
PINCTRL_PIN(PIN_OTG_1, "otg-1"),
@@ -856,6 +1974,15 @@ static const struct tegra_xusb_padctl_soc tegra124_soc = {
.functions = tegra124_functions,
.num_lanes = ARRAY_SIZE(tegra124_lanes),
.lanes = tegra124_lanes,
+ .rx_wander = 0xf,
+ .rx_eq = 0xf070,
+ .cdr_cntl = 0x24,
+ .dfe_cntl = 0x002008ee,
+ .hs_slew = 0xe,
+ .ls_rslew = {0x3, 0x0, 0x0},
+ .hs_discon_level = 0x5,
+ .spare_in = 0x1,
+ .hsic_port_offset = 6,
};

static const struct of_device_id tegra_xusb_padctl_of_match[] = {
@@ -864,6 +1991,80 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);

+static int tegra_xusb_read_fuse_calibration(struct tegra_xusb_padctl *padctl)
+{
+ unsigned int i;
+ int err;
+ u32 value;
+
+ err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
+ padctl->calib.hs_curr_level[i] =
+ (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) &
+ FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK;
+ }
+ padctl->calib.hs_iref_cap =
+ (value >> FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT) &
+ FUSE_SKU_CALIB_HS_IREF_CAP_MASK;
+ padctl->calib.hs_term_range_adj =
+ (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) &
+ FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK;
+ padctl->calib.hs_squelch_level =
+ (value >> FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT) &
+ FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK;
+
+ return 0;
+}
+
+static int tegra_xusb_setup_usb(struct tegra_xusb_padctl *padctl)
+{
+ struct phy *phy;
+ unsigned int i;
+
+ for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
+ phy = devm_phy_create(padctl->dev, NULL, &usb3_phy_ops, NULL);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i] = phy;
+ phy_set_drvdata(phy, padctl);
+ }
+
+ for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
+ char reg_name[sizeof("vbus-N")];
+
+ sprintf(reg_name, "vbus-%d", i);
+ padctl->vbus[i] = devm_regulator_get(padctl->dev, reg_name);
+ if (IS_ERR(padctl->vbus[i]))
+ return PTR_ERR(padctl->vbus[i]);
+
+ phy = devm_phy_create(padctl->dev, NULL, &utmi_phy_ops, NULL);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i] = phy;
+ phy_set_drvdata(phy, padctl);
+ }
+
+ padctl->vddio_hsic = devm_regulator_get(padctl->dev, "vddio-hsic");
+ if (IS_ERR(padctl->vddio_hsic))
+ return PTR_ERR(padctl->vddio_hsic);
+
+ for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
+ phy = devm_phy_create(padctl->dev, NULL, &hsic_phy_ops, NULL);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i] = phy;
+ phy_set_drvdata(phy, padctl);
+ }
+
+ return 0;
+}
+
static int tegra_xusb_padctl_probe(struct platform_device *pdev)
{
struct tegra_xusb_padctl *padctl;
@@ -888,6 +2089,10 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
if (IS_ERR(padctl->regs))
return PTR_ERR(padctl->regs);

+ err = tegra_xusb_read_fuse_calibration(padctl);
+ if (err < 0)
+ return err;
+
padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(padctl->rst))
return PTR_ERR(padctl->rst);
@@ -928,6 +2133,26 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
padctl->phys[TEGRA_XUSB_PADCTL_SATA] = phy;
phy_set_drvdata(phy, padctl);

+ INIT_WORK(&padctl->mbox_req_work, tegra_xusb_phy_mbox_work);
+ padctl->mbox_client.dev = &pdev->dev;
+ padctl->mbox_client.tx_block = true;
+ padctl->mbox_client.tx_tout = 0;
+ padctl->mbox_client.rx_callback = tegra_xusb_phy_mbox_rx;
+ padctl->mbox_chan = mbox_request_channel(&padctl->mbox_client, 0);
+ if (IS_ERR(padctl->mbox_chan)) {
+ err = PTR_ERR(padctl->mbox_chan);
+ if (err == -EPROBE_DEFER) {
+ goto unregister;
+ } else {
+ dev_warn(&pdev->dev,
+ "failed to get mailbox, USB support disabled");
+ }
+ } else {
+ err = tegra_xusb_setup_usb(padctl);
+ if (err)
+ goto unregister;
+ }
+
padctl->provider = devm_of_phy_provider_register(&pdev->dev,
tegra_xusb_padctl_xlate);
if (IS_ERR(padctl->provider)) {
@@ -950,6 +2175,11 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
int err;

+ if (!IS_ERR(padctl->mbox_chan)) {
+ cancel_work_sync(&padctl->mbox_req_work);
+ mbox_free_channel(padctl->mbox_chan);
+ }
+
pinctrl_unregister(padctl->pinctrl);

err = reset_control_assert(padctl->rst);
diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h
index 5ce5e12..0136dc1 100644
--- a/include/soc/tegra/xusb.h
+++ b/include/soc/tegra/xusb.h
@@ -10,6 +10,13 @@
#ifndef __SOC_TEGRA_XUSB_H__
#define __SOC_TEGRA_XUSB_H__

+#define TEGRA_XUSB_USB3_PHYS 2
+#define TEGRA_XUSB_UTMI_PHYS 3
+#define TEGRA_XUSB_HSIC_PHYS 2
+#define TEGRA_XUSB_NUM_USB_PHYS (TEGRA_XUSB_USB3_PHYS + TEGRA_XUSB_UTMI_PHYS + \
+ TEGRA_XUSB_HSIC_PHYS)
+#define TEGRA_XUSB_NUM_PHYS (TEGRA_XUSB_NUM_USB_PHYS + 2) /* + SATA & PCIe */
+
/* Command requests from the firmware */
enum tegra_xusb_mbox_cmd {
MBOX_CMD_MSG_ENABLED = 1,
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:19:38

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 08/12] of: Add NVIDIA Tegra xHCI controller binding

Add device-tree binding documentation for the xHCI controller present
on Tegra124 and later SoCs.

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
No changes from v5.
Changes from v4:
- Updated regulator names, as suggested by Thierry.
No changes from v3.
Changes from v2:
- Added mbox-names property.
Changes from v1:
- Updated to use common mailbox bindings.
- Added remaining XUSB-related clocks and resets.
- Updated list of power supplies to be more accurate wrt to the hardware.
---
.../bindings/usb/nvidia,tegra124-xhci.txt | 104 +++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra124-xhci.txt

diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra124-xhci.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xhci.txt
new file mode 100644
index 0000000..0047311
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xhci.txt
@@ -0,0 +1,104 @@
+NVIDIA Tegra xHCI controller
+============================
+
+The Tegra xHCI controller supports both USB2 and USB3 interfaces exposed
+by the Tegra XUSB pad controller.
+
+Required properties:
+--------------------
+ - compatible: Should be "nvidia,tegra124-xhci".
+ - reg: Address and length of the register sets. There should be three
+ entries in the following order: xHCI host registers, FPCI registers, and
+ IPFS registers.
+ - interrupts: xHCI host interrupt.
+ - clocks: Must contain an entry for each entry in clock-names.
+ See ../clock/clock-bindings.txt for details.
+ - clock-names: Must include the following entries:
+ - xusb_host
+ - xusb_host_src
+ - xusb_dev
+ - xusb_dev_src
+ - xusb_falcon_src
+ - xusb_ss
+ - xusb_ss_src
+ - xusb_ss_div2
+ - xusb_hs_src
+ - xusb_fs_src
+ - pll_u_480m
+ - clk_m
+ - pll_e
+ - resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+ - reset-names: Must include the following entries:
+ - xusb_host
+ - xusb_dev
+ - xusb_ss
+ - xusb
+ Note that xusb_dev is the shared reset for xusb_dev and xusb_dev_src and
+ that xusb is the shared reset for xusb_{ss,hs,fs,falcon,host}_src.
+ - mboxes: Must contain an entry for the XUSB mailbox channel.
+ See ../mailbox/mailbox.txt for details.
+ - mbox-names: Must include the following entries:
+ - xusb
+ - avddio-pex-supply: PCIe/USB3 analog logic power supply. Must supply 1.05V.
+ - dvddio-pex-supply: PCIe/USB3 digital logic power supply. Must supply 1.05V.
+ - avdd-usb-supply: USB controller power supply. Must supply 3.3V.
+ - avdd-pll-utmip-supply: UTMI PLL power supply. Must supply 1.8V.
+ - avdd-pll-erefe-supply: PLLE reference PLL power supply. Must supply 1.05V.
+ - avdd-usb-ss-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05V.
+ - hvdd-usb-ss-supply: High-voltage PCIe/USB3 power supply. Must supply 3.3V.
+ - hvdd-usb-ss-pll-e-supply: High-voltage PLLE power supply. Must supply 3.3V.
+
+Optional properties:
+--------------------
+ - phys: Must contain an entry for each entry in phy-names.
+ See ../phy/phy-bindings.txt for details.
+ - phy-names: Should include an entry for each PHY used by the controller.
+ May be a subset of the following:
+ - utmi-{0,1,2}
+ - hsic-{0,1}
+ - usb3-{0,1}
+
+Example:
+--------
+ usb@0,70090000 {
+ compatible = "nvidia,tegra124-xhci";
+ reg = <0x0 0x70090000 0x0 0x8000>,
+ <0x0 0x70098000 0x0 0x1000>,
+ <0x0 0x70099000 0x0 0x1000>;
+ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_XUSB_HOST>,
+ <&tegra_car TEGRA124_CLK_XUSB_HOST_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_DEV>,
+ <&tegra_car TEGRA124_CLK_XUSB_DEV_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_FALCON_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS_DIV2>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_HS_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_FS_SRC>,
+ <&tegra_car TEGRA124_CLK_PLL_U_480M>,
+ <&tegra_car TEGRA124_CLK_CLK_M>,
+ <&tegra_car TEGRA124_CLK_PLL_E>;
+ clock-names = "xusb_host", "xusb_host_src", "xusb_dev",
+ "xusb_dev_src", "xusb_falcon_src", "xusb_ss",
+ "xusb_ss_div2", "xusb_ss_src", "xusb_hs_src",
+ "xusb_fs_src", "pll_u_480m", "clk_m", "pll_e";
+ resets = <&tegra_car 89>, <&tegra_car 95>, <&tegra_car 156>,
+ <&tegra_car 143>;
+ reset-names = "xusb_host", "xusb_dev", "xusb_ss", "xusb";
+ mboxes = <&xusb_mbox>;
+ mbox-names = "xusb";
+ phys = <&padctl TEGRA_XUSB_PADCTL_UTMI_P1>, /* mini-PCIe USB */
+ <&padctl TEGRA_XUSB_PADCTL_UTMI_P2>, /* USB A */
+ <&padctl TEGRA_XUSB_PADCTL_USB3_P0>; /* USB A */
+ phy-names = "utmi-1", "utmi-2", "usb3-0";
+ avddio-pex-supply = <&vdd_1v05_run>;
+ dvddio-pex-supply = <&vdd_1v05_run>;
+ avdd-usb-supply = <&vdd_3v3_lp0>;
+ avdd-pll-utmip-supply = <&vddio_1v8>;
+ avdd-pll-erefe-supply = <&avdd_1v05_run>;
+ avdd-usb-ss-pll-supply = <&vdd_1v05_run>;
+ hvdd-usb-ss-supply = <&vdd_3v3_lp0>;
+ hvdd-usb-ss-pll-e-supply = <&vdd_3v3_lp0>;
+ };
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:20:37

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 05/12] mailbox: Add NVIDIA Tegra XUSB mailbox driver

The Tegra xHCI controller's firmware communicates requests to the host
processor through a mailbox interface. While there is only a single
physical channel, messages sent by the controller can be divided
into two groups: those intended for the PHY driver and those intended
for the host-controller driver. The requesting driver is assigned
one of two virtual channels when the single physical channel is
requested. All incoming messages are sent to both virtual channels.

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
---
Thierry,

I've left this as a separate driver because I don't think it should be
combined with the xHCI host. Stephen and I discussed this earlier in
review of v1 of this series and he agreed that the mailbox belongs in
its own DT node and driver.

Changes from v5:
- Poll for TX completion using MBOX_OWNER field.
Changes from v4:
- Use chan->cl to indicate channel allocation status
- Addressed review comments from Thierry
No changes from v3.
Changes from v2:
- Fixed mailbox IRQ vs. channel alloc/free race.
- Renamed defines to match TRM.
- Dropped channel specifier and instead allocated virtual channels as they
were requested.
- Removed MODULE_ALIAS.
Changes from v1:
- Converted to common mailbox framework.
- Removed useless polling sequences in TX path.
- Moved xusb include from linux/ to soc/tegra/
---
drivers/mailbox/Kconfig | 3 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/tegra-xusb-mailbox.c | 278 +++++++++++++++++++++++++++++++++++
include/soc/tegra/xusb.h | 43 ++++++
4 files changed, 326 insertions(+)
create mode 100644 drivers/mailbox/tegra-xusb-mailbox.c
create mode 100644 include/soc/tegra/xusb.h

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 9fd9c67..97369c2 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -33,4 +33,7 @@ config OMAP_MBOX_KFIFO_SIZE
Specify the default size of mailbox's kfifo buffers (bytes).
This can also be changed at runtime (via the mbox_kfifo_size
module parameter).
+
+config TEGRA_XUSB_MBOX
+ def_bool y if ARCH_TEGRA
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 94ed7ce..7f0af9c 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -5,3 +5,5 @@ obj-$(CONFIG_MAILBOX) += mailbox.o
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o

obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
+
+obj-$(CONFIG_TEGRA_XUSB_MBOX) += tegra-xusb-mailbox.o
diff --git a/drivers/mailbox/tegra-xusb-mailbox.c b/drivers/mailbox/tegra-xusb-mailbox.c
new file mode 100644
index 0000000..d5874e4
--- /dev/null
+++ b/drivers/mailbox/tegra-xusb-mailbox.c
@@ -0,0 +1,278 @@
+/*
+ * NVIDIA Tegra XUSB mailbox driver
+ *
+ * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/xusb.h>
+
+#define XUSB_MBOX_NUM_CHANS 2 /* Host + PHY */
+
+#define XUSB_CFG_ARU_MBOX_CMD 0xe4
+#define MBOX_DEST_FALC BIT(27)
+#define MBOX_DEST_PME BIT(28)
+#define MBOX_DEST_SMI BIT(29)
+#define MBOX_DEST_XHCI BIT(30)
+#define MBOX_INT_EN BIT(31)
+#define XUSB_CFG_ARU_MBOX_DATA_IN 0xe8
+#define CMD_DATA_SHIFT 0
+#define CMD_DATA_MASK 0xffffff
+#define CMD_TYPE_SHIFT 24
+#define CMD_TYPE_MASK 0xff
+#define XUSB_CFG_ARU_MBOX_DATA_OUT 0xec
+#define XUSB_CFG_ARU_MBOX_OWNER 0xf0
+#define MBOX_OWNER_NONE 0
+#define MBOX_OWNER_FW 1
+#define MBOX_OWNER_SW 2
+#define XUSB_CFG_ARU_SMI_INTR 0x428
+#define MBOX_SMI_INTR_FW_HANG BIT(1)
+#define MBOX_SMI_INTR_EN BIT(3)
+
+struct tegra_xusb_mbox {
+ struct mbox_controller mbox;
+ void __iomem *regs;
+ spinlock_t lock;
+};
+
+static inline u32 mbox_readl(struct tegra_xusb_mbox *mbox, unsigned long offset)
+{
+ return readl(mbox->regs + offset);
+}
+
+static inline void mbox_writel(struct tegra_xusb_mbox *mbox, u32 val,
+ unsigned long offset)
+{
+ writel(val, mbox->regs + offset);
+}
+
+static inline struct tegra_xusb_mbox *to_tegra_mbox(struct mbox_controller *c)
+{
+ return container_of(c, struct tegra_xusb_mbox, mbox);
+}
+
+static inline u32 mbox_pack_msg(struct tegra_xusb_mbox_msg *msg)
+{
+ u32 val;
+
+ val = (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT;
+ val |= (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT;
+
+ return val;
+}
+
+static inline void mbox_unpack_msg(u32 val, struct tegra_xusb_mbox_msg *msg)
+{
+ msg->cmd = (val >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
+ msg->data = (val >> CMD_DATA_SHIFT) & CMD_DATA_MASK;
+}
+
+static int tegra_xusb_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+ struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox);
+ struct tegra_xusb_mbox_msg *msg = data;
+ unsigned long flags;
+ u32 reg, owner;
+
+ dev_dbg(mbox->mbox.dev, "TX message %#x:%#x\n", msg->cmd, msg->data);
+
+ /* ACK/NAK must be sent with the controller as the mailbox owner */
+ if (msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)
+ owner = MBOX_OWNER_FW;
+ else
+ owner = MBOX_OWNER_SW;
+
+ spin_lock_irqsave(&mbox->lock, flags);
+
+ /* Acquire mailbox */
+ if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != MBOX_OWNER_NONE) {
+ dev_err(mbox->mbox.dev, "Mailbox not idle\n");
+ goto busy;
+ }
+ mbox_writel(mbox, owner, XUSB_CFG_ARU_MBOX_OWNER);
+ if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != owner) {
+ dev_err(mbox->mbox.dev, "Failed to acquire mailbox");
+ goto busy;
+ }
+
+ mbox_writel(mbox, mbox_pack_msg(msg), XUSB_CFG_ARU_MBOX_DATA_IN);
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD);
+ reg |= MBOX_INT_EN | MBOX_DEST_FALC;
+ mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD);
+
+ spin_unlock_irqrestore(&mbox->lock, flags);
+
+ return 0;
+busy:
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return -EBUSY;
+}
+
+static int tegra_xusb_mbox_startup(struct mbox_chan *chan)
+{
+ return 0;
+}
+
+static void tegra_xusb_mbox_shutdown(struct mbox_chan *chan)
+{
+}
+
+static bool tegra_xusb_mbox_last_tx_done(struct mbox_chan *chan)
+{
+ struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox);
+
+ return mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) == MBOX_OWNER_NONE;
+}
+
+static const struct mbox_chan_ops tegra_xusb_mbox_chan_ops = {
+ .send_data = tegra_xusb_mbox_send_data,
+ .startup = tegra_xusb_mbox_startup,
+ .shutdown = tegra_xusb_mbox_shutdown,
+ .last_tx_done = tegra_xusb_mbox_last_tx_done,
+};
+
+static irqreturn_t tegra_xusb_mbox_irq(int irq, void *p)
+{
+ struct tegra_xusb_mbox *mbox = p;
+ struct tegra_xusb_mbox_msg msg;
+ unsigned int i;
+ u32 reg;
+
+ spin_lock(&mbox->lock);
+
+ /* Clear mbox interrupts */
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_SMI_INTR);
+ if (reg & MBOX_SMI_INTR_FW_HANG)
+ dev_err(mbox->mbox.dev, "Controller firmware hang\n");
+ mbox_writel(mbox, reg, XUSB_CFG_ARU_SMI_INTR);
+
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_DATA_OUT);
+ mbox_unpack_msg(reg, &msg);
+
+ /*
+ * Set the mailbox back to idle. The recipient of the message is
+ * responsible for sending an ACK/NAK, if necessary.
+ */
+ reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD);
+ reg &= ~MBOX_DEST_SMI;
+ mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD);
+ mbox_writel(mbox, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER);
+
+ dev_dbg(mbox->mbox.dev, "RX message %#x:%#x\n", msg.cmd, msg.data);
+ for (i = 0; i < XUSB_MBOX_NUM_CHANS; i++) {
+ if (mbox->mbox.chans[i].cl)
+ mbox_chan_received_data(&mbox->mbox.chans[i], &msg);
+ }
+
+ spin_unlock(&mbox->lock);
+
+ return IRQ_HANDLED;
+}
+
+static struct mbox_chan *tegra_xusb_mbox_of_xlate(struct mbox_controller *ctlr,
+ const struct of_phandle_args *sp)
+{
+ struct tegra_xusb_mbox *mbox = to_tegra_mbox(ctlr);
+ struct mbox_chan *chan = ERR_PTR(-EINVAL);
+ unsigned long flags;
+ unsigned int i;
+
+ /* Pick the first available (virtual) channel. */
+ spin_lock_irqsave(&mbox->lock, flags);
+ for (i = 0; XUSB_MBOX_NUM_CHANS; i++) {
+ if (!ctlr->chans[i].cl) {
+ chan = &ctlr->chans[i];
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mbox->lock, flags);
+
+ return chan;
+}
+
+static const struct of_device_id tegra_xusb_mbox_of_match[] = {
+ { .compatible = "nvidia,tegra124-xusb-mbox" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_xusb_mbox_of_match);
+
+static int tegra_xusb_mbox_probe(struct platform_device *pdev)
+{
+ struct tegra_xusb_mbox *mbox;
+ struct resource *res;
+ int irq, ret;
+
+ mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, mbox);
+ spin_lock_init(&mbox->lock);
+
+ mbox->mbox.dev = &pdev->dev;
+ mbox->mbox.chans = devm_kcalloc(&pdev->dev, XUSB_MBOX_NUM_CHANS,
+ sizeof(*mbox->mbox.chans), GFP_KERNEL);
+ if (!mbox->mbox.chans)
+ return -ENOMEM;
+ mbox->mbox.num_chans = XUSB_MBOX_NUM_CHANS;
+ mbox->mbox.ops = &tegra_xusb_mbox_chan_ops;
+ mbox->mbox.txdone_poll = true;
+ mbox->mbox.txpoll_period = 1;
+ mbox->mbox.of_xlate = tegra_xusb_mbox_of_xlate;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ mbox->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!mbox->regs)
+ return -ENOMEM;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(&pdev->dev, irq, tegra_xusb_mbox_irq, 0,
+ dev_name(&pdev->dev), mbox);
+ if (ret < 0)
+ return ret;
+
+ ret = mbox_controller_register(&mbox->mbox);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to register mailbox: %d\n", ret);
+
+ return ret;
+}
+
+static int tegra_xusb_mbox_remove(struct platform_device *pdev)
+{
+ struct tegra_xusb_mbox *mbox = platform_get_drvdata(pdev);
+
+ mbox_controller_unregister(&mbox->mbox);
+
+ return 0;
+}
+
+static struct platform_driver tegra_xusb_mbox_driver = {
+ .probe = tegra_xusb_mbox_probe,
+ .remove = tegra_xusb_mbox_remove,
+ .driver = {
+ .name = "tegra-xusb-mbox",
+ .of_match_table = of_match_ptr(tegra_xusb_mbox_of_match),
+ },
+};
+module_platform_driver(tegra_xusb_mbox_driver);
+
+MODULE_AUTHOR("Andrew Bresticker <[email protected]>");
+MODULE_DESCRIPTION("NVIDIA Tegra XUSB mailbox driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h
new file mode 100644
index 0000000..5ce5e12
--- /dev/null
+++ b/include/soc/tegra/xusb.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __SOC_TEGRA_XUSB_H__
+#define __SOC_TEGRA_XUSB_H__
+
+/* Command requests from the firmware */
+enum tegra_xusb_mbox_cmd {
+ MBOX_CMD_MSG_ENABLED = 1,
+ MBOX_CMD_INC_FALC_CLOCK,
+ MBOX_CMD_DEC_FALC_CLOCK,
+ MBOX_CMD_INC_SSPI_CLOCK,
+ MBOX_CMD_DEC_SSPI_CLOCK,
+ MBOX_CMD_SET_BW, /* no ACK/NAK required */
+ MBOX_CMD_SET_SS_PWR_GATING,
+ MBOX_CMD_SET_SS_PWR_UNGATING,
+ MBOX_CMD_SAVE_DFE_CTLE_CTX,
+ MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */
+ MBOX_CMD_AIRPLANE_MODE_DISABLED, /* unused */
+ MBOX_CMD_START_HSIC_IDLE,
+ MBOX_CMD_STOP_HSIC_IDLE,
+ MBOX_CMD_DBC_WAKE_STACK, /* unused */
+ MBOX_CMD_HSIC_PRETEND_CONNECT,
+
+ MBOX_CMD_MAX,
+
+ /* Response message to above commands */
+ MBOX_CMD_ACK = 128,
+ MBOX_CMD_NAK
+};
+
+struct tegra_xusb_mbox_msg {
+ u32 cmd;
+ u32 data;
+};
+
+#endif /* __SOC_TEGRA_XUSB_H__ */
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:17:35

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 01/12] xhci: Set shared HCD's hcd_priv in xhci_gen_setup

xhci_gen_setup() sets the hcd_priv field for the primary HCD, but not
for the shared HCD, requiring xHCI host-controller drivers to set it
between usb_create_shared_hcd() and usb_add_hcd(). There's no reason
xhci_gen_setup() can't set the shared HCD's hcd_priv as well, so move
that bit out of the host-controller drivers and into xhci_gen_setup().

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Felipe Balbi <[email protected]>
---
No changes from v5.
New for v5.
Peviously posted here: https://lkml.org/lkml/2014/10/30/726
---
drivers/usb/host/xhci-pci.c | 5 -----
drivers/usb/host/xhci-plat.c | 5 -----
drivers/usb/host/xhci.c | 6 +++---
3 files changed, 3 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 9a69b1f..31025e4 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -221,11 +221,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto dealloc_usb2_hcd;
}

- /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset)
- * is called by usb_add_hcd().
- */
- *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
-
retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
IRQF_SHARED);
if (retval)
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 3d78b0c..5bc33bc 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -149,11 +149,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
(pdata && pdata->usb3_lpm_capable))
xhci->quirks |= XHCI_LPM_SUPPORT;
- /*
- * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
- * is called by usb_add_hcd().
- */
- *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;

if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2a5d45b..ae05471 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4843,9 +4843,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
*/
hcd->has_tt = 1;
} else {
- /* xHCI private pointer was set in xhci_pci_probe for the second
- * registered roothub.
- */
+ xhci = hcd_to_xhci(hcd->primary_hcd);
+ *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
+
return 0;
}

--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:21:10

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 04/12] of: Add NVIDIA Tegra XUSB mailbox binding

Add device-tree bindings for the Tegra XUSB mailbox which will be used
for communication between the Tegra xHCI controller's firmware and the
host processor.

Signed-off-by: Andrew Bresticker <[email protected]>
Reviewed-by: Stephen Warren <[email protected]>
Acked-by: Jassi Brar <[email protected]>
---
No changes from v3/v4/v5.
Changes from v2:
- Dropped channel specifier.
- Added pointer to mailbox documentation.
Changes from v1:
- Updated to use common mailbox bindings.
---
.../bindings/mailbox/nvidia,tegra124-xusb-mbox.txt | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt

diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt
new file mode 100644
index 0000000..b35ea6e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt
@@ -0,0 +1,32 @@
+NVIDIA Tegra XUSB mailbox
+=========================
+
+The Tegra XUSB mailbox is used by the Tegra xHCI controller's firmware to
+communicate requests to the host and PHY drivers.
+
+Refer to ./mailbox.txt for generic information about mailbox device-tree
+bindings.
+
+Required properties:
+--------------------
+ - compatible: Should be "nvidia,tegra124-xusb-mbox".
+ - reg: Address and length of the XUSB FPCI registers.
+ - interrupts: XUSB mailbox interrupt.
+ - #mbox-cells: Should be 0. There is only one physical channel.
+
+Example:
+--------
+ xusb_mbox: mailbox@0,70098000 {
+ compatible = "nvidia,tegra124-xusb-mbox";
+ reg = <0x0 0x70098000 0x0 0x1000>;
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+
+ #mbox-cells = <0>;
+ };
+
+ usb@0,70090000 {
+ ...
+ mboxes = <&xusb_mbox>;
+ mbox-names = "xusb";
+ ...
+ };
--
2.1.0.rc2.206.gedb03e5

2014-11-25 00:22:00

by Andrew Bresticker

[permalink] [raw]
Subject: [PATCH V6 02/12] mailbox: Make struct mbox_controller's ops field const

The mailbox controller's channel ops ought to be read-only.

Signed-off-by: Andrew Bresticker <[email protected]>
---
No changes from v5.
New for v5.
---
include/linux/mailbox_controller.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index d4cf96f..68c4245 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -72,7 +72,7 @@ struct mbox_chan_ops {
*/
struct mbox_controller {
struct device *dev;
- struct mbox_chan_ops *ops;
+ const struct mbox_chan_ops *ops;
struct mbox_chan *chans;
int num_chans;
bool txdone_irq;
--
2.1.0.rc2.206.gedb03e5

2014-11-25 13:32:26

by Jassi Brar

[permalink] [raw]
Subject: Re: [PATCH V6 00/12] Tegra xHCI support

On 25 November 2014 at 05:47, Andrew Bresticker <[email protected]> wrote:
> This series adds support for xHCI on NVIDIA Tegra SoCs. This includes:
> - patches 1, 2, and 3: minor cleanups for mailbox framework and xHCI,
> - patches 4 and 5: adding a driver for the mailbox used to communicate
> with the xHCI controller's firmware,
> - patches 6 and 7: extending the XUSB pad controller driver to support
> the USB PHY types (UTMI, HSIC, and USB3),
> - patches 8 and 9: adding a xHCI host-controller driver, and
> - patches 10, 11, and 12: updating the relevant DT files.
>
> The mailbox driver (patch 5) has a compile-time dependency on patch 2 and
> a run-time dependency on patch 3. Both the PHY (patch 7) and host (patch 9)
> drivers have compile-time dependencies on the mailbox driver. The host
> driver also has a run-time dependency on patch 1. Because of this, this
> entire series should probably go through the Tegra tree.
>
Why shouldn't I pick 2 & 3 at least?

-Jassi

2014-11-25 13:50:40

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH V6 07/12] pinctrl: tegra-xusb: Add USB PHY support

Hi,

On Tuesday 25 November 2014 05:47 AM, Andrew Bresticker wrote:
> In addition to the PCIe and SATA PHYs, the XUSB pad controller also
> supports 3 UTMI, 2 HSIC, and 2 USB3 PHYs. Each USB3 PHY uses a single
> PCIe or SATA lane and is mapped to one of the three UTMI ports.
>
> The xHCI controller will also send messages intended for the PHY driver,
> so request and listen for messages on the mailbox's PHY channel.
>
> Signed-off-by: Andrew Bresticker <[email protected]>
> Acked-by: Linus Walleij <[email protected]>
> Reviewed-by: Stephen Warren <[email protected]>
> ---
> No changes from v5.
> Changes from v4:
> - Disabled USB support on missing mailbox channel instead of failing
> to probe.
> - Made usb3-port a pinconfig property.
> - Addressed review comments from Thierry.
> No changes from v3.
> Changes from v2:
> - Added support for nvidia,otg-hs-curr-level-offset property.
> - Moved mailbox request handling to workqueue.
> - Added filtering out of non-PHY mailbox messages.
> - Dropped "-otg" from VBUS supplies.
> Changes from v1:
> - Updated to use common mailbox API.
> - Added SATA PHY enable sequence for USB3 ports using the SATA lane.
> - Made USB3 port-to-lane mappins a top-level binding rather than a pinconfig
> binding.
> ---
> drivers/pinctrl/Kconfig | 1 +
> drivers/pinctrl/pinctrl-tegra-xusb.c | 1262 +++++++++++++++++++++++++++++++++-
> include/soc/tegra/xusb.h | 7 +
> 3 files changed, 1254 insertions(+), 16 deletions(-)

The devm_phy_create() API has changed (see linux-phy next) and the patch that
modified the existing devm_phy_create() in pinctrl-tegra-xusb.c has also been
merged in linux-phy tree.

Thanks
Kishon
>
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index c6a66de..b2a96f3 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -160,6 +160,7 @@ config PINCTRL_TEGRA124
>
> config PINCTRL_TEGRA_XUSB
> def_bool y if ARCH_TEGRA
> + depends on MAILBOX
> select GENERIC_PHY
> select PINCONF
> select PINMUX
> diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
> index 1631ec9..37ad84e 100644
> --- a/drivers/pinctrl/pinctrl-tegra-xusb.c
> +++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
> @@ -13,23 +13,54 @@
>
> #include <linux/delay.h>
> #include <linux/io.h>
> +#include <linux/mailbox_client.h>
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/phy/phy.h>
> #include <linux/pinctrl/pinctrl.h>
> #include <linux/pinctrl/pinmux.h>
> #include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> +#include <linux/workqueue.h>
> +
> +#include <soc/tegra/fuse.h>
> +#include <soc/tegra/xusb.h>
>
> #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
>
> #include "core.h"
> #include "pinctrl-utils.h"
>
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? 15 : 0)
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT 13
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT 11
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
> +
> +#define XUSB_PADCTL_USB2_PORT_CAP 0x008
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_PORT_CAP_DISABLED 0x0
> +#define XUSB_PADCTL_USB2_PORT_CAP_HOST 0x1
> +#define XUSB_PADCTL_USB2_PORT_CAP_DEVICE 0x2
> +#define XUSB_PADCTL_USB2_PORT_CAP_OTG 0x3
> +
> +#define XUSB_PADCTL_SS_PORT_MAP 0x014
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_SS_PORT_MAP_PORT_MASK 0x7
> +
> #define XUSB_PADCTL_ELPG_PROGRAM 0x01c
> #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
> #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
> #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(x) \
> + (1 << (17 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
>
> #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
> #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
> @@ -41,17 +72,136 @@
> #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
> #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
>
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(x) (0x058 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK 0xffff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0x7
> +
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(x) (0x068 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK 0x7f
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(x) ((x) < 2 ? 0x078 + (x) * 4 : \
> + 0x0f8 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT 28
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK 0x3
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(x) ((x) < 2 ? 0x090 + (x) * 4 : \
> + 0x11c + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN (1 << 8)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(x) ((x) < 2 ? 0x098 + (x) * 4 : \
> + 0x128 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK 0x7f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z 0x21
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP 0x32
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP 0x33
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z 0x48
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z 0xa1
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x0a0 + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 20)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 19)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT 14
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT 6
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK 0x3f
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x0ac + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT 9
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0x7
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
> +
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0b8
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 12)
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 2
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x3
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x0c0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT 12
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT 8
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x0c8 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE (1 << 10)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA (1 << 9)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE (1 << 8)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA (1 << 7)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI (1 << 5)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX (1 << 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX (1 << 3)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX (1 << 2)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN (1 << 0)
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x0d0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x0e0
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK 0x1f
> +
> #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
> #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
> #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK 0x3
> #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
> #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
> #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
>
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13c
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN (1 << 12)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL (1 << 4)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT 0
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK 0x7
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS (1 << 7)
> +
> #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
> #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
> #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
>
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14c
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15c
> +
> struct tegra_xusb_padctl_function {
> const char *name;
> const char * const *groups;
> @@ -72,6 +222,16 @@ struct tegra_xusb_padctl_soc {
>
> const struct tegra_xusb_padctl_lane *lanes;
> unsigned int num_lanes;
> +
> + u32 rx_wander;
> + u32 rx_eq;
> + u32 cdr_cntl;
> + u32 dfe_cntl;
> + u32 hs_slew;
> + u32 ls_rslew[TEGRA_XUSB_UTMI_PHYS];
> + u32 hs_discon_level;
> + u32 spare_in;
> + unsigned int hsic_port_offset;
> };
>
> struct tegra_xusb_padctl_lane {
> @@ -86,6 +246,22 @@ struct tegra_xusb_padctl_lane {
> unsigned int num_funcs;
> };
>
> +struct tegra_xusb_fuse_calibration {
> + u32 hs_curr_level[TEGRA_XUSB_UTMI_PHYS];
> + u32 hs_iref_cap;
> + u32 hs_term_range_adj;
> + u32 hs_squelch_level;
> +};
> +
> +struct tegra_xusb_usb3_port {
> + unsigned int lane;
> + bool context_saved;
> + u32 tap1_val;
> + u32 amp_val;
> + u32 ctle_z_val;
> + u32 ctle_g_val;
> +};
> +
> struct tegra_xusb_padctl {
> struct device *dev;
> void __iomem *regs;
> @@ -93,13 +269,25 @@ struct tegra_xusb_padctl {
> struct reset_control *rst;
>
> const struct tegra_xusb_padctl_soc *soc;
> + struct tegra_xusb_fuse_calibration calib;
> struct pinctrl_dev *pinctrl;
> struct pinctrl_desc desc;
>
> struct phy_provider *provider;
> - struct phy *phys[2];
> + struct phy *phys[TEGRA_XUSB_NUM_PHYS];
>
> unsigned int enable;
> +
> + struct work_struct mbox_req_work;
> + struct tegra_xusb_mbox_msg mbox_req;
> + struct mbox_client mbox_client;
> + struct mbox_chan *mbox_chan;
> +
> + struct tegra_xusb_usb3_port usb3_ports[TEGRA_XUSB_USB3_PHYS];
> + unsigned int utmi_enable;
> + unsigned int hs_curr_level_offset[TEGRA_XUSB_UTMI_PHYS];
> + struct regulator *vbus[TEGRA_XUSB_UTMI_PHYS];
> + struct regulator *vddio_hsic;
> };
>
> static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
> @@ -114,6 +302,53 @@ static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
> return readl(padctl->regs + offset);
> }
>
> +static inline struct tegra_xusb_padctl *
> +mbox_work_to_padctl(struct work_struct *work)
> +{
> + return container_of(work, struct tegra_xusb_padctl, mbox_req_work);
> +}
> +
> +#define PIN_OTG_0 0
> +#define PIN_OTG_1 1
> +#define PIN_OTG_2 2
> +#define PIN_ULPI_0 3
> +#define PIN_HSIC_0 4
> +#define PIN_HSIC_1 5
> +#define PIN_PCIE_0 6
> +#define PIN_PCIE_1 7
> +#define PIN_PCIE_2 8
> +#define PIN_PCIE_3 9
> +#define PIN_PCIE_4 10
> +#define PIN_SATA_0 11
> +
> +static inline bool lane_is_otg(unsigned int lane)
> +{
> + return lane >= PIN_OTG_0 && lane <= PIN_OTG_2;
> +}
> +
> +static inline bool lane_is_hsic(unsigned int lane)
> +{
> + return lane >= PIN_HSIC_0 && lane <= PIN_HSIC_1;
> +}
> +
> +static inline bool lane_is_pcie_or_sata(unsigned int lane)
> +{
> + return lane >= PIN_PCIE_0 && lane <= PIN_SATA_0;
> +}
> +
> +static int lane_to_usb3_port(struct tegra_xusb_padctl *padctl,
> + unsigned int lane)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
> + if (padctl->usb3_ports[i].lane == lane)
> + return i;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int tegra_xusb_padctl_get_groups_count(struct pinctrl_dev *pinctrl)
> {
> struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
> @@ -131,6 +366,17 @@ static const char *tegra_xusb_padctl_get_group_name(struct pinctrl_dev *pinctrl,
>
> enum tegra_xusb_padctl_param {
> TEGRA_XUSB_PADCTL_IDDQ,
> + TEGRA_XUSB_PADCTL_USB3_PORT,
> + TEGRA_XUSB_PADCTL_USB2_PORT,
> + TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM,
> + TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM,
> + TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM,
> + TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN,
> + TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP,
> + TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN,
> + TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP,
> + TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM,
> + TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET,
> };
>
> static const struct tegra_xusb_padctl_property {
> @@ -138,6 +384,18 @@ static const struct tegra_xusb_padctl_property {
> enum tegra_xusb_padctl_param param;
> } properties[] = {
> { "nvidia,iddq", TEGRA_XUSB_PADCTL_IDDQ },
> + { "nvidia,usb3-port", TEGRA_XUSB_PADCTL_USB3_PORT },
> + { "nvidia,usb2-port", TEGRA_XUSB_PADCTL_USB2_PORT },
> + { "nvidia,hsic-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM },
> + { "nvidia,hsic-rx-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM },
> + { "nvidia,hsic-rx-data-trim", TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM },
> + { "nvidia,hsic-tx-rtune-n", TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN },
> + { "nvidia,hsic-tx-rtune-p", TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP },
> + { "nvidia,hsic-tx-rslew-n", TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN },
> + { "nvidia,hsic-tx-rslew-p", TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP },
> + { "nvidia,hsic-auto-term", TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM },
> + { "nvidia,otg-hs-curr-level-offset",
> + TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET },
> };
>
> #define TEGRA_XUSB_PADCTL_PACK(param, value) ((param) << 16 | (value))
> @@ -322,6 +580,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
> const struct tegra_xusb_padctl_lane *lane;
> enum tegra_xusb_padctl_param param;
> u32 value;
> + int port;
>
> param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(*config);
> lane = &padctl->soc->lanes[group];
> @@ -338,8 +597,136 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
> value = 0;
> else
> value = 1;
> + break;
>
> - *config = TEGRA_XUSB_PADCTL_PACK(param, value);
> + case TEGRA_XUSB_PADCTL_USB3_PORT:
> + value = lane_to_usb3_port(padctl, group);
> + if (value < 0) {
> + dev_err(padctl->dev,
> + "Pin %d not mapped to USB3 port\n", group);
> + return -EINVAL;
> + }
> + break;
> +
> + case TEGRA_XUSB_PADCTL_USB2_PORT:
> + port = lane_to_usb3_port(padctl, group);
> + if (port < 0) {
> + dev_err(padctl->dev,
> + "Pin %d not mapped to USB3 port\n", group);
> + return -EINVAL;
> + }
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP) >>
> + XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
> + value &= XUSB_PADCTL_SS_PORT_MAP_PORT_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + value = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
> + value &= XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(port)) >>
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(port)) >>
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> + if (value & XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN)
> + value = 1;
> + else
> + value = 0;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET:
> + if (!lane_is_otg(group)) {
> + dev_err(padctl->dev, "Pin %d is not an OTG pad\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_OTG_0;
> + value = padctl->hs_curr_level_offset[port];
> break;
>
> default:
> @@ -348,6 +735,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
> return -ENOTSUPP;
> }
>
> + *config = TEGRA_XUSB_PADCTL_PACK(param, value);
> return 0;
> }
>
> @@ -362,6 +750,7 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
> unsigned long value;
> unsigned int i;
> u32 regval;
> + int port;
>
> lane = &padctl->soc->lanes[group];
>
> @@ -385,6 +774,206 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
> padctl_writel(padctl, regval, lane->offset);
> break;
>
> + case TEGRA_XUSB_PADCTL_USB3_PORT:
> + if (value >= TEGRA_XUSB_USB3_PHYS) {
> + dev_err(padctl->dev, "Invalid USB3 port: %lu\n",
> + value);
> + return -EINVAL;
> + }
> + if (!lane_is_pcie_or_sata(group)) {
> + dev_err(padctl->dev,
> + "USB3 port not applicable for pin %d\n",
> + group);
> + return -EINVAL;
> + }
> +
> + padctl->usb3_ports[value].lane = group;
> + break;
> +
> + case TEGRA_XUSB_PADCTL_USB2_PORT:
> + if (value >= TEGRA_XUSB_UTMI_PHYS) {
> + dev_err(padctl->dev, "Invalid USB2 port: %lu\n",
> + value);
> + return -EINVAL;
> + }
> + if (!lane_is_pcie_or_sata(group)) {
> + dev_err(padctl->dev,
> + "USB2 port not applicable for pin %d\n",
> + group);
> + return -EINVAL;
> + }
> + port = lane_to_usb3_port(padctl, group);
> + if (port < 0) {
> + dev_err(padctl->dev,
> + "Pin %d not mapped to USB3 port\n",
> + group);
> + return -EINVAL;
> + }
> +
> + regval = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
> + regval &= ~(XUSB_PADCTL_SS_PORT_MAP_PORT_MASK <<
> + XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port));
> + regval |= value <<
> + XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
> + padctl_writel(padctl, regval, XUSB_PADCTL_SS_PORT_MAP);
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + value &= XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK;
> + padctl_writel(padctl, value,
> + XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL2(port));
> + regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT);
> + regval |= value <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL2(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL2(port));
> + regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT);
> + regval |= value <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL2(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT);
> + regval |= value <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT);
> + regval |= value <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT);
> + regval |= value <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT);
> + regval |= value <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL0(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM:
> + if (!lane_is_hsic(group)) {
> + dev_err(padctl->dev, "Pin %d not an HSIC\n",
> + group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_HSIC_0;
> + regval = padctl_readl(padctl,
> + XUSB_PADCTL_HSIC_PADX_CTL1(port));
> + if (!value)
> + regval &= ~XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
> + else
> + regval |= XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
> + padctl_writel(padctl, regval,
> + XUSB_PADCTL_HSIC_PADX_CTL1(port));
> + break;
> +
> + case TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET:
> + if (!lane_is_otg(group)) {
> + dev_err(padctl->dev,
> + "Pin %d is not an OTG pad\n", group);
> + return -EINVAL;
> + }
> +
> + port = group - PIN_OTG_0;
> + value &= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK;
> + padctl->hs_curr_level_offset[port] = value;
> + break;
> +
> default:
> dev_err(padctl->dev,
> "invalid configuration parameter: %04x\n",
> @@ -671,6 +1260,548 @@ static const struct phy_ops sata_phy_ops = {
> .owner = THIS_MODULE,
> };
>
> +static int usb3_phy_to_port(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + unsigned int i;
> +
> + for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
> + if (phy == padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i])
> + return i;
> + }
> + WARN_ON(1);
> +
> + return -EINVAL;
> +}
> +
> +static int usb3_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + int port = usb3_phy_to_port(phy);
> + unsigned int lane;
> + u32 value, offset;
> +
> + if (port < 0)
> + return port;
> +
> + lane = padctl->usb3_ports[port].lane;
> + if (!lane_is_pcie_or_sata(lane)) {
> + dev_err(padctl->dev, "USB3 PHY %d mapped to invalid lane: %d\n",
> + port, lane);
> + return -EINVAL;
> + }
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT));
> + value |= (padctl->soc->rx_wander <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
> + (padctl->soc->cdr_cntl <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) |
> + (padctl->soc->rx_eq <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT);
> + if (padctl->usb3_ports[port].context_saved) {
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
> + value |= (padctl->usb3_ports[port].ctle_g_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (padctl->usb3_ports[port].ctle_z_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
> + }
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> +
> + value = padctl->soc->dfe_cntl;
> + if (padctl->usb3_ports[port].context_saved) {
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
> + value |= (padctl->usb3_ports[port].tap1_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (padctl->usb3_ports[port].amp_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
> + }
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
> +
> + offset = (lane == PIN_SATA_0) ?
> + XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 :
> + XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane - PIN_PCIE_0);
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT);
> + value |= padctl->soc->spare_in <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + offset = (lane == PIN_SATA_0) ?
> + XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 :
> + XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane - PIN_PCIE_0);
> + value = padctl_readl(padctl, offset);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN;
> + padctl_writel(padctl, value, offset);
> +
> + /* Enable SATA PHY when SATA lane is used */
> + if (lane == PIN_SATA_0) {
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value &= ~(XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT);
> + value |= 0x2 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
> + value &= ~((XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
> + (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
> + (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN);
> + value |= (0x7 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
> + (0x8 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
> + (0x8 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
> + }
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + return 0;
> +}
> +
> +static int usb3_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + int port = usb3_phy_to_port(phy);
> + u32 value;
> +
> + if (port < 0)
> + return port;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(250, 350);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + return 0;
> +}
> +
> +static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
> + unsigned int port)
> +{
> + unsigned int lane = padctl->usb3_ports[port].lane;
> + u32 value, offset;
> +
> + if (port >= TEGRA_XUSB_USB3_PHYS)
> + return -EINVAL;
> +
> + padctl->usb3_ports[port].context_saved = true;
> +
> + offset = (lane == PIN_SATA_0) ?
> + XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 :
> + XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane - PIN_PCIE_0);
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + padctl->usb3_ports[port].tap1_val = value &
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + padctl->usb3_ports[port].amp_val = value &
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
> + value |= (padctl->usb3_ports[port].tap1_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (padctl->usb3_ports[port].amp_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + padctl->usb3_ports[port].ctle_g_val = value &
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + padctl->usb3_ports[port].ctle_z_val = value &
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
> + value |= (padctl->usb3_ports[port].ctle_g_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (padctl->usb3_ports[port].ctle_z_val <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> +
> + return 0;
> +}
> +
> +static const struct phy_ops usb3_phy_ops = {
> + .init = tegra_xusb_phy_init,
> + .exit = tegra_xusb_phy_exit,
> + .power_on = usb3_phy_power_on,
> + .power_off = usb3_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static int utmi_phy_to_port(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + unsigned int i;
> +
> + for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
> + if (phy == padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i])
> + return i;
> + }
> + WARN_ON(1);
> +
> + return -EINVAL;
> +}
> +
> +static int utmi_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + int port = utmi_phy_to_port(phy);
> + int err;
> + u32 value;
> +
> + if (port < 0)
> + return port;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> + value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
> + (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT));
> + value |= (padctl->calib.hs_squelch_level <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
> + (padctl->soc->hs_discon_level <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
> + value &= ~(XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK <<
> + XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(port));
> + value |= XUSB_PADCTL_USB2_PORT_CAP_HOST <<
> + XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(port);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port));
> + value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) |
> + (XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT) |
> + (XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT) |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
> + value |= (padctl->calib.hs_curr_level[port] +
> + padctl->hs_curr_level_offset[port]) <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
> + value |= padctl->soc->hs_slew <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT;
> + value |= padctl->soc->ls_rslew[port] <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT;
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port));
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
> + value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
> + (XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT) |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP);
> + value |= (padctl->calib.hs_term_range_adj <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
> + (padctl->calib.hs_iref_cap <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
> +
> + err = regulator_enable(padctl->vbus[port]);
> + if (err)
> + return err;
> +
> + mutex_lock(&padctl->lock);
> +
> + if (padctl->utmi_enable++ > 0)
> + goto out;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> + value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +out:
> + mutex_unlock(&padctl->lock);
> + return 0;
> +}
> +
> +static int utmi_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + int port = utmi_phy_to_port(phy);
> + u32 value;
> +
> + if (port < 0)
> + return port;
> +
> + regulator_disable(padctl->vbus[port]);
> +
> + mutex_lock(&padctl->lock);
> +
> + if (WARN_ON(padctl->utmi_enable == 0))
> + goto out;
> +
> + if (--padctl->utmi_enable > 0)
> + goto out;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> + value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +out:
> + mutex_unlock(&padctl->lock);
> + return 0;
> +}
> +
> +static const struct phy_ops utmi_phy_ops = {
> + .init = tegra_xusb_phy_init,
> + .exit = tegra_xusb_phy_exit,
> + .power_on = utmi_phy_power_on,
> + .power_off = utmi_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static int hsic_phy_to_port(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + unsigned int i;
> +
> + for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
> + if (phy == padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i])
> + return i;
> + }
> + WARN_ON(1);
> +
> + return -EINVAL;
> +}
> +
> +static int hsic_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + int port = hsic_phy_to_port(phy);
> + int err;
> + u32 value;
> +
> + if (port < 0)
> + return port;
> +
> + err = regulator_enable(padctl->vddio_hsic);
> + if (err)
> + return err;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX);
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +
> + return 0;
> +}
> +
> +static int hsic_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> + int port = hsic_phy_to_port(phy);
> + u32 value;
> +
> + if (port < 0)
> + return port;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX;
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +
> + regulator_disable(padctl->vddio_hsic);
> +
> + return 0;
> +}
> +
> +static void hsic_phy_set_idle(struct tegra_xusb_padctl *padctl,
> + unsigned int port, bool idle)
> +{
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> + if (idle)
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
> + else
> + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE);
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +}
> +
> +static const struct phy_ops hsic_phy_ops = {
> + .init = tegra_xusb_phy_init,
> + .exit = tegra_xusb_phy_exit,
> + .power_on = hsic_phy_power_on,
> + .power_off = hsic_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static void tegra_xusb_phy_mbox_work(struct work_struct *work)
> +{
> + struct tegra_xusb_padctl *padctl = mbox_work_to_padctl(work);
> + struct tegra_xusb_mbox_msg *msg = &padctl->mbox_req;
> + struct tegra_xusb_mbox_msg resp;
> + unsigned int i;
> + u32 ports;
> +
> + resp.cmd = 0;
> + switch (msg->cmd) {
> + case MBOX_CMD_SAVE_DFE_CTLE_CTX:
> + resp.data = msg->data;
> + if (usb3_phy_save_context(padctl, msg->data) < 0)
> + resp.cmd = MBOX_CMD_NAK;
> + else
> + resp.cmd = MBOX_CMD_ACK;
> + break;
> + case MBOX_CMD_START_HSIC_IDLE:
> + case MBOX_CMD_STOP_HSIC_IDLE:
> + ports = msg->data >> (padctl->soc->hsic_port_offset + 1);
> + resp.data = msg->data;
> + resp.cmd = MBOX_CMD_ACK;
> + for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
> + if (!(ports & BIT(i)))
> + continue;
> + if (msg->cmd == MBOX_CMD_START_HSIC_IDLE)
> + hsic_phy_set_idle(padctl, i, true);
> + else
> + hsic_phy_set_idle(padctl, i, false);
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (resp.cmd)
> + mbox_send_message(padctl->mbox_chan, &resp);
> +}
> +
> +static bool is_phy_mbox_message(u32 cmd)
> +{
> + switch (cmd) {
> + case MBOX_CMD_SAVE_DFE_CTLE_CTX:
> + case MBOX_CMD_START_HSIC_IDLE:
> + case MBOX_CMD_STOP_HSIC_IDLE:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static void tegra_xusb_phy_mbox_rx(struct mbox_client *cl, void *data)
> +{
> + struct tegra_xusb_padctl *padctl = dev_get_drvdata(cl->dev);
> + struct tegra_xusb_mbox_msg *msg = data;
> +
> + if (is_phy_mbox_message(msg->cmd)) {
> + padctl->mbox_req = *msg;
> + schedule_work(&padctl->mbox_req_work);
> + }
> +}
> +
> static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
> struct of_phandle_args *args)
> {
> @@ -680,25 +1811,12 @@ static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
> if (args->args_count <= 0)
> return ERR_PTR(-EINVAL);
>
> - if (index >= ARRAY_SIZE(padctl->phys))
> + if (index >= ARRAY_SIZE(padctl->phys) && !padctl->phys[index])
> return ERR_PTR(-EINVAL);
>
> return padctl->phys[index];
> }
>
> -#define PIN_OTG_0 0
> -#define PIN_OTG_1 1
> -#define PIN_OTG_2 2
> -#define PIN_ULPI_0 3
> -#define PIN_HSIC_0 4
> -#define PIN_HSIC_1 5
> -#define PIN_PCIE_0 6
> -#define PIN_PCIE_1 7
> -#define PIN_PCIE_2 8
> -#define PIN_PCIE_3 9
> -#define PIN_PCIE_4 10
> -#define PIN_SATA_0 11
> -
> static const struct pinctrl_pin_desc tegra124_pins[] = {
> PINCTRL_PIN(PIN_OTG_0, "otg-0"),
> PINCTRL_PIN(PIN_OTG_1, "otg-1"),
> @@ -856,6 +1974,15 @@ static const struct tegra_xusb_padctl_soc tegra124_soc = {
> .functions = tegra124_functions,
> .num_lanes = ARRAY_SIZE(tegra124_lanes),
> .lanes = tegra124_lanes,
> + .rx_wander = 0xf,
> + .rx_eq = 0xf070,
> + .cdr_cntl = 0x24,
> + .dfe_cntl = 0x002008ee,
> + .hs_slew = 0xe,
> + .ls_rslew = {0x3, 0x0, 0x0},
> + .hs_discon_level = 0x5,
> + .spare_in = 0x1,
> + .hsic_port_offset = 6,
> };
>
> static const struct of_device_id tegra_xusb_padctl_of_match[] = {
> @@ -864,6 +1991,80 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
>
> +static int tegra_xusb_read_fuse_calibration(struct tegra_xusb_padctl *padctl)
> +{
> + unsigned int i;
> + int err;
> + u32 value;
> +
> + err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
> + if (err < 0)
> + return err;
> +
> + for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
> + padctl->calib.hs_curr_level[i] =
> + (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) &
> + FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK;
> + }
> + padctl->calib.hs_iref_cap =
> + (value >> FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT) &
> + FUSE_SKU_CALIB_HS_IREF_CAP_MASK;
> + padctl->calib.hs_term_range_adj =
> + (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) &
> + FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK;
> + padctl->calib.hs_squelch_level =
> + (value >> FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT) &
> + FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK;
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_setup_usb(struct tegra_xusb_padctl *padctl)
> +{
> + struct phy *phy;
> + unsigned int i;
> +
> + for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
> + phy = devm_phy_create(padctl->dev, NULL, &usb3_phy_ops, NULL);
> + if (IS_ERR(phy))
> + return PTR_ERR(phy);
> +
> + padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i] = phy;
> + phy_set_drvdata(phy, padctl);
> + }
> +
> + for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
> + char reg_name[sizeof("vbus-N")];
> +
> + sprintf(reg_name, "vbus-%d", i);
> + padctl->vbus[i] = devm_regulator_get(padctl->dev, reg_name);
> + if (IS_ERR(padctl->vbus[i]))
> + return PTR_ERR(padctl->vbus[i]);
> +
> + phy = devm_phy_create(padctl->dev, NULL, &utmi_phy_ops, NULL);
> + if (IS_ERR(phy))
> + return PTR_ERR(phy);
> +
> + padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i] = phy;
> + phy_set_drvdata(phy, padctl);
> + }
> +
> + padctl->vddio_hsic = devm_regulator_get(padctl->dev, "vddio-hsic");
> + if (IS_ERR(padctl->vddio_hsic))
> + return PTR_ERR(padctl->vddio_hsic);
> +
> + for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
> + phy = devm_phy_create(padctl->dev, NULL, &hsic_phy_ops, NULL);
> + if (IS_ERR(phy))
> + return PTR_ERR(phy);
> +
> + padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i] = phy;
> + phy_set_drvdata(phy, padctl);
> + }
> +
> + return 0;
> +}
> +
> static int tegra_xusb_padctl_probe(struct platform_device *pdev)
> {
> struct tegra_xusb_padctl *padctl;
> @@ -888,6 +2089,10 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
> if (IS_ERR(padctl->regs))
> return PTR_ERR(padctl->regs);
>
> + err = tegra_xusb_read_fuse_calibration(padctl);
> + if (err < 0)
> + return err;
> +
> padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
> if (IS_ERR(padctl->rst))
> return PTR_ERR(padctl->rst);
> @@ -928,6 +2133,26 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
> padctl->phys[TEGRA_XUSB_PADCTL_SATA] = phy;
> phy_set_drvdata(phy, padctl);
>
> + INIT_WORK(&padctl->mbox_req_work, tegra_xusb_phy_mbox_work);
> + padctl->mbox_client.dev = &pdev->dev;
> + padctl->mbox_client.tx_block = true;
> + padctl->mbox_client.tx_tout = 0;
> + padctl->mbox_client.rx_callback = tegra_xusb_phy_mbox_rx;
> + padctl->mbox_chan = mbox_request_channel(&padctl->mbox_client, 0);
> + if (IS_ERR(padctl->mbox_chan)) {
> + err = PTR_ERR(padctl->mbox_chan);
> + if (err == -EPROBE_DEFER) {
> + goto unregister;
> + } else {
> + dev_warn(&pdev->dev,
> + "failed to get mailbox, USB support disabled");
> + }
> + } else {
> + err = tegra_xusb_setup_usb(padctl);
> + if (err)
> + goto unregister;
> + }
> +
> padctl->provider = devm_of_phy_provider_register(&pdev->dev,
> tegra_xusb_padctl_xlate);
> if (IS_ERR(padctl->provider)) {
> @@ -950,6 +2175,11 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
> struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
> int err;
>
> + if (!IS_ERR(padctl->mbox_chan)) {
> + cancel_work_sync(&padctl->mbox_req_work);
> + mbox_free_channel(padctl->mbox_chan);
> + }
> +
> pinctrl_unregister(padctl->pinctrl);
>
> err = reset_control_assert(padctl->rst);
> diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h
> index 5ce5e12..0136dc1 100644
> --- a/include/soc/tegra/xusb.h
> +++ b/include/soc/tegra/xusb.h
> @@ -10,6 +10,13 @@
> #ifndef __SOC_TEGRA_XUSB_H__
> #define __SOC_TEGRA_XUSB_H__
>
> +#define TEGRA_XUSB_USB3_PHYS 2
> +#define TEGRA_XUSB_UTMI_PHYS 3
> +#define TEGRA_XUSB_HSIC_PHYS 2
> +#define TEGRA_XUSB_NUM_USB_PHYS (TEGRA_XUSB_USB3_PHYS + TEGRA_XUSB_UTMI_PHYS + \
> + TEGRA_XUSB_HSIC_PHYS)
> +#define TEGRA_XUSB_NUM_PHYS (TEGRA_XUSB_NUM_USB_PHYS + 2) /* + SATA & PCIe */
> +
> /* Command requests from the firmware */
> enum tegra_xusb_mbox_cmd {
> MBOX_CMD_MSG_ENABLED = 1,
>

2014-11-26 19:31:21

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH V6 00/12] Tegra xHCI support

On Tue, Nov 25, 2014 at 5:32 AM, Jassi Brar <[email protected]> wrote:
> On 25 November 2014 at 05:47, Andrew Bresticker <[email protected]> wrote:
>> This series adds support for xHCI on NVIDIA Tegra SoCs. This includes:
>> - patches 1, 2, and 3: minor cleanups for mailbox framework and xHCI,
>> - patches 4 and 5: adding a driver for the mailbox used to communicate
>> with the xHCI controller's firmware,
>> - patches 6 and 7: extending the XUSB pad controller driver to support
>> the USB PHY types (UTMI, HSIC, and USB3),
>> - patches 8 and 9: adding a xHCI host-controller driver, and
>> - patches 10, 11, and 12: updating the relevant DT files.
>>
>> The mailbox driver (patch 5) has a compile-time dependency on patch 2 and
>> a run-time dependency on patch 3. Both the PHY (patch 7) and host (patch 9)
>> drivers have compile-time dependencies on the mailbox driver. The host
>> driver also has a run-time dependency on patch 1. Because of this, this
>> entire series should probably go through the Tegra tree.
>>
> Why shouldn't I pick 2 & 3 at least?

I don't see why not. Because of the PHY API change I'm going to have
to re-spin the series and at this point 3.19 is looking pretty
unlikely. Maybe we could get a Tegra maintainer's ACK for patches 4
and 5 so that you could take them through your tree as well for 3.19?
(Stephen, Thierry, Alex?)

2014-11-26 19:42:02

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH V6 07/12] pinctrl: tegra-xusb: Add USB PHY support

On Tue, Nov 25, 2014 at 5:49 AM, Kishon Vijay Abraham I <[email protected]> wrote:
> Hi,
>
> On Tuesday 25 November 2014 05:47 AM, Andrew Bresticker wrote:
>> In addition to the PCIe and SATA PHYs, the XUSB pad controller also
>> supports 3 UTMI, 2 HSIC, and 2 USB3 PHYs. Each USB3 PHY uses a single
>> PCIe or SATA lane and is mapped to one of the three UTMI ports.
>>
>> The xHCI controller will also send messages intended for the PHY driver,
>> so request and listen for messages on the mailbox's PHY channel.
>>
>> Signed-off-by: Andrew Bresticker <[email protected]>
>> Acked-by: Linus Walleij <[email protected]>
>> Reviewed-by: Stephen Warren <[email protected]>
>> ---
>> No changes from v5.
>> Changes from v4:
>> - Disabled USB support on missing mailbox channel instead of failing
>> to probe.
>> - Made usb3-port a pinconfig property.
>> - Addressed review comments from Thierry.
>> No changes from v3.
>> Changes from v2:
>> - Added support for nvidia,otg-hs-curr-level-offset property.
>> - Moved mailbox request handling to workqueue.
>> - Added filtering out of non-PHY mailbox messages.
>> - Dropped "-otg" from VBUS supplies.
>> Changes from v1:
>> - Updated to use common mailbox API.
>> - Added SATA PHY enable sequence for USB3 ports using the SATA lane.
>> - Made USB3 port-to-lane mappins a top-level binding rather than a pinconfig
>> binding.
>> ---
>> drivers/pinctrl/Kconfig | 1 +
>> drivers/pinctrl/pinctrl-tegra-xusb.c | 1262 +++++++++++++++++++++++++++++++++-
>> include/soc/tegra/xusb.h | 7 +
>> 3 files changed, 1254 insertions(+), 16 deletions(-)
>
> The devm_phy_create() API has changed (see linux-phy next) and the patch that
> modified the existing devm_phy_create() in pinctrl-tegra-xusb.c has also been
> merged in linux-phy tree.

Ok, I'll rebase on top of that.

2014-12-02 09:47:50

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH V6 05/12] mailbox: Add NVIDIA Tegra XUSB mailbox driver

On Mon, Nov 24, 2014 at 04:17:17PM -0800, Andrew Bresticker wrote:
> The Tegra xHCI controller's firmware communicates requests to the host
> processor through a mailbox interface. While there is only a single
> physical channel, messages sent by the controller can be divided
> into two groups: those intended for the PHY driver and those intended
> for the host-controller driver. The requesting driver is assigned
> one of two virtual channels when the single physical channel is
> requested. All incoming messages are sent to both virtual channels.
>
> Signed-off-by: Andrew Bresticker <[email protected]>
> Reviewed-by: Stephen Warren <[email protected]>
> ---
> Thierry,
>
> I've left this as a separate driver because I don't think it should be
> combined with the xHCI host. Stephen and I discussed this earlier in
> review of v1 of this series and he agreed that the mailbox belongs in
> its own DT node and driver.

My objection wasn't strictly against merging the drivers. What I don't
want to see is two devices sharing the same memory-mapped regions in DT.

That's based on earlier discussion with Stephen as well, because we have
had the same problem with USB and USB-PHY before. Unless I completely
misinterpreted we came to the conclusion that we should avoid this in
the future.

Like I said, this doesn't mean that both need to be in the same driver.
We could use MFD for example, or provide an entry point in the mailbox
driver that can be called from the XUSB driver to instantiate the
mailbox (which is really the same as MFD).

Thierry


Attachments:
(No filename) (1.53 kB)
(No filename) (819.00 B)
Download all attachments

2014-12-02 19:06:22

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH V6 05/12] mailbox: Add NVIDIA Tegra XUSB mailbox driver

On Tue, Dec 2, 2014 at 1:47 AM, Thierry Reding <[email protected]> wrote:
> On Mon, Nov 24, 2014 at 04:17:17PM -0800, Andrew Bresticker wrote:
>> The Tegra xHCI controller's firmware communicates requests to the host
>> processor through a mailbox interface. While there is only a single
>> physical channel, messages sent by the controller can be divided
>> into two groups: those intended for the PHY driver and those intended
>> for the host-controller driver. The requesting driver is assigned
>> one of two virtual channels when the single physical channel is
>> requested. All incoming messages are sent to both virtual channels.
>>
>> Signed-off-by: Andrew Bresticker <[email protected]>
>> Reviewed-by: Stephen Warren <[email protected]>
>> ---
>> Thierry,
>>
>> I've left this as a separate driver because I don't think it should be
>> combined with the xHCI host. Stephen and I discussed this earlier in
>> review of v1 of this series and he agreed that the mailbox belongs in
>> its own DT node and driver.
>
> My objection wasn't strictly against merging the drivers. What I don't
> want to see is two devices sharing the same memory-mapped regions in DT.
>
> That's based on earlier discussion with Stephen as well, because we have
> had the same problem with USB and USB-PHY before. Unless I completely
> misinterpreted we came to the conclusion that we should avoid this in
> the future.

Yes, I understand your objection to having multiple drivers map the
same IO memory, however Stephen and I discussed this before [1] and
came to the conclusion that, although mapping the MMIO region in both
drivers was non-ideal, the alternatives had their own drawbacks as
well and that the approach I had taken was fine.

> Like I said, this doesn't mean that both need to be in the same driver.
> We could use MFD for example, or provide an entry point in the mailbox
> driver that can be called from the XUSB driver to instantiate the
> mailbox (which is really the same as MFD).

This does not look like the typical MFD, but I would agree that it's
the only reasonable alternative. If Lee is fine with it, then I
suppose I could go the MFD route... Lee: for context, the mailbox
registers live in one of the host-controller's MMIO regions - there
are no other shared resources. This has been discussed, along with a
possible binding, in [1] as well.

[1] https://lkml.org/lkml/2014/8/27/727

2015-02-25 16:01:22

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH V6 00/12] Tegra xHCI support

diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
index 4af09d6235c1..9ca9ca5f85c6 100644
--- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
@@ -29,10 +29,26 @@ Required properties:
- xusb

Optional properties:
--------------------
-- vbus-{0,1,2}-supply: VBUS regulator for the corresponding UTMI pad.
+--------------------
- vddio-hsic-supply: VDDIO regulator for the HSIC pads.

+PHY nodes:
+----------
+
+An optional child node named "phys" can contain nodes describing additional
+properties of each PHY. Only USB3 and UTMI PHYs can be complemented in this
+way, in which case the name of each node must match one of the following:
+
+ usb3-0, usb3-1, utmi-0, utmi-1, utmi-2
+
+Required properties for USB3 PHYs:
+- nvidia,lanes: specifies the name of the lane that this USB3 PHY uses
+- nvidia,port: specifies the number of the USB2 port that is used for this
+ USB3 PHY
+
+Optional properties for UTMI PHYs:
+- vbus-supply: regulator providing the VBUS voltage for the UTMI pad
+
Lane muxing:
------------

@@ -98,9 +114,7 @@ divided into four groups:

Valid functions for this group are: "pcie", "usb3", "sata", "rsvd".

- Only the nvidia,iddq, nvidia,usb2-port, and nvidia,usb3-port properties
- apply. The nvidia,usb2-port and nvidia,usb3-port properties are required
- when the function is usb3.
+ Only the nvidia,iddq property applies.

Example:
========
@@ -148,7 +162,24 @@ Board file extract:
pinctrl-0 = <&padctl_default>;
pinctrl-names = "default";

- vbus-2-supply = <&vdd_usb3_vbus>;
+ phys {
+ usb3-0 {
+ status = "okay";
+
+ nvidia,lanes = "pcie-0";
+ nvidia,port = <2>;
+ };
+
+ utmi-1 {
+ status = "okay";
+ };
+
+ utmi-2 {
+ status = "okay";
+
+ vbus-supply = <&vdd_usb3_vbus>;
+ };
+ };

padctl_default: pinmux {
otg {
@@ -160,8 +191,6 @@ Board file extract:
nvidia,lanes = "pcie-0";
nvidia,function = "usb3";
nvidia,iddq = <0>;
- nvidia,usb2-port = <2>;
- nvidia,usb3-port = <0>;
};

pcie {
diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
index 526826b790f8..bd7af1073d4c 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
@@ -1724,7 +1724,24 @@
pinctrl-0 = <&padctl_default>;
pinctrl-names = "default";

- vbus-2-supply = <&vdd_usb3_vbus>;
+ phys {
+ usb3-0 {
+ status = "okay";
+
+ nvidia,lanes = "pcie-0";
+ nvidia,port = <2>;
+ };
+
+ utmi-1 {
+ status = "okay";
+ };
+
+ utmi-2 {
+ status = "okay";
+
+ vbus-supply = <&vdd_usb3_vbus>;
+ };
+ };

padctl_default: pinmux {
otg {
@@ -1736,8 +1753,6 @@
nvidia,lanes = "pcie-0";
nvidia,function = "usb3";
nvidia,iddq = <0>;
- nvidia,usb2-port = <2>;
- nvidia,usb3-port = <0>;
};

pcie {
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index c16c5e932a4c..31c3d0ee6305 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -685,6 +685,28 @@
mbox-names = "xusb";

#phy-cells = <1>;
+
+ phys {
+ usb3-0 {
+ status = "disabled";
+ };
+
+ usb3-1 {
+ status = "disabled";
+ };
+
+ utmi-0 {
+ status = "disabled";
+ };
+
+ utmi-1 {
+ status = "disabled";
+ };
+
+ utmi-2 {
+ status = "disabled";
+ };
+ };
};

sdhci@0,700b0000 {
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
index cfda6ad6457f..2c1ec538402d 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -254,13 +254,25 @@ struct tegra_xusb_fuse_calibration {
u32 hs_squelch_level;
};

-struct tegra_xusb_usb3_port {
- unsigned int lane;
+struct tegra_xusb_usb3_phy {
+ struct tegra_xusb_padctl *padctl;
bool context_saved;
- u32 tap1_val;
- u32 amp_val;
- u32 ctle_z_val;
- u32 ctle_g_val;
+ unsigned int index;
+ unsigned int lane;
+ unsigned int port;
+
+ u32 tap1;
+ u32 amp;
+ u32 ctle_z;
+ u32 ctle_g;
+};
+
+struct tegra_xusb_utmi_phy {
+ struct tegra_xusb_padctl *padctl;
+ unsigned int index;
+
+ unsigned int hs_curr_level_offset;
+ struct regulator *supply;
};

struct tegra_xusb_padctl {
@@ -284,10 +296,7 @@ struct tegra_xusb_padctl {
struct mbox_client mbox_client;
struct mbox_chan *mbox_chan;

- struct tegra_xusb_usb3_port usb3_ports[TEGRA_XUSB_USB3_PHYS];
unsigned int utmi_enable;
- unsigned int hs_curr_level_offset[TEGRA_XUSB_UTMI_PHYS];
- struct regulator *vbus[TEGRA_XUSB_UTMI_PHYS];
struct regulator *vddio_hsic;
};

@@ -337,19 +346,6 @@ static inline bool lane_is_pcie_or_sata(unsigned int lane)
return lane >= PIN_PCIE_0 && lane <= PIN_SATA_0;
}

-static int lane_to_usb3_port(struct tegra_xusb_padctl *padctl,
- unsigned int lane)
-{
- unsigned int i;
-
- for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
- if (padctl->usb3_ports[i].lane == lane)
- return i;
- }
-
- return -EINVAL;
-}
-
static int tegra_xusb_padctl_get_groups_count(struct pinctrl_dev *pinctrl)
{
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
@@ -367,8 +363,6 @@ static const char *tegra_xusb_padctl_get_group_name(struct pinctrl_dev *pinctrl,

enum tegra_xusb_padctl_param {
TEGRA_XUSB_PADCTL_IDDQ,
- TEGRA_XUSB_PADCTL_USB3_PORT,
- TEGRA_XUSB_PADCTL_USB2_PORT,
TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM,
TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM,
TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM,
@@ -385,8 +379,6 @@ static const struct tegra_xusb_padctl_property {
enum tegra_xusb_padctl_param param;
} properties[] = {
{ "nvidia,iddq", TEGRA_XUSB_PADCTL_IDDQ },
- { "nvidia,usb3-port", TEGRA_XUSB_PADCTL_USB3_PORT },
- { "nvidia,usb2-port", TEGRA_XUSB_PADCTL_USB2_PORT },
{ "nvidia,hsic-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM },
{ "nvidia,hsic-rx-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM },
{ "nvidia,hsic-rx-data-trim", TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM },
@@ -604,28 +596,6 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
value = 1;
break;

- case TEGRA_XUSB_PADCTL_USB3_PORT:
- value = lane_to_usb3_port(padctl, group);
- if (value < 0) {
- dev_err(padctl->dev,
- "Pin %d not mapped to USB3 port\n", group);
- return -EINVAL;
- }
- break;
-
- case TEGRA_XUSB_PADCTL_USB2_PORT:
- port = lane_to_usb3_port(padctl, group);
- if (port < 0) {
- dev_err(padctl->dev,
- "Pin %d not mapped to USB3 port\n", group);
- return -EINVAL;
- }
-
- value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP) >>
- XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
- value &= XUSB_PADCTL_SS_PORT_MAP_PORT_MASK;
- break;
-
case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
if (!lane_is_hsic(group)) {
dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
@@ -728,10 +698,15 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
dev_err(padctl->dev, "Pin %d is not an OTG pad\n",
group);
return -EINVAL;
- }
+ } else {
+ unsigned int index = group - PIN_OTG_0;
+ struct tegra_xusb_utmi_phy *utmi;
+ struct phy *phy;

- port = group - PIN_OTG_0;
- value = padctl->hs_curr_level_offset[port];
+ phy = padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + index];
+ utmi = phy_get_drvdata(phy);
+ value = utmi->hs_curr_level_offset;
+ }
break;

default:
@@ -779,50 +754,6 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
padctl_writel(padctl, regval, lane->offset);
break;

- case TEGRA_XUSB_PADCTL_USB3_PORT:
- if (value >= TEGRA_XUSB_USB3_PHYS) {
- dev_err(padctl->dev, "Invalid USB3 port: %lu\n",
- value);
- return -EINVAL;
- }
- if (!lane_is_pcie_or_sata(group)) {
- dev_err(padctl->dev,
- "USB3 port not applicable for pin %d\n",
- group);
- return -EINVAL;
- }
-
- padctl->usb3_ports[value].lane = group;
- break;
-
- case TEGRA_XUSB_PADCTL_USB2_PORT:
- if (value >= TEGRA_XUSB_UTMI_PHYS) {
- dev_err(padctl->dev, "Invalid USB2 port: %lu\n",
- value);
- return -EINVAL;
- }
- if (!lane_is_pcie_or_sata(group)) {
- dev_err(padctl->dev,
- "USB2 port not applicable for pin %d\n",
- group);
- return -EINVAL;
- }
- port = lane_to_usb3_port(padctl, group);
- if (port < 0) {
- dev_err(padctl->dev,
- "Pin %d not mapped to USB3 port\n",
- group);
- return -EINVAL;
- }
-
- regval = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
- regval &= ~(XUSB_PADCTL_SS_PORT_MAP_PORT_MASK <<
- XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port));
- regval |= value <<
- XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
- padctl_writel(padctl, regval, XUSB_PADCTL_SS_PORT_MAP);
- break;
-
case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
if (!lane_is_hsic(group)) {
dev_err(padctl->dev, "Pin %d not an HSIC\n",
@@ -972,11 +903,17 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
dev_err(padctl->dev,
"Pin %d is not an OTG pad\n", group);
return -EINVAL;
- }
+ } else {
+ unsigned int index = group - PIN_OTG_0;
+ struct tegra_xusb_utmi_phy *utmi;
+ struct phy *phy;

- port = group - PIN_OTG_0;
- value &= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK;
- padctl->hs_curr_level_offset[port] = value;
+ phy = padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + index];
+ utmi = phy_get_drvdata(phy);
+
+ value &= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK;
+ utmi->hs_curr_level_offset = value;
+ }
break;

default:
@@ -1265,36 +1202,46 @@ static const struct phy_ops sata_phy_ops = {
.owner = THIS_MODULE,
};

-static int usb3_phy_to_port(struct phy *phy)
+static int usb3_phy_init(struct phy *phy)
{
- struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
- unsigned int i;
+ struct tegra_xusb_usb3_phy *usb = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = usb->padctl;
+ u32 value;
+ int err;

- for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
- if (phy == padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i])
- return i;
- }
- WARN_ON(1);
+ err = tegra_xusb_padctl_enable(padctl);
+ if (err < 0)
+ return err;

- return -EINVAL;
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ value &= ~(XUSB_PADCTL_SS_PORT_MAP_PORT_MASK <<
+ XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(usb->index));
+ value |= usb->port <<
+ XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(usb->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+ return 0;
}

-static int usb3_phy_power_on(struct phy *phy)
+static int usb3_phy_exit(struct phy *phy)
{
- struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
- int port = usb3_phy_to_port(phy);
- unsigned int lane;
- u32 value, offset;
+ struct tegra_xusb_usb3_phy *usb = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = usb->padctl;
+ u32 value;

- if (port < 0)
- return port;
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ value |= 0x7 << XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(usb->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);

- lane = padctl->usb3_ports[port].lane;
- if (!lane_is_pcie_or_sata(lane)) {
- dev_err(padctl->dev, "USB3 PHY %d mapped to invalid lane: %d\n",
- port, lane);
- return -EINVAL;
- }
+ return tegra_xusb_padctl_disable(padctl);
+}
+
+static int usb3_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_usb3_phy *usb = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = usb->padctl;
+ unsigned int port = usb->index;
+ u32 value, offset;

value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK <<
@@ -1309,34 +1256,34 @@ static int usb3_phy_power_on(struct phy *phy)
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) |
(padctl->soc->rx_eq <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT);
- if (padctl->usb3_ports[port].context_saved) {
+ if (usb->context_saved) {
value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
(XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
- value |= (padctl->usb3_ports[port].ctle_g_val <<
+ value |= (usb->ctle_g <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
- (padctl->usb3_ports[port].ctle_z_val <<
+ (usb->ctle_z <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
}
padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));

value = padctl->soc->dfe_cntl;
- if (padctl->usb3_ports[port].context_saved) {
+ if (usb->context_saved) {
value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
(XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
- value |= (padctl->usb3_ports[port].tap1_val <<
+ value |= (usb->tap1 <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
- (padctl->usb3_ports[port].amp_val <<
+ (usb->amp <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
}
padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));

- offset = (lane == PIN_SATA_0) ?
+ offset = (usb->lane == PIN_SATA_0) ?
XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 :
- XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane - PIN_PCIE_0);
+ XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(usb->lane - PIN_PCIE_0);
value = padctl_readl(padctl, offset);
value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK <<
XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT);
@@ -1344,15 +1291,15 @@ static int usb3_phy_power_on(struct phy *phy)
XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT;
padctl_writel(padctl, value, offset);

- offset = (lane == PIN_SATA_0) ?
+ offset = (usb->lane == PIN_SATA_0) ?
XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 :
- XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane - PIN_PCIE_0);
+ XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(usb->lane - PIN_PCIE_0);
value = padctl_readl(padctl, offset);
value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN;
padctl_writel(padctl, value, offset);

/* Enable SATA PHY when SATA lane is used */
- if (lane == PIN_SATA_0) {
+ if (usb->lane == PIN_SATA_0) {
value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
value &= ~(XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK <<
XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT);
@@ -1401,13 +1348,11 @@ static int usb3_phy_power_on(struct phy *phy)

static int usb3_phy_power_off(struct phy *phy)
{
- struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
- int port = usb3_phy_to_port(phy);
+ struct tegra_xusb_usb3_phy *usb = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = usb->padctl;
+ unsigned int port = usb->index;
u32 value;

- if (port < 0)
- return port;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
@@ -1427,20 +1372,21 @@ static int usb3_phy_power_off(struct phy *phy)
return 0;
}

-static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
- unsigned int port)
+static int tegra_xusb_usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
+ unsigned int port)
{
- unsigned int lane = padctl->usb3_ports[port].lane;
+ struct tegra_xusb_usb3_phy *usb;
u32 value, offset;

if (port >= TEGRA_XUSB_USB3_PHYS)
return -EINVAL;

- padctl->usb3_ports[port].context_saved = true;
+ usb = phy_get_drvdata(padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + port]);
+ usb->context_saved = true;

- offset = (lane == PIN_SATA_0) ?
+ offset = (usb->lane == PIN_SATA_0) ?
XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 :
- XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane - PIN_PCIE_0);
+ XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(usb->lane - PIN_PCIE_0);

value = padctl_readl(padctl, offset);
value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
@@ -1451,8 +1397,7 @@ static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,

value = padctl_readl(padctl, offset) >>
XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
- padctl->usb3_ports[port].tap1_val = value &
- XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;
+ usb->tap1 = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;

value = padctl_readl(padctl, offset);
value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
@@ -1463,17 +1408,16 @@ static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,

value = padctl_readl(padctl, offset) >>
XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
- padctl->usb3_ports[port].amp_val = value &
- XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;
+ usb->amp = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;

value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
(XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
- value |= (padctl->usb3_ports[port].tap1_val <<
+ value |= (usb->tap1 <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
- (padctl->usb3_ports[port].amp_val <<
+ (usb->amp <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));

@@ -1493,7 +1437,7 @@ static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,

value = padctl_readl(padctl, offset) >>
XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
- padctl->usb3_ports[port].ctle_g_val = value &
+ usb->ctle_g = value &
XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;

value = padctl_readl(padctl, offset);
@@ -1505,7 +1449,7 @@ static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,

value = padctl_readl(padctl, offset) >>
XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
- padctl->usb3_ports[port].ctle_z_val = value &
+ usb->ctle_z = value &
XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;

value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
@@ -1513,9 +1457,9 @@ static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
(XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
- value |= (padctl->usb3_ports[port].ctle_g_val <<
+ value |= (usb->ctle_g <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
- (padctl->usb3_ports[port].ctle_z_val <<
+ (usb->ctle_z <<
XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));

@@ -1523,36 +1467,34 @@ static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
}

static const struct phy_ops usb3_phy_ops = {
- .init = tegra_xusb_phy_init,
- .exit = tegra_xusb_phy_exit,
+ .init = usb3_phy_init,
+ .exit = usb3_phy_exit,
.power_on = usb3_phy_power_on,
.power_off = usb3_phy_power_off,
.owner = THIS_MODULE,
};

-static int utmi_phy_to_port(struct phy *phy)
+static int utmi_phy_init(struct phy *phy)
{
- struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
- unsigned int i;
+ struct tegra_xusb_utmi_phy *utmi = phy_get_drvdata(phy);

- for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
- if (phy == padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i])
- return i;
- }
- WARN_ON(1);
+ return tegra_xusb_padctl_enable(utmi->padctl);
+}

- return -EINVAL;
+static int utmi_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_utmi_phy *utmi = phy_get_drvdata(phy);
+
+ return tegra_xusb_padctl_disable(utmi->padctl);
}

static int utmi_phy_power_on(struct phy *phy)
{
- struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
- int port = utmi_phy_to_port(phy);
- int err;
+ struct tegra_xusb_utmi_phy *utmi = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = utmi->padctl;
+ unsigned int port = utmi->index;
u32 value;
-
- if (port < 0)
- return port;
+ int err;

value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
@@ -1583,7 +1525,7 @@ static int utmi_phy_power_on(struct phy *phy)
XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
value |= (padctl->calib.hs_curr_level[port] +
- padctl->hs_curr_level_offset[port]) <<
+ utmi->hs_curr_level_offset) <<
XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
value |= padctl->soc->hs_slew <<
XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT;
@@ -1605,7 +1547,7 @@ static int utmi_phy_power_on(struct phy *phy)
XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT);
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));

- err = regulator_enable(padctl->vbus[port]);
+ err = regulator_enable(utmi->supply);
if (err)
return err;

@@ -1625,15 +1567,10 @@ out:

static int utmi_phy_power_off(struct phy *phy)
{
- struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
- int port = utmi_phy_to_port(phy);
+ struct tegra_xusb_utmi_phy *utmi = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = utmi->padctl;
u32 value;

- if (port < 0)
- return port;
-
- regulator_disable(padctl->vbus[port]);
-
mutex_lock(&padctl->lock);

if (WARN_ON(padctl->utmi_enable == 0))
@@ -1647,13 +1584,14 @@ static int utmi_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);

out:
+ regulator_disable(utmi->supply);
mutex_unlock(&padctl->lock);
return 0;
}

static const struct phy_ops utmi_phy_ops = {
- .init = tegra_xusb_phy_init,
- .exit = tegra_xusb_phy_exit,
+ .init = utmi_phy_init,
+ .exit = utmi_phy_exit,
.power_on = utmi_phy_power_on,
.power_off = utmi_phy_power_off,
.owner = THIS_MODULE,
@@ -1757,7 +1695,7 @@ static void tegra_xusb_phy_mbox_work(struct work_struct *work)
switch (msg->cmd) {
case MBOX_CMD_SAVE_DFE_CTLE_CTX:
resp.data = msg->data;
- if (usb3_phy_save_context(padctl, msg->data) < 0)
+ if (tegra_xusb_usb3_phy_save_context(padctl, msg->data) < 0)
resp.cmd = MBOX_CMD_NAK;
else
resp.cmd = MBOX_CMD_ACK;
@@ -2027,34 +1965,189 @@ static int tegra_xusb_read_fuse_calibration(struct tegra_xusb_padctl *padctl)
return 0;
}

+static int tegra_xusb_padctl_find_pin_by_name(struct tegra_xusb_padctl *padctl,
+ const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < padctl->soc->num_pins; i++) {
+ const struct pinctrl_pin_desc *pin = &padctl->soc->pins[i];
+
+ if (strcmp(pin->name, name) == 0)
+ return pin->number;
+ }
+
+ return -ENODEV;
+}
+
+static struct device_node *
+tegra_xusb_padctl_find_phy_node(struct tegra_xusb_padctl *padctl,
+ const char *type, unsigned int index)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_name(padctl->dev->of_node, "phys");
+ if (np) {
+ struct device_node *of_node;
+ char *name;
+
+ name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
+ of_node = of_find_node_by_name(np, name);
+ kfree(name);
+
+ of_node_put(np);
+ np = of_node;
+ }
+
+ return np;
+}
+
+static int tegra_xusb_usb3_phy_parse_dt(struct phy *phy)
+{
+ struct tegra_xusb_usb3_phy *usb = phy_get_drvdata(phy);
+ struct device_node *np = phy->dev.of_node;
+ const char *lane = NULL;
+ u32 value;
+ int err;
+
+ if (!np)
+ return 0;
+
+ /* only a single lane can be mapped to each USB3 port */
+ err = of_property_count_strings(np, "nvidia,lanes");
+ if (err < 0 && err != -EINVAL) {
+ dev_err(&phy->dev, "failed to get number of lanes: %d\n", err);
+ return err;
+ }
+
+ if (err > 1)
+ dev_warn(&phy->dev, "found %d lanes, expected 1\n", err);
+
+ err = of_property_read_string(np, "nvidia,lanes", &lane);
+ if (err < 0) {
+ dev_err(&phy->dev, "failed to read lanes: %d\n", err);
+ return err;
+ }
+
+ if (lane) {
+ err = tegra_xusb_padctl_find_pin_by_name(usb->padctl, lane);
+ if (err < 0) {
+ dev_err(&phy->dev, "unknown pin: %s\n", lane);
+ return err;
+ }
+
+ if (!lane_is_pcie_or_sata(err)) {
+ dev_err(&phy->dev,
+ "USB3 PHY %u mapped to invalid lane %s\n",
+ usb->index, lane);
+ return -EINVAL;
+ }
+
+ usb->lane = err;
+ }
+
+ err = of_property_read_u32_index(np, "nvidia,port", 0, &value);
+ if (err < 0) {
+ dev_err(&phy->dev, "failed to read port: %d\n", err);
+ return err;
+ }
+
+ usb->port = value;
+
+ return 0;
+}
+
+static struct phy *tegra_xusb_usb3_phy_create(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_usb3_phy *usb;
+ struct device_node *np;
+ struct phy *phy;
+ int err;
+
+ /*
+ * If there is no supplemental configuration in the device tree the
+ * PHY is unusable. But it is valid to configure only a single PHY,
+ * hence return NULL instead of an error to mark the PHY as not in
+ * use. Similarly if the PHY is marked as disabled, don't register
+ * it.
+ */
+ np = tegra_xusb_padctl_find_phy_node(padctl, "usb3", index);
+ if (!np || !of_device_is_available(np))
+ return NULL;
+
+ phy = devm_phy_create(padctl->dev, np, &usb3_phy_ops);
+ if (IS_ERR(phy))
+ return phy;
+
+ usb = devm_kzalloc(&phy->dev, sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ phy_set_drvdata(phy, usb);
+ usb->padctl = padctl;
+ usb->index = index;
+
+ err = tegra_xusb_usb3_phy_parse_dt(phy);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return phy;
+}
+
+static struct phy *tegra_xusb_utmi_phy_create(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_utmi_phy *utmi;
+ struct device_node *np;
+ struct phy *phy;
+
+ /*
+ * UTMI PHYs don't require additional properties, but if the PHY is
+ * marked as disabled there is no reason to register it.
+ */
+ np = tegra_xusb_padctl_find_phy_node(padctl, "utmi", index);
+ if (np && !of_device_is_available(np))
+ return NULL;
+
+ phy = devm_phy_create(padctl->dev, np, &utmi_phy_ops);
+ if (IS_ERR(phy))
+ return ERR_CAST(phy);
+
+ utmi = devm_kzalloc(&phy->dev, sizeof(*utmi), GFP_KERNEL);
+ if (!utmi)
+ return ERR_PTR(-ENOMEM);
+
+ phy_set_drvdata(phy, utmi);
+ utmi->padctl = padctl;
+ utmi->index = index;
+
+ utmi->supply = devm_regulator_get(&phy->dev, "vbus");
+ if (IS_ERR(utmi->supply))
+ return ERR_CAST(utmi->supply);
+
+ return phy;
+}
+
static int tegra_xusb_setup_usb(struct tegra_xusb_padctl *padctl)
{
struct phy *phy;
unsigned int i;

for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
- phy = devm_phy_create(padctl->dev, NULL, &usb3_phy_ops);
+ phy = tegra_xusb_usb3_phy_create(padctl, i);
if (IS_ERR(phy))
return PTR_ERR(phy);

padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i] = phy;
- phy_set_drvdata(phy, padctl);
}

for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
- char reg_name[sizeof("vbus-N")];
-
- sprintf(reg_name, "vbus-%d", i);
- padctl->vbus[i] = devm_regulator_get(padctl->dev, reg_name);
- if (IS_ERR(padctl->vbus[i]))
- return PTR_ERR(padctl->vbus[i]);
-
- phy = devm_phy_create(padctl->dev, NULL, &utmi_phy_ops);
+ phy = tegra_xusb_utmi_phy_create(padctl, i);
if (IS_ERR(phy))
return PTR_ERR(phy);

padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i] = phy;
- phy_set_drvdata(phy, padctl);
}

padctl->vddio_hsic = devm_regulator_get(padctl->dev, "vddio-hsic");


Attachments:
(No filename) (0.00 B)
(No filename) (819.00 B)
Download all attachments

2015-02-25 17:27:40

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH V6 00/12] Tegra xHCI support

Hi Thierry,

> Sorry for taking so awfully long to look at this. I've spent some time
> looking at various pieces of documentation and I concluded that
> representing the port assignment as muxing options doesn't seem right
> after all. Instead I've come up with an alternate proposal (attached).
> Could you take a look and see if that sounds reasonable to you?

Thanks for taking a look at this. I've been meaning to pick this
series back up, but haven't had quite enough bandwidth lately.

This all looks good to me, just one comment below:

> +PHY nodes:
> +----------
> +
> +An optional child node named "phys" can contain nodes describing additional
> +properties of each PHY. Only USB3 and UTMI PHYs can be complemented in this
> +way, in which case the name of each node must match one of the following:
> +
> + usb3-0, usb3-1, utmi-0, utmi-1, utmi-2
> +
> +Required properties for USB3 PHYs:
> +- nvidia,lanes: specifies the name of the lane that this USB3 PHY uses
> +- nvidia,port: specifies the number of the USB2 port that is used for this
> + USB3 PHY
> +
> +Optional properties for UTMI PHYs:
> +- vbus-supply: regulator providing the VBUS voltage for the UTMI pad

What about the HSIC PHYs? Shouldn't they be represented as PHY nodes as well?

2015-02-25 21:15:45

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH V6 00/12] Tegra xHCI support

On Wed, Feb 25, 2015 at 09:27:36AM -0800, Andrew Bresticker wrote:
> Hi Thierry,
>
> > Sorry for taking so awfully long to look at this. I've spent some time
> > looking at various pieces of documentation and I concluded that
> > representing the port assignment as muxing options doesn't seem right
> > after all. Instead I've come up with an alternate proposal (attached).
> > Could you take a look and see if that sounds reasonable to you?
>
> Thanks for taking a look at this. I've been meaning to pick this
> series back up, but haven't had quite enough bandwidth lately.
>
> This all looks good to me, just one comment below:
>
> > +PHY nodes:
> > +----------
> > +
> > +An optional child node named "phys" can contain nodes describing additional
> > +properties of each PHY. Only USB3 and UTMI PHYs can be complemented in this
> > +way, in which case the name of each node must match one of the following:
> > +
> > + usb3-0, usb3-1, utmi-0, utmi-1, utmi-2
> > +
> > +Required properties for USB3 PHYs:
> > +- nvidia,lanes: specifies the name of the lane that this USB3 PHY uses
> > +- nvidia,port: specifies the number of the USB2 port that is used for this
> > + USB3 PHY
> > +
> > +Optional properties for UTMI PHYs:
> > +- vbus-supply: regulator providing the VBUS voltage for the UTMI pad
>
> What about the HSIC PHYs? Shouldn't they be represented as PHY nodes as well?

Yes, they could. The PCIe and SATA PHYs could as well. I haven't
included them because they currently don't take any properties. In
addition to that, perhaps some of the nvidia,hsic-* properties could be
moved into the PHY nodes, too. But they're also properties of the pin,
so keeping them in the pinmux nodes seems fine as well.

On a slightly different topic, I've been trying to wrap my head around
the use of the nvidia,port property and my conclusion was that in fact
one of the physical ports is shared between USB2 and USB3. That is the
utmi-2 PHY and usb3-0 PHY go to the very same port. The vbus-supply
specified in the Jetson TK1 DTS would support that (it's associated with
utmi-2 but named vdd_usb3_reg, and the USB3 port doesn't work without
it). Can you confirm that?

Thierry


Attachments:
(No filename) (2.13 kB)
(No filename) (819.00 B)
Download all attachments

2015-02-25 21:20:22

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH V6 00/12] Tegra xHCI support

On Wed, Feb 25, 2015 at 1:15 PM, Thierry Reding
<[email protected]> wrote:
> On Wed, Feb 25, 2015 at 09:27:36AM -0800, Andrew Bresticker wrote:
>> Hi Thierry,
>>
>> > Sorry for taking so awfully long to look at this. I've spent some time
>> > looking at various pieces of documentation and I concluded that
>> > representing the port assignment as muxing options doesn't seem right
>> > after all. Instead I've come up with an alternate proposal (attached).
>> > Could you take a look and see if that sounds reasonable to you?
>>
>> Thanks for taking a look at this. I've been meaning to pick this
>> series back up, but haven't had quite enough bandwidth lately.
>>
>> This all looks good to me, just one comment below:
>>
>> > +PHY nodes:
>> > +----------
>> > +
>> > +An optional child node named "phys" can contain nodes describing additional
>> > +properties of each PHY. Only USB3 and UTMI PHYs can be complemented in this
>> > +way, in which case the name of each node must match one of the following:
>> > +
>> > + usb3-0, usb3-1, utmi-0, utmi-1, utmi-2
>> > +
>> > +Required properties for USB3 PHYs:
>> > +- nvidia,lanes: specifies the name of the lane that this USB3 PHY uses
>> > +- nvidia,port: specifies the number of the USB2 port that is used for this
>> > + USB3 PHY
>> > +
>> > +Optional properties for UTMI PHYs:
>> > +- vbus-supply: regulator providing the VBUS voltage for the UTMI pad
>>
>> What about the HSIC PHYs? Shouldn't they be represented as PHY nodes as well?
>
> Yes, they could. The PCIe and SATA PHYs could as well. I haven't
> included them because they currently don't take any properties. In
> addition to that, perhaps some of the nvidia,hsic-* properties could be
> moved into the PHY nodes, too. But they're also properties of the pin,
> so keeping them in the pinmux nodes seems fine as well.
>
> On a slightly different topic, I've been trying to wrap my head around
> the use of the nvidia,port property and my conclusion was that in fact
> one of the physical ports is shared between USB2 and USB3. That is the
> utmi-2 PHY and usb3-0 PHY go to the very same port. The vbus-supply
> specified in the Jetson TK1 DTS would support that (it's associated with
> utmi-2 but named vdd_usb3_reg, and the USB3 port doesn't work without
> it). Can you confirm that?

Yes, that is my understanding as well.