diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-18 13:14:02 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-18 13:14:02 -0700 |
commit | 4a5219edcdae52bfb5eea0dfc2a7bd575961dad7 (patch) | |
tree | 863f6efeac5fe9ba4f1f8d23b32b2947130ed510 | |
parent | 9797f6b0504122e4ad9ff047a3d0521ad6706386 (diff) | |
parent | 5420f9fd159761b88978c312c3b350546f8615bb (diff) | |
download | linux-4a5219edcdae52bfb5eea0dfc2a7bd575961dad7.tar.bz2 |
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Arnd Bergmann:
"Driver updates for ARM SoCs, these contain various things that touch
the drivers/ directory but got merged through arm-soc for practical
reasons.
For the most part, this is now related to power management
controllers, which have not yet been abstracted into a separate
subsystem, and typically require some code in drivers/soc or arch/arm
to control the power domains.
Another large chunk here is a rework of the NVIDIA Tegra USB3.0
support, which was surprisingly tricky and took a long time to get
done.
Finally, reset controller handling as always gets merged through here
as well"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (97 commits)
arm-ccn: Enable building as module
soc/tegra: pmc: Add generic PM domain support
usb: xhci: tegra: Add Tegra210 support
usb: xhci: Add NVIDIA Tegra XUSB controller driver
dt-bindings: usb: xhci-tegra: Add Tegra210 XUSB controller support
dt-bindings: usb: Add NVIDIA Tegra XUSB controller binding
PCI: tegra: Support per-lane PHYs
dt-bindings: pci: tegra: Update for per-lane PHYs
phy: tegra: Add Tegra210 support
phy: Add Tegra XUSB pad controller support
dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support
dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding
phy: core: Allow children node to be overridden
clk: tegra: Add interface to enable hardware control of SATA/XUSB PLLs
drivers: firmware: psci: make two helper functions inline
soc: renesas: rcar-sysc: Add support for R-Car H3 power areas
soc: renesas: rcar-sysc: Add support for R-Car E2 power areas
soc: renesas: rcar-sysc: Add support for R-Car M2-N power areas
soc: renesas: rcar-sysc: Add support for R-Car M2-W power areas
soc: renesas: rcar-sysc: Add support for R-Car H2 power areas
...
99 files changed, 11511 insertions, 975 deletions
diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt index 02c27004d4a8..a74b37b07e5c 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt @@ -1,16 +1,20 @@ NVIDIA Tegra Power Management Controller (PMC) +== Power Management Controller Node == + The PMC block interacts with an external Power Management Unit. The PMC mostly controls the entry and exit of the system from different sleep modes. It provides power-gating controllers for SoC and CPU power-islands. Required properties: - name : Should be pmc -- compatible : For Tegra20, must contain "nvidia,tegra20-pmc". For Tegra30, - must contain "nvidia,tegra30-pmc". For Tegra114, must contain - "nvidia,tegra114-pmc". For Tegra124, must contain "nvidia,tegra124-pmc". - Otherwise, must contain "nvidia,<chip>-pmc", plus at least one of the - above, where <chip> is tegra132. +- compatible : Should contain one of the following: + For Tegra20 must contain "nvidia,tegra20-pmc". + For Tegra30 must contain "nvidia,tegra30-pmc". + For Tegra114 must contain "nvidia,tegra114-pmc" + For Tegra124 must contain "nvidia,tegra124-pmc" + For Tegra132 must contain "nvidia,tegra124-pmc" + For Tegra210 must contain "nvidia,tegra210-pmc" - reg : Offset and length of the register set for the device - clocks : Must contain an entry for each entry in clock-names. See ../clocks/clock-bindings.txt for details. @@ -68,6 +72,11 @@ Optional properties for hardware-triggered thermal reset (inside 'i2c-thermtrip' Defaults to 0. Valid values are described in section 12.5.2 "Pinmux Support" of the Tegra4 Technical Reference Manual. +Optional nodes: +- powergates : This node contains a hierarchy of power domain nodes, which + should match the powergates on the Tegra SoC. See "Powergate + Nodes" below. + Example: / SoC dts including file @@ -113,3 +122,76 @@ pmc@7000f400 { }; ... }; + + +== Powergate Nodes == + +Each of the powergate nodes represents a power-domain on the Tegra SoC +that can be power-gated by the Tegra PMC. The name of the powergate node +should be one of the below. Note that not every powergate is applicable +to all Tegra devices and the following list shows which powergates are +applicable to which devices. Please refer to the Tegra TRM for more +details on the various powergates. + + Name Description Devices Applicable + 3d 3D Graphics Tegra20/114/124/210 + 3d0 3D Graphics 0 Tegra30 + 3d1 3D Graphics 1 Tegra30 + aud Audio Tegra210 + dfd Debug Tegra210 + dis Display A Tegra114/124/210 + disb Display B Tegra114/124/210 + heg 2D Graphics Tegra30/114/124/210 + iram Internal RAM Tegra124/210 + mpe MPEG Encode All + nvdec NVIDIA Video Decode Engine Tegra210 + nvjpg NVIDIA JPEG Engine Tegra210 + pcie PCIE Tegra20/30/124/210 + sata SATA Tegra30/124/210 + sor Display interfaces Tegra124/210 + ve2 Video Encode Engine 2 Tegra210 + venc Video Encode Engine All + vdec Video Decode Engine Tegra20/30/114/124 + vic Video Imaging Compositor Tegra124/210 + xusba USB Partition A Tegra114/124/210 + xusbb USB Partition B Tegra114/124/210 + xusbc USB Partition C Tegra114/124/210 + +Required properties: + - clocks: Must contain an entry for each clock required by the PMC for + controlling a power-gate. See ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each reset required by the PMC for + controlling a power-gate. See ../reset/reset.txt for details. + - #power-domain-cells: Must be 0. + +Example: + + pmc: pmc@7000e400 { + compatible = "nvidia,tegra210-pmc"; + reg = <0x0 0x7000e400 0x0 0x400>; + clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; + + powergates { + pd_audio: aud { + clocks = <&tegra_car TEGRA210_CLK_APE>, + <&tegra_car TEGRA210_CLK_APB2APE>; + resets = <&tegra_car 198>; + #power-domain-cells = <0>; + }; + }; + }; + + +== Powergate Clients == + +Hardware blocks belonging to a power domain should contain a "power-domains" +property that is a phandle pointing to the corresponding powergate node. + +Example: + + adma: adma@702e2000 { + ... + power-domains = <&pd_audio>; + ... + }; diff --git a/Documentation/devicetree/bindings/memory-controllers/exynos-srom.txt b/Documentation/devicetree/bindings/memory-controllers/exynos-srom.txt new file mode 100644 index 000000000000..f633b5d0f8ca --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/exynos-srom.txt @@ -0,0 +1,79 @@ +SAMSUNG Exynos SoCs SROM Controller driver. + +Required properties: +- compatible : Should contain "samsung,exynos4210-srom". + +- reg: offset and length of the register set + +Optional properties: +The SROM controller can be used to attach external peripherals. In this case +extra properties, describing the bus behind it, should be specified as below: + +- #address-cells: Must be set to 2 to allow device address translation. + Address is specified as (bank#, offset). + +- #size-cells: Must be set to 1 to allow device size passing + +- ranges: Must be set up to reflect the memory layout with four integer values + per bank: + <bank-number> 0 <parent address of bank> <size> + +Sub-nodes: +The actual device nodes should be added as subnodes to the SROMc node. These +subnodes, in addition to regular device specification, should contain the following +properties, describing configuration of the relevant SROM bank: + +Required properties: +- reg: bank number, base address (relative to start of the bank) and size of + the memory mapped for the device. Note that base address will be + typically 0 as this is the start of the bank. + +- samsung,srom-timing : array of 6 integers, specifying bank timings in the + following order: Tacp, Tcah, Tcoh, Tacc, Tcos, Tacs. + Each value is specified in cycles and has the following + meaning and valid range: + Tacp : Page mode access cycle at Page mode (0 - 15) + Tcah : Address holding time after CSn (0 - 15) + Tcoh : Chip selection hold on OEn (0 - 15) + Tacc : Access cycle (0 - 31, the actual time is N + 1) + Tcos : Chip selection set-up before OEn (0 - 15) + Tacs : Address set-up before CSn (0 - 15) + +Optional properties: +- reg-io-width : data width in bytes (1 or 2). If omitted, default of 1 is used. + +- samsung,srom-page-mode : if page mode is set, 4 data page mode will be configured, + else normal (1 data) page mode will be set. + +Example: basic definition, no banks are configured + memory-controller@12570000 { + compatible = "samsung,exynos4210-srom"; + reg = <0x12570000 0x14>; + }; + +Example: SROMc with SMSC911x ethernet chip on bank 3 + memory-controller@12570000 { + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x04000000 0x20000 // Bank0 + 1 0 0x05000000 0x20000 // Bank1 + 2 0 0x06000000 0x20000 // Bank2 + 3 0 0x07000000 0x20000>; // Bank3 + + compatible = "samsung,exynos4210-srom"; + reg = <0x12570000 0x14>; + + ethernet@3,0 { + compatible = "smsc,lan9115"; + reg = <3 0 0x10000>; // Bank 3, offset = 0 + phy-mode = "mii"; + interrupt-parent = <&gpx0>; + interrupts = <5 8>; + reg-io-width = <2>; + smsc,irq-push-pull; + smsc,force-internal-phy; + + samsung,srom-page-mode; + samsung,srom-timing = <9 12 1 9 1 1>; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/arm-versatile.txt b/Documentation/devicetree/bindings/mtd/arm-versatile.txt index beace4b89daa..4ec28796a3c0 100644 --- a/Documentation/devicetree/bindings/mtd/arm-versatile.txt +++ b/Documentation/devicetree/bindings/mtd/arm-versatile.txt @@ -1,8 +1,26 @@ Flash device on ARM Versatile board +These flash chips are found in the ARM reference designs like Integrator, +Versatile, RealView, Versatile Express etc. + +They are regular CFI compatible (Intel or AMD extended) flash chips with +some special write protect/VPP bits that can be controlled by the machine's +system controller. + Required properties: -- compatible : must be "arm,versatile-flash"; +- compatible : must be "arm,versatile-flash", "cfi-flash"; +- reg : memory address for the flash chip - bank-width : width in bytes of flash interface. +For the rest of the properties, see mtd-physmap.txt. + The device tree may optionally contain sub-nodes describing partitions of the address space. See partition.txt for more detail. + +Example: + +flash@34000000 { + compatible = "arm,versatile-flash", "cfi-flash"; + reg = <0x34000000 0x4000000>; + bank-width = <4>; +}; diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt index 75321ae23c08..b8cc395fffea 100644 --- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt @@ -60,11 +60,14 @@ Required properties: - afi - pcie_x -Required properties on Tegra124 and later: +Required properties on Tegra124 and later (deprecated): - phys: Must contain an entry for each entry in phy-names. - phy-names: Must include the following entries: - pcie +These properties are deprecated in favour of per-lane PHYs define in each of +the root ports (see below). + Power supplies for Tegra20: - avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V. - vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V. @@ -122,11 +125,22 @@ Required properties: - Root port 0 uses 4 lanes, root port 1 is unused. - Both root ports use 2 lanes. -Example: +Required properties for Tegra124 and later: +- phys: Must contain an phandle to a PHY for each entry in phy-names. +- phy-names: Must include an entry for each active lane. Note that the number + of entries does not have to (though usually will) be equal to the specified + number of lanes in the nvidia,num-lanes property. Entries are of the form + "pcie-N": where N ranges from 0 to the value specified in nvidia,num-lanes. + +Examples: +========= + +Tegra20: +-------- SoC DTSI: - pcie-controller { + pcie-controller@80003000 { compatible = "nvidia,tegra20-pcie"; device_type = "pci"; reg = <0x80003000 0x00000800 /* PADS registers */ @@ -186,10 +200,9 @@ SoC DTSI: }; }; - Board DTS: - pcie-controller { + pcie-controller@80003000 { status = "okay"; vdd-supply = <&pci_vdd_reg>; @@ -222,3 +235,204 @@ if a device on the PCI bus provides a non-probeable bus such as I2C or SPI, device nodes need to be added in order to allow the bus' children to be instantiated at the proper location in the operating system's device tree (as illustrated by the optional nodes in the example above). + +Tegra30: +-------- + +SoC DTSI: + + pcie-controller@00003000 { + compatible = "nvidia,tegra30-pcie"; + device_type = "pci"; + reg = <0x00003000 0x00000800 /* PADS registers */ + 0x00003800 0x00000200 /* AFI registers */ + 0x10000000 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH /* controller interrupt */ + GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x00000000 0x00000000 0 0x00001000 /* port 0 configuration space */ + 0x82000000 0 0x00001000 0x00001000 0 0x00001000 /* port 1 configuration space */ + 0x82000000 0 0x00004000 0x00004000 0 0x00001000 /* port 2 configuration space */ + 0x81000000 0 0 0x02000000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x20000000 0x20000000 0 0x08000000 /* non-prefetchable memory */ + 0xc2000000 0 0x28000000 0x28000000 0 0x18000000>; /* prefetchable memory */ + + clocks = <&tegra_car TEGRA30_CLK_PCIE>, + <&tegra_car TEGRA30_CLK_AFI>, + <&tegra_car TEGRA30_CLK_PLL_E>, + <&tegra_car TEGRA30_CLK_CML0>; + clock-names = "pex", "afi", "pll_e", "cml"; + resets = <&tegra_car 70>, + <&tegra_car 72>, + <&tegra_car 74>; + reset-names = "pex", "afi", "pcie_x"; + status = "disabled"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x00000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x00001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@3,0 { + device_type = "pci"; + assigned-addresses = <0x82001800 0 0x00004000 0 0x1000>; + reg = <0x001800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + }; + +Board DTS: + + pcie-controller@00003000 { + status = "okay"; + + avdd-pexa-supply = <&ldo1_reg>; + vdd-pexa-supply = <&ldo1_reg>; + avdd-pexb-supply = <&ldo1_reg>; + vdd-pexb-supply = <&ldo1_reg>; + avdd-pex-pll-supply = <&ldo1_reg>; + avdd-plle-supply = <&ldo1_reg>; + vddio-pex-ctl-supply = <&sys_3v3_reg>; + hvdd-pex-supply = <&sys_3v3_pexs_reg>; + + pci@1,0 { + status = "okay"; + }; + + pci@3,0 { + status = "okay"; + }; + }; + +Tegra124: +--------- + +SoC DTSI: + + pcie-controller@01003000 { + compatible = "nvidia,tegra124-pcie"; + device_type = "pci"; + reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */ + 0x0 0x01003800 0x0 0x00000800 /* AFI registers */ + 0x0 0x02000000 0x0 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */ + <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */ + 0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */ + 0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */ + 0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */ + 0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */ + + clocks = <&tegra_car TEGRA124_CLK_PCIE>, + <&tegra_car TEGRA124_CLK_AFI>, + <&tegra_car TEGRA124_CLK_PLL_E>, + <&tegra_car TEGRA124_CLK_CML0>; + clock-names = "pex", "afi", "pll_e", "cml"; + resets = <&tegra_car 70>, + <&tegra_car 72>, + <&tegra_car 74>; + reset-names = "pex", "afi", "pcie_x"; + status = "disabled"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <1>; + }; + }; + +Board DTS: + + pcie-controller@01003000 { + status = "okay"; + + avddio-pex-supply = <&vdd_1v05_run>; + dvddio-pex-supply = <&vdd_1v05_run>; + avdd-pex-pll-supply = <&vdd_1v05_run>; + hvdd-pex-supply = <&vdd_3v3_lp0>; + hvdd-pex-pll-e-supply = <&vdd_3v3_lp0>; + vddio-pex-ctl-supply = <&vdd_3v3_lp0>; + avdd-pll-erefe-supply = <&avdd_1v05_run>; + + /* Mini PCIe */ + pci@1,0 { + phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>; + phy-names = "pcie-0"; + status = "okay"; + }; + + /* Gigabit Ethernet */ + pci@2,0 { + phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>; + phy-names = "pcie-0"; + status = "okay"; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt new file mode 100644 index 000000000000..0bf1ae243552 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt @@ -0,0 +1,733 @@ +Device tree binding for NVIDIA Tegra XUSB pad controller +======================================================== + +The Tegra XUSB pad controller manages a set of I/O lanes (with differential +signals) which connect directly to pins/pads on the SoC package. Each lane +is controlled by a HW block referred to as a "pad" in the Tegra hardware +documentation. Each such "pad" may control either one or multiple lanes, +and thus contains any logic common to all its lanes. Each lane can be +separately configured and powered up. + +Some of the lanes are high-speed lanes, which can be used for PCIe, SATA or +super-speed USB. Other lanes are for various types of low-speed, full-speed +or high-speed USB (such as UTMI, ULPI and HSIC). The XUSB pad controller +contains a software-configurable mux that sits between the I/O controller +ports (e.g. PCIe) and the lanes. + +In addition to per-lane configuration, USB 3.0 ports may require additional +settings on a per-board basis. + +Pads will be represented as children of the top-level XUSB pad controller +device tree node. Each lane exposed by the pad will be represented by its +own subnode and can be referenced by users of the lane using the standard +PHY bindings, as described by the phy-bindings.txt file in this directory. + +The Tegra hardware documentation refers to the connection between the XUSB +pad controller and the XUSB controller as "ports". This is confusing since +"port" is typically used to denote the physical USB receptacle. The device +tree binding in this document uses the term "port" to refer to the logical +abstraction of the signals that are routed to a USB receptacle (i.e. a PHY +for the USB signal, the VBUS power supply, the USB 2.0 companion port for +USB 3.0 receptacles, ...). + +Required properties: +-------------------- +- compatible: Must be: + - Tegra124: "nvidia,tegra124-xusb-padctl" + - Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl" + - Tegra210: "nvidia,tegra210-xusb-padctl" +- reg: Physical base address and length of the controller's registers. +- resets: Must contain an entry for each entry in reset-names. +- reset-names: Must include the following entries: + - "padctl" + + +Pad nodes: +========== + +A required child node named "pads" contains a list of subnodes, one for each +of the pads exposed by the XUSB pad controller. Each pad may need additional +resources that can be referenced in its pad node. + +The "status" property is used to enable or disable the use of a pad. If set +to "disabled", the pad will not be used on the given board. In order to use +the pad and any of its lanes, this property must be set to "okay". + +For Tegra124 and Tegra132, the following pads exist: usb2, ulpi, hsic, pcie +and sata. No extra resources are required for operation of these pads. + +For Tegra210, the following pads exist: usb2, hsic, pcie and sata. Below is +a description of the properties of each pad. + +UTMI pad: +--------- + +Required properties: +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Must contain the following entries: + - "trk": phandle and specifier referring to the USB2 tracking clock + +HSIC pad: +--------- + +Required properties: +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Must contain the following entries: + - "trk": phandle and specifier referring to the HSIC tracking clock + +PCIe pad: +--------- + +Required properties: +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Must contain the following entries: + - "pll": phandle and specifier referring to the PLLE +- resets: Must contain an entry for each entry in reset-names. +- reset-names: Must contain the following entries: + - "phy": reset for the PCIe UPHY block + +SATA pad: +--------- + +Required properties: +- resets: Must contain an entry for each entry in reset-names. +- reset-names: Must contain the following entries: + - "phy": reset for the SATA UPHY block + + +PHY nodes: +========== + +Each pad node has a child named "lanes" that contains one or more children of +its own, each representing one of the lanes controlled by the pad. + +Required properties: +-------------------- +- status: Defines the operation status of the PHY. Valid values are: + - "disabled": the PHY is disabled + - "okay": the PHY is enabled +- #phy-cells: Should be 0. Since each lane represents a single PHY, there is + no need for an additional specifier. +- nvidia,function: The output function of the PHY. See below for a list of + valid functions per SoC generation. + +For Tegra124 and Tegra132, the list of valid PHY nodes is given below: +- usb2: usb2-0, usb2-1, usb2-2 + - functions: "snps", "xusb", "uart" +- ulpi: ulpi-0 + - functions: "snps", "xusb" +- hsic: hsic-0, hsic-1 + - functions: "snps", "xusb" +- pcie: pcie-0, pcie-1, pcie-2, pcie-3, pcie-4 + - functions: "pcie", "usb3-ss" +- sata: sata-0 + - functions: "usb3-ss", "sata" + +For Tegra210, the list of valid PHY nodes is given below: +- utmi: utmi-0, utmi-1, utmi-2, utmi-3 + - functions: "snps", "xusb", "uart" +- hsic: hsic-0, hsic-1 + - functions: "snps", "xusb" +- pcie: pcie-0, pcie-1, pcie-2, pcie-3, pcie-4, pcie-5, pcie-6 + - functions: "pcie-x1", "usb3-ss", "pcie-x4" +- sata: sata-0 + - functions: "usb3-ss", "sata" + + +Port nodes: +=========== + +A required child node named "ports" contains a list of all the ports exposed +by the XUSB pad controller. Per-port configuration is only required for USB. + +USB2 ports: +----------- + +Required properties: +- status: Defines the operation status of the port. Valid values are: + - "disabled": the port is disabled + - "okay": the port is enabled +- mode: A string that determines the mode in which to run the port. Valid + values are: + - "host": for USB host mode + - "device": for USB device mode + - "otg": for USB OTG mode + +Optional properties: +- nvidia,internal: A boolean property whose presence determines that a port + is internal. In the absence of this property the port is considered to be + external. +- vbus-supply: phandle to a regulator supplying the VBUS voltage. + +ULPI ports: +----------- + +Optional properties: +- status: Defines the operation status of the port. Valid values are: + - "disabled": the port is disabled + - "okay": the port is enabled +- nvidia,internal: A boolean property whose presence determines that a port + is internal. In the absence of this property the port is considered to be + external. +- vbus-supply: phandle to a regulator supplying the VBUS voltage. + +HSIC ports: +----------- + +Required properties: +- status: Defines the operation status of the port. Valid values are: + - "disabled": the port is disabled + - "okay": the port is enabled + +Optional properties: +- vbus-supply: phandle to a regulator supplying the VBUS voltage. + +Super-speed USB ports: +---------------------- + +Required properties: +- status: Defines the operation status of the port. Valid values are: + - "disabled": the port is disabled + - "okay": the port is enabled +- nvidia,usb2-companion: A single cell that specifies the physical port number + to map this super-speed USB port to. The range of valid port numbers varies + with the SoC generation: + - 0-2: for Tegra124 and Tegra132 + - 0-3: for Tegra210 + +Optional properties: +- nvidia,internal: A boolean property whose presence determines that a port + is internal. In the absence of this property the port is considered to be + external. + +For Tegra124 and Tegra132, the XUSB pad controller exposes the following +ports: +- 3x USB2: usb2-0, usb2-1, usb2-2 +- 1x ULPI: ulpi-0 +- 2x HSIC: hsic-0, hsic-1 +- 2x super-speed USB: usb3-0, usb3-1 + +For Tegra210, the XUSB pad controller exposes the following ports: +- 4x USB2: usb2-0, usb2-1, usb2-2, usb2-3 +- 2x HSIC: hsic-0, hsic-1 +- 4x super-speed USB: usb3-0, usb3-1, usb3-2, usb3-3 + + +Examples: +========= + +Tegra124 and Tegra132: +---------------------- + +SoC include: + + padctl@7009f000 { + /* for Tegra124 */ + compatible = "nvidia,tegra124-xusb-padctl"; + /* for Tegra132 */ + compatible = "nvidia,tegra132-xusb-padctl", + "nvidia,tegra124-xusb-padctl"; + reg = <0x0 0x7009f000 0x0 0x1000>; + resets = <&tegra_car 142>; + reset-names = "padctl"; + + pads { + usb2 { + status = "disabled"; + + lanes { + usb2-0 { + status = "disabled"; + #phy-cells = <0>; + }; + + usb2-1 { + status = "disabled"; + #phy-cells = <0>; + }; + + usb2-2 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + ulpi { + status = "disabled"; + + lanes { + ulpi-0 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + hsic { + status = "disabled"; + + lanes { + hsic-0 { + status = "disabled"; + #phy-cells = <0>; + }; + + hsic-1 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + pcie { + status = "disabled"; + + lanes { + pcie-0 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-1 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-2 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-3 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-4 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + sata { + status = "disabled"; + + lanes { + sata-0 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + }; + + ports { + usb2-0 { + status = "disabled"; + }; + + usb2-1 { + status = "disabled"; + }; + + usb2-2 { + status = "disabled"; + }; + + ulpi-0 { + status = "disabled"; + }; + + hsic-0 { + status = "disabled"; + }; + + hsic-1 { + status = "disabled"; + }; + + usb3-0 { + status = "disabled"; + }; + + usb3-1 { + status = "disabled"; + }; + }; + }; + +Board file: + + padctl@7009f000 { + status = "okay"; + + pads { + usb2 { + status = "okay"; + + lanes { + usb2-0 { + nvidia,function = "xusb"; + status = "okay"; + }; + + usb2-1 { + nvidia,function = "xusb"; + status = "okay"; + }; + + usb2-2 { + nvidia,function = "xusb"; + status = "okay"; + }; + }; + }; + + pcie { + status = "okay"; + + lanes { + pcie-0 { + nvidia,function = "usb3-ss"; + status = "okay"; + }; + + pcie-2 { + nvidia,function = "pcie"; + status = "okay"; + }; + + pcie-4 { + nvidia,function = "pcie"; + status = "okay"; + }; + }; + }; + + sata { + status = "okay"; + + lanes { + sata-0 { + nvidia,function = "sata"; + status = "okay"; + }; + }; + }; + }; + + ports { + /* Micro A/B */ + usb2-0 { + status = "okay"; + mode = "otg"; + }; + + /* Mini PCIe */ + usb2-1 { + status = "okay"; + mode = "host"; + }; + + /* USB3 */ + usb2-2 { + status = "okay"; + mode = "host"; + + vbus-supply = <&vdd_usb3_vbus>; + }; + + usb3-0 { + nvidia,port = <2>; + status = "okay"; + }; + }; + }; + +Tegra210: +--------- + +SoC include: + + padctl@7009f000 { + compatible = "nvidia,tegra210-xusb-padctl"; + reg = <0x0 0x7009f000 0x0 0x1000>; + resets = <&tegra_car 142>; + reset-names = "padctl"; + + status = "disabled"; + + pads { + usb2 { + clocks = <&tegra_car TEGRA210_CLK_USB2_TRK>; + clock-names = "trk"; + status = "disabled"; + + lanes { + usb2-0 { + status = "disabled"; + #phy-cells = <0>; + }; + + usb2-1 { + status = "disabled"; + #phy-cells = <0>; + }; + + usb2-2 { + status = "disabled"; + #phy-cells = <0>; + }; + + usb2-3 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + hsic { + clocks = <&tegra_car TEGRA210_CLK_HSIC_TRK>; + clock-names = "trk"; + status = "disabled"; + + lanes { + hsic-0 { + status = "disabled"; + #phy-cells = <0>; + }; + + hsic-1 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + pcie { + clocks = <&tegra_car TEGRA210_CLK_PLL_E>; + clock-names = "pll"; + resets = <&tegra_car 205>; + reset-names = "phy"; + status = "disabled"; + + lanes { + pcie-0 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-1 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-2 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-3 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-4 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-5 { + status = "disabled"; + #phy-cells = <0>; + }; + + pcie-6 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + + sata { + clocks = <&tegra_car TEGRA210_CLK_PLL_E>; + clock-names = "pll"; + resets = <&tegra_car 204>; + reset-names = "phy"; + status = "disabled"; + + lanes { + sata-0 { + status = "disabled"; + #phy-cells = <0>; + }; + }; + }; + }; + + ports { + usb2-0 { + status = "disabled"; + }; + + usb2-1 { + status = "disabled"; + }; + + usb2-2 { + status = "disabled"; + }; + + usb2-3 { + status = "disabled"; + }; + + hsic-0 { + status = "disabled"; + }; + + hsic-1 { + status = "disabled"; + }; + + usb3-0 { + status = "disabled"; + }; + + usb3-1 { + status = "disabled"; + }; + + usb3-2 { + status = "disabled"; + }; + + usb3-3 { + status = "disabled"; + }; + }; + }; + +Board file: + + padctl@7009f000 { + status = "okay"; + + pads { + usb2 { + status = "okay"; + + lanes { + usb2-0 { + nvidia,function = "xusb"; + status = "okay"; + }; + + usb2-1 { + nvidia,function = "xusb"; + status = "okay"; + }; + + usb2-2 { + nvidia,function = "xusb"; + status = "okay"; + }; + + usb2-3 { + nvidia,function = "xusb"; + status = "okay"; + }; + }; + }; + + pcie { + status = "okay"; + + lanes { + pcie-0 { + nvidia,function = "pcie-x1"; + status = "okay"; + }; + + pcie-1 { + nvidia,function = "pcie-x4"; + status = "okay"; + }; + + pcie-2 { + nvidia,function = "pcie-x4"; + status = "okay"; + }; + + pcie-3 { + nvidia,function = "pcie-x4"; + status = "okay"; + }; + + pcie-4 { + nvidia,function = "pcie-x4"; + status = "okay"; + }; + + pcie-5 { + nvidia,function = "usb3-ss"; + status = "okay"; + }; + + pcie-6 { + nvidia,function = "usb3-ss"; + status = "okay"; + }; + }; + }; + + sata { + status = "okay"; + + lanes { + sata-0 { + nvidia,function = "sata"; + status = "okay"; + }; + }; + }; + }; + + ports { + usb2-0 { + status = "okay"; + mode = "otg"; + }; + + usb2-1 { + status = "okay"; + vbus-supply = <&vdd_5v0_rtl>; + mode = "host"; + }; + + usb2-2 { + status = "okay"; + vbus-supply = <&vdd_usb_vbus>; + mode = "host"; + }; + + usb2-3 { + status = "okay"; + mode = "host"; + }; + + usb3-0 { + status = "okay"; + nvidia,lanes = "pcie-6"; + nvidia,port = <1>; + }; + + usb3-1 { + status = "okay"; + nvidia,lanes = "pcie-5"; + nvidia,port = <2>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt index 30676ded85bb..8a6223dbc143 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt @@ -1,6 +1,12 @@ Device tree binding for NVIDIA Tegra XUSB pad controller ======================================================== +NOTE: It turns out that this binding isn't an accurate description of the XUSB +pad controller. While the description is good enough for the functional subset +required for PCIe and SATA, it lacks the flexibility to represent the features +needed for USB. For the new binding, see ../phy/nvidia,tegra-xusb-padctl.txt. +The binding described in this file is deprecated and should not be used. + The Tegra XUSB pad controller manages a set of lanes, each of which can be assigned to one out of a set of different pads. Some of these pads have an associated PHY that must be powered up before the pad can be used. diff --git a/Documentation/devicetree/bindings/reset/oxnas,reset.txt b/Documentation/devicetree/bindings/reset/oxnas,reset.txt new file mode 100644 index 000000000000..6f06db930030 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/oxnas,reset.txt @@ -0,0 +1,58 @@ +Oxford Semiconductor OXNAS SoC Family RESET Controller +================================================ + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required properties: +- compatible: Should be "oxsemi,ox810se-reset" +- #reset-cells: 1, see below + +Parent node should have the following properties : +- compatible: Should be "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd" + +For OX810SE, the indices are : + - 0 : ARM + - 1 : COPRO + - 2 : Reserved + - 3 : Reserved + - 4 : USBHS + - 5 : USBHSPHY + - 6 : MAC + - 7 : PCI + - 8 : DMA + - 9 : DPE + - 10 : DDR + - 11 : SATA + - 12 : SATA_LINK + - 13 : SATA_PHY + - 14 : Reserved + - 15 : NAND + - 16 : GPIO + - 17 : UART1 + - 18 : UART2 + - 19 : MISC + - 20 : I2S + - 21 : AHB_MON + - 22 : UART3 + - 23 : UART4 + - 24 : SGDMA + - 25 : Reserved + - 26 : Reserved + - 27 : Reserved + - 28 : Reserved + - 29 : Reserved + - 30 : Reserved + - 31 : BUS + +example: + +sys: sys-ctrl@000000 { + compatible = "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd"; + reg = <0x000000 0x100000>; + + reset: reset-controller { + compatible = "oxsemi,ox810se-reset"; + #reset-cells = <1>; + }; +}; diff --git a/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt b/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt index 13dc6a3fdb4a..f909ce06afc4 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt +++ b/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt @@ -7,6 +7,7 @@ Required properties for power domain controller: - compatible: Should be one of the following. "rockchip,rk3288-power-controller" - for RK3288 SoCs. "rockchip,rk3368-power-controller" - for RK3368 SoCs. + "rockchip,rk3399-power-controller" - for RK3399 SoCs. - #power-domain-cells: Number of cells in a power-domain specifier. Should be 1 for multiple PM domains. - #address-cells: Should be 1. @@ -16,8 +17,18 @@ Required properties for power domain sub nodes: - reg: index of the power domain, should use macros in: "include/dt-bindings/power/rk3288-power.h" - for RK3288 type power domain. "include/dt-bindings/power/rk3368-power.h" - for RK3368 type power domain. + "include/dt-bindings/power/rk3399-power.h" - for RK3399 type power domain. - clocks (optional): phandles to clocks which need to be enabled while power domain switches state. +- pm_qos (optional): phandles to qos blocks which need to be saved and restored + while power domain switches state. + +Qos Example: + + qos_gpu: qos_gpu@ffaf0000 { + compatible ="syscon"; + reg = <0x0 0xffaf0000 0x0 0x20>; + }; Example: @@ -30,6 +41,7 @@ Example: pd_gpu { reg = <RK3288_PD_GPU>; clocks = <&cru ACLK_GPU>; + pm_qos = <&qos_gpu>; }; }; @@ -45,12 +57,41 @@ Example: }; }; +Example 2: + power: power-controller { + compatible = "rockchip,rk3399-power-controller"; + #power-domain-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + pd_vio { + #address-cells = <1>; + #size-cells = <0>; + reg = <RK3399_PD_VIO>; + + pd_vo { + #address-cells = <1>; + #size-cells = <0>; + reg = <RK3399_PD_VO>; + + pd_vopb { + reg = <RK3399_PD_VOPB>; + }; + + pd_vopl { + reg = <RK3399_PD_VOPL>; + }; + }; + }; + }; + Node of a device using power domains must have a power-domains property, containing a phandle to the power device node and an index specifying which power domain to use. The index should use macros in: "include/dt-bindings/power/rk3288-power.h" - for rk3288 type power domain. "include/dt-bindings/power/rk3368-power.h" - for rk3368 type power domain. + "include/dt-bindings/power/rk3399-power.h" - for rk3399 type power domain. Example of the node using power domain: @@ -65,3 +106,9 @@ Example of the node using power domain: power-domains = <&power RK3368_PD_GPU_1>; /* ... */ }; + + node { + /* ... */ + power-domains = <&power RK3399_PD_VOPB>; + /* ... */ + }; diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt new file mode 100644 index 000000000000..d28295a3e55f --- /dev/null +++ b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt @@ -0,0 +1,120 @@ +NVIDIA Tegra xHCI controller +============================ + +The Tegra xHCI controller supports both USB2 and USB3 interfaces exposed by +the Tegra XUSB pad controller. + +Required properties: +-------------------- +- compatible: Must be: + - Tegra124: "nvidia,tegra124-xusb" + - Tegra132: "nvidia,tegra132-xusb", "nvidia,tegra124-xusb" + - Tegra210: "nvidia,tegra210-xusb" +- reg: Must contain the base and length of the xHCI host registers, XUSB FPCI + registers and XUSB IPFS registers. +- reg-names: Must contain the following entries: + - "hcd" + - "fpci" + - "ipfs" +- interrupts: Must contain the xHCI host interrupt and the mailbox 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_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_ss + - xusb_src + Note that xusb_src is the shared reset for xusb_{ss,hs,fs,falcon,host}_src. +- nvidia,xusb-padctl: phandle to the XUSB pad controller that is used to + configure the USB pads used by the XHCI controller + +For Tegra124 and Tegra132: +- avddio-pex-supply: PCIe/USB3 analog logic power supply. Must supply 1.05 V. +- dvddio-pex-supply: PCIe/USB3 digital logic power supply. Must supply 1.05 V. +- avdd-usb-supply: USB controller power supply. Must supply 3.3 V. +- avdd-pll-utmip-supply: UTMI PLL power supply. Must supply 1.8 V. +- avdd-pll-erefe-supply: PLLE reference PLL power supply. Must supply 1.05 V. +- avdd-usb-ss-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V. +- hvdd-usb-ss-supply: High-voltage PCIe/USB3 power supply. Must supply 3.3 V. +- hvdd-usb-ss-pll-e-supply: High-voltage PLLE power supply. Must supply 3.3 V. + +For Tegra210: +- dvddio-pex-supply: PCIe/USB3 analog logic power supply. Must supply 1.05 V. +- hvddio-pex-supply: High-voltage PCIe/USB3 power supply. Must supply 1.8 V. +- avdd-usb-supply: USB controller power supply. Must supply 3.3 V. +- avdd-pll-utmip-supply: UTMI PLL power supply. Must supply 1.8 V. +- avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V. +- dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V. +- hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V. + +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. The + following PHYs are available: + - Tegra124: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1 + - Tegra132: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1 + - Tegra210: usb2-0, usb2-1, usb2-2, usb2-3, hsic-0, usb3-0, usb3-1, usb3-2, + usb3-3 + +Example: +-------- + + usb@0,70090000 { + compatible = "nvidia,tegra124-xusb"; + reg = <0x0 0x70090000 0x0 0x8000>, + <0x0 0x70098000 0x0 0x1000>, + <0x0 0x70099000 0x0 0x1000>; + reg-names = "hcd", "fpci", "ipfs"; + + interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&tegra_car TEGRA124_CLK_XUSB_HOST>, + <&tegra_car TEGRA124_CLK_XUSB_HOST_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_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 156>, <&tegra_car 143>; + reset-names = "xusb_host", "xusb_ss", "xusb_src"; + + nvidia,xusb-padctl = <&padctl>; + + phys = <&{/padctl@0,7009f000/pads/usb2/usb2-1}>, /* mini-PCIe USB */ + <&{/padctl@0,7009f000/pads/usb2/usb2-2}>, /* USB A */ + <&{/padctl@0,7009f000/pads/pcie/pcie-0}>; /* 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>; + }; diff --git a/Documentation/phy.txt b/Documentation/phy.txt index b388c5af9e72..0aa994bd9a91 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -31,16 +31,28 @@ should provide its own implementation of of_xlate. of_xlate is used only for dt boot case. #define of_phy_provider_register(dev, xlate) \ - __of_phy_provider_register((dev), THIS_MODULE, (xlate)) + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate)) #define devm_of_phy_provider_register(dev, xlate) \ - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate)) + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate)) of_phy_provider_register and devm_of_phy_provider_register macros can be used to register the phy_provider and it takes device and of_xlate as arguments. For the dt boot case, all PHY providers should use one of the above 2 macros to register the PHY provider. +Often the device tree nodes associated with a PHY provider will contain a set +of children that each represent a single PHY. Some bindings may nest the child +nodes within extra levels for context and extensibility, in which case the low +level of_phy_provider_register_full() and devm_of_phy_provider_register_full() +macros can be used to override the node containing the children. + +#define of_phy_provider_register_full(dev, children, xlate) \ + __of_phy_provider_register(dev, children, THIS_MODULE, xlate) + +#define devm_of_phy_provider_register_full(dev, children, xlate) \ + __devm_of_phy_provider_register_full(dev, children, THIS_MODULE, xlate) + void devm_of_phy_provider_unregister(struct device *dev, struct phy_provider *phy_provider); void of_phy_provider_unregister(struct phy_provider *phy_provider); diff --git a/MAINTAINERS b/MAINTAINERS index 578e3f0214f6..804bc4fd154f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1535,6 +1535,8 @@ Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git next S: Supported F: arch/arm64/boot/dts/renesas/ +F: drivers/soc/renesas/ +F: include/linux/soc/renesas/ ARM/RISCPC ARCHITECTURE M: Russell King <linux@armlinux.org.uk> @@ -1584,6 +1586,7 @@ F: arch/arm/mach-s5p*/ F: arch/arm/mach-exynos*/ F: drivers/*/*s3c2410* F: drivers/*/*/*s3c2410* +F: drivers/memory/samsung/* F: drivers/soc/samsung/* F: drivers/spi/spi-s3c* F: sound/soc/samsung/* @@ -1648,6 +1651,8 @@ F: arch/arm/configs/shmobile_defconfig F: arch/arm/include/debug/renesas-scif.S F: arch/arm/mach-shmobile/ F: drivers/sh/ +F: drivers/soc/renesas/ +F: include/linux/soc/renesas/ ARM/SOCFPGA ARCHITECTURE M: Dinh Nguyen <dinguyen@opensource.altera.com> diff --git a/arch/arm/boot/dts/integrator.dtsi b/arch/arm/boot/dts/integrator.dtsi index b82f0e6d9a63..6fe0dd1d3541 100644 --- a/arch/arm/boot/dts/integrator.dtsi +++ b/arch/arm/boot/dts/integrator.dtsi @@ -52,8 +52,9 @@ }; flash@24000000 { - compatible = "cfi-flash"; + compatible = "arm,versatile-flash", "cfi-flash"; reg = <0x24000000 0x02000000>; + bank-width = <4>; }; fpga { diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts index d23320af5ea7..409e069b3a84 100644 --- a/arch/arm/boot/dts/versatile-ab.dts +++ b/arch/arm/boot/dts/versatile-ab.dts @@ -119,8 +119,9 @@ }; flash@34000000 { - compatible = "arm,versatile-flash"; - reg = <0x34000000 0x4000000>; + /* 64 MiB NOR flash in non-interleaved chips */ + compatible = "arm,versatile-flash", "cfi-flash"; + reg = <0x34000000 0x04000000>; bank-width = <4>; }; diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index 7ef121472cdd..68ab6412392a 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -173,12 +173,12 @@ config ARCH_BRCMSTB select ARM_GIC select ARM_ERRATA_798181 if SMP select HAVE_ARM_ARCH_TIMER - select BRCMSTB_GISB_ARB select BRCMSTB_L2_IRQ select BCM7120_L2_IRQ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE select ARCH_WANT_OPTIONAL_GPIOLIB select SOC_BRCMSTB + select SOC_BUS help Say Y if you intend to run the kernel on a Broadcom ARM-based STB chipset. diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 207fa2c737a6..e65aa7d11b20 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -18,6 +18,7 @@ menuconfig ARCH_EXYNOS select COMMON_CLK_SAMSUNG select EXYNOS_THERMAL select EXYNOS_PMU + select EXYNOS_SROM select HAVE_ARM_SCU if SMP select HAVE_S3C2410_I2C if I2C select HAVE_S3C2410_WATCHDOG if WATCHDOG @@ -26,11 +27,13 @@ menuconfig ARCH_EXYNOS select PINCTRL_EXYNOS select PM_GENERIC_DOMAINS if PM select S5P_DEV_MFC + select SAMSUNG_MC select SOC_SAMSUNG select SRAM select THERMAL select THERMAL_OF select MFD_SYSCON + select MEMORY select CLKSRC_EXYNOS_MCT select POWER_RESET select POWER_RESET_SYSCON diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 4d3b056fd786..52ccf247e079 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -31,11 +31,6 @@ static struct map_desc exynos4_iodesc[] __initdata = { { - .virtual = (unsigned long)S5P_VA_SROMC, - .pfn = __phys_to_pfn(EXYNOS4_PA_SROMC), - .length = SZ_4K, - .type = MT_DEVICE, - }, { .virtual = (unsigned long)S5P_VA_CMU, .pfn = __phys_to_pfn(EXYNOS4_PA_CMU), .length = SZ_128K, @@ -58,15 +53,6 @@ static struct map_desc exynos4_iodesc[] __initdata = { }, }; -static struct map_desc exynos5_iodesc[] __initdata = { - { - .virtual = (unsigned long)S5P_VA_SROMC, - .pfn = __phys_to_pfn(EXYNOS5_PA_SROMC), - .length = SZ_4K, - .type = MT_DEVICE, - }, -}; - static struct platform_device exynos_cpuidle = { .name = "exynos_cpuidle", #ifdef CONFIG_ARM_EXYNOS_CPUIDLE @@ -138,9 +124,6 @@ static void __init exynos_map_io(void) { if (soc_is_exynos4()) iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc)); - - if (soc_is_exynos5()) - iotable_init(exynos5_iodesc, ARRAY_SIZE(exynos5_iodesc)); } static void __init exynos_init_io(void) diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index c88325d56743..c48ba4fbdfd2 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -25,7 +25,4 @@ #define EXYNOS4_PA_COREPERI 0x10500000 -#define EXYNOS4_PA_SROMC 0x12570000 -#define EXYNOS5_PA_SROMC 0x12250000 - #endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-exynos/regs-srom.h b/arch/arm/mach-exynos/regs-srom.h deleted file mode 100644 index 5c4d4427db7b..000000000000 --- a/arch/arm/mach-exynos/regs-srom.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * S5P SROMC register definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __PLAT_SAMSUNG_REGS_SROM_H -#define __PLAT_SAMSUNG_REGS_SROM_H __FILE__ - -#include <mach/map.h> - -#define S5P_SROMREG(x) (S5P_VA_SROMC + (x)) - -#define S5P_SROM_BW S5P_SROMREG(0x0) -#define S5P_SROM_BC0 S5P_SROMREG(0x4) -#define S5P_SROM_BC1 S5P_SROMREG(0x8) -#define S5P_SROM_BC2 S5P_SROMREG(0xc) -#define S5P_SROM_BC3 S5P_SROMREG(0x10) -#define S5P_SROM_BC4 S5P_SROMREG(0x14) -#define S5P_SROM_BC5 S5P_SROMREG(0x18) - -/* one register BW holds 4 x 4-bit packed settings for NCS0 - NCS3 */ - -#define S5P_SROM_BW__DATAWIDTH__SHIFT 0 -#define S5P_SROM_BW__ADDRMODE__SHIFT 1 -#define S5P_SROM_BW__WAITENABLE__SHIFT 2 -#define S5P_SROM_BW__BYTEENABLE__SHIFT 3 - -#define S5P_SROM_BW__CS_MASK 0xf - -#define S5P_SROM_BW__NCS0__SHIFT 0 -#define S5P_SROM_BW__NCS1__SHIFT 4 -#define S5P_SROM_BW__NCS2__SHIFT 8 -#define S5P_SROM_BW__NCS3__SHIFT 12 -#define S5P_SROM_BW__NCS4__SHIFT 16 -#define S5P_SROM_BW__NCS5__SHIFT 20 - -/* applies to same to BCS0 - BCS3 */ - -#define S5P_SROM_BCX__PMC__SHIFT 0 -#define S5P_SROM_BCX__TACP__SHIFT 4 -#define S5P_SROM_BCX__TCAH__SHIFT 8 -#define S5P_SROM_BCX__TCOH__SHIFT 12 -#define S5P_SROM_BCX__TACC__SHIFT 16 -#define S5P_SROM_BCX__TCOS__SHIFT 24 -#define S5P_SROM_BCX__TACS__SHIFT 28 - -#endif /* __PLAT_SAMSUNG_REGS_SROM_H */ diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c index fee2b003e662..f21690937b7d 100644 --- a/arch/arm/mach-exynos/suspend.c +++ b/arch/arm/mach-exynos/suspend.c @@ -34,10 +34,11 @@ #include <asm/smp_scu.h> #include <asm/suspend.h> +#include <mach/map.h> + #include <plat/pm-common.h> #include "common.h" -#include "regs-srom.h" #define REG_TABLE_END (-1U) @@ -53,15 +54,6 @@ struct exynos_wkup_irq { u32 mask; }; -static struct sleep_save exynos_core_save[] = { - /* SROM side */ - SAVE_ITEM(S5P_SROM_BW), - SAVE_ITEM(S5P_SROM_BC0), - SAVE_ITEM(S5P_SROM_BC1), - SAVE_ITEM(S5P_SROM_BC2), - SAVE_ITEM(S5P_SROM_BC3), -}; - struct exynos_pm_data { const struct exynos_wkup_irq *wkup_irq; unsigned int wake_disable_mask; @@ -343,8 +335,6 @@ static void exynos_pm_prepare(void) /* Set wake-up mask registers */ exynos_pm_set_wakeup_mask(); - s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - exynos_pm_enter_sleep_mode(); /* ensure at least INFORM0 has the resume address */ @@ -375,8 +365,6 @@ static void exynos5420_pm_prepare(void) /* Set wake-up mask registers */ exynos_pm_set_wakeup_mask(); - s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - exynos_pmu_spare3 = pmu_raw_readl(S5P_PMU_SPARE3); /* * The cpu state needs to be saved and restored so that the @@ -467,8 +455,6 @@ static void exynos_pm_resume(void) /* For release retention */ exynos_pm_release_retention(); - s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - if (cpuid == ARM_CPU_PART_CORTEX_A9) scu_enable(S5P_VA_SCU); @@ -535,8 +521,6 @@ static void exynos5420_pm_resume(void) pmu_raw_writel(exynos_pmu_spare3, S5P_PMU_SPARE3); - s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - early_wakeup: tmp = pmu_raw_readl(EXYNOS5420_SFR_AXI_CGDIS1); diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c index 5b0e363fe5ba..2b118f20c62c 100644 --- a/arch/arm/mach-integrator/integrator_ap.c +++ b/arch/arm/mach-integrator/integrator_ap.c @@ -29,7 +29,6 @@ #include <linux/amba/kmi.h> #include <linux/io.h> #include <linux/irqchip.h> -#include <linux/mtd/physmap.h> #include <linux/platform_data/clk-integrator.h> #include <linux/of_irq.h> #include <linux/of_address.h> @@ -147,65 +146,6 @@ static int __init irq_syscore_init(void) device_initcall(irq_syscore_init); /* - * Flash handling. - */ -static int ap_flash_init(struct platform_device *dev) -{ - u32 tmp; - - writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, - ap_syscon_base + INTEGRATOR_SC_CTRLC_OFFSET); - - tmp = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET) | - INTEGRATOR_EBI_WRITE_ENABLE; - writel(tmp, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); - - if (!(readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET) - & INTEGRATOR_EBI_WRITE_ENABLE)) { - writel(0xa05f, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); - writel(tmp, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); - writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); - } - return 0; -} - -static void ap_flash_exit(struct platform_device *dev) -{ - u32 tmp; - - writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, - ap_syscon_base + INTEGRATOR_SC_CTRLC_OFFSET); - - tmp = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET) & - ~INTEGRATOR_EBI_WRITE_ENABLE; - writel(tmp, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); - - if (readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET) & - INTEGRATOR_EBI_WRITE_ENABLE) { - writel(0xa05f, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); - writel(tmp, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); - writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); - } -} - -static void ap_flash_set_vpp(struct platform_device *pdev, int on) -{ - if (on) - writel(INTEGRATOR_SC_CTRL_nFLVPPEN, - ap_syscon_base + INTEGRATOR_SC_CTRLS_OFFSET); - else - writel(INTEGRATOR_SC_CTRL_nFLVPPEN, - ap_syscon_base + INTEGRATOR_SC_CTRLC_OFFSET); -} - -static struct physmap_flash_data ap_flash_data = { - .width = 4, - .init = ap_flash_init, - .exit = ap_flash_exit, - .set_vpp = ap_flash_set_vpp, -}; - -/* * For the PL010 found in the Integrator/AP some of the UART control is * implemented in the system controller and accessed using a callback * from the driver. @@ -266,8 +206,6 @@ static struct of_dev_auxdata ap_auxdata_lookup[] __initdata = { "kmi0", NULL), OF_DEV_AUXDATA("arm,primecell", KMI1_BASE, "kmi1", NULL), - OF_DEV_AUXDATA("cfi-flash", INTEGRATOR_FLASH_BASE, - "physmap-flash", &ap_flash_data), { /* sentinel */ }, }; diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index b5fb71a36ee6..6f6b051e81e0 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -23,7 +23,6 @@ #include <linux/io.h> #include <linux/irqchip.h> #include <linux/gfp.h> -#include <linux/mtd/physmap.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -43,14 +42,8 @@ /* Base address to the CP controller */ static void __iomem *intcp_con_base; -#define INTCP_PA_FLASH_BASE 0x24000000 - #define INTCP_PA_CLCD_BASE 0xc0000000 -#define INTCP_FLASHPROG 0x04 -#define CINTEGRATOR_FLASHPROG_FLVPPEN (1 << 0) -#define CINTEGRATOR_FLASHPROG_FLWREN (1 << 1) - /* * Logical Physical * f1000000 10000000 Core module registers @@ -108,48 +101,6 @@ static void __init intcp_map_io(void) } /* - * Flash handling. - */ -static int intcp_flash_init(struct platform_device *dev) -{ - u32 val; - - val = readl(intcp_con_base + INTCP_FLASHPROG); - val |= CINTEGRATOR_FLASHPROG_FLWREN; - writel(val, intcp_con_base + INTCP_FLASHPROG); - - return 0; -} - -static void intcp_flash_exit(struct platform_device *dev) -{ - u32 val; - - val = readl(intcp_con_base + INTCP_FLASHPROG); - val &= ~(CINTEGRATOR_FLASHPROG_FLVPPEN|CINTEGRATOR_FLASHPROG_FLWREN); - writel(val, intcp_con_base + INTCP_FLASHPROG); -} - -static void intcp_flash_set_vpp(struct platform_device *pdev, int on) -{ - u32 val; - - val = readl(intcp_con_base + INTCP_FLASHPROG); - if (on) - val |= CINTEGRATOR_FLASHPROG_FLVPPEN; - else - val &= ~CINTEGRATOR_FLASHPROG_FLVPPEN; - writel(val, intcp_con_base + INTCP_FLASHPROG); -} - -static struct physmap_flash_data intcp_flash_data = { - .width = 4, - .init = intcp_flash_init, - .exit = intcp_flash_exit, - .set_vpp = intcp_flash_set_vpp, -}; - -/* * It seems that the card insertion interrupt remains active after * we've acknowledged it. We therefore ignore the interrupt, and * rely on reading it from the SIC. This also means that we must @@ -260,8 +211,6 @@ static struct of_dev_auxdata intcp_auxdata_lookup[] __initdata = { "aaci", &mmc_data), OF_DEV_AUXDATA("arm,primecell", INTCP_PA_CLCD_BASE, "clcd", &clcd_data), - OF_DEV_AUXDATA("cfi-flash", INTCP_PA_FLASH_BASE, - "physmap-flash", &intcp_flash_data), { /* sentinel */ }, }; diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c index 9c2e38d30f47..a6e3c98b95ed 100644 --- a/arch/arm/mach-mediatek/mediatek.c +++ b/arch/arm/mach-mediatek/mediatek.c @@ -29,6 +29,7 @@ static void __init mediatek_timer_init(void) void __iomem *gpt_base; if (of_machine_is_compatible("mediatek,mt6589") || + of_machine_is_compatible("mediatek,mt7623") || of_machine_is_compatible("mediatek,mt8135") || of_machine_is_compatible("mediatek,mt8127")) { /* turn on GPT6 which ungates arch timer clocks */ diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index f2bc5c353119..fe4ccb52f921 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -4,11 +4,6 @@ config ARCH_SHMOBILE config ARCH_SHMOBILE_MULTI bool -config PM_RCAR - bool - select PM - select PM_GENERIC_DOMAINS - config PM_RMOBILE bool select PM @@ -16,13 +11,15 @@ config PM_RMOBILE config ARCH_RCAR_GEN1 bool - select PM_RCAR + select PM + select PM_GENERIC_DOMAINS select RENESAS_INTC_IRQPIN select SYS_SUPPORTS_SH_TMU config ARCH_RCAR_GEN2 bool - select PM_RCAR + select PM + select PM_GENERIC_DOMAINS select RENESAS_IRQC select SYS_SUPPORTS_SH_CMT select PCI_DOMAINS if PCI diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index c9ea0e6ff4f9..fc95f7bd2dd9 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -38,7 +38,6 @@ smp-$(CONFIG_ARCH_EMEV2) += smp-emev2.o headsmp-scu.o platsmp-scu.o # PM objects obj-$(CONFIG_SUSPEND) += suspend.o -obj-$(CONFIG_PM_RCAR) += pm-rcar.o obj-$(CONFIG_PM_RMOBILE) += pm-rmobile.o obj-$(CONFIG_ARCH_RCAR_GEN2) += pm-rcar-gen2.o diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c index 14c42a1bdf1e..4174cbcbc467 100644 --- a/arch/arm/mach-shmobile/pm-r8a7779.c +++ b/arch/arm/mach-shmobile/pm-r8a7779.c @@ -9,9 +9,10 @@ * for more details. */ +#include <linux/soc/renesas/rcar-sysc.h> + #include <asm/io.h> -#include "pm-rcar.h" #include "r8a7779.h" /* SYSC */ diff --git a/arch/arm/mach-shmobile/pm-rcar-gen2.c b/arch/arm/mach-shmobile/pm-rcar-gen2.c index 6815781ad116..691ac166a277 100644 --- a/arch/arm/mach-shmobile/pm-rcar-gen2.c +++ b/arch/arm/mach-shmobile/pm-rcar-gen2.c @@ -13,9 +13,9 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/smp.h> +#include <linux/soc/renesas/rcar-sysc.h> #include <asm/io.h> #include "common.h" -#include "pm-rcar.h" #include "rcar-gen2.h" /* RST */ diff --git a/arch/arm/mach-shmobile/pm-rcar.c b/arch/arm/mach-shmobile/pm-rcar.c deleted file mode 100644 index 0af05d288b09..000000000000 --- a/arch/arm/mach-shmobile/pm-rcar.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * R-Car SYSC Power management support - * - * Copyright (C) 2014 Magnus Damm - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/mm.h> -#include <linux/spinlock.h> -#include <linux/io.h> -#include "pm-rcar.h" - -/* SYSC Common */ -#define SYSCSR 0x00 /* SYSC Status Register */ -#define SYSCISR 0x04 /* Interrupt Status Register */ -#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ -#define SYSCIER 0x0c /* Interrupt Enable Register */ -#define SYSCIMR 0x10 /* Interrupt Mask Register */ - -/* SYSC Status Register */ -#define SYSCSR_PONENB 1 /* Ready for power resume requests */ -#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ - -/* - * Power Control Register Offsets inside the register block for each domain - * Note: The "CR" registers for ARM cores exist on H1 only - * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 - */ -#define PWRSR_OFFS 0x00 /* Power Status Register */ -#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ -#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ -#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ -#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ -#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ - - -#define SYSCSR_RETRIES 100 -#define SYSCSR_DELAY_US 1 - -#define PWRER_RETRIES 100 -#define PWRER_DELAY_US 1 - -#define SYSCISR_RETRIES 1000 -#define SYSCISR_DELAY_US 1 - -static void __iomem *rcar_sysc_base; -static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ - -static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) -{ - unsigned int sr_bit, reg_offs; - int k; - - if (on) { - sr_bit = SYSCSR_PONENB; - reg_offs = PWRONCR_OFFS; - } else { - sr_bit = SYSCSR_POFFENB; - reg_offs = PWROFFCR_OFFS; - } - - /* Wait until SYSC is ready to accept a power request */ - for (k = 0; k < SYSCSR_RETRIES; k++) { - if (ioread32(rcar_sysc_base + SYSCSR) & BIT(sr_bit)) - break; - udelay(SYSCSR_DELAY_US); - } - - if (k == SYSCSR_RETRIES) - return -EAGAIN; - - /* Submit power shutoff or power resume request */ - iowrite32(BIT(sysc_ch->chan_bit), - rcar_sysc_base + sysc_ch->chan_offs + reg_offs); - - return 0; -} - -static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) -{ - unsigned int isr_mask = BIT(sysc_ch->isr_bit); - unsigned int chan_mask = BIT(sysc_ch->chan_bit); - unsigned int status; - unsigned long flags; - int ret = 0; - int k; - - spin_lock_irqsave(&rcar_sysc_lock, flags); - - iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); - - /* Submit power shutoff or resume request until it was accepted */ - for (k = 0; k < PWRER_RETRIES; k++) { - ret = rcar_sysc_pwr_on_off(sysc_ch, on); - if (ret) - goto out; - - status = ioread32(rcar_sysc_base + - sysc_ch->chan_offs + PWRER_OFFS); - if (!(status & chan_mask)) - break; - - udelay(PWRER_DELAY_US); - } - - if (k == PWRER_RETRIES) { - ret = -EIO; - goto out; - } - - /* Wait until the power shutoff or resume request has completed * */ - for (k = 0; k < SYSCISR_RETRIES; k++) { - if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask) - break; - udelay(SYSCISR_DELAY_US); - } - - if (k == SYSCISR_RETRIES) - ret = -EIO; - - iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); - - out: - spin_unlock_irqrestore(&rcar_sysc_lock, flags); - - pr_debug("sysc power domain %d: %08x -> %d\n", - sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); - return ret; -} - -int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) -{ - return rcar_sysc_power(sysc_ch, false); -} - -int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) -{ - return rcar_sysc_power(sysc_ch, true); -} - -bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) -{ - unsigned int st; - - st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); - if (st & BIT(sysc_ch->chan_bit)) - return true; - - return false; -} - -void __iomem *rcar_sysc_init(phys_addr_t base) -{ - rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); - if (!rcar_sysc_base) - panic("unable to ioremap R-Car SYSC hardware block\n"); - - return rcar_sysc_base; -} diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c index f5c31fbc10b2..c6951ee24588 100644 --- a/arch/arm/mach-shmobile/smp-r8a7779.c +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -19,13 +19,13 @@ #include <linux/spinlock.h> #include <linux/io.h> #include <linux/delay.h> +#include <linux/soc/renesas/rcar-sysc.h> #include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <asm/smp_scu.h> #include "common.h" -#include "pm-rcar.h" #include "r8a7779.h" #define AVECR IOMEM(0xfe700040) diff --git a/arch/arm/mach-shmobile/smp-r8a7790.c b/arch/arm/mach-shmobile/smp-r8a7790.c index f6426c6fdefc..28f26d5362d8 100644 --- a/arch/arm/mach-shmobile/smp-r8a7790.c +++ b/arch/arm/mach-shmobile/smp-r8a7790.c @@ -17,12 +17,12 @@ #include <linux/init.h> #include <linux/smp.h> #include <linux/io.h> +#include <linux/soc/renesas/rcar-sysc.h> #include <asm/smp_plat.h> #include "common.h" #include "platsmp-apmu.h" -#include "pm-rcar.h" #include "rcar-gen2.h" #include "r8a7790.h" diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index f3f61dbbda97..75620ae73913 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -108,19 +108,9 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) * be un-gated by un-toggling the power gate register * manually. */ - if (!tegra_pmc_cpu_is_powered(cpu)) { - ret = tegra_pmc_cpu_power_on(cpu); - if (ret) - return ret; - - /* Wait for the power to come up. */ - timeout = jiffies + msecs_to_jiffies(100); - while (!tegra_pmc_cpu_is_powered(cpu)) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - udelay(10); - } - } + ret = tegra_pmc_cpu_power_on(cpu); + if (ret) + return ret; remove_clamps: /* CPU partition is powered. Enable the CPU clock. */ diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c index dff1c0595b67..d643b9210dbd 100644 --- a/arch/arm/mach-versatile/versatile_dt.c +++ b/arch/arm/mach-versatile/versatile_dt.c @@ -32,7 +32,6 @@ #include <linux/amba/clcd.h> #include <linux/platform_data/video-clcd-versatile.h> #include <linux/amba/mmci.h> -#include <linux/mtd/physmap.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> @@ -42,27 +41,15 @@ #define __io_address(n) ((void __iomem __force *)IO_ADDRESS(n)) /* - * Memory definitions - */ -#define VERSATILE_FLASH_BASE 0x34000000 -#define VERSATILE_FLASH_SIZE SZ_64M - -/* * ------------------------------------------------------------------------ * Versatile Registers * ------------------------------------------------------------------------ */ #define VERSATILE_SYS_PCICTL_OFFSET 0x44 #define VERSATILE_SYS_MCI_OFFSET 0x48 -#define VERSATILE_SYS_FLASH_OFFSET 0x4C #define VERSATILE_SYS_CLCD_OFFSET 0x50 /* - * VERSATILE_SYS_FLASH - */ -#define VERSATILE_FLASHPROG_FLVPPEN (1 << 0) /* Enable writing to flash */ - -/* * VERSATILE peripheral addresses */ #define VERSATILE_MMCI0_BASE 0x10005000 /* MMC interface */ @@ -86,39 +73,6 @@ static void __iomem *versatile_sys_base; static void __iomem *versatile_ib2_ctrl; -static void versatile_flash_set_vpp(struct platform_device *pdev, int on) -{ - u32 val; - - val = readl(versatile_sys_base + VERSATILE_SYS_FLASH_OFFSET); - if (on) - val |= VERSATILE_FLASHPROG_FLVPPEN; - else - val &= ~VERSATILE_FLASHPROG_FLVPPEN; - writel(val, versatile_sys_base + VERSATILE_SYS_FLASH_OFFSET); -} - -static struct physmap_flash_data versatile_flash_data = { - .width = 4, - .set_vpp = versatile_flash_set_vpp, -}; - -static struct resource versatile_flash_resource = { - .start = VERSATILE_FLASH_BASE, - .end = VERSATILE_FLASH_BASE + VERSATILE_FLASH_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -struct platform_device versatile_flash_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &versatile_flash_data, - }, - .num_resources = 1, - .resource = &versatile_flash_resource, -}; - unsigned int mmc_status(struct device *dev) { struct amba_device *adev = container_of(dev, struct amba_device, dev); @@ -390,7 +344,6 @@ static void __init versatile_dt_init(void) versatile_dt_pci_init(); - platform_device_register(&versatile_flash_device); of_platform_populate(NULL, of_default_bus_match_table, versatile_auxdata_lookup, NULL); } diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h index 4ec9a7050185..b63aeebb93f3 100644 --- a/arch/arm/plat-samsung/include/plat/map-s5p.h +++ b/arch/arm/plat-samsung/include/plat/map-s5p.h @@ -18,7 +18,6 @@ #define S5P_VA_DMC0 S3C_ADDR(0x02440000) #define S5P_VA_DMC1 S3C_ADDR(0x02480000) -#define S5P_VA_SROMC S3C_ADDR(0x024C0000) #define S5P_VA_COREPERI_BASE S3C_ADDR(0x02800000) #define S5P_VA_COREPERI(x) (S5P_VA_COREPERI_BASE + (x)) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index d4a3a3133da5..c5a7de9bc783 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -48,7 +48,7 @@ config ARM_CCI5xx_PMU If unsure, say Y config ARM_CCN - bool "ARM CCN driver support" + tristate "ARM CCN driver support" depends on ARM || ARM64 depends on PERF_EVENTS help @@ -58,6 +58,7 @@ config ARM_CCN config BRCMSTB_GISB_ARB bool "Broadcom STB GISB bus arbiter" depends on ARM || MIPS + default ARCH_BRCMSTB || BMIPS_GENERIC help Driver for the Broadcom Set Top Box System-on-a-chip internal bus arbiter. This driver provides timeout and target abort error handling @@ -110,7 +111,7 @@ config OMAP_OCP2SCP config SIMPLE_PM_BUS bool "Simple Power-Managed Bus Driver" depends on OF && PM - depends on ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST help Driver for transparent busses that don't need a real driver, but where the bus controller is part of a PM domain, or under the control diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c index 7082c7268845..acc3eb542c74 100644 --- a/drivers/bus/arm-ccn.c +++ b/drivers/bus/arm-ccn.c @@ -1189,7 +1189,7 @@ static int arm_ccn_pmu_cpu_notifier(struct notifier_block *nb, perf_pmu_migrate_context(&dt->pmu, cpu, target); cpumask_set_cpu(target, &dt->cpu); if (ccn->irq) - WARN_ON(irq_set_affinity(ccn->irq, &dt->cpu) != 0); + WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0); default: break; } @@ -1278,7 +1278,7 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) /* Also make sure that the overflow interrupt is handled by this CPU */ if (ccn->irq) { - err = irq_set_affinity(ccn->irq, &ccn->dt.cpu); + err = irq_set_affinity_hint(ccn->irq, &ccn->dt.cpu); if (err) { dev_err(ccn->dev, "Failed to set interrupt affinity!\n"); goto error_set_affinity; @@ -1306,7 +1306,8 @@ static void arm_ccn_pmu_cleanup(struct arm_ccn *ccn) { int i; - irq_set_affinity(ccn->irq, cpu_possible_mask); + if (ccn->irq) + irq_set_affinity_hint(ccn->irq, NULL); unregister_cpu_notifier(&ccn->dt.cpu_nb); for (i = 0; i < ccn->num_xps; i++) writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 16f7d33421d8..c45554957499 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -201,6 +201,7 @@ source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/qcom/Kconfig" +source "drivers/clk/renesas/Kconfig" source "drivers/clk/samsung/Kconfig" source "drivers/clk/tegra/Kconfig" source "drivers/clk/ti/Kconfig" diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig new file mode 100644 index 000000000000..2115ce410cfb --- /dev/null +++ b/drivers/clk/renesas/Kconfig @@ -0,0 +1,16 @@ +config CLK_RENESAS_CPG_MSSR + bool + default y if ARCH_R8A7795 + +config CLK_RENESAS_CPG_MSTP + bool + default y if ARCH_R7S72100 + default y if ARCH_R8A73A4 + default y if ARCH_R8A7740 + default y if ARCH_R8A7778 + default y if ARCH_R8A7779 + default y if ARCH_R8A7790 + default y if ARCH_R8A7791 + default y if ARCH_R8A7793 + default y if ARCH_R8A7794 + default y if ARCH_SH73A0 diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index 7e2579b30326..ead8bb843524 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -1,13 +1,15 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o -obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o clk-mstp.o -obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o clk-mstp.o clk-div6.o -obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o clk-mstp.o clk-div6.o -obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o clk-mstp.o -obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o clk-mstp.o -obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o clk-mstp.o clk-div6.o -obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o clk-mstp.o clk-div6.o -obj-$(CONFIG_ARCH_R8A7793) += clk-rcar-gen2.o clk-mstp.o clk-div6.o -obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o clk-mstp.o clk-div6.o -obj-$(CONFIG_ARCH_R8A7795) += renesas-cpg-mssr.o \ - r8a7795-cpg-mssr.o clk-div6.o -obj-$(CONFIG_ARCH_SH73A0) += clk-sh73a0.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o +obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o +obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o +obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7793) += clk-rcar-gen2.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7795) += r8a7795-cpg-mssr.o +obj-$(CONFIG_ARCH_SH73A0) += clk-sh73a0.o clk-div6.o + +obj-$(CONFIG_CLK_RENESAS_CPG_MSSR) += renesas-cpg-mssr.o clk-div6.o +obj-$(CONFIG_CLK_RENESAS_CPG_MSTP) += clk-mstp.o diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index 3d44e183aedd..8b597b9a3804 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -243,9 +243,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) } CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init); - -#ifdef CONFIG_PM_GENERIC_DOMAINS_OF -int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev) +int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev) { struct device_node *np = dev->of_node; struct of_phandle_args clkspec; @@ -297,7 +295,7 @@ fail_put: return error; } -void cpg_mstp_detach_dev(struct generic_pm_domain *domain, struct device *dev) +void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev) { if (!list_empty(&dev->power.subsys_data->clock_list)) pm_clk_destroy(dev); @@ -326,4 +324,3 @@ void __init cpg_mstp_add_clk_domain(struct device_node *np) of_genpd_add_provider_simple(np, pd); } -#endif /* !CONFIG_PM_GENERIC_DOMAINS_OF */ diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index b2198aef5ed4..6af7f5b6e824 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -13,6 +13,7 @@ */ #include <linux/bug.h> +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/device.h> #include <linux/err.h> @@ -26,6 +27,7 @@ #include "renesas-cpg-mssr.h" +#define CPG_RCKCR 0x240 enum clk_ids { /* Core Clock Outputs exported to DT */ @@ -50,6 +52,7 @@ enum clk_ids { CLK_S3, CLK_SDSRC, CLK_SSPSRC, + CLK_RINT, /* Module Clocks */ MOD_CLK_BASE @@ -63,8 +66,12 @@ enum r8a7795_clk_types { CLK_TYPE_GEN3_PLL3, CLK_TYPE_GEN3_PLL4, CLK_TYPE_GEN3_SD, + CLK_TYPE_GEN3_R, }; +#define DEF_GEN3_SD(_name, _id, _parent, _offset) \ + DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD, _parent, .offset = _offset) + static const struct cpg_core_clk r8a7795_core_clks[] __initconst = { /* External Clock Inputs */ DEF_INPUT("extal", CLK_EXTAL), @@ -102,10 +109,10 @@ static const struct cpg_core_clk r8a7795_core_clks[] __initconst = { DEF_FIXED("s3d2", R8A7795_CLK_S3D2, CLK_S3, 2, 1), DEF_FIXED("s3d4", R8A7795_CLK_S3D4, CLK_S3, 4, 1), - DEF_SD("sd0", R8A7795_CLK_SD0, CLK_PLL1_DIV2, 0x0074), - DEF_SD("sd1", R8A7795_CLK_SD1, CLK_PLL1_DIV2, 0x0078), - DEF_SD("sd2", R8A7795_CLK_SD2, CLK_PLL1_DIV2, 0x0268), - DEF_SD("sd3", R8A7795_CLK_SD3, CLK_PLL1_DIV2, 0x026c), + DEF_GEN3_SD("sd0", R8A7795_CLK_SD0, CLK_PLL1_DIV2, 0x0074), + DEF_GEN3_SD("sd1", R8A7795_CLK_SD1, CLK_PLL1_DIV2, 0x0078), + DEF_GEN3_SD("sd2", R8A7795_CLK_SD2, CLK_PLL1_DIV2, 0x0268), + DEF_GEN3_SD("sd3", R8A7795_CLK_SD3, CLK_PLL1_DIV2, 0x026c), DEF_FIXED("cl", R8A7795_CLK_CL, CLK_PLL1_DIV2, 48, 1), DEF_FIXED("cp", R8A7795_CLK_CP, CLK_EXTAL, 2, 1), @@ -113,6 +120,11 @@ static const struct cpg_core_clk r8a7795_core_clks[] __initconst = { DEF_DIV6P1("mso", R8A7795_CLK_MSO, CLK_PLL1_DIV4, 0x014), DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV2, 0x250), DEF_DIV6P1("canfd", R8A7795_CLK_CANFD, CLK_PLL1_DIV4, 0x244), + + DEF_DIV6_RO("osc", R8A7795_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8), + DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32), + + DEF_BASE("r", R8A7795_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT), }; static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { @@ -139,6 +151,7 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { DEF_MOD("usb3-if0", 328, R8A7795_CLK_S3D1), DEF_MOD("usb-dmac0", 330, R8A7795_CLK_S3D1), DEF_MOD("usb-dmac1", 331, R8A7795_CLK_S3D1), + DEF_MOD("rwdt0", 402, R8A7795_CLK_R), DEF_MOD("intc-ex", 407, R8A7795_CLK_CP), DEF_MOD("intc-ap", 408, R8A7795_CLK_S3D1), DEF_MOD("audmac0", 502, R8A7795_CLK_S3D4), @@ -148,6 +161,7 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { DEF_MOD("hscif2", 518, R8A7795_CLK_S3D1), DEF_MOD("hscif1", 519, R8A7795_CLK_S3D1), DEF_MOD("hscif0", 520, R8A7795_CLK_S3D1), + DEF_MOD("pwm", 523, R8A7795_CLK_S3D4), DEF_MOD("fcpvd3", 600, R8A7795_CLK_S2D1), DEF_MOD("fcpvd2", 601, R8A7795_CLK_S2D1), DEF_MOD("fcpvd1", 602, R8A7795_CLK_S2D1), @@ -578,6 +592,18 @@ struct clk * __init r8a7795_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_SD: return cpg_sd_clk_register(core, base, __clk_get_name(parent)); + case CLK_TYPE_GEN3_R: + /* RINT is default. Only if EXTALR is populated, we switch to it */ + value = readl(base + CPG_RCKCR) & 0x3f; + + if (clk_get_rate(clks[CLK_EXTALR])) { + parent = clks[CLK_EXTALR]; + value |= BIT(15); + } + + writel(value, base + CPG_RCKCR); + break; + default: return ERR_PTR(-EINVAL); } diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 58e24b326a48..1f2dc3629f0e 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -15,6 +15,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/clk/renesas.h> #include <linux/device.h> #include <linux/init.h> #include <linux/mod_devicetable.h> @@ -253,7 +254,7 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, { struct clk *clk = NULL, *parent; struct device *dev = priv->dev; - unsigned int id = core->id; + unsigned int id = core->id, div = core->div; const char *parent_name; WARN_DEBUG(id >= priv->num_core_clks); @@ -266,6 +267,7 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_FF: case CLK_TYPE_DIV6P1: + case CLK_TYPE_DIV6_RO: WARN_DEBUG(core->parent >= priv->num_core_clks); parent = priv->clks[core->parent]; if (IS_ERR(parent)) { @@ -274,13 +276,18 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, } parent_name = __clk_get_name(parent); - if (core->type == CLK_TYPE_FF) { - clk = clk_register_fixed_factor(NULL, core->name, - parent_name, 0, - core->mult, core->div); - } else { + + if (core->type == CLK_TYPE_DIV6_RO) + /* Multiply with the DIV6 register value */ + div *= (readl(priv->base + core->offset) & 0x3f) + 1; + + if (core->type == CLK_TYPE_DIV6P1) { clk = cpg_div6_register(core->name, 1, &parent_name, priv->base + core->offset); + } else { + clk = clk_register_fixed_factor(NULL, core->name, + parent_name, 0, + core->mult, div); } break; @@ -375,8 +382,6 @@ fail: kfree(clock); } - -#ifdef CONFIG_PM_GENERIC_DOMAINS_OF struct cpg_mssr_clk_domain { struct generic_pm_domain genpd; struct device_node *np; @@ -384,6 +389,8 @@ struct cpg_mssr_clk_domain { unsigned int core_pm_clks[0]; }; +static struct cpg_mssr_clk_domain *cpg_mssr_clk_domain; + static bool cpg_mssr_is_pm_clk(const struct of_phandle_args *clkspec, struct cpg_mssr_clk_domain *pd) { @@ -407,17 +414,20 @@ static bool cpg_mssr_is_pm_clk(const struct of_phandle_args *clkspec, } } -static int cpg_mssr_attach_dev(struct generic_pm_domain *genpd, - struct device *dev) +int cpg_mssr_attach_dev(struct generic_pm_domain *unused, struct device *dev) { - struct cpg_mssr_clk_domain *pd = - container_of(genpd, struct cpg_mssr_clk_domain, genpd); + struct cpg_mssr_clk_domain *pd = cpg_mssr_clk_domain; struct device_node *np = dev->of_node; struct of_phandle_args clkspec; struct clk *clk; int i = 0; int error; + if (!pd) { + dev_dbg(dev, "CPG/MSSR clock domain not yet available\n"); + return -EPROBE_DEFER; + } + while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, &clkspec)) { if (cpg_mssr_is_pm_clk(&clkspec, pd)) @@ -457,8 +467,7 @@ fail_put: return error; } -static void cpg_mssr_detach_dev(struct generic_pm_domain *genpd, - struct device *dev) +void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev) { if (!list_empty(&dev->power.subsys_data->clock_list)) pm_clk_destroy(dev); @@ -487,19 +496,11 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev, pm_genpd_init(genpd, &simple_qos_governor, false); genpd->attach_dev = cpg_mssr_attach_dev; genpd->detach_dev = cpg_mssr_detach_dev; + cpg_mssr_clk_domain = pd; of_genpd_add_provider_simple(np, genpd); return 0; } -#else -static inline int cpg_mssr_add_clk_domain(struct device *dev, - const unsigned int *core_pm_clks, - unsigned int num_core_pm_clks) -{ - return 0; -} -#endif /* !CONFIG_PM_GENERIC_DOMAINS_OF */ - static const struct of_device_id cpg_mssr_match[] = { #ifdef CONFIG_ARCH_R8A7795 diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 952b6957233b..0d1e3e811e79 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -37,6 +37,7 @@ enum clk_types { CLK_TYPE_IN, /* External Clock Input */ CLK_TYPE_FF, /* Fixed Factor Clock */ CLK_TYPE_DIV6P1, /* DIV6 Clock with 1 parent clock */ + CLK_TYPE_DIV6_RO, /* DIV6 Clock read only with extra divisor */ /* Custom definitions start here */ CLK_TYPE_CUSTOM, @@ -53,9 +54,8 @@ enum clk_types { DEF_BASE(_name, _id, CLK_TYPE_FF, _parent, .div = _div, .mult = _mult) #define DEF_DIV6P1(_name, _id, _parent, _offset) \ DEF_BASE(_name, _id, CLK_TYPE_DIV6P1, _parent, .offset = _offset) -#define DEF_SD(_name, _id, _parent, _offset) \ - DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD, _parent, .offset = _offset) - +#define DEF_DIV6_RO(_name, _id, _parent, _offset, _div) \ + DEF_BASE(_name, _id, CLK_TYPE_DIV6_RO, _parent, .offset = _offset, .div = _div, .mult = 1) /* * Definitions of Module Clocks diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 637041fd53ad..3d0edee1f9fe 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -175,6 +175,19 @@ #define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) #define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) +#define SATA_PLL_CFG0 0x490 +#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) +#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET BIT(2) +#define SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13) +#define SATA_PLL_CFG0_SEQ_ENABLE BIT(24) + +#define XUSBIO_PLL_CFG0 0x51c +#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) +#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2) +#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6) +#define XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13) +#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24) + #define UTMIPLL_HW_PWRDN_CFG0 0x52c #define UTMIPLL_HW_PWRDN_CFG0_UTMIPLL_LOCK BIT(31) #define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25) @@ -416,6 +429,51 @@ static const char *mux_pllmcp_clkm[] = { #define PLLU_MISC0_WRITE_MASK 0xbfffffff #define PLLU_MISC1_WRITE_MASK 0x00000007 +void tegra210_xusb_pll_hw_control_enable(void) +{ + u32 val; + + val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0); + val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL | + XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL); + val |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET | + XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ; + writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_control_enable); + +void tegra210_xusb_pll_hw_sequence_start(void) +{ + u32 val; + + val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0); + val |= XUSBIO_PLL_CFG0_SEQ_ENABLE; + writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_sequence_start); + +void tegra210_sata_pll_hw_control_enable(void) +{ + u32 val; + + val = readl_relaxed(clk_base + SATA_PLL_CFG0); + val &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL; + val |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET | + SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ; + writel_relaxed(val, clk_base + SATA_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_control_enable); + +void tegra210_sata_pll_hw_sequence_start(void) +{ + u32 val; + + val = readl_relaxed(clk_base + SATA_PLL_CFG0); + val |= SATA_PLL_CFG0_SEQ_ENABLE; + writel_relaxed(val, clk_base + SATA_PLL_CFG0); +} +EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_sequence_start); + static inline void _pll_misc_chk_default(void __iomem *base, struct tegra_clk_pll_params *params, u8 misc_num, u32 default_val, u32 mask) diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index fa4ea22ca12e..03e04582791c 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -91,7 +91,7 @@ static inline bool psci_has_ext_power_state(void) PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; } -bool psci_power_state_loses_context(u32 state) +static inline bool psci_power_state_loses_context(u32 state) { const u32 mask = psci_has_ext_power_state() ? PSCI_1_0_EXT_POWER_STATE_TYPE_MASK : @@ -100,7 +100,7 @@ bool psci_power_state_loses_context(u32 state) return state & mask; } -bool psci_power_state_is_valid(u32 state) +static inline bool psci_power_state_is_valid(u32 state) { const u32 valid_mask = psci_has_ext_power_state() ? PSCI_1_0_EXT_POWER_STATE_MASK : @@ -563,7 +563,7 @@ out_put_node: return err; } -static const struct of_device_id const psci_of_match[] __initconst = { +static const struct of_device_id psci_of_match[] __initconst = { { .compatible = "arm,psci", .data = psci_0_1_init}, { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, { .compatible = "arm,psci-1.0", .data = psci_0_2_init}, diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 8a10f5b7d9dc..f52d6cb24ff5 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -121,7 +121,7 @@ struct tegra_dc { spinlock_t lock; struct drm_crtc base; - int powergate; + unsigned int powergate; int pipe; struct clk *clk; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 51d5cd20c26a..c61a284133e0 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -122,6 +122,7 @@ config MTK_SMI mainly help enable/disable iommu and control the power domain and clocks for each local arbiter. +source "drivers/memory/samsung/Kconfig" source "drivers/memory/tegra/Kconfig" endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 890bdf402449..cb0b7a1df11a 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o obj-$(CONFIG_MTK_SMI) += mtk-smi.o +obj-$(CONFIG_SAMSUNG_MC) += samsung/ obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/samsung/Kconfig b/drivers/memory/samsung/Kconfig new file mode 100644 index 000000000000..9de12222061c --- /dev/null +++ b/drivers/memory/samsung/Kconfig @@ -0,0 +1,13 @@ +config SAMSUNG_MC + bool "Samsung Exynos Memory Controller support" if COMPILE_TEST + help + Support for the Memory Controller (MC) devices found on + Samsung Exynos SoCs. + +if SAMSUNG_MC + +config EXYNOS_SROM + bool "Exynos SROM controller driver" if COMPILE_TEST + depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM) + +endif diff --git a/drivers/memory/samsung/Makefile b/drivers/memory/samsung/Makefile new file mode 100644 index 000000000000..9c554d5522ad --- /dev/null +++ b/drivers/memory/samsung/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c new file mode 100644 index 000000000000..96756fb4d6bd --- /dev/null +++ b/drivers/memory/samsung/exynos-srom.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS - SROM Controller support + * Author: Pankaj Dubey <pankaj.dubey@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "exynos-srom.h" + +static const unsigned long exynos_srom_offsets[] = { + /* SROM side */ + EXYNOS_SROM_BW, + EXYNOS_SROM_BC0, + EXYNOS_SROM_BC1, + EXYNOS_SROM_BC2, + EXYNOS_SROM_BC3, +}; + +/** + * struct exynos_srom_reg_dump: register dump of SROM Controller registers. + * @offset: srom register offset from the controller base address. + * @value: the value of register under the offset. + */ +struct exynos_srom_reg_dump { + u32 offset; + u32 value; +}; + +/** + * struct exynos_srom: platform data for exynos srom controller driver. + * @dev: platform device pointer + * @reg_base: srom base address + * @reg_offset: exynos_srom_reg_dump pointer to hold offset and its value. + */ +struct exynos_srom { + struct device *dev; + void __iomem *reg_base; + struct exynos_srom_reg_dump *reg_offset; +}; + +static struct exynos_srom_reg_dump *exynos_srom_alloc_reg_dump( + const unsigned long *rdump, + unsigned long nr_rdump) +{ + struct exynos_srom_reg_dump *rd; + unsigned int i; + + rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); + if (!rd) + return NULL; + + for (i = 0; i < nr_rdump; ++i) + rd[i].offset = rdump[i]; + + return rd; +} + +static int exynos_srom_configure_bank(struct exynos_srom *srom, + struct device_node *np) +{ + u32 bank, width, pmc = 0; + u32 timing[6]; + u32 cs, bw; + + if (of_property_read_u32(np, "reg", &bank)) + return -EINVAL; + if (of_property_read_u32(np, "reg-io-width", &width)) + width = 1; + if (of_property_read_bool(np, "samsung,srom-page-mode")) + pmc = 1 << EXYNOS_SROM_BCX__PMC__SHIFT; + if (of_property_read_u32_array(np, "samsung,srom-timing", timing, + ARRAY_SIZE(timing))) + return -EINVAL; + + bank *= 4; /* Convert bank into shift/offset */ + + cs = 1 << EXYNOS_SROM_BW__BYTEENABLE__SHIFT; + if (width == 2) + cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; + + bw = __raw_readl(srom->reg_base + EXYNOS_SROM_BW); + bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); + __raw_writel(bw, srom->reg_base + EXYNOS_SROM_BW); + + __raw_writel(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | + (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | + (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | + (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | + (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | + (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), + srom->reg_base + EXYNOS_SROM_BC0 + bank); + + return 0; +} + +static int exynos_srom_probe(struct platform_device *pdev) +{ + struct device_node *np, *child; + struct exynos_srom *srom; + struct device *dev = &pdev->dev; + bool bad_bank_config = false; + + np = dev->of_node; + if (!np) { + dev_err(&pdev->dev, "could not find device info\n"); + return -EINVAL; + } + + srom = devm_kzalloc(&pdev->dev, + sizeof(struct exynos_srom), GFP_KERNEL); + if (!srom) + return -ENOMEM; + + srom->dev = dev; + srom->reg_base = of_iomap(np, 0); + if (!srom->reg_base) { + dev_err(&pdev->dev, "iomap of exynos srom controller failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, srom); + + srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, + sizeof(exynos_srom_offsets)); + if (!srom->reg_offset) { + iounmap(srom->reg_base); + return -ENOMEM; + } + + for_each_child_of_node(np, child) { + if (exynos_srom_configure_bank(srom, child)) { + dev_err(dev, + "Could not decode bank configuration for %s\n", + child->name); + bad_bank_config = true; + } + } + + /* + * If any bank failed to configure, we still provide suspend/resume, + * but do not probe child devices + */ + if (bad_bank_config) + return 0; + + return of_platform_populate(np, NULL, NULL, dev); +} + +static int exynos_srom_remove(struct platform_device *pdev) +{ + struct exynos_srom *srom = platform_get_drvdata(pdev); + + kfree(srom->reg_offset); + iounmap(srom->reg_base); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static void exynos_srom_save(void __iomem *base, + struct exynos_srom_reg_dump *rd, + unsigned int num_regs) +{ + for (; num_regs > 0; --num_regs, ++rd) + rd->value = readl(base + rd->offset); +} + +static void exynos_srom_restore(void __iomem *base, + const struct exynos_srom_reg_dump *rd, + unsigned int num_regs) +{ + for (; num_regs > 0; --num_regs, ++rd) + writel(rd->value, base + rd->offset); +} + +static int exynos_srom_suspend(struct device *dev) +{ + struct exynos_srom *srom = dev_get_drvdata(dev); + + exynos_srom_save(srom->reg_base, srom->reg_offset, + ARRAY_SIZE(exynos_srom_offsets)); + return 0; +} + +static int exynos_srom_resume(struct device *dev) +{ + struct exynos_srom *srom = dev_get_drvdata(dev); + + exynos_srom_restore(srom->reg_base, srom->reg_offset, + ARRAY_SIZE(exynos_srom_offsets)); + return 0; +} +#endif + +static const struct of_device_id of_exynos_srom_ids[] = { + { + .compatible = "samsung,exynos4210-srom", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_exynos_srom_ids); + +static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); + +static struct platform_driver exynos_srom_driver = { + .probe = exynos_srom_probe, + .remove = exynos_srom_remove, + .driver = { + .name = "exynos-srom", + .of_match_table = of_exynos_srom_ids, + .pm = &exynos_srom_pm_ops, + }, +}; +module_platform_driver(exynos_srom_driver); + +MODULE_AUTHOR("Pankaj Dubey <pankaj.dubey@samsung.com>"); +MODULE_DESCRIPTION("Exynos SROM Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/memory/samsung/exynos-srom.h b/drivers/memory/samsung/exynos-srom.h new file mode 100644 index 000000000000..34660c6a57a9 --- /dev/null +++ b/drivers/memory/samsung/exynos-srom.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Exynos SROMC register definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __EXYNOS_SROM_H +#define __EXYNOS_SROM_H __FILE__ + +#define EXYNOS_SROMREG(x) (x) + +#define EXYNOS_SROM_BW EXYNOS_SROMREG(0x0) +#define EXYNOS_SROM_BC0 EXYNOS_SROMREG(0x4) +#define EXYNOS_SROM_BC1 EXYNOS_SROMREG(0x8) +#define EXYNOS_SROM_BC2 EXYNOS_SROMREG(0xc) +#define EXYNOS_SROM_BC3 EXYNOS_SROMREG(0x10) +#define EXYNOS_SROM_BC4 EXYNOS_SROMREG(0x14) +#define EXYNOS_SROM_BC5 EXYNOS_SROMREG(0x18) + +/* one register BW holds 4 x 4-bit packed settings for NCS0 - NCS3 */ + +#define EXYNOS_SROM_BW__DATAWIDTH__SHIFT 0 +#define EXYNOS_SROM_BW__ADDRMODE__SHIFT 1 +#define EXYNOS_SROM_BW__WAITENABLE__SHIFT 2 +#define EXYNOS_SROM_BW__BYTEENABLE__SHIFT 3 + +#define EXYNOS_SROM_BW__CS_MASK 0xf + +#define EXYNOS_SROM_BW__NCS0__SHIFT 0 +#define EXYNOS_SROM_BW__NCS1__SHIFT 4 +#define EXYNOS_SROM_BW__NCS2__SHIFT 8 +#define EXYNOS_SROM_BW__NCS3__SHIFT 12 +#define EXYNOS_SROM_BW__NCS4__SHIFT 16 +#define EXYNOS_SROM_BW__NCS5__SHIFT 20 + +/* applies to same to BCS0 - BCS3 */ + +#define EXYNOS_SROM_BCX__PMC__SHIFT 0 +#define EXYNOS_SROM_BCX__TACP__SHIFT 4 +#define EXYNOS_SROM_BCX__TCAH__SHIFT 8 +#define EXYNOS_SROM_BCX__TCOH__SHIFT 12 +#define EXYNOS_SROM_BCX__TACC__SHIFT 16 +#define EXYNOS_SROM_BCX__TCOS__SHIFT 24 +#define EXYNOS_SROM_BCX__TACS__SHIFT 28 + +#endif /* __EXYNOS_SROM_H */ diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 7c95a656f9e4..392f9eff5fb7 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -74,6 +74,16 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_PHYSMAP_OF_VERSATILE + bool "Support ARM Versatile physmap OF" + depends on MTD_PHYSMAP_OF + depends on MFD_SYSCON + default y if (ARCH_INTEGRATOR || ARCH_VERSATILE || REALVIEW_DT) + help + This provides some extra DT physmap parsing for the ARM Versatile + platforms, basically to add a VPP (write protection) callback so + the flash can be taken out of write protection. + config MTD_PMC_MSP_EVM tristate "CFI Flash device mapped on PMC-Sierra MSP" depends on PMC_MSP && MTD_CFI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 141c91a5b24c..644f7d36d35d 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -18,6 +18,9 @@ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o +ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE +obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of_versatile.o +endif obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 70c453144f00..22f3858c0364 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -24,6 +24,7 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/slab.h> +#include "physmap_of_versatile.h" struct of_flash_list { struct mtd_info *mtd; @@ -240,6 +241,11 @@ static int of_flash_probe(struct platform_device *dev) info->list[i].map.size = res_size; info->list[i].map.bankwidth = be32_to_cpup(width); info->list[i].map.device_node = dp; + err = of_flash_probe_versatile(dev, dp, &info->list[i].map); + if (err) { + dev_err(&dev->dev, "Can't probe Versatile VPP\n"); + return err; + } err = -ENOMEM; info->list[i].map.virt = ioremap(info->list[i].map.phys, diff --git a/drivers/mtd/maps/physmap_of_versatile.c b/drivers/mtd/maps/physmap_of_versatile.c new file mode 100644 index 000000000000..0f39b2a015f4 --- /dev/null +++ b/drivers/mtd/maps/physmap_of_versatile.c @@ -0,0 +1,255 @@ +/* + * Versatile OF physmap driver add-on + * + * Copyright (c) 2016, Linaro Limited + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <linux/export.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/mtd/map.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include "physmap_of_versatile.h" + +static struct regmap *syscon_regmap; + +enum versatile_flashprot { + INTEGRATOR_AP_FLASHPROT, + INTEGRATOR_CP_FLASHPROT, + VERSATILE_FLASHPROT, + REALVIEW_FLASHPROT, +}; + +static const struct of_device_id syscon_match[] = { + { + .compatible = "arm,integrator-ap-syscon", + .data = (void *)INTEGRATOR_AP_FLASHPROT, + }, + { + .compatible = "arm,integrator-cp-syscon", + .data = (void *)INTEGRATOR_CP_FLASHPROT, + }, + { + .compatible = "arm,core-module-versatile", + .data = (void *)VERSATILE_FLASHPROT, + }, + { + .compatible = "arm,realview-eb-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pb1176-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pb11mp-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pba8-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pbx-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + {}, +}; + +/* + * Flash protection handling for the Integrator/AP + */ +#define INTEGRATOR_SC_CTRLS_OFFSET 0x08 +#define INTEGRATOR_SC_CTRLC_OFFSET 0x0C +#define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) +#define INTEGRATOR_SC_CTRL_FLWP BIT(2) + +#define INTEGRATOR_EBI_CSR1_OFFSET 0x04 +/* The manual says bit 2, the code says bit 3, trust the code */ +#define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) +#define INTEGRATOR_EBI_LOCK_OFFSET 0x20 +#define INTEGRATOR_EBI_LOCK_VAL 0xA05F + +static const struct of_device_id ebi_match[] = { + { .compatible = "arm,external-bus-interface"}, + { }, +}; + +static int ap_flash_init(struct platform_device *pdev) +{ + struct device_node *ebi; + static void __iomem *ebi_base; + u32 val; + int ret; + + /* Look up the EBI */ + ebi = of_find_matching_node(NULL, ebi_match); + if (!ebi) { + return -ENODEV; + } + ebi_base = of_iomap(ebi, 0); + if (!ebi_base) + return -ENODEV; + + /* Clear VPP and write protection bits */ + ret = regmap_write(syscon_regmap, + INTEGRATOR_SC_CTRLC_OFFSET, + INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); + if (ret) + dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n"); + + /* Unlock the EBI */ + writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); + + /* Enable write cycles on the EBI, CSR1 (flash) */ + val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); + val |= INTEGRATOR_EBI_WRITE_ENABLE; + writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); + + /* Lock the EBI again */ + writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); + iounmap(ebi_base); + + return 0; +} + +static void ap_flash_set_vpp(struct map_info *map, int on) +{ + int ret; + + if (on) { + ret = regmap_write(syscon_regmap, + INTEGRATOR_SC_CTRLS_OFFSET, + INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); + if (ret) + pr_err("error enabling AP VPP\n"); + } else { + ret = regmap_write(syscon_regmap, + INTEGRATOR_SC_CTRLC_OFFSET, + INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); + if (ret) + pr_err("error disabling AP VPP\n"); + } +} + +/* + * Flash protection handling for the Integrator/CP + */ + +#define INTCP_FLASHPROG_OFFSET 0x04 +#define CINTEGRATOR_FLVPPEN BIT(0) +#define CINTEGRATOR_FLWREN BIT(1) +#define CINTEGRATOR_FLMASK BIT(0)|BIT(1) + +static void cp_flash_set_vpp(struct map_info *map, int on) +{ + int ret; + + if (on) { + ret = regmap_update_bits(syscon_regmap, + INTCP_FLASHPROG_OFFSET, + CINTEGRATOR_FLMASK, + CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); + if (ret) + pr_err("error setting CP VPP\n"); + } else { + ret = regmap_update_bits(syscon_regmap, + INTCP_FLASHPROG_OFFSET, + CINTEGRATOR_FLMASK, + 0); + if (ret) + pr_err("error setting CP VPP\n"); + } +} + +/* + * Flash protection handling for the Versatiles and RealViews + */ + +#define VERSATILE_SYS_FLASH_OFFSET 0x4C + +static void versatile_flash_set_vpp(struct map_info *map, int on) +{ + int ret; + + ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, + 0x01, !!on); + if (ret) + pr_err("error setting Versatile VPP\n"); +} + +int of_flash_probe_versatile(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + struct device_node *sysnp; + const struct of_device_id *devid; + struct regmap *rmap; + static enum versatile_flashprot versatile_flashprot; + int ret; + + /* Not all flash chips use this protection line */ + if (!of_device_is_compatible(np, "arm,versatile-flash")) + return 0; + + /* For first chip probed, look up the syscon regmap */ + if (!syscon_regmap) { + sysnp = of_find_matching_node_and_match(NULL, + syscon_match, + &devid); + if (!sysnp) + return -ENODEV; + + versatile_flashprot = (enum versatile_flashprot)devid->data; + rmap = syscon_node_to_regmap(sysnp); + if (IS_ERR(rmap)) + return PTR_ERR(rmap); + + syscon_regmap = rmap; + } + + switch (versatile_flashprot) { + case INTEGRATOR_AP_FLASHPROT: + ret = ap_flash_init(pdev); + if (ret) + return ret; + map->set_vpp = ap_flash_set_vpp; + dev_info(&pdev->dev, "Integrator/AP flash protection\n"); + break; + case INTEGRATOR_CP_FLASHPROT: + map->set_vpp = cp_flash_set_vpp; + dev_info(&pdev->dev, "Integrator/CP flash protection\n"); + break; + case VERSATILE_FLASHPROT: + case REALVIEW_FLASHPROT: + map->set_vpp = versatile_flash_set_vpp; + dev_info(&pdev->dev, "versatile/realview flash protection\n"); + break; + default: + dev_info(&pdev->dev, "device marked as Versatile flash " + "but no system controller was found\n"); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_flash_probe_versatile); diff --git a/drivers/mtd/maps/physmap_of_versatile.h b/drivers/mtd/maps/physmap_of_versatile.h new file mode 100644 index 000000000000..5b86f6dc6b3d --- /dev/null +++ b/drivers/mtd/maps/physmap_of_versatile.h @@ -0,0 +1,16 @@ +#include <linux/of.h> +#include <linux/mtd/map.h> + +#ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE +int of_flash_probe_versatile(struct platform_device *pdev, + struct device_node *np, + struct map_info *map); +#else +static inline +int of_flash_probe_versatile(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + return 0; +} +#endif diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 68d1f41b3cbf..c388468c202a 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -295,6 +295,7 @@ struct tegra_pcie { struct reset_control *afi_rst; struct reset_control *pcie_xrst; + bool legacy_phy; struct phy *phy; struct tegra_msi msi; @@ -311,11 +312,14 @@ struct tegra_pcie { struct tegra_pcie_port { struct tegra_pcie *pcie; + struct device_node *np; struct list_head list; struct resource regs; void __iomem *base; unsigned int index; unsigned int lanes; + + struct phy **phys; }; struct tegra_pcie_bus { @@ -860,6 +864,128 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) return 0; } +static int tegra_pcie_phy_disable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc_data *soc = pcie->soc_data; + u32 value; + + /* disable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value &= ~(PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); + pads_writel(pcie, value, PADS_CTL); + + /* override IDDQ */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, PADS_CTL, value); + + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + usleep_range(20, 100); + + return 0; +} + +static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + unsigned int i; + int err; + + for (i = 0; i < port->lanes; i++) { + err = phy_power_on(port->phys[i]); + if (err < 0) { + dev_err(dev, "failed to power on PHY#%u: %d\n", i, + err); + return err; + } + } + + return 0; +} + +static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + unsigned int i; + int err; + + for (i = 0; i < port->lanes; i++) { + err = phy_power_off(port->phys[i]); + if (err < 0) { + dev_err(dev, "failed to power off PHY#%u: %d\n", i, + err); + return err; + } + } + + return 0; +} + +static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + int err; + + if (pcie->legacy_phy) { + if (pcie->phy) + err = phy_power_on(pcie->phy); + else + err = tegra_pcie_phy_enable(pcie); + + if (err < 0) + dev_err(pcie->dev, "failed to power on PHY: %d\n", err); + + return err; + } + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_phy_power_on(port); + if (err < 0) { + dev_err(pcie->dev, + "failed to power on PCIe port %u PHY: %d\n", + port->index, err); + return err; + } + } + + return 0; +} + +static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + int err; + + if (pcie->legacy_phy) { + if (pcie->phy) + err = phy_power_off(pcie->phy); + else + err = tegra_pcie_phy_disable(pcie); + + if (err < 0) + dev_err(pcie->dev, "failed to power off PHY: %d\n", + err); + + return err; + } + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_phy_power_off(port); + if (err < 0) { + dev_err(pcie->dev, + "failed to power off PCIe port %u PHY: %d\n", + port->index, err); + return err; + } + } + + return 0; +} + static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) { const struct tegra_pcie_soc_data *soc = pcie->soc_data; @@ -899,13 +1025,9 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); } - if (!pcie->phy) - err = tegra_pcie_phy_enable(pcie); - else - err = phy_power_on(pcie->phy); - + err = tegra_pcie_phy_power_on(pcie); if (err < 0) { - dev_err(pcie->dev, "failed to power on PHY: %d\n", err); + dev_err(pcie->dev, "failed to power on PHY(s): %d\n", err); return err; } @@ -942,9 +1064,9 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie) /* TODO: disable and unprepare clocks? */ - err = phy_power_off(pcie->phy); + err = tegra_pcie_phy_power_off(pcie); if (err < 0) - dev_warn(pcie->dev, "failed to power off PHY: %d\n", err); + dev_err(pcie->dev, "failed to power off PHY(s): %d\n", err); reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); @@ -1049,6 +1171,100 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie) return 0; } +static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie) +{ + int err; + + pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); + if (IS_ERR(pcie->phy)) { + err = PTR_ERR(pcie->phy); + dev_err(pcie->dev, "failed to get PHY: %d\n", err); + return err; + } + + err = phy_init(pcie->phy); + if (err < 0) { + dev_err(pcie->dev, "failed to initialize PHY: %d\n", err); + return err; + } + + pcie->legacy_phy = true; + + return 0; +} + +static struct phy *devm_of_phy_optional_get_index(struct device *dev, + struct device_node *np, + const char *consumer, + unsigned int index) +{ + struct phy *phy; + char *name; + + name = kasprintf(GFP_KERNEL, "%s-%u", consumer, index); + if (!name) + return ERR_PTR(-ENOMEM); + + phy = devm_of_phy_get(dev, np, name); + kfree(name); + + if (IS_ERR(phy) && PTR_ERR(phy) == -ENODEV) + phy = NULL; + + return phy; +} + +static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + struct phy *phy; + unsigned int i; + int err; + + port->phys = devm_kcalloc(dev, sizeof(phy), port->lanes, GFP_KERNEL); + if (!port->phys) + return -ENOMEM; + + for (i = 0; i < port->lanes; i++) { + phy = devm_of_phy_optional_get_index(dev, port->np, "pcie", i); + if (IS_ERR(phy)) { + dev_err(dev, "failed to get PHY#%u: %ld\n", i, + PTR_ERR(phy)); + return PTR_ERR(phy); + } + + err = phy_init(phy); + if (err < 0) { + dev_err(dev, "failed to initialize PHY#%u: %d\n", i, + err); + return err; + } + + port->phys[i] = phy; + } + + return 0; +} + +static int tegra_pcie_phys_get(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc_data *soc = pcie->soc_data; + struct device_node *np = pcie->dev->of_node; + struct tegra_pcie_port *port; + int err; + + if (!soc->has_gen2 || of_find_property(np, "phys", NULL) != NULL) + return tegra_pcie_phys_get_legacy(pcie); + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_get_phys(port); + if (err < 0) + return err; + } + + return 0; +} + static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct platform_device *pdev = to_platform_device(pcie->dev); @@ -1067,16 +1283,9 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) return err; } - pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); - if (IS_ERR(pcie->phy)) { - err = PTR_ERR(pcie->phy); - dev_err(&pdev->dev, "failed to get PHY: %d\n", err); - return err; - } - - err = phy_init(pcie->phy); + err = tegra_pcie_phys_get(pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err); + dev_err(&pdev->dev, "failed to get PHYs: %d\n", err); return err; } @@ -1752,6 +1961,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) rp->index = index; rp->lanes = value; rp->pcie = pcie; + rp->np = port; rp->base = devm_ioremap_resource(pcie->dev, &rp->regs); if (IS_ERR(rp->base)) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 26566db09de0..27e5f6ee9a2a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -421,4 +421,6 @@ config PHY_CYGNUS_PCIE Enable this to support the Broadcom Cygnus PCIe PHY. If unsure, say N. +source "drivers/phy/tegra/Kconfig" + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 24596a96a887..d4f06e69fd9a 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -52,3 +52,5 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o + +obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index e7e574dc667a..b72e9a3b6429 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -141,7 +141,7 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node) if (phy_provider->dev->of_node == node) return phy_provider; - for_each_child_of_node(phy_provider->dev->of_node, child) + for_each_child_of_node(phy_provider->children, child) if (child == node) return phy_provider; } @@ -811,24 +811,59 @@ EXPORT_SYMBOL_GPL(devm_phy_destroy); /** * __of_phy_provider_register() - create/register phy provider with the framework * @dev: struct device of the phy provider + * @children: device node containing children (if different from dev->of_node) * @owner: the module owner containing of_xlate * @of_xlate: function pointer to obtain phy instance from phy provider * * Creates struct phy_provider from dev and of_xlate function pointer. * This is used in the case of dt boot for finding the phy instance from * phy provider. + * + * If the PHY provider doesn't nest children directly but uses a separate + * child node to contain the individual children, the @children parameter + * can be used to override the default. If NULL, the default (dev->of_node) + * will be used. If non-NULL, the device node must be a child (or further + * descendant) of dev->of_node. Otherwise an ERR_PTR()-encoded -EINVAL + * error code is returned. */ struct phy_provider *__of_phy_provider_register(struct device *dev, - struct module *owner, struct phy * (*of_xlate)(struct device *dev, - struct of_phandle_args *args)) + struct device_node *children, struct module *owner, + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)) { struct phy_provider *phy_provider; + /* + * If specified, the device node containing the children must itself + * be the provider's device node or a child (or further descendant) + * thereof. + */ + if (children) { + struct device_node *parent = of_node_get(children), *next; + + while (parent) { + if (parent == dev->of_node) + break; + + next = of_get_parent(parent); + of_node_put(parent); + parent = next; + } + + if (!parent) + return ERR_PTR(-EINVAL); + + of_node_put(parent); + } else { + children = dev->of_node; + } + phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL); if (!phy_provider) return ERR_PTR(-ENOMEM); phy_provider->dev = dev; + phy_provider->children = of_node_get(children); phy_provider->owner = owner; phy_provider->of_xlate = of_xlate; @@ -854,8 +889,9 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register); * on the devres data, then, devres data is freed. */ struct phy_provider *__devm_of_phy_provider_register(struct device *dev, - struct module *owner, struct phy * (*of_xlate)(struct device *dev, - struct of_phandle_args *args)) + struct device_node *children, struct module *owner, + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)) { struct phy_provider **ptr, *phy_provider; @@ -863,7 +899,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - phy_provider = __of_phy_provider_register(dev, owner, of_xlate); + phy_provider = __of_phy_provider_register(dev, children, owner, + of_xlate); if (!IS_ERR(phy_provider)) { *ptr = phy_provider; devres_add(dev, ptr); @@ -888,6 +925,7 @@ void of_phy_provider_unregister(struct phy_provider *phy_provider) mutex_lock(&phy_provider_mutex); list_del(&phy_provider->list); + of_node_put(phy_provider->children); kfree(phy_provider); mutex_unlock(&phy_provider_mutex); } diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig new file mode 100644 index 000000000000..a3b1de953fb7 --- /dev/null +++ b/drivers/phy/tegra/Kconfig @@ -0,0 +1,8 @@ +config PHY_TEGRA_XUSB + tristate "NVIDIA Tegra XUSB pad controller driver" + depends on ARCH_TEGRA + help + Choose this option if you have an NVIDIA Tegra SoC. + + To compile this driver as a module, choose M here: the module will + be called phy-tegra-xusb. diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile new file mode 100644 index 000000000000..898589238fd9 --- /dev/null +++ b/drivers/phy/tegra/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_PHY_TEGRA_XUSB) += phy-tegra-xusb.o + +phy-tegra-xusb-y += xusb.o +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c new file mode 100644 index 000000000000..119957249a51 --- /dev/null +++ b/drivers/phy/tegra/xusb-tegra124.c @@ -0,0 +1,1752 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#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/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <soc/tegra/fuse.h> + +#include "xusb.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_INTERNAL(x) (1 << (((x) * 4) + 3)) +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 4) +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 4)) +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 4)) +#define XUSB_PADCTL_SS_PORT_MAP_PORT_MAP_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) +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf << 12) +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044 +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6) +#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_CDR_CNTL_VAL 0x24 +#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_EQ_VAL 0xf070 +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4 +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0xf +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL 0xf + +#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_USB3_PAD_CTL4_DFE_CNTL_VAL 0x002008ee + +#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_CTL2_SPARE_IN_VAL 0x1 + +#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_LS_RSLEW_VAL(x) ((x) ? 0x0 : 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_SLEW_VAL 0x0e +#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_DISCON_LEVEL_VAL 0x5 +#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_USB3_PAD_MUX 0x134 +#define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) +#define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (6 + (x))) + +#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 tegra124_xusb_fuse_calibration { + u32 hs_curr_level[3]; + u32 hs_iref_cap; + u32 hs_term_range_adj; + u32 hs_squelch_level; +}; + +struct tegra124_xusb_padctl { + struct tegra_xusb_padctl base; + + struct tegra124_xusb_fuse_calibration fuse; +}; + +static inline struct tegra124_xusb_padctl * +to_tegra124_xusb_padctl(struct tegra_xusb_padctl *padctl) +{ + return container_of(padctl, struct tegra124_xusb_padctl, base); +} + +static int tegra124_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) +{ + u32 value; + + mutex_lock(&padctl->lock); + + if (padctl->enable++ > 0) + goto out; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + 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_AUX_MUX_LP0_CLAMP_EN_EARLY; + 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_AUX_MUX_LP0_VCORE_DOWN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + +out: + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra124_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) +{ + u32 value; + + mutex_lock(&padctl->lock); + + if (WARN_ON(padctl->enable == 0)) + goto out; + + if (--padctl->enable > 0) + goto out; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + 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_AUX_MUX_LP0_CLAMP_EN_EARLY; + 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_AUX_MUX_LP0_CLAMP_EN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + +out: + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra124_usb3_save_context(struct tegra_xusb_padctl *padctl, + unsigned int index) +{ + struct tegra_xusb_usb3_port *port; + struct tegra_xusb_lane *lane; + u32 value, offset; + + port = tegra_xusb_find_usb3_port(padctl, index); + if (!port) + return -ENODEV; + + port->context_saved = true; + lane = port->base.lane; + + if (lane->pad == padctl->pcie) + offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane->index); + else + offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6; + + 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; + port->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 << + 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; + port->amp = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index)); + 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 |= (port->tap1 << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) | + (port->amp << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index)); + + 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; + port->ctle_g = 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; + port->ctle_z = value & + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index)); + 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 |= (port->ctle_g << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) | + (port->ctle_z << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index)); + + return 0; +} + +static int tegra124_hsic_set_idle(struct tegra_xusb_padctl *padctl, + unsigned int index, bool idle) +{ + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + + 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(index)); + + return 0; +} + +#define TEGRA124_LANE(_name, _offset, _shift, _mask, _type) \ + { \ + .name = _name, \ + .offset = _offset, \ + .shift = _shift, \ + .mask = _mask, \ + .num_funcs = ARRAY_SIZE(tegra124_##_type##_functions), \ + .funcs = tegra124_##_type##_functions, \ + } + +static const char * const tegra124_usb2_functions[] = { + "snps", + "xusb", + "uart", +}; + +static const struct tegra_xusb_lane_soc tegra124_usb2_lanes[] = { + TEGRA124_LANE("usb2-0", 0x004, 0, 0x3, usb2), + TEGRA124_LANE("usb2-1", 0x004, 2, 0x3, usb2), + TEGRA124_LANE("usb2-2", 0x004, 4, 0x3, usb2), +}; + +static struct tegra_xusb_lane * +tegra124_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_usb2_lane *usb2; + int err; + + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); + if (!usb2) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&usb2->base.list); + usb2->base.soc = &pad->soc->lanes[index]; + usb2->base.index = index; + usb2->base.pad = pad; + usb2->base.np = np; + + err = tegra_xusb_lane_parse_dt(&usb2->base, np); + if (err < 0) { + kfree(usb2); + return ERR_PTR(err); + } + + return &usb2->base; +} + +static void tegra124_usb2_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane); + + kfree(usb2); +} + +static const struct tegra_xusb_lane_ops tegra124_usb2_lane_ops = { + .probe = tegra124_usb2_lane_probe, + .remove = tegra124_usb2_lane_remove, +}; + +static int tegra124_usb2_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra124_usb2_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra124_usb2_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane); + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra124_xusb_padctl *priv; + struct tegra_xusb_usb2_port *port; + unsigned int index = lane->index; + u32 value; + int err; + + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + priv = to_tegra124_xusb_padctl(padctl); + + 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 |= (priv->fuse.hs_squelch_level << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) | + (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL << + 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(index)); + value |= XUSB_PADCTL_USB2_PORT_CAP_HOST << + XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(index); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + 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 |= (priv->fuse.hs_curr_level[index] + + usb2->hs_curr_level_offset) << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT; + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_VAL << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT; + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_VAL(index) << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + 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 |= (priv->fuse.hs_term_range_adj << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) | + (priv->fuse.hs_iref_cap << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + + err = regulator_enable(port->supply); + if (err) + return err; + + mutex_lock(&pad->lock); + + if (pad->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(&pad->lock); + return 0; +} + +static int tegra124_usb2_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port; + u32 value; + + port = tegra_xusb_find_usb2_port(padctl, lane->index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", + lane->index); + return -ENODEV; + } + + mutex_lock(&pad->lock); + + if (WARN_ON(pad->enable == 0)) + goto out; + + if (--pad->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: + regulator_disable(port->supply); + mutex_unlock(&pad->lock); + return 0; +} + +static const struct phy_ops tegra124_usb2_phy_ops = { + .init = tegra124_usb2_phy_init, + .exit = tegra124_usb2_phy_exit, + .power_on = tegra124_usb2_phy_power_on, + .power_off = tegra124_usb2_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra124_usb2_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_usb2_pad *usb2; + struct tegra_xusb_pad *pad; + int err; + + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); + if (!usb2) + return ERR_PTR(-ENOMEM); + + mutex_init(&usb2->lock); + + pad = &usb2->base; + pad->ops = &tegra124_usb2_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(usb2); + goto out; + } + + err = tegra_xusb_pad_register(pad, &tegra124_usb2_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra124_usb2_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad); + + kfree(usb2); +} + +static const struct tegra_xusb_pad_ops tegra124_usb2_ops = { + .probe = tegra124_usb2_pad_probe, + .remove = tegra124_usb2_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra124_usb2_pad = { + .name = "usb2", + .num_lanes = ARRAY_SIZE(tegra124_usb2_lanes), + .lanes = tegra124_usb2_lanes, + .ops = &tegra124_usb2_ops, +}; + +static const char * const tegra124_ulpi_functions[] = { + "snps", + "xusb", +}; + +static const struct tegra_xusb_lane_soc tegra124_ulpi_lanes[] = { + TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, ulpi), +}; + +static struct tegra_xusb_lane * +tegra124_ulpi_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_ulpi_lane *ulpi; + int err; + + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&ulpi->base.list); + ulpi->base.soc = &pad->soc->lanes[index]; + ulpi->base.index = index; + ulpi->base.pad = pad; + ulpi->base.np = np; + + err = tegra_xusb_lane_parse_dt(&ulpi->base, np); + if (err < 0) { + kfree(ulpi); + return ERR_PTR(err); + } + + return &ulpi->base; +} + +static void tegra124_ulpi_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_ulpi_lane *ulpi = to_ulpi_lane(lane); + + kfree(ulpi); +} + +static const struct tegra_xusb_lane_ops tegra124_ulpi_lane_ops = { + .probe = tegra124_ulpi_lane_probe, + .remove = tegra124_ulpi_lane_remove, +}; + +static int tegra124_ulpi_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra124_ulpi_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra124_ulpi_phy_power_on(struct phy *phy) +{ + return 0; +} + +static int tegra124_ulpi_phy_power_off(struct phy *phy) +{ + return 0; +} + +static const struct phy_ops tegra124_ulpi_phy_ops = { + .init = tegra124_ulpi_phy_init, + .exit = tegra124_ulpi_phy_exit, + .power_on = tegra124_ulpi_phy_power_on, + .power_off = tegra124_ulpi_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra124_ulpi_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_ulpi_pad *ulpi; + struct tegra_xusb_pad *pad; + int err; + + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) + return ERR_PTR(-ENOMEM); + + pad = &ulpi->base; + pad->ops = &tegra124_ulpi_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(ulpi); + goto out; + } + + err = tegra_xusb_pad_register(pad, &tegra124_ulpi_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra124_ulpi_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_ulpi_pad *ulpi = to_ulpi_pad(pad); + + kfree(ulpi); +} + +static const struct tegra_xusb_pad_ops tegra124_ulpi_ops = { + .probe = tegra124_ulpi_pad_probe, + .remove = tegra124_ulpi_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra124_ulpi_pad = { + .name = "ulpi", + .num_lanes = ARRAY_SIZE(tegra124_ulpi_lanes), + .lanes = tegra124_ulpi_lanes, + .ops = &tegra124_ulpi_ops, +}; + +static const char * const tegra124_hsic_functions[] = { + "snps", + "xusb", +}; + +static const struct tegra_xusb_lane_soc tegra124_hsic_lanes[] = { + TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, hsic), + TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, hsic), +}; + +static struct tegra_xusb_lane * +tegra124_hsic_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_hsic_lane *hsic; + int err; + + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); + if (!hsic) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&hsic->base.list); + hsic->base.soc = &pad->soc->lanes[index]; + hsic->base.index = index; + hsic->base.pad = pad; + hsic->base.np = np; + + err = tegra_xusb_lane_parse_dt(&hsic->base, np); + if (err < 0) { + kfree(hsic); + return ERR_PTR(err); + } + + return &hsic->base; +} + +static void tegra124_hsic_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane); + + kfree(hsic); +} + +static const struct tegra_xusb_lane_ops tegra124_hsic_lane_ops = { + .probe = tegra124_hsic_lane_probe, + .remove = tegra124_hsic_lane_remove, +}; + +static int tegra124_hsic_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra124_hsic_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra124_hsic_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane); + struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + int err; + + err = regulator_enable(pad->supply); + if (err) + return err; + + padctl_writel(padctl, hsic->strobe_trim, + XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + + if (hsic->auto_term) + value |= XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN; + else + value &= ~XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN; + + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + value &= ~((XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT)); + value |= (hsic->tx_rtune_n << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT) | + (hsic->tx_rtune_p << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT) | + (hsic->tx_rslew_n << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT) | + (hsic->tx_rslew_p << + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(index)); + value &= ~((XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT)); + value |= (hsic->rx_strobe_trim << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) | + (hsic->rx_data_trim << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL2(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + 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(index)); + + return 0; +} + +static int tegra124_hsic_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + 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(index)); + + regulator_disable(pad->supply); + + return 0; +} + +static const struct phy_ops tegra124_hsic_phy_ops = { + .init = tegra124_hsic_phy_init, + .exit = tegra124_hsic_phy_exit, + .power_on = tegra124_hsic_phy_power_on, + .power_off = tegra124_hsic_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra124_hsic_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_hsic_pad *hsic; + struct tegra_xusb_pad *pad; + int err; + + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); + if (!hsic) + return ERR_PTR(-ENOMEM); + + pad = &hsic->base; + pad->ops = &tegra124_hsic_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(hsic); + goto out; + } + + err = tegra_xusb_pad_register(pad, &tegra124_hsic_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra124_hsic_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_hsic_pad *hsic = to_hsic_pad(pad); + + kfree(hsic); +} + +static const struct tegra_xusb_pad_ops tegra124_hsic_ops = { + .probe = tegra124_hsic_pad_probe, + .remove = tegra124_hsic_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra124_hsic_pad = { + .name = "hsic", + .num_lanes = ARRAY_SIZE(tegra124_hsic_lanes), + .lanes = tegra124_hsic_lanes, + .ops = &tegra124_hsic_ops, +}; + +static const char * const tegra124_pcie_functions[] = { + "pcie", + "usb3-ss", + "sata", +}; + +static const struct tegra_xusb_lane_soc tegra124_pcie_lanes[] = { + TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, pcie), + TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, pcie), + TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, pcie), + TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, pcie), + TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, pcie), +}; + +static struct tegra_xusb_lane * +tegra124_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_pcie_lane *pcie; + int err; + + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&pcie->base.list); + pcie->base.soc = &pad->soc->lanes[index]; + pcie->base.index = index; + pcie->base.pad = pad; + pcie->base.np = np; + + err = tegra_xusb_lane_parse_dt(&pcie->base, np); + if (err < 0) { + kfree(pcie); + return ERR_PTR(err); + } + + return &pcie->base; +} + +static void tegra124_pcie_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_pcie_lane *pcie = to_pcie_lane(lane); + + kfree(pcie); +} + +static const struct tegra_xusb_lane_ops tegra124_pcie_lane_ops = { + .probe = tegra124_pcie_lane_probe, + .remove = tegra124_pcie_lane_remove, +}; + +static int tegra124_pcie_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra124_pcie_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra124_pcie_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned long timeout; + int err = -ETIMEDOUT; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); + value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN | + XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN | + XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + + timeout = jiffies + msecs_to_jiffies(50); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + if (value & XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) { + err = 0; + break; + } + + usleep_range(100, 200); + } + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + + return err; +} + +static int tegra124_pcie_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + + return 0; +} + +static const struct phy_ops tegra124_pcie_phy_ops = { + .init = tegra124_pcie_phy_init, + .exit = tegra124_pcie_phy_exit, + .power_on = tegra124_pcie_phy_power_on, + .power_off = tegra124_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra124_pcie_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_pcie_pad *pcie; + struct tegra_xusb_pad *pad; + int err; + + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return ERR_PTR(-ENOMEM); + + pad = &pcie->base; + pad->ops = &tegra124_pcie_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(pcie); + goto out; + } + + err = tegra_xusb_pad_register(pad, &tegra124_pcie_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra124_pcie_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(pad); + + kfree(pcie); +} + +static const struct tegra_xusb_pad_ops tegra124_pcie_ops = { + .probe = tegra124_pcie_pad_probe, + .remove = tegra124_pcie_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra124_pcie_pad = { + .name = "pcie", + .num_lanes = ARRAY_SIZE(tegra124_pcie_lanes), + .lanes = tegra124_pcie_lanes, + .ops = &tegra124_pcie_ops, +}; + +static const struct tegra_xusb_lane_soc tegra124_sata_lanes[] = { + TEGRA124_LANE("sata-0", 0x134, 26, 0x3, pcie), +}; + +static struct tegra_xusb_lane * +tegra124_sata_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_sata_lane *sata; + int err; + + sata = kzalloc(sizeof(*sata), GFP_KERNEL); + if (!sata) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&sata->base.list); + sata->base.soc = &pad->soc->lanes[index]; + sata->base.index = index; + sata->base.pad = pad; + sata->base.np = np; + + err = tegra_xusb_lane_parse_dt(&sata->base, np); + if (err < 0) { + kfree(sata); + return ERR_PTR(err); + } + + return &sata->base; +} + +static void tegra124_sata_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_sata_lane *sata = to_sata_lane(lane); + + kfree(sata); +} + +static const struct tegra_xusb_lane_ops tegra124_sata_lane_ops = { + .probe = tegra124_sata_lane_probe, + .remove = tegra124_sata_lane_remove, +}; + +static int tegra124_sata_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra124_sata_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra124_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra124_sata_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned long timeout; + int err = -ETIMEDOUT; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + timeout = jiffies + msecs_to_jiffies(50); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + if (value & XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) { + err = 0; + break; + } + + usleep_range(100, 200); + } + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + + return err; +} + +static int tegra124_sata_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + + return 0; +} + +static const struct phy_ops tegra124_sata_phy_ops = { + .init = tegra124_sata_phy_init, + .exit = tegra124_sata_phy_exit, + .power_on = tegra124_sata_phy_power_on, + .power_off = tegra124_sata_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra124_sata_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_sata_pad *sata; + struct tegra_xusb_pad *pad; + int err; + + sata = kzalloc(sizeof(*sata), GFP_KERNEL); + if (!sata) + return ERR_PTR(-ENOMEM); + + pad = &sata->base; + pad->ops = &tegra124_sata_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(sata); + goto out; + } + + err = tegra_xusb_pad_register(pad, &tegra124_sata_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra124_sata_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_sata_pad *sata = to_sata_pad(pad); + + kfree(sata); +} + +static const struct tegra_xusb_pad_ops tegra124_sata_ops = { + .probe = tegra124_sata_pad_probe, + .remove = tegra124_sata_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra124_sata_pad = { + .name = "sata", + .num_lanes = ARRAY_SIZE(tegra124_sata_lanes), + .lanes = tegra124_sata_lanes, + .ops = &tegra124_sata_ops, +}; + +static const struct tegra_xusb_pad_soc *tegra124_pads[] = { + &tegra124_usb2_pad, + &tegra124_ulpi_pad, + &tegra124_hsic_pad, + &tegra124_pcie_pad, + &tegra124_sata_pad, +}; + +static int tegra124_usb2_port_enable(struct tegra_xusb_port *port) +{ + return 0; +} + +static void tegra124_usb2_port_disable(struct tegra_xusb_port *port) +{ +} + +static struct tegra_xusb_lane * +tegra124_usb2_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_find_lane(port->padctl, "usb2", port->index); +} + +static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = { + .enable = tegra124_usb2_port_enable, + .disable = tegra124_usb2_port_disable, + .map = tegra124_usb2_port_map, +}; + +static int tegra124_ulpi_port_enable(struct tegra_xusb_port *port) +{ + return 0; +} + +static void tegra124_ulpi_port_disable(struct tegra_xusb_port *port) +{ +} + +static struct tegra_xusb_lane * +tegra124_ulpi_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_find_lane(port->padctl, "ulpi", port->index); +} + +static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = { + .enable = tegra124_ulpi_port_enable, + .disable = tegra124_ulpi_port_disable, + .map = tegra124_ulpi_port_map, +}; + +static int tegra124_hsic_port_enable(struct tegra_xusb_port *port) +{ + return 0; +} + +static void tegra124_hsic_port_disable(struct tegra_xusb_port *port) +{ +} + +static struct tegra_xusb_lane * +tegra124_hsic_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_find_lane(port->padctl, "hsic", port->index); +} + +static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = { + .enable = tegra124_hsic_port_enable, + .disable = tegra124_hsic_port_disable, + .map = tegra124_hsic_port_map, +}; + +static int tegra124_usb3_port_enable(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + struct tegra_xusb_padctl *padctl = port->padctl; + struct tegra_xusb_lane *lane = usb3->base.lane; + unsigned int index = port->index, offset; + int ret = 0; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + + if (!usb3->internal) + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + else + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); + + /* + * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks + * and conditionalize based on mux function? This seems to work, but + * might not be the exact proper sequence. + */ + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index)); + 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 |= (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) | + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_VAL << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) | + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_VAL << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT); + + if (usb3->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 |= (usb3->ctle_g << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) | + (usb3->ctle_z << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT); + } + + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index)); + + value = XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_VAL; + + if (usb3->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 |= (usb3->tap1 << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) | + (usb3->amp << + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT); + } + + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index)); + + if (lane->pad == padctl->pcie) + offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane->index); + else + offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2; + + 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 |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_VAL << + XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT; + padctl_writel(padctl, value, offset); + + if (lane->pad == padctl->pcie) + offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane->index); + else + offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5; + + 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->pad == padctl->sata) { + 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_VCORE_DOWN(index); + 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(index); + 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(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + return ret; +} + +static void tegra124_usb3_port_disable(struct tegra_xusb_port *port) +{ + struct tegra_xusb_padctl *padctl = port->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port->index); + 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(port->index); + 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->index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(port->index); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(port->index, 0x7); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); +} + +static const struct tegra_xusb_lane_map tegra124_usb3_map[] = { + { 0, "pcie", 0 }, + { 1, "pcie", 1 }, + { 1, "sata", 0 }, + { 0, NULL, 0 }, +}; + +static struct tegra_xusb_lane * +tegra124_usb3_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_port_find_lane(port, tegra124_usb3_map, "usb3-ss"); +} + +static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = { + .enable = tegra124_usb3_port_enable, + .disable = tegra124_usb3_port_disable, + .map = tegra124_usb3_port_map, +}; + +static int +tegra124_xusb_read_fuse_calibration(struct tegra124_xusb_fuse_calibration *fuse) +{ + 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 < ARRAY_SIZE(fuse->hs_curr_level); i++) { + fuse->hs_curr_level[i] = + (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) & + FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK; + } + fuse->hs_iref_cap = + (value >> FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT) & + FUSE_SKU_CALIB_HS_IREF_CAP_MASK; + fuse->hs_term_range_adj = + (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) & + FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK; + fuse->hs_squelch_level = + (value >> FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT) & + FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK; + + return 0; +} + +static struct tegra_xusb_padctl * +tegra124_xusb_padctl_probe(struct device *dev, + const struct tegra_xusb_padctl_soc *soc) +{ + struct tegra124_xusb_padctl *padctl; + int err; + + padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); + if (!padctl) + return ERR_PTR(-ENOMEM); + + padctl->base.dev = dev; + padctl->base.soc = soc; + + err = tegra124_xusb_read_fuse_calibration(&padctl->fuse); + if (err < 0) + return ERR_PTR(err); + + return &padctl->base; +} + +static void tegra124_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) +{ +} + +static const struct tegra_xusb_padctl_ops tegra124_xusb_padctl_ops = { + .probe = tegra124_xusb_padctl_probe, + .remove = tegra124_xusb_padctl_remove, + .usb3_save_context = tegra124_usb3_save_context, + .hsic_set_idle = tegra124_hsic_set_idle, +}; + +const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc = { + .num_pads = ARRAY_SIZE(tegra124_pads), + .pads = tegra124_pads, + .ports = { + .usb2 = { + .ops = &tegra124_usb2_port_ops, + .count = 3, + }, + .ulpi = { + .ops = &tegra124_ulpi_port_ops, + .count = 1, + }, + .hsic = { + .ops = &tegra124_hsic_port_ops, + .count = 2, + }, + .usb3 = { + .ops = &tegra124_usb3_port_ops, + .count = 2, + }, + }, + .ops = &tegra124_xusb_padctl_ops, +}; +EXPORT_SYMBOL_GPL(tegra124_xusb_padctl_soc); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra 124 XUSB Pad Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c new file mode 100644 index 000000000000..9d0689ebd28c --- /dev/null +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -0,0 +1,2045 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2015 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/clk.h> +#include <linux/clk/tegra.h> +#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/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <soc/tegra/fuse.h> + +#include "xusb.h" + +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) \ + ((x) ? (11 + ((x) - 1) * 6) : 0) +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7 +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf + +#define FUSE_USB_CALIB_EXT_RPD_CTRL_SHIFT 0 +#define FUSE_USB_CALIB_EXT_RPD_CTRL_MASK 0x1f + +#define XUSB_PADCTL_USB2_PAD_MUX 0x004 +#define XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT 16 +#define XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_MASK 0x3 +#define XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_XUSB 0x1 +#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT 18 +#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK 0x3 +#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB 0x1 + +#define XUSB_PADCTL_USB2_PORT_CAP 0x008 +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(x) (0x1 << ((x) * 4)) +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(x) (0x3 << ((x) * 4)) + +#define XUSB_PADCTL_SS_PORT_MAP 0x014 +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(x) (1 << (((x) * 5) + 4)) +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 5) +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 5)) +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5)) + +#define XUSB_PADCTL_ELPG_PROGRAM1 0x024 +#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) +#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) +#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN (1 << 29) +#define XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(x) (1 << (2 + (x) * 3)) +#define XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(x) \ + (1 << (1 + (x) * 3)) +#define XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(x) (1 << ((x) * 3)) + +#define XUSB_PADCTL_USB3_PAD_MUX 0x028 +#define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) +#define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x))) + +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x084 + (x) * 0x40) +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT 7 +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3 +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6) + +#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x088 + (x) * 0x40) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 29) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 27) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 26) +#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) (0x08c + (x) * 0x40) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT 26 +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_MASK 0x1f +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3 +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0xf +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) + +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11) +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 3 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL 0x7 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x7 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL 0x2 + +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 0x288 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK (1 << 26) +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT 19 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK 0x7f +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL 0x0a +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12 +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e + +#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20) +#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18) +#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA1 (1 << 17) +#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA0 (1 << 16) +#define XUSB_PADCTL_HSIC_PAD_CTL0_RPD_STROBE (1 << 15) +#define XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 (1 << 14) +#define XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 (1 << 13) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_STROBE (1 << 9) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA1 (1 << 8) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA0 (1 << 7) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_STROBE (1 << 6) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA1 (1 << 5) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA0 (1 << 4) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_STROBE (1 << 3) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA1 (1 << 2) +#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA0 (1 << 1) + +#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x304 + (x) * 0x20) +#define XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_SHIFT 0 +#define XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_MASK 0xf + +#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x308 + (x) * 0x20) +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 8 +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0xf +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0 +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0xff + +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL 0x340 +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_PD_TRK (1 << 19) +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_SHIFT 12 +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_MASK 0x7f +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_VAL 0x0a +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_SHIFT 5 +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_MASK 0x7f +#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_VAL 0x1e + +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x344 + +#define XUSB_PADCTL_UPHY_PLL_P0_CTL1 0x360 +#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT 20 +#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_MASK 0xff +#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_USB_VAL 0x19 +#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SATA_VAL 0x1e +#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_SHIFT 16 +#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK 0x3 +#define XUSB_PADCTL_UPHY_PLL_CTL1_LOCKDET_STATUS (1 << 15) +#define XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD (1 << 4) +#define XUSB_PADCTL_UPHY_PLL_CTL1_ENABLE (1 << 3) +#define XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_SHIFT 1 +#define XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_MASK 0x3 +#define XUSB_PADCTL_UPHY_PLL_CTL1_IDDQ (1 << 0) + +#define XUSB_PADCTL_UPHY_PLL_P0_CTL2 0x364 +#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT 4 +#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK 0xffffff +#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_VAL 0x136 +#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD (1 << 2) +#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE (1 << 1) +#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN (1 << 0) + +#define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c +#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN (1 << 15) +#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT 12 +#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK 0x3 +#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_USB_VAL 0x2 +#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL 0x0 +#define XUSB_PADCTL_UPHY_PLL_CTL4_REFCLKBUF_EN (1 << 8) +#define XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_SHIFT 4 +#define XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_MASK 0xf + +#define XUSB_PADCTL_UPHY_PLL_P0_CTL5 0x370 +#define XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT 16 +#define XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_MASK 0xff +#define XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_VAL 0x2a + +#define XUSB_PADCTL_UPHY_PLL_P0_CTL8 0x37c +#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE (1 << 31) +#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD (1 << 15) +#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN (1 << 13) +#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN (1 << 12) + +#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL1(x) (0x460 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_SHIFT 20 +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_MASK 0x3 +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_VAL 0x1 +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13) + +#define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860 + +#define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864 + +#define XUSB_PADCTL_UPHY_PLL_S0_CTL4 0x86c + +#define XUSB_PADCTL_UPHY_PLL_S0_CTL5 0x870 + +#define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c + +#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960 + +#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16 +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK 0x3 +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL 0x2 + +#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(x) (0xa64 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT 0 +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK 0xffff +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL 0x00fc + +#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(x) (0xa68 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL 0xc0077f1f + +#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(x) (0xa6c + (x) * 0x40) +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT 16 +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK 0xffff +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL 0x01c7 + +#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(x) (0xa74 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL 0xfcf01368 + +struct tegra210_xusb_fuse_calibration { + u32 hs_curr_level[4]; + u32 hs_term_range_adj; + u32 rpd_ctrl; +}; + +struct tegra210_xusb_padctl { + struct tegra_xusb_padctl base; + + struct tegra210_xusb_fuse_calibration fuse; +}; + +static inline struct tegra210_xusb_padctl * +to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl) +{ + return container_of(padctl, struct tegra210_xusb_padctl, base); +} + +/* must be called under padctl->lock */ +static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); + unsigned long timeout; + u32 value; + int err; + + if (pcie->enable > 0) { + pcie->enable++; + return 0; + } + + err = clk_prepare_enable(pcie->pll); + if (err < 0) + return err; + + err = reset_control_deassert(pcie->rst); + if (err < 0) + goto disable; + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + value &= ~(XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_VAL << + XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL5); + value &= ~(XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_VAL << + XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL5); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + value |= XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL4); + value &= ~((XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT) | + (XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_SHIFT)); + value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_USB_VAL << + XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT) | + XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL4); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_SHIFT) | + (XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_MASK << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT)); + value |= XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_USB_VAL << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + value &= ~(XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_MASK << + XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL4); + value |= XUSB_PADCTL_UPHY_PLL_CTL4_REFCLKBUF_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL4); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + if (value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + if (!(value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE)) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + value |= XUSB_PADCTL_UPHY_PLL_CTL1_ENABLE; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + if (value & XUSB_PADCTL_UPHY_PLL_CTL1_LOCKDET_STATUS) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN | + XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + if (value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + if (!(value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE)) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + + tegra210_xusb_pll_hw_control_enable(); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8); + + usleep_range(10, 20); + + tegra210_xusb_pll_hw_sequence_start(); + + pcie->enable++; + + return 0; + +reset: + reset_control_assert(pcie->rst); +disable: + clk_disable_unprepare(pcie->pll); + return err; +} + +static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); + + mutex_lock(&padctl->lock); + + if (WARN_ON(pcie->enable == 0)) + goto unlock; + + if (--pcie->enable > 0) + goto unlock; + + reset_control_assert(pcie->rst); + clk_disable_unprepare(pcie->pll); + +unlock: + mutex_unlock(&padctl->lock); +} + +/* must be called under padctl->lock */ +static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb) +{ + struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); + unsigned long timeout; + u32 value; + int err; + + if (sata->enable > 0) { + sata->enable++; + return 0; + } + + err = clk_prepare_enable(sata->pll); + if (err < 0) + return err; + + err = reset_control_deassert(sata->rst); + if (err < 0) + goto disable; + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + value &= ~(XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_VAL << + XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL5); + value &= ~(XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_VAL << + XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL5); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL4); + value &= ~((XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT) | + (XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_MASK << + XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_SHIFT)); + value |= XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN; + + if (usb) + value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_USB_VAL << + XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT); + else + value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL << + XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT); + + /* XXX PLL0_XDIGCLK_EN */ + /* + value &= ~(1 << 19); + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4); + */ + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_SHIFT) | + (XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_MASK << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT)); + + if (usb) + value |= XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_USB_VAL << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT; + else + value |= XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SATA_VAL << + XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT; + + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_IDDQ; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + value &= ~(XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_MASK << + XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL4); + value |= XUSB_PADCTL_UPHY_PLL_CTL4_REFCLKBUF_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + if (value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + if (!(value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE)) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + value |= XUSB_PADCTL_UPHY_PLL_CTL1_ENABLE; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + if (value & XUSB_PADCTL_UPHY_PLL_CTL1_LOCKDET_STATUS) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN | + XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + if (value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout)) { + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + if (!(value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE)) + break; + + usleep_range(10, 20); + } + + if (time_after_eq(jiffies, timeout)) { + err = -ETIMEDOUT; + goto reset; + } + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + + tegra210_sata_pll_hw_control_enable(); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8); + + usleep_range(10, 20); + + tegra210_sata_pll_hw_sequence_start(); + + sata->enable++; + + return 0; + +reset: + reset_control_assert(sata->rst); +disable: + clk_disable_unprepare(sata->pll); + return err; +} + +static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); + + mutex_lock(&padctl->lock); + + if (WARN_ON(sata->enable == 0)) + goto unlock; + + if (--sata->enable > 0) + goto unlock; + + reset_control_assert(sata->rst); + clk_disable_unprepare(sata->pll); + +unlock: + mutex_unlock(&padctl->lock); +} + +static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) +{ + u32 value; + + mutex_lock(&padctl->lock); + + if (padctl->enable++ > 0) + goto out; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + +out: + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) +{ + u32 value; + + mutex_lock(&padctl->lock); + + if (WARN_ON(padctl->enable == 0)) + goto out; + + if (--padctl->enable > 0) + goto out; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + +out: + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, + unsigned int index, bool idle) +{ + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPD_STROBE); + + if (idle) + value |= XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE; + else + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE); + + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + + return 0; +} + +static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, + unsigned int index, bool enable) +{ + struct tegra_xusb_port *port; + struct tegra_xusb_lane *lane; + u32 value, offset; + + port = tegra_xusb_find_port(padctl, "usb3", index); + if (!port) + return -ENODEV; + + lane = port->lane; + + if (lane->pad == padctl->pcie) + offset = XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL1(lane->index); + else + offset = XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1; + + value = padctl_readl(padctl, offset); + + value &= ~((XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_MASK << + XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_SHIFT) | + XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN | + XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD); + + if (!enable) { + value |= (XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_VAL << + XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_SHIFT) | + XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN | + XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD; + } + + padctl_writel(padctl, value, offset); + + return 0; +} + +#define TEGRA210_LANE(_name, _offset, _shift, _mask, _type) \ + { \ + .name = _name, \ + .offset = _offset, \ + .shift = _shift, \ + .mask = _mask, \ + .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions), \ + .funcs = tegra210_##_type##_functions, \ + } + +static const char *tegra210_usb2_functions[] = { + "snps", + "xusb", + "uart" +}; + +static const struct tegra_xusb_lane_soc tegra210_usb2_lanes[] = { + TEGRA210_LANE("usb2-0", 0x004, 0, 0x3, usb2), + TEGRA210_LANE("usb2-1", 0x004, 2, 0x3, usb2), + TEGRA210_LANE("usb2-2", 0x004, 4, 0x3, usb2), + TEGRA210_LANE("usb2-3", 0x004, 6, 0x3, usb2), +}; + +static struct tegra_xusb_lane * +tegra210_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_usb2_lane *usb2; + int err; + + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); + if (!usb2) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&usb2->base.list); + usb2->base.soc = &pad->soc->lanes[index]; + usb2->base.index = index; + usb2->base.pad = pad; + usb2->base.np = np; + + err = tegra_xusb_lane_parse_dt(&usb2->base, np); + if (err < 0) { + kfree(usb2); + return ERR_PTR(err); + } + + return &usb2->base; +} + +static void tegra210_usb2_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane); + + kfree(usb2); +} + +static const struct tegra_xusb_lane_ops tegra210_usb2_lane_ops = { + .probe = tegra210_usb2_lane_probe, + .remove = tegra210_usb2_lane_remove, +}; + +static int tegra210_usb2_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); + value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK << + XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT); + value |= XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB << + XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); + + return tegra210_xusb_padctl_enable(padctl); +} + +static int tegra210_usb2_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra210_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra210_usb2_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane); + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv; + struct tegra_xusb_usb2_port *port; + unsigned int index = lane->index; + u32 value; + int err; + + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + priv = to_tegra210_xusb_padctl(padctl); + + 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 |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT); + + if (tegra_sku_info.revision < TEGRA_REVISION_A02) + value |= + (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL << + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_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_PORTX_CAP_MASK(index); + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + 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_PD | + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 | + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI); + value |= (priv->fuse.hs_curr_level[index] + + usb2->hs_curr_level_offset) << + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + 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_RPD_CTRL_MASK << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT) | + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR | + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD | + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD); + value |= (priv->fuse.hs_term_range_adj << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) | + (priv->fuse.rpd_ctrl << + XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + + value = padctl_readl(padctl, + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); + value &= ~(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK << + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT); + value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; + padctl_writel(padctl, value, + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); + + err = regulator_enable(port->supply); + if (err) + return err; + + mutex_lock(&padctl->lock); + + if (pad->enable > 0) { + pad->enable++; + mutex_unlock(&padctl->lock); + return 0; + } + + err = clk_prepare_enable(pad->clk); + if (err) + goto disable_regulator; + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) | + (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT)); + value |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) | + (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL << + XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + + 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); + + udelay(1); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + + udelay(50); + + clk_disable_unprepare(pad->clk); + + pad->enable++; + mutex_unlock(&padctl->lock); + + return 0; + +disable_regulator: + regulator_disable(port->supply); + mutex_unlock(&padctl->lock); + return err; +} + +static int tegra210_usb2_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port; + u32 value; + + port = tegra_xusb_find_usb2_port(padctl, lane->index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", + lane->index); + return -ENODEV; + } + + mutex_lock(&padctl->lock); + + if (WARN_ON(pad->enable == 0)) + goto out; + + if (--pad->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: + regulator_disable(port->supply); + mutex_unlock(&padctl->lock); + return 0; +} + +static const struct phy_ops tegra210_usb2_phy_ops = { + .init = tegra210_usb2_phy_init, + .exit = tegra210_usb2_phy_exit, + .power_on = tegra210_usb2_phy_power_on, + .power_off = tegra210_usb2_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra210_usb2_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_usb2_pad *usb2; + struct tegra_xusb_pad *pad; + int err; + + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); + if (!usb2) + return ERR_PTR(-ENOMEM); + + pad = &usb2->base; + pad->ops = &tegra210_usb2_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(usb2); + goto out; + } + + usb2->clk = devm_clk_get(&pad->dev, "trk"); + if (IS_ERR(usb2->clk)) { + err = PTR_ERR(usb2->clk); + dev_err(&pad->dev, "failed to get trk clock: %d\n", err); + goto unregister; + } + + err = tegra_xusb_pad_register(pad, &tegra210_usb2_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra210_usb2_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad); + + kfree(usb2); +} + +static const struct tegra_xusb_pad_ops tegra210_usb2_ops = { + .probe = tegra210_usb2_pad_probe, + .remove = tegra210_usb2_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra210_usb2_pad = { + .name = "usb2", + .num_lanes = ARRAY_SIZE(tegra210_usb2_lanes), + .lanes = tegra210_usb2_lanes, + .ops = &tegra210_usb2_ops, +}; + +static const char *tegra210_hsic_functions[] = { + "snps", + "xusb", +}; + +static const struct tegra_xusb_lane_soc tegra210_hsic_lanes[] = { + TEGRA210_LANE("hsic-0", 0x004, 14, 0x1, hsic), +}; + +static struct tegra_xusb_lane * +tegra210_hsic_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_hsic_lane *hsic; + int err; + + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); + if (!hsic) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&hsic->base.list); + hsic->base.soc = &pad->soc->lanes[index]; + hsic->base.index = index; + hsic->base.pad = pad; + hsic->base.np = np; + + err = tegra_xusb_lane_parse_dt(&hsic->base, np); + if (err < 0) { + kfree(hsic); + return ERR_PTR(err); + } + + return &hsic->base; +} + +static void tegra210_hsic_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane); + + kfree(hsic); +} + +static const struct tegra_xusb_lane_ops tegra210_hsic_lane_ops = { + .probe = tegra210_hsic_lane_probe, + .remove = tegra210_hsic_lane_remove, +}; + +static int tegra210_hsic_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); + value &= ~(XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_MASK << + XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT); + value |= XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_XUSB << + XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); + + return tegra210_xusb_padctl_enable(padctl); +} + +static int tegra210_hsic_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra210_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra210_hsic_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane); + struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv; + unsigned int index = lane->index; + u32 value; + int err; + + priv = to_tegra210_xusb_padctl(padctl); + + err = regulator_enable(pad->supply); + if (err) + return err; + + padctl_writel(padctl, hsic->strobe_trim, + XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_MASK << + XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_SHIFT); + value |= (hsic->tx_rtune_p << + XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(index)); + value &= ~((XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT)); + value |= (hsic->rx_strobe_trim << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) | + (hsic->rx_data_trim << + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL2(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_STROBE | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_STROBE | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_STROBE); + value |= XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_RPD_STROBE; + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + + err = clk_prepare_enable(pad->clk); + if (err) + goto disable; + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL); + value &= ~((XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_MASK << + XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_MASK << + XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_SHIFT)); + value |= (XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_VAL << + XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_SHIFT) | + (XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_VAL << + XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_SHIFT); + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PAD_TRK_CTL); + + udelay(1); + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL); + value &= ~XUSB_PADCTL_HSIC_PAD_TRK_CTL_PD_TRK; + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PAD_TRK_CTL); + + udelay(50); + + clk_disable_unprepare(pad->clk); + + return 0; + +disable: + regulator_disable(pad->supply); + return err; +} + +static int tegra210_hsic_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index)); + value |= XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_STROBE | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_STROBE | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA0 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA1 | + XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_STROBE; + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index)); + + regulator_disable(pad->supply); + + return 0; +} + +static const struct phy_ops tegra210_hsic_phy_ops = { + .init = tegra210_hsic_phy_init, + .exit = tegra210_hsic_phy_exit, + .power_on = tegra210_hsic_phy_power_on, + .power_off = tegra210_hsic_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra210_hsic_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_hsic_pad *hsic; + struct tegra_xusb_pad *pad; + int err; + + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); + if (!hsic) + return ERR_PTR(-ENOMEM); + + pad = &hsic->base; + pad->ops = &tegra210_hsic_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(hsic); + goto out; + } + + hsic->clk = devm_clk_get(&pad->dev, "trk"); + if (IS_ERR(hsic->clk)) { + err = PTR_ERR(hsic->clk); + dev_err(&pad->dev, "failed to get trk clock: %d\n", err); + goto unregister; + } + + err = tegra_xusb_pad_register(pad, &tegra210_hsic_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra210_hsic_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_hsic_pad *hsic = to_hsic_pad(pad); + + kfree(hsic); +} + +static const struct tegra_xusb_pad_ops tegra210_hsic_ops = { + .probe = tegra210_hsic_pad_probe, + .remove = tegra210_hsic_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra210_hsic_pad = { + .name = "hsic", + .num_lanes = ARRAY_SIZE(tegra210_hsic_lanes), + .lanes = tegra210_hsic_lanes, + .ops = &tegra210_hsic_ops, +}; + +static const char *tegra210_pcie_functions[] = { + "pcie-x1", + "usb3-ss", + "sata", + "pcie-x4", +}; + +static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = { + TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, pcie), + TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, pcie), + TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, pcie), + TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, pcie), + TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, pcie), + TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, pcie), + TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie), +}; + +static struct tegra_xusb_lane * +tegra210_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_pcie_lane *pcie; + int err; + + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&pcie->base.list); + pcie->base.soc = &pad->soc->lanes[index]; + pcie->base.index = index; + pcie->base.pad = pad; + pcie->base.np = np; + + err = tegra_xusb_lane_parse_dt(&pcie->base, np); + if (err < 0) { + kfree(pcie); + return ERR_PTR(err); + } + + return &pcie->base; +} + +static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_pcie_lane *pcie = to_pcie_lane(lane); + + kfree(pcie); +} + +static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = { + .probe = tegra210_pcie_lane_probe, + .remove = tegra210_pcie_lane_remove, +}; + +static int tegra210_pcie_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra210_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra210_pcie_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra210_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra210_pcie_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + int err; + + mutex_lock(&padctl->lock); + + err = tegra210_pex_uphy_enable(padctl); + if (err < 0) + goto unlock; + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + +unlock: + mutex_unlock(&padctl->lock); + return err; +} + +static int tegra210_pcie_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + + tegra210_pex_uphy_disable(padctl); + + return 0; +} + +static const struct phy_ops tegra210_pcie_phy_ops = { + .init = tegra210_pcie_phy_init, + .exit = tegra210_pcie_phy_exit, + .power_on = tegra210_pcie_phy_power_on, + .power_off = tegra210_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra210_pcie_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_pcie_pad *pcie; + struct tegra_xusb_pad *pad; + int err; + + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return ERR_PTR(-ENOMEM); + + pad = &pcie->base; + pad->ops = &tegra210_pcie_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(pcie); + goto out; + } + + pcie->pll = devm_clk_get(&pad->dev, "pll"); + if (IS_ERR(pcie->pll)) { + err = PTR_ERR(pcie->pll); + dev_err(&pad->dev, "failed to get PLL: %d\n", err); + goto unregister; + } + + pcie->rst = devm_reset_control_get(&pad->dev, "phy"); + if (IS_ERR(pcie->rst)) { + err = PTR_ERR(pcie->rst); + dev_err(&pad->dev, "failed to get PCIe pad reset: %d\n", err); + goto unregister; + } + + err = tegra_xusb_pad_register(pad, &tegra210_pcie_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra210_pcie_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(pad); + + kfree(pcie); +} + +static const struct tegra_xusb_pad_ops tegra210_pcie_ops = { + .probe = tegra210_pcie_pad_probe, + .remove = tegra210_pcie_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra210_pcie_pad = { + .name = "pcie", + .num_lanes = ARRAY_SIZE(tegra210_pcie_lanes), + .lanes = tegra210_pcie_lanes, + .ops = &tegra210_pcie_ops, +}; + +static const struct tegra_xusb_lane_soc tegra210_sata_lanes[] = { + TEGRA210_LANE("sata-0", 0x028, 30, 0x3, pcie), +}; + +static struct tegra_xusb_lane * +tegra210_sata_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, + unsigned int index) +{ + struct tegra_xusb_sata_lane *sata; + int err; + + sata = kzalloc(sizeof(*sata), GFP_KERNEL); + if (!sata) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&sata->base.list); + sata->base.soc = &pad->soc->lanes[index]; + sata->base.index = index; + sata->base.pad = pad; + sata->base.np = np; + + err = tegra_xusb_lane_parse_dt(&sata->base, np); + if (err < 0) { + kfree(sata); + return ERR_PTR(err); + } + + return &sata->base; +} + +static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_sata_lane *sata = to_sata_lane(lane); + + kfree(sata); +} + +static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = { + .probe = tegra210_sata_lane_probe, + .remove = tegra210_sata_lane_remove, +}; + +static int tegra210_sata_phy_init(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra210_xusb_padctl_enable(lane->pad->padctl); +} + +static int tegra210_sata_phy_exit(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + return tegra210_xusb_padctl_disable(lane->pad->padctl); +} + +static int tegra210_sata_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + int err; + + mutex_lock(&padctl->lock); + + err = tegra210_sata_uphy_enable(padctl, false); + if (err < 0) + goto unlock; + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + +unlock: + mutex_unlock(&padctl->lock); + return err; +} + +static int tegra210_sata_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + + tegra210_sata_uphy_disable(lane->pad->padctl); + + return 0; +} + +static const struct phy_ops tegra210_sata_phy_ops = { + .init = tegra210_sata_phy_init, + .exit = tegra210_sata_phy_exit, + .power_on = tegra210_sata_phy_power_on, + .power_off = tegra210_sata_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct tegra_xusb_pad * +tegra210_sata_pad_probe(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np) +{ + struct tegra_xusb_sata_pad *sata; + struct tegra_xusb_pad *pad; + int err; + + sata = kzalloc(sizeof(*sata), GFP_KERNEL); + if (!sata) + return ERR_PTR(-ENOMEM); + + pad = &sata->base; + pad->ops = &tegra210_sata_lane_ops; + pad->soc = soc; + + err = tegra_xusb_pad_init(pad, padctl, np); + if (err < 0) { + kfree(sata); + goto out; + } + + sata->rst = devm_reset_control_get(&pad->dev, "phy"); + if (IS_ERR(sata->rst)) { + err = PTR_ERR(sata->rst); + dev_err(&pad->dev, "failed to get SATA pad reset: %d\n", err); + goto unregister; + } + + err = tegra_xusb_pad_register(pad, &tegra210_sata_phy_ops); + if (err < 0) + goto unregister; + + dev_set_drvdata(&pad->dev, pad); + + return pad; + +unregister: + device_unregister(&pad->dev); +out: + return ERR_PTR(err); +} + +static void tegra210_sata_pad_remove(struct tegra_xusb_pad *pad) +{ + struct tegra_xusb_sata_pad *sata = to_sata_pad(pad); + + kfree(sata); +} + +static const struct tegra_xusb_pad_ops tegra210_sata_ops = { + .probe = tegra210_sata_pad_probe, + .remove = tegra210_sata_pad_remove, +}; + +static const struct tegra_xusb_pad_soc tegra210_sata_pad = { + .name = "sata", + .num_lanes = ARRAY_SIZE(tegra210_sata_lanes), + .lanes = tegra210_sata_lanes, + .ops = &tegra210_sata_ops, +}; + +static const struct tegra_xusb_pad_soc * const tegra210_pads[] = { + &tegra210_usb2_pad, + &tegra210_hsic_pad, + &tegra210_pcie_pad, + &tegra210_sata_pad, +}; + +static int tegra210_usb2_port_enable(struct tegra_xusb_port *port) +{ + return 0; +} + +static void tegra210_usb2_port_disable(struct tegra_xusb_port *port) +{ +} + +static struct tegra_xusb_lane * +tegra210_usb2_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_find_lane(port->padctl, "usb2", port->index); +} + +static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = { + .enable = tegra210_usb2_port_enable, + .disable = tegra210_usb2_port_disable, + .map = tegra210_usb2_port_map, +}; + +static int tegra210_hsic_port_enable(struct tegra_xusb_port *port) +{ + return 0; +} + +static void tegra210_hsic_port_disable(struct tegra_xusb_port *port) +{ +} + +static struct tegra_xusb_lane * +tegra210_hsic_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_find_lane(port->padctl, "hsic", port->index); +} + +static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = { + .enable = tegra210_hsic_port_enable, + .disable = tegra210_hsic_port_disable, + .map = tegra210_hsic_port_map, +}; + +static int tegra210_usb3_port_enable(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + struct tegra_xusb_padctl *padctl = port->padctl; + struct tegra_xusb_lane *lane = usb3->base.lane; + unsigned int index = port->index; + u32 value; + int err; + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + + if (!usb3->internal) + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + else + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); + + /* + * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks + * and conditionalize based on mux function? This seems to work, but + * might not be the exact proper sequence. + */ + err = regulator_enable(usb3->supply); + if (err < 0) + return err; + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); + + padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL, + XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); + + padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL, + XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index)); + + if (lane->pad == padctl->sata) + err = tegra210_sata_uphy_enable(padctl, true); + else + err = tegra210_pex_uphy_enable(padctl); + + if (err) { + dev_err(&port->dev, "%s: failed to enable UPHY: %d\n", + __func__, err); + return err; + } + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + return 0; +} + +static void tegra210_usb3_port_disable(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + struct tegra_xusb_padctl *padctl = port->padctl; + struct tegra_xusb_lane *lane = port->lane; + unsigned int index = port->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + if (lane->pad == padctl->sata) + tegra210_sata_uphy_disable(padctl); + else + tegra210_pex_uphy_disable(padctl); + + regulator_disable(usb3->supply); + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, 0x7); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); +} + +static const struct tegra_xusb_lane_map tegra210_usb3_map[] = { + { 0, "pcie", 6 }, + { 1, "pcie", 5 }, + { 2, "pcie", 0 }, + { 2, "pcie", 3 }, + { 3, "pcie", 4 }, + { 3, "pcie", 4 }, + { 0, NULL, 0 } +}; + +static struct tegra_xusb_lane * +tegra210_usb3_port_map(struct tegra_xusb_port *port) +{ + return tegra_xusb_port_find_lane(port, tegra210_usb3_map, "usb3-ss"); +} + +static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = { + .enable = tegra210_usb3_port_enable, + .disable = tegra210_usb3_port_disable, + .map = tegra210_usb3_port_map, +}; + +static int +tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration *fuse) +{ + unsigned int i; + u32 value; + int err; + + err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(fuse->hs_curr_level); i++) { + fuse->hs_curr_level[i] = + (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) & + FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK; + } + + fuse->hs_term_range_adj = + (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) & + FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK; + + err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value); + if (err < 0) + return err; + + fuse->rpd_ctrl = + (value >> FUSE_USB_CALIB_EXT_RPD_CTRL_SHIFT) & + FUSE_USB_CALIB_EXT_RPD_CTRL_MASK; + + return 0; +} + +static struct tegra_xusb_padctl * +tegra210_xusb_padctl_probe(struct device *dev, + const struct tegra_xusb_padctl_soc *soc) +{ + struct tegra210_xusb_padctl *padctl; + int err; + + padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); + if (!padctl) + return ERR_PTR(-ENOMEM); + + padctl->base.dev = dev; + padctl->base.soc = soc; + + err = tegra210_xusb_read_fuse_calibration(&padctl->fuse); + if (err < 0) + return ERR_PTR(err); + + return &padctl->base; +} + +static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) +{ +} + +static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { + .probe = tegra210_xusb_padctl_probe, + .remove = tegra210_xusb_padctl_remove, + .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, + .hsic_set_idle = tegra210_hsic_set_idle, +}; + +const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = { + .num_pads = ARRAY_SIZE(tegra210_pads), + .pads = tegra210_pads, + .ports = { + .usb2 = { + .ops = &tegra210_usb2_port_ops, + .count = 4, + }, + .hsic = { + .ops = &tegra210_hsic_port_ops, + .count = 1, + }, + .usb3 = { + .ops = &tegra210_usb3_port_ops, + .count = 4, + }, + }, + .ops = &tegra210_xusb_padctl_ops, +}; +EXPORT_SYMBOL_GPL(tegra210_xusb_padctl_soc); + +MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); +MODULE_DESCRIPTION("NVIDIA Tegra 210 XUSB Pad Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c new file mode 100644 index 000000000000..ec83dfdbc206 --- /dev/null +++ b/drivers/phy/tegra/xusb.c @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include <soc/tegra/fuse.h> + +#include "xusb.h" + +static struct phy *tegra_xusb_pad_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct tegra_xusb_pad *pad = dev_get_drvdata(dev); + struct phy *phy = NULL; + unsigned int i; + + if (args->args_count != 0) + return ERR_PTR(-EINVAL); + + for (i = 0; i < pad->soc->num_lanes; i++) { + if (!pad->lanes[i]) + continue; + + if (pad->lanes[i]->dev.of_node == args->np) { + phy = pad->lanes[i]; + break; + } + } + + if (phy == NULL) + phy = ERR_PTR(-ENODEV); + + return phy; +} + +static const struct of_device_id tegra_xusb_padctl_of_match[] = { +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) + { + .compatible = "nvidia,tegra124-xusb-padctl", + .data = &tegra124_xusb_padctl_soc, + }, +#endif +#if defined(CONFIG_ARCH_TEGRA_210_SOC) + { + .compatible = "nvidia,tegra210-xusb-padctl", + .data = &tegra210_xusb_padctl_soc, + }, +#endif + { } +}; +MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match); + +static struct device_node * +tegra_xusb_find_pad_node(struct tegra_xusb_padctl *padctl, const char *name) +{ + /* + * of_find_node_by_name() drops a reference, so make sure to grab one. + */ + struct device_node *np = of_node_get(padctl->dev->of_node); + + np = of_find_node_by_name(np, "pads"); + if (np) + np = of_find_node_by_name(np, name); + + return np; +} + +static struct device_node * +tegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index) +{ + /* + * of_find_node_by_name() drops a reference, so make sure to grab one. + */ + struct device_node *np = of_node_get(pad->dev.of_node); + + np = of_find_node_by_name(np, "lanes"); + if (!np) + return NULL; + + return of_find_node_by_name(np, pad->soc->lanes[index].name); +} + +int tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane, + const char *function) +{ + unsigned int i; + + for (i = 0; i < lane->soc->num_funcs; i++) + if (strcmp(function, lane->soc->funcs[i]) == 0) + return i; + + return -EINVAL; +} + +int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane, + struct device_node *np) +{ + struct device *dev = &lane->pad->dev; + const char *function; + int err; + + err = of_property_read_string(np, "nvidia,function", &function); + if (err < 0) + return err; + + err = tegra_xusb_lane_lookup_function(lane, function); + if (err < 0) { + dev_err(dev, "invalid function \"%s\" for lane \"%s\"\n", + function, np->name); + return err; + } + + lane->function = err; + + return 0; +} + +static void tegra_xusb_lane_destroy(struct phy *phy) +{ + if (phy) { + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + lane->pad->ops->remove(lane); + phy_destroy(phy); + } +} + +static void tegra_xusb_pad_release(struct device *dev) +{ + struct tegra_xusb_pad *pad = to_tegra_xusb_pad(dev); + + pad->soc->ops->remove(pad); +} + +static struct device_type tegra_xusb_pad_type = { + .release = tegra_xusb_pad_release, +}; + +int tegra_xusb_pad_init(struct tegra_xusb_pad *pad, + struct tegra_xusb_padctl *padctl, + struct device_node *np) +{ + int err; + + device_initialize(&pad->dev); + INIT_LIST_HEAD(&pad->list); + pad->dev.parent = padctl->dev; + pad->dev.type = &tegra_xusb_pad_type; + pad->dev.of_node = np; + pad->padctl = padctl; + + err = dev_set_name(&pad->dev, "%s", pad->soc->name); + if (err < 0) + goto unregister; + + err = device_add(&pad->dev); + if (err < 0) + goto unregister; + + return 0; + +unregister: + device_unregister(&pad->dev); + return err; +} + +int tegra_xusb_pad_register(struct tegra_xusb_pad *pad, + const struct phy_ops *ops) +{ + struct device_node *children; + struct phy *lane; + unsigned int i; + int err; + + children = of_find_node_by_name(pad->dev.of_node, "lanes"); + if (!children) + return -ENODEV; + + pad->lanes = devm_kcalloc(&pad->dev, pad->soc->num_lanes, sizeof(lane), + GFP_KERNEL); + if (!pad->lanes) { + of_node_put(children); + return -ENOMEM; + } + + for (i = 0; i < pad->soc->num_lanes; i++) { + struct device_node *np = tegra_xusb_pad_find_phy_node(pad, i); + struct tegra_xusb_lane *lane; + + /* skip disabled lanes */ + if (!np || !of_device_is_available(np)) { + of_node_put(np); + continue; + } + + pad->lanes[i] = phy_create(&pad->dev, np, ops); + if (IS_ERR(pad->lanes[i])) { + err = PTR_ERR(pad->lanes[i]); + of_node_put(np); + goto remove; + } + + lane = pad->ops->probe(pad, np, i); + if (IS_ERR(lane)) { + phy_destroy(pad->lanes[i]); + err = PTR_ERR(lane); + goto remove; + } + + list_add_tail(&lane->list, &pad->padctl->lanes); + phy_set_drvdata(pad->lanes[i], lane); + } + + pad->provider = of_phy_provider_register_full(&pad->dev, children, + tegra_xusb_pad_of_xlate); + if (IS_ERR(pad->provider)) { + err = PTR_ERR(pad->provider); + goto remove; + } + + return 0; + +remove: + while (i--) + tegra_xusb_lane_destroy(pad->lanes[i]); + + of_node_put(children); + + return err; +} + +void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad) +{ + unsigned int i = pad->soc->num_lanes; + + of_phy_provider_unregister(pad->provider); + + while (i--) + tegra_xusb_lane_destroy(pad->lanes[i]); + + device_unregister(&pad->dev); +} + +static struct tegra_xusb_pad * +tegra_xusb_pad_create(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc) +{ + struct tegra_xusb_pad *pad; + struct device_node *np; + int err; + + np = tegra_xusb_find_pad_node(padctl, soc->name); + if (!np || !of_device_is_available(np)) + return NULL; + + pad = soc->ops->probe(padctl, soc, np); + if (IS_ERR(pad)) { + err = PTR_ERR(pad); + dev_err(padctl->dev, "failed to create pad %s: %d\n", + soc->name, err); + return ERR_PTR(err); + } + + /* XXX move this into ->probe() to avoid string comparison */ + if (strcmp(soc->name, "pcie") == 0) + padctl->pcie = pad; + + if (strcmp(soc->name, "sata") == 0) + padctl->sata = pad; + + if (strcmp(soc->name, "usb2") == 0) + padctl->usb2 = pad; + + if (strcmp(soc->name, "ulpi") == 0) + padctl->ulpi = pad; + + if (strcmp(soc->name, "hsic") == 0) + padctl->hsic = pad; + + return pad; +} + +static void __tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_pad *pad, *tmp; + + list_for_each_entry_safe_reverse(pad, tmp, &padctl->pads, list) { + list_del(&pad->list); + tegra_xusb_pad_unregister(pad); + } +} + +static void tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + __tegra_xusb_remove_pads(padctl); + mutex_unlock(&padctl->lock); +} + +static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + const struct tegra_xusb_lane_soc *soc = lane->soc; + u32 value; + + /* choose function */ + value = padctl_readl(padctl, soc->offset); + value &= ~(soc->mask << soc->shift); + value |= lane->function << soc->shift; + padctl_writel(padctl, value, soc->offset); +} + +static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) +{ + unsigned int i; + + for (i = 0; i < pad->soc->num_lanes; i++) { + struct tegra_xusb_lane *lane; + + if (pad->lanes[i]) { + lane = phy_get_drvdata(pad->lanes[i]); + tegra_xusb_lane_program(lane); + } + } +} + +static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_pad *pad; + unsigned int i; + + mutex_lock(&padctl->lock); + + for (i = 0; i < padctl->soc->num_pads; i++) { + const struct tegra_xusb_pad_soc *soc = padctl->soc->pads[i]; + int err; + + pad = tegra_xusb_pad_create(padctl, soc); + if (IS_ERR(pad)) { + err = PTR_ERR(pad); + dev_err(padctl->dev, "failed to create pad %s: %d\n", + soc->name, err); + __tegra_xusb_remove_pads(padctl); + mutex_unlock(&padctl->lock); + return err; + } + + if (!pad) + continue; + + list_add_tail(&pad->list, &padctl->pads); + } + + list_for_each_entry(pad, &padctl->pads, list) + tegra_xusb_pad_program(pad); + + mutex_unlock(&padctl->lock); + return 0; +} + +static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, + const char *function) +{ + const char *func = lane->soc->funcs[lane->function]; + + return strcmp(function, func) == 0; +} + +struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl, + const char *type, + unsigned int index) +{ + struct tegra_xusb_lane *lane, *hit = ERR_PTR(-ENODEV); + char *name; + + name = kasprintf(GFP_KERNEL, "%s-%u", type, index); + if (!name) + return ERR_PTR(-ENOMEM); + + list_for_each_entry(lane, &padctl->lanes, list) { + if (strcmp(lane->soc->name, name) == 0) { + hit = lane; + break; + } + } + + kfree(name); + return hit; +} + +struct tegra_xusb_lane * +tegra_xusb_port_find_lane(struct tegra_xusb_port *port, + const struct tegra_xusb_lane_map *map, + const char *function) +{ + struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV); + + for (map = map; map->type; map++) { + if (port->index != map->port) + continue; + + lane = tegra_xusb_find_lane(port->padctl, map->type, + map->index); + if (IS_ERR(lane)) + continue; + + if (!tegra_xusb_lane_check(lane, function)) + continue; + + if (!IS_ERR(match)) + dev_err(&port->dev, "conflicting match: %s-%u / %s\n", + map->type, map->index, match->soc->name); + else + match = lane; + } + + return match; +} + +static struct device_node * +tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type, + unsigned int index) +{ + /* + * of_find_node_by_name() drops a reference, so make sure to grab one. + */ + struct device_node *np = of_node_get(padctl->dev->of_node); + + np = of_find_node_by_name(np, "ports"); + if (np) { + char *name; + + name = kasprintf(GFP_KERNEL, "%s-%u", type, index); + np = of_find_node_by_name(np, name); + kfree(name); + } + + return np; +} + +struct tegra_xusb_port * +tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type, + unsigned int index) +{ + struct tegra_xusb_port *port; + struct device_node *np; + + np = tegra_xusb_find_port_node(padctl, type, index); + if (!np) + return NULL; + + list_for_each_entry(port, &padctl->ports, list) { + if (np == port->dev.of_node) { + of_node_put(np); + return port; + } + } + + of_node_put(np); + + return NULL; +} + +struct tegra_xusb_usb2_port * +tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index) +{ + struct tegra_xusb_port *port; + + port = tegra_xusb_find_port(padctl, "usb2", index); + if (port) + return to_usb2_port(port); + + return NULL; +} + +struct tegra_xusb_usb3_port * +tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index) +{ + struct tegra_xusb_port *port; + + port = tegra_xusb_find_port(padctl, "usb3", index); + if (port) + return to_usb3_port(port); + + return NULL; +} + +static void tegra_xusb_port_release(struct device *dev) +{ +} + +static struct device_type tegra_xusb_port_type = { + .release = tegra_xusb_port_release, +}; + +static int tegra_xusb_port_init(struct tegra_xusb_port *port, + struct tegra_xusb_padctl *padctl, + struct device_node *np, + const char *name, + unsigned int index) +{ + int err; + + INIT_LIST_HEAD(&port->list); + port->padctl = padctl; + port->index = index; + + device_initialize(&port->dev); + port->dev.type = &tegra_xusb_port_type; + port->dev.of_node = of_node_get(np); + port->dev.parent = padctl->dev; + + err = dev_set_name(&port->dev, "%s-%u", name, index); + if (err < 0) + goto unregister; + + err = device_add(&port->dev); + if (err < 0) + goto unregister; + + return 0; + +unregister: + device_unregister(&port->dev); + return err; +} + +static void tegra_xusb_port_unregister(struct tegra_xusb_port *port) +{ + device_unregister(&port->dev); +} + +static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) +{ + struct tegra_xusb_port *port = &usb2->base; + struct device_node *np = port->dev.of_node; + + usb2->internal = of_property_read_bool(np, "nvidia,internal"); + + usb2->supply = devm_regulator_get(&port->dev, "vbus"); + if (IS_ERR(usb2->supply)) + return PTR_ERR(usb2->supply); + + return 0; +} + +static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl, + unsigned int index) +{ + struct tegra_xusb_usb2_port *usb2; + struct device_node *np; + int err = 0; + + /* + * USB2 ports don't require additional properties, but if the port is + * marked as disabled there is no reason to register it. + */ + np = tegra_xusb_find_port_node(padctl, "usb2", index); + if (!np || !of_device_is_available(np)) + goto out; + + usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL); + if (!usb2) { + err = -ENOMEM; + goto out; + } + + err = tegra_xusb_port_init(&usb2->base, padctl, np, "usb2", index); + if (err < 0) + goto out; + + usb2->base.ops = padctl->soc->ports.usb2.ops; + + usb2->base.lane = usb2->base.ops->map(&usb2->base); + if (IS_ERR(usb2->base.lane)) { + err = PTR_ERR(usb2->base.lane); + goto out; + } + + err = tegra_xusb_usb2_port_parse_dt(usb2); + if (err < 0) { + tegra_xusb_port_unregister(&usb2->base); + goto out; + } + + list_add_tail(&usb2->base.list, &padctl->ports); + +out: + of_node_put(np); + return err; +} + +static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi) +{ + struct tegra_xusb_port *port = &ulpi->base; + struct device_node *np = port->dev.of_node; + + ulpi->internal = of_property_read_bool(np, "nvidia,internal"); + + return 0; +} + +static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl, + unsigned int index) +{ + struct tegra_xusb_ulpi_port *ulpi; + struct device_node *np; + int err = 0; + + np = tegra_xusb_find_port_node(padctl, "ulpi", index); + if (!np || !of_device_is_available(np)) + goto out; + + ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) { + err = -ENOMEM; + goto out; + } + + err = tegra_xusb_port_init(&ulpi->base, padctl, np, "ulpi", index); + if (err < 0) + goto out; + + ulpi->base.ops = padctl->soc->ports.ulpi.ops; + + ulpi->base.lane = ulpi->base.ops->map(&ulpi->base); + if (IS_ERR(ulpi->base.lane)) { + err = PTR_ERR(ulpi->base.lane); + goto out; + } + + err = tegra_xusb_ulpi_port_parse_dt(ulpi); + if (err < 0) { + tegra_xusb_port_unregister(&ulpi->base); + goto out; + } + + list_add_tail(&ulpi->base.list, &padctl->ports); + +out: + of_node_put(np); + return err; +} + +static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic) +{ + /* XXX */ + return 0; +} + +static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl, + unsigned int index) +{ + struct tegra_xusb_hsic_port *hsic; + struct device_node *np; + int err = 0; + + np = tegra_xusb_find_port_node(padctl, "hsic", index); + if (!np || !of_device_is_available(np)) + goto out; + + hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL); + if (!hsic) { + err = -ENOMEM; + goto out; + } + + err = tegra_xusb_port_init(&hsic->base, padctl, np, "hsic", index); + if (err < 0) + goto out; + + hsic->base.ops = padctl->soc->ports.hsic.ops; + + hsic->base.lane = hsic->base.ops->map(&hsic->base); + if (IS_ERR(hsic->base.lane)) { + err = PTR_ERR(hsic->base.lane); + goto out; + } + + err = tegra_xusb_hsic_port_parse_dt(hsic); + if (err < 0) { + tegra_xusb_port_unregister(&hsic->base); + goto out; + } + + list_add_tail(&hsic->base.list, &padctl->ports); + +out: + of_node_put(np); + return err; +} + +static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) +{ + struct tegra_xusb_port *port = &usb3->base; + struct device_node *np = port->dev.of_node; + u32 value; + int err; + + err = of_property_read_u32(np, "nvidia,usb2-companion", &value); + if (err < 0) { + dev_err(&port->dev, "failed to read port: %d\n", err); + return err; + } + + usb3->port = value; + + usb3->internal = of_property_read_bool(np, "nvidia,internal"); + + usb3->supply = devm_regulator_get(&port->dev, "vbus"); + if (IS_ERR(usb3->supply)) + return PTR_ERR(usb3->supply); + + return 0; +} + +static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl, + unsigned int index) +{ + struct tegra_xusb_usb3_port *usb3; + struct device_node *np; + int err = 0; + + /* + * If there is no supplemental configuration in the device tree the + * port is unusable. But it is valid to configure only a single port, + * hence return 0 instead of an error to allow ports to be optional. + */ + np = tegra_xusb_find_port_node(padctl, "usb3", index); + if (!np || !of_device_is_available(np)) + goto out; + + usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL); + if (!usb3) { + err = -ENOMEM; + goto out; + } + + err = tegra_xusb_port_init(&usb3->base, padctl, np, "usb3", index); + if (err < 0) + goto out; + + usb3->base.ops = padctl->soc->ports.usb3.ops; + + usb3->base.lane = usb3->base.ops->map(&usb3->base); + if (IS_ERR(usb3->base.lane)) { + err = PTR_ERR(usb3->base.lane); + goto out; + } + + err = tegra_xusb_usb3_port_parse_dt(usb3); + if (err < 0) { + tegra_xusb_port_unregister(&usb3->base); + goto out; + } + + list_add_tail(&usb3->base.list, &padctl->ports); + +out: + of_node_put(np); + return err; +} + +static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_port *port, *tmp; + + list_for_each_entry_safe_reverse(port, tmp, &padctl->ports, list) { + list_del(&port->list); + tegra_xusb_port_unregister(port); + } +} + +static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl) +{ + struct tegra_xusb_port *port; + unsigned int i; + int err = 0; + + mutex_lock(&padctl->lock); + + for (i = 0; i < padctl->soc->ports.usb2.count; i++) { + err = tegra_xusb_add_usb2_port(padctl, i); + if (err < 0) + goto remove_ports; + } + + for (i = 0; i < padctl->soc->ports.ulpi.count; i++) { + err = tegra_xusb_add_ulpi_port(padctl, i); + if (err < 0) + goto remove_ports; + } + + for (i = 0; i < padctl->soc->ports.hsic.count; i++) { + err = tegra_xusb_add_hsic_port(padctl, i); + if (err < 0) + goto remove_ports; + } + + for (i = 0; i < padctl->soc->ports.usb3.count; i++) { + err = tegra_xusb_add_usb3_port(padctl, i); + if (err < 0) + goto remove_ports; + } + + list_for_each_entry(port, &padctl->ports, list) { + err = port->ops->enable(port); + if (err < 0) + dev_err(padctl->dev, "failed to enable port %s: %d\n", + dev_name(&port->dev), err); + } + + goto unlock; + +remove_ports: + __tegra_xusb_remove_ports(padctl); +unlock: + mutex_unlock(&padctl->lock); + return err; +} + +static void tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + __tegra_xusb_remove_ports(padctl); + mutex_unlock(&padctl->lock); +} + +static int tegra_xusb_padctl_probe(struct platform_device *pdev) +{ + struct device_node *np = of_node_get(pdev->dev.of_node); + const struct tegra_xusb_padctl_soc *soc; + struct tegra_xusb_padctl *padctl; + const struct of_device_id *match; + struct resource *res; + int err; + + /* for backwards compatibility with old device trees */ + np = of_find_node_by_name(np, "pads"); + if (!np) { + dev_warn(&pdev->dev, "deprecated DT, using legacy driver\n"); + return tegra_xusb_padctl_legacy_probe(pdev); + } + + of_node_put(np); + + match = of_match_node(tegra_xusb_padctl_of_match, pdev->dev.of_node); + soc = match->data; + + padctl = soc->ops->probe(&pdev->dev, soc); + if (IS_ERR(padctl)) + return PTR_ERR(padctl); + + platform_set_drvdata(pdev, padctl); + INIT_LIST_HEAD(&padctl->ports); + INIT_LIST_HEAD(&padctl->lanes); + INIT_LIST_HEAD(&padctl->pads); + mutex_init(&padctl->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + padctl->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(padctl->regs)) { + err = PTR_ERR(padctl->regs); + goto remove; + } + + padctl->rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(padctl->rst)) { + err = PTR_ERR(padctl->rst); + goto remove; + } + + err = reset_control_deassert(padctl->rst); + if (err < 0) + goto remove; + + err = tegra_xusb_setup_pads(padctl); + if (err < 0) { + dev_err(&pdev->dev, "failed to setup pads: %d\n", err); + goto reset; + } + + err = tegra_xusb_setup_ports(padctl); + if (err) { + dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err); + goto remove_pads; + } + + return 0; + +remove_pads: + tegra_xusb_remove_pads(padctl); +reset: + reset_control_assert(padctl->rst); +remove: + soc->ops->remove(padctl); + return err; +} + +static int tegra_xusb_padctl_remove(struct platform_device *pdev) +{ + struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev); + int err; + + tegra_xusb_remove_ports(padctl); + tegra_xusb_remove_pads(padctl); + + err = reset_control_assert(padctl->rst); + if (err < 0) + dev_err(&pdev->dev, "failed to assert reset: %d\n", err); + + padctl->soc->ops->remove(padctl); + + return err; +} + +static struct platform_driver tegra_xusb_padctl_driver = { + .driver = { + .name = "tegra-xusb-padctl", + .of_match_table = tegra_xusb_padctl_of_match, + }, + .probe = tegra_xusb_padctl_probe, + .remove = tegra_xusb_padctl_remove, +}; +module_platform_driver(tegra_xusb_padctl_driver); + +struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev) +{ + struct tegra_xusb_padctl *padctl; + struct platform_device *pdev; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "nvidia,xusb-padctl", 0); + if (!np) + return ERR_PTR(-EINVAL); + + /* + * This is slightly ugly. A better implementation would be to keep a + * registry of pad controllers, but since there will almost certainly + * only ever be one per SoC that would be a little overkill. + */ + pdev = of_find_device_by_node(np); + if (!pdev) { + of_node_put(np); + return ERR_PTR(-ENODEV); + } + + of_node_put(np); + + padctl = platform_get_drvdata(pdev); + if (!padctl) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + return padctl; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get); + +void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl) +{ + if (padctl) + put_device(padctl->dev); +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_put); + +int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl, + unsigned int port) +{ + if (padctl->soc->ops->usb3_save_context) + return padctl->soc->ops->usb3_save_context(padctl, port); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context); + +int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, + unsigned int port, bool idle) +{ + if (padctl->soc->ops->hsic_set_idle) + return padctl->soc->ops->hsic_set_idle(padctl, port, idle); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); + +int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, + unsigned int port, bool enable) +{ + if (padctl->soc->ops->usb3_set_lfps_detect) + return padctl->soc->ops->usb3_set_lfps_detect(padctl, port, + enable); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h new file mode 100644 index 000000000000..b49dbc36efa3 --- /dev/null +++ b/drivers/phy/tegra/xusb.h @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2015, 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __PHY_TEGRA_XUSB_H +#define __PHY_TEGRA_XUSB_H + +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> + +/* legacy entry points for backwards-compatibility */ +int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev); +int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev); + +struct phy; +struct phy_provider; +struct platform_device; +struct regulator; + +/* + * lanes + */ +struct tegra_xusb_lane_soc { + const char *name; + + unsigned int offset; + unsigned int shift; + unsigned int mask; + + const char * const *funcs; + unsigned int num_funcs; +}; + +struct tegra_xusb_lane { + const struct tegra_xusb_lane_soc *soc; + struct tegra_xusb_pad *pad; + struct device_node *np; + struct list_head list; + unsigned int function; + unsigned int index; +}; + +int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane, + struct device_node *np); + +struct tegra_xusb_usb2_lane { + struct tegra_xusb_lane base; + + u32 hs_curr_level_offset; +}; + +static inline struct tegra_xusb_usb2_lane * +to_usb2_lane(struct tegra_xusb_lane *lane) +{ + return container_of(lane, struct tegra_xusb_usb2_lane, base); +} + +struct tegra_xusb_ulpi_lane { + struct tegra_xusb_lane base; +}; + +static inline struct tegra_xusb_ulpi_lane * +to_ulpi_lane(struct tegra_xusb_lane *lane) +{ + return container_of(lane, struct tegra_xusb_ulpi_lane, base); +} + +struct tegra_xusb_hsic_lane { + struct tegra_xusb_lane base; + + u32 strobe_trim; + u32 rx_strobe_trim; + u32 rx_data_trim; + u32 tx_rtune_n; + u32 tx_rtune_p; + u32 tx_rslew_n; + u32 tx_rslew_p; + bool auto_term; +}; + +static inline struct tegra_xusb_hsic_lane * +to_hsic_lane(struct tegra_xusb_lane *lane) +{ + return container_of(lane, struct tegra_xusb_hsic_lane, base); +} + +struct tegra_xusb_pcie_lane { + struct tegra_xusb_lane base; +}; + +static inline struct tegra_xusb_pcie_lane * +to_pcie_lane(struct tegra_xusb_lane *lane) +{ + return container_of(lane, struct tegra_xusb_pcie_lane, base); +} + +struct tegra_xusb_sata_lane { + struct tegra_xusb_lane base; +}; + +static inline struct tegra_xusb_sata_lane * +to_sata_lane(struct tegra_xusb_lane *lane) +{ + return container_of(lane, struct tegra_xusb_sata_lane, base); +} + +struct tegra_xusb_lane_ops { + struct tegra_xusb_lane *(*probe)(struct tegra_xusb_pad *pad, + struct device_node *np, + unsigned int index); + void (*remove)(struct tegra_xusb_lane *lane); +}; + +/* + * pads + */ +struct tegra_xusb_pad_soc; +struct tegra_xusb_padctl; + +struct tegra_xusb_pad_ops { + struct tegra_xusb_pad *(*probe)(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_pad_soc *soc, + struct device_node *np); + void (*remove)(struct tegra_xusb_pad *pad); +}; + +struct tegra_xusb_pad_soc { + const char *name; + + const struct tegra_xusb_lane_soc *lanes; + unsigned int num_lanes; + + const struct tegra_xusb_pad_ops *ops; +}; + +struct tegra_xusb_pad { + const struct tegra_xusb_pad_soc *soc; + struct tegra_xusb_padctl *padctl; + struct phy_provider *provider; + struct phy **lanes; + struct device dev; + + const struct tegra_xusb_lane_ops *ops; + + struct list_head list; +}; + +static inline struct tegra_xusb_pad *to_tegra_xusb_pad(struct device *dev) +{ + return container_of(dev, struct tegra_xusb_pad, dev); +} + +int tegra_xusb_pad_init(struct tegra_xusb_pad *pad, + struct tegra_xusb_padctl *padctl, + struct device_node *np); +int tegra_xusb_pad_register(struct tegra_xusb_pad *pad, + const struct phy_ops *ops); +void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad); + +struct tegra_xusb_usb2_pad { + struct tegra_xusb_pad base; + + struct clk *clk; + unsigned int enable; + struct mutex lock; +}; + +static inline struct tegra_xusb_usb2_pad * +to_usb2_pad(struct tegra_xusb_pad *pad) +{ + return container_of(pad, struct tegra_xusb_usb2_pad, base); +} + +struct tegra_xusb_ulpi_pad { + struct tegra_xusb_pad base; +}; + +static inline struct tegra_xusb_ulpi_pad * +to_ulpi_pad(struct tegra_xusb_pad *pad) +{ + return container_of(pad, struct tegra_xusb_ulpi_pad, base); +} + +struct tegra_xusb_hsic_pad { + struct tegra_xusb_pad base; + + struct regulator *supply; + struct clk *clk; +}; + +static inline struct tegra_xusb_hsic_pad * +to_hsic_pad(struct tegra_xusb_pad *pad) +{ + return container_of(pad, struct tegra_xusb_hsic_pad, base); +} + +struct tegra_xusb_pcie_pad { + struct tegra_xusb_pad base; + + struct reset_control *rst; + struct clk *pll; + + unsigned int enable; +}; + +static inline struct tegra_xusb_pcie_pad * +to_pcie_pad(struct tegra_xusb_pad *pad) +{ + return container_of(pad, struct tegra_xusb_pcie_pad, base); +} + +struct tegra_xusb_sata_pad { + struct tegra_xusb_pad base; + + struct reset_control *rst; + struct clk *pll; + + unsigned int enable; +}; + +static inline struct tegra_xusb_sata_pad * +to_sata_pad(struct tegra_xusb_pad *pad) +{ + return container_of(pad, struct tegra_xusb_sata_pad, base); +} + +/* + * ports + */ +struct tegra_xusb_port_ops; + +struct tegra_xusb_port { + struct tegra_xusb_padctl *padctl; + struct tegra_xusb_lane *lane; + unsigned int index; + + struct list_head list; + struct device dev; + + const struct tegra_xusb_port_ops *ops; +}; + +struct tegra_xusb_lane_map { + unsigned int port; + const char *type; + unsigned int index; + const char *func; +}; + +struct tegra_xusb_lane * +tegra_xusb_port_find_lane(struct tegra_xusb_port *port, + const struct tegra_xusb_lane_map *map, + const char *function); + +struct tegra_xusb_port * +tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type, + unsigned int index); + +struct tegra_xusb_usb2_port { + struct tegra_xusb_port base; + + struct regulator *supply; + bool internal; +}; + +static inline struct tegra_xusb_usb2_port * +to_usb2_port(struct tegra_xusb_port *port) +{ + return container_of(port, struct tegra_xusb_usb2_port, base); +} + +struct tegra_xusb_usb2_port * +tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, + unsigned int index); + +struct tegra_xusb_ulpi_port { + struct tegra_xusb_port base; + + struct regulator *supply; + bool internal; +}; + +static inline struct tegra_xusb_ulpi_port * +to_ulpi_port(struct tegra_xusb_port *port) +{ + return container_of(port, struct tegra_xusb_ulpi_port, base); +} + +struct tegra_xusb_hsic_port { + struct tegra_xusb_port base; +}; + +static inline struct tegra_xusb_hsic_port * +to_hsic_port(struct tegra_xusb_port *port) +{ + return container_of(port, struct tegra_xusb_hsic_port, base); +} + +struct tegra_xusb_usb3_port { + struct tegra_xusb_port base; + struct regulator *supply; + bool context_saved; + unsigned int port; + bool internal; + + u32 tap1; + u32 amp; + u32 ctle_z; + u32 ctle_g; +}; + +static inline struct tegra_xusb_usb3_port * +to_usb3_port(struct tegra_xusb_port *port) +{ + return container_of(port, struct tegra_xusb_usb3_port, base); +} + +struct tegra_xusb_usb3_port * +tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, + unsigned int index); + +struct tegra_xusb_port_ops { + int (*enable)(struct tegra_xusb_port *port); + void (*disable)(struct tegra_xusb_port *port); + struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port); +}; + +/* + * pad controller + */ +struct tegra_xusb_padctl_soc; + +struct tegra_xusb_padctl_ops { + struct tegra_xusb_padctl * + (*probe)(struct device *dev, + const struct tegra_xusb_padctl_soc *soc); + void (*remove)(struct tegra_xusb_padctl *padctl); + + int (*usb3_save_context)(struct tegra_xusb_padctl *padctl, + unsigned int index); + int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl, + unsigned int index, bool idle); + int (*usb3_set_lfps_detect)(struct tegra_xusb_padctl *padctl, + unsigned int index, bool enable); +}; + +struct tegra_xusb_padctl_soc { + const struct tegra_xusb_pad_soc * const *pads; + unsigned int num_pads; + + struct { + struct { + const struct tegra_xusb_port_ops *ops; + unsigned int count; + } usb2, ulpi, hsic, usb3; + } ports; + + const struct tegra_xusb_padctl_ops *ops; +}; + +struct tegra_xusb_padctl { + struct device *dev; + void __iomem *regs; + struct mutex lock; + struct reset_control *rst; + + const struct tegra_xusb_padctl_soc *soc; + + struct tegra_xusb_pad *pcie; + struct tegra_xusb_pad *sata; + struct tegra_xusb_pad *ulpi; + struct tegra_xusb_pad *usb2; + struct tegra_xusb_pad *hsic; + + struct list_head ports; + struct list_head lanes; + struct list_head pads; + + unsigned int enable; + + struct clk *clk; +}; + +static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value, + unsigned long offset) +{ + dev_dbg(padctl->dev, "%08lx < %08x\n", offset, value); + writel(value, padctl->regs + offset); +} + +static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl, + unsigned long offset) +{ + u32 value = readl(padctl->regs + offset); + dev_dbg(padctl->dev, "%08lx > %08x\n", offset, value); + return value; +} + +struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl, + const char *name, + unsigned int index); + +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) +extern const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc; +#endif +#if defined(CONFIG_ARCH_TEGRA_210_SOC) +extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc; +#endif + +#endif /* __PHY_TEGRA_XUSB_H */ diff --git a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c index 2f06029c9405..946cda3fee35 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c @@ -873,7 +873,7 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match); -static int tegra_xusb_padctl_probe(struct platform_device *pdev) +int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev) { struct tegra_xusb_padctl *padctl; const struct of_device_id *match; @@ -955,8 +955,9 @@ reset: reset_control_assert(padctl->rst); return err; } +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_probe); -static int tegra_xusb_padctl_remove(struct platform_device *pdev) +int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev) { struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev); int err; @@ -969,17 +970,4 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev) return err; } - -static struct platform_driver tegra_xusb_padctl_driver = { - .driver = { - .name = "tegra-xusb-padctl", - .of_match_table = tegra_xusb_padctl_of_match, - }, - .probe = tegra_xusb_padctl_probe, - .remove = tegra_xusb_padctl_remove, -}; -module_platform_driver(tegra_xusb_padctl_driver); - -MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); -MODULE_DESCRIPTION("Tegra 124 XUSB Pad Control driver"); -MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_remove); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index df37212a5cbd..0b2733db0e9e 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -12,5 +12,8 @@ menuconfig RESET_CONTROLLER If unsure, say no. +config RESET_OXNAS + bool + source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index a1fc8eda79f3..f173fc3847b4 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o obj-$(CONFIG_ATH79) += reset-ath79.o +obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index f15f150b79da..72b32bd15549 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -8,6 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#include <linux/atomic.h> #include <linux/device.h> #include <linux/err.h> #include <linux/export.h> @@ -18,19 +19,27 @@ #include <linux/reset-controller.h> #include <linux/slab.h> -static DEFINE_MUTEX(reset_controller_list_mutex); +static DEFINE_MUTEX(reset_list_mutex); static LIST_HEAD(reset_controller_list); /** * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device * this reset control belongs to + * @list: list entry for the rcdev's reset controller list * @id: ID of the reset controller in the reset * controller device + * @refcnt: Number of gets of this reset_control + * @shared: Is this a shared (1), or an exclusive (0) reset_control? + * @deassert_cnt: Number of times this reset line has been deasserted */ struct reset_control { struct reset_controller_dev *rcdev; + struct list_head list; unsigned int id; + unsigned int refcnt; + int shared; + atomic_t deassert_count; }; /** @@ -62,9 +71,11 @@ int reset_controller_register(struct reset_controller_dev *rcdev) rcdev->of_xlate = of_reset_simple_xlate; } - mutex_lock(&reset_controller_list_mutex); + INIT_LIST_HEAD(&rcdev->reset_control_head); + + mutex_lock(&reset_list_mutex); list_add(&rcdev->list, &reset_controller_list); - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return 0; } @@ -76,18 +87,23 @@ EXPORT_SYMBOL_GPL(reset_controller_register); */ void reset_controller_unregister(struct reset_controller_dev *rcdev) { - mutex_lock(&reset_controller_list_mutex); + mutex_lock(&reset_list_mutex); list_del(&rcdev->list); - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_controller_unregister); /** * reset_control_reset - reset the controlled device * @rstc: reset controller + * + * Calling this on a shared reset controller is an error. */ int reset_control_reset(struct reset_control *rstc) { + if (WARN_ON(rstc->shared)) + return -EINVAL; + if (rstc->rcdev->ops->reset) return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); @@ -98,26 +114,48 @@ EXPORT_SYMBOL_GPL(reset_control_reset); /** * reset_control_assert - asserts the reset line * @rstc: reset controller + * + * Calling this on an exclusive reset controller guarantees that the reset + * will be asserted. When called on a shared reset controller the line may + * still be deasserted, as long as other users keep it so. + * + * For shared reset controls a driver cannot expect the hw's registers and + * internal state to be reset, but must be prepared for this to happen. */ int reset_control_assert(struct reset_control *rstc) { - if (rstc->rcdev->ops->assert) - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + if (!rstc->rcdev->ops->assert) + return -ENOTSUPP; - return -ENOTSUPP; + if (rstc->shared) { + if (WARN_ON(atomic_read(&rstc->deassert_count) == 0)) + return -EINVAL; + + if (atomic_dec_return(&rstc->deassert_count) != 0) + return 0; + } + + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_assert); /** * reset_control_deassert - deasserts the reset line * @rstc: reset controller + * + * After calling this function, the reset is guaranteed to be deasserted. */ int reset_control_deassert(struct reset_control *rstc) { - if (rstc->rcdev->ops->deassert) - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + if (!rstc->rcdev->ops->deassert) + return -ENOTSUPP; - return -ENOTSUPP; + if (rstc->shared) { + if (atomic_inc_return(&rstc->deassert_count) != 1) + return 0; + } + + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert); @@ -136,18 +174,54 @@ int reset_control_status(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_status); -/** - * of_reset_control_get_by_index - Lookup and obtain a reference to a reset - * controller by index. - * @node: device to be reset by the controller - * @index: index of the reset controller - * - * This is to be used to perform a list of resets for a device or power domain - * in whatever order. Returns a struct reset_control or IS_ERR() condition - * containing errno. - */ -struct reset_control *of_reset_control_get_by_index(struct device_node *node, - int index) +static struct reset_control *__reset_control_get( + struct reset_controller_dev *rcdev, + unsigned int index, int shared) +{ + struct reset_control *rstc; + + lockdep_assert_held(&reset_list_mutex); + + list_for_each_entry(rstc, &rcdev->reset_control_head, list) { + if (rstc->id == index) { + if (WARN_ON(!rstc->shared || !shared)) + return ERR_PTR(-EBUSY); + + rstc->refcnt++; + return rstc; + } + } + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return ERR_PTR(-ENOMEM); + + try_module_get(rcdev->owner); + + rstc->rcdev = rcdev; + list_add(&rstc->list, &rcdev->reset_control_head); + rstc->id = index; + rstc->refcnt = 1; + rstc->shared = shared; + + return rstc; +} + +static void __reset_control_put(struct reset_control *rstc) +{ + lockdep_assert_held(&reset_list_mutex); + + if (--rstc->refcnt) + return; + + module_put(rstc->rcdev->owner); + + list_del(&rstc->list); + kfree(rstc); +} + +struct reset_control *__of_reset_control_get(struct device_node *node, + const char *id, int index, int shared) { struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; @@ -155,12 +229,22 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, int rstc_id; int ret; + if (!node) + return ERR_PTR(-EINVAL); + + if (id) { + index = of_property_match_string(node, + "reset-names", id); + if (index < 0) + return ERR_PTR(-ENOENT); + } + ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", index, &args); if (ret) return ERR_PTR(ret); - mutex_lock(&reset_controller_list_mutex); + mutex_lock(&reset_list_mutex); rcdev = NULL; list_for_each_entry(r, &reset_controller_list, list) { if (args.np == r->of_node) { @@ -171,78 +255,29 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, of_node_put(args.np); if (!rcdev) { - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return ERR_PTR(-EPROBE_DEFER); } if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return ERR_PTR(-EINVAL); } rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { - mutex_unlock(&reset_controller_list_mutex); + mutex_unlock(&reset_list_mutex); return ERR_PTR(rstc_id); } - try_module_get(rcdev->owner); - mutex_unlock(&reset_controller_list_mutex); - - rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); - if (!rstc) { - module_put(rcdev->owner); - return ERR_PTR(-ENOMEM); - } + /* reset_list_mutex also protects the rcdev's reset_control list */ + rstc = __reset_control_get(rcdev, rstc_id, shared); - rstc->rcdev = rcdev; - rstc->id = rstc_id; + mutex_unlock(&reset_list_mutex); return rstc; } -EXPORT_SYMBOL_GPL(of_reset_control_get_by_index); - -/** - * of_reset_control_get - Lookup and obtain a reference to a reset controller. - * @node: device to be reset by the controller - * @id: reset line name - * - * Returns a struct reset_control or IS_ERR() condition containing errno. - * - * Use of id names is optional. - */ -struct reset_control *of_reset_control_get(struct device_node *node, - const char *id) -{ - int index = 0; - - if (id) { - index = of_property_match_string(node, - "reset-names", id); - if (index < 0) - return ERR_PTR(-ENOENT); - } - return of_reset_control_get_by_index(node, index); -} -EXPORT_SYMBOL_GPL(of_reset_control_get); - -/** - * reset_control_get - Lookup and obtain a reference to a reset controller. - * @dev: device to be reset by the controller - * @id: reset line name - * - * Returns a struct reset_control or IS_ERR() condition containing errno. - * - * Use of id names is optional. - */ -struct reset_control *reset_control_get(struct device *dev, const char *id) -{ - if (!dev) - return ERR_PTR(-EINVAL); - - return of_reset_control_get(dev->of_node, id); -} -EXPORT_SYMBOL_GPL(reset_control_get); +EXPORT_SYMBOL_GPL(__of_reset_control_get); /** * reset_control_put - free the reset controller @@ -254,8 +289,9 @@ void reset_control_put(struct reset_control *rstc) if (IS_ERR(rstc)) return; - module_put(rstc->rcdev->owner); - kfree(rstc); + mutex_lock(&reset_list_mutex); + __reset_control_put(rstc); + mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_control_put); @@ -264,16 +300,8 @@ static void devm_reset_control_release(struct device *dev, void *res) reset_control_put(*(struct reset_control **)res); } -/** - * devm_reset_control_get - resource managed reset_control_get() - * @dev: device to be reset by the controller - * @id: reset line name - * - * Managed reset_control_get(). For reset controllers returned from this - * function, reset_control_put() is called automatically on driver detach. - * See reset_control_get() for more information. - */ -struct reset_control *devm_reset_control_get(struct device *dev, const char *id) +struct reset_control *__devm_reset_control_get(struct device *dev, + const char *id, int index, int shared) { struct reset_control **ptr, *rstc; @@ -282,7 +310,8 @@ struct reset_control *devm_reset_control_get(struct device *dev, const char *id) if (!ptr) return ERR_PTR(-ENOMEM); - rstc = reset_control_get(dev, id); + rstc = __of_reset_control_get(dev ? dev->of_node : NULL, + id, index, shared); if (!IS_ERR(rstc)) { *ptr = rstc; devres_add(dev, ptr); @@ -292,7 +321,7 @@ struct reset_control *devm_reset_control_get(struct device *dev, const char *id) return rstc; } -EXPORT_SYMBOL_GPL(devm_reset_control_get); +EXPORT_SYMBOL_GPL(__devm_reset_control_get); /** * device_reset - find reset controller associated with the device diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c index 3b8a4f5a1ff6..54cca0055171 100644 --- a/drivers/reset/reset-lpc18xx.c +++ b/drivers/reset/reset-lpc18xx.c @@ -35,6 +35,7 @@ struct lpc18xx_rgu_data { struct reset_controller_dev rcdev; + struct notifier_block restart_nb; struct clk *clk_delay; struct clk *clk_reg; void __iomem *base; @@ -44,12 +45,13 @@ struct lpc18xx_rgu_data { #define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev) -static void __iomem *rgu_base; - -static int lpc18xx_rgu_restart(struct notifier_block *this, unsigned long mode, +static int lpc18xx_rgu_restart(struct notifier_block *nb, unsigned long mode, void *cmd) { - writel(BIT(LPC18XX_RGU_CORE_RST), rgu_base + LPC18XX_RGU_CTRL0); + struct lpc18xx_rgu_data *rc = container_of(nb, struct lpc18xx_rgu_data, + restart_nb); + + writel(BIT(LPC18XX_RGU_CORE_RST), rc->base + LPC18XX_RGU_CTRL0); mdelay(2000); pr_emerg("%s: unable to restart system\n", __func__); @@ -57,11 +59,6 @@ static int lpc18xx_rgu_restart(struct notifier_block *this, unsigned long mode, return NOTIFY_DONE; } -static struct notifier_block lpc18xx_rgu_restart_nb = { - .notifier_call = lpc18xx_rgu_restart, - .priority = 192, -}; - /* * The LPC18xx RGU has mostly self-deasserting resets except for the * two reset lines going to the internal Cortex-M0 cores. @@ -205,8 +202,9 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev) goto dis_clks; } - rgu_base = rc->base; - ret = register_restart_handler(&lpc18xx_rgu_restart_nb); + rc->restart_nb.priority = 192, + rc->restart_nb.notifier_call = lpc18xx_rgu_restart, + ret = register_restart_handler(&rc->restart_nb); if (ret) dev_warn(&pdev->dev, "failed to register restart handler\n"); @@ -225,7 +223,7 @@ static int lpc18xx_rgu_remove(struct platform_device *pdev) struct lpc18xx_rgu_data *rc = platform_get_drvdata(pdev); int ret; - ret = unregister_restart_handler(&lpc18xx_rgu_restart_nb); + ret = unregister_restart_handler(&rc->restart_nb); if (ret) dev_warn(&pdev->dev, "failed to unregister restart handler\n"); diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c new file mode 100644 index 000000000000..c60fb2dace3e --- /dev/null +++ b/drivers/reset/reset-oxnas.c @@ -0,0 +1,136 @@ +/* + * drivers/reset/reset-oxnas.c + * + * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Ma Haijun <mahaijuns@gmail.com> + * Copyright (C) 2009 Oxford Semiconductor Ltd + * + * 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +/* Regmap offsets */ +#define RST_SET_REGOFFSET 0x34 +#define RST_CLR_REGOFFSET 0x38 + +struct oxnas_reset { + struct regmap *regmap; + struct reset_controller_dev rcdev; +}; + +static int oxnas_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct oxnas_reset *data = + container_of(rcdev, struct oxnas_reset, rcdev); + + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id)); + msleep(50); + regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id)); + + return 0; +} + +static int oxnas_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct oxnas_reset *data = + container_of(rcdev, struct oxnas_reset, rcdev); + + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id)); + + return 0; +} + +static int oxnas_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct oxnas_reset *data = + container_of(rcdev, struct oxnas_reset, rcdev); + + regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id)); + + return 0; +} + +static const struct reset_control_ops oxnas_reset_ops = { + .reset = oxnas_reset_reset, + .assert = oxnas_reset_assert, + .deassert = oxnas_reset_deassert, +}; + +static const struct of_device_id oxnas_reset_dt_ids[] = { + { .compatible = "oxsemi,ox810se-reset", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids); + +static int oxnas_reset_probe(struct platform_device *pdev) +{ + struct oxnas_reset *data; + struct device *parent; + + parent = pdev->dev.parent; + if (!parent) { + dev_err(&pdev->dev, "no parent\n"); + return -ENODEV; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = syscon_node_to_regmap(parent->of_node); + if (IS_ERR(data->regmap)) { + dev_err(&pdev->dev, "failed to get parent regmap\n"); + return PTR_ERR(data->regmap); + } + + platform_set_drvdata(pdev, data); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = 32; + data->rcdev.ops = &oxnas_reset_ops; + data->rcdev.of_node = pdev->dev.of_node; + + return reset_controller_register(&data->rcdev); +} + +static int oxnas_reset_remove(struct platform_device *pdev) +{ + struct oxnas_reset *data = platform_get_drvdata(pdev); + + reset_controller_unregister(&data->rcdev); + + return 0; +} + +static struct platform_driver oxnas_reset_driver = { + .probe = oxnas_reset_probe, + .remove = oxnas_reset_remove, + .driver = { + .name = "oxnas-reset", + .of_match_table = oxnas_reset_dt_ids, + }, +}; + +module_platform_driver(oxnas_reset_driver); diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 5ade71306ee1..380230f03874 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -9,7 +9,8 @@ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_ARCH_QCOM) += qcom/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-$(CONFIG_ARCH_RENESAS) += renesas/ +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/soc/brcmstb/Kconfig b/drivers/soc/brcmstb/Kconfig index 39cab3bd544d..7fec3b4c80a1 100644 --- a/drivers/soc/brcmstb/Kconfig +++ b/drivers/soc/brcmstb/Kconfig @@ -1,6 +1,7 @@ menuconfig SOC_BRCMSTB bool "Broadcom STB SoC drivers" depends on ARM + select SOC_BUS help Enables drivers for the Broadcom Set-Top Box (STB) series of chips. This option alone enables only some support code, while the drivers diff --git a/drivers/soc/brcmstb/common.c b/drivers/soc/brcmstb/common.c index c262c029b1b8..94e7335553f4 100644 --- a/drivers/soc/brcmstb/common.c +++ b/drivers/soc/brcmstb/common.c @@ -12,10 +12,18 @@ * GNU General Public License for more details. */ +#include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/soc/brcmstb/brcmstb.h> +#include <linux/sys_soc.h> #include <soc/brcmstb/common.h> +static u32 family_id; +static u32 product_id; + static const struct of_device_id brcmstb_machine_match[] = { { .compatible = "brcm,brcmstb", }, { } @@ -31,3 +39,61 @@ bool soc_is_brcmstb(void) return of_match_node(brcmstb_machine_match, root) != NULL; } + +static const struct of_device_id sun_top_ctrl_match[] = { + { .compatible = "brcm,brcmstb-sun-top-ctrl", }, + { } +}; + +static int __init brcmstb_soc_device_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *sun_top_ctrl; + void __iomem *sun_top_ctrl_base; + int ret = 0; + + sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match); + if (!sun_top_ctrl) + return -ENODEV; + + sun_top_ctrl_base = of_iomap(sun_top_ctrl, 0); + if (!sun_top_ctrl_base) + return -ENODEV; + + family_id = readl(sun_top_ctrl_base); + product_id = readl(sun_top_ctrl_base + 0x4); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) { + ret = -ENOMEM; + goto out; + } + + soc_dev_attr->family = kasprintf(GFP_KERNEL, "%x", + family_id >> 28 ? + family_id >> 16 : family_id >> 8); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%x", + product_id >> 28 ? + product_id >> 16 : product_id >> 8); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c%d", + ((product_id & 0xf0) >> 4) + 'A', + product_id & 0xf); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + ret = -ENODEV; + goto out; + } + + return 0; + +out: + iounmap(sun_top_ctrl_base); + return ret; +} +arch_initcall(brcmstb_soc_device_init); diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 0d9b19a78d27..3c3e56df526e 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -52,6 +52,7 @@ #define PWRAP_DEW_WRITE_TEST_VAL 0xa55a /* macro for manual command */ +#define PWRAP_MAN_CMD_SPI_WRITE_NEW (1 << 14) #define PWRAP_MAN_CMD_SPI_WRITE (1 << 13) #define PWRAP_MAN_CMD_OP_CSH (0x0 << 8) #define PWRAP_MAN_CMD_OP_CSL (0x1 << 8) @@ -69,33 +70,75 @@ PWRAP_WDT_SRC_EN_HARB_STAUPD_DLE | \ PWRAP_WDT_SRC_EN_HARB_STAUPD_ALE) -/* macro for slave device wrapper registers */ -#define PWRAP_DEW_BASE 0xbc00 -#define PWRAP_DEW_EVENT_OUT_EN (PWRAP_DEW_BASE + 0x0) -#define PWRAP_DEW_DIO_EN (PWRAP_DEW_BASE + 0x2) -#define PWRAP_DEW_EVENT_SRC_EN (PWRAP_DEW_BASE + 0x4) -#define PWRAP_DEW_EVENT_SRC (PWRAP_DEW_BASE + 0x6) -#define PWRAP_DEW_EVENT_FLAG (PWRAP_DEW_BASE + 0x8) -#define PWRAP_DEW_READ_TEST (PWRAP_DEW_BASE + 0xa) -#define PWRAP_DEW_WRITE_TEST (PWRAP_DEW_BASE + 0xc) -#define PWRAP_DEW_CRC_EN (PWRAP_DEW_BASE + 0xe) -#define PWRAP_DEW_CRC_VAL (PWRAP_DEW_BASE + 0x10) -#define PWRAP_DEW_MON_GRP_SEL (PWRAP_DEW_BASE + 0x12) -#define PWRAP_DEW_MON_FLAG_SEL (PWRAP_DEW_BASE + 0x14) -#define PWRAP_DEW_EVENT_TEST (PWRAP_DEW_BASE + 0x16) -#define PWRAP_DEW_CIPHER_KEY_SEL (PWRAP_DEW_BASE + 0x18) -#define PWRAP_DEW_CIPHER_IV_SEL (PWRAP_DEW_BASE + 0x1a) -#define PWRAP_DEW_CIPHER_LOAD (PWRAP_DEW_BASE + 0x1c) -#define PWRAP_DEW_CIPHER_START (PWRAP_DEW_BASE + 0x1e) -#define PWRAP_DEW_CIPHER_RDY (PWRAP_DEW_BASE + 0x20) -#define PWRAP_DEW_CIPHER_MODE (PWRAP_DEW_BASE + 0x22) -#define PWRAP_DEW_CIPHER_SWRST (PWRAP_DEW_BASE + 0x24) -#define PWRAP_MT8173_DEW_CIPHER_IV0 (PWRAP_DEW_BASE + 0x26) -#define PWRAP_MT8173_DEW_CIPHER_IV1 (PWRAP_DEW_BASE + 0x28) -#define PWRAP_MT8173_DEW_CIPHER_IV2 (PWRAP_DEW_BASE + 0x2a) -#define PWRAP_MT8173_DEW_CIPHER_IV3 (PWRAP_DEW_BASE + 0x2c) -#define PWRAP_MT8173_DEW_CIPHER_IV4 (PWRAP_DEW_BASE + 0x2e) -#define PWRAP_MT8173_DEW_CIPHER_IV5 (PWRAP_DEW_BASE + 0x30) +/* defines for slave device wrapper registers */ +enum dew_regs { + PWRAP_DEW_BASE, + PWRAP_DEW_DIO_EN, + PWRAP_DEW_READ_TEST, + PWRAP_DEW_WRITE_TEST, + PWRAP_DEW_CRC_EN, + PWRAP_DEW_CRC_VAL, + PWRAP_DEW_MON_GRP_SEL, + PWRAP_DEW_CIPHER_KEY_SEL, + PWRAP_DEW_CIPHER_IV_SEL, + PWRAP_DEW_CIPHER_RDY, + PWRAP_DEW_CIPHER_MODE, + PWRAP_DEW_CIPHER_SWRST, + + /* MT6397 only regs */ + PWRAP_DEW_EVENT_OUT_EN, + PWRAP_DEW_EVENT_SRC_EN, + PWRAP_DEW_EVENT_SRC, + PWRAP_DEW_EVENT_FLAG, + PWRAP_DEW_MON_FLAG_SEL, + PWRAP_DEW_EVENT_TEST, + PWRAP_DEW_CIPHER_LOAD, + PWRAP_DEW_CIPHER_START, + + /* MT6323 only regs */ + PWRAP_DEW_CIPHER_EN, + PWRAP_DEW_RDDMY_NO, +}; + +static const u32 mt6323_regs[] = { + [PWRAP_DEW_BASE] = 0x0000, + [PWRAP_DEW_DIO_EN] = 0x018a, + [PWRAP_DEW_READ_TEST] = 0x018c, + [PWRAP_DEW_WRITE_TEST] = 0x018e, + [PWRAP_DEW_CRC_EN] = 0x0192, + [PWRAP_DEW_CRC_VAL] = 0x0194, + [PWRAP_DEW_MON_GRP_SEL] = 0x0196, + [PWRAP_DEW_CIPHER_KEY_SEL] = 0x0198, + [PWRAP_DEW_CIPHER_IV_SEL] = 0x019a, + [PWRAP_DEW_CIPHER_EN] = 0x019c, + [PWRAP_DEW_CIPHER_RDY] = 0x019e, + [PWRAP_DEW_CIPHER_MODE] = 0x01a0, + [PWRAP_DEW_CIPHER_SWRST] = 0x01a2, + [PWRAP_DEW_RDDMY_NO] = 0x01a4, +}; + +static const u32 mt6397_regs[] = { + [PWRAP_DEW_BASE] = 0xbc00, + [PWRAP_DEW_EVENT_OUT_EN] = 0xbc00, + [PWRAP_DEW_DIO_EN] = 0xbc02, + [PWRAP_DEW_EVENT_SRC_EN] = 0xbc04, + [PWRAP_DEW_EVENT_SRC] = 0xbc06, + [PWRAP_DEW_EVENT_FLAG] = 0xbc08, + [PWRAP_DEW_READ_TEST] = 0xbc0a, + [PWRAP_DEW_WRITE_TEST] = 0xbc0c, + [PWRAP_DEW_CRC_EN] = 0xbc0e, + [PWRAP_DEW_CRC_VAL] = 0xbc10, + [PWRAP_DEW_MON_GRP_SEL] = 0xbc12, + [PWRAP_DEW_MON_FLAG_SEL] = 0xbc14, + [PWRAP_DEW_EVENT_TEST] = 0xbc16, + [PWRAP_DEW_CIPHER_KEY_SEL] = 0xbc18, + [PWRAP_DEW_CIPHER_IV_SEL] = 0xbc1a, + [PWRAP_DEW_CIPHER_LOAD] = 0xbc1c, + [PWRAP_DEW_CIPHER_START] = 0xbc1e, + [PWRAP_DEW_CIPHER_RDY] = 0xbc20, + [PWRAP_DEW_CIPHER_MODE] = 0xbc22, + [PWRAP_DEW_CIPHER_SWRST] = 0xbc24, +}; enum pwrap_regs { PWRAP_MUX_SEL, @@ -158,6 +201,13 @@ enum pwrap_regs { PWRAP_DCM_EN, PWRAP_DCM_DBC_PRD, + /* MT2701 only regs */ + PWRAP_ADC_CMD_ADDR, + PWRAP_PWRAP_ADC_CMD, + PWRAP_ADC_RDY_ADDR, + PWRAP_ADC_RDATA_ADDR1, + PWRAP_ADC_RDATA_ADDR2, + /* MT8135 only regs */ PWRAP_CSHEXT, PWRAP_EVENT_IN_EN, @@ -194,6 +244,92 @@ enum pwrap_regs { PWRAP_CIPHER_EN, }; +static int mt2701_regs[] = { + [PWRAP_MUX_SEL] = 0x0, + [PWRAP_WRAP_EN] = 0x4, + [PWRAP_DIO_EN] = 0x8, + [PWRAP_SIDLY] = 0xc, + [PWRAP_RDDMY] = 0x18, + [PWRAP_SI_CK_CON] = 0x1c, + [PWRAP_CSHEXT_WRITE] = 0x20, + [PWRAP_CSHEXT_READ] = 0x24, + [PWRAP_CSLEXT_START] = 0x28, + [PWRAP_CSLEXT_END] = 0x2c, + [PWRAP_STAUPD_PRD] = 0x30, + [PWRAP_STAUPD_GRPEN] = 0x34, + [PWRAP_STAUPD_MAN_TRIG] = 0x38, + [PWRAP_STAUPD_STA] = 0x3c, + [PWRAP_WRAP_STA] = 0x44, + [PWRAP_HARB_INIT] = 0x48, + [PWRAP_HARB_HPRIO] = 0x4c, + [PWRAP_HIPRIO_ARB_EN] = 0x50, + [PWRAP_HARB_STA0] = 0x54, + [PWRAP_HARB_STA1] = 0x58, + [PWRAP_MAN_EN] = 0x5c, + [PWRAP_MAN_CMD] = 0x60, + [PWRAP_MAN_RDATA] = 0x64, + [PWRAP_MAN_VLDCLR] = 0x68, + [PWRAP_WACS0_EN] = 0x6c, + [PWRAP_INIT_DONE0] = 0x70, + [PWRAP_WACS0_CMD] = 0x74, + [PWRAP_WACS0_RDATA] = 0x78, + [PWRAP_WACS0_VLDCLR] = 0x7c, + [PWRAP_WACS1_EN] = 0x80, + [PWRAP_INIT_DONE1] = 0x84, + [PWRAP_WACS1_CMD] = 0x88, + [PWRAP_WACS1_RDATA] = 0x8c, + [PWRAP_WACS1_VLDCLR] = 0x90, + [PWRAP_WACS2_EN] = 0x94, + [PWRAP_INIT_DONE2] = 0x98, + [PWRAP_WACS2_CMD] = 0x9c, + [PWRAP_WACS2_RDATA] = 0xa0, + [PWRAP_WACS2_VLDCLR] = 0xa4, + [PWRAP_INT_EN] = 0xa8, + [PWRAP_INT_FLG_RAW] = 0xac, + [PWRAP_INT_FLG] = 0xb0, + [PWRAP_INT_CLR] = 0xb4, + [PWRAP_SIG_ADR] = 0xb8, + [PWRAP_SIG_MODE] = 0xbc, + [PWRAP_SIG_VALUE] = 0xc0, + [PWRAP_SIG_ERRVAL] = 0xc4, + [PWRAP_CRC_EN] = 0xc8, + [PWRAP_TIMER_EN] = 0xcc, + [PWRAP_TIMER_STA] = 0xd0, + [PWRAP_WDT_UNIT] = 0xd4, + [PWRAP_WDT_SRC_EN] = 0xd8, + [PWRAP_WDT_FLG] = 0xdc, + [PWRAP_DEBUG_INT_SEL] = 0xe0, + [PWRAP_DVFS_ADR0] = 0xe4, + [PWRAP_DVFS_WDATA0] = 0xe8, + [PWRAP_DVFS_ADR1] = 0xec, + [PWRAP_DVFS_WDATA1] = 0xf0, + [PWRAP_DVFS_ADR2] = 0xf4, + [PWRAP_DVFS_WDATA2] = 0xf8, + [PWRAP_DVFS_ADR3] = 0xfc, + [PWRAP_DVFS_WDATA3] = 0x100, + [PWRAP_DVFS_ADR4] = 0x104, + [PWRAP_DVFS_WDATA4] = 0x108, + [PWRAP_DVFS_ADR5] = 0x10c, + [PWRAP_DVFS_WDATA5] = 0x110, + [PWRAP_DVFS_ADR6] = 0x114, + [PWRAP_DVFS_WDATA6] = 0x118, + [PWRAP_DVFS_ADR7] = 0x11c, + [PWRAP_DVFS_WDATA7] = 0x120, + [PWRAP_CIPHER_KEY_SEL] = 0x124, + [PWRAP_CIPHER_IV_SEL] = 0x128, + [PWRAP_CIPHER_EN] = 0x12c, + [PWRAP_CIPHER_RDY] = 0x130, + [PWRAP_CIPHER_MODE] = 0x134, + [PWRAP_CIPHER_SWRST] = 0x138, + [PWRAP_DCM_EN] = 0x13c, + [PWRAP_DCM_DBC_PRD] = 0x140, + [PWRAP_ADC_CMD_ADDR] = 0x144, + [PWRAP_PWRAP_ADC_CMD] = 0x148, + [PWRAP_ADC_RDY_ADDR] = 0x14c, + [PWRAP_ADC_RDATA_ADDR1] = 0x150, + [PWRAP_ADC_RDATA_ADDR2] = 0x154, +}; + static int mt8173_regs[] = { [PWRAP_MUX_SEL] = 0x0, [PWRAP_WRAP_EN] = 0x4, @@ -349,36 +485,28 @@ static int mt8135_regs[] = { [PWRAP_DCM_DBC_PRD] = 0x160, }; +enum pmic_type { + PMIC_MT6323, + PMIC_MT6397, +}; + enum pwrap_type { + PWRAP_MT2701, PWRAP_MT8135, PWRAP_MT8173, }; -struct pmic_wrapper_type { - int *regs; - enum pwrap_type type; - u32 arb_en_all; -}; - -static struct pmic_wrapper_type pwrap_mt8135 = { - .regs = mt8135_regs, - .type = PWRAP_MT8135, - .arb_en_all = 0x1ff, -}; - -static struct pmic_wrapper_type pwrap_mt8173 = { - .regs = mt8173_regs, - .type = PWRAP_MT8173, - .arb_en_all = 0x3f, +struct pwrap_slv_type { + const u32 *dew_regs; + enum pmic_type type; }; struct pmic_wrapper { struct device *dev; void __iomem *base; struct regmap *regmap; - int *regs; - enum pwrap_type type; - u32 arb_en_all; + const struct pmic_wrapper_type *master; + const struct pwrap_slv_type *slave; struct clk *clk_spi; struct clk *clk_wrap; struct reset_control *rstc; @@ -387,24 +515,26 @@ struct pmic_wrapper { void __iomem *bridge_base; }; -static inline int pwrap_is_mt8135(struct pmic_wrapper *wrp) -{ - return wrp->type == PWRAP_MT8135; -} - -static inline int pwrap_is_mt8173(struct pmic_wrapper *wrp) -{ - return wrp->type == PWRAP_MT8173; -} +struct pmic_wrapper_type { + int *regs; + enum pwrap_type type; + u32 arb_en_all; + u32 int_en_all; + u32 spi_w; + u32 wdt_src; + int has_bridge:1; + int (*init_reg_clock)(struct pmic_wrapper *wrp); + int (*init_soc_specific)(struct pmic_wrapper *wrp); +}; static u32 pwrap_readl(struct pmic_wrapper *wrp, enum pwrap_regs reg) { - return readl(wrp->base + wrp->regs[reg]); + return readl(wrp->base + wrp->master->regs[reg]); } static void pwrap_writel(struct pmic_wrapper *wrp, u32 val, enum pwrap_regs reg) { - writel(val, wrp->base + wrp->regs[reg]); + writel(val, wrp->base + wrp->master->regs[reg]); } static bool pwrap_is_fsm_idle(struct pmic_wrapper *wrp) @@ -522,15 +652,15 @@ static int pwrap_reset_spislave(struct pmic_wrapper *wrp) pwrap_writel(wrp, 1, PWRAP_MAN_EN); pwrap_writel(wrp, 0, PWRAP_DIO_EN); - pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_CSL, + pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_CSL, PWRAP_MAN_CMD); - pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_OUTS, + pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS, PWRAP_MAN_CMD); - pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_CSH, + pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_CSH, PWRAP_MAN_CMD); for (i = 0; i < 4; i++) - pwrap_writel(wrp, PWRAP_MAN_CMD_SPI_WRITE | PWRAP_MAN_CMD_OP_OUTS, + pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS, PWRAP_MAN_CMD); ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle); @@ -562,7 +692,8 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp) for (i = 0; i < 4; i++) { pwrap_writel(wrp, i, PWRAP_SIDLY); - pwrap_read(wrp, PWRAP_DEW_READ_TEST, &rdata); + pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], + &rdata); if (rdata == PWRAP_DEW_READ_TEST_VAL) { dev_dbg(wrp->dev, "[Read Test] pass, SIDLY=%x\n", i); pass |= 1 << i; @@ -580,19 +711,47 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp) return 0; } -static int pwrap_init_reg_clock(struct pmic_wrapper *wrp) +static int pwrap_mt8135_init_reg_clock(struct pmic_wrapper *wrp) +{ + pwrap_writel(wrp, 0x4, PWRAP_CSHEXT); + pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE); + pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ); + pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START); + pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END); + + return 0; +} + +static int pwrap_mt8173_init_reg_clock(struct pmic_wrapper *wrp) { - if (pwrap_is_mt8135(wrp)) { - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT); - pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE); - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ); - pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START); - pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END); - } else { - pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE); - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ); + pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE); + pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ); + pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); + pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); + + return 0; +} + +static int pwrap_mt2701_init_reg_clock(struct pmic_wrapper *wrp) +{ + switch (wrp->slave->type) { + case PMIC_MT6397: + pwrap_writel(wrp, 0xc, PWRAP_RDDMY); + pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_WRITE); + pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_READ); + pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); + pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); + break; + + case PMIC_MT6323: + pwrap_writel(wrp, 0x8, PWRAP_RDDMY); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_RDDMY_NO], + 0x8); + pwrap_writel(wrp, 0x5, PWRAP_CSHEXT_WRITE); + pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_READ); pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); + break; } return 0; @@ -608,7 +767,8 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp) u32 rdata; int ret; - ret = pwrap_read(wrp, PWRAP_DEW_CIPHER_RDY, &rdata); + ret = pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_RDY], + &rdata); if (ret) return 0; @@ -625,20 +785,37 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) pwrap_writel(wrp, 0x1, PWRAP_CIPHER_KEY_SEL); pwrap_writel(wrp, 0x2, PWRAP_CIPHER_IV_SEL); - if (pwrap_is_mt8135(wrp)) { + switch (wrp->master->type) { + case PWRAP_MT8135: pwrap_writel(wrp, 1, PWRAP_CIPHER_LOAD); pwrap_writel(wrp, 1, PWRAP_CIPHER_START); - } else { + break; + case PWRAP_MT2701: + case PWRAP_MT8173: pwrap_writel(wrp, 1, PWRAP_CIPHER_EN); + break; } /* Config cipher mode @PMIC */ - pwrap_write(wrp, PWRAP_DEW_CIPHER_SWRST, 0x1); - pwrap_write(wrp, PWRAP_DEW_CIPHER_SWRST, 0x0); - pwrap_write(wrp, PWRAP_DEW_CIPHER_KEY_SEL, 0x1); - pwrap_write(wrp, PWRAP_DEW_CIPHER_IV_SEL, 0x2); - pwrap_write(wrp, PWRAP_DEW_CIPHER_LOAD, 0x1); - pwrap_write(wrp, PWRAP_DEW_CIPHER_START, 0x1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_SWRST], 0x1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_SWRST], 0x0); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_KEY_SEL], 0x1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_IV_SEL], 0x2); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_LOAD], 0x1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_START], 0x1); + + switch (wrp->slave->type) { + case PMIC_MT6397: + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_LOAD], + 0x1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_START], + 0x1); + break; + case PMIC_MT6323: + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_EN], + 0x1); + break; + } /* wait for cipher data ready@AP */ ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready); @@ -655,7 +832,7 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) } /* wait for cipher mode idle */ - pwrap_write(wrp, PWRAP_DEW_CIPHER_MODE, 0x1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_MODE], 0x1); ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle); if (ret) { dev_err(wrp->dev, "cipher mode idle fail, ret=%d\n", ret); @@ -665,9 +842,11 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) pwrap_writel(wrp, 1, PWRAP_CIPHER_MODE); /* Write Test */ - if (pwrap_write(wrp, PWRAP_DEW_WRITE_TEST, PWRAP_DEW_WRITE_TEST_VAL) || - pwrap_read(wrp, PWRAP_DEW_WRITE_TEST, &rdata) || - (rdata != PWRAP_DEW_WRITE_TEST_VAL)) { + if (pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_WRITE_TEST], + PWRAP_DEW_WRITE_TEST_VAL) || + pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_WRITE_TEST], + &rdata) || + (rdata != PWRAP_DEW_WRITE_TEST_VAL)) { dev_err(wrp->dev, "rdata=0x%04X\n", rdata); return -EFAULT; } @@ -675,6 +854,63 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) return 0; } +static int pwrap_mt8135_init_soc_specific(struct pmic_wrapper *wrp) +{ + /* enable pwrap events and pwrap bridge in AP side */ + pwrap_writel(wrp, 0x1, PWRAP_EVENT_IN_EN); + pwrap_writel(wrp, 0xffff, PWRAP_EVENT_DST_EN); + writel(0x7f, wrp->bridge_base + PWRAP_MT8135_BRIDGE_IORD_ARB_EN); + writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WACS3_EN); + writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WACS4_EN); + writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WDT_UNIT); + writel(0xffff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WDT_SRC_EN); + writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_TIMER_EN); + writel(0x7ff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INT_EN); + + /* enable PMIC event out and sources */ + if (pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_EVENT_OUT_EN], + 0x1) || + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_EVENT_SRC_EN], + 0xffff)) { + dev_err(wrp->dev, "enable dewrap fail\n"); + return -EFAULT; + } + + return 0; +} + +static int pwrap_mt8173_init_soc_specific(struct pmic_wrapper *wrp) +{ + /* PMIC_DEWRAP enables */ + if (pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_EVENT_OUT_EN], + 0x1) || + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_EVENT_SRC_EN], + 0xffff)) { + dev_err(wrp->dev, "enable dewrap fail\n"); + return -EFAULT; + } + + return 0; +} + +static int pwrap_mt2701_init_soc_specific(struct pmic_wrapper *wrp) +{ + /* GPS_INTF initialization */ + switch (wrp->slave->type) { + case PMIC_MT6323: + pwrap_writel(wrp, 0x076c, PWRAP_ADC_CMD_ADDR); + pwrap_writel(wrp, 0x8000, PWRAP_PWRAP_ADC_CMD); + pwrap_writel(wrp, 0x072c, PWRAP_ADC_RDY_ADDR); + pwrap_writel(wrp, 0x072e, PWRAP_ADC_RDATA_ADDR1); + pwrap_writel(wrp, 0x0730, PWRAP_ADC_RDATA_ADDR2); + break; + default: + break; + } + + return 0; +} + static int pwrap_init(struct pmic_wrapper *wrp) { int ret; @@ -684,7 +920,7 @@ static int pwrap_init(struct pmic_wrapper *wrp) if (wrp->rstc_bridge) reset_control_reset(wrp->rstc_bridge); - if (pwrap_is_mt8173(wrp)) { + if (wrp->master->type == PWRAP_MT8173) { /* Enable DCM */ pwrap_writel(wrp, 3, PWRAP_DCM_EN); pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD); @@ -697,11 +933,11 @@ static int pwrap_init(struct pmic_wrapper *wrp) pwrap_writel(wrp, 1, PWRAP_WRAP_EN); - pwrap_writel(wrp, wrp->arb_en_all, PWRAP_HIPRIO_ARB_EN); + pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN); pwrap_writel(wrp, 1, PWRAP_WACS2_EN); - ret = pwrap_init_reg_clock(wrp); + ret = wrp->master->init_reg_clock(wrp); if (ret) return ret; @@ -711,7 +947,7 @@ static int pwrap_init(struct pmic_wrapper *wrp) return ret; /* Enable dual IO mode */ - pwrap_write(wrp, PWRAP_DEW_DIO_EN, 1); + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1); /* Check IDLE & INIT_DONE in advance */ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle); @@ -723,7 +959,7 @@ static int pwrap_init(struct pmic_wrapper *wrp) pwrap_writel(wrp, 1, PWRAP_DIO_EN); /* Read Test */ - pwrap_read(wrp, PWRAP_DEW_READ_TEST, &rdata); + pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata); if (rdata != PWRAP_DEW_READ_TEST_VAL) { dev_err(wrp->dev, "Read test failed after switch to DIO mode: 0x%04x != 0x%04x\n", PWRAP_DEW_READ_TEST_VAL, rdata); @@ -736,15 +972,16 @@ static int pwrap_init(struct pmic_wrapper *wrp) return ret; /* Signature checking - using CRC */ - if (pwrap_write(wrp, PWRAP_DEW_CRC_EN, 0x1)) + if (pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_EN], 0x1)) return -EFAULT; pwrap_writel(wrp, 0x1, PWRAP_CRC_EN); pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE); - pwrap_writel(wrp, PWRAP_DEW_CRC_VAL, PWRAP_SIG_ADR); - pwrap_writel(wrp, wrp->arb_en_all, PWRAP_HIPRIO_ARB_EN); + pwrap_writel(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_VAL], + PWRAP_SIG_ADR); + pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN); - if (pwrap_is_mt8135(wrp)) + if (wrp->master->type == PWRAP_MT8135) pwrap_writel(wrp, 0x7, PWRAP_RRARB_EN); pwrap_writel(wrp, 0x1, PWRAP_WACS0_EN); @@ -753,31 +990,10 @@ static int pwrap_init(struct pmic_wrapper *wrp) pwrap_writel(wrp, 0x5, PWRAP_STAUPD_PRD); pwrap_writel(wrp, 0xff, PWRAP_STAUPD_GRPEN); - if (pwrap_is_mt8135(wrp)) { - /* enable pwrap events and pwrap bridge in AP side */ - pwrap_writel(wrp, 0x1, PWRAP_EVENT_IN_EN); - pwrap_writel(wrp, 0xffff, PWRAP_EVENT_DST_EN); - writel(0x7f, wrp->bridge_base + PWRAP_MT8135_BRIDGE_IORD_ARB_EN); - writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WACS3_EN); - writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WACS4_EN); - writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WDT_UNIT); - writel(0xffff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_WDT_SRC_EN); - writel(0x1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_TIMER_EN); - writel(0x7ff, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INT_EN); - - /* enable PMIC event out and sources */ - if (pwrap_write(wrp, PWRAP_DEW_EVENT_OUT_EN, 0x1) || - pwrap_write(wrp, PWRAP_DEW_EVENT_SRC_EN, 0xffff)) { - dev_err(wrp->dev, "enable dewrap fail\n"); - return -EFAULT; - } - } else { - /* PMIC_DEWRAP enables */ - if (pwrap_write(wrp, PWRAP_DEW_EVENT_OUT_EN, 0x1) || - pwrap_write(wrp, PWRAP_DEW_EVENT_SRC_EN, 0xffff)) { - dev_err(wrp->dev, "enable dewrap fail\n"); - return -EFAULT; - } + if (wrp->master->init_soc_specific) { + ret = wrp->master->init_soc_specific(wrp); + if (ret) + return ret; } /* Setup the init done registers */ @@ -785,7 +1001,7 @@ static int pwrap_init(struct pmic_wrapper *wrp) pwrap_writel(wrp, 1, PWRAP_INIT_DONE0); pwrap_writel(wrp, 1, PWRAP_INIT_DONE1); - if (pwrap_is_mt8135(wrp)) { + if (wrp->master->has_bridge) { writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE3); writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE4); } @@ -816,8 +1032,70 @@ static const struct regmap_config pwrap_regmap_config = { .max_register = 0xffff, }; +static const struct pwrap_slv_type pmic_mt6323 = { + .dew_regs = mt6323_regs, + .type = PMIC_MT6323, +}; + +static const struct pwrap_slv_type pmic_mt6397 = { + .dew_regs = mt6397_regs, + .type = PMIC_MT6397, +}; + +static const struct of_device_id of_slave_match_tbl[] = { + { + .compatible = "mediatek,mt6323", + .data = &pmic_mt6323, + }, { + .compatible = "mediatek,mt6397", + .data = &pmic_mt6397, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, of_slave_match_tbl); + +static const struct pmic_wrapper_type pwrap_mt2701 = { + .regs = mt2701_regs, + .type = PWRAP_MT2701, + .arb_en_all = 0x3f, + .int_en_all = ~(BIT(31) | BIT(2)), + .spi_w = PWRAP_MAN_CMD_SPI_WRITE_NEW, + .wdt_src = PWRAP_WDT_SRC_MASK_ALL, + .has_bridge = 0, + .init_reg_clock = pwrap_mt2701_init_reg_clock, + .init_soc_specific = pwrap_mt2701_init_soc_specific, +}; + +static struct pmic_wrapper_type pwrap_mt8135 = { + .regs = mt8135_regs, + .type = PWRAP_MT8135, + .arb_en_all = 0x1ff, + .int_en_all = ~(BIT(31) | BIT(1)), + .spi_w = PWRAP_MAN_CMD_SPI_WRITE, + .wdt_src = PWRAP_WDT_SRC_MASK_ALL, + .has_bridge = 1, + .init_reg_clock = pwrap_mt8135_init_reg_clock, + .init_soc_specific = pwrap_mt8135_init_soc_specific, +}; + +static struct pmic_wrapper_type pwrap_mt8173 = { + .regs = mt8173_regs, + .type = PWRAP_MT8173, + .arb_en_all = 0x3f, + .int_en_all = ~(BIT(31) | BIT(1)), + .spi_w = PWRAP_MAN_CMD_SPI_WRITE, + .wdt_src = PWRAP_WDT_SRC_MASK_NO_STAUPD, + .has_bridge = 0, + .init_reg_clock = pwrap_mt8173_init_reg_clock, + .init_soc_specific = pwrap_mt8173_init_soc_specific, +}; + static struct of_device_id of_pwrap_match_tbl[] = { { + .compatible = "mediatek,mt2701-pwrap", + .data = &pwrap_mt2701, + }, { .compatible = "mediatek,mt8135-pwrap", .data = &pwrap_mt8135, }, { @@ -831,24 +1109,30 @@ MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl); static int pwrap_probe(struct platform_device *pdev) { - int ret, irq, wdt_src; + int ret, irq; struct pmic_wrapper *wrp; struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id = of_match_device(of_pwrap_match_tbl, &pdev->dev); - const struct pmic_wrapper_type *type; + const struct of_device_id *of_slave_id = NULL; struct resource *res; + if (pdev->dev.of_node->child) + of_slave_id = of_match_node(of_slave_match_tbl, + pdev->dev.of_node->child); + if (!of_slave_id) { + dev_dbg(&pdev->dev, "slave pmic should be defined in dts\n"); + return -EINVAL; + } + wrp = devm_kzalloc(&pdev->dev, sizeof(*wrp), GFP_KERNEL); if (!wrp) return -ENOMEM; platform_set_drvdata(pdev, wrp); - type = of_id->data; - wrp->regs = type->regs; - wrp->type = type->type; - wrp->arb_en_all = type->arb_en_all; + wrp->master = of_id->data; + wrp->slave = of_slave_id->data; wrp->dev = &pdev->dev; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap"); @@ -863,7 +1147,7 @@ static int pwrap_probe(struct platform_device *pdev) return ret; } - if (pwrap_is_mt8135(wrp)) { + if (wrp->master->has_bridge) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap-bridge"); wrp->bridge_base = devm_ioremap_resource(wrp->dev, res); @@ -925,11 +1209,9 @@ static int pwrap_probe(struct platform_device *pdev) * Since STAUPD was not used on mt8173 platform, * so STAUPD of WDT_SRC which should be turned off */ - wdt_src = pwrap_is_mt8173(wrp) ? - PWRAP_WDT_SRC_MASK_NO_STAUPD : PWRAP_WDT_SRC_MASK_ALL; - pwrap_writel(wrp, wdt_src, PWRAP_WDT_SRC_EN); + pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN); pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN); - pwrap_writel(wrp, ~((1 << 31) | (1 << 1)), PWRAP_INT_EN); + pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN); irq = platform_get_irq(pdev, 0); ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH, diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile new file mode 100644 index 000000000000..151fcd3f025b --- /dev/null +++ b/drivers/soc/renesas/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_ARCH_R8A7779) += rcar-sysc.o r8a7779-sysc.o +obj-$(CONFIG_ARCH_R8A7790) += rcar-sysc.o r8a7790-sysc.o +obj-$(CONFIG_ARCH_R8A7791) += rcar-sysc.o r8a7791-sysc.o +# R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. +obj-$(CONFIG_ARCH_R8A7793) += rcar-sysc.o r8a7791-sysc.o +obj-$(CONFIG_ARCH_R8A7794) += rcar-sysc.o r8a7794-sysc.o +obj-$(CONFIG_ARCH_R8A7795) += rcar-sysc.o r8a7795-sysc.o diff --git a/drivers/soc/renesas/r8a7779-sysc.c b/drivers/soc/renesas/r8a7779-sysc.c new file mode 100644 index 000000000000..9e8e6b7faa04 --- /dev/null +++ b/drivers/soc/renesas/r8a7779-sysc.c @@ -0,0 +1,34 @@ +/* + * Renesas R-Car H1 System Controller + * + * Copyright (C) 2016 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a7779-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7779_areas[] __initconst = { + { "always-on", 0, 0, R8A7779_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "arm1", 0x40, 1, R8A7779_PD_ARM1, R8A7779_PD_ALWAYS_ON, + PD_CPU_CR }, + { "arm2", 0x40, 2, R8A7779_PD_ARM2, R8A7779_PD_ALWAYS_ON, + PD_CPU_CR }, + { "arm3", 0x40, 3, R8A7779_PD_ARM3, R8A7779_PD_ALWAYS_ON, + PD_CPU_CR }, + { "sgx", 0xc0, 0, R8A7779_PD_SGX, R8A7779_PD_ALWAYS_ON }, + { "vdp", 0x100, 0, R8A7779_PD_VDP, R8A7779_PD_ALWAYS_ON }, + { "imp", 0x140, 0, R8A7779_PD_IMP, R8A7779_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7779_sysc_info __initconst = { + .areas = r8a7779_areas, + .num_areas = ARRAY_SIZE(r8a7779_areas), +}; diff --git a/drivers/soc/renesas/r8a7790-sysc.c b/drivers/soc/renesas/r8a7790-sysc.c new file mode 100644 index 000000000000..7a567ad0ff73 --- /dev/null +++ b/drivers/soc/renesas/r8a7790-sysc.c @@ -0,0 +1,48 @@ +/* + * Renesas R-Car H2 System Controller + * + * Copyright (C) 2016 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a7790-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7790_areas[] __initconst = { + { "always-on", 0, 0, R8A7790_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7790_PD_CA15_SCU, R8A7790_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7790_PD_CA15_CPU0, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7790_PD_CA15_CPU1, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu2", 0x40, 2, R8A7790_PD_CA15_CPU2, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu3", 0x40, 3, R8A7790_PD_CA15_CPU3, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca7-scu", 0x100, 0, R8A7790_PD_CA7_SCU, R8A7790_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7790_PD_CA7_CPU0, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7790_PD_CA7_CPU1, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu2", 0x1c0, 2, R8A7790_PD_CA7_CPU2, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu3", 0x1c0, 3, R8A7790_PD_CA7_CPU3, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "sh-4a", 0x80, 0, R8A7790_PD_SH_4A, R8A7790_PD_ALWAYS_ON }, + { "rgx", 0xc0, 0, R8A7790_PD_RGX, R8A7790_PD_ALWAYS_ON }, + { "imp", 0x140, 0, R8A7790_PD_IMP, R8A7790_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7790_sysc_info __initconst = { + .areas = r8a7790_areas, + .num_areas = ARRAY_SIZE(r8a7790_areas), +}; diff --git a/drivers/soc/renesas/r8a7791-sysc.c b/drivers/soc/renesas/r8a7791-sysc.c new file mode 100644 index 000000000000..03b9f41a34e6 --- /dev/null +++ b/drivers/soc/renesas/r8a7791-sysc.c @@ -0,0 +1,33 @@ +/* + * Renesas R-Car M2-W/N System Controller + * + * Copyright (C) 2016 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a7791-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7791_areas[] __initconst = { + { "always-on", 0, 0, R8A7791_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7791_PD_CA15_SCU, R8A7791_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7791_PD_CA15_CPU0, R8A7791_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7791_PD_CA15_CPU1, R8A7791_PD_CA15_SCU, + PD_CPU_NOCR }, + { "sh-4a", 0x80, 0, R8A7791_PD_SH_4A, R8A7791_PD_ALWAYS_ON }, + { "sgx", 0xc0, 0, R8A7791_PD_SGX, R8A7791_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7791_sysc_info __initconst = { + .areas = r8a7791_areas, + .num_areas = ARRAY_SIZE(r8a7791_areas), +}; diff --git a/drivers/soc/renesas/r8a7794-sysc.c b/drivers/soc/renesas/r8a7794-sysc.c new file mode 100644 index 000000000000..c4da2941e06c --- /dev/null +++ b/drivers/soc/renesas/r8a7794-sysc.c @@ -0,0 +1,33 @@ +/* + * Renesas R-Car E2 System Controller + * + * Copyright (C) 2016 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a7794-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7794_areas[] __initconst = { + { "always-on", 0, 0, R8A7794_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca7-scu", 0x100, 0, R8A7794_PD_CA7_SCU, R8A7794_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7794_PD_CA7_CPU0, R8A7794_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7794_PD_CA7_CPU1, R8A7794_PD_CA7_SCU, + PD_CPU_NOCR }, + { "sh-4a", 0x80, 0, R8A7794_PD_SH_4A, R8A7794_PD_ALWAYS_ON }, + { "sgx", 0xc0, 0, R8A7794_PD_SGX, R8A7794_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7794_sysc_info __initconst = { + .areas = r8a7794_areas, + .num_areas = ARRAY_SIZE(r8a7794_areas), +}; diff --git a/drivers/soc/renesas/r8a7795-sysc.c b/drivers/soc/renesas/r8a7795-sysc.c new file mode 100644 index 000000000000..5e7537c96f7b --- /dev/null +++ b/drivers/soc/renesas/r8a7795-sysc.c @@ -0,0 +1,56 @@ +/* + * Renesas R-Car H3 System Controller + * + * Copyright (C) 2016 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a7795-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7795_areas[] __initconst = { + { "always-on", 0, 0, R8A7795_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A7795_PD_CA57_SCU, R8A7795_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A7795_PD_CA57_CPU0, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A7795_PD_CA57_CPU1, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu2", 0x80, 2, R8A7795_PD_CA57_CPU2, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu3", 0x80, 3, R8A7795_PD_CA57_CPU3, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A7795_PD_CA53_SCU, R8A7795_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A7795_PD_CA53_CPU0, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A7795_PD_CA53_CPU1, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A7795_PD_CA53_CPU2, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A7795_PD_CA53_CPU3, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "a3vp", 0x340, 0, R8A7795_PD_A3VP, R8A7795_PD_ALWAYS_ON }, + { "cr7", 0x240, 0, R8A7795_PD_CR7, R8A7795_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A7795_PD_A3VC, R8A7795_PD_ALWAYS_ON }, + { "a2vc0", 0x3c0, 0, R8A7795_PD_A2VC0, R8A7795_PD_A3VC }, + { "a2vc1", 0x3c0, 1, R8A7795_PD_A2VC1, R8A7795_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A7795_PD_3DG_A, R8A7795_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A7795_PD_3DG_B, R8A7795_PD_3DG_A }, + { "3dg-c", 0x100, 2, R8A7795_PD_3DG_C, R8A7795_PD_3DG_B }, + { "3dg-d", 0x100, 3, R8A7795_PD_3DG_D, R8A7795_PD_3DG_C }, + { "3dg-e", 0x100, 4, R8A7795_PD_3DG_E, R8A7795_PD_3DG_D }, + { "a3ir", 0x180, 0, R8A7795_PD_A3IR, R8A7795_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7795_sysc_info __initconst = { + .areas = r8a7795_areas, + .num_areas = ARRAY_SIZE(r8a7795_areas), +}; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c new file mode 100644 index 000000000000..79dbc770895f --- /dev/null +++ b/drivers/soc/renesas/rcar-sysc.c @@ -0,0 +1,401 @@ +/* + * R-Car SYSC Power management support + * + * Copyright (C) 2014 Magnus Damm + * Copyright (C) 2015-2016 Glider bvba + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/clk/renesas.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/mm.h> +#include <linux/of_address.h> +#include <linux/pm_domain.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/soc/renesas/rcar-sysc.h> + +#include "rcar-sysc.h" + +/* SYSC Common */ +#define SYSCSR 0x00 /* SYSC Status Register */ +#define SYSCISR 0x04 /* Interrupt Status Register */ +#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ +#define SYSCIER 0x0c /* Interrupt Enable Register */ +#define SYSCIMR 0x10 /* Interrupt Mask Register */ + +/* SYSC Status Register */ +#define SYSCSR_PONENB 1 /* Ready for power resume requests */ +#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ + +/* + * Power Control Register Offsets inside the register block for each domain + * Note: The "CR" registers for ARM cores exist on H1 only + * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 + * Use PSCI on R-Car Gen3 + */ +#define PWRSR_OFFS 0x00 /* Power Status Register */ +#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ +#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ +#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ +#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ +#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ + + +#define SYSCSR_RETRIES 100 +#define SYSCSR_DELAY_US 1 + +#define PWRER_RETRIES 100 +#define PWRER_DELAY_US 1 + +#define SYSCISR_RETRIES 1000 +#define SYSCISR_DELAY_US 1 + +#define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ + +static void __iomem *rcar_sysc_base; +static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ + +static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) +{ + unsigned int sr_bit, reg_offs; + int k; + + if (on) { + sr_bit = SYSCSR_PONENB; + reg_offs = PWRONCR_OFFS; + } else { + sr_bit = SYSCSR_POFFENB; + reg_offs = PWROFFCR_OFFS; + } + + /* Wait until SYSC is ready to accept a power request */ + for (k = 0; k < SYSCSR_RETRIES; k++) { + if (ioread32(rcar_sysc_base + SYSCSR) & BIT(sr_bit)) + break; + udelay(SYSCSR_DELAY_US); + } + + if (k == SYSCSR_RETRIES) + return -EAGAIN; + + /* Submit power shutoff or power resume request */ + iowrite32(BIT(sysc_ch->chan_bit), + rcar_sysc_base + sysc_ch->chan_offs + reg_offs); + + return 0; +} + +static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) +{ + unsigned int isr_mask = BIT(sysc_ch->isr_bit); + unsigned int chan_mask = BIT(sysc_ch->chan_bit); + unsigned int status; + unsigned long flags; + int ret = 0; + int k; + + spin_lock_irqsave(&rcar_sysc_lock, flags); + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + /* Submit power shutoff or resume request until it was accepted */ + for (k = 0; k < PWRER_RETRIES; k++) { + ret = rcar_sysc_pwr_on_off(sysc_ch, on); + if (ret) + goto out; + + status = ioread32(rcar_sysc_base + + sysc_ch->chan_offs + PWRER_OFFS); + if (!(status & chan_mask)) + break; + + udelay(PWRER_DELAY_US); + } + + if (k == PWRER_RETRIES) { + ret = -EIO; + goto out; + } + + /* Wait until the power shutoff or resume request has completed * */ + for (k = 0; k < SYSCISR_RETRIES; k++) { + if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask) + break; + udelay(SYSCISR_DELAY_US); + } + + if (k == SYSCISR_RETRIES) + ret = -EIO; + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + out: + spin_unlock_irqrestore(&rcar_sysc_lock, flags); + + pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", + sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); + return ret; +} + +int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_power(sysc_ch, false); +} + +int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) +{ + return rcar_sysc_power(sysc_ch, true); +} + +static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) +{ + unsigned int st; + + st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); + if (st & BIT(sysc_ch->chan_bit)) + return true; + + return false; +} + +void __iomem *rcar_sysc_init(phys_addr_t base) +{ + rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); + if (!rcar_sysc_base) + panic("unable to ioremap R-Car SYSC hardware block\n"); + + return rcar_sysc_base; +} + +struct rcar_sysc_pd { + struct generic_pm_domain genpd; + struct rcar_sysc_ch ch; + unsigned int flags; + char name[0]; +}; + +static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct rcar_sysc_pd, genpd); +} + +static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) +{ + struct rcar_sysc_pd *pd = to_rcar_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + + if (pd->flags & PD_NO_CR) { + pr_debug("%s: Cannot control %s\n", __func__, genpd->name); + return -EBUSY; + } + + if (pd->flags & PD_BUSY) { + pr_debug("%s: %s busy\n", __func__, genpd->name); + return -EBUSY; + } + + return rcar_sysc_power_down(&pd->ch); +} + +static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) +{ + struct rcar_sysc_pd *pd = to_rcar_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + + if (pd->flags & PD_NO_CR) { + pr_debug("%s: Cannot control %s\n", __func__, genpd->name); + return 0; + } + + return rcar_sysc_power_up(&pd->ch); +} + +static bool has_cpg_mstp; + +static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) +{ + struct generic_pm_domain *genpd = &pd->genpd; + const char *name = pd->genpd.name; + struct dev_power_governor *gov = &simple_qos_governor; + + if (pd->flags & PD_CPU) { + /* + * This domain contains a CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "CPU"); + pd->flags |= PD_BUSY; + gov = &pm_domain_always_on_gov; + } else if (pd->flags & PD_SCU) { + /* + * This domain contains an SCU and cache-controller, and + * therefore it should only be turned off if the CPU cores are + * not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "SCU"); + pd->flags |= PD_BUSY; + gov = &pm_domain_always_on_gov; + } else if (pd->flags & PD_NO_CR) { + /* + * This domain cannot be turned off. + */ + pd->flags |= PD_BUSY; + gov = &pm_domain_always_on_gov; + } + + if (!(pd->flags & (PD_CPU | PD_SCU))) { + /* Enable Clock Domain for I/O devices */ + genpd->flags = GENPD_FLAG_PM_CLK; + if (has_cpg_mstp) { + genpd->attach_dev = cpg_mstp_attach_dev; + genpd->detach_dev = cpg_mstp_detach_dev; + } else { + genpd->attach_dev = cpg_mssr_attach_dev; + genpd->detach_dev = cpg_mssr_detach_dev; + } + } + + genpd->power_off = rcar_sysc_pd_power_off; + genpd->power_on = rcar_sysc_pd_power_on; + + if (pd->flags & (PD_CPU | PD_NO_CR)) { + /* Skip CPUs (handled by SMP code) and areas without control */ + pr_debug("%s: Not touching %s\n", __func__, genpd->name); + goto finalize; + } + + if (!rcar_sysc_power_is_off(&pd->ch)) { + pr_debug("%s: %s is already powered\n", __func__, genpd->name); + goto finalize; + } + + rcar_sysc_power_up(&pd->ch); + +finalize: + pm_genpd_init(genpd, gov, false); +} + +static const struct of_device_id rcar_sysc_matches[] = { +#ifdef CONFIG_ARCH_R8A7779 + { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info }, +#endif +#ifdef CONFIG_ARCH_R8A7790 + { .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info }, +#endif +#ifdef CONFIG_ARCH_R8A7791 + { .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info }, +#endif +#ifdef CONFIG_ARCH_R8A7793 + /* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */ + { .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info }, +#endif +#ifdef CONFIG_ARCH_R8A7794 + { .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info }, +#endif +#ifdef CONFIG_ARCH_R8A7795 + { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, +#endif + { /* sentinel */ } +}; + +struct rcar_pm_domains { + struct genpd_onecell_data onecell_data; + struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1]; +}; + +static int __init rcar_sysc_pd_init(void) +{ + const struct rcar_sysc_info *info; + const struct of_device_id *match; + struct rcar_pm_domains *domains; + struct device_node *np; + u32 syscier, syscimr; + void __iomem *base; + unsigned int i; + int error; + + np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match); + if (!np) + return -ENODEV; + + info = match->data; + + has_cpg_mstp = of_find_compatible_node(NULL, NULL, + "renesas,cpg-mstp-clocks"); + + base = of_iomap(np, 0); + if (!base) { + pr_warn("%s: Cannot map regs\n", np->full_name); + error = -ENOMEM; + goto out_put; + } + + rcar_sysc_base = base; + + domains = kzalloc(sizeof(*domains), GFP_KERNEL); + if (!domains) { + error = -ENOMEM; + goto out_put; + } + + domains->onecell_data.domains = domains->domains; + domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); + + for (i = 0, syscier = 0; i < info->num_areas; i++) + syscier |= BIT(info->areas[i].isr_bit); + + /* + * Mask all interrupt sources to prevent the CPU from receiving them. + * Make sure not to clear reserved bits that were set before. + */ + syscimr = ioread32(base + SYSCIMR); + syscimr |= syscier; + pr_debug("%s: syscimr = 0x%08x\n", np->full_name, syscimr); + iowrite32(syscimr, base + SYSCIMR); + + /* + * SYSC needs all interrupt sources enabled to control power. + */ + pr_debug("%s: syscier = 0x%08x\n", np->full_name, syscier); + iowrite32(syscier, base + SYSCIER); + + for (i = 0; i < info->num_areas; i++) { + const struct rcar_sysc_area *area = &info->areas[i]; + struct rcar_sysc_pd *pd; + + pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL); + if (!pd) { + error = -ENOMEM; + goto out_put; + } + + strcpy(pd->name, area->name); + pd->genpd.name = pd->name; + pd->ch.chan_offs = area->chan_offs; + pd->ch.chan_bit = area->chan_bit; + pd->ch.isr_bit = area->isr_bit; + pd->flags = area->flags; + + rcar_sysc_pd_setup(pd); + if (area->parent >= 0) + pm_genpd_add_subdomain(domains->domains[area->parent], + &pd->genpd); + + domains->domains[area->isr_bit] = &pd->genpd; + } + + of_genpd_add_provider_onecell(np, &domains->onecell_data); + +out_put: + of_node_put(np); + return error; +} +early_initcall(rcar_sysc_pd_init); diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h new file mode 100644 index 000000000000..5e766174c2f4 --- /dev/null +++ b/drivers/soc/renesas/rcar-sysc.h @@ -0,0 +1,58 @@ +/* + * Renesas R-Car System Controller + * + * Copyright (C) 2016 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ +#ifndef __SOC_RENESAS_RCAR_SYSC_H__ +#define __SOC_RENESAS_RCAR_SYSC_H__ + +#include <linux/types.h> + + +/* + * Power Domain flags + */ +#define PD_CPU BIT(0) /* Area contains main CPU core */ +#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ +#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ + +#define PD_BUSY BIT(3) /* Busy, for internal use only */ + +#define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */ +#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */ +#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ + + +/* + * Description of a Power Area + */ + +struct rcar_sysc_area { + const char *name; + u16 chan_offs; /* Offset of PWRSR register for this area */ + u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */ + u8 isr_bit; /* Bit in SYSCI*R */ + int parent; /* -1 if none */ + unsigned int flags; /* See PD_* */ +}; + + +/* + * SoC-specific Power Area Description + */ + +struct rcar_sysc_info { + const struct rcar_sysc_area *areas; + unsigned int num_areas; +}; + +extern const struct rcar_sysc_info r8a7779_sysc_info; +extern const struct rcar_sysc_info r8a7790_sysc_info; +extern const struct rcar_sysc_info r8a7791_sysc_info; +extern const struct rcar_sysc_info r8a7794_sysc_info; +extern const struct rcar_sysc_info r8a7795_sysc_info; +#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */ diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 43155e1f97b9..44842a205e4b 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -19,6 +19,7 @@ #include <linux/mfd/syscon.h> #include <dt-bindings/power/rk3288-power.h> #include <dt-bindings/power/rk3368-power.h> +#include <dt-bindings/power/rk3399-power.h> struct rockchip_domain_info { int pwr_mask; @@ -45,10 +46,20 @@ struct rockchip_pmu_info { const struct rockchip_domain_info *domain_info; }; +#define MAX_QOS_REGS_NUM 5 +#define QOS_PRIORITY 0x08 +#define QOS_MODE 0x0c +#define QOS_BANDWIDTH 0x10 +#define QOS_SATURATION 0x14 +#define QOS_EXTCONTROL 0x18 + struct rockchip_pm_domain { struct generic_pm_domain genpd; const struct rockchip_domain_info *info; struct rockchip_pmu *pmu; + int num_qos; + struct regmap **qos_regmap; + u32 *qos_save_regs[MAX_QOS_REGS_NUM]; int num_clks; struct clk *clks[]; }; @@ -66,11 +77,11 @@ struct rockchip_pmu { #define DOMAIN(pwr, status, req, idle, ack) \ { \ - .pwr_mask = BIT(pwr), \ - .status_mask = BIT(status), \ - .req_mask = BIT(req), \ - .idle_mask = BIT(idle), \ - .ack_mask = BIT(ack), \ + .pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \ + .status_mask = (status >= 0) ? BIT(status) : 0, \ + .req_mask = (req >= 0) ? BIT(req) : 0, \ + .idle_mask = (idle >= 0) ? BIT(idle) : 0, \ + .ack_mask = (ack >= 0) ? BIT(ack) : 0, \ } #define DOMAIN_RK3288(pwr, status, req) \ @@ -79,6 +90,9 @@ struct rockchip_pmu { #define DOMAIN_RK3368(pwr, status, req) \ DOMAIN(pwr, status, req, (req) + 16, req) +#define DOMAIN_RK3399(pwr, status, req) \ + DOMAIN(pwr, status, req, req, req) + static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd) { struct rockchip_pmu *pmu = pd->pmu; @@ -96,6 +110,9 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, struct rockchip_pmu *pmu = pd->pmu; unsigned int val; + if (pd_info->req_mask == 0) + return 0; + regmap_update_bits(pmu->regmap, pmu->info->req_offset, pd_info->req_mask, idle ? -1U : 0); @@ -111,11 +128,64 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, return 0; } +static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd) +{ + int i; + + for (i = 0; i < pd->num_qos; i++) { + regmap_read(pd->qos_regmap[i], + QOS_PRIORITY, + &pd->qos_save_regs[0][i]); + regmap_read(pd->qos_regmap[i], + QOS_MODE, + &pd->qos_save_regs[1][i]); + regmap_read(pd->qos_regmap[i], + QOS_BANDWIDTH, + &pd->qos_save_regs[2][i]); + regmap_read(pd->qos_regmap[i], + QOS_SATURATION, + &pd->qos_save_regs[3][i]); + regmap_read(pd->qos_regmap[i], + QOS_EXTCONTROL, + &pd->qos_save_regs[4][i]); + } + return 0; +} + +static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd) +{ + int i; + + for (i = 0; i < pd->num_qos; i++) { + regmap_write(pd->qos_regmap[i], + QOS_PRIORITY, + pd->qos_save_regs[0][i]); + regmap_write(pd->qos_regmap[i], + QOS_MODE, + pd->qos_save_regs[1][i]); + regmap_write(pd->qos_regmap[i], + QOS_BANDWIDTH, + pd->qos_save_regs[2][i]); + regmap_write(pd->qos_regmap[i], + QOS_SATURATION, + pd->qos_save_regs[3][i]); + regmap_write(pd->qos_regmap[i], + QOS_EXTCONTROL, + pd->qos_save_regs[4][i]); + } + + return 0; +} + static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd) { struct rockchip_pmu *pmu = pd->pmu; unsigned int val; + /* check idle status for idle-only domains */ + if (pd->info->status_mask == 0) + return !rockchip_pmu_domain_is_idle(pd); + regmap_read(pmu->regmap, pmu->info->status_offset, &val); /* 1'b0: power on, 1'b1: power off */ @@ -127,6 +197,9 @@ static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, { struct rockchip_pmu *pmu = pd->pmu; + if (pd->info->pwr_mask == 0) + return; + regmap_update_bits(pmu->regmap, pmu->info->pwr_offset, pd->info->pwr_mask, on ? 0 : -1U); @@ -147,7 +220,7 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) clk_enable(pd->clks[i]); if (!power_on) { - /* FIXME: add code to save AXI_QOS */ + rockchip_pmu_save_qos(pd); /* if powering down, idle request to NIU first */ rockchip_pmu_set_idle_request(pd, true); @@ -159,7 +232,7 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) /* if powering up, leave idle mode */ rockchip_pmu_set_idle_request(pd, false); - /* FIXME: add code to restore AXI_QOS */ + rockchip_pmu_restore_qos(pd); } for (i = pd->num_clks - 1; i >= 0; i--) @@ -227,9 +300,10 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, { const struct rockchip_domain_info *pd_info; struct rockchip_pm_domain *pd; + struct device_node *qos_node; struct clk *clk; int clk_cnt; - int i; + int i, j; u32 id; int error; @@ -289,6 +363,45 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, clk, node->name); } + pd->num_qos = of_count_phandle_with_args(node, "pm_qos", + NULL); + + if (pd->num_qos > 0) { + pd->qos_regmap = devm_kcalloc(pmu->dev, pd->num_qos, + sizeof(*pd->qos_regmap), + GFP_KERNEL); + if (!pd->qos_regmap) { + error = -ENOMEM; + goto err_out; + } + + for (j = 0; j < MAX_QOS_REGS_NUM; j++) { + pd->qos_save_regs[j] = devm_kcalloc(pmu->dev, + pd->num_qos, + sizeof(u32), + GFP_KERNEL); + if (!pd->qos_save_regs[j]) { + error = -ENOMEM; + goto err_out; + } + } + + for (j = 0; j < pd->num_qos; j++) { + qos_node = of_parse_phandle(node, "pm_qos", j); + if (!qos_node) { + error = -ENODEV; + goto err_out; + } + pd->qos_regmap[j] = syscon_node_to_regmap(qos_node); + if (IS_ERR(pd->qos_regmap[j])) { + error = -ENODEV; + of_node_put(qos_node); + goto err_out; + } + of_node_put(qos_node); + } + } + error = rockchip_pd_power(pd, true); if (error) { dev_err(pmu->dev, @@ -360,6 +473,61 @@ static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu, regmap_write(pmu->regmap, domain_reg_offset + 4, count); } +static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, + struct device_node *parent) +{ + struct device_node *np; + struct generic_pm_domain *child_domain, *parent_domain; + int error; + + for_each_child_of_node(parent, np) { + u32 idx; + + error = of_property_read_u32(parent, "reg", &idx); + if (error) { + dev_err(pmu->dev, + "%s: failed to retrieve domain id (reg): %d\n", + parent->name, error); + goto err_out; + } + parent_domain = pmu->genpd_data.domains[idx]; + + error = rockchip_pm_add_one_domain(pmu, np); + if (error) { + dev_err(pmu->dev, "failed to handle node %s: %d\n", + np->name, error); + goto err_out; + } + + error = of_property_read_u32(np, "reg", &idx); + if (error) { + dev_err(pmu->dev, + "%s: failed to retrieve domain id (reg): %d\n", + np->name, error); + goto err_out; + } + child_domain = pmu->genpd_data.domains[idx]; + + error = pm_genpd_add_subdomain(parent_domain, child_domain); + if (error) { + dev_err(pmu->dev, "%s failed to add subdomain %s: %d\n", + parent_domain->name, child_domain->name, error); + goto err_out; + } else { + dev_dbg(pmu->dev, "%s add subdomain: %s\n", + parent_domain->name, child_domain->name); + } + + rockchip_pm_add_subdomain(pmu, np); + } + + return 0; + +err_out: + of_node_put(np); + return error; +} + static int rockchip_pm_domain_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -406,6 +574,10 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev) } pmu->regmap = syscon_node_to_regmap(parent->of_node); + if (IS_ERR(pmu->regmap)) { + dev_err(dev, "no regmap available\n"); + return PTR_ERR(pmu->regmap); + } /* * Configure power up and down transition delays for CORE @@ -426,6 +598,14 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev) of_node_put(node); goto err_out; } + + error = rockchip_pm_add_subdomain(pmu, node); + if (error < 0) { + dev_err(dev, "failed to handle subdomain node %s: %d\n", + node->name, error); + of_node_put(node); + goto err_out; + } } if (error) { @@ -457,6 +637,36 @@ static const struct rockchip_domain_info rk3368_pm_domains[] = { [RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2), }; +static const struct rockchip_domain_info rk3399_pm_domains[] = { + [RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1), + [RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1), + [RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1), + [RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15), + [RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16), + [RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1), + [RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2), + [RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14), + [RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17), + [RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0), + [RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3), + [RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4), + [RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5), + [RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6), + [RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1), + [RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7), + [RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8), + [RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9), + [RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10), + [RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11), + [RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23), + [RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24), + [RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12), + [RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22), + [RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27), + [RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28), + [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29), +}; + static const struct rockchip_pmu_info rk3288_pmu = { .pwr_offset = 0x08, .status_offset = 0x0c, @@ -491,6 +701,23 @@ static const struct rockchip_pmu_info rk3368_pmu = { .domain_info = rk3368_pm_domains, }; +static const struct rockchip_pmu_info rk3399_pmu = { + .pwr_offset = 0x14, + .status_offset = 0x18, + .req_offset = 0x60, + .idle_offset = 0x64, + .ack_offset = 0x68, + + .core_pwrcnt_offset = 0x9c, + .gpu_pwrcnt_offset = 0xa4, + + .core_power_transition_time = 24, + .gpu_power_transition_time = 24, + + .num_domains = ARRAY_SIZE(rk3399_pm_domains), + .domain_info = rk3399_pm_domains, +}; + static const struct of_device_id rockchip_pm_domain_dt_match[] = { { .compatible = "rockchip,rk3288-power-controller", @@ -500,6 +727,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .compatible = "rockchip,rk3368-power-controller", .data = (void *)&rk3368_pmu, }, + { + .compatible = "rockchip,rk3399-power-controller", + .data = (void *)&rk3399_pmu, + }, { /* sentinel */ }, }; diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index bc34cf7482fb..bb173456bbff 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -28,12 +28,16 @@ #include <linux/export.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/reboot.h> #include <linux/reset.h> #include <linux/seq_file.h> +#include <linux/slab.h> #include <linux/spinlock.h> #include <soc/tegra/common.h> @@ -101,6 +105,16 @@ #define GPU_RG_CNTRL 0x2d4 +struct tegra_powergate { + struct generic_pm_domain genpd; + struct tegra_pmc *pmc; + unsigned int id; + struct clk **clks; + unsigned int num_clks; + struct reset_control **resets; + unsigned int num_resets; +}; + struct tegra_pmc_soc { unsigned int num_powergates; const char *const *powergates; @@ -113,8 +127,11 @@ struct tegra_pmc_soc { /** * struct tegra_pmc - NVIDIA Tegra PMC + * @dev: pointer to PMC device structure * @base: pointer to I/O remapped register region * @clk: pointer to pclk clock + * @soc: pointer to SoC data structure + * @debugfs: pointer to debugfs entry * @rate: currently configured rate of pclk * @suspend_mode: lowest suspend mode available * @cpu_good_time: CPU power good time (in microseconds) @@ -128,12 +145,14 @@ struct tegra_pmc_soc { * @cpu_pwr_good_en: CPU power good signal is enabled * @lp0_vec_phys: physical base address of the LP0 warm boot code * @lp0_vec_size: size of the LP0 warm boot code + * @powergates_available: Bitmap of available power gates * @powergates_lock: mutex for power gate register access */ struct tegra_pmc { struct device *dev; void __iomem *base; struct clk *clk; + struct dentry *debugfs; const struct tegra_pmc_soc *soc; @@ -151,6 +170,7 @@ struct tegra_pmc { bool cpu_pwr_good_en; u32 lp0_vec_phys; u32 lp0_vec_size; + DECLARE_BITMAP(powergates_available, TEGRA_POWERGATE_MAX); struct mutex powergates_lock; }; @@ -160,6 +180,12 @@ static struct tegra_pmc *pmc = &(struct tegra_pmc) { .suspend_mode = TEGRA_SUSPEND_NONE, }; +static inline struct tegra_powergate * +to_powergate(struct generic_pm_domain *domain) +{ + return container_of(domain, struct tegra_powergate, genpd); +} + static u32 tegra_pmc_readl(unsigned long offset) { return readl(pmc->base + offset); @@ -170,38 +196,287 @@ static void tegra_pmc_writel(u32 value, unsigned long offset) writel(value, pmc->base + offset); } +static inline bool tegra_powergate_state(int id) +{ + if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps) + return (tegra_pmc_readl(GPU_RG_CNTRL) & 0x1) == 0; + else + return (tegra_pmc_readl(PWRGATE_STATUS) & BIT(id)) != 0; +} + +static inline bool tegra_powergate_is_valid(int id) +{ + return (pmc->soc && pmc->soc->powergates[id]); +} + +static inline bool tegra_powergate_is_available(int id) +{ + return test_bit(id, pmc->powergates_available); +} + +static int tegra_powergate_lookup(struct tegra_pmc *pmc, const char *name) +{ + unsigned int i; + + if (!pmc || !pmc->soc || !name) + return -EINVAL; + + for (i = 0; i < pmc->soc->num_powergates; i++) { + if (!tegra_powergate_is_valid(i)) + continue; + + if (!strcmp(name, pmc->soc->powergates[i])) + return i; + } + + dev_err(pmc->dev, "powergate %s not found\n", name); + + return -ENODEV; +} + /** * tegra_powergate_set() - set the state of a partition * @id: partition ID * @new_state: new state of the partition */ -static int tegra_powergate_set(int id, bool new_state) +static int tegra_powergate_set(unsigned int id, bool new_state) { bool status; + int err; - mutex_lock(&pmc->powergates_lock); + if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps) + return -EINVAL; - status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id); + mutex_lock(&pmc->powergates_lock); - if (status == new_state) { + if (tegra_powergate_state(id) == new_state) { mutex_unlock(&pmc->powergates_lock); return 0; } tegra_pmc_writel(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); + err = readx_poll_timeout(tegra_powergate_state, id, status, + status == new_state, 10, 100000); + + mutex_unlock(&pmc->powergates_lock); + + return err; +} + +static int __tegra_powergate_remove_clamping(unsigned int id) +{ + u32 mask; + + mutex_lock(&pmc->powergates_lock); + + /* + * On Tegra124 and later, the clamps for the GPU are controlled by a + * separate register (with different semantics). + */ + if (id == TEGRA_POWERGATE_3D) { + if (pmc->soc->has_gpu_clamps) { + tegra_pmc_writel(0, GPU_RG_CNTRL); + goto out; + } + } + + /* + * Tegra 2 has a bug where PCIE and VDE clamping masks are + * swapped relatively to the partition ids + */ + if (id == TEGRA_POWERGATE_VDEC) + mask = (1 << TEGRA_POWERGATE_PCIE); + else if (id == TEGRA_POWERGATE_PCIE) + mask = (1 << TEGRA_POWERGATE_VDEC); + else + mask = (1 << id); + + tegra_pmc_writel(mask, REMOVE_CLAMPING); + +out: mutex_unlock(&pmc->powergates_lock); return 0; } +static void tegra_powergate_disable_clocks(struct tegra_powergate *pg) +{ + unsigned int i; + + for (i = 0; i < pg->num_clks; i++) + clk_disable_unprepare(pg->clks[i]); +} + +static int tegra_powergate_enable_clocks(struct tegra_powergate *pg) +{ + unsigned int i; + int err; + + for (i = 0; i < pg->num_clks; i++) { + err = clk_prepare_enable(pg->clks[i]); + if (err) + goto out; + } + + return 0; + +out: + while (i--) + clk_disable_unprepare(pg->clks[i]); + + return err; +} + +static int tegra_powergate_reset_assert(struct tegra_powergate *pg) +{ + unsigned int i; + int err; + + for (i = 0; i < pg->num_resets; i++) { + err = reset_control_assert(pg->resets[i]); + if (err) + return err; + } + + return 0; +} + +static int tegra_powergate_reset_deassert(struct tegra_powergate *pg) +{ + unsigned int i; + int err; + + for (i = 0; i < pg->num_resets; i++) { + err = reset_control_deassert(pg->resets[i]); + if (err) + return err; + } + + return 0; +} + +static int tegra_powergate_power_up(struct tegra_powergate *pg, + bool disable_clocks) +{ + int err; + + err = tegra_powergate_reset_assert(pg); + if (err) + return err; + + usleep_range(10, 20); + + err = tegra_powergate_set(pg->id, true); + if (err < 0) + return err; + + usleep_range(10, 20); + + err = tegra_powergate_enable_clocks(pg); + if (err) + goto disable_clks; + + usleep_range(10, 20); + + err = __tegra_powergate_remove_clamping(pg->id); + if (err) + goto disable_clks; + + usleep_range(10, 20); + + err = tegra_powergate_reset_deassert(pg); + if (err) + goto powergate_off; + + usleep_range(10, 20); + + if (disable_clocks) + tegra_powergate_disable_clocks(pg); + + return 0; + +disable_clks: + tegra_powergate_disable_clocks(pg); + usleep_range(10, 20); +powergate_off: + tegra_powergate_set(pg->id, false); + + return err; +} + +static int tegra_powergate_power_down(struct tegra_powergate *pg) +{ + int err; + + err = tegra_powergate_enable_clocks(pg); + if (err) + return err; + + usleep_range(10, 20); + + err = tegra_powergate_reset_assert(pg); + if (err) + goto disable_clks; + + usleep_range(10, 20); + + tegra_powergate_disable_clocks(pg); + + usleep_range(10, 20); + + err = tegra_powergate_set(pg->id, false); + if (err) + goto assert_resets; + + return 0; + +assert_resets: + tegra_powergate_enable_clocks(pg); + usleep_range(10, 20); + tegra_powergate_reset_deassert(pg); + usleep_range(10, 20); +disable_clks: + tegra_powergate_disable_clocks(pg); + + return err; +} + +static int tegra_genpd_power_on(struct generic_pm_domain *domain) +{ + struct tegra_powergate *pg = to_powergate(domain); + struct tegra_pmc *pmc = pg->pmc; + int err; + + err = tegra_powergate_power_up(pg, true); + if (err) + dev_err(pmc->dev, "failed to turn on PM domain %s: %d\n", + pg->genpd.name, err); + + return err; +} + +static int tegra_genpd_power_off(struct generic_pm_domain *domain) +{ + struct tegra_powergate *pg = to_powergate(domain); + struct tegra_pmc *pmc = pg->pmc; + int err; + + err = tegra_powergate_power_down(pg); + if (err) + dev_err(pmc->dev, "failed to turn off PM domain %s: %d\n", + pg->genpd.name, err); + + return err; +} + /** * tegra_powergate_power_on() - power on partition * @id: partition ID */ -int tegra_powergate_power_on(int id) +int tegra_powergate_power_on(unsigned int id) { - if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + if (!tegra_powergate_is_available(id)) return -EINVAL; return tegra_powergate_set(id, true); @@ -211,9 +486,9 @@ int tegra_powergate_power_on(int id) * tegra_powergate_power_off() - power off partition * @id: partition ID */ -int tegra_powergate_power_off(int id) +int tegra_powergate_power_off(unsigned int id) { - if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + if (!tegra_powergate_is_available(id)) return -EINVAL; return tegra_powergate_set(id, false); @@ -224,53 +499,30 @@ EXPORT_SYMBOL(tegra_powergate_power_off); * tegra_powergate_is_powered() - check if partition is powered * @id: partition ID */ -int tegra_powergate_is_powered(int id) +int tegra_powergate_is_powered(unsigned int id) { - u32 status; + int status; - if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + if (!tegra_powergate_is_valid(id)) return -EINVAL; - status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id); - return !!status; + mutex_lock(&pmc->powergates_lock); + status = tegra_powergate_state(id); + mutex_unlock(&pmc->powergates_lock); + + return status; } /** * tegra_powergate_remove_clamping() - remove power clamps for partition * @id: partition ID */ -int tegra_powergate_remove_clamping(int id) +int tegra_powergate_remove_clamping(unsigned int id) { - u32 mask; - - if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + if (!tegra_powergate_is_available(id)) return -EINVAL; - /* - * On Tegra124 and later, the clamps for the GPU are controlled by a - * separate register (with different semantics). - */ - if (id == TEGRA_POWERGATE_3D) { - if (pmc->soc->has_gpu_clamps) { - tegra_pmc_writel(0, GPU_RG_CNTRL); - return 0; - } - } - - /* - * Tegra 2 has a bug where PCIE and VDE clamping masks are - * swapped relatively to the partition ids - */ - if (id == TEGRA_POWERGATE_VDEC) - mask = (1 << TEGRA_POWERGATE_PCIE); - else if (id == TEGRA_POWERGATE_PCIE) - mask = (1 << TEGRA_POWERGATE_VDEC); - else - mask = (1 << id); - - tegra_pmc_writel(mask, REMOVE_CLAMPING); - - return 0; + return __tegra_powergate_remove_clamping(id); } EXPORT_SYMBOL(tegra_powergate_remove_clamping); @@ -282,38 +534,23 @@ EXPORT_SYMBOL(tegra_powergate_remove_clamping); * * Must be called with clk disabled, and returns with clk enabled. */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk, +int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, struct reset_control *rst) { - int ret; - - reset_control_assert(rst); - - ret = tegra_powergate_power_on(id); - if (ret) - goto err_power; - - ret = clk_prepare_enable(clk); - if (ret) - goto err_clk; - - usleep_range(10, 20); - - ret = tegra_powergate_remove_clamping(id); - if (ret) - goto err_clamp; + struct tegra_powergate pg; + int err; - usleep_range(10, 20); - reset_control_deassert(rst); + pg.id = id; + pg.clks = &clk; + pg.num_clks = 1; + pg.resets = &rst; + pg.num_resets = 1; - return 0; + err = tegra_powergate_power_up(&pg, false); + if (err) + pr_err("failed to turn on partition %d: %d\n", id, err); -err_clamp: - clk_disable_unprepare(clk); -err_clk: - tegra_powergate_power_off(id); -err_power: - return ret; + return err; } EXPORT_SYMBOL(tegra_powergate_sequence_power_up); @@ -325,9 +562,9 @@ EXPORT_SYMBOL(tegra_powergate_sequence_power_up); * Returns the partition ID corresponding to the CPU partition ID or a * negative error code on failure. */ -static int tegra_get_cpu_powergate_id(int cpuid) +static int tegra_get_cpu_powergate_id(unsigned int cpuid) { - if (pmc->soc && cpuid > 0 && cpuid < pmc->soc->num_cpu_powergates) + if (pmc->soc && cpuid < pmc->soc->num_cpu_powergates) return pmc->soc->cpu_powergates[cpuid]; return -EINVAL; @@ -337,7 +574,7 @@ static int tegra_get_cpu_powergate_id(int cpuid) * tegra_pmc_cpu_is_powered() - check if CPU partition is powered * @cpuid: CPU partition ID */ -bool tegra_pmc_cpu_is_powered(int cpuid) +bool tegra_pmc_cpu_is_powered(unsigned int cpuid) { int id; @@ -352,7 +589,7 @@ bool tegra_pmc_cpu_is_powered(int cpuid) * tegra_pmc_cpu_power_on() - power on CPU partition * @cpuid: CPU partition ID */ -int tegra_pmc_cpu_power_on(int cpuid) +int tegra_pmc_cpu_power_on(unsigned int cpuid) { int id; @@ -367,7 +604,7 @@ int tegra_pmc_cpu_power_on(int cpuid) * tegra_pmc_cpu_remove_clamping() - remove power clamps for CPU partition * @cpuid: CPU partition ID */ -int tegra_pmc_cpu_remove_clamping(int cpuid) +int tegra_pmc_cpu_remove_clamping(unsigned int cpuid) { int id; @@ -416,16 +653,18 @@ static struct notifier_block tegra_pmc_restart_handler = { static int powergate_show(struct seq_file *s, void *data) { unsigned int i; + int status; seq_printf(s, " powergate powered\n"); seq_printf(s, "------------------\n"); for (i = 0; i < pmc->soc->num_powergates; i++) { - if (!pmc->soc->powergates[i]) + status = tegra_powergate_is_powered(i); + if (status < 0) continue; seq_printf(s, " %9s %7s\n", pmc->soc->powergates[i], - tegra_powergate_is_powered(i) ? "yes" : "no"); + status ? "yes" : "no"); } return 0; @@ -445,17 +684,164 @@ static const struct file_operations powergate_fops = { static int tegra_powergate_debugfs_init(void) { - struct dentry *d; + pmc->debugfs = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, + &powergate_fops); + if (!pmc->debugfs) + return -ENOMEM; + + return 0; +} + +static int tegra_powergate_of_get_clks(struct tegra_powergate *pg, + struct device_node *np) +{ + struct clk *clk; + unsigned int i, count; + int err; + + count = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (count == 0) + return -ENODEV; + + pg->clks = kcalloc(count, sizeof(clk), GFP_KERNEL); + if (!pg->clks) + return -ENOMEM; + + for (i = 0; i < count; i++) { + pg->clks[i] = of_clk_get(np, i); + if (IS_ERR(pg->clks[i])) { + err = PTR_ERR(pg->clks[i]); + goto err; + } + } + + pg->num_clks = count; + + return 0; + +err: + while (i--) + clk_put(pg->clks[i]); + kfree(pg->clks); + + return err; +} + +static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, + struct device_node *np) +{ + struct reset_control *rst; + unsigned int i, count; + int err; + + count = of_count_phandle_with_args(np, "resets", "#reset-cells"); + if (count == 0) + return -ENODEV; - d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, - &powergate_fops); - if (!d) + pg->resets = kcalloc(count, sizeof(rst), GFP_KERNEL); + if (!pg->resets) return -ENOMEM; + for (i = 0; i < count; i++) { + pg->resets[i] = of_reset_control_get_by_index(np, i); + if (IS_ERR(pg->resets[i])) { + err = PTR_ERR(pg->resets[i]); + goto error; + } + } + + pg->num_resets = count; + return 0; + +error: + while (i--) + reset_control_put(pg->resets[i]); + kfree(pg->resets); + + return err; +} + +static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) +{ + struct tegra_powergate *pg; + bool off; + int id; + + pg = kzalloc(sizeof(*pg), GFP_KERNEL); + if (!pg) + goto error; + + id = tegra_powergate_lookup(pmc, np->name); + if (id < 0) + goto free_mem; + + /* + * Clear the bit for this powergate so it cannot be managed + * directly via the legacy APIs for controlling powergates. + */ + clear_bit(id, pmc->powergates_available); + + pg->id = id; + pg->genpd.name = np->name; + pg->genpd.power_off = tegra_genpd_power_off; + pg->genpd.power_on = tegra_genpd_power_on; + pg->pmc = pmc; + + if (tegra_powergate_of_get_clks(pg, np)) + goto set_available; + + if (tegra_powergate_of_get_resets(pg, np)) + goto remove_clks; + + off = !tegra_powergate_is_powered(pg->id); + + pm_genpd_init(&pg->genpd, NULL, off); + + if (of_genpd_add_provider_simple(np, &pg->genpd)) + goto remove_resets; + + dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name); + + return; + +remove_resets: + while (pg->num_resets--) + reset_control_put(pg->resets[pg->num_resets]); + kfree(pg->resets); + +remove_clks: + while (pg->num_clks--) + clk_put(pg->clks[pg->num_clks]); + kfree(pg->clks); + +set_available: + set_bit(id, pmc->powergates_available); + +free_mem: + kfree(pg); + +error: + dev_err(pmc->dev, "failed to create power domain for %s\n", np->name); +} + +static void tegra_powergate_init(struct tegra_pmc *pmc) +{ + struct device_node *np, *child; + + np = of_get_child_by_name(pmc->dev->of_node, "powergates"); + if (!np) + return; + + for_each_child_of_node(np, child) { + tegra_powergate_add(pmc, child); + of_node_put(child); + } + + of_node_put(np); } -static int tegra_io_rail_prepare(int id, unsigned long *request, +static int tegra_io_rail_prepare(unsigned int id, unsigned long *request, unsigned long *status, unsigned int *bit) { unsigned long rate, value; @@ -512,15 +898,17 @@ static void tegra_io_rail_unprepare(void) tegra_pmc_writel(DPD_SAMPLE_DISABLE, DPD_SAMPLE); } -int tegra_io_rail_power_on(int id) +int tegra_io_rail_power_on(unsigned int id) { unsigned long request, status, value; unsigned int bit, mask; int err; + mutex_lock(&pmc->powergates_lock); + err = tegra_io_rail_prepare(id, &request, &status, &bit); - if (err < 0) - return err; + if (err) + goto error; mask = 1 << bit; @@ -531,27 +919,32 @@ int tegra_io_rail_power_on(int id) tegra_pmc_writel(value, request); err = tegra_io_rail_poll(status, mask, 0, 250); - if (err < 0) { + if (err) { pr_info("tegra_io_rail_poll() failed: %d\n", err); - return err; + goto error; } tegra_io_rail_unprepare(); - return 0; +error: + mutex_unlock(&pmc->powergates_lock); + + return err; } EXPORT_SYMBOL(tegra_io_rail_power_on); -int tegra_io_rail_power_off(int id) +int tegra_io_rail_power_off(unsigned int id) { unsigned long request, status, value; unsigned int bit, mask; int err; + mutex_lock(&pmc->powergates_lock); + err = tegra_io_rail_prepare(id, &request, &status, &bit); - if (err < 0) { + if (err) { pr_info("tegra_io_rail_prepare() failed: %d\n", err); - return err; + goto error; } mask = 1 << bit; @@ -563,12 +956,15 @@ int tegra_io_rail_power_off(int id) tegra_pmc_writel(value, request); err = tegra_io_rail_poll(status, mask, mask, 250); - if (err < 0) - return err; + if (err) + goto error; tegra_io_rail_unprepare(); - return 0; +error: + mutex_unlock(&pmc->powergates_lock); + + return err; } EXPORT_SYMBOL(tegra_io_rail_power_off); @@ -727,7 +1123,7 @@ static void tegra_pmc_init(struct tegra_pmc *pmc) tegra_pmc_writel(value, PMC_CNTRL); } -void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) +static void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) { static const char disabled[] = "emergency thermal reset disabled"; u32 pmu_addr, ctrl_id, reg_addr, reg_data, pinmux; @@ -805,7 +1201,7 @@ out: static int tegra_pmc_probe(struct platform_device *pdev) { - void __iomem *base = pmc->base; + void __iomem *base; struct resource *res; int err; @@ -815,11 +1211,9 @@ static int tegra_pmc_probe(struct platform_device *pdev) /* take over the memory region from the early initialization */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pmc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pmc->base)) - return PTR_ERR(pmc->base); - - iounmap(base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); pmc->clk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(pmc->clk)) { @@ -842,11 +1236,19 @@ static int tegra_pmc_probe(struct platform_device *pdev) err = register_restart_handler(&tegra_pmc_restart_handler); if (err) { + debugfs_remove(pmc->debugfs); dev_err(&pdev->dev, "unable to register restart handler, %d\n", err); return err; } + tegra_powergate_init(pmc); + + mutex_lock(&pmc->powergates_lock); + iounmap(pmc->base); + pmc->base = base; + mutex_unlock(&pmc->powergates_lock); + return 0; } @@ -964,7 +1366,6 @@ static const char * const tegra124_powergates[] = { [TEGRA_POWERGATE_VENC] = "venc", [TEGRA_POWERGATE_PCIE] = "pcie", [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_L2] = "l2", [TEGRA_POWERGATE_MPE] = "mpe", [TEGRA_POWERGATE_HEG] = "heg", [TEGRA_POWERGATE_SATA] = "sata", @@ -1006,17 +1407,13 @@ static const char * const tegra210_powergates[] = { [TEGRA_POWERGATE_3D] = "3d", [TEGRA_POWERGATE_VENC] = "venc", [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_L2] = "l2", [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", [TEGRA_POWERGATE_SATA] = "sata", [TEGRA_POWERGATE_CPU1] = "cpu1", [TEGRA_POWERGATE_CPU2] = "cpu2", [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", [TEGRA_POWERGATE_CPU0] = "cpu0", [TEGRA_POWERGATE_C0NC] = "c0nc", - [TEGRA_POWERGATE_C1NC] = "c1nc", [TEGRA_POWERGATE_SOR] = "sor", [TEGRA_POWERGATE_DIS] = "dis", [TEGRA_POWERGATE_DISB] = "disb", @@ -1080,6 +1477,7 @@ static int __init tegra_pmc_early_init(void) const struct of_device_id *match; struct device_node *np; struct resource regs; + unsigned int i; bool invert; u32 value; @@ -1129,6 +1527,11 @@ static int __init tegra_pmc_early_init(void) return -ENXIO; } + /* Create a bit-map of the available and valid partitions */ + for (i = 0; i < pmc->soc->num_powergates; i++) + if (pmc->soc->powergates[i]) + set_bit(i, pmc->powergates_available); + mutex_init(&pmc->powergates_lock); /* diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 3050b18b2447..6acac6201af0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -69,6 +69,15 @@ 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 PHY_TEGRA_XUSB + 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 a9ddd3c9ec94..6ef785b0ea8f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o +obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c new file mode 100644 index 000000000000..0f53ae0f464e --- /dev/null +++ b/drivers/usb/host/xhci-tegra.c @@ -0,0 +1,1331 @@ +/* + * 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/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/phy/tegra/xusb.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include "xhci.h" + +#define TEGRA_XHCI_SS_HIGH_SPEED 120000000 +#define TEGRA_XHCI_SS_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 + +/* FPCI mailbox registers */ +#define XUSB_CFG_ARU_MBOX_CMD 0x0e4 +#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 0x0e8 +#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 0x0ec +#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0 +#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) + +/* 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_xusb_fw_header { + 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]; /* Pad to 256 bytes */ +}; + +struct tegra_xusb_phy_type { + const char *name; + unsigned int num; +}; + +struct tegra_xusb_soc { + const char *firmware; + const char * const *supply_names; + unsigned int num_supplies; + const struct tegra_xusb_phy_type *phy_types; + unsigned int num_types; + + struct { + struct { + unsigned int offset; + unsigned int count; + } usb2, ulpi, hsic, usb3; + } ports; + + bool scale_ss_clock; +}; + +struct tegra_xusb { + struct device *dev; + void __iomem *regs; + struct usb_hcd *hcd; + + struct mutex lock; + + int xhci_irq; + int mbox_irq; + + void __iomem *ipfs_base; + void __iomem *fpci_base; + + const struct tegra_xusb_soc *soc; + + struct regulator_bulk_data *supplies; + + struct tegra_xusb_padctl *padctl; + + struct clk *host_clk; + struct clk *falcon_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; + unsigned int num_phys; + + /* Firmware loading related */ + struct { + size_t size; + void *virt; + dma_addr_t phys; + } fw; +}; + +static struct hc_driver __read_mostly tegra_xhci_hc_driver; + +static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset) +{ + return readl(tegra->fpci_base + offset); +} + +static inline void fpci_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) +{ + writel(value, tegra->fpci_base + offset); +} + +static inline u32 ipfs_readl(struct tegra_xusb *tegra, unsigned int offset) +{ + return readl(tegra->ipfs_base + offset); +} + +static inline void ipfs_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) +{ + writel(value, tegra->ipfs_base + offset); +} + +static u32 csb_readl(struct tegra_xusb *tegra, unsigned int offset) +{ + u32 page = CSB_PAGE_SELECT(offset); + u32 ofs = CSB_PAGE_OFFSET(offset); + + fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE); + + return fpci_readl(tegra, XUSB_CFG_CSB_BASE_ADDR + ofs); +} + +static void csb_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) +{ + u32 page = CSB_PAGE_SELECT(offset); + u32 ofs = CSB_PAGE_OFFSET(offset); + + fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE); + fpci_writel(tegra, value, XUSB_CFG_CSB_BASE_ADDR + ofs); +} + +static int tegra_xusb_set_ss_clk(struct tegra_xusb *tegra, + unsigned long rate) +{ + unsigned long new_parent_rate, old_parent_rate; + struct clk *clk = tegra->ss_src_clk; + unsigned int div; + int err; + + if (clk_get_rate(clk) == rate) + return 0; + + switch (rate) { + case TEGRA_XHCI_SS_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; + + err = clk_set_rate(clk, old_parent_rate / div); + if (err) + return err; + + err = clk_set_parent(clk, tegra->pll_u_480m); + if (err) + return err; + + /* + * The rate should already be correct, but set it again just + * to be sure. + */ + err = clk_set_rate(clk, rate); + if (err) + return err; + + break; + + case TEGRA_XHCI_SS_LOW_SPEED: + /* Reparent to CLK_M */ + err = clk_set_parent(clk, tegra->clk_m); + if (err) + return err; + + err = clk_set_rate(clk, rate); + if (err) + return err; + + break; + + default: + dev_err(tegra->dev, "Invalid SS rate: %lu Hz\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 unsigned long extract_field(u32 value, unsigned int start, + unsigned int count) +{ + return (value >> start) & ((1 << count) - 1); +} + +/* 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_RESET_SSPI, + MBOX_CMD_DISABLE_SS_LFPS_DETECTION, + MBOX_CMD_ENABLE_SS_LFPS_DETECTION, + + MBOX_CMD_MAX, + + /* Response message to above commands */ + MBOX_CMD_ACK = 128, + MBOX_CMD_NAK +}; + +static const char * const mbox_cmd_name[] = { + [ 1] = "MSG_ENABLE", + [ 2] = "INC_FALCON_CLOCK", + [ 3] = "DEC_FALCON_CLOCK", + [ 4] = "INC_SSPI_CLOCK", + [ 5] = "DEC_SSPI_CLOCK", + [ 6] = "SET_BW", + [ 7] = "SET_SS_PWR_GATING", + [ 8] = "SET_SS_PWR_UNGATING", + [ 9] = "SAVE_DFE_CTLE_CTX", + [ 10] = "AIRPLANE_MODE_ENABLED", + [ 11] = "AIRPLANE_MODE_DISABLED", + [ 12] = "START_HSIC_IDLE", + [ 13] = "STOP_HSIC_IDLE", + [ 14] = "DBC_WAKE_STACK", + [ 15] = "HSIC_PRETEND_CONNECT", + [ 16] = "RESET_SSPI", + [ 17] = "DISABLE_SS_LFPS_DETECTION", + [ 18] = "ENABLE_SS_LFPS_DETECTION", + [128] = "ACK", + [129] = "NAK", +}; + +struct tegra_xusb_mbox_msg { + u32 cmd; + u32 data; +}; + +static inline u32 tegra_xusb_mbox_pack(const struct tegra_xusb_mbox_msg *msg) +{ + return (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT | + (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT; +} +static inline void tegra_xusb_mbox_unpack(struct tegra_xusb_mbox_msg *msg, + u32 value) +{ + msg->cmd = (value >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK; + msg->data = (value >> CMD_DATA_SHIFT) & CMD_DATA_MASK; +} + +static bool tegra_xusb_mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd) +{ + switch (cmd) { + case MBOX_CMD_SET_BW: + case MBOX_CMD_ACK: + case MBOX_CMD_NAK: + return false; + + default: + return true; + } +} + +static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, + const struct tegra_xusb_mbox_msg *msg) +{ + bool wait_for_idle = false; + u32 value; + + /* + * Acquire the mailbox. The firmware still owns the mailbox for + * ACK/NAK messages. + */ + if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) { + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + if (value != MBOX_OWNER_NONE) { + dev_err(tegra->dev, "mailbox is busy\n"); + return -EBUSY; + } + + fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER); + + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + if (value != MBOX_OWNER_SW) { + dev_err(tegra->dev, "failed to acquire mailbox\n"); + return -EBUSY; + } + + wait_for_idle = true; + } + + value = tegra_xusb_mbox_pack(msg); + fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN); + + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD); + value |= MBOX_INT_EN | MBOX_DEST_FALC; + fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD); + + if (wait_for_idle) { + unsigned long timeout = jiffies + msecs_to_jiffies(250); + + while (time_before(jiffies, timeout)) { + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + if (value == MBOX_OWNER_NONE) + break; + + usleep_range(10, 20); + } + + if (time_after(jiffies, timeout)) + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + + if (value != MBOX_OWNER_NONE) + return -ETIMEDOUT; + } + + return 0; +} + +static irqreturn_t tegra_xusb_mbox_irq(int irq, void *data) +{ + struct tegra_xusb *tegra = data; + u32 value; + + /* clear mailbox interrupts */ + value = fpci_readl(tegra, XUSB_CFG_ARU_SMI_INTR); + fpci_writel(tegra, value, XUSB_CFG_ARU_SMI_INTR); + + if (value & MBOX_SMI_INTR_FW_HANG) + dev_err(tegra->dev, "controller firmware hang\n"); + + return IRQ_WAKE_THREAD; +} + +static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra, + const struct tegra_xusb_mbox_msg *msg) +{ + struct tegra_xusb_padctl *padctl = tegra->padctl; + const struct tegra_xusb_soc *soc = tegra->soc; + struct device *dev = tegra->dev; + struct tegra_xusb_mbox_msg rsp; + unsigned long mask; + unsigned int port; + bool idle, enable; + int err; + + memset(&rsp, 0, sizeof(rsp)); + + switch (msg->cmd) { + case MBOX_CMD_INC_FALC_CLOCK: + case MBOX_CMD_DEC_FALC_CLOCK: + rsp.data = clk_get_rate(tegra->falcon_clk) / 1000; + if (rsp.data != msg->data) + rsp.cmd = MBOX_CMD_NAK; + else + rsp.cmd = MBOX_CMD_ACK; + + break; + + case MBOX_CMD_INC_SSPI_CLOCK: + case MBOX_CMD_DEC_SSPI_CLOCK: + if (tegra->soc->scale_ss_clock) { + err = tegra_xusb_set_ss_clk(tegra, msg->data * 1000); + if (err < 0) + rsp.cmd = MBOX_CMD_NAK; + else + rsp.cmd = MBOX_CMD_ACK; + + rsp.data = clk_get_rate(tegra->ss_src_clk) / 1000; + } else { + rsp.cmd = MBOX_CMD_ACK; + rsp.data = msg->data; + } + + 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; + + case MBOX_CMD_SAVE_DFE_CTLE_CTX: + err = tegra_xusb_padctl_usb3_save_context(padctl, msg->data); + if (err < 0) { + dev_err(dev, "failed to save context for USB3#%u: %d\n", + msg->data, err); + rsp.cmd = MBOX_CMD_NAK; + } else { + rsp.cmd = MBOX_CMD_ACK; + } + + rsp.data = msg->data; + break; + + case MBOX_CMD_START_HSIC_IDLE: + case MBOX_CMD_STOP_HSIC_IDLE: + if (msg->cmd == MBOX_CMD_STOP_HSIC_IDLE) + idle = false; + else + idle = true; + + mask = extract_field(msg->data, 1 + soc->ports.hsic.offset, + soc->ports.hsic.count); + + for_each_set_bit(port, &mask, 32) { + err = tegra_xusb_padctl_hsic_set_idle(padctl, port, + idle); + if (err < 0) + break; + } + + if (err < 0) { + dev_err(dev, "failed to set HSIC#%u %s: %d\n", port, + idle ? "idle" : "busy", err); + rsp.cmd = MBOX_CMD_NAK; + } else { + rsp.cmd = MBOX_CMD_ACK; + } + + rsp.data = msg->data; + break; + + case MBOX_CMD_DISABLE_SS_LFPS_DETECTION: + case MBOX_CMD_ENABLE_SS_LFPS_DETECTION: + if (msg->cmd == MBOX_CMD_DISABLE_SS_LFPS_DETECTION) + enable = false; + else + enable = true; + + mask = extract_field(msg->data, 1 + soc->ports.usb3.offset, + soc->ports.usb3.count); + + for_each_set_bit(port, &mask, soc->ports.usb3.count) { + err = tegra_xusb_padctl_usb3_set_lfps_detect(padctl, + port, + enable); + if (err < 0) + break; + } + + if (err < 0) { + dev_err(dev, + "failed to %s LFPS detection on USB3#%u: %d\n", + enable ? "enable" : "disable", port, err); + rsp.cmd = MBOX_CMD_NAK; + } else { + rsp.cmd = MBOX_CMD_ACK; + } + + rsp.data = msg->data; + break; + + default: + dev_warn(dev, "unknown message: %#x\n", msg->cmd); + break; + } + + if (rsp.cmd) { + const char *cmd = (rsp.cmd == MBOX_CMD_ACK) ? "ACK" : "NAK"; + + err = tegra_xusb_mbox_send(tegra, &rsp); + if (err < 0) + dev_err(dev, "failed to send %s: %d\n", cmd, err); + } +} + +static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) +{ + struct tegra_xusb *tegra = data; + struct tegra_xusb_mbox_msg msg; + u32 value; + + mutex_lock(&tegra->lock); + + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT); + tegra_xusb_mbox_unpack(&msg, value); + + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD); + value &= ~MBOX_DEST_SMI; + fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD); + + /* clear mailbox owner if no ACK/NAK is required */ + if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd)) + fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER); + + tegra_xusb_mbox_handle(tegra, &msg); + + mutex_unlock(&tegra->lock); + return IRQ_HANDLED; +} + +static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra, + struct resource *regs) +{ + u32 value; + + value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0); + value |= IPFS_EN_FPCI; + ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0); + + usleep_range(10, 20); + + /* Program BAR0 space */ + value = fpci_readl(tegra, XUSB_CFG_4); + value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); + value |= regs->start & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); + fpci_writel(tegra, value, XUSB_CFG_4); + + usleep_range(100, 200); + + /* Enable bus master */ + value = fpci_readl(tegra, XUSB_CFG_1); + value |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN; + fpci_writel(tegra, value, XUSB_CFG_1); + + /* Enable interrupt assertion */ + value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0); + value |= IPFS_IP_INT_MASK; + ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0); + + /* Set hysteresis */ + ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0); +} + +static int tegra_xusb_clk_enable(struct tegra_xusb *tegra) +{ + int err; + + err = clk_prepare_enable(tegra->pll_e); + if (err < 0) + return err; + + err = clk_prepare_enable(tegra->host_clk); + if (err < 0) + goto disable_plle; + + err = clk_prepare_enable(tegra->ss_clk); + if (err < 0) + goto disable_host; + + err = clk_prepare_enable(tegra->falcon_clk); + if (err < 0) + goto disable_ss; + + err = clk_prepare_enable(tegra->fs_src_clk); + if (err < 0) + goto disable_falc; + + err = clk_prepare_enable(tegra->hs_src_clk); + if (err < 0) + goto disable_fs_src; + + if (tegra->soc->scale_ss_clock) { + err = tegra_xusb_set_ss_clk(tegra, TEGRA_XHCI_SS_HIGH_SPEED); + if (err < 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->falcon_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 err; +} + +static void tegra_xusb_clk_disable(struct tegra_xusb *tegra) +{ + clk_disable_unprepare(tegra->pll_e); + clk_disable_unprepare(tegra->host_clk); + clk_disable_unprepare(tegra->ss_clk); + clk_disable_unprepare(tegra->falcon_clk); + clk_disable_unprepare(tegra->fs_src_clk); + clk_disable_unprepare(tegra->hs_src_clk); +} + +static int tegra_xusb_phy_enable(struct tegra_xusb *tegra) +{ + unsigned int i; + int err; + + for (i = 0; i < tegra->num_phys; i++) { + err = phy_init(tegra->phys[i]); + if (err) + goto disable_phy; + + err = phy_power_on(tegra->phys[i]); + if (err) { + phy_exit(tegra->phys[i]); + goto disable_phy; + } + } + + return 0; + +disable_phy: + while (i--) { + phy_power_off(tegra->phys[i]); + phy_exit(tegra->phys[i]); + } + + return err; +} + +static void tegra_xusb_phy_disable(struct tegra_xusb *tegra) +{ + unsigned int i; + + for (i = 0; i < tegra->num_phys; i++) { + phy_power_off(tegra->phys[i]); + phy_exit(tegra->phys[i]); + } +} + +static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) +{ + unsigned int code_tag_blocks, code_size_blocks, code_blocks; + struct tegra_xusb_fw_header *header; + struct device *dev = tegra->dev; + const struct firmware *fw; + unsigned long timeout; + time_t timestamp; + struct tm time; + u64 address; + u32 value; + int err; + + err = request_firmware(&fw, tegra->soc->firmware, tegra->dev); + if (err < 0) { + dev_err(tegra->dev, "failed to request firmware: %d\n", err); + return err; + } + + /* Load Falcon controller with its firmware. */ + header = (struct tegra_xusb_fw_header *)fw->data; + tegra->fw.size = le32_to_cpu(header->fwimg_len); + + tegra->fw.virt = dma_alloc_coherent(tegra->dev, tegra->fw.size, + &tegra->fw.phys, GFP_KERNEL); + if (!tegra->fw.virt) { + dev_err(tegra->dev, "failed to allocate memory for firmware\n"); + release_firmware(fw); + return -ENOMEM; + } + + header = (struct tegra_xusb_fw_header *)tegra->fw.virt; + memcpy(tegra->fw.virt, fw->data, tegra->fw.size); + release_firmware(fw); + + if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) { + dev_info(dev, "Firmware already loaded, Falcon state %#x\n", + csb_readl(tegra, XUSB_FALC_CPUCTL)); + return 0; + } + + /* 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. + */ + address = tegra->fw.phys + sizeof(*header); + csb_writel(tegra, address >> 32, XUSB_CSB_MP_ILOAD_BASE_HI); + csb_writel(tegra, address, XUSB_CSB_MP_ILOAD_BASE_LO); + + /* 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(header->boot_codetag), + IMEM_BLOCK_SIZE); + code_size_blocks = DIV_ROUND_UP(le32_to_cpu(header->boot_codesize), + IMEM_BLOCK_SIZE); + code_blocks = code_tag_blocks + code_size_blocks; + + value = ((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, value, 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); + + value = ((code_tag_blocks & IMFILLRNG1_TAG_MASK) << + IMFILLRNG1_TAG_LO_SHIFT) | + ((code_blocks & IMFILLRNG1_TAG_MASK) << + IMFILLRNG1_TAG_HI_SHIFT); + csb_writel(tegra, value, XUSB_FALC_IMFILLRNG1); + + csb_writel(tegra, 0, XUSB_FALC_DMACTL); + + msleep(50); + + csb_writel(tegra, le32_to_cpu(header->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; + } + + timestamp = le32_to_cpu(header->fwimg_created_time); + time_to_tm(timestamp, 0, &time); + + dev_info(dev, "Firmware timestamp: %ld-%02d-%02d %02d:%02d:%02d UTC\n", + time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, + time.tm_hour, time.tm_min, time.tm_sec); + + return 0; +} + +static int tegra_xusb_probe(struct platform_device *pdev) +{ + struct tegra_xusb_mbox_msg msg; + struct resource *res, *regs; + struct tegra_xusb *tegra; + struct xhci_hcd *xhci; + unsigned int i, j, k; + struct phy *phy; + int err; + + BUILD_BUG_ON(sizeof(struct tegra_xusb_fw_header) != 256); + + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return -ENOMEM; + + tegra->soc = of_device_get_match_data(&pdev->dev); + mutex_init(&tegra->lock); + tegra->dev = &pdev->dev; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tegra->regs = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(tegra->regs)) + return PTR_ERR(tegra->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->fpci_base)) + return PTR_ERR(tegra->fpci_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->ipfs_base)) + return PTR_ERR(tegra->ipfs_base); + + tegra->xhci_irq = platform_get_irq(pdev, 0); + if (tegra->xhci_irq < 0) + return tegra->xhci_irq; + + tegra->mbox_irq = platform_get_irq(pdev, 1); + if (tegra->mbox_irq < 0) + return tegra->mbox_irq; + + tegra->padctl = tegra_xusb_padctl_get(&pdev->dev); + if (IS_ERR(tegra->padctl)) + return PTR_ERR(tegra->padctl); + + tegra->host_rst = devm_reset_control_get(&pdev->dev, "xusb_host"); + if (IS_ERR(tegra->host_rst)) { + err = PTR_ERR(tegra->host_rst); + dev_err(&pdev->dev, "failed to get xusb_host reset: %d\n", err); + goto put_padctl; + } + + tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss"); + if (IS_ERR(tegra->ss_rst)) { + err = PTR_ERR(tegra->ss_rst); + dev_err(&pdev->dev, "failed to get xusb_ss reset: %d\n", err); + goto put_padctl; + } + + tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host"); + if (IS_ERR(tegra->host_clk)) { + err = PTR_ERR(tegra->host_clk); + dev_err(&pdev->dev, "failed to get xusb_host: %d\n", err); + goto put_padctl; + } + + tegra->falcon_clk = devm_clk_get(&pdev->dev, "xusb_falcon_src"); + if (IS_ERR(tegra->falcon_clk)) { + err = PTR_ERR(tegra->falcon_clk); + dev_err(&pdev->dev, "failed to get xusb_falcon_src: %d\n", err); + goto put_padctl; + } + + tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss"); + if (IS_ERR(tegra->ss_clk)) { + err = PTR_ERR(tegra->ss_clk); + dev_err(&pdev->dev, "failed to get xusb_ss: %d\n", err); + goto put_padctl; + } + + tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src"); + if (IS_ERR(tegra->ss_src_clk)) { + err = PTR_ERR(tegra->ss_src_clk); + dev_err(&pdev->dev, "failed to get xusb_ss_src: %d\n", err); + goto put_padctl; + } + + tegra->hs_src_clk = devm_clk_get(&pdev->dev, "xusb_hs_src"); + if (IS_ERR(tegra->hs_src_clk)) { + err = PTR_ERR(tegra->hs_src_clk); + dev_err(&pdev->dev, "failed to get xusb_hs_src: %d\n", err); + goto put_padctl; + } + + tegra->fs_src_clk = devm_clk_get(&pdev->dev, "xusb_fs_src"); + if (IS_ERR(tegra->fs_src_clk)) { + err = PTR_ERR(tegra->fs_src_clk); + dev_err(&pdev->dev, "failed to get xusb_fs_src: %d\n", err); + goto put_padctl; + } + + tegra->pll_u_480m = devm_clk_get(&pdev->dev, "pll_u_480m"); + if (IS_ERR(tegra->pll_u_480m)) { + err = PTR_ERR(tegra->pll_u_480m); + dev_err(&pdev->dev, "failed to get pll_u_480m: %d\n", err); + goto put_padctl; + } + + tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m"); + if (IS_ERR(tegra->clk_m)) { + err = PTR_ERR(tegra->clk_m); + dev_err(&pdev->dev, "failed to get clk_m: %d\n", err); + goto put_padctl; + } + + tegra->pll_e = devm_clk_get(&pdev->dev, "pll_e"); + if (IS_ERR(tegra->pll_e)) { + err = PTR_ERR(tegra->pll_e); + dev_err(&pdev->dev, "failed to get pll_e: %d\n", err); + goto put_padctl; + } + + tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies, + sizeof(*tegra->supplies), GFP_KERNEL); + if (!tegra->supplies) { + err = -ENOMEM; + goto put_padctl; + } + + for (i = 0; i < tegra->soc->num_supplies; i++) + tegra->supplies[i].supply = tegra->soc->supply_names[i]; + + err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies, + tegra->supplies); + if (err) { + dev_err(&pdev->dev, "failed to get regulators: %d\n", err); + goto put_padctl; + } + + for (i = 0; i < tegra->soc->num_types; i++) + tegra->num_phys += tegra->soc->phy_types[i].num; + + tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys, + sizeof(*tegra->phys), GFP_KERNEL); + if (!tegra->phys) { + dev_err(&pdev->dev, "failed to allocate PHY array\n"); + err = -ENOMEM; + goto put_padctl; + } + + for (i = 0, k = 0; i < tegra->soc->num_types; i++) { + char prop[8]; + + for (j = 0; j < tegra->soc->phy_types[i].num; j++) { + snprintf(prop, sizeof(prop), "%s-%d", + tegra->soc->phy_types[i].name, j); + + phy = devm_phy_optional_get(&pdev->dev, prop); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, + "failed to get PHY %s: %ld\n", prop, + PTR_ERR(phy)); + err = PTR_ERR(phy); + goto put_padctl; + } + + tegra->phys[k++] = phy; + } + } + + err = tegra_xusb_clk_enable(tegra); + if (err) { + dev_err(&pdev->dev, "failed to enable clocks: %d\n", err); + goto put_padctl; + } + + err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies); + if (err) { + dev_err(&pdev->dev, "failed to enable regulators: %d\n", err); + goto disable_clk; + } + + err = tegra_xusb_phy_enable(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err); + goto disable_regulator; + } + + tegra_xusb_ipfs_config(tegra, regs); + + err = tegra_xusb_load_firmware(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to load firmware: %d\n", err); + goto disable_phy; + } + + tegra->hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!tegra->hcd) { + err = -ENOMEM; + goto disable_phy; + } + + /* + * This must happen after usb_create_hcd(), because usb_create_hcd() + * will overwrite the drvdata of the device with the hcd it creates. + */ + platform_set_drvdata(pdev, tegra); + + tegra->hcd->regs = tegra->regs; + tegra->hcd->rsrc_start = regs->start; + tegra->hcd->rsrc_len = resource_size(regs); + + err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED); + if (err < 0) { + dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err); + goto put_usb2; + } + + device_wakeup_enable(tegra->hcd->self.controller); + + xhci = hcd_to_xhci(tegra->hcd); + + xhci->shared_hcd = usb_create_shared_hcd(&tegra_xhci_hc_driver, + &pdev->dev, + dev_name(&pdev->dev), + tegra->hcd); + if (!xhci->shared_hcd) { + dev_err(&pdev->dev, "failed to create shared HCD\n"); + goto remove_usb2; + } + + err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED); + if (err < 0) { + dev_err(&pdev->dev, "failed to add shared HCD: %d\n", err); + goto put_usb3; + } + + mutex_lock(&tegra->lock); + + /* Enable firmware messages from controller. */ + msg.cmd = MBOX_CMD_MSG_ENABLED; + msg.data = 0; + + err = tegra_xusb_mbox_send(tegra, &msg); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable messages: %d\n", err); + mutex_unlock(&tegra->lock); + goto remove_usb3; + } + + mutex_unlock(&tegra->lock); + + err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq, + tegra_xusb_mbox_irq, + tegra_xusb_mbox_thread, 0, + dev_name(&pdev->dev), tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); + goto remove_usb3; + } + + return 0; + +remove_usb3: + usb_remove_hcd(xhci->shared_hcd); +put_usb3: + usb_put_hcd(xhci->shared_hcd); +remove_usb2: + usb_remove_hcd(tegra->hcd); +put_usb2: + usb_put_hcd(tegra->hcd); +disable_phy: + tegra_xusb_phy_disable(tegra); +disable_regulator: + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); +disable_clk: + tegra_xusb_clk_disable(tegra); +put_padctl: + tegra_xusb_padctl_put(tegra->padctl); + return err; +} + +static int tegra_xusb_remove(struct platform_device *pdev) +{ + struct tegra_xusb *tegra = platform_get_drvdata(pdev); + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + + usb_remove_hcd(xhci->shared_hcd); + usb_put_hcd(xhci->shared_hcd); + usb_remove_hcd(tegra->hcd); + usb_put_hcd(tegra->hcd); + + dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt, + tegra->fw.phys); + + tegra_xusb_phy_disable(tegra); + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); + tegra_xusb_clk_disable(tegra); + + tegra_xusb_padctl_put(tegra->padctl); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_xusb_suspend(struct device *dev) +{ + struct tegra_xusb *tegra = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + bool wakeup = device_may_wakeup(dev); + + /* TODO: Powergate controller across suspend/resume. */ + return xhci_suspend(xhci, wakeup); +} + +static int tegra_xusb_resume(struct device *dev) +{ + struct tegra_xusb *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_xusb_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_xusb_suspend, tegra_xusb_resume) +}; + +static const char * const tegra124_supply_names[] = { + "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 tegra_xusb_phy_type tegra124_phy_types[] = { + { .name = "usb3", .num = 2, }, + { .name = "usb2", .num = 3, }, + { .name = "hsic", .num = 2, }, +}; + +static const struct tegra_xusb_soc tegra124_soc = { + .firmware = "nvidia/tegra124/xusb.bin", + .supply_names = tegra124_supply_names, + .num_supplies = ARRAY_SIZE(tegra124_supply_names), + .phy_types = tegra124_phy_types, + .num_types = ARRAY_SIZE(tegra124_phy_types), + .ports = { + .usb2 = { .offset = 4, .count = 4, }, + .hsic = { .offset = 6, .count = 2, }, + .usb3 = { .offset = 0, .count = 2, }, + }, + .scale_ss_clock = true, +}; +MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); + +static const char * const tegra210_supply_names[] = { + "dvddio-pex", + "hvddio-pex", + "avdd-usb", + "avdd-pll-utmip", + "avdd-pll-uerefe", + "dvdd-pex-pll", + "hvdd-pex-pll-e", +}; + +static const struct tegra_xusb_phy_type tegra210_phy_types[] = { + { .name = "usb3", .num = 4, }, + { .name = "usb2", .num = 4, }, + { .name = "hsic", .num = 1, }, +}; + +static const struct tegra_xusb_soc tegra210_soc = { + .firmware = "nvidia/tegra210/xusb.bin", + .supply_names = tegra210_supply_names, + .num_supplies = ARRAY_SIZE(tegra210_supply_names), + .phy_types = tegra210_phy_types, + .num_types = ARRAY_SIZE(tegra210_phy_types), + .ports = { + .usb2 = { .offset = 4, .count = 4, }, + .hsic = { .offset = 8, .count = 1, }, + .usb3 = { .offset = 0, .count = 4, }, + }, + .scale_ss_clock = false, +}; +MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); + +static const struct of_device_id tegra_xusb_of_match[] = { + { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc }, + { .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); + +static struct platform_driver tegra_xusb_driver = { + .probe = tegra_xusb_probe, + .remove = tegra_xusb_remove, + .driver = { + .name = "tegra-xusb", + .pm = &tegra_xusb_pm_ops, + .of_match_table = tegra_xusb_of_match, + }, +}; + +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 xhci_driver_overrides tegra_xhci_overrides __initconst = { + .extra_priv_size = sizeof(struct xhci_hcd), + .reset = tegra_xhci_setup, +}; + +static int __init tegra_xusb_init(void) +{ + xhci_init_driver(&tegra_xhci_hc_driver, &tegra_xhci_overrides); + + return platform_driver_register(&tegra_xusb_driver); +} +module_init(tegra_xusb_init); + +static void __exit tegra_xusb_exit(void) +{ + platform_driver_unregister(&tegra_xusb_driver); +} +module_exit(tegra_xusb_exit); + +MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); +MODULE_DESCRIPTION("NVIDIA Tegra XUSB xHCI host-controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/power/rk3399-power.h b/include/dt-bindings/power/rk3399-power.h new file mode 100644 index 000000000000..168b3bfbd6f5 --- /dev/null +++ b/include/dt-bindings/power/rk3399-power.h @@ -0,0 +1,53 @@ +#ifndef __DT_BINDINGS_POWER_RK3399_POWER_H__ +#define __DT_BINDINGS_POWER_RK3399_POWER_H__ + +/* VD_CORE_L */ +#define RK3399_PD_A53_L0 0 +#define RK3399_PD_A53_L1 1 +#define RK3399_PD_A53_L2 2 +#define RK3399_PD_A53_L3 3 +#define RK3399_PD_SCU_L 4 + +/* VD_CORE_B */ +#define RK3399_PD_A72_B0 5 +#define RK3399_PD_A72_B1 6 +#define RK3399_PD_SCU_B 7 + +/* VD_LOGIC */ +#define RK3399_PD_TCPD0 8 +#define RK3399_PD_TCPD1 9 +#define RK3399_PD_CCI 10 +#define RK3399_PD_CCI0 11 +#define RK3399_PD_CCI1 12 +#define RK3399_PD_PERILP 13 +#define RK3399_PD_PERIHP 14 +#define RK3399_PD_VIO 15 +#define RK3399_PD_VO 16 +#define RK3399_PD_VOPB 17 +#define RK3399_PD_VOPL 18 +#define RK3399_PD_ISP0 19 +#define RK3399_PD_ISP1 20 +#define RK3399_PD_HDCP 21 +#define RK3399_PD_GMAC 22 +#define RK3399_PD_EMMC 23 +#define RK3399_PD_USB3 24 +#define RK3399_PD_EDP 25 +#define RK3399_PD_GIC 26 +#define RK3399_PD_SD 27 +#define RK3399_PD_SDIOAUDIO 28 +#define RK3399_PD_ALIVE 29 + +/* VD_CENTER */ +#define RK3399_PD_CENTER 30 +#define RK3399_PD_VCODEC 31 +#define RK3399_PD_VDU 32 +#define RK3399_PD_RGA 33 +#define RK3399_PD_IEP 34 + +/* VD_GPU */ +#define RK3399_PD_GPU 35 + +/* VD_PMU */ +#define RK3399_PD_PMU 36 + +#endif diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 7adfd80fbf55..ba6fa4148515 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -24,12 +24,20 @@ void r8a7778_clocks_init(u32 mode); void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); -#ifdef CONFIG_PM_GENERIC_DOMAINS_OF void cpg_mstp_add_clk_domain(struct device_node *np); -int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev); -void cpg_mstp_detach_dev(struct generic_pm_domain *domain, struct device *dev); +#ifdef CONFIG_CLK_RENESAS_CPG_MSTP +int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev); +void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev); #else -static inline void cpg_mstp_add_clk_domain(struct device_node *np) {} +#define cpg_mstp_attach_dev NULL +#define cpg_mstp_detach_dev NULL #endif +#ifdef CONFIG_CLK_RENESAS_CPG_MSSR +int cpg_mssr_attach_dev(struct generic_pm_domain *unused, struct device *dev); +void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev); +#else +#define cpg_mssr_attach_dev NULL +#define cpg_mssr_detach_dev NULL +#endif #endif diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h index 57bf7aab4516..7007a5f48080 100644 --- a/include/linux/clk/tegra.h +++ b/include/linux/clk/tegra.h @@ -121,4 +121,9 @@ static inline void tegra_cpu_clock_resume(void) } #endif +extern void tegra210_xusb_pll_hw_control_enable(void); +extern void tegra210_xusb_pll_hw_sequence_start(void); +extern void tegra210_sata_pll_hw_control_enable(void); +extern void tegra210_sata_pll_hw_sequence_start(void); + #endif /* __LINUX_CLK_TEGRA_H_ */ diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 8cf05e341cff..a810f2a18842 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -77,6 +77,7 @@ struct phy { */ struct phy_provider { struct device *dev; + struct device_node *children; struct module *owner; struct list_head list; struct phy * (*of_xlate)(struct device *dev, @@ -93,10 +94,16 @@ struct phy_lookup { #define to_phy(a) (container_of((a), struct phy, dev)) #define of_phy_provider_register(dev, xlate) \ - __of_phy_provider_register((dev), THIS_MODULE, (xlate)) + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate)) #define devm_of_phy_provider_register(dev, xlate) \ - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate)) + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate)) + +#define of_phy_provider_register_full(dev, children, xlate) \ + __of_phy_provider_register(dev, children, THIS_MODULE, xlate) + +#define devm_of_phy_provider_register_full(dev, children, xlate) \ + __devm_of_phy_provider_register(dev, children, THIS_MODULE, xlate) static inline void phy_set_drvdata(struct phy *phy, void *data) { @@ -147,11 +154,13 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node, void phy_destroy(struct phy *phy); void devm_phy_destroy(struct device *dev, struct phy *phy); struct phy_provider *__of_phy_provider_register(struct device *dev, - struct module *owner, struct phy * (*of_xlate)(struct device *dev, - struct of_phandle_args *args)); + struct device_node *children, struct module *owner, + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)); struct phy_provider *__devm_of_phy_provider_register(struct device *dev, - struct module *owner, struct phy * (*of_xlate)(struct device *dev, - struct of_phandle_args *args)); + struct device_node *children, struct module *owner, + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)); void of_phy_provider_unregister(struct phy_provider *phy_provider); void devm_of_phy_provider_unregister(struct device *dev, struct phy_provider *phy_provider); @@ -312,15 +321,17 @@ static inline void devm_phy_destroy(struct device *dev, struct phy *phy) } static inline struct phy_provider *__of_phy_provider_register( - struct device *dev, struct module *owner, struct phy * (*of_xlate)( - struct device *dev, struct of_phandle_args *args)) + struct device *dev, struct device_node *children, struct module *owner, + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)) { return ERR_PTR(-ENOSYS); } static inline struct phy_provider *__devm_of_phy_provider_register(struct device - *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev, - struct of_phandle_args *args)) + *dev, struct device_node *children, struct module *owner, + struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)) { return ERR_PTR(-ENOSYS); } diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h new file mode 100644 index 000000000000..8e1a57a78d9f --- /dev/null +++ b/include/linux/phy/tegra/xusb.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef PHY_TEGRA_XUSB_H +#define PHY_TEGRA_XUSB_H + +struct tegra_xusb_padctl; +struct device; + +struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev); +void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl); + +int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl, + unsigned int port); +int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, + unsigned int port, bool idle); +int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, + unsigned int port, bool enable); + +#endif /* PHY_TEGRA_XUSB_H */ diff --git a/include/linux/psci.h b/include/linux/psci.h index 393efe2edf9a..bdea1cb5e1db 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -21,8 +21,6 @@ #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 bool psci_tos_resident_on(int cpu); -bool psci_power_state_loses_context(u32 state); -bool psci_power_state_is_valid(u32 state); int psci_cpu_init_idle(unsigned int cpu); int psci_cpu_suspend_enter(unsigned long index); diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index a3a5bcdb1d02..b91ba932bbd4 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -31,6 +31,7 @@ struct of_phandle_args; * @ops: a pointer to device specific struct reset_control_ops * @owner: kernel module of the reset controller driver * @list: internal list of reset controller devices + * @reset_control_head: head of internal list of requested reset controls * @of_node: corresponding device tree node as phandle target * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the @@ -41,6 +42,7 @@ struct reset_controller_dev { const struct reset_control_ops *ops; struct module *owner; struct list_head list; + struct list_head reset_control_head; struct device_node *of_node; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, diff --git a/include/linux/reset.h b/include/linux/reset.h index c4c097de0ba9..ec0306ce7b92 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -1,8 +1,8 @@ #ifndef _LINUX_RESET_H_ #define _LINUX_RESET_H_ -struct device; -struct device_node; +#include <linux/device.h> + struct reset_control; #ifdef CONFIG_RESET_CONTROLLER @@ -12,9 +12,11 @@ int reset_control_assert(struct reset_control *rstc); int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); -struct reset_control *reset_control_get(struct device *dev, const char *id); +struct reset_control *__of_reset_control_get(struct device_node *node, + const char *id, int index, int shared); void reset_control_put(struct reset_control *rstc); -struct reset_control *devm_reset_control_get(struct device *dev, const char *id); +struct reset_control *__devm_reset_control_get(struct device *dev, + const char *id, int index, int shared); int __must_check device_reset(struct device *dev); @@ -23,24 +25,6 @@ static inline int device_reset_optional(struct device *dev) return device_reset(dev); } -static inline struct reset_control *reset_control_get_optional( - struct device *dev, const char *id) -{ - return reset_control_get(dev, id); -} - -static inline struct reset_control *devm_reset_control_get_optional( - struct device *dev, const char *id) -{ - return devm_reset_control_get(dev, id); -} - -struct reset_control *of_reset_control_get(struct device_node *node, - const char *id); - -struct reset_control *of_reset_control_get_by_index( - struct device_node *node, int index); - #else static inline int reset_control_reset(struct reset_control *rstc) @@ -72,49 +56,191 @@ static inline void reset_control_put(struct reset_control *rstc) WARN_ON(1); } +static inline int __must_check device_reset(struct device *dev) +{ + WARN_ON(1); + return -ENOTSUPP; +} + static inline int device_reset_optional(struct device *dev) { return -ENOTSUPP; } -static inline struct reset_control *__must_check reset_control_get( - struct device *dev, const char *id) +static inline struct reset_control *__of_reset_control_get( + struct device_node *node, + const char *id, int index, int shared) { - WARN_ON(1); return ERR_PTR(-EINVAL); } -static inline struct reset_control *__must_check devm_reset_control_get( +static inline struct reset_control *__devm_reset_control_get( + struct device *dev, + const char *id, int index, int shared) +{ + return ERR_PTR(-EINVAL); +} + +#endif /* CONFIG_RESET_CONTROLLER */ + +/** + * reset_control_get - Lookup and obtain an exclusive reference to a + * reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * If this function is called more then once for the same reset_control it will + * return -EBUSY. + * + * See reset_control_get_shared for details on shared references to + * reset-controls. + * + * Use of id names is optional. + */ +static inline struct reset_control *__must_check reset_control_get( struct device *dev, const char *id) { +#ifndef CONFIG_RESET_CONTROLLER WARN_ON(1); - return ERR_PTR(-EINVAL); +#endif + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0); } static inline struct reset_control *reset_control_get_optional( struct device *dev, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0); } -static inline struct reset_control *devm_reset_control_get_optional( +/** + * reset_control_get_shared - Lookup and obtain a shared reference to a + * reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * This function is intended for use with reset-controls which are shared + * between hardware-blocks. + * + * When a reset-control is shared, the behavior of reset_control_assert / + * deassert is changed, the reset-core will keep track of a deassert_count + * and only (re-)assert the reset after reset_control_assert has been called + * as many times as reset_control_deassert was called. Also see the remark + * about shared reset-controls in the reset_control_assert docs. + * + * Calling reset_control_assert without first calling reset_control_deassert + * is not allowed on a shared reset control. Calling reset_control_reset is + * also not allowed on a shared reset control. + * + * Use of id names is optional. + */ +static inline struct reset_control *reset_control_get_shared( struct device *dev, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 1); } +/** + * of_reset_control_get - Lookup and obtain an exclusive reference to a + * reset controller. + * @node: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ static inline struct reset_control *of_reset_control_get( struct device_node *node, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(node, id, 0, 0); } +/** + * of_reset_control_get_by_index - Lookup and obtain an exclusive reference to + * a reset controller by index. + * @node: device to be reset by the controller + * @index: index of the reset controller + * + * This is to be used to perform a list of resets for a device or power domain + * in whatever order. Returns a struct reset_control or IS_ERR() condition + * containing errno. + */ static inline struct reset_control *of_reset_control_get_by_index( - struct device_node *node, int index) + struct device_node *node, int index) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(node, NULL, index, 0); } -#endif /* CONFIG_RESET_CONTROLLER */ +/** + * devm_reset_control_get - resource managed reset_control_get() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +static inline struct reset_control *__must_check devm_reset_control_get( + struct device *dev, const char *id) +{ +#ifndef CONFIG_RESET_CONTROLLER + WARN_ON(1); +#endif + return __devm_reset_control_get(dev, id, 0, 0); +} + +static inline struct reset_control *devm_reset_control_get_optional( + struct device *dev, const char *id) +{ + return __devm_reset_control_get(dev, id, 0, 0); +} + +/** + * devm_reset_control_get_by_index - resource managed reset_control_get + * @dev: device to be reset by the controller + * @index: index of the reset controller + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +static inline struct reset_control *devm_reset_control_get_by_index( + struct device *dev, int index) +{ + return __devm_reset_control_get(dev, NULL, index, 0); +} + +/** + * devm_reset_control_get_shared - resource managed reset_control_get_shared() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get_shared(). For reset controllers returned from + * this function, reset_control_put() is called automatically on driver detach. + * See reset_control_get_shared() for more information. + */ +static inline struct reset_control *devm_reset_control_get_shared( + struct device *dev, const char *id) +{ + return __devm_reset_control_get(dev, id, 0, 1); +} + +/** + * devm_reset_control_get_shared_by_index - resource managed + * reset_control_get_shared + * @dev: device to be reset by the controller + * @index: index of the reset controller + * + * Managed reset_control_get_shared(). For reset controllers returned from + * this function, reset_control_put() is called automatically on driver detach. + * See reset_control_get_shared() for more information. + */ +static inline struct reset_control *devm_reset_control_get_shared_by_index( + struct device *dev, int index) +{ + return __devm_reset_control_get(dev, NULL, index, 1); +} #endif diff --git a/arch/arm/mach-shmobile/pm-rcar.h b/include/linux/soc/renesas/rcar-sysc.h index 1b901db4a24c..92fc613ab23d 100644 --- a/arch/arm/mach-shmobile/pm-rcar.h +++ b/include/linux/soc/renesas/rcar-sysc.h @@ -1,5 +1,7 @@ -#ifndef PM_RCAR_H -#define PM_RCAR_H +#ifndef __LINUX_SOC_RENESAS_RCAR_SYSC_H__ +#define __LINUX_SOC_RENESAS_RCAR_SYSC_H__ + +#include <linux/types.h> struct rcar_sysc_ch { u16 chan_offs; @@ -9,7 +11,6 @@ struct rcar_sysc_ch { int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch); int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch); -bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch); void __iomem *rcar_sysc_init(phys_addr_t base); -#endif /* PM_RCAR_H */ +#endif /* __LINUX_SOC_RENESAS_RCAR_SYSC_H__ */ diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 961b821b6a46..b4c9219e7f95 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -26,6 +26,7 @@ #define TEGRA_FUSE_SKU_CALIB_0 0xf0 #define TEGRA30_FUSE_SATA_CALIB 0x124 +#define TEGRA_FUSE_USB_CALIB_EXT_0 0x250 #ifndef __ASSEMBLY__ diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index d18efe402ff1..e9e53473a63e 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -33,9 +33,9 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_SMP -bool tegra_pmc_cpu_is_powered(int cpuid); -int tegra_pmc_cpu_power_on(int cpuid); -int tegra_pmc_cpu_remove_clamping(int cpuid); +bool tegra_pmc_cpu_is_powered(unsigned int cpuid); +int tegra_pmc_cpu_power_on(unsigned int cpuid); +int tegra_pmc_cpu_remove_clamping(unsigned int cpuid); #endif /* CONFIG_SMP */ /* @@ -72,6 +72,7 @@ int tegra_pmc_cpu_remove_clamping(int cpuid); #define TEGRA_POWERGATE_AUD 27 #define TEGRA_POWERGATE_DFD 28 #define TEGRA_POWERGATE_VE2 29 +#define TEGRA_POWERGATE_MAX TEGRA_POWERGATE_VE2 #define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D @@ -108,50 +109,51 @@ int tegra_pmc_cpu_remove_clamping(int cpuid); #define TEGRA_IO_RAIL_SYS_DDC 58 #ifdef CONFIG_ARCH_TEGRA -int tegra_powergate_is_powered(int id); -int tegra_powergate_power_on(int id); -int tegra_powergate_power_off(int id); -int tegra_powergate_remove_clamping(int id); +int tegra_powergate_is_powered(unsigned int id); +int tegra_powergate_power_on(unsigned int id); +int tegra_powergate_power_off(unsigned int id); +int tegra_powergate_remove_clamping(unsigned int id); /* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk, +int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, struct reset_control *rst); -int tegra_io_rail_power_on(int id); -int tegra_io_rail_power_off(int id); +int tegra_io_rail_power_on(unsigned int id); +int tegra_io_rail_power_off(unsigned int id); #else -static inline int tegra_powergate_is_powered(int id) +static inline int tegra_powergate_is_powered(unsigned int id) { return -ENOSYS; } -static inline int tegra_powergate_power_on(int id) +static inline int tegra_powergate_power_on(unsigned int id) { return -ENOSYS; } -static inline int tegra_powergate_power_off(int id) +static inline int tegra_powergate_power_off(unsigned int id) { return -ENOSYS; } -static inline int tegra_powergate_remove_clamping(int id) +static inline int tegra_powergate_remove_clamping(unsigned int id) { return -ENOSYS; } -static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, +static inline int tegra_powergate_sequence_power_up(unsigned int id, + struct clk *clk, struct reset_control *rst) { return -ENOSYS; } -static inline int tegra_io_rail_power_on(int id) +static inline int tegra_io_rail_power_on(unsigned int id) { return -ENOSYS; } -static inline int tegra_io_rail_power_off(int id) +static inline int tegra_io_rail_power_off(unsigned int id) { return -ENOSYS; } |