phy: for 4.4
*) Add new PHY driver for Broadcom's cygnus PCIe PHY *) Add USB3 PHY driver for mediatek's SoCs *) Add VBUS regulator support for Samsung's exynos PHY *) Misc cleanup Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJWIa6IAAoJEA5ceFyATYLZEOwP/2NxVw/ED29m1XvuaC045jgB ehnh4AL1VW7GboovJ63rATKWJZjpYft+iPyq3qTXtwO61znpwzYus8xABzqvkPCU SIuISV7DjTArFAlmiNVhOoIykpnhskdimiCE0yEA7Cv38KchKgwdvgVti7NiAthG rTCokoUEj3JR5t7foMm9uV/VK25SuMOjTfbC2OIpBHQQEqNfTofsMCej5tyPCRZe pbZN4uNGbENquxyRfNN7kjXZgzLsXPvC2QXGB0jcTNtUpuk5JPym2dnqH6dflsE4 Nl5Qb4zSkJ/+DcH2lFzrUNkN2E5AwEhkoi2pf+BPMFqtACdvkBtRgjWgJg/9TGwG XFtOG+HuDO2rVvPuQ8ToZxd2Sd/Od296by3Tqc1jIbG2oPXSz07zupjAdC/RT2dn K887cLhbGNgcMgTsjTPeW6J0EWkzPnF45KrFwZ1d1m66URlI00R9+fUJnEuAjoAK LK83+g0sV3XO/kUy3V0g6Xz07Y8M0fKRJLdjIlef5M8sG82hJa3edrOURxkApfRC kmGkM9ITaTPQY/TpKp4HUG7AEQ9bIOqVcBfhppdnNKa2pd4jI6kPyzV0wJEZrrQy +rB3giOAO+UWASOy5H9gf75E/ZRdnJAWqnxvZTJa+L0xh1CkaxrhZ+BSpwWsHaDb P33V6GQvLy5tYaJeeXqn =evaU -----END PGP SIGNATURE----- Merge tag 'phy-for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next Kishon writes: phy: for 4.4 *) Add new PHY driver for Broadcom's cygnus PCIe PHY *) Add USB3 PHY driver for mediatek's SoCs *) Add VBUS regulator support for Samsung's exynos PHY *) Misc cleanup Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
commit
539669779a
|
@ -0,0 +1,47 @@
|
|||
Broadcom Cygnus PCIe PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "brcm,cygnus-pcie-phy"
|
||||
- reg: base address and length of the PCIe PHY block
|
||||
- #address-cells: must be 1
|
||||
- #size-cells: must be 0
|
||||
|
||||
Each PCIe PHY should be represented by a child node
|
||||
|
||||
Required properties For the child node:
|
||||
- reg: the PHY ID
|
||||
0 - PCIe RC 0
|
||||
1 - PCIe RC 1
|
||||
- #phy-cells: must be 0
|
||||
|
||||
Example:
|
||||
pcie_phy: phy@0301d0a0 {
|
||||
compatible = "brcm,cygnus-pcie-phy";
|
||||
reg = <0x0301d0a0 0x14>;
|
||||
|
||||
pcie0_phy: phy@0 {
|
||||
reg = <0>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
pcie1_phy: phy@1 {
|
||||
reg = <1>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* users of the PCIe phy */
|
||||
|
||||
pcie0: pcie@18012000 {
|
||||
...
|
||||
...
|
||||
phys = <&pcie0_phy>;
|
||||
phy-names = "pcie-phy";
|
||||
};
|
||||
|
||||
pcie1: pcie@18013000 {
|
||||
...
|
||||
...
|
||||
phys = <pcie1_phy>;
|
||||
phy-names = "pcie-phy";
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
mt65xx USB3.0 PHY binding
|
||||
--------------------------
|
||||
|
||||
This binding describes a usb3.0 phy for mt65xx platforms of Medaitek SoC.
|
||||
|
||||
Required properties (controller (parent) node):
|
||||
- compatible : should be "mediatek,mt8173-u3phy"
|
||||
- reg : offset and length of register for phy, exclude port's
|
||||
register.
|
||||
- clocks : a list of phandle + clock-specifier pairs, one for each
|
||||
entry in clock-names
|
||||
- clock-names : must contain
|
||||
"u3phya_ref": for reference clock of usb3.0 analog phy.
|
||||
|
||||
Required nodes : a sub-node is required for each port the controller
|
||||
provides. Address range information including the usual
|
||||
'reg' property is used inside these nodes to describe
|
||||
the controller's topology.
|
||||
|
||||
Required properties (port (child) node):
|
||||
- reg : address and length of the register set for the port.
|
||||
- #phy-cells : should be 1 (See second example)
|
||||
cell after port phandle is phy type from:
|
||||
- PHY_TYPE_USB2
|
||||
- PHY_TYPE_USB3
|
||||
|
||||
Example:
|
||||
|
||||
u3phy: usb-phy@11290000 {
|
||||
compatible = "mediatek,mt8173-u3phy";
|
||||
reg = <0 0x11290000 0 0x800>;
|
||||
clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
|
||||
clock-names = "u3phya_ref";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
status = "okay";
|
||||
|
||||
phy_port0: port@11290800 {
|
||||
reg = <0 0x11290800 0 0x800>;
|
||||
#phy-cells = <1>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
phy_port1: port@11291000 {
|
||||
reg = <0 0x11291000 0 0x800>;
|
||||
#phy-cells = <1>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
Specifying phy control of devices
|
||||
---------------------------------
|
||||
|
||||
Device nodes should specify the configuration required in their "phys"
|
||||
property, containing a phandle to the phy port node and a device type;
|
||||
phy-names for each port are optional.
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
usb30: usb@11270000 {
|
||||
...
|
||||
phys = <&phy_port0 PHY_TYPE_USB3>;
|
||||
phy-names = "usb3-0";
|
||||
...
|
||||
};
|
|
@ -44,6 +44,9 @@ Required properties:
|
|||
- the "ref" clock is used to get the rate of the clock provided to the
|
||||
PHY module
|
||||
|
||||
Optional properties:
|
||||
- vbus-supply: power-supply phandle for vbus power source
|
||||
|
||||
The first phandle argument in the PHY specifier identifies the PHY, its
|
||||
meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
|
||||
and Exynos 4212) it is as follows:
|
||||
|
|
|
@ -1297,6 +1297,13 @@ F: arch/arm/mach-mediatek/
|
|||
N: mtk
|
||||
K: mediatek
|
||||
|
||||
ARM/Mediatek USB3 PHY DRIVER
|
||||
M: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/phy/phy-mt65xx-usb3.c
|
||||
|
||||
ARM/MICREL KS8695 ARCHITECTURE
|
||||
M: Greg Ungerer <gerg@uclinux.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
|
@ -206,6 +206,15 @@ config PHY_HIX5HD2_SATA
|
|||
help
|
||||
Support for SATA PHY on Hisilicon hix5hd2 Soc.
|
||||
|
||||
config PHY_MT65XX_USB3
|
||||
tristate "Mediatek USB3.0 PHY Driver"
|
||||
depends on ARCH_MEDIATEK && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Say 'Y' here to add support for Mediatek USB3.0 PHY driver
|
||||
for mt65xx SoCs. it supports two usb2.0 ports and
|
||||
one usb3.0 port.
|
||||
|
||||
config PHY_SUN4I_USB
|
||||
tristate "Allwinner sunxi SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
|
@ -371,4 +380,13 @@ config PHY_BRCMSTB_SATA
|
|||
Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
|
||||
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
|
||||
|
||||
config PHY_CYGNUS_PCIE
|
||||
tristate "Broadcom Cygnus PCIe PHY driver"
|
||||
depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
default ARCH_BCM_CYGNUS
|
||||
help
|
||||
Enable this to support the Broadcom Cygnus PCIe PHY.
|
||||
If unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -23,6 +23,7 @@ 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_MT65XX_USB3) += phy-mt65xx-usb3.o
|
||||
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
||||
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
|
||||
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
|
||||
|
@ -46,3 +47,4 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
|
|||
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
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Broadcom Corporation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; 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/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define PCIE_CFG_OFFSET 0x00
|
||||
#define PCIE1_PHY_IDDQ_SHIFT 10
|
||||
#define PCIE0_PHY_IDDQ_SHIFT 2
|
||||
|
||||
enum cygnus_pcie_phy_id {
|
||||
CYGNUS_PHY_PCIE0 = 0,
|
||||
CYGNUS_PHY_PCIE1,
|
||||
MAX_NUM_PHYS,
|
||||
};
|
||||
|
||||
struct cygnus_pcie_phy_core;
|
||||
|
||||
/**
|
||||
* struct cygnus_pcie_phy - Cygnus PCIe PHY device
|
||||
* @core: pointer to the Cygnus PCIe PHY core control
|
||||
* @id: internal ID to identify the Cygnus PCIe PHY
|
||||
* @phy: pointer to the kernel PHY device
|
||||
*/
|
||||
struct cygnus_pcie_phy {
|
||||
struct cygnus_pcie_phy_core *core;
|
||||
enum cygnus_pcie_phy_id id;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control
|
||||
* @dev: pointer to device
|
||||
* @base: base register
|
||||
* @lock: mutex to protect access to individual PHYs
|
||||
* @phys: pointer to Cygnus PHY device
|
||||
*/
|
||||
struct cygnus_pcie_phy_core {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct mutex lock;
|
||||
struct cygnus_pcie_phy phys[MAX_NUM_PHYS];
|
||||
};
|
||||
|
||||
static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable)
|
||||
{
|
||||
struct cygnus_pcie_phy_core *core = phy->core;
|
||||
unsigned shift;
|
||||
u32 val;
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
|
||||
switch (phy->id) {
|
||||
case CYGNUS_PHY_PCIE0:
|
||||
shift = PCIE0_PHY_IDDQ_SHIFT;
|
||||
break;
|
||||
|
||||
case CYGNUS_PHY_PCIE1:
|
||||
shift = PCIE1_PHY_IDDQ_SHIFT;
|
||||
break;
|
||||
|
||||
default:
|
||||
mutex_unlock(&core->lock);
|
||||
dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
val = readl(core->base + PCIE_CFG_OFFSET);
|
||||
val &= ~BIT(shift);
|
||||
writel(val, core->base + PCIE_CFG_OFFSET);
|
||||
/*
|
||||
* Wait 50 ms for the PCIe Serdes to stabilize after the analog
|
||||
* front end is brought up
|
||||
*/
|
||||
msleep(50);
|
||||
} else {
|
||||
val = readl(core->base + PCIE_CFG_OFFSET);
|
||||
val |= BIT(shift);
|
||||
writel(val, core->base + PCIE_CFG_OFFSET);
|
||||
}
|
||||
|
||||
mutex_unlock(&core->lock);
|
||||
dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id,
|
||||
enable ? "enabled" : "disabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_pcie_phy_power_on(struct phy *p)
|
||||
{
|
||||
struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
|
||||
|
||||
return cygnus_pcie_power_config(phy, true);
|
||||
}
|
||||
|
||||
static int cygnus_pcie_phy_power_off(struct phy *p)
|
||||
{
|
||||
struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
|
||||
|
||||
return cygnus_pcie_power_config(phy, false);
|
||||
}
|
||||
|
||||
static struct phy_ops cygnus_pcie_phy_ops = {
|
||||
.power_on = cygnus_pcie_phy_power_on,
|
||||
.power_off = cygnus_pcie_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cygnus_pcie_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node, *child;
|
||||
struct cygnus_pcie_phy_core *core;
|
||||
struct phy_provider *provider;
|
||||
struct resource *res;
|
||||
unsigned cnt = 0;
|
||||
|
||||
if (of_get_child_count(node) == 0) {
|
||||
dev_err(dev, "PHY no child node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
|
||||
if (!core)
|
||||
return -ENOMEM;
|
||||
|
||||
core->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
core->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(core->base))
|
||||
return PTR_ERR(core->base);
|
||||
|
||||
mutex_init(&core->lock);
|
||||
|
||||
for_each_available_child_of_node(node, child) {
|
||||
unsigned int id;
|
||||
struct cygnus_pcie_phy *p;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &id)) {
|
||||
dev_err(dev, "missing reg property for %s\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (id >= MAX_NUM_PHYS) {
|
||||
dev_err(dev, "invalid PHY id: %u\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (core->phys[id].phy) {
|
||||
dev_err(dev, "duplicated PHY id: %u\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
p = &core->phys[id];
|
||||
p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops);
|
||||
if (IS_ERR(p->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(p->phy);
|
||||
}
|
||||
|
||||
p->core = core;
|
||||
p->id = id;
|
||||
phy_set_drvdata(p->phy, p);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, core);
|
||||
|
||||
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(provider);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cygnus_pcie_phy_match_table[] = {
|
||||
{ .compatible = "brcm,cygnus-pcie-phy" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table);
|
||||
|
||||
static struct platform_driver cygnus_pcie_phy_driver = {
|
||||
.driver = {
|
||||
.name = "cygnus-pcie-phy",
|
||||
.of_match_table = cygnus_pcie_phy_match_table,
|
||||
},
|
||||
.probe = cygnus_pcie_phy_probe,
|
||||
};
|
||||
module_platform_driver(cygnus_pcie_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
|
||||
MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,506 @@
|
|||
/*
|
||||
* Copyright (c) 2015 MediaTek Inc.
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/*
|
||||
* for sifslv2 register, but exclude port's;
|
||||
* relative to USB3_SIF2_BASE base address
|
||||
*/
|
||||
#define SSUSB_SIFSLV_SPLLC 0x0000
|
||||
|
||||
/* offsets of sub-segment in each port registers */
|
||||
#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
|
||||
#define SSUSB_SIFSLV_U3PHYD_BASE 0x0100
|
||||
#define SSUSB_USB30_PHYA_SIV_B_BASE 0x0300
|
||||
#define SSUSB_SIFSLV_U3PHYA_DA_BASE 0x0400
|
||||
|
||||
#define U3P_USBPHYACR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0000)
|
||||
#define PA0_RG_U2PLL_FORCE_ON BIT(15)
|
||||
|
||||
#define U3P_USBPHYACR2 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0008)
|
||||
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
|
||||
|
||||
#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
|
||||
#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)
|
||||
|
||||
#define U3P_USBPHYACR6 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0018)
|
||||
#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 U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
|
||||
#define P2C_RG_USB20_GPIO_CTL BIT(9)
|
||||
#define P2C_USB20_GPIO_MODE BIT(8)
|
||||
#define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE)
|
||||
|
||||
#define U3D_U2PHYDCR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0060)
|
||||
#define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24)
|
||||
|
||||
#define U3P_U2PHYDTM0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0068)
|
||||
#define P2C_FORCE_UART_EN BIT(26)
|
||||
#define P2C_FORCE_DATAIN BIT(23)
|
||||
#define P2C_FORCE_DM_PULLDOWN BIT(21)
|
||||
#define P2C_FORCE_DP_PULLDOWN BIT(20)
|
||||
#define P2C_FORCE_XCVRSEL BIT(19)
|
||||
#define P2C_FORCE_SUSPENDM BIT(18)
|
||||
#define P2C_FORCE_TERMSEL BIT(17)
|
||||
#define P2C_RG_DATAIN GENMASK(13, 10)
|
||||
#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10)
|
||||
#define P2C_RG_DMPULLDOWN BIT(7)
|
||||
#define P2C_RG_DPPULLDOWN BIT(6)
|
||||
#define P2C_RG_XCVRSEL GENMASK(5, 4)
|
||||
#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4)
|
||||
#define P2C_RG_SUSPENDM BIT(3)
|
||||
#define P2C_RG_TERMSEL BIT(2)
|
||||
#define P2C_DTM0_PART_MASK \
|
||||
(P2C_FORCE_DATAIN | P2C_FORCE_DM_PULLDOWN | \
|
||||
P2C_FORCE_DP_PULLDOWN | P2C_FORCE_XCVRSEL | \
|
||||
P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \
|
||||
P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL)
|
||||
|
||||
#define U3P_U2PHYDTM1 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x006C)
|
||||
#define P2C_RG_UART_EN BIT(16)
|
||||
#define P2C_RG_VBUSVALID BIT(5)
|
||||
#define P2C_RG_SESSEND BIT(4)
|
||||
#define P2C_RG_AVALID BIT(2)
|
||||
|
||||
#define U3P_U3_PHYA_REG0 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0000)
|
||||
#define P3A_RG_U3_VUSB10_ON BIT(5)
|
||||
|
||||
#define U3P_U3_PHYA_REG6 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0018)
|
||||
#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28)
|
||||
#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28)
|
||||
|
||||
#define U3P_U3_PHYA_REG9 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0024)
|
||||
#define P3A_RG_RX_DAC_MUX GENMASK(5, 1)
|
||||
#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1)
|
||||
|
||||
#define U3P_U3PHYA_DA_REG0 (SSUSB_SIFSLV_U3PHYA_DA_BASE + 0x0000)
|
||||
#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10)
|
||||
#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10)
|
||||
|
||||
#define U3P_PHYD_CDR1 (SSUSB_SIFSLV_U3PHYD_BASE + 0x005c)
|
||||
#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24)
|
||||
#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24)
|
||||
#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8)
|
||||
#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8)
|
||||
|
||||
#define U3P_XTALCTL3 (SSUSB_SIFSLV_SPLLC + 0x0018)
|
||||
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
|
||||
#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
|
||||
|
||||
struct mt65xx_phy_instance {
|
||||
struct phy *phy;
|
||||
void __iomem *port_base;
|
||||
u32 index;
|
||||
u8 type;
|
||||
};
|
||||
|
||||
struct mt65xx_u3phy {
|
||||
struct device *dev;
|
||||
void __iomem *sif_base; /* include sif2, but exclude port's */
|
||||
struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
|
||||
struct mt65xx_phy_instance **phys;
|
||||
int nphys;
|
||||
};
|
||||
|
||||
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
|
||||
struct mt65xx_phy_instance *instance)
|
||||
{
|
||||
void __iomem *port_base = instance->port_base;
|
||||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
/* switch to USB function. (system register, force ip into usb mode) */
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp &= ~P2C_FORCE_UART_EN;
|
||||
tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM1);
|
||||
tmp &= ~P2C_RG_UART_EN;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM1);
|
||||
|
||||
if (!index) {
|
||||
tmp = readl(port_base + U3P_U2PHYACR4);
|
||||
tmp &= ~P2C_U2_GPIO_CTR_MSK;
|
||||
writel(tmp, port_base + U3P_U2PHYACR4);
|
||||
|
||||
tmp = readl(port_base + U3P_USBPHYACR2);
|
||||
tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR2);
|
||||
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
} else {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
|
||||
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;
|
||||
writel(tmp, port_base + U3P_USBPHYACR6);
|
||||
|
||||
tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
|
||||
tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
|
||||
tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
|
||||
writel(tmp, port_base + U3P_U3PHYA_DA_REG0);
|
||||
|
||||
tmp = readl(port_base + U3P_U3_PHYA_REG9);
|
||||
tmp &= ~P3A_RG_RX_DAC_MUX;
|
||||
tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
|
||||
writel(tmp, port_base + U3P_U3_PHYA_REG9);
|
||||
|
||||
tmp = readl(port_base + U3P_U3_PHYA_REG6);
|
||||
tmp &= ~P3A_RG_TX_EIDLE_CM;
|
||||
tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
|
||||
writel(tmp, port_base + U3P_U3_PHYA_REG6);
|
||||
|
||||
tmp = readl(port_base + U3P_PHYD_CDR1);
|
||||
tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
|
||||
tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
|
||||
writel(tmp, port_base + U3P_PHYD_CDR1);
|
||||
|
||||
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
|
||||
}
|
||||
|
||||
static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
|
||||
struct mt65xx_phy_instance *instance)
|
||||
{
|
||||
void __iomem *port_base = instance->port_base;
|
||||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
if (!index) {
|
||||
/* Set RG_SSUSB_VUSB10_ON as 1 after VUSB10 ready */
|
||||
tmp = readl(port_base + U3P_U3_PHYA_REG0);
|
||||
tmp |= P3A_RG_U3_VUSB10_ON;
|
||||
writel(tmp, port_base + U3P_U3_PHYA_REG0);
|
||||
}
|
||||
|
||||
/* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL);
|
||||
tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
|
||||
/* OTG Enable */
|
||||
tmp = readl(port_base + U3P_USBPHYACR6);
|
||||
tmp |= PA6_RG_U2_OTG_VBUSCMP_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR6);
|
||||
|
||||
if (!index) {
|
||||
tmp = readl(u3phy->sif_base + U3P_XTALCTL3);
|
||||
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 */
|
||||
tmp = readl(port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
}
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM1);
|
||||
tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID;
|
||||
tmp &= ~P2C_RG_SESSEND;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM1);
|
||||
|
||||
/* USB 2.0 slew rate calibration */
|
||||
tmp = readl(port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
|
||||
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4);
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
|
||||
if (index) {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
}
|
||||
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
|
||||
}
|
||||
|
||||
static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
|
||||
struct mt65xx_phy_instance *instance)
|
||||
{
|
||||
void __iomem *port_base = instance->port_base;
|
||||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
|
||||
tmp |= P2C_FORCE_SUSPENDM;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
|
||||
/* OTG Disable */
|
||||
tmp = readl(port_base + U3P_USBPHYACR6);
|
||||
tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR6);
|
||||
|
||||
if (!index) {
|
||||
/* (also disable)Change 100uA current switch to USB2.0 */
|
||||
tmp = readl(port_base + U3P_USBPHYACR5);
|
||||
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
|
||||
writel(tmp, port_base + U3P_USBPHYACR5);
|
||||
}
|
||||
|
||||
/* let suspendm=0, set utmi into analog power down */
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp &= ~P2C_RG_SUSPENDM;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
udelay(1);
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM1);
|
||||
tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
|
||||
tmp |= P2C_RG_SESSEND;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM1);
|
||||
|
||||
if (!index) {
|
||||
tmp = readl(port_base + U3P_U3_PHYA_REG0);
|
||||
tmp &= ~P3A_RG_U3_VUSB10_ON;
|
||||
writel(tmp, port_base + U3P_U3_PHYA_REG0);
|
||||
} else {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
}
|
||||
|
||||
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
|
||||
}
|
||||
|
||||
static void phy_instance_exit(struct mt65xx_u3phy *u3phy,
|
||||
struct mt65xx_phy_instance *instance)
|
||||
{
|
||||
void __iomem *port_base = instance->port_base;
|
||||
u32 index = instance->index;
|
||||
u32 tmp;
|
||||
|
||||
if (index) {
|
||||
tmp = readl(port_base + U3D_U2PHYDCR0);
|
||||
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
|
||||
writel(tmp, port_base + U3D_U2PHYDCR0);
|
||||
|
||||
tmp = readl(port_base + U3P_U2PHYDTM0);
|
||||
tmp &= ~P2C_FORCE_SUSPENDM;
|
||||
writel(tmp, port_base + U3P_U2PHYDTM0);
|
||||
}
|
||||
}
|
||||
|
||||
static int mt65xx_phy_init(struct phy *phy)
|
||||
{
|
||||
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
|
||||
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(u3phy->u3phya_ref);
|
||||
if (ret) {
|
||||
dev_err(u3phy->dev, "failed to enable u3phya_ref\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_instance_init(u3phy, instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt65xx_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
|
||||
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
|
||||
|
||||
phy_instance_power_on(u3phy, instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt65xx_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
|
||||
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
|
||||
|
||||
phy_instance_power_off(u3phy, instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt65xx_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
|
||||
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
|
||||
|
||||
phy_instance_exit(u3phy, instance);
|
||||
clk_disable_unprepare(u3phy->u3phya_ref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy *mt65xx_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct mt65xx_u3phy *u3phy = dev_get_drvdata(dev);
|
||||
struct mt65xx_phy_instance *instance = NULL;
|
||||
struct device_node *phy_np = args->np;
|
||||
int index;
|
||||
|
||||
|
||||
if (args->args_count != 1) {
|
||||
dev_err(dev, "invalid number of cells in 'phy' property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
for (index = 0; index < u3phy->nphys; index++)
|
||||
if (phy_np == u3phy->phys[index]->phy->dev.of_node) {
|
||||
instance = u3phy->phys[index];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
dev_err(dev, "failed to find appropriate phy\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
instance->type = args->args[0];
|
||||
|
||||
if (!(instance->type == PHY_TYPE_USB2 ||
|
||||
instance->type == PHY_TYPE_USB3)) {
|
||||
dev_err(dev, "unsupported device type: %d\n", instance->type);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return instance->phy;
|
||||
}
|
||||
|
||||
static struct phy_ops mt65xx_u3phy_ops = {
|
||||
.init = mt65xx_phy_init,
|
||||
.exit = mt65xx_phy_exit,
|
||||
.power_on = mt65xx_phy_power_on,
|
||||
.power_off = mt65xx_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mt65xx_u3phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child_np;
|
||||
struct phy_provider *provider;
|
||||
struct resource *sif_res;
|
||||
struct mt65xx_u3phy *u3phy;
|
||||
struct resource res;
|
||||
int port;
|
||||
|
||||
u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
|
||||
if (!u3phy)
|
||||
return -ENOMEM;
|
||||
|
||||
u3phy->nphys = of_get_child_count(np);
|
||||
u3phy->phys = devm_kcalloc(dev, u3phy->nphys,
|
||||
sizeof(*u3phy->phys), GFP_KERNEL);
|
||||
if (!u3phy->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
u3phy->dev = dev;
|
||||
platform_set_drvdata(pdev, u3phy);
|
||||
|
||||
sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
u3phy->sif_base = devm_ioremap_resource(dev, sif_res);
|
||||
if (IS_ERR(u3phy->sif_base)) {
|
||||
dev_err(dev, "failed to remap sif regs\n");
|
||||
return PTR_ERR(u3phy->sif_base);
|
||||
}
|
||||
|
||||
u3phy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");
|
||||
if (IS_ERR(u3phy->u3phya_ref)) {
|
||||
dev_err(dev, "error to get u3phya_ref\n");
|
||||
return PTR_ERR(u3phy->u3phya_ref);
|
||||
}
|
||||
|
||||
port = 0;
|
||||
for_each_child_of_node(np, child_np) {
|
||||
struct mt65xx_phy_instance *instance;
|
||||
struct phy *phy;
|
||||
int retval;
|
||||
|
||||
instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
|
||||
if (!instance)
|
||||
return -ENOMEM;
|
||||
|
||||
u3phy->phys[port] = instance;
|
||||
|
||||
phy = devm_phy_create(dev, child_np, &mt65xx_u3phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create phy\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
retval = of_address_to_resource(child_np, 0, &res);
|
||||
if (retval) {
|
||||
dev_err(dev, "failed to get address resource(id-%d)\n",
|
||||
port);
|
||||
return retval;
|
||||
}
|
||||
|
||||
instance->port_base = devm_ioremap_resource(&phy->dev, &res);
|
||||
if (IS_ERR(instance->port_base)) {
|
||||
dev_err(dev, "failed to remap phy regs\n");
|
||||
return PTR_ERR(instance->port_base);
|
||||
}
|
||||
|
||||
instance->phy = phy;
|
||||
instance->index = port;
|
||||
phy_set_drvdata(phy, instance);
|
||||
port++;
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, mt65xx_phy_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id mt65xx_u3phy_id_table[] = {
|
||||
{ .compatible = "mediatek,mt8173-u3phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table);
|
||||
|
||||
static struct platform_driver mt65xx_u3phy_driver = {
|
||||
.probe = mt65xx_u3phy_probe,
|
||||
.driver = {
|
||||
.name = "mt65xx-u3phy",
|
||||
.of_match_table = mt65xx_u3phy_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mt65xx_u3phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
|
||||
MODULE_DESCRIPTION("mt65xx USB PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -27,6 +27,13 @@ static int samsung_usb2_phy_power_on(struct phy *phy)
|
|||
|
||||
dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
|
||||
inst->cfg->label);
|
||||
|
||||
if (drv->vbus) {
|
||||
ret = regulator_enable(drv->vbus);
|
||||
if (ret)
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drv->clk);
|
||||
if (ret)
|
||||
goto err_main_clk;
|
||||
|
@ -48,6 +55,9 @@ err_power_on:
|
|||
err_instance_clk:
|
||||
clk_disable_unprepare(drv->clk);
|
||||
err_main_clk:
|
||||
if (drv->vbus)
|
||||
regulator_disable(drv->vbus);
|
||||
err_regulator:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -55,7 +65,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
|
|||
{
|
||||
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
|
||||
inst->cfg->label);
|
||||
|
@ -68,7 +78,10 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
|
|||
}
|
||||
clk_disable_unprepare(drv->ref_clk);
|
||||
clk_disable_unprepare(drv->clk);
|
||||
return 0;
|
||||
if (drv->vbus)
|
||||
ret = regulator_disable(drv->vbus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct phy_ops samsung_usb2_phy_ops = {
|
||||
|
@ -203,6 +216,14 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
drv->vbus = devm_regulator_get(dev, "vbus");
|
||||
if (IS_ERR(drv->vbus)) {
|
||||
ret = PTR_ERR(drv->vbus);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
drv->vbus = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < drv->cfg->num_phys; i++) {
|
||||
char *label = drv->cfg->phys[i].label;
|
||||
struct samsung_usb2_phy_instance *p = &drv->instances[i];
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define KHZ 1000
|
||||
#define MHZ (KHZ * KHZ)
|
||||
|
@ -37,6 +38,7 @@ struct samsung_usb2_phy_driver {
|
|||
const struct samsung_usb2_phy_config *cfg;
|
||||
struct clk *clk;
|
||||
struct clk *ref_clk;
|
||||
struct regulator *vbus;
|
||||
unsigned long ref_rate;
|
||||
u32 ref_reg_val;
|
||||
struct device *dev;
|
||||
|
|
|
@ -551,19 +551,15 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
|
||||
if (IS_ERR(data->id_det_gpio)) {
|
||||
if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
data->id_det_gpio = NULL;
|
||||
}
|
||||
data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(data->id_det_gpio))
|
||||
return PTR_ERR(data->id_det_gpio);
|
||||
|
||||
data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
|
||||
if (IS_ERR(data->vbus_det_gpio)) {
|
||||
if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
data->vbus_det_gpio = NULL;
|
||||
}
|
||||
data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(data->vbus_det_gpio))
|
||||
return PTR_ERR(data->vbus_det_gpio);
|
||||
|
||||
if (of_find_property(np, "usb0_vbus_power-supply", NULL)) {
|
||||
data->vbus_power_supply = devm_power_supply_get_by_phandle(dev,
|
||||
|
|
Loading…
Reference in New Issue