Commit 842f57ba authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'for-3.19' of...

Merge tag 'for-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-testing

Kishon writes:

Improvements in phy-core specifically on PHY core finds the PHY in the case
of non-dt boot. Adds three new PHY drivers using the PHY framework and some
miscellaneous fixes and cleanups.
parents fc625960 eee47538
......@@ -6,11 +6,17 @@ Required Properties:
- interrupts : Interrupt controller is using
- nr-ports : Number of SATA ports in use.
Optional Properties:
- phys : List of phandles to sata phys
- phy-names : Should be "0", "1", etc, one number per phandle
Example:
sata@80000 {
compatible = "marvell,orion-sata";
reg = <0x80000 0x5000>;
interrupts = <21>;
phys = <&sata_phy0>, <&sata_phy1>;
phy-names = "0", "1";
nr-ports = <2>;
}
......@@ -2,7 +2,9 @@ Berlin SATA PHY
---------------
Required properties:
- compatible: should be "marvell,berlin2q-sata-phy"
- compatible: should be one of
"marvell,berlin2-sata-phy"
"marvell,berlin2q-sata-phy"
- address-cells: should be 1
- size-cells: should be 0
- phy-cells: from the generic PHY bindings, must be 1
......
* Marvell Berlin USB PHY
Required properties:
- compatible: "marvell,berlin2-usb-phy" or "marvell,berlin2cd-usb-phy"
- reg: base address and length of the registers
- #phys-cells: should be 0
- resets: reference to the reset controller
Example:
usb-phy@f774000 {
compatible = "marvell,berlin2-usb-phy";
reg = <0xf774000 0x128>;
#phy-cells = <0>;
resets = <&chip 0x104 14>;
};
STMicroelectronics STi MIPHY28LP PHY binding
============================================
This binding describes a miphy device that is used to control PHY hardware
for SATA, PCIe or USB3.
Required properties (controller (parent) node):
- compatible : Should be "st,miphy28lp-phy".
- st,syscfg : Should be a phandle of the system configuration register group
which contain the SATA, PCIe or USB3 mode setting bits.
Required nodes : A sub-node is required for each channel the controller
provides. Address range information including the usual
'reg' and 'reg-names' properties are used inside these
nodes to describe the controller's topology. These nodes
are translated by the driver's .xlate() function.
Required properties (port (child) node):
- #phy-cells : Should be 1 (See second example)
Cell after port phandle is device type from:
- PHY_TYPE_SATA
- PHY_TYPE_PCI
- PHY_TYPE_USB3
- reg : Address and length of the register set for the device.
- reg-names : The names of the register addresses corresponding to the registers
filled in "reg". It can also contain the offset of the system configuration
registers used as glue-logic to setup the device for SATA/PCIe or USB3
devices.
- resets : phandle to the parent reset controller.
- reset-names : Associated name must be "miphy-sw-rst".
Optional properties (port (child) node):
- st,osc-rdy : to check the MIPHY0_OSC_RDY status in the glue-logic. This
is not available in all the MiPHY. For example, for STiH407, only the
MiPHY0 has this bit.
- st,osc-force-ext : to select the external oscillator. This can change from
different MiPHY inside the same SoC.
- st,sata_gen : to select which SATA_SPDMODE has to be set in the SATA system config
register.
- st,px_rx_pol_inv : to invert polarity of RXn/RXp (respectively negative line and positive
line).
- st,scc-on : enable ssc to reduce effects of EMI (only for sata or PCIe).
- st,tx-impedance-comp : to compensate tx impedance avoiding out of range values.
example:
miphy28lp_phy: miphy28lp@9b22000 {
compatible = "st,miphy28lp-phy";
st,syscfg = <&syscfg_core>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
phy_port0: port@9b22000 {
reg = <0x9b22000 0xff>,
<0x9b09000 0xff>,
<0x9b04000 0xff>,
<0x114 0x4>, /* sysctrl MiPHY cntrl */
<0x818 0x4>, /* sysctrl MiPHY status*/
<0xe0 0x4>, /* sysctrl PCIe */
<0xec 0x4>; /* sysctrl SATA */
reg-names = "sata-up",
"pcie-up",
"pipew",
"miphy-ctrl-glue",
"miphy-status-glue",
"pcie-glue",
"sata-glue";
#phy-cells = <1>;
st,osc-rdy;
reset-names = "miphy-sw-rst";
resets = <&softreset STIH407_MIPHY0_SOFTRESET>;
};
phy_port1: port@9b2a000 {
reg = <0x9b2a000 0xff>,
<0x9b19000 0xff>,
<0x9b14000 0xff>,
<0x118 0x4>,
<0x81c 0x4>,
<0xe4 0x4>,
<0xf0 0x4>;
reg-names = "sata-up",
"pcie-up",
"pipew",
"miphy-ctrl-glue",
"miphy-status-glue",
"pcie-glue",
"sata-glue";
#phy-cells = <1>;
st,osc-force-ext;
reset-names = "miphy-sw-rst";
resets = <&softreset STIH407_MIPHY1_SOFTRESET>;
};
phy_port2: port@8f95000 {
reg = <0x8f95000 0xff>,
<0x8f90000 0xff>,
<0x11c 0x4>,
<0x820 0x4>;
reg-names = "pipew",
"usb3-up",
"miphy-ctrl-glue",
"miphy-status-glue";
#phy-cells = <1>;
reset-names = "miphy-sw-rst";
resets = <&softreset STIH407_MIPHY2_SOFTRESET>;
};
};
Specifying phy control of devices
=================================
Device nodes should specify the configuration required in their "phys"
property, containing a phandle to the miphy device node and an index
specifying which configuration to use, as described in phy-bindings.txt.
example:
sata0: sata@9b20000 {
...
phys = <&phy_port0 PHY_TYPE_SATA>;
...
};
Macro definitions for the supported miphy configuration can be found in:
include/dt-bindings/phy/phy-miphy28lp.h
* Marvell MVEBU SATA PHY
Power control for the SATA phy found on Marvell MVEBU SoCs.
This document extends the binding described in phy-bindings.txt
Required properties :
- reg : Offset and length of the register set for the SATA device
- compatible : Should be "marvell,mvebu-sata-phy"
- clocks : phandle of clock and specifier that supplies the device
- clock-names : Should be "sata"
Example:
sata-phy@84000 {
compatible = "marvell,mvebu-sata-phy";
reg = <0x84000 0x0334>;
clocks = <&gate_clk 15>;
clock-names = "sata";
#phy-cells = <0>;
status = "ok";
};
Armada 375 USB cluster
----------------------
Armada 375 comes with an USB2 host and device controller and an USB3
controller. The USB cluster control register allows to manage common
features of both USB controllers.
Required properties:
- compatible: "marvell,armada-375-usb-cluster"
- reg: Should contain usb cluster register location and length.
- #phy-cells : from the generic phy bindings, must be 1. Possible
values are 1 (USB2), 2 (USB3).
Example:
usbcluster: usb-cluster@18400 {
compatible = "marvell,armada-375-usb-cluster";
reg = <0x18400 0x4>;
#phy-cells = <1>
};
......@@ -128,6 +128,7 @@ Required properties:
- compatible : Should be set to one of the following supported values:
- "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC,
- "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC.
- "samsung,exynos7-usbdrd-phy" - for exynos7 SoC.
- reg : Register offset and length of USB DRD PHY register set;
- clocks: Clock IDs array as required by the controller
- clock-names: names of clocks correseponding to IDs in the clock property;
......@@ -138,6 +139,11 @@ Required properties:
PHY operations, associated by phy name. It is used to
determine bit values for clock settings register.
For Exynos5420 this is given as 'sclk_usbphy30' in CMU.
- optional clocks: Exynos7 SoC has now following additional
gate clocks available:
- phy_pipe: for PIPE3 phy
- phy_utmi: for UTMI+ phy
- itp: for ITP generation
- samsung,pmu-syscon: phandle for PMU system controller interface, used to
control pmu registers for power isolation.
- #phy-cells : from the generic PHY bindings, must be 1;
......
......@@ -54,18 +54,14 @@ The PHY driver should create the PHY in order for other peripheral controllers
to make use of it. The PHY framework provides 2 APIs to create the PHY.
struct phy *phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data);
const struct phy_ops *ops);
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data);
const struct phy_ops *ops);
The PHY drivers can use one of the above 2 APIs to create the PHY by passing
the device pointer, phy ops and init_data.
the device pointer and phy ops.
phy_ops is a set of function pointers for performing PHY operations such as
init, exit, power_on and power_off. *init_data* is mandatory to get a reference
to the PHY in the case of non-dt boot. See section *Board File Initialization*
on how init_data should be used.
init, exit, power_on and power_off.
Inorder to dereference the private data (in phy_ops), the phy provider driver
can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in
......@@ -137,42 +133,18 @@ There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync,
phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and
phy_pm_runtime_forbid for performing PM operations.
8. Board File Initialization
Certain board file initialization is necessary in order to get a reference
to the PHY in the case of non-dt boot.
Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe,
then in the board file the following initialization should be done.
struct phy_consumer consumers[] = {
PHY_CONSUMER("dwc3.0", "usb"),
PHY_CONSUMER("pcie.0", "pcie"),
PHY_CONSUMER("sata.0", "sata"),
};
PHY_CONSUMER takes 2 parameters, first is the device name of the controller
(PHY consumer) and second is the port name.
struct phy_init_data init_data = {
.consumers = consumers,
.num_consumers = ARRAY_SIZE(consumers),
};
static const struct platform_device pipe3_phy_dev = {
.name = "pipe3-phy",
.id = -1,
.dev = {
.platform_data = {
.init_data = &init_data,
},
},
};
then, while doing phy_create, the PHY driver should pass this init_data
phy_create(dev, ops, pdata->init_data);
and the controller driver (phy consumer) should pass the port name along with
the device to get a reference to the PHY
phy_get(dev, "pcie");
8. PHY Mappings
In order to get reference to a PHY without help from DeviceTree, the framework
offers lookups which can be compared to clkdev that allow clk structures to be
bound to devices. A lookup can be made be made during runtime when a handle to
the struct phy already exists.
The framework offers the following API for registering and unregistering the
lookups.
int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id);
void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id);
9. DeviceTree Binding
......
......@@ -15,6 +15,13 @@ config GENERIC_PHY
phy users can obtain reference to the PHY. All the users of this
framework should select this config.
config PHY_BERLIN_USB
tristate "Marvell Berlin USB PHY Driver"
depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
select GENERIC_PHY
help
Enable this to support the USB PHY on Marvell Berlin SoCs.
config PHY_BERLIN_SATA
tristate "Marvell Berlin SATA PHY driver"
depends on ARCH_BERLIN && HAS_IOMEM && OF
......@@ -22,6 +29,12 @@ config PHY_BERLIN_SATA
help
Enable this to support the SATA PHY on Marvell Berlin SoCs.
config ARMADA375_USBCLUSTER_PHY
def_bool y
depends on MACH_ARMADA_375 || COMPILE_TEST
depends on OF
select GENERIC_PHY
config PHY_EXYNOS_MIPI_VIDEO
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM
......@@ -38,6 +51,14 @@ config PHY_MVEBU_SATA
depends on OF
select GENERIC_PHY
config PHY_MIPHY28LP
tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407"
depends on ARCH_STI
select GENERIC_PHY
help
Enable this to support the miphy transceiver (for SATA/PCIE/USB3)
that is part of STMicroelectronics STiH407 SoC.
config PHY_MIPHY365X
tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
depends on ARCH_STI
......@@ -193,7 +214,7 @@ config PHY_EXYNOS5250_USB2
config PHY_EXYNOS5_USBDRD
tristate "Exynos5 SoC series USB DRD PHY driver"
depends on ARCH_EXYNOS5 && OF
depends on ARCH_EXYNOS && OF
depends on HAS_IOMEM
depends on USB_DWC3_EXYNOS
select GENERIC_PHY
......
......@@ -3,11 +3,14 @@
#
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
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_OMAP_CONTROL_PHY) += phy-omap-control.o
......
/*
* USB cluster support for Armada 375 platform.
*
* Copyright (C) 2014 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2 or later. This program is licensed "as is"
* without any warranty of any kind, whether express or implied.
*
* Armada 375 comes with an USB2 host and device controller and an
* USB3 controller. The USB cluster control register allows to manage
* common features of both USB controllers.
*/
#include <dt-bindings/phy/phy.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#define USB2_PHY_CONFIG_DISABLE BIT(0)
struct armada375_cluster_phy {
struct phy *phy;
void __iomem *reg;
bool use_usb3;
int phy_provided;
};
static int armada375_usb_phy_init(struct phy *phy)
{
struct armada375_cluster_phy *cluster_phy;
u32 reg;
cluster_phy = dev_get_drvdata(phy->dev.parent);
if (!cluster_phy)
return -ENODEV;
reg = readl(cluster_phy->reg);
if (cluster_phy->use_usb3)
reg |= USB2_PHY_CONFIG_DISABLE;
else
reg &= ~USB2_PHY_CONFIG_DISABLE;
writel(reg, cluster_phy->reg);
return 0;
}
static struct phy_ops armada375_usb_phy_ops = {
.init = armada375_usb_phy_init,
.owner = THIS_MODULE,
};
/*
* Only one controller can use this PHY. We shouldn't have the case
* when two controllers want to use this PHY. But if this case occurs
* then we provide a phy to the first one and return an error for the
* next one. This error has also to be an error returned by
* devm_phy_optional_get() so different from ENODEV for USB2. In the
* USB3 case it still optional and we use ENODEV.
*/
static struct phy *armada375_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev);
if (!cluster_phy)
return ERR_PTR(-ENODEV);
/*
* Either the phy had never been requested and then the first
* usb claiming it can get it, or it had already been
* requested in this case, we only allow to use it with the
* same configuration.
*/
if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) &&
(cluster_phy->phy_provided != args->args[0]))) {
dev_err(dev, "This PHY has already been provided!\n");
dev_err(dev, "Check your device tree, only one controller can use it\n.");
if (args->args[0] == PHY_TYPE_USB2)
return ERR_PTR(-EBUSY);
else
return ERR_PTR(-ENODEV);
}
if (args->args[0] == PHY_TYPE_USB2)
cluster_phy->use_usb3 = false;
else if (args->args[0] == PHY_TYPE_USB3)
cluster_phy->use_usb3 = true;
else {
dev_err(dev, "Invalid PHY mode\n");
return ERR_PTR(-ENODEV);
}
/* Store which phy mode is used for next test */
cluster_phy->phy_provided = args->args[0];
return cluster_phy->phy;
}
static int armada375_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy *phy;
struct phy_provider *phy_provider;
void __iomem *usb_cluster_base;
struct resource *res;
struct armada375_cluster_phy *cluster_phy;
cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL);
if (!cluster_phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
if (!usb_cluster_base)
return -ENOMEM;
phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(phy);
}
cluster_phy->phy = phy;
cluster_phy->reg = usb_cluster_base;
dev_set_drvdata(dev, cluster_phy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
armada375_usb_phy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id of_usb_cluster_table[] = {
{ .compatible = "marvell,armada-375-usb-cluster", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
static struct platform_driver armada375_usb_phy_driver = {
.probe = armada375_usb_phy_probe,
.driver = {
.of_match_table = of_usb_cluster_table,
.name = "armada-375-usb-cluster",
.owner = THIS_MODULE,
}
};
module_platform_driver(armada375_usb_phy_driver);
MODULE_DESCRIPTION("Armada 375 USB cluster driver");
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
MODULE_LICENSE("GPL");
......@@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
gphy = devm_phy_create(dev, NULL, &ops, NULL);
gphy = devm_phy_create(dev, NULL, &ops);
if (IS_ERR(gphy))
return PTR_ERR(gphy);
......
......@@ -30,7 +30,8 @@
#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
#define PHY_BASE 0x200
#define BG2_PHY_BASE 0x080
#define BG2Q_PHY_BASE 0x200
/* register 0x01 */
#define REF_FREF_SEL_25 BIT(0)
......@@ -61,15 +62,16 @@ struct phy_berlin_priv {
struct clk *clk;
struct phy_berlin_desc **phys;
unsigned nphys;
u32 phy_base;
};
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
u32 mask, u32 val)
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg,
u32 phy_base, u32 reg, u32 mask, u32 val)
{
u32 regval;
/* select register */
writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR);
/* set bits */
regval = readl(ctrl_reg + PORT_VSR_DATA);
......@@ -103,17 +105,20 @@ static int phy_berlin_sata_power_on(struct phy *phy)
writel(regval, priv->base + HOST_VSA_DATA);
/* set PHY mode and ref freq to 25 MHz */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
REF_FREF_SEL_25 | PHY_MODE_SATA);
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01,
0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA);
/* set PHY up to 6 Gbps */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25,
0x0c00, PHY_GEN_MAX_6_0);
/* set 40 bits width */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23,
0x0c00, DATA_BIT_WIDTH_40);
/* use max pll rate */
phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02,
0x0000, USE_MAX_PLL_RATE);
/* set Gen3 controller speed */
regval = readl(ctrl_reg + PORT_SCR_CTL);
......@@ -218,6 +223,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
if (!priv->phys)
return -ENOMEM;
if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy"))
priv->phy_base = BG2_PHY_BASE;
else
priv->phy_base = BG2Q_PHY_BASE;
dev_set_drvdata(dev, priv);
spin_lock_init(&priv->lock);
......@@ -239,7 +249,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
if (!phy_desc)
return -ENOMEM;
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", phy_id);
return PTR_ERR(phy);
......@@ -258,13 +268,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
phy_provider =
devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id phy_berlin_sata_of_match[] = {
{ .compatible = "marvell,berlin2-sata-phy" },
{ .compatible = "marvell,berlin2q-sata-phy" },
{ },
};
......
/*
* Copyright (C) 2014 Marvell Technology Group Ltd.
*
* Antoine Tenart <antoine.tenart@free-electrons.com>
* Jisheng Zhang <jszhang@marvell.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* 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>
#define USB_PHY_PLL 0x04
#define USB_PHY_PLL_CONTROL 0x08
#define USB_PHY_TX_CTRL0 0x10
#define USB_PHY_TX_CTRL1 0x14
#define USB_PHY_TX_CTRL2 0x18
#define USB_PHY_RX_CTRL 0x20
#define USB_PHY_ANALOG 0x34
/* USB_PHY_PLL */
#define CLK_REF_DIV(x) ((x) << 4)
#define FEEDBACK_CLK_DIV(x) ((x) << 8)
/* USB_PHY_PLL_CONTROL */
#define CLK_STABLE BIT(0)
#define PLL_CTRL_PIN BIT(1)
#define PLL_CTRL_REG BIT(2)
#define PLL_ON BIT(3)
#define PHASE_OFF_TOL_125 (0x0 << 5)
#define PHASE_OFF_TOL_250 BIT(5)
#define KVC0_CALIB (0x0 << 9)
#define KVC0_REG_CTRL BIT(9)
#define KVC0_HIGH (0x0 << 10)
#define KVC0_LOW (0x3 << 10)
#define CLK_BLK_EN BIT(13)
/* USB_PHY_TX_CTRL0 */
#define EXT_HS_RCAL_EN BIT(3)
#define EXT_FS_RCAL_EN BIT(4)
#define IMPCAL_VTH_DIV(x) ((x) << 5)
#define EXT_RS_RCAL_DIV(x) ((x) << 8)
#define EXT_FS_RCAL_DIV(x) ((x) << 12)
/* USB_PHY_TX_CTRL1 */
#define TX_VDD15_14 (0x0 << 4)
#define TX_VDD15_15 BIT(4)
#define TX_VDD15_16 (0x2 << 4)
#define TX_VDD15_17 (0x3 << 4)
#define TX_VDD12_VDD (0x0 << 6)
#define TX_VDD12_11 BIT(6)
#define TX_VDD12_12 (0x2 << 6)
#define TX_VDD12_13 (0x3 << 6)
#define LOW_VDD_EN BIT(8)
#define TX_OUT_AMP(x) ((x) << 9)
/* USB_PHY_TX_CTRL2 */
#define TX_CHAN_CTRL_REG(x) ((x) << 0)
#define DRV_SLEWRATE(x) ((x) << 4)
#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6)
#define IMP_CAL_FS_HS_DLY_1 BIT(6)
#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6)
#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6)
#define FS_DRV_EN_MASK(x) ((x) << 8)
#define HS_DRV_EN_MASK(x) ((x) << 12)
/* USB_PHY_RX_CTRL */
#define PHASE_FREEZE_DLY_2_CL (0x0 << 0)
#define PHASE_FREEZE_DLY_4_CL BIT(0)
#define ACK_LENGTH_8_CL (0x0 << 2)
#define ACK_LENGTH_12_CL BIT(2)
#define ACK_LENGTH_16_CL (0x2 << 2)
#define ACK_LENGTH_20_CL (0x3 << 2)
#define SQ_LENGTH_3 (0x0 << 4)
#define SQ_LENGTH_6 BIT(4)
#define SQ_LENGTH_9 (0x2 << 4)
#define SQ_LENGTH_12 (0x3 << 4)
#define DISCON_THRESHOLD_260 (0x0 << 6)
#define DISCON_THRESHOLD_270 BIT(6)
#define DISCON_THRESHOLD_280 (0x2 << 6)
#define DISCON_THRESHOLD_290 (0x3 << 6)
#define SQ_THRESHOLD(x) ((x) << 8)
#define LPF_COEF(x) ((x) << 12)
#define INTPL_CUR_10 (0x0 << 14)
#define INTPL_CUR_20 BIT(14)
#define INTPL_CUR_30 (0x2 << 14)
#define INTPL_CUR_40 (0x3 << 14)
/* USB_PHY_ANALOG */
#define ANA_PWR_UP BIT(1)
#define ANA_PWR_DOWN BIT(2)
#define V2I_VCO_RATIO(x) ((x) << 7)
#define R_ROTATE_90 (0x0 << 10)
#define R_ROTATE_0 BIT(10)
#define MODE_TEST_EN BIT(11)
#define ANA_TEST_DC_CTRL(x) ((x) << 12)
#define to_phy_berlin_usb_priv(p) \
container_of((p), struct phy_berlin_usb_priv, phy)
static const u32 phy_berlin_pll_dividers[] = {
/* Berlin 2 */
CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
/* Berlin 2CD */
CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55),
};
struct phy_berlin_usb_priv {
void __iomem *base;
struct phy *phy;
struct reset_control *rst_ctrl;
u32 pll_divider;
};
static int phy_berlin_usb_power_on(struct phy *phy)
{
struct phy_berlin_usb_priv *priv = dev_get_drvdata(phy->dev.parent);
reset_control_reset(priv->rst_ctrl);
writel(priv->pll_divider,
priv->base + USB_PHY_PLL);
writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
priv->base + USB_PHY_ANALOG);
writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
priv->base + USB_PHY_TX_CTRL0);
writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) |
EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
priv->base + USB_PHY_TX_CTRL0);
writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 |
FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
return 0;
}
static struct phy_ops phy_berlin_usb_ops = {
.power_on = phy_berlin_usb_power_on,
.owner = THIS_MODULE,
};
static const struct of_device_id phy_berlin_sata_of_match[] = {
{
.compatible = "marvell,berlin2-usb-phy",
.data = &phy_berlin_pll_dividers[0],
},
{
.compatible = "marvell,berlin2cd-usb-phy",
.data = &phy_berlin_pll_dividers[1],
},
{ },
};
MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
static int phy_berlin_usb_probe(struct platform_device *pdev)
{
const struct of_device_id *match =
of_match_device(phy_berlin_sata_of_match, &pdev->dev);
struct phy_berlin_usb_priv *priv;
struct resource *res;
struct phy_provider *phy_provider;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(priv->rst_ctrl))
return PTR_ERR(priv->rst_ctrl);
priv->pll_divider = *((u32 *)match->data);
priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops);
if (IS_ERR(priv->phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(priv->phy);
}
platform_set_drvdata(pdev, priv);
phy_provider =
devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
static struct platform_driver phy_berlin_usb_driver = {
.probe = phy_berlin_usb_probe,
.driver = {
.name = "phy-berlin-usb",
.owner = THIS_MODULE,
.of_match_table = phy_berlin_sata_of_match,
},
};
module_platform_driver(phy_berlin_usb_driver);
MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB");
MODULE_LICENSE("GPL");
......@@ -26,6 +26,7 @@
static struct class *phy_class;
static DEFINE_MUTEX(phy_provider_mutex);
static LIST_HEAD(phy_provider_list);
static LIST_HEAD(phys);
static DEFINE_IDA(phy_ida);
static void devm_phy_release(struct device *dev, void *res)
......@@ -54,34 +55,79 @@ static int devm_phy_match(struct device *dev, void *res, void *match_data)
return res == match_data;
}
static struct phy *phy_lookup(struct device *device, const char *port)
/**
* phy_create_lookup() - allocate and register PHY/device association
* @phy: the phy of the association
* @con_id: connection ID string on device
* @dev_id: the device of the association
*
* Creates and registers phy_lookup entry.
*/
int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id)
{
unsigned int count;
struct phy *phy;
struct device *dev;
struct phy_consumer *consumers;
struct class_dev_iter iter;
struct phy_lookup *pl;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
if (!phy || !dev_id || !con_id)
return -EINVAL;
if (!phy->init_data)
continue;
count = phy->init_data->num_consumers;
consumers = phy->init_data->consumers;
while (count--) {
if (!strcmp(consumers->dev_name, dev_name(device)) &&
!strcmp(consumers->port, port)) {
class_dev_iter_exit(&iter);
return phy;
}
consumers++;
pl = kzalloc(sizeof(*pl), GFP_KERNEL);
if (!pl)
return -ENOMEM;
pl->dev_id = dev_id;
pl->con_id = con_id;
pl->phy = phy;
mutex_lock(&phy_provider_mutex);
list_add_tail(&pl->node, &phys);
mutex_unlock(&phy_provider_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(phy_create_lookup);
/**
* phy_remove_lookup() - find and remove PHY/device association
* @phy: the phy of the association
* @con_id: connection ID string on device
* @dev_id: the device of the association
*
* Finds and unregisters phy_lookup entry that was created with
* phy_create_lookup().
*/
void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id)
{
struct phy_lookup *pl;
if (!phy || !dev_id || !con_id)
return;
mutex_lock(&phy_provider_mutex);
list_for_each_entry(pl, &phys, node)
if (pl->phy == phy && !strcmp(pl->dev_id, dev_id) &&
!strcmp(pl->con_id, con_id)) {
list_del(&pl->node);
kfree(pl);
break;
}
}
mutex_unlock(&phy_provider_mutex);
}
EXPORT_SYMBOL_GPL(phy_remove_lookup);
class_dev_iter_exit(&iter);
return ERR_PTR(-ENODEV);
static struct phy *phy_find(struct device *dev, const char *con_id)
{
const char *dev_id = dev_name(dev);
struct phy_lookup *p, *pl = NULL;
mutex_lock(&phy_provider_mutex);
list_for_each_entry(p, &phys, node)
if (!strcmp(p->dev_id, dev_id) && !strcmp(p->con_id, con_id)) {
pl = p;
break;
}
mutex_unlock(&phy_provider_mutex);
return pl ? pl->phy : ERR_PTR(-ENODEV);
}
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
......@@ -414,21 +460,13 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
{
struct phy *phy;
struct class_dev_iter iter;
struct device_node *node = dev->of_node;
struct device_node *child;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
if (node != phy->dev.of_node) {
for_each_child_of_node(node, child) {
if (child == phy->dev.of_node)
goto phy_found;
}
if (args->np != phy->dev.of_node)
continue;
}
phy_found:
class_dev_iter_exit(&iter);
return phy;
}
......@@ -463,7 +501,7 @@ struct phy *phy_get(struct device *dev, const char *string)
string);
phy = _of_phy_get(dev->of_node, index);
} else {
phy = phy_lookup(dev, string);
phy = phy_find(dev, string);
}
if (IS_ERR(phy))
return phy;
......@@ -588,13 +626,11 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
* @dev: device that is creating the new phy
* @node: device node of the phy
* @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL
*
* Called to create a phy using phy framework.
*/
struct phy *phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data)
const struct phy_ops *ops)
{
int ret;
int id;
......@@ -632,7 +668,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
phy->dev.of_node = node ?: dev->of_node;
phy->id = id;
phy->ops = ops;
phy->init_data = init_data;
ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
if (ret)
......@@ -667,7 +702,6 @@ EXPORT_SYMBOL_GPL(phy_create);
* @dev: device that is creating the new phy
* @node: device node of the phy
* @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL
*
* Creates a new PHY device adding it to the PHY class.
* While at that, it also associates the device with the phy using devres.
......@@ -675,8 +709,7 @@ EXPORT_SYMBOL_GPL(phy_create);
* then, devres data is freed.
*/
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data)
const struct phy_ops *ops)
{
struct phy **ptr, *phy;
......@@ -684,7 +717,7 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
if (!ptr)
return ERR_PTR(-ENOMEM);
phy = phy_create(dev, node, ops, init_data);
phy = phy_create(dev, node, ops);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
......
......@@ -112,7 +112,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node);
state->drvdata = match->data;
phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create Display Port PHY\n");
return PTR_ERR(phy);
......
......@@ -137,7 +137,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
struct phy *phy = devm_phy_create(dev, NULL,
&exynos_mipi_video_phy_ops, NULL);
&exynos_mipi_video_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy);
......
......@@ -141,6 +141,7 @@ struct exynos5_usbdrd_phy_drvdata {
const struct exynos5_usbdrd_phy_config *phy_cfg;
u32 pmu_offset_usbdrd0_phy;
u32 pmu_offset_usbdrd1_phy;
bool has_common_clk_gate;
};
/**
......@@ -148,6 +149,9 @@ struct exynos5_usbdrd_phy_drvdata {
* @dev: pointer to device instance of this platform device
* @reg_phy: usb phy controller register memory base
* @clk: phy clock for register access
* @pipeclk: clock for pipe3 phy
* @utmiclk: clock for utmi+ phy
* @itpclk: clock for ITP generation
* @drv_data: pointer to SoC level driver data structure
* @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
* instances each with its 'phy' and 'phy_cfg'.
......@@ -155,12 +159,16 @@ struct exynos5_usbdrd_phy_drvdata {
* reference clocks' for SS and HS operations
* @ref_clk: reference clock to PHY block from which PHY's
* operational clocks are derived
* @ref_rate: rate of above reference clock
* vbus: VBUS regulator for phy
* vbus_boost: Boost regulator for VBUS present on few Exynos boards
*/
struct exynos5_usbdrd_phy {
struct device *dev;
void __iomem *reg_phy;
struct clk *clk;
struct clk *pipeclk;
struct clk *utmiclk;
struct clk *itpclk;
const struct exynos5_usbdrd_phy_drvdata *drv_data;
struct phy_usb_instance {
struct phy *phy;
......@@ -172,6 +180,7 @@ struct exynos5_usbdrd_phy {
u32 extrefclk;
struct clk *ref_clk;
struct regulator *vbus;
struct regulator *vbus_boost;
};
static inline
......@@ -447,13 +456,27 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy)
dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
clk_prepare_enable(phy_drd->ref_clk);
if (!phy_drd->drv_data->has_common_clk_gate) {
clk_prepare_enable(phy_drd->pipeclk);
clk_prepare_enable(phy_drd->utmiclk);
clk_prepare_enable(phy_drd->itpclk);
}
/* Enable VBUS supply */
if (phy_drd->vbus_boost) {
ret = regulator_enable(phy_drd->vbus_boost);
if (ret) {
dev_err(phy_drd->dev,
"Failed to enable VBUS boost supply\n");
goto fail_vbus;
}
}
if (phy_drd->vbus) {
ret = regulator_enable(phy_drd->vbus);
if (ret) {
dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
goto fail_vbus;
goto fail_vbus_boost;
}
}
......@@ -462,8 +485,17 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy)
return 0;
fail_vbus_boost:
if (phy_drd->vbus_boost)
regulator_disable(phy_drd->vbus_boost);
fail_vbus:
clk_disable_unprepare(phy_drd->ref_clk);
if (!phy_drd->drv_data->has_common_clk_gate) {
clk_disable_unprepare(phy_drd->itpclk);
clk_disable_unprepare(phy_drd->utmiclk);
clk_disable_unprepare(phy_drd->pipeclk);
}
return ret;
}
......@@ -481,8 +513,15 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
/* Disable VBUS supply */
if (phy_drd->vbus)
regulator_disable(phy_drd->vbus);
if (phy_drd->vbus_boost)
regulator_disable(phy_drd->vbus_boost);
clk_disable_unprepare(phy_drd->ref_clk);
if (!phy_drd->drv_data->has_common_clk_gate) {
clk_disable_unprepare(phy_drd->itpclk);
clk_disable_unprepare(phy_drd->pipeclk);
clk_disable_unprepare(phy_drd->utmiclk);
}
return 0;
}
......@@ -506,6 +545,57 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
.owner = THIS_MODULE,
};
static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
{
unsigned long ref_rate;
int ret;
phy_drd->clk = devm_clk_get(phy_drd->dev, "phy");
if (IS_ERR(phy_drd->clk)) {
dev_err(phy_drd->dev, "Failed to get phy clock\n");
return PTR_ERR(phy_drd->clk);
}
phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref");
if (IS_ERR(phy_drd->ref_clk)) {
dev_err(phy_drd->dev, "Failed to get phy reference clock\n");
return PTR_ERR(phy_drd->ref_clk);
}
ref_rate = clk_get_rate(phy_drd->ref_clk);
ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
if (ret) {
dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
ref_rate);
return ret;
}
if (!phy_drd->drv_data->has_common_clk_gate) {
phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe");
if (IS_ERR(phy_drd->pipeclk)) {
dev_info(phy_drd->dev,
"PIPE3 phy operational clock not specified\n");
phy_drd->pipeclk = NULL;
}
phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi");
if (IS_ERR(phy_drd->utmiclk)) {
dev_info(phy_drd->dev,
"UTMI phy operational clock not specified\n");
phy_drd->utmiclk = NULL;
}
phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp");
if (IS_ERR(phy_drd->itpclk)) {
dev_info(phy_drd->dev,
"ITP clock from main OSC not specified\n");
phy_drd->itpclk = NULL;
}
}
return 0;
}
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
{
.id = EXYNOS5_DRDPHY_UTMI,
......@@ -525,11 +615,19 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
.phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
.pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
.has_common_clk_gate = true,
};
static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
.phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
.has_common_clk_gate = true,
};
static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
.phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
.has_common_clk_gate = false,
};
static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
......@@ -539,6 +637,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
}, {
.compatible = "samsung,exynos5420-usbdrd-phy",
.data = &exynos5420_usbdrd_phy
}, {
.compatible = "samsung,exynos7-usbdrd-phy",
.data = &exynos7_usbdrd_phy
},
{ },
};
......@@ -555,7 +656,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
const struct exynos5_usbdrd_phy_drvdata *drv_data;
struct regmap *reg_pmu;
u32 pmu_offset;
unsigned long ref_rate;
int i, ret;
int channel;
......@@ -576,23 +676,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
drv_data = match->data;
phy_drd->drv_data = drv_data;
phy_drd->clk = devm_clk_get(dev, "phy");
if (IS_ERR(phy_drd->clk)) {
dev_err(dev, "Failed to get clock of phy controller\n");
return PTR_ERR(phy_drd->clk);
}
phy_drd->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(phy_drd->ref_clk)) {
dev_err(dev, "Failed to get reference clock of usbdrd phy\n");
return PTR_ERR(phy_drd->ref_clk);
}
ref_rate = clk_get_rate(phy_drd->ref_clk);
ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
ret = exynos5_usbdrd_phy_clk_handle(phy_drd);
if (ret) {
dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
ref_rate);
dev_err(dev, "Failed to initialize clocks\n");
return ret;
}
......@@ -622,7 +708,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
break;
}
/* Get Vbus regulator */
/* Get Vbus regulators */
phy_drd->vbus = devm_regulator_get(dev, "vbus");
if (IS_ERR(phy_drd->vbus)) {
ret = PTR_ERR(phy_drd->vbus);
......@@ -633,12 +719,21 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
phy_drd->vbus = NULL;
}
phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost");
if (IS_ERR(phy_drd->vbus_boost)) {
ret = PTR_ERR(phy_drd->vbus_boost);
if (ret == -EPROBE_DEFER)
return ret;
dev_warn(dev, "Failed to get VBUS boost supply regulator\n");
phy_drd->vbus_boost = NULL;
}
dev_vdbg(dev, "Creating usbdrd_phy phy\n");
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
struct phy *phy = devm_phy_create(dev, NULL,
&exynos5_usbdrd_phy_ops,
NULL);
&exynos5_usbdrd_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "Failed to create usbdrd_phy phy\n");
return PTR_ERR(phy);
......
......@@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
return ret;
}
sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL);
sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
if (IS_ERR(sata_phy->phy)) {
clk_disable_unprepare(sata_phy->phyclk);
dev_err(dev, "failed to create PHY\n");
......
......@@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
if (IS_ERR(priv->peri_ctrl))
priv->peri_ctrl = NULL;
phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(phy);
......@@ -164,10 +164,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
......
/*
* Copyright (C) 2014 STMicroelectronics
*
* STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407).
*
* Author: Alexandre Torgue <alexandre.torgue@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
*/
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <dt-bindings/phy/phy.h>
/* MiPHY registers */
#define MIPHY_CONF_RESET 0x00
#define RST_APPLI_SW BIT(0)
#define RST_CONF_SW BIT(1)
#define RST_MACRO_SW BIT(2)
#define MIPHY_RESET 0x01
#define RST_PLL_SW BIT(0)
#define RST_COMP_SW BIT(2)
#define MIPHY_STATUS_1 0x02
#define PHY_RDY BIT(0)
#define HFC_RDY BIT(1)
#define HFC_PLL BIT(2)
#define MIPHY_CONTROL 0x04
#define TERM_EN_SW BIT(2)
#define DIS_LINK_RST BIT(3)
#define AUTO_RST_RX BIT(4)
#define PX_RX_POL BIT(5)
#define MIPHY_BOUNDARY_SEL 0x0a
#define TX_SEL BIT(6)
#define SSC_SEL BIT(4)
#define GENSEL_SEL BIT(0)
#define MIPHY_BOUNDARY_1 0x0b
#define MIPHY_BOUNDARY_2 0x0c
#define SSC_EN_SW BIT(2)
#define MIPHY_PLL_CLKREF_FREQ 0x0d
#define MIPHY_SPEED 0x0e
#define TX_SPDSEL_80DEC 0
#define TX_SPDSEL_40DEC 1
#define TX_SPDSEL_20DEC 2
#define RX_SPDSEL_80DEC 0
#define RX_SPDSEL_40DEC (1 << 2)
#define RX_SPDSEL_20DEC (2 << 2)
#define MIPHY_CONF 0x0f
#define MIPHY_CTRL_TEST_SEL 0x20
#define MIPHY_CTRL_TEST_1 0x21
#define MIPHY_CTRL_TEST_2 0x22
#define MIPHY_CTRL_TEST_3 0x23
#define MIPHY_CTRL_TEST_4 0x24
#define MIPHY_FEEDBACK_TEST 0x25
#define MIPHY_DEBUG_BUS 0x26
#define MIPHY_DEBUG_STATUS_MSB 0x27
#define MIPHY_DEBUG_STATUS_LSB 0x28
#define MIPHY_PWR_RAIL_1 0x29
#define MIPHY_PWR_RAIL_2 0x2a
#define MIPHY_SYNCHAR_CONTROL 0x30
#define MIPHY_COMP_FSM_1 0x3a
#define COMP_START BIT(6)
#define MIPHY_COMP_FSM_6 0x3f
#define COMP_DONE BIT(7)
#define MIPHY_COMP_POSTP 0x42
#define MIPHY_TX_CTRL_1 0x49
#define TX_REG_STEP_0V 0
#define TX_REG_STEP_P_25MV 1
#define TX_REG_STEP_P_50MV 2
#define TX_REG_STEP_N_25MV 7
#define TX_REG_STEP_N_50MV 6
#define TX_REG_STEP_N_75MV 5
#define MIPHY_TX_CTRL_2 0x4a
#define TX_SLEW_SW_40_PS 0
#define TX_SLEW_SW_80_PS 1
#define TX_SLEW_SW_120_PS 2
#define MIPHY_TX_CTRL_3 0x4b
#define MIPHY_TX_CAL_MAN 0x4e
#define TX_SLEW_CAL_MAN_EN BIT(0)
#define MIPHY_TST_BIAS_BOOST_2 0x62
#define MIPHY_BIAS_BOOST_1 0x63
#define MIPHY_BIAS_BOOST_2 0x64
#define MIPHY_RX_DESBUFF_FDB_2 0x67
#define MIPHY_RX_DESBUFF_FDB_3 0x68
#define MIPHY_SIGDET_COMPENS1 0x69
#define MIPHY_SIGDET_COMPENS2 0x6a
#define MIPHY_JITTER_PERIOD 0x6b
#define MIPHY_JITTER_AMPLITUDE_1 0x6c
#define MIPHY_JITTER_AMPLITUDE_2 0x6d
#define MIPHY_JITTER_AMPLITUDE_3 0x6e
#define MIPHY_RX_K_GAIN 0x78
#define MIPHY_RX_BUFFER_CTRL 0x7a
#define VGA_GAIN BIT(0)
#define EQ_DC_GAIN BIT(2)
#define EQ_BOOST_GAIN BIT(3)
#define MIPHY_RX_VGA_GAIN 0x7b
#define MIPHY_RX_EQU_GAIN_1 0x7f
#define MIPHY_RX_EQU_GAIN_2 0x80
#define MIPHY_RX_EQU_GAIN_3 0x81
#define MIPHY_RX_CAL_CTRL_1 0x97
#define MIPHY_RX_CAL_CTRL_2 0x98
#define MIPHY_RX_CAL_OFFSET_CTRL 0x99
#define CAL_OFFSET_VGA_64 (0x03 << 0)
#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2)
#define VGA_OFFSET_POLARITY BIT(4)
#define OFFSET_COMPENSATION_EN BIT(6)
#define MIPHY_RX_CAL_VGA_STEP 0x9a
#define MIPHY_RX_CAL_EYE_MIN 0x9d
#define MIPHY_RX_CAL_OPT_LENGTH 0x9f
#define MIPHY_RX_LOCK_CTRL_1 0xc1
#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2
#define MIPHY_RX_LOCK_STEP 0xc4
#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9
#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca
#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb
#define MIPHY_RX_SIGDET_DATA_SEL 0xcc
#define EN_ULTRA_LOW_POWER BIT(0)
#define EN_FIRST_HALF BIT(1)
#define EN_SECOND_HALF BIT(2)
#define EN_DIGIT_SIGNAL_CHECK BIT(3)
#define MIPHY_RX_POWER_CTRL_1 0xcd
#define MIPHY_RX_POWER_CTRL_2 0xce
#define MIPHY_PLL_CALSET_CTRL 0xd3
#define MIPHY_PLL_CALSET_1 0xd4
#define MIPHY_PLL_CALSET_2 0xd5
#define MIPHY_PLL_CALSET_3 0xd6
#define MIPHY_PLL_CALSET_4 0xd7
#define MIPHY_PLL_SBR_1 0xe3
#define SET_NEW_CHANGE BIT(1)
#define MIPHY_PLL_SBR_2 0xe4
#define MIPHY_PLL_SBR_3 0xe5
#define MIPHY_PLL_SBR_4 0xe6
#define MIPHY_PLL_COMMON_MISC_2 0xe9
#define START_ACT_FILT BIT(6)
#define MIPHY_PLL_SPAREIN 0xeb
/*
* On STiH407 the glue logic can be different among MiPHY devices; for example:
* MiPHY0: OSC_FORCE_EXT means:
* 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1
* MiPHY1: OSC_FORCE_EXT means:
* 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1
* Some devices have not the possibility to check if the osc is ready.
*/
#define MIPHY_OSC_FORCE_EXT BIT(3)
#define MIPHY_OSC_RDY BIT(5)
#define MIPHY_CTRL_MASK 0x0f
#define MIPHY_CTRL_DEFAULT 0
#define MIPHY_CTRL_SYNC_D_EN BIT(2)
/* SATA / PCIe defines */
#define SATA_CTRL_MASK 0x07
#define PCIE_CTRL_MASK 0xff
#define SATA_CTRL_SELECT_SATA 1
#define SATA_CTRL_SELECT_PCIE 0
#define SYSCFG_PCIE_PCIE_VAL 0x80
#define SATA_SPDMODE 1
#define MIPHY_SATA_BANK_NB 3
#define MIPHY_PCIE_BANK_NB 2
struct miphy28lp_phy {
struct phy *phy;
struct miphy28lp_dev *phydev;
void __iomem *base;
void __iomem *pipebase;
bool osc_force_ext;
bool osc_rdy;
bool px_rx_pol_inv;
bool ssc;
bool tx_impedance;
struct reset_control *miphy_rst;
u32 sata_gen;
/* Sysconfig registers offsets needed to configure the device */
u32 syscfg_miphy_ctrl;
u32 syscfg_miphy_status;
u32 syscfg_pci;
u32 syscfg_sata;
u8 type;
};
struct miphy28lp_dev {
struct device *dev;
struct regmap *regmap;
struct mutex miphy_mutex;
struct miphy28lp_phy **phys;
};
struct miphy_initval {
u16 reg;
u16 val;
};
enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 };
static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" };
struct pll_ratio {
int clk_ref;
int calset_1;
int calset_2;
int calset_3;
int calset_4;
int cal_ctrl;
};
static struct pll_ratio sata_pll_ratio = {
.clk_ref = 0x1e,
.calset_1 = 0xc8,
.calset_2 = 0x00,
.calset_3 = 0x00,
.calset_4 = 0x00,
.cal_ctrl = 0x00,
};
static struct pll_ratio pcie_pll_ratio = {
.clk_ref = 0x1e,
.calset_1 = 0xa6,
.calset_2 = 0xaa,
.calset_3 = 0xaa,
.calset_4 = 0x00,
.cal_ctrl = 0x00,
};
static struct pll_ratio usb3_pll_ratio = {
.clk_ref = 0x1e,
.calset_1 = 0xa6,
.calset_2 = 0xaa,
.calset_3 = 0xaa,
.calset_4 = 0x04,
.cal_ctrl = 0x00,
};
struct miphy28lp_pll_gen {
int bank;
int speed;
int bias_boost_1;
int bias_boost_2;
int tx_ctrl_1;
int tx_ctrl_2;
int tx_ctrl_3;
int rx_k_gain;
int rx_vga_gain;
int rx_equ_gain_1;
int rx_equ_gain_2;
int rx_equ_gain_3;
int rx_buff_ctrl;
};
static struct miphy28lp_pll_gen sata_pll_gen[] = {
{
.bank = 0x00,
.speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC,
.bias_boost_1 = 0x00,
.bias_boost_2 = 0xae,
.tx_ctrl_2 = 0x53,
.tx_ctrl_3 = 0x00,
.rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
.rx_vga_gain = 0x00,
.rx_equ_gain_1 = 0x7d,
.rx_equ_gain_2 = 0x56,
.rx_equ_gain_3 = 0x00,
},
{
.bank = 0x01,
.speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC,
.bias_boost_1 = 0x00,
.bias_boost_2 = 0xae,
.tx_ctrl_2 = 0x72,
.tx_ctrl_3 = 0x20,
.rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
.rx_vga_gain = 0x00,
.rx_equ_gain_1 = 0x7d,
.rx_equ_gain_2 = 0x56,
.rx_equ_gain_3 = 0x00,
},
{
.bank = 0x02,
.speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC,
.bias_boost_1 = 0x00,
.bias_boost_2 = 0xae,
.tx_ctrl_2 = 0xc0,
.tx_ctrl_3 = 0x20,
.rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
.rx_vga_gain = 0x00,
.rx_equ_gain_1 = 0x7d,
.rx_equ_gain_2 = 0x56,
.rx_equ_gain_3 = 0x00,
},
};
static struct miphy28lp_pll_gen pcie_pll_gen[] = {
{
.bank = 0x00,
.speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC,
.bias_boost_1 = 0x00,
.bias_boost_2 = 0xa5,
.tx_ctrl_1 = TX_REG_STEP_N_25MV,
.tx_ctrl_2 = 0x71,
.tx_ctrl_3 = 0x60,
.rx_k_gain = 0x98,
.rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
.rx_vga_gain = 0x00,
.rx_equ_gain_1 = 0x79,
.rx_equ_gain_2 = 0x56,
},
{
.bank = 0x01,
.speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC,
.bias_boost_1 = 0x00,
.bias_boost_2 = 0xa5,
.tx_ctrl_1 = TX_REG_STEP_N_25MV,
.tx_ctrl_2 = 0x70,
.tx_ctrl_3 = 0x60,
.rx_k_gain = 0xcc,
.rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
.rx_vga_gain = 0x00,
.rx_equ_gain_1 = 0x78,
.rx_equ_gain_2 = 0x07,
},
};
static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy)
{
void *base = miphy_phy->base;
u8 val;
/* Putting Macro in reset */
writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET);
val = RST_APPLI_SW | RST_CONF_SW;
writeb_relaxed(val, base + MIPHY_CONF_RESET);
writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET);
/* Bringing the MIPHY-CPU registers out of reset */
if (miphy_phy->type == PHY_TYPE_PCIE) {
val = AUTO_RST_RX | TERM_EN_SW;
writeb_relaxed(val, base + MIPHY_CONTROL);
} else {
val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST;
writeb_relaxed(val, base + MIPHY_CONTROL);
}
}
static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy,
struct pll_ratio *pll_ratio)
{
void *base = miphy_phy->base;
u8 val;
/* Applying PLL Settings */
writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN);
writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ);
/* PLL Ratio */
writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1);
writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2);
writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3);
writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4);
writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL);
writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL);
val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN;
writeb_relaxed(val, base + MIPHY_TX_CAL_MAN);
val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64;
if (miphy_phy->type != PHY_TYPE_SATA)
val |= OFFSET_COMPENSATION_EN;
writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL);
if (miphy_phy->type == PHY_TYPE_USB3) {
writeb_relaxed(0x00, base + MIPHY_CONF);
writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP);
writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA);
writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL);
writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL);
val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF;
writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL);
}
}
static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
int i;
for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) {
struct miphy28lp_pll_gen *gen = &sata_pll_gen[i];
/* Banked settings */
writeb_relaxed(gen->bank, base + MIPHY_CONF);
writeb_relaxed(gen->speed, base + MIPHY_SPEED);
writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1);
writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2);
/* TX buffer Settings */
writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2);
writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3);
/* RX Buffer Settings */
writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL);
writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN);
writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1);
writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2);
writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3);
}
}
static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
int i;
for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) {
struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i];
/* Banked settings */
writeb_relaxed(gen->bank, base + MIPHY_CONF);
writeb_relaxed(gen->speed, base + MIPHY_SPEED);
writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1);
writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2);
/* TX buffer Settings */
writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1);
writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2);
writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3);
writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN);
/* RX Buffer Settings */
writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL);
writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN);
writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1);
writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2);
}
}
static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy)
{
unsigned long finish = jiffies + 5 * HZ;
u8 val;
/* Waiting for Compensation to complete */
do {
val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6);
if (time_after_eq(jiffies, finish))
return -EBUSY;
cpu_relax();
} while (!(val & COMP_DONE));
return 0;
}
static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy,
struct pll_ratio *pll_ratio)
{
void __iomem *base = miphy_phy->base;
/* Poll for HFC ready after reset release */
/* Compensation measurement */
writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET);
writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2);
writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ);
writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1);
if (miphy_phy->type == PHY_TYPE_PCIE)
writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET);
writeb_relaxed(0x00, base + MIPHY_RESET);
writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2);
writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
/* TX compensation offset to re-center TX impedance */
writeb_relaxed(0x00, base + MIPHY_COMP_POSTP);
if (miphy_phy->type == PHY_TYPE_PCIE)
return miphy28lp_wait_compensation(miphy_phy);
return 0;
}
static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
u8 val;
/* MIPHY Reset */
writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET);
writeb_relaxed(0x00, base + MIPHY_CONF_RESET);
writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET);
val = RST_COMP_SW | RST_PLL_SW;
writeb_relaxed(val, base + MIPHY_RESET);
writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2);
writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ);
writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1);
writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET);
writeb_relaxed(0x00, base + MIPHY_RESET);
writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2);
writeb_relaxed(0x00, base + MIPHY_CONF);
writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1);
writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2);
writeb_relaxed(0x00, base + MIPHY_CONF);
writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS);
writeb_relaxed(0x00, base + MIPHY_CONF);
}
static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
u8 val;
/* Compensate Tx impedance to avoid out of range values */
/*
* Enable the SSC on PLL for all banks
* SSC Modulation @ 31 KHz and 4000 ppm modulation amp
*/
val = readb_relaxed(base + MIPHY_BOUNDARY_2);
val |= SSC_EN_SW;
writeb_relaxed(val, base + MIPHY_BOUNDARY_2);
val = readb_relaxed(base + MIPHY_BOUNDARY_SEL);
val |= SSC_SEL;
writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL);
for (val = 0; val < MIPHY_SATA_BANK_NB; val++) {
writeb_relaxed(val, base + MIPHY_CONF);
/* Add value to each reference clock cycle */
/* and define the period length of the SSC */
writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2);
writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3);
writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4);
/* Clear any previous request */
writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
/* requests the PLL to take in account new parameters */
writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
/* To be sure there is no other pending requests */
writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
}
}
static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
u8 val;
/* Compensate Tx impedance to avoid out of range values */
/*
* Enable the SSC on PLL for all banks
* SSC Modulation @ 31 KHz and 4000 ppm modulation amp
*/
val = readb_relaxed(base + MIPHY_BOUNDARY_2);
val |= SSC_EN_SW;
writeb_relaxed(val, base + MIPHY_BOUNDARY_2);
val = readb_relaxed(base + MIPHY_BOUNDARY_SEL);
val |= SSC_SEL;
writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL);
for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) {
writeb_relaxed(val, base + MIPHY_CONF);
/* Validate Step component */
writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3);
writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4);
/* Validate Period component */
writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2);
writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4);
/* Clear any previous request */
writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
/* requests the PLL to take in account new parameters */
writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
/* To be sure there is no other pending requests */
writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
}
}
static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy)
{
/* Compensate Tx impedance to avoid out of range values */
writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP);
}
static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
int err;
u8 val;
/* Putting Macro in reset */
miphy28lp_set_reset(miphy_phy);
/* PLL calibration */
miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio);
/* Banked settings Gen1/Gen2/Gen3 */
miphy28lp_sata_config_gen(miphy_phy);
/* Power control */
/* Input bridge enable, manual input bridge control */
writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1);
/* Macro out of reset */
writeb_relaxed(0x00, base + MIPHY_CONF_RESET);
/* Poll for HFC ready after reset release */
/* Compensation measurement */
err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio);
if (err)
return err;
if (miphy_phy->px_rx_pol_inv) {
/* Invert Rx polarity */
val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL);
val |= PX_RX_POL;
writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL);
}
if (miphy_phy->ssc)
miphy_sata_tune_ssc(miphy_phy);
if (miphy_phy->tx_impedance)
miphy_tune_tx_impedance(miphy_phy);
return 0;
}
static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
int err;
/* Putting Macro in reset */
miphy28lp_set_reset(miphy_phy);
/* PLL calibration */
miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio);
/* Banked settings Gen1/Gen2 */
miphy28lp_pcie_config_gen(miphy_phy);
/* Power control */
/* Input bridge enable, manual input bridge control */
writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1);
/* Macro out of reset */
writeb_relaxed(0x00, base + MIPHY_CONF_RESET);
/* Poll for HFC ready after reset release */
/* Compensation measurement */
err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio);
if (err)
return err;
if (miphy_phy->ssc)
miphy_pcie_tune_ssc(miphy_phy);
if (miphy_phy->tx_impedance)
miphy_tune_tx_impedance(miphy_phy);
return 0;
}
static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy)
{
void __iomem *base = miphy_phy->base;
u8 val;
/* Putting Macro in reset */
miphy28lp_set_reset(miphy_phy);
/* PLL calibration */
miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio);
/* Writing The Speed Rate */
writeb_relaxed(0x00, base + MIPHY_CONF);
val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC;
writeb_relaxed(val, base + MIPHY_SPEED);
/* RX Channel compensation and calibration */
writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT);
writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1);
writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2);
val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY |
CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64;
writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL);
writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP);
writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH);
val = EQ_DC_GAIN | VGA_GAIN;
writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL);
writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1);
writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL);
/* TX compensation offset to re-center TX impedance */
writeb_relaxed(0x02, base + MIPHY_COMP_POSTP);
/* Enable GENSEL_SEL and SSC */
/* TX_SEL=0 swing preemp forced by pipe registres */
val = SSC_SEL | GENSEL_SEL;
writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL);
/* MIPHY Bias boost */
writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1);
writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2);
/* SSC modulation */
writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2);
/* MIPHY TX control */
writeb_relaxed(0x00, base + MIPHY_CONF);
/* Validate Step component */
writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3);
writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4);
/* Validate Period component */
writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2);
writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4);
/* Clear any previous request */
writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
/* requests the PLL to take in account new parameters */
writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1);
/* To be sure there is no other pending requests */
writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
/* Rx PI controller settings */
writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN);
/* MIPHY RX input bridge control */
/* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */
writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1);
writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1);
writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2);
/* MIPHY Reset for usb3 */
miphy28_usb3_miphy_reset(miphy_phy);
}
static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy)
{
unsigned long finish = jiffies + 5 * HZ;
u8 mask = HFC_PLL | HFC_RDY;
u8 val;
/*
* For PCIe and USB3 check only that PLL and HFC are ready
* For SATA check also that phy is ready!
*/
if (miphy_phy->type == PHY_TYPE_SATA)
mask |= PHY_RDY;
do {
val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1);
if ((val & mask) != mask)
cpu_relax();
else
return 0;
} while (!time_after_eq(jiffies, finish));
return -EBUSY;
}
static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy)
{
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
unsigned long finish = jiffies + 5 * HZ;
u32 val;
if (!miphy_phy->osc_rdy)
return 0;
if (!miphy_phy->syscfg_miphy_status)
return -EINVAL;
do {
regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status,
&val);
if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY)
cpu_relax();
else
return 0;
} while (!time_after_eq(jiffies, finish));
return -EBUSY;
}
static int miphy28lp_get_resource_byname(struct device_node *child,
char *rname, struct resource *res)
{
int index;
index = of_property_match_string(child, "reg-names", rname);
if (index < 0)
return -ENODEV;
return of_address_to_resource(child, index, res);
}
static int miphy28lp_get_one_addr(struct device *dev,
struct device_node *child, char *rname,
void __iomem **base)
{
struct resource res;
int ret;
ret = miphy28lp_get_resource_byname(child, rname, &res);
if (!ret) {
*base = devm_ioremap(dev, res.start, resource_size(&res));
if (!*base) {
dev_err(dev, "failed to ioremap %s address region\n"
, rname);
return -ENOENT;
}
}
return 0;
}
/* MiPHY reset and sysconf setup */
static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val)
{
int err;
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
if (!miphy_phy->syscfg_miphy_ctrl)
return -EINVAL;
err = reset_control_assert(miphy_phy->miphy_rst);
if (err) {
dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n");
return err;
}
if (miphy_phy->osc_force_ext)
miphy_val |= MIPHY_OSC_FORCE_EXT;
regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl,
MIPHY_CTRL_MASK, miphy_val);
err = reset_control_deassert(miphy_phy->miphy_rst);
if (err) {
dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n");
return err;
}
return miphy_osc_is_ready(miphy_phy);
}
static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy)
{
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int err, sata_conf = SATA_CTRL_SELECT_SATA;
if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci)
|| (!miphy_phy->base))
return -EINVAL;
dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base);
/* Configure the glue-logic */
sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE);
regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata,
SATA_CTRL_MASK, sata_conf);
regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci,
PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
/* MiPHY path and clocking init */
err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT);
if (err) {
dev_err(miphy_dev->dev, "SATA phy setup failed\n");
return err;
}
/* initialize miphy */
miphy28lp_configure_sata(miphy_phy);
return miphy_is_ready(miphy_phy);
}
static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy)
{
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int err;
if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci)
|| (!miphy_phy->base) || (!miphy_phy->pipebase))
return -EINVAL;
dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base);
/* Configure the glue-logic */
regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata,
SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci,
PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL);
/* MiPHY path and clocking init */
err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT);
if (err) {
dev_err(miphy_dev->dev, "PCIe phy setup failed\n");
return err;
}
/* initialize miphy */
err = miphy28lp_configure_pcie(miphy_phy);
if (err)
return err;
/* PIPE Wrapper Configuration */
writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */
writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */
writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */
writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */
writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */
writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */
/* Wait for phy_ready */
return miphy_is_ready(miphy_phy);
}
static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy)
{
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int err;
if ((!miphy_phy->base) || (!miphy_phy->pipebase))
return -EINVAL;
dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base);
/* MiPHY path and clocking init */
err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN);
if (err) {
dev_err(miphy_dev->dev, "USB3 phy setup failed\n");
return err;
}
/* initialize miphy */
miphy28lp_configure_usb3(miphy_phy);
/* PIPE Wrapper Configuration */
writeb_relaxed(0x68, miphy_phy->pipebase + 0x23);
writeb_relaxed(0x61, miphy_phy->pipebase + 0x24);
writeb_relaxed(0x68, miphy_phy->pipebase + 0x26);
writeb_relaxed(0x61, miphy_phy->pipebase + 0x27);
writeb_relaxed(0x18, miphy_phy->pipebase + 0x29);
writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a);
/* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */
writeb_relaxed(0X67, miphy_phy->pipebase + 0x68);
writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69);
writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a);
writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b);
writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c);
writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d);
writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e);
writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f);
return miphy_is_ready(miphy_phy);
}
static int miphy28lp_init(struct phy *phy)
{
struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy);
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int ret;
mutex_lock(&miphy_dev->miphy_mutex);
switch (miphy_phy->type) {
case PHY_TYPE_SATA:
ret = miphy28lp_init_sata(miphy_phy);
break;
case PHY_TYPE_PCIE:
ret = miphy28lp_init_pcie(miphy_phy);
break;
case PHY_TYPE_USB3:
ret = miphy28lp_init_usb3(miphy_phy);
break;
default:
return -EINVAL;
}
mutex_unlock(&miphy_dev->miphy_mutex);
return ret;
}
static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy)
{
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
struct device_node *phynode = miphy_phy->phy->dev.of_node;
int err;
if ((miphy_phy->type != PHY_TYPE_SATA) &&
(miphy_phy->type != PHY_TYPE_PCIE) &&
(miphy_phy->type != PHY_TYPE_USB3)) {
return -EINVAL;
}
err = miphy28lp_get_one_addr(miphy_dev->dev, phynode,
PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA],
&miphy_phy->base);
if (err)
return err;
if ((miphy_phy->type == PHY_TYPE_PCIE) ||
(miphy_phy->type == PHY_TYPE_USB3)) {
err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew",
&miphy_phy->pipebase);
if (err)
return err;
}
return 0;
}
static struct phy *miphy28lp_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev);
struct miphy28lp_phy *miphy_phy = NULL;
struct device_node *phynode = args->np;
int ret, index = 0;
if (!of_device_is_available(phynode)) {
dev_warn(dev, "Requested PHY is disabled\n");
return ERR_PTR(-ENODEV);
}
if (args->args_count != 1) {
dev_err(dev, "Invalid number of cells in 'phy' property\n");
return ERR_PTR(-EINVAL);
}
for (index = 0; index < of_get_child_count(dev->of_node); index++)
if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
miphy_phy = miphy_dev->phys[index];
break;
}
if (!miphy_phy) {
dev_err(dev, "Failed to find appropriate phy\n");
return ERR_PTR(-EINVAL);
}
miphy_phy->type = args->args[0];
ret = miphy28lp_get_addr(miphy_phy);
if (ret < 0)
return ERR_PTR(ret);
return miphy_phy->phy;
}
static struct phy_ops miphy28lp_ops = {
.init = miphy28lp_init,
};
static int miphy28lp_probe_resets(struct device_node *node,
struct miphy28lp_phy *miphy_phy)
{
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int err;
miphy_phy->miphy_rst = of_reset_control_get(node, "miphy-sw-rst");
if (IS_ERR(miphy_phy->miphy_rst)) {
dev_err(miphy_dev->dev,
"miphy soft reset control not defined\n");
return PTR_ERR(miphy_phy->miphy_rst);
}
err = reset_control_deassert(miphy_phy->miphy_rst);
if (err) {
dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n");
return err;
}
return 0;
}
static int miphy28lp_of_probe(struct device_node *np,
struct miphy28lp_phy *miphy_phy)
{
struct resource res;
miphy_phy->osc_force_ext =
of_property_read_bool(np, "st,osc-force-ext");
miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy");
miphy_phy->px_rx_pol_inv =
of_property_read_bool(np, "st,px_rx_pol_inv");
miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on");
miphy_phy->tx_impedance =
of_property_read_bool(np, "st,tx-impedance-comp");
of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen);
if (!miphy_phy->sata_gen)
miphy_phy->sata_gen = SATA_GEN1;
if (!miphy28lp_get_resource_byname(np, "miphy-ctrl-glue", &res))
miphy_phy->syscfg_miphy_ctrl = res.start;
if (!miphy28lp_get_resource_byname(np, "miphy-status-glue", &res))
miphy_phy->syscfg_miphy_status = res.start;
if (!miphy28lp_get_resource_byname(np, "pcie-glue", &res))
miphy_phy->syscfg_pci = res.start;
if (!miphy28lp_get_resource_byname(np, "sata-glue", &res))
miphy_phy->syscfg_sata = res.start;
return 0;
}
static int miphy28lp_probe(struct platform_device *pdev)
{
struct device_node *child, *np = pdev->dev.of_node;
struct miphy28lp_dev *miphy_dev;
struct phy_provider *provider;
struct phy *phy;
int chancount, port = 0;
int ret;
miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
if (!miphy_dev)
return -ENOMEM;
chancount = of_get_child_count(np);
miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
GFP_KERNEL);
if (!miphy_dev->phys)
return -ENOMEM;
miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(miphy_dev->regmap)) {
dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
return PTR_ERR(miphy_dev->regmap);
}
miphy_dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, miphy_dev);
mutex_init(&miphy_dev->miphy_mutex);
for_each_child_of_node(np, child) {
struct miphy28lp_phy *miphy_phy;
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
GFP_KERNEL);
if (!miphy_phy)
return -ENOMEM;
miphy_dev->phys[port] = miphy_phy;
phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(phy);
}
miphy_dev->phys[port]->phy = phy;
miphy_dev->phys[port]->phydev = miphy_dev;
ret = miphy28lp_of_probe(child, miphy_phy);
if (ret)
return ret;
ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]);
if (ret)
return ret;
phy_set_drvdata(phy, miphy_dev->phys[port]);
port++;
}
provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate);
if (IS_ERR(provider))
return PTR_ERR(provider);
return 0;
}
static const struct of_device_id miphy28lp_of_match[] = {
{.compatible = "st,miphy28lp-phy", },
{},
};
MODULE_DEVICE_TABLE(of, miphy28lp_of_match);
static struct platform_driver miphy28lp_driver = {
.probe = miphy28lp_probe,
.driver = {
.name = "miphy28lp-phy",
.owner = THIS_MODULE,
.of_match_table = miphy28lp_of_match,
}
};
module_platform_driver(miphy28lp_driver);
MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver");
MODULE_LICENSE("GPL v2");
......@@ -593,7 +593,7 @@ static int miphy365x_probe(struct platform_device *pdev)
miphy_dev->phys[port] = miphy_phy;
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(phy);
......@@ -610,10 +610,7 @@ static int miphy365x_probe(struct platform_device *pdev)
}
provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
if (IS_ERR(provider))
return PTR_ERR(provider);
return 0;
return PTR_ERR_OR_ZERO(provider);
}
static const struct of_device_id miphy365x_of_match[] = {
......
......@@ -101,7 +101,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
......
......@@ -256,7 +256,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
if (IS_ERR(generic_phy)) {
pm_runtime_disable(phy->dev);
return PTR_ERR(generic_phy);
......
......@@ -228,8 +228,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
if (IS_ERR(phy->mmio))
return PTR_ERR(phy->mmio);
generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
NULL);
generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "%s: failed to create phy\n", __func__);
return PTR_ERR(generic_phy);
......
......@@ -150,8 +150,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
if (IS_ERR(phy->mmio))
return PTR_ERR(phy->mmio);
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
NULL);
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "%s: failed to create phy\n", __func__);
return PTR_ERR(generic_phy);
......
......@@ -304,7 +304,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
phy->select_value = select_value[channel_num][n];
phy->phy = devm_phy_create(dev, NULL,
&rcar_gen2_phy_ops, NULL);
&rcar_gen2_phy_ops);
if (IS_ERR(phy->phy)) {
dev_err(dev, "Failed to create PHY\n");
return PTR_ERR(phy->phy);
......
......@@ -202,8 +202,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
struct samsung_usb2_phy_instance *p = &drv->instances[i];
dev_dbg(dev, "Creating phy \"%s\"\n", label);
p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
NULL);
p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops);
if (IS_ERR(p->phy)) {
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
label);
......
......@@ -227,7 +227,7 @@ static int spear1310_miphy_probe(struct platform_device *pdev)
return -EINVAL;
}
priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL);
priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "failed to create SATA PCIe PHY\n");
return PTR_ERR(priv->phy);
......
......@@ -259,7 +259,7 @@ static int spear1340_miphy_probe(struct platform_device *pdev)
return PTR_ERR(priv->misc);
}
priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL);
priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "failed to create SATA PCIe PHY\n");
return PTR_ERR(priv->phy);
......
......@@ -137,7 +137,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
}
phy_dev->param = res->start;
phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL);
phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create Display Port PHY\n");
return PTR_ERR(phy);
......
......@@ -148,7 +148,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy_dev->clk);
}
phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL);
phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
......@@ -160,10 +160,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev)
phy_set_drvdata(phy, phy_dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id stih41x_usb_phy_of_match[] = {
......
......@@ -157,6 +157,10 @@ 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);
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
......@@ -213,7 +217,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
if (args->args[0] >= data->num_phys)
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
......@@ -255,8 +259,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
if (IS_ERR(data->base))
return PTR_ERR(data->base);
/* Skip 0, 0 is the phy for otg which is not yet supported. */
for (i = 1; i < data->num_phys; i++) {
for (i = 0; i < data->num_phys; i++) {
struct sun4i_usb_phy *phy = data->phys + i;
char name[16];
......@@ -295,7 +298,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy->pmu);
}
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops);
if (IS_ERR(phy->phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy->phy);
......
......@@ -399,7 +399,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
......
......@@ -644,7 +644,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
struct usb_otg *otg;
struct device_node *np = pdev->dev.of_node;
struct phy_provider *phy_provider;
struct phy_init_data *init_data = NULL;
twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
if (!twl)
......@@ -655,7 +654,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
(enum twl4030_usb_mode *)&twl->usb_mode);
else if (pdata) {
twl->usb_mode = pdata->usb_mode;
init_data = pdata->init_data;
} else {
dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
return -EINVAL;
......@@ -680,7 +678,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral;
phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
phy = devm_phy_create(twl->dev, NULL, &ops);
if (IS_ERR(phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n");
return PTR_ERR(phy);
......@@ -733,6 +731,11 @@ static int twl4030_usb_probe(struct platform_device *pdev)
return status;
}
if (pdata)
err = phy_create_lookup(phy, "usb", "musb-hdrc.0");
if (err)
return err;
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(twl->dev);
......
......@@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
ctx->dev = &pdev->dev;
platform_set_drvdata(pdev, ctx);
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops);
if (IS_ERR(ctx->phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n");
rc = PTR_ERR(ctx->phy);
......
......@@ -910,7 +910,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
goto reset;
}
phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops, NULL);
phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops);
if (IS_ERR(phy)) {
err = PTR_ERR(phy);
goto unregister;
......@@ -919,7 +919,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
phy_set_drvdata(phy, padctl);
phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops, NULL);
phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops);
if (IS_ERR(phy)) {
err = PTR_ERR(phy);
goto unregister;
......
......@@ -29,8 +29,7 @@ int dwc3_host_init(struct dwc3 *dwc)
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
ret = -ENOMEM;
goto err0;
return -ENOMEM;
}
dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
......@@ -60,22 +59,33 @@ int dwc3_host_init(struct dwc3 *dwc)
goto err1;
}
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
dev_name(&xhci->dev));
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(&xhci->dev));
ret = platform_device_add(xhci);
if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n");
goto err1;
goto err2;
}
return 0;
err2:
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
dev_name(&xhci->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(&xhci->dev));
err1:
platform_device_put(xhci);
err0:
return ret;
}
void dwc3_host_exit(struct dwc3 *dwc)
{
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
dev_name(&dwc->xhci->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(&dwc->xhci->dev));
platform_device_unregister(dwc->xhci);
}
/*
*
* This header provides constants for the phy framework
*
* Copyright (C) 2014 STMicroelectronics
* Author: Gabriel Fernandez <gabriel.fernandez@st.com>
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _DT_BINDINGS_PHY
#define _DT_BINDINGS_PHY
#define PHY_NONE 0
#define PHY_TYPE_SATA 1
#define PHY_TYPE_PCIE 2
#define PHY_TYPE_USB2 3
#define PHY_TYPE_USB3 4
#endif /* _DT_BINDINGS_PHY */
......@@ -61,7 +61,6 @@ struct phy {
struct device dev;
int id;
const struct phy_ops *ops;
struct phy_init_data *init_data;
struct mutex mutex;
int init_count;
int power_count;
......@@ -84,33 +83,14 @@ struct phy_provider {
struct of_phandle_args *args);
};
/**
* struct phy_consumer - represents the phy consumer
* @dev_name: the device name of the controller that will use this PHY device
* @port: name given to the consumer port
*/
struct phy_consumer {
const char *dev_name;
const char *port;
};
/**
* struct phy_init_data - contains the list of PHY consumers
* @num_consumers: number of consumers for this PHY device
* @consumers: list of PHY consumers
*/
struct phy_init_data {
unsigned int num_consumers;
struct phy_consumer *consumers;
struct phy_lookup {
struct list_head node;
const char *dev_id;
const char *con_id;
struct phy *phy;
};
#define PHY_CONSUMER(_dev_name, _port) \
{ \
.dev_name = _dev_name, \
.port = _port, \
}
#define to_phy(dev) (container_of((dev), struct phy, dev))
#define to_phy(a) (container_of((a), struct phy, dev))
#define of_phy_provider_register(dev, xlate) \
__of_phy_provider_register((dev), THIS_MODULE, (xlate))
......@@ -159,10 +139,9 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id);
struct phy *of_phy_simple_xlate(struct device *dev,
struct of_phandle_args *args);
struct phy *phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data);
const struct phy_ops *ops);
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops, struct phy_init_data *init_data);
const struct phy_ops *ops);
void phy_destroy(struct phy *phy);
void devm_phy_destroy(struct device *dev, struct phy *phy);
struct phy_provider *__of_phy_provider_register(struct device *dev,
......@@ -174,6 +153,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
void of_phy_provider_unregister(struct phy_provider *phy_provider);
void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider);
int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id);
void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id);
#else
static inline int phy_pm_runtime_get(struct phy *phy)
{
......@@ -301,16 +282,14 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev,
static inline struct phy *phy_create(struct device *dev,
struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data)
const struct phy_ops *ops)
{
return ERR_PTR(-ENOSYS);
}
static inline struct phy *devm_phy_create(struct device *dev,
struct device_node *node,
const struct phy_ops *ops,
struct phy_init_data *init_data)
const struct phy_ops *ops)
{
return ERR_PTR(-ENOSYS);
}
......@@ -345,6 +324,13 @@ static inline void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider)
{
}
static inline int
phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id)
{
return 0;
}
static inline void phy_remove_lookup(struct phy *phy, const char *con_id,
const char *dev_id) { }
#endif
#endif /* __DRIVERS_PHY_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment