Commit b7187e00 authored by Biju Das's avatar Biju Das Committed by Kishon Vijay Abraham I

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: default avatarBiju Das <biju.das@bp.renesas.com>
Reviewed-and-Tested-by: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent d6c4aee8
...@@ -4,6 +4,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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 = { ...@@ -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[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
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