forked from Minki/linux
phy: for 4.5
*) new PHY driver for hi6220 usb and rcar gen3 usb2 *) deprecate phy-omap-control driver. phy-omap-control driver was added when there was no proper infrastructure for doing control module initialization. The phy-omap-control driver is not an 'actual' PHY driver and it was just a hack to do PHY related control module initialization. Now with SYSCON framework in the kernel, control module setttings can be done using APIs provided by syscon. *) usbphy-internal pll creates the needed 480MHz and is also a supply-clock back to the core clock-controller in Rockchip SoCs. This is now modeled as a real clock. *) calibrate mt65xx usb3 PHY for better eye diagram and receiver sensitivity. *) Miscellaneous cleanups. Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJWeR+CAAoJEA5ceFyATYLZG8QP/jlux4ejRKhaO/BYd2gSEOij H6CO+3O+oQqgKHdDvgGrE5vAqRfxBZI9UErOEBUcPOY+Qdp0s5Wg657WuJR1sSDK iNwho1AS2ZuQHqxVDaDToBCTIwliDVkT0+eIgq5tGZaHBh0/iOUEzjnODcaR36a/ VFPPTTFmkeX9Y0qzyWx/eVASBNguKo6/8/h9PV0zIFkunaE28rDvR2F9x4QSfBaV 737iivHmVxe+/dQ3EwLymrVu1LMwporEqctWUFzUVHsSZfJH7lCErzCs5qPNCoOw OqqHJXmk+FtYix3GMj+sGSD3qR83ml031EeCxpmHQ4OVNfTTIodF/po9K9NRkM8K 4rA9EkV5xjgWZFw/38ANvozcUXDbVOKMqhwMAH8hLWvIDzO4HfPY/hpRpXzTc+vo c7QowH+SP/8lXWGEVvFNjDWaowC7ajkAq94RFXQLTucySP9jgaDDD9nK2onMPD2S Z2t7QXgdu9zuLziWeigriSIsjQIJ7gCfAHpUAaeX9VDocgzTqOoweYFTQuU89S9S FrfE/B3k+ZI+RDwvsbAShzmMxoYSpct5fhwjA+6D0L1v7piU0GSAT5wMGYFumC32 BQWi41I2o5ory3rOOfzFa5GUxBEG8dJNjIgJKvcMT/2QH0zinY6c03a1wN+V7iRV i1YqBUUvHCEnbsNHrxmN =Bu7H -----END PGP SIGNATURE----- Merge tag 'phy-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next Kishon writes: phy: for 4.5 *) new PHY driver for hi6220 usb and rcar gen3 usb2 *) deprecate phy-omap-control driver. phy-omap-control driver was added when there was no proper infrastructure for doing control module initialization. The phy-omap-control driver is not an 'actual' PHY driver and it was just a hack to do PHY related control module initialization. Now with SYSCON framework in the kernel, control module setttings can be done using APIs provided by syscon. *) usbphy-internal pll creates the needed 480MHz and is also a supply-clock back to the core clock-controller in Rockchip SoCs. This is now modeled as a real clock. *) calibrate mt65xx usb3 PHY for better eye diagram and receiver sensitivity. *) Miscellaneous cleanups. Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
commit
daf273350d
@ -2,6 +2,7 @@
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one or more of
|
||||
"brcm,bcm7425-sata-phy"
|
||||
"brcm,bcm7445-sata-phy"
|
||||
"brcm,phy-sata3"
|
||||
- address-cells: should be 1
|
||||
|
16
Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt
Normal file
16
Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt
Normal file
@ -0,0 +1,16 @@
|
||||
Hisilicon hi6220 usb PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "hisilicon,hi6220-usb-phy"
|
||||
- #phy-cells: must be 0
|
||||
- hisilicon,peripheral-syscon: phandle of syscon used to control phy.
|
||||
Refer to phy/phy-bindings.txt for the generic PHY binding properties
|
||||
|
||||
Example:
|
||||
usb_phy: usbphy {
|
||||
compatible = "hisilicon,hi6220-usb-phy";
|
||||
#phy-cells = <0>;
|
||||
phy-supply = <&fixed_5v_hub>;
|
||||
hisilicon,peripheral-syscon = <&sys_ctrl>;
|
||||
};
|
39
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
Normal file
39
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
Normal file
@ -0,0 +1,39 @@
|
||||
* Renesas R-Car generation 3 USB 2.0 PHY
|
||||
|
||||
This file provides information on what the device node for the R-Car generation
|
||||
3 USB 2.0 PHY contains.
|
||||
|
||||
Required properties:
|
||||
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
|
||||
SoC.
|
||||
- reg: offset and length of the partial USB 2.0 Host register block.
|
||||
- reg-names: must be "usb2_host".
|
||||
- clocks: clock phandle and specifier pair(s).
|
||||
- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
|
||||
|
||||
Optional properties:
|
||||
To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
|
||||
combined, the device tree node should set HSUSB properties to reg and reg-names
|
||||
properties. This is because HSUSB has registers to select USB 2.0 host or
|
||||
peripheral at that channel:
|
||||
- reg: offset and length of the partial HSUSB register block.
|
||||
- reg-names: must be "hsusb".
|
||||
- interrupts: interrupt specifier for the PHY.
|
||||
|
||||
Example (R-Car H3):
|
||||
|
||||
usb-phy@ee080200 {
|
||||
compatible = "renesas,usb2-phy-r8a7795";
|
||||
reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>;
|
||||
reg-names = "usb2_host", "hsusb";
|
||||
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>,
|
||||
<&mstp7_clks R8A7795_CLK_HSUSB>;
|
||||
};
|
||||
|
||||
usb-phy@ee0a0200 {
|
||||
compatible = "renesas,usb2-phy-r8a7795";
|
||||
reg = <0 0xee0a0200 0 0x700>;
|
||||
reg-names = "usb2_host";
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
|
||||
};
|
@ -1,7 +1,10 @@
|
||||
ROCKCHIP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: rockchip,rk3288-usb-phy
|
||||
- compatible: matching the soc type, one of
|
||||
"rockchip,rk3066a-usb-phy"
|
||||
"rockchip,rk3188-usb-phy"
|
||||
"rockchip,rk3288-usb-phy"
|
||||
- rockchip,grf : phandle to the syscon managing the "general
|
||||
register files"
|
||||
- #address-cells: should be 1
|
||||
@ -21,6 +24,7 @@ required properties:
|
||||
Optional Properties:
|
||||
- clocks : phandle + clock specifier for the phy clocks
|
||||
- clock-names: string, clock name, must be "phyclk"
|
||||
- #clock-cells: for users of the phy-pll, should be 0
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -9,6 +9,7 @@ Required properties:
|
||||
* allwinner,sun7i-a20-usb-phy
|
||||
* allwinner,sun8i-a23-usb-phy
|
||||
* allwinner,sun8i-a33-usb-phy
|
||||
* allwinner,sun8i-h3-usb-phy
|
||||
- reg : a list of offset + length pairs
|
||||
- reg-names :
|
||||
* "phy_ctrl"
|
||||
|
@ -31,6 +31,8 @@ OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
|
||||
in DRA7x
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
@ -40,10 +42,14 @@ Required properties:
|
||||
* "wkupclk" - wakeup clock.
|
||||
* "refclk" - reference clock (optional).
|
||||
|
||||
Optional properties:
|
||||
Deprecated properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
Recommended properies:
|
||||
- syscon-phy-power : phandle/offset pair. Phandle to the system control
|
||||
module and the register offset to power on/off the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
@ -77,14 +83,22 @@ Required properties:
|
||||
* "div-clk" - apll clock
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
- id: If there are multiple instance of the same type, in order to
|
||||
differentiate between each instance "id" can be used (e.g., multi-lane PCIe
|
||||
PHY). If "id" is not provided, it is set to default value of '1'.
|
||||
- syscon-pllreset: Handle to system control region that contains the
|
||||
CTRL_CORE_SMA_SW_0 register and register offset to the CTRL_CORE_SMA_SW_0
|
||||
register that contains the SATA_PLL_SOFT_RESET bit. Only valid for sata_phy.
|
||||
- syscon-pcs : phandle/offset pair. Phandle to the system control module and the
|
||||
register offset to write the PCS delay value.
|
||||
|
||||
Deprecated properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
Recommended properies:
|
||||
- syscon-phy-power : phandle/offset pair. Phandle to the system control
|
||||
module and the register offset to power on/off the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
|
@ -8961,6 +8961,12 @@ L: linux-sh@vger.kernel.org
|
||||
F: drivers/net/ethernet/renesas/
|
||||
F: include/linux/sh_eth.h
|
||||
|
||||
RENESAS USB2 PHY DRIVER
|
||||
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
L: linux-sh@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/phy/phy-rcar-gen3-usb2.c
|
||||
|
||||
RESET CONTROLLER FRAMEWORK
|
||||
M: Philipp Zabel <p.zabel@pengutronix.de>
|
||||
S: Maintained
|
||||
|
@ -118,6 +118,13 @@ config PHY_RCAR_GEN2
|
||||
help
|
||||
Support for USB PHY found on Renesas R-Car generation 2 SoCs.
|
||||
|
||||
config PHY_RCAR_GEN3_USB2
|
||||
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
|
||||
depends on OF && ARCH_SHMOBILE
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
|
||||
|
||||
config OMAP_CONTROL_PHY
|
||||
tristate "OMAP CONTROL PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
@ -215,6 +222,15 @@ config PHY_MT65XX_USB3
|
||||
for mt65xx SoCs. it supports two usb2.0 ports and
|
||||
one usb3.0 port.
|
||||
|
||||
config PHY_HI6220_USB
|
||||
tristate "hi6220 USB PHY support"
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enable this to support the HISILICON HI6220 USB PHY.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PHY_SUN4I_USB
|
||||
tristate "Allwinner sunxi SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
@ -374,11 +390,11 @@ config PHY_TUSB1210
|
||||
|
||||
config PHY_BRCMSTB_SATA
|
||||
tristate "Broadcom STB SATA PHY driver"
|
||||
depends on ARCH_BRCMSTB
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
|
||||
Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs.
|
||||
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
|
||||
|
||||
config PHY_CYGNUS_PCIE
|
||||
|
@ -17,12 +17,14 @@ obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
|
||||
obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
|
||||
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
|
||||
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
|
||||
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o
|
||||
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
|
||||
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
|
||||
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
|
||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
|
||||
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
|
||||
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
|
||||
obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
|
||||
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
||||
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
|
||||
|
@ -9,11 +9,9 @@
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
@ -195,7 +193,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
phy_set_drvdata(phy, priv);
|
||||
|
||||
phy_provider =
|
||||
|
@ -26,13 +26,21 @@
|
||||
|
||||
#define SATA_MDIO_BANK_OFFSET 0x23c
|
||||
#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4)
|
||||
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
|
||||
#define SATA_MDIO_REG_LENGTH 0x1f00
|
||||
|
||||
#define MAX_PORTS 2
|
||||
|
||||
/* Register offset between PHYs in PCB space */
|
||||
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
|
||||
#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000
|
||||
|
||||
/* The older SATA PHY registers duplicated per port registers within the map,
|
||||
* rather than having a separate map per port.
|
||||
*/
|
||||
#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10
|
||||
|
||||
enum brcm_sata_phy_version {
|
||||
BRCM_SATA_PHY_28NM,
|
||||
BRCM_SATA_PHY_40NM,
|
||||
};
|
||||
|
||||
struct brcm_sata_port {
|
||||
int portnum;
|
||||
@ -44,11 +52,12 @@ struct brcm_sata_port {
|
||||
struct brcm_sata_phy {
|
||||
struct device *dev;
|
||||
void __iomem *phy_base;
|
||||
enum brcm_sata_phy_version version;
|
||||
|
||||
struct brcm_sata_port phys[MAX_PORTS];
|
||||
};
|
||||
|
||||
enum sata_mdio_phy_regs_28nm {
|
||||
enum sata_mdio_phy_regs {
|
||||
PLL_REG_BANK_0 = 0x50,
|
||||
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
|
||||
|
||||
@ -66,8 +75,16 @@ enum sata_mdio_phy_regs_28nm {
|
||||
static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
|
||||
{
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 offset = 0;
|
||||
|
||||
return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE);
|
||||
if (priv->version == BRCM_SATA_PHY_28NM)
|
||||
offset = SATA_MDIO_REG_28NM_SPACE_SIZE;
|
||||
else if (priv->version == BRCM_SATA_PHY_40NM)
|
||||
offset = SATA_MDIO_REG_40NM_SPACE_SIZE;
|
||||
else
|
||||
dev_err(priv->dev, "invalid phy version\n");
|
||||
|
||||
return priv->phy_base + (port->portnum * offset);
|
||||
}
|
||||
|
||||
static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
|
||||
@ -86,7 +103,7 @@ static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
|
||||
#define FMAX_VAL_DEFAULT 0x3df
|
||||
#define FMAX_VAL_SSC 0x83
|
||||
|
||||
static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port)
|
||||
static void brcm_sata_cfg_ssc(struct brcm_sata_port *port)
|
||||
{
|
||||
void __iomem *base = brcm_sata_phy_base(port);
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
@ -117,18 +134,21 @@ static int brcm_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
struct brcm_sata_port *port = phy_get_drvdata(phy);
|
||||
|
||||
brcm_sata_cfg_ssc_28nm(port);
|
||||
brcm_sata_cfg_ssc(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops phy_ops_28nm = {
|
||||
static const struct phy_ops phy_ops = {
|
||||
.init = brcm_sata_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_sata_phy_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7445-sata-phy" },
|
||||
{ .compatible = "brcm,bcm7445-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_28NM },
|
||||
{ .compatible = "brcm,bcm7425-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_40NM },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
|
||||
@ -137,6 +157,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
const struct of_device_id *of_id;
|
||||
struct brcm_sata_phy *priv;
|
||||
struct resource *res;
|
||||
struct phy_provider *provider;
|
||||
@ -156,6 +177,12 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->phy_base))
|
||||
return PTR_ERR(priv->phy_base);
|
||||
|
||||
of_id = of_match_node(brcm_sata_phy_of_match, dn);
|
||||
if (of_id)
|
||||
priv->version = (enum brcm_sata_phy_version)of_id->data;
|
||||
else
|
||||
priv->version = BRCM_SATA_PHY_28NM;
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
unsigned int id;
|
||||
struct brcm_sata_port *port;
|
||||
@ -181,7 +208,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
port = &priv->phys[id];
|
||||
port->portnum = id;
|
||||
port->phy_priv = priv;
|
||||
port->phy = devm_phy_create(dev, child, &phy_ops_28nm);
|
||||
port->phy = devm_phy_create(dev, child, &phy_ops);
|
||||
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
|
||||
if (IS_ERR(port->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
168
drivers/phy/phy-hi6220-usb.c
Normal file
168
drivers/phy/phy-hi6220-usb.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Linaro Ltd.
|
||||
* Copyright (c) 2015 Hisilicon Limited.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define SC_PERIPH_CTRL4 0x00c
|
||||
|
||||
#define CTRL4_PICO_SIDDQ BIT(6)
|
||||
#define CTRL4_PICO_OGDISABLE BIT(8)
|
||||
#define CTRL4_PICO_VBUSVLDEXT BIT(10)
|
||||
#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11)
|
||||
#define CTRL4_OTG_PHY_SEL BIT(21)
|
||||
|
||||
#define SC_PERIPH_CTRL5 0x010
|
||||
|
||||
#define CTRL5_USBOTG_RES_SEL BIT(3)
|
||||
#define CTRL5_PICOPHY_ACAENB BIT(4)
|
||||
#define CTRL5_PICOPHY_BC_MODE BIT(5)
|
||||
#define CTRL5_PICOPHY_CHRGSEL BIT(6)
|
||||
#define CTRL5_PICOPHY_VDATSRCEND BIT(7)
|
||||
#define CTRL5_PICOPHY_VDATDETENB BIT(8)
|
||||
#define CTRL5_PICOPHY_DCDENB BIT(9)
|
||||
#define CTRL5_PICOPHY_IDDIG BIT(10)
|
||||
|
||||
#define SC_PERIPH_CTRL8 0x018
|
||||
#define SC_PERIPH_RSTEN0 0x300
|
||||
#define SC_PERIPH_RSTDIS0 0x304
|
||||
|
||||
#define RST0_USBOTG_BUS BIT(4)
|
||||
#define RST0_POR_PICOPHY BIT(5)
|
||||
#define RST0_USBOTG BIT(6)
|
||||
#define RST0_USBOTG_32K BIT(7)
|
||||
|
||||
#define EYE_PATTERN_PARA 0x7053348c
|
||||
|
||||
struct hi6220_priv {
|
||||
struct regmap *reg;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static void hi6220_phy_init(struct hi6220_priv *priv)
|
||||
{
|
||||
struct regmap *reg = priv->reg;
|
||||
u32 val, mask;
|
||||
|
||||
val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
|
||||
RST0_USBOTG | RST0_USBOTG_32K;
|
||||
mask = val;
|
||||
regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
|
||||
regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
|
||||
}
|
||||
|
||||
static int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
|
||||
{
|
||||
struct regmap *reg = priv->reg;
|
||||
u32 val, mask;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
|
||||
mask = val | CTRL5_PICOPHY_BC_MODE;
|
||||
ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
|
||||
CTRL4_OTG_PHY_SEL;
|
||||
mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
|
||||
ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
val = CTRL4_PICO_SIDDQ;
|
||||
mask = val;
|
||||
ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi6220_phy_start(struct phy *phy)
|
||||
{
|
||||
struct hi6220_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
return hi6220_phy_setup(priv, true);
|
||||
}
|
||||
|
||||
static int hi6220_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct hi6220_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
return hi6220_phy_setup(priv, false);
|
||||
}
|
||||
|
||||
static struct phy_ops hi6220_phy_ops = {
|
||||
.init = hi6220_phy_start,
|
||||
.exit = hi6220_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int hi6220_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *phy;
|
||||
struct hi6220_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"hisilicon,peripheral-syscon");
|
||||
if (IS_ERR(priv->reg)) {
|
||||
dev_err(dev, "no hisilicon,peripheral-syscon\n");
|
||||
return PTR_ERR(priv->reg);
|
||||
}
|
||||
|
||||
hi6220_phy_init(priv);
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
phy_set_drvdata(phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id hi6220_phy_of_match[] = {
|
||||
{.compatible = "hisilicon,hi6220-usb-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
|
||||
|
||||
static struct platform_driver hi6220_phy_driver = {
|
||||
.probe = hi6220_phy_probe,
|
||||
.driver = {
|
||||
.name = "hi6220-usb-phy",
|
||||
.of_match_table = hi6220_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(hi6220_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
|
||||
MODULE_ALIAS("platform:hi6220-usb-phy");
|
||||
MODULE_LICENSE("GPL");
|
@ -17,6 +17,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
@ -27,6 +28,7 @@
|
||||
* relative to USB3_SIF2_BASE base address
|
||||
*/
|
||||
#define SSUSB_SIFSLV_SPLLC 0x0000
|
||||
#define SSUSB_SIFSLV_U2FREQ 0x0100
|
||||
|
||||
/* offsets of sub-segment in each port registers */
|
||||
#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
|
||||
@ -41,6 +43,7 @@
|
||||
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
|
||||
|
||||
#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
|
||||
#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
|
||||
#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
|
||||
#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
|
||||
#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
|
||||
@ -49,6 +52,8 @@
|
||||
#define PA6_RG_U2_ISO_EN BIT(31)
|
||||
#define PA6_RG_U2_BC11_SW_EN BIT(23)
|
||||
#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
|
||||
#define PA6_RG_U2_SQTH GENMASK(3, 0)
|
||||
#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
|
||||
|
||||
#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
|
||||
#define P2C_RG_USB20_GPIO_CTL BIT(9)
|
||||
@ -111,6 +116,24 @@
|
||||
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
|
||||
#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
|
||||
|
||||
#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00)
|
||||
#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
|
||||
#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
|
||||
#define P2F_RG_FREQDET_EN BIT(24)
|
||||
#define P2F_RG_CYCLECNT GENMASK(23, 0)
|
||||
#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
|
||||
|
||||
#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c)
|
||||
|
||||
#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10)
|
||||
#define P2F_USB_FM_VALID BIT(0)
|
||||
#define P2F_RG_FRCK_EN BIT(8)
|
||||
|
||||
#define U3P_REF_CLK 26 /* MHZ */
|
||||
#define U3P_SLEW_RATE_COEF 28
|
||||
#define U3P_SR_COEF_DIVISOR 1000
|
||||
#define U3P_FM_DET_CYCLE_CNT 1024
|
||||
|
||||
struct mt65xx_phy_instance {
|
||||
struct phy *phy;
|
||||
void __iomem *port_base;
|
||||
@ -126,6 +149,77 @@ struct mt65xx_u3phy {
|
||||
int nphys;
|
||||
};
|
||||
|
||||
static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
|
||||
struct mt65xx_phy_instance *instance)
|
||||
{
|
||||
void __iomem *sif_base = u3phy->sif_base;
|
||||
int calibration_val;
|
||||
int fm_out;
|
||||
u32 tmp;
|
||||
|
||||
/* enable USB ring oscillator */
|
||||
tmp = readl(instance->port_base + U3P_USBPHYACR5);
|
||||
tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
|
||||
writel(tmp, instance->port_base + U3P_USBPHYACR5);
|
||||
udelay(1);
|
||||
|
||||
/*enable free run clock */
|
||||
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
|
||||
tmp |= P2F_RG_FRCK_EN;
|
||||
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
|
||||
|
||||
/* set cycle count as 1024, and select u2 channel */
|
||||
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
|
||||
tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
|
||||
tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
|
||||
tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
|
||||
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
|
||||
|
||||
/* enable frequency meter */
|
||||
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
|
||||
tmp |= P2F_RG_FREQDET_EN;
|
||||
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
|
||||
|
||||
/* ignore return value */
|
||||
readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
|
||||
(tmp & P2F_USB_FM_VALID), 10, 200);
|
||||
|
||||
fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
|
||||
|
||||
/* disable frequency meter */
|
||||
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
|
||||
tmp &= ~P2F_RG_FREQDET_EN;
|
||||
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
|
||||
|
||||
/*disable free run clock */
|
||||
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
|
||||
tmp &= ~P2F_RG_FRCK_EN;
|
||||
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
|
||||
|
||||
if (fm_out) {
|
||||
/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
|
||||
tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
|
||||
tmp /= fm_out;
|
||||
calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
|
||||
} else {
|
||||
/* if FM detection fail, set default value */
|
||||
calibration_val = 4;
|
||||
}
|
||||
dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n",
|
||||
instance->index, fm_out, calibration_val);
|
||||
|
||||
/* set HS slew rate */
|
||||
tmp = readl(instance->port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
|
||||
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
|
||||
writel(tmp, instance->port_base + U3P_USBPHYACR5);
|
||||
|
||||
/* disable USB ring oscillator */
|
||||
tmp = readl(instance->port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
|
||||
writel(tmp, instance->port_base + U3P_USBPHYACR5);
|
||||
}
|
||||
|
||||
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
|
||||
struct mt65xx_phy_instance *instance)
|
||||
{
|
||||
@ -165,9 +259,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
}
|
||||
|
||||
/* DP/DM BC1.1 path Disable */
|
||||
tmp = readl(port_base + U3P_USBPHYACR6);
|
||||
tmp &= ~PA6_RG_U2_BC11_SW_EN;
|
||||
tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
|
||||
tmp &= ~PA6_RG_U2_SQTH;
|
||||
tmp |= PA6_RG_U2_SQTH_VAL(2);
|
||||
writel(tmp, port_base + U3P_USBPHYACR6);
|
||||
|
||||
tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
|
||||
@ -223,9 +318,9 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
|
||||
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
|
||||
writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
|
||||
|
||||
/* [mt8173]disable Change 100uA current from SSUSB */
|
||||
/* [mt8173]switch 100uA current to SSUSB */
|
||||
tmp = readl(port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
|
||||
tmp |= PA5_RG_U2_HS_100U_U3_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
}
|
||||
|
||||
@ -270,7 +365,7 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
|
||||
writel(tmp, port_base + U3P_USBPHYACR6);
|
||||
|
||||
if (!index) {
|
||||
/* (also disable)Change 100uA current switch to USB2.0 */
|
||||
/* switch 100uA current back to USB2.0 */
|
||||
tmp = readl(port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
@ -340,6 +435,7 @@ static int mt65xx_phy_power_on(struct phy *phy)
|
||||
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
|
||||
|
||||
phy_instance_power_on(u3phy, instance);
|
||||
hs_slew_rate_calibrate(u3phy, instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
|
||||
@ -97,22 +99,38 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_usb_phy_power(struct omap_usb *phy, int on)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!phy->syscon_phy_power) {
|
||||
omap_control_phy_power(phy->control_dev, on);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (on)
|
||||
val = phy->power_on;
|
||||
else
|
||||
val = phy->power_off;
|
||||
|
||||
ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
|
||||
phy->mask, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_usb_power_off(struct phy *x)
|
||||
{
|
||||
struct omap_usb *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
|
||||
return 0;
|
||||
return omap_usb_phy_power(phy, false);
|
||||
}
|
||||
|
||||
static int omap_usb_power_on(struct phy *x)
|
||||
{
|
||||
struct omap_usb *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 1);
|
||||
|
||||
return 0;
|
||||
return omap_usb_phy_power(phy, true);
|
||||
}
|
||||
|
||||
static int omap_usb_init(struct phy *x)
|
||||
@ -147,21 +165,38 @@ static const struct phy_ops ops = {
|
||||
static const struct usb_phy_data omap_usb2_data = {
|
||||
.label = "omap_usb2",
|
||||
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
|
||||
.mask = OMAP_DEV_PHY_PD,
|
||||
.power_off = OMAP_DEV_PHY_PD,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data omap5_usb2_data = {
|
||||
.label = "omap5_usb2",
|
||||
.flags = 0,
|
||||
.mask = OMAP_DEV_PHY_PD,
|
||||
.power_off = OMAP_DEV_PHY_PD,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data dra7x_usb2_data = {
|
||||
.label = "dra7x_usb2",
|
||||
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
|
||||
.mask = OMAP_DEV_PHY_PD,
|
||||
.power_off = OMAP_DEV_PHY_PD,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data dra7x_usb2_phy2_data = {
|
||||
.label = "dra7x_usb2_phy2",
|
||||
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
|
||||
.mask = OMAP_USB2_PHY_PD,
|
||||
.power_off = OMAP_USB2_PHY_PD,
|
||||
};
|
||||
|
||||
static const struct usb_phy_data am437x_usb2_data = {
|
||||
.label = "am437x_usb2",
|
||||
.flags = 0,
|
||||
.mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
|
||||
AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
|
||||
.power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
|
||||
.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
|
||||
};
|
||||
|
||||
static const struct of_device_id omap_usb2_id_table[] = {
|
||||
@ -177,6 +212,10 @@ static const struct of_device_id omap_usb2_id_table[] = {
|
||||
.compatible = "ti,dra7x-usb2",
|
||||
.data = &dra7x_usb2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra7x-usb2-phy2",
|
||||
.data = &dra7x_usb2_phy2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am437x-usb2",
|
||||
.data = &am437x_usb2_data,
|
||||
@ -219,6 +258,9 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
phy->phy.label = phy_data->label;
|
||||
phy->phy.otg = otg;
|
||||
phy->phy.type = USB_PHY_TYPE_USB2;
|
||||
phy->mask = phy_data->mask;
|
||||
phy->power_on = phy_data->power_on;
|
||||
phy->power_off = phy_data->power_off;
|
||||
|
||||
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@ -228,20 +270,35 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
|
||||
}
|
||||
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
if (!control_node) {
|
||||
dev_err(&pdev->dev, "Failed to get control device phandle\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-phy-power");
|
||||
if (IS_ERR(phy->syscon_phy_power)) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"can't get syscon-phy-power, using control device\n");
|
||||
phy->syscon_phy_power = NULL;
|
||||
|
||||
control_pdev = of_find_device_by_node(control_node);
|
||||
if (!control_pdev) {
|
||||
dev_err(&pdev->dev, "Failed to get control device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
if (!control_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get control device phandle\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
control_pdev = of_find_device_by_node(control_node);
|
||||
if (!control_pdev) {
|
||||
dev_err(&pdev->dev, "Failed to get control device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
} else {
|
||||
if (of_property_read_u32_index(node,
|
||||
"syscon-phy-power", 1,
|
||||
&phy->power_reg)) {
|
||||
dev_err(&pdev->dev,
|
||||
"couldn't get power reg. offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
otg->set_host = omap_usb_set_host;
|
||||
otg->set_peripheral = omap_usb_set_peripheral;
|
||||
@ -261,6 +318,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
omap_usb_power_off(generic_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(phy->dev,
|
||||
of_phy_simple_xlate);
|
||||
|
378
drivers/phy/phy-rcar-gen3-usb2.c
Normal file
378
drivers/phy/phy-rcar-gen3-usb2.c
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Renesas R-Car Gen3 for USB2.0 PHY driver
|
||||
*
|
||||
* Copyright (C) 2015 Renesas Electronics Corporation
|
||||
*
|
||||
* This is based on the phy-rcar-gen2 driver:
|
||||
* Copyright (C) 2014 Renesas Solutions Corp.
|
||||
* Copyright (C) 2014 Cogent Embedded, Inc.
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/******* USB2.0 Host registers (original offset is +0x200) *******/
|
||||
#define USB2_INT_ENABLE 0x000
|
||||
#define USB2_USBCTR 0x00c
|
||||
#define USB2_SPD_RSM_TIMSET 0x10c
|
||||
#define USB2_OC_TIMSET 0x110
|
||||
#define USB2_COMMCTRL 0x600
|
||||
#define USB2_OBINTSTA 0x604
|
||||
#define USB2_OBINTEN 0x608
|
||||
#define USB2_VBCTRL 0x60c
|
||||
#define USB2_LINECTRL1 0x610
|
||||
#define USB2_ADPCTRL 0x630
|
||||
|
||||
/* INT_ENABLE */
|
||||
#define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
|
||||
#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2)
|
||||
#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1)
|
||||
#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \
|
||||
USB2_INT_ENABLE_USBH_INTB_EN | \
|
||||
USB2_INT_ENABLE_USBH_INTA_EN)
|
||||
|
||||
/* USBCTR */
|
||||
#define USB2_USBCTR_DIRPD BIT(2)
|
||||
#define USB2_USBCTR_PLL_RST BIT(1)
|
||||
|
||||
/* SPD_RSM_TIMSET */
|
||||
#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b
|
||||
|
||||
/* OC_TIMSET */
|
||||
#define USB2_OC_TIMSET_INIT 0x000209ab
|
||||
|
||||
/* COMMCTRL */
|
||||
#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */
|
||||
|
||||
/* OBINTSTA and OBINTEN */
|
||||
#define USB2_OBINT_SESSVLDCHG BIT(12)
|
||||
#define USB2_OBINT_IDDIGCHG BIT(11)
|
||||
#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \
|
||||
USB2_OBINT_IDDIGCHG)
|
||||
|
||||
/* VBCTRL */
|
||||
#define USB2_VBCTRL_DRVVBUSSEL BIT(8)
|
||||
|
||||
/* LINECTRL1 */
|
||||
#define USB2_LINECTRL1_DPRPD_EN BIT(19)
|
||||
#define USB2_LINECTRL1_DP_RPD BIT(18)
|
||||
#define USB2_LINECTRL1_DMRPD_EN BIT(17)
|
||||
#define USB2_LINECTRL1_DM_RPD BIT(16)
|
||||
|
||||
/* ADPCTRL */
|
||||
#define USB2_ADPCTRL_OTGSESSVLD BIT(20)
|
||||
#define USB2_ADPCTRL_IDDIG BIT(19)
|
||||
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
||||
#define USB2_ADPCTRL_DRVVBUS BIT(4)
|
||||
|
||||
/******* HSUSB registers (original offset is +0x100) *******/
|
||||
#define HSUSB_LPSTS 0x02
|
||||
#define HSUSB_UGCTRL2 0x84
|
||||
|
||||
/* Low Power Status register (LPSTS) */
|
||||
#define HSUSB_LPSTS_SUSPM 0x4000
|
||||
|
||||
/* USB General control register 2 (UGCTRL2) */
|
||||
#define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */
|
||||
#define HSUSB_UGCTRL2_USB0SEL 0x00000030
|
||||
#define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010
|
||||
#define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020
|
||||
#define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030
|
||||
|
||||
struct rcar_gen3_data {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct rcar_gen3_chan {
|
||||
struct rcar_gen3_data usb2;
|
||||
struct rcar_gen3_data hsusb;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
u32 val = readl(usb2_base + USB2_COMMCTRL);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host);
|
||||
if (host)
|
||||
val &= ~USB2_COMMCTRL_OTG_PERI;
|
||||
else
|
||||
val |= USB2_COMMCTRL_OTG_PERI;
|
||||
writel(val, usb2_base + USB2_COMMCTRL);
|
||||
}
|
||||
|
||||
static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
u32 val = readl(usb2_base + USB2_LINECTRL1);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm);
|
||||
val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
|
||||
if (dp)
|
||||
val |= USB2_LINECTRL1_DP_RPD;
|
||||
if (dm)
|
||||
val |= USB2_LINECTRL1_DM_RPD;
|
||||
writel(val, usb2_base + USB2_LINECTRL1);
|
||||
}
|
||||
|
||||
static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
u32 val = readl(usb2_base + USB2_ADPCTRL);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus);
|
||||
if (vbus)
|
||||
val |= USB2_ADPCTRL_DRVVBUS;
|
||||
else
|
||||
val &= ~USB2_ADPCTRL_DRVVBUS;
|
||||
writel(val, usb2_base + USB2_ADPCTRL);
|
||||
}
|
||||
|
||||
static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
rcar_gen3_set_linectrl(ch, 1, 1);
|
||||
rcar_gen3_set_host_mode(ch, 1);
|
||||
rcar_gen3_enable_vbus_ctrl(ch, 1);
|
||||
}
|
||||
|
||||
static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
rcar_gen3_set_linectrl(ch, 0, 1);
|
||||
rcar_gen3_set_host_mode(ch, 0);
|
||||
rcar_gen3_enable_vbus_ctrl(ch, 0);
|
||||
}
|
||||
|
||||
static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
return !!(readl(ch->usb2.base + USB2_ADPCTRL) &
|
||||
USB2_ADPCTRL_OTGSESSVLD);
|
||||
}
|
||||
|
||||
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
return !!(readl(ch->usb2.base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
|
||||
}
|
||||
|
||||
static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
bool is_host = true;
|
||||
|
||||
/* B-device? */
|
||||
if (rcar_gen3_check_id(ch) && rcar_gen3_check_vbus(ch))
|
||||
is_host = false;
|
||||
|
||||
if (is_host)
|
||||
rcar_gen3_init_for_host(ch);
|
||||
else
|
||||
rcar_gen3_init_for_peri(ch);
|
||||
}
|
||||
|
||||
static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
u32 val;
|
||||
|
||||
val = readl(usb2_base + USB2_VBCTRL);
|
||||
writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
|
||||
val = readl(usb2_base + USB2_OBINTEN);
|
||||
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
|
||||
val = readl(usb2_base + USB2_ADPCTRL);
|
||||
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
|
||||
val = readl(usb2_base + USB2_LINECTRL1);
|
||||
rcar_gen3_set_linectrl(ch, 0, 0);
|
||||
writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN,
|
||||
usb2_base + USB2_LINECTRL1);
|
||||
|
||||
rcar_gen3_device_recognition(ch);
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_init(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *usb2_base = channel->usb2.base;
|
||||
void __iomem *hsusb_base = channel->hsusb.base;
|
||||
u32 val;
|
||||
|
||||
/* Initialize USB2 part */
|
||||
writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
|
||||
writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
|
||||
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
|
||||
|
||||
/* Initialize HSUSB part */
|
||||
if (hsusb_base) {
|
||||
val = readl(hsusb_base + HSUSB_UGCTRL2);
|
||||
val = (val & ~HSUSB_UGCTRL2_USB0SEL) |
|
||||
HSUSB_UGCTRL2_USB0SEL_OTG;
|
||||
writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2);
|
||||
|
||||
/* Initialize otg part */
|
||||
rcar_gen3_init_otg(channel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_exit(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
|
||||
writel(0, channel->usb2.base + USB2_INT_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *usb2_base = channel->usb2.base;
|
||||
void __iomem *hsusb_base = channel->hsusb.base;
|
||||
u32 val;
|
||||
|
||||
val = readl(usb2_base + USB2_USBCTR);
|
||||
val |= USB2_USBCTR_PLL_RST;
|
||||
writel(val, usb2_base + USB2_USBCTR);
|
||||
val &= ~USB2_USBCTR_PLL_RST;
|
||||
writel(val, usb2_base + USB2_USBCTR);
|
||||
|
||||
/*
|
||||
* TODO: To reduce power consuming, this driver should set the SUSPM
|
||||
* after the PHY detects ID pin as peripheral.
|
||||
*/
|
||||
if (hsusb_base) {
|
||||
/* Power on HSUSB PHY */
|
||||
val = readw(hsusb_base + HSUSB_LPSTS);
|
||||
val |= HSUSB_LPSTS_SUSPM;
|
||||
writew(val, hsusb_base + HSUSB_LPSTS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *hsusb_base = channel->hsusb.base;
|
||||
u32 val;
|
||||
|
||||
if (hsusb_base) {
|
||||
/* Power off HSUSB PHY */
|
||||
val = readw(hsusb_base + HSUSB_LPSTS);
|
||||
val &= ~HSUSB_LPSTS_SUSPM;
|
||||
writew(val, hsusb_base + HSUSB_LPSTS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops rcar_gen3_phy_usb2_ops = {
|
||||
.init = rcar_gen3_phy_usb2_init,
|
||||
.exit = rcar_gen3_phy_usb2_exit,
|
||||
.power_on = rcar_gen3_phy_usb2_power_on,
|
||||
.power_off = rcar_gen3_phy_usb2_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
||||
{
|
||||
struct rcar_gen3_chan *ch = _ch;
|
||||
void __iomem *usb2_base = ch->usb2.base;
|
||||
u32 status = readl(usb2_base + USB2_OBINTSTA);
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (status & USB2_OBINT_BITS) {
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
|
||||
rcar_gen3_device_recognition(ch);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
|
||||
{ .compatible = "renesas,usb2-phy-r8a7795" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
|
||||
|
||||
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rcar_gen3_chan *channel;
|
||||
struct phy_provider *provider;
|
||||
struct resource *res;
|
||||
|
||||
if (!dev->of_node) {
|
||||
dev_err(dev, "This driver needs device tree\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL);
|
||||
if (!channel)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host");
|
||||
channel->usb2.base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(channel->usb2.base))
|
||||
return PTR_ERR(channel->usb2.base);
|
||||
|
||||
/* "hsusb" memory resource is optional */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb");
|
||||
|
||||
/* To avoid error message by devm_ioremap_resource() */
|
||||
if (res) {
|
||||
int irq;
|
||||
|
||||
channel->hsusb.base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(channel->hsusb.base))
|
||||
channel->hsusb.base = NULL;
|
||||
/* call request_irq for OTG */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0)
|
||||
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
|
||||
IRQF_SHARED, dev_name(dev),
|
||||
channel);
|
||||
if (irq < 0)
|
||||
dev_err(dev, "No irq handler (%d)\n", irq);
|
||||
}
|
||||
|
||||
/* devm_phy_create() will call pm_runtime_enable(dev); */
|
||||
channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
|
||||
if (IS_ERR(channel->phy)) {
|
||||
dev_err(dev, "Failed to create USB2 PHY\n");
|
||||
return PTR_ERR(channel->phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(channel->phy, channel);
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(provider))
|
||||
dev_err(dev, "Failed to register PHY provider\n");
|
||||
|
||||
return PTR_ERR_OR_ZERO(provider);
|
||||
}
|
||||
|
||||
static struct platform_driver rcar_gen3_phy_usb2_driver = {
|
||||
.driver = {
|
||||
.name = "phy_rcar_gen3_usb2",
|
||||
.of_match_table = rcar_gen3_phy_usb2_match_table,
|
||||
},
|
||||
.probe = rcar_gen3_phy_usb2_probe,
|
||||
};
|
||||
module_platform_driver(rcar_gen3_phy_usb2_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY");
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
|
@ -15,12 +15,14 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -36,31 +38,91 @@
|
||||
#define SIDDQ_ON BIT(13)
|
||||
#define SIDDQ_OFF (0 << 13)
|
||||
|
||||
struct rockchip_usb_phys {
|
||||
int reg;
|
||||
const char *pll_name;
|
||||
};
|
||||
|
||||
struct rockchip_usb_phy_pdata {
|
||||
struct rockchip_usb_phys *phys;
|
||||
};
|
||||
|
||||
struct rockchip_usb_phy_base {
|
||||
struct device *dev;
|
||||
struct regmap *reg_base;
|
||||
const struct rockchip_usb_phy_pdata *pdata;
|
||||
};
|
||||
|
||||
struct rockchip_usb_phy {
|
||||
struct rockchip_usb_phy_base *base;
|
||||
struct device_node *np;
|
||||
unsigned int reg_offset;
|
||||
struct regmap *reg_base;
|
||||
struct clk *clk;
|
||||
struct clk *clk480m;
|
||||
struct clk_hw clk480m_hw;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
|
||||
bool siddq)
|
||||
{
|
||||
return regmap_write(phy->reg_base, phy->reg_offset,
|
||||
return regmap_write(phy->base->reg_base, phy->reg_offset,
|
||||
SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
|
||||
}
|
||||
|
||||
static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 480000000;
|
||||
}
|
||||
|
||||
static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct rockchip_usb_phy *phy = container_of(hw,
|
||||
struct rockchip_usb_phy,
|
||||
clk480m_hw);
|
||||
|
||||
/* Power down usb phy analog blocks by set siddq 1 */
|
||||
rockchip_usb_phy_power(phy, 1);
|
||||
}
|
||||
|
||||
static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct rockchip_usb_phy *phy = container_of(hw,
|
||||
struct rockchip_usb_phy,
|
||||
clk480m_hw);
|
||||
|
||||
/* Power up usb phy analog blocks by set siddq 0 */
|
||||
return rockchip_usb_phy_power(phy, 0);
|
||||
}
|
||||
|
||||
static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct rockchip_usb_phy *phy = container_of(hw,
|
||||
struct rockchip_usb_phy,
|
||||
clk480m_hw);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (val & SIDDQ_ON) ? 0 : 1;
|
||||
}
|
||||
|
||||
static const struct clk_ops rockchip_usb_phy480m_ops = {
|
||||
.enable = rockchip_usb_phy480m_enable,
|
||||
.disable = rockchip_usb_phy480m_disable,
|
||||
.is_enabled = rockchip_usb_phy480m_is_enabled,
|
||||
.recalc_rate = rockchip_usb_phy480m_recalc_rate,
|
||||
};
|
||||
|
||||
static int rockchip_usb_phy_power_off(struct phy *_phy)
|
||||
{
|
||||
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
int ret = 0;
|
||||
|
||||
/* Power down usb phy analog blocks by set siddq 1 */
|
||||
ret = rockchip_usb_phy_power(phy, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(phy->clk);
|
||||
clk_disable_unprepare(phy->clk480m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -68,20 +130,8 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
|
||||
static int rockchip_usb_phy_power_on(struct phy *_phy)
|
||||
{
|
||||
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
int ret = 0;
|
||||
|
||||
ret = clk_prepare_enable(phy->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Power up usb phy analog blocks by set siddq 0 */
|
||||
ret = rockchip_usb_phy_power(phy, 0);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(phy->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return clk_prepare_enable(phy->clk480m);
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
@ -90,66 +140,179 @@ static const struct phy_ops ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void rockchip_usb_phy_action(void *data)
|
||||
{
|
||||
struct rockchip_usb_phy *rk_phy = data;
|
||||
|
||||
of_clk_del_provider(rk_phy->np);
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
|
||||
if (rk_phy->clk)
|
||||
clk_put(rk_phy->clk);
|
||||
}
|
||||
|
||||
static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
|
||||
struct device_node *child)
|
||||
{
|
||||
struct rockchip_usb_phy *rk_phy;
|
||||
unsigned int reg_offset;
|
||||
const char *clk_name;
|
||||
struct clk_init_data init;
|
||||
int err, i;
|
||||
|
||||
rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
|
||||
if (!rk_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
rk_phy->base = base;
|
||||
rk_phy->np = child;
|
||||
|
||||
if (of_property_read_u32(child, "reg", ®_offset)) {
|
||||
dev_err(base->dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rk_phy->reg_offset = reg_offset;
|
||||
|
||||
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
|
||||
if (IS_ERR(rk_phy->clk))
|
||||
rk_phy->clk = NULL;
|
||||
|
||||
i = 0;
|
||||
init.name = NULL;
|
||||
while (base->pdata->phys[i].reg) {
|
||||
if (base->pdata->phys[i].reg == reg_offset) {
|
||||
init.name = base->pdata->phys[i].pll_name;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!init.name) {
|
||||
dev_err(base->dev, "phy data not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rk_phy->clk) {
|
||||
clk_name = __clk_get_name(rk_phy->clk);
|
||||
init.flags = 0;
|
||||
init.parent_names = &clk_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.flags = CLK_IS_ROOT;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
|
||||
init.ops = &rockchip_usb_phy480m_ops;
|
||||
rk_phy->clk480m_hw.init = &init;
|
||||
|
||||
rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
|
||||
if (IS_ERR(rk_phy->clk480m)) {
|
||||
err = PTR_ERR(rk_phy->clk480m);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
err = of_clk_add_provider(child, of_clk_src_simple_get,
|
||||
rk_phy->clk480m);
|
||||
if (err < 0)
|
||||
goto err_clk_prov;
|
||||
|
||||
err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
|
||||
if (err)
|
||||
goto err_devm_action;
|
||||
|
||||
rk_phy->phy = devm_phy_create(base->dev, child, &ops);
|
||||
if (IS_ERR(rk_phy->phy)) {
|
||||
dev_err(base->dev, "failed to create PHY\n");
|
||||
return PTR_ERR(rk_phy->phy);
|
||||
}
|
||||
phy_set_drvdata(rk_phy->phy, rk_phy);
|
||||
|
||||
/* only power up usb phy when it use, so disable it when init*/
|
||||
return rockchip_usb_phy_power(rk_phy, 1);
|
||||
|
||||
err_devm_action:
|
||||
of_clk_del_provider(child);
|
||||
err_clk_prov:
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
err_clk:
|
||||
if (rk_phy->clk)
|
||||
clk_put(rk_phy->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
|
||||
.phys = (struct rockchip_usb_phys[]){
|
||||
{ .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
|
||||
{ .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
|
||||
{ /* sentinel */ }
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rockchip_usb_phy_pdata rk3188_pdata = {
|
||||
.phys = (struct rockchip_usb_phys[]){
|
||||
{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
|
||||
{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
|
||||
{ /* sentinel */ }
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rockchip_usb_phy_pdata rk3288_pdata = {
|
||||
.phys = (struct rockchip_usb_phys[]){
|
||||
{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
|
||||
{ .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
|
||||
{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
|
||||
{ /* sentinel */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int rockchip_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rockchip_usb_phy *rk_phy;
|
||||
struct rockchip_usb_phy_base *phy_base;
|
||||
struct phy_provider *phy_provider;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *child;
|
||||
struct regmap *grf;
|
||||
unsigned int reg_offset;
|
||||
int err;
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
|
||||
if (!phy_base)
|
||||
return -ENOMEM;
|
||||
|
||||
match = of_match_device(dev->driver->of_match_table, dev);
|
||||
if (!match || !match->data) {
|
||||
dev_err(dev, "missing phy data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy_base->pdata = match->data;
|
||||
|
||||
phy_base->dev = dev;
|
||||
phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,grf");
|
||||
if (IS_ERR(phy_base->reg_base)) {
|
||||
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
|
||||
return PTR_ERR(grf);
|
||||
return PTR_ERR(phy_base->reg_base);
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
|
||||
if (!rk_phy) {
|
||||
err = -ENOMEM;
|
||||
goto put_child;
|
||||
err = rockchip_usb_phy_init(phy_base, child);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(child, "reg", ®_offset)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
err = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
rk_phy->reg_offset = reg_offset;
|
||||
rk_phy->reg_base = grf;
|
||||
|
||||
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
|
||||
if (IS_ERR(rk_phy->clk))
|
||||
rk_phy->clk = NULL;
|
||||
|
||||
rk_phy->phy = devm_phy_create(dev, child, &ops);
|
||||
if (IS_ERR(rk_phy->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
err = PTR_ERR(rk_phy->phy);
|
||||
goto put_child;
|
||||
}
|
||||
phy_set_drvdata(rk_phy->phy, rk_phy);
|
||||
|
||||
/* only power up usb phy when it use, so disable it when init*/
|
||||
err = rockchip_usb_phy_power(rk_phy, 1);
|
||||
if (err)
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
put_child:
|
||||
of_node_put(child);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3288-usb-phy" },
|
||||
{ .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
|
||||
{ .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
|
||||
{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-sun4i-usb.h>
|
||||
@ -46,6 +47,9 @@
|
||||
#define REG_PHYBIST 0x08
|
||||
#define REG_PHYTUNE 0x0c
|
||||
#define REG_PHYCTL_A33 0x10
|
||||
#define REG_PHY_UNK_H3 0x20
|
||||
|
||||
#define REG_PMU_UNK_H3 0x10
|
||||
|
||||
#define PHYCTL_DATA BIT(7)
|
||||
|
||||
@ -79,7 +83,7 @@
|
||||
#define PHY_DISCON_TH_SEL 0x2a
|
||||
#define PHY_SQUELCH_DETECT 0x3c
|
||||
|
||||
#define MAX_PHYS 3
|
||||
#define MAX_PHYS 4
|
||||
|
||||
/*
|
||||
* Note do not raise the debounce time, we must report Vusb high within 100ms
|
||||
@ -88,12 +92,24 @@
|
||||
#define DEBOUNCE_TIME msecs_to_jiffies(50)
|
||||
#define POLL_TIME msecs_to_jiffies(250)
|
||||
|
||||
enum sun4i_usb_phy_type {
|
||||
sun4i_a10_phy,
|
||||
sun8i_a33_phy,
|
||||
sun8i_h3_phy,
|
||||
};
|
||||
|
||||
struct sun4i_usb_phy_cfg {
|
||||
int num_phys;
|
||||
enum sun4i_usb_phy_type type;
|
||||
u32 disc_thresh;
|
||||
u8 phyctl_offset;
|
||||
bool dedicated_clocks;
|
||||
};
|
||||
|
||||
struct sun4i_usb_phy_data {
|
||||
void __iomem *base;
|
||||
const struct sun4i_usb_phy_cfg *cfg;
|
||||
struct mutex mutex;
|
||||
int num_phys;
|
||||
u32 disc_thresh;
|
||||
bool has_a33_phyctl;
|
||||
struct sun4i_usb_phy {
|
||||
struct phy *phy;
|
||||
void __iomem *pmu;
|
||||
@ -159,17 +175,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
|
||||
{
|
||||
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
|
||||
u32 temp, usbc_bit = BIT(phy->index * 2);
|
||||
void *phyctl;
|
||||
void *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
|
||||
int i;
|
||||
|
||||
mutex_lock(&phy_data->mutex);
|
||||
|
||||
if (phy_data->has_a33_phyctl) {
|
||||
phyctl = phy_data->base + REG_PHYCTL_A33;
|
||||
if (phy_data->cfg->type == sun8i_a33_phy) {
|
||||
/* A33 needs us to set phyctl to 0 explicitly */
|
||||
writel(0, phyctl);
|
||||
} else {
|
||||
phyctl = phy_data->base + REG_PHYCTL_A10;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
@ -230,6 +243,7 @@ static int sun4i_usb_phy_init(struct phy *_phy)
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = clk_prepare_enable(phy->clk);
|
||||
if (ret)
|
||||
@ -241,15 +255,26 @@ static int sun4i_usb_phy_init(struct phy *_phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable USB 45 Ohm resistor calibration */
|
||||
if (phy->index == 0)
|
||||
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
|
||||
if (data->cfg->type == sun8i_h3_phy) {
|
||||
if (phy->index == 0) {
|
||||
val = readl(data->base + REG_PHY_UNK_H3);
|
||||
writel(val & ~1, data->base + REG_PHY_UNK_H3);
|
||||
}
|
||||
|
||||
/* Adjust PHY's magnitude and rate */
|
||||
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
|
||||
val = readl(phy->pmu + REG_PMU_UNK_H3);
|
||||
writel(val & ~2, phy->pmu + REG_PMU_UNK_H3);
|
||||
} else {
|
||||
/* Enable USB 45 Ohm resistor calibration */
|
||||
if (phy->index == 0)
|
||||
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
|
||||
|
||||
/* Disconnect threshold adjustment */
|
||||
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
|
||||
/* Adjust PHY's magnitude and rate */
|
||||
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
|
||||
|
||||
/* Disconnect threshold adjustment */
|
||||
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
|
||||
data->cfg->disc_thresh, 2);
|
||||
}
|
||||
|
||||
sun4i_usb_phy_passby(phy, 1);
|
||||
|
||||
@ -476,7 +501,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
|
||||
{
|
||||
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (args->args[0] >= data->num_phys)
|
||||
if (args->args[0] >= data->cfg->num_phys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return data->phys[args->args[0]].phy;
|
||||
@ -511,7 +536,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct phy_provider *phy_provider;
|
||||
bool dedicated_clocks;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
|
||||
@ -522,29 +546,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
mutex_init(&data->mutex);
|
||||
INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
|
||||
of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
|
||||
of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
|
||||
data->num_phys = 2;
|
||||
else
|
||||
data->num_phys = 3;
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
|
||||
of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy"))
|
||||
data->disc_thresh = 2;
|
||||
else
|
||||
data->disc_thresh = 3;
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") ||
|
||||
of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
|
||||
of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
|
||||
dedicated_clocks = true;
|
||||
else
|
||||
dedicated_clocks = false;
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
|
||||
data->has_a33_phyctl = true;
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
if (!data->cfg)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
|
||||
data->base = devm_ioremap_resource(dev, res);
|
||||
@ -590,7 +594,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < data->num_phys; i++) {
|
||||
for (i = 0; i < data->cfg->num_phys; i++) {
|
||||
struct sun4i_usb_phy *phy = data->phys + i;
|
||||
char name[16];
|
||||
|
||||
@ -602,7 +606,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
phy->vbus = NULL;
|
||||
}
|
||||
|
||||
if (dedicated_clocks)
|
||||
if (data->cfg->dedicated_clocks)
|
||||
snprintf(name, sizeof(name), "usb%d_phy", i);
|
||||
else
|
||||
strlcpy(name, "usb_phy", sizeof(name));
|
||||
@ -689,13 +693,69 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
|
||||
.num_phys = 3,
|
||||
.type = sun4i_a10_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A10,
|
||||
.dedicated_clocks = false,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
|
||||
.num_phys = 2,
|
||||
.type = sun4i_a10_phy,
|
||||
.disc_thresh = 2,
|
||||
.phyctl_offset = REG_PHYCTL_A10,
|
||||
.dedicated_clocks = false,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
|
||||
.num_phys = 3,
|
||||
.type = sun4i_a10_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A10,
|
||||
.dedicated_clocks = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
|
||||
.num_phys = 3,
|
||||
.type = sun4i_a10_phy,
|
||||
.disc_thresh = 2,
|
||||
.phyctl_offset = REG_PHYCTL_A10,
|
||||
.dedicated_clocks = false,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
|
||||
.num_phys = 2,
|
||||
.type = sun4i_a10_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A10,
|
||||
.dedicated_clocks = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
|
||||
.num_phys = 2,
|
||||
.type = sun8i_a33_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A33,
|
||||
.dedicated_clocks = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
|
||||
.num_phys = 4,
|
||||
.type = sun8i_h3_phy,
|
||||
.disc_thresh = 3,
|
||||
.dedicated_clocks = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy" },
|
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy" },
|
||||
{ .compatible = "allwinner,sun6i-a31-usb-phy" },
|
||||
{ .compatible = "allwinner,sun7i-a20-usb-phy" },
|
||||
{ .compatible = "allwinner,sun8i-a23-usb-phy" },
|
||||
{ .compatible = "allwinner,sun8i-a33-usb-phy" },
|
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
|
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
|
||||
{ .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
|
||||
{ .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
|
||||
{ .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
|
||||
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
|
||||
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
|
||||
|
@ -56,6 +56,18 @@
|
||||
|
||||
#define SATA_PLL_SOFT_RESET BIT(18)
|
||||
|
||||
#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000
|
||||
#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14
|
||||
|
||||
#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000
|
||||
#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22
|
||||
|
||||
#define PIPE3_PHY_TX_RX_POWERON 0x3
|
||||
#define PIPE3_PHY_TX_RX_POWEROFF 0x0
|
||||
|
||||
#define PCIE_PCS_MASK 0xFF0000
|
||||
#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
|
||||
|
||||
/*
|
||||
* This is an Empirical value that works, need to confirm the actual
|
||||
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
|
||||
@ -86,8 +98,12 @@ struct ti_pipe3 {
|
||||
struct clk *refclk;
|
||||
struct clk *div_clk;
|
||||
struct pipe3_dpll_map *dpll_map;
|
||||
struct regmap *phy_power_syscon; /* ctrl. reg. acces */
|
||||
struct regmap *pcs_syscon; /* ctrl. reg. acces */
|
||||
struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */
|
||||
unsigned int dpll_reset_reg; /* reg. index within syscon */
|
||||
unsigned int power_reg; /* power reg. index within syscon */
|
||||
unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */
|
||||
bool sata_refclk_enabled;
|
||||
};
|
||||
|
||||
@ -144,20 +160,49 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy);
|
||||
|
||||
static int ti_pipe3_power_off(struct phy *x)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
if (!phy->phy_power_syscon) {
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
|
||||
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||
PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_pipe3_power_on(struct phy *x)
|
||||
{
|
||||
u32 val;
|
||||
u32 mask;
|
||||
int ret;
|
||||
unsigned long rate;
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 1);
|
||||
if (!phy->phy_power_syscon) {
|
||||
omap_control_phy_power(phy->control_dev, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
rate = clk_get_rate(phy->sys_clk);
|
||||
if (!rate) {
|
||||
dev_err(phy->dev, "Invalid clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rate = rate / 1000000;
|
||||
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
|
||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
|
||||
val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
||||
|
||||
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||
mask, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
|
||||
@ -229,8 +274,15 @@ static int ti_pipe3_init(struct phy *x)
|
||||
* 18-1804.
|
||||
*/
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
|
||||
omap_control_pcie_pcs(phy->control_dev, 0x96);
|
||||
return 0;
|
||||
if (!phy->pcs_syscon) {
|
||||
omap_control_pcie_pcs(phy->control_dev, 0x96);
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
|
||||
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
|
||||
PCIE_PCS_MASK, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Bring it out of IDLE if it is IDLE */
|
||||
@ -308,51 +360,15 @@ static const struct phy_ops ops = {
|
||||
|
||||
static const struct of_device_id ti_pipe3_id_table[];
|
||||
|
||||
static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
|
||||
{
|
||||
struct ti_pipe3 *phy;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct resource *res;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *control_node;
|
||||
struct platform_device *control_pdev;
|
||||
const struct of_device_id *match;
|
||||
struct clk *clk;
|
||||
struct device *dev = phy->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->dev = &pdev->dev;
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
match = of_match_device(ti_pipe3_id_table, &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
|
||||
if (!phy->dpll_map) {
|
||||
dev_err(&pdev->dev, "no DPLL data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pll_ctrl");
|
||||
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(phy->pll_ctrl_base))
|
||||
return PTR_ERR(phy->pll_ctrl_base);
|
||||
|
||||
phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
|
||||
if (IS_ERR(phy->sys_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get sysclk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
phy->refclk = devm_clk_get(phy->dev, "refclk");
|
||||
phy->refclk = devm_clk_get(dev, "refclk");
|
||||
if (IS_ERR(phy->refclk)) {
|
||||
dev_err(&pdev->dev, "unable to get refclk\n");
|
||||
dev_err(dev, "unable to get refclk\n");
|
||||
/* older DTBs have missing refclk in SATA PHY
|
||||
* so don't bail out in case of SATA PHY.
|
||||
*/
|
||||
@ -361,80 +377,194 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
|
||||
phy->wkupclk = devm_clk_get(dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
dev_err(&pdev->dev, "unable to get wkupclk\n");
|
||||
dev_err(dev, "unable to get wkupclk\n");
|
||||
return PTR_ERR(phy->wkupclk);
|
||||
}
|
||||
} else {
|
||||
phy->wkupclk = ERR_PTR(-ENODEV);
|
||||
phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-pllreset");
|
||||
if (IS_ERR(phy->dpll_reset_syscon)) {
|
||||
dev_info(&pdev->dev,
|
||||
"can't get syscon-pllreset, sata dpll won't idle\n");
|
||||
phy->dpll_reset_syscon = NULL;
|
||||
} else {
|
||||
if (of_property_read_u32_index(node,
|
||||
"syscon-pllreset", 1,
|
||||
&phy->dpll_reset_reg)) {
|
||||
dev_err(&pdev->dev,
|
||||
"couldn't get pllreset reg. offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") ||
|
||||
phy->phy_power_syscon) {
|
||||
phy->sys_clk = devm_clk_get(dev, "sysclk");
|
||||
if (IS_ERR(phy->sys_clk)) {
|
||||
dev_err(dev, "unable to get sysclk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
|
||||
clk = devm_clk_get(phy->dev, "dpll_ref");
|
||||
clk = devm_clk_get(dev, "dpll_ref");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get dpll ref clk\n");
|
||||
dev_err(dev, "unable to get dpll ref clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 1500000000);
|
||||
|
||||
clk = devm_clk_get(phy->dev, "dpll_ref_m2");
|
||||
clk = devm_clk_get(dev, "dpll_ref_m2");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
|
||||
dev_err(dev, "unable to get dpll ref m2 clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 100000000);
|
||||
|
||||
clk = devm_clk_get(phy->dev, "phy-div");
|
||||
clk = devm_clk_get(dev, "phy-div");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get phy-div clk\n");
|
||||
dev_err(dev, "unable to get phy-div clk\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_set_rate(clk, 100000000);
|
||||
|
||||
phy->div_clk = devm_clk_get(phy->dev, "div-clk");
|
||||
phy->div_clk = devm_clk_get(dev, "div-clk");
|
||||
if (IS_ERR(phy->div_clk)) {
|
||||
dev_err(&pdev->dev, "unable to get div-clk\n");
|
||||
dev_err(dev, "unable to get div-clk\n");
|
||||
return PTR_ERR(phy->div_clk);
|
||||
}
|
||||
} else {
|
||||
phy->div_clk = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
if (!control_node) {
|
||||
dev_err(&pdev->dev, "Failed to get control device phandle\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
|
||||
{
|
||||
struct device *dev = phy->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *control_node;
|
||||
struct platform_device *control_pdev;
|
||||
|
||||
phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-phy-power");
|
||||
if (IS_ERR(phy->phy_power_syscon)) {
|
||||
dev_dbg(dev,
|
||||
"can't get syscon-phy-power, using control device\n");
|
||||
phy->phy_power_syscon = NULL;
|
||||
} else {
|
||||
if (of_property_read_u32_index(node,
|
||||
"syscon-phy-power", 1,
|
||||
&phy->power_reg)) {
|
||||
dev_err(dev, "couldn't get power reg. offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!phy->phy_power_syscon) {
|
||||
control_node = of_parse_phandle(node, "ctrl-module", 0);
|
||||
if (!control_node) {
|
||||
dev_err(dev, "Failed to get control device phandle\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control_pdev = of_find_device_by_node(control_node);
|
||||
if (!control_pdev) {
|
||||
dev_err(dev, "Failed to get control device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-pcs");
|
||||
if (IS_ERR(phy->pcs_syscon)) {
|
||||
dev_dbg(dev,
|
||||
"can't get syscon-pcs, using omap control\n");
|
||||
phy->pcs_syscon = NULL;
|
||||
} else {
|
||||
if (of_property_read_u32_index(node,
|
||||
"syscon-pcs", 1,
|
||||
&phy->pcie_pcs_reg)) {
|
||||
dev_err(dev,
|
||||
"couldn't get pcie pcs reg. offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-pllreset");
|
||||
if (IS_ERR(phy->dpll_reset_syscon)) {
|
||||
dev_info(dev,
|
||||
"can't get syscon-pllreset, sata dpll won't idle\n");
|
||||
phy->dpll_reset_syscon = NULL;
|
||||
} else {
|
||||
if (of_property_read_u32_index(node,
|
||||
"syscon-pllreset", 1,
|
||||
&phy->dpll_reset_reg)) {
|
||||
dev_err(dev,
|
||||
"couldn't get pllreset reg. offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = phy->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
|
||||
return 0;
|
||||
|
||||
match = of_match_device(ti_pipe3_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
|
||||
if (!phy->dpll_map) {
|
||||
dev_err(dev, "no DPLL data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control_pdev = of_find_device_by_node(control_node);
|
||||
if (!control_pdev) {
|
||||
dev_err(&pdev->dev, "Failed to get control device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pll_ctrl");
|
||||
phy->pll_ctrl_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->pll_ctrl_base))
|
||||
return PTR_ERR(phy->pll_ctrl_base);
|
||||
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
omap_control_phy_power(phy->control_dev, 0);
|
||||
static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_pipe3 *phy;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->dev = dev;
|
||||
|
||||
ret = ti_pipe3_get_pll_base(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_pipe3_get_sysctrl(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_pipe3_get_clk(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/*
|
||||
* Prevent auto-disable of refclk for SATA PHY due to Errata i783
|
||||
@ -446,13 +576,15 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
|
||||
generic_phy = devm_phy_create(dev, NULL, &ops);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
phy_provider = devm_of_phy_provider_register(phy->dev,
|
||||
of_phy_simple_xlate);
|
||||
|
||||
ti_pipe3_power_off(generic_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
|
@ -30,6 +30,12 @@ struct usb_dpll_params {
|
||||
u32 mf;
|
||||
};
|
||||
|
||||
enum omap_usb_phy_type {
|
||||
TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */
|
||||
TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
|
||||
TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
|
||||
};
|
||||
|
||||
struct omap_usb {
|
||||
struct usb_phy phy;
|
||||
struct phy_companion *comparator;
|
||||
@ -40,11 +46,20 @@ struct omap_usb {
|
||||
struct clk *wkupclk;
|
||||
struct clk *optclk;
|
||||
u8 flags;
|
||||
enum omap_usb_phy_type type;
|
||||
struct regmap *syscon_phy_power; /* ctrl. reg. acces */
|
||||
unsigned int power_reg; /* power reg. index within syscon */
|
||||
u32 mask;
|
||||
u32 power_on;
|
||||
u32 power_off;
|
||||
};
|
||||
|
||||
struct usb_phy_data {
|
||||
const char *label;
|
||||
u8 flags;
|
||||
u32 mask;
|
||||
u32 power_on;
|
||||
u32 power_off;
|
||||
};
|
||||
|
||||
/* Driver Flags */
|
||||
@ -52,6 +67,14 @@ struct usb_phy_data {
|
||||
#define OMAP_USB2_HAS_SET_VBUS (1 << 1)
|
||||
#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2)
|
||||
|
||||
#define OMAP_DEV_PHY_PD BIT(0)
|
||||
#define OMAP_USB2_PHY_PD BIT(28)
|
||||
|
||||
#define AM437X_USB2_PHY_PD BIT(0)
|
||||
#define AM437X_USB2_OTG_PD BIT(1)
|
||||
#define AM437X_USB2_OTGVDET_EN BIT(19)
|
||||
#define AM437X_USB2_OTGSESSEND_EN BIT(20)
|
||||
|
||||
#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy)
|
||||
|
||||
#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
|
||||
|
Loading…
Reference in New Issue
Block a user