phy: renesas: phy-rcar-gen2: Add support for r8a77470
This patch adds support for RZ/G1C (r8a77470) SoC. RZ/G1C SoC has a PLL register shared between hsusb0 and hsusb1. Compared to other RZ/G1 and R-Car Gen2/3, USB Host needs to deassert the pll reset. Signed-off-by: Biju Das <biju.das@bp.renesas.com> Reviewed-and-Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
parent
d6c4aee8d1
commit
b7187e001a
|
@ -4,6 +4,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Renesas Solutions Corp.
|
* Copyright (C) 2014 Renesas Solutions Corp.
|
||||||
* Copyright (C) 2014 Cogent Embedded, Inc.
|
* Copyright (C) 2014 Cogent Embedded, Inc.
|
||||||
|
* Copyright (C) 2019 Renesas Electronics Corp.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
#define USBHS_LPSTS 0x02
|
#define USBHS_LPSTS 0x02
|
||||||
#define USBHS_UGCTRL 0x80
|
#define USBHS_UGCTRL 0x80
|
||||||
|
@ -35,6 +37,8 @@
|
||||||
#define USBHS_UGCTRL2_USB0SEL 0x00000030
|
#define USBHS_UGCTRL2_USB0SEL 0x00000030
|
||||||
#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
|
#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
|
||||||
#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
|
#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
|
||||||
|
#define USBHS_UGCTRL2_USB0SEL_USB20 0x00000010
|
||||||
|
#define USBHS_UGCTRL2_USB0SEL_HS_USB20 0x00000020
|
||||||
|
|
||||||
/* USB General status register (UGSTS) */
|
/* USB General status register (UGSTS) */
|
||||||
#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */
|
#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */
|
||||||
|
@ -64,6 +68,11 @@ struct rcar_gen2_phy_driver {
|
||||||
struct rcar_gen2_channel *channels;
|
struct rcar_gen2_channel *channels;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct rcar_gen2_phy_data {
|
||||||
|
const struct phy_ops *gen2_phy_ops;
|
||||||
|
const u32 (*select_value)[PHYS_PER_CHANNEL];
|
||||||
|
};
|
||||||
|
|
||||||
static int rcar_gen2_phy_init(struct phy *p)
|
static int rcar_gen2_phy_init(struct phy *p)
|
||||||
{
|
{
|
||||||
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
||||||
|
@ -180,6 +189,60 @@ static int rcar_gen2_phy_power_off(struct phy *p)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rz_g1c_phy_power_on(struct phy *p)
|
||||||
|
{
|
||||||
|
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
||||||
|
struct rcar_gen2_phy_driver *drv = phy->channel->drv;
|
||||||
|
void __iomem *base = drv->base;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&drv->lock, flags);
|
||||||
|
|
||||||
|
/* Power on USBHS PHY */
|
||||||
|
value = readl(base + USBHS_UGCTRL);
|
||||||
|
value &= ~USBHS_UGCTRL_PLLRESET;
|
||||||
|
writel(value, base + USBHS_UGCTRL);
|
||||||
|
|
||||||
|
/* As per the data sheet wait 340 micro sec for power stable */
|
||||||
|
udelay(340);
|
||||||
|
|
||||||
|
if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
|
||||||
|
value = readw(base + USBHS_LPSTS);
|
||||||
|
value |= USBHS_LPSTS_SUSPM;
|
||||||
|
writew(value, base + USBHS_LPSTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&drv->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rz_g1c_phy_power_off(struct phy *p)
|
||||||
|
{
|
||||||
|
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
||||||
|
struct rcar_gen2_phy_driver *drv = phy->channel->drv;
|
||||||
|
void __iomem *base = drv->base;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&drv->lock, flags);
|
||||||
|
/* Power off USBHS PHY */
|
||||||
|
if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
|
||||||
|
value = readw(base + USBHS_LPSTS);
|
||||||
|
value &= ~USBHS_LPSTS_SUSPM;
|
||||||
|
writew(value, base + USBHS_LPSTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = readl(base + USBHS_UGCTRL);
|
||||||
|
value |= USBHS_UGCTRL_PLLRESET;
|
||||||
|
writel(value, base + USBHS_UGCTRL);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&drv->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct phy_ops rcar_gen2_phy_ops = {
|
static const struct phy_ops rcar_gen2_phy_ops = {
|
||||||
.init = rcar_gen2_phy_init,
|
.init = rcar_gen2_phy_init,
|
||||||
.exit = rcar_gen2_phy_exit,
|
.exit = rcar_gen2_phy_exit,
|
||||||
|
@ -188,12 +251,55 @@ static const struct phy_ops rcar_gen2_phy_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct phy_ops rz_g1c_phy_ops = {
|
||||||
|
.init = rcar_gen2_phy_init,
|
||||||
|
.exit = rcar_gen2_phy_exit,
|
||||||
|
.power_on = rz_g1c_phy_power_on,
|
||||||
|
.power_off = rz_g1c_phy_power_off,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 pci_select_value[][PHYS_PER_CHANNEL] = {
|
||||||
|
[0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
|
||||||
|
[2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 usb20_select_value[][PHYS_PER_CHANNEL] = {
|
||||||
|
{ USBHS_UGCTRL2_USB0SEL_USB20, USBHS_UGCTRL2_USB0SEL_HS_USB20 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = {
|
||||||
|
.gen2_phy_ops = &rcar_gen2_phy_ops,
|
||||||
|
.select_value = pci_select_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = {
|
||||||
|
.gen2_phy_ops = &rz_g1c_phy_ops,
|
||||||
|
.select_value = usb20_select_value,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id rcar_gen2_phy_match_table[] = {
|
static const struct of_device_id rcar_gen2_phy_match_table[] = {
|
||||||
{ .compatible = "renesas,usb-phy-r8a7790" },
|
{
|
||||||
{ .compatible = "renesas,usb-phy-r8a7791" },
|
.compatible = "renesas,usb-phy-r8a77470",
|
||||||
{ .compatible = "renesas,usb-phy-r8a7794" },
|
.data = &rz_g1c_usb_phy_data,
|
||||||
{ .compatible = "renesas,rcar-gen2-usb-phy" },
|
},
|
||||||
{ }
|
{
|
||||||
|
.compatible = "renesas,usb-phy-r8a7790",
|
||||||
|
.data = &rcar_gen2_usb_phy_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "renesas,usb-phy-r8a7791",
|
||||||
|
.data = &rcar_gen2_usb_phy_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "renesas,usb-phy-r8a7794",
|
||||||
|
.data = &rcar_gen2_usb_phy_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "renesas,rcar-gen2-usb-phy",
|
||||||
|
.data = &rcar_gen2_usb_phy_data,
|
||||||
|
},
|
||||||
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
|
MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
|
||||||
|
|
||||||
|
@ -224,11 +330,6 @@ static const u32 select_mask[] = {
|
||||||
[2] = USBHS_UGCTRL2_USB2SEL,
|
[2] = USBHS_UGCTRL2_USB2SEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 select_value[][PHYS_PER_CHANNEL] = {
|
|
||||||
[0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
|
|
||||||
[2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
@ -238,6 +339,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
const struct rcar_gen2_phy_data *data;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (!dev->of_node) {
|
if (!dev->of_node) {
|
||||||
|
@ -266,6 +368,10 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
drv->clk = clk;
|
drv->clk = clk;
|
||||||
drv->base = base;
|
drv->base = base;
|
||||||
|
|
||||||
|
data = of_device_get_match_data(dev);
|
||||||
|
if (!data)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
drv->num_channels = of_get_child_count(dev->of_node);
|
drv->num_channels = of_get_child_count(dev->of_node);
|
||||||
drv->channels = devm_kcalloc(dev, drv->num_channels,
|
drv->channels = devm_kcalloc(dev, drv->num_channels,
|
||||||
sizeof(struct rcar_gen2_channel),
|
sizeof(struct rcar_gen2_channel),
|
||||||
|
@ -294,10 +400,10 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
phy->channel = channel;
|
phy->channel = channel;
|
||||||
phy->number = n;
|
phy->number = n;
|
||||||
phy->select_value = select_value[channel_num][n];
|
phy->select_value = data->select_value[channel_num][n];
|
||||||
|
|
||||||
phy->phy = devm_phy_create(dev, NULL,
|
phy->phy = devm_phy_create(dev, NULL,
|
||||||
&rcar_gen2_phy_ops);
|
data->gen2_phy_ops);
|
||||||
if (IS_ERR(phy->phy)) {
|
if (IS_ERR(phy->phy)) {
|
||||||
dev_err(dev, "Failed to create PHY\n");
|
dev_err(dev, "Failed to create PHY\n");
|
||||||
return PTR_ERR(phy->phy);
|
return PTR_ERR(phy->phy);
|
||||||
|
|
Loading…
Reference in New Issue