mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 20:46:48 +02:00
phy: renesas: rcar-gen3-usb2: Add regulator for OTG VBUS control
Enable OTG VBUS control on R-Car Gen3 USB2 PHY by registering a regulator driver that manages the VBOUT line. This change allows the controller to handle VBUS output for OTG ports using the regulator framework when the platform requires hardware-based VBUS control. Without this, some platforms cannot properly manage VBUS power on OTG- capable ports, leading to potential USB functionality issues. Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> Link: https://patch.msgid.link/6c1aebf60b4d8ff0c51a8243c68b397c1a384867.1766405010.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
230c817a16
commit
b6d7dd1577
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/usb/of.h>
|
#include <linux/usb/of.h>
|
||||||
|
|
@ -141,6 +142,7 @@ struct rcar_gen3_chan {
|
||||||
bool extcon_host;
|
bool extcon_host;
|
||||||
bool is_otg_channel;
|
bool is_otg_channel;
|
||||||
bool uses_otg_pins;
|
bool uses_otg_pins;
|
||||||
|
bool otg_internal_reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rcar_gen3_phy_drv_data {
|
struct rcar_gen3_phy_drv_data {
|
||||||
|
|
@ -225,6 +227,11 @@ static void rcar_gen3_phy_usb2_set_vbus(struct rcar_gen3_chan *ch,
|
||||||
|
|
||||||
static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
|
static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
|
||||||
{
|
{
|
||||||
|
if (ch->otg_internal_reg) {
|
||||||
|
regulator_hardware_enable(ch->vbus, vbus);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ch->phy_data->no_adp_ctrl || ch->phy_data->vblvl_ctrl) {
|
if (ch->phy_data->no_adp_ctrl || ch->phy_data->vblvl_ctrl) {
|
||||||
if (ch->vbus)
|
if (ch->vbus)
|
||||||
regulator_hardware_enable(ch->vbus, vbus);
|
regulator_hardware_enable(ch->vbus, vbus);
|
||||||
|
|
@ -593,7 +600,7 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
||||||
u32 val;
|
u32 val;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (channel->vbus) {
|
if (channel->vbus && !channel->otg_internal_reg) {
|
||||||
ret = regulator_enable(channel->vbus);
|
ret = regulator_enable(channel->vbus);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -634,7 +641,7 @@ static int rcar_gen3_phy_usb2_power_off(struct phy *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel->vbus)
|
if (channel->vbus && !channel->otg_internal_reg)
|
||||||
ret = regulator_disable(channel->vbus);
|
ret = regulator_disable(channel->vbus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -809,6 +816,128 @@ static int rcar_gen3_phy_usb2_init_bus(struct rcar_gen3_chan *channel)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rcar_gen3_phy_usb2_regulator_endisable(struct regulator_dev *rdev,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
struct rcar_gen3_chan *channel = rdev_get_drvdata(rdev);
|
||||||
|
struct device *dev = channel->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_resume_and_get(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_warn(dev, "pm_runtime_get failed: %i\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcar_gen3_phy_usb2_set_vbus(channel, USB2_VBCTRL,
|
||||||
|
USB2_VBCTRL_VBOUT, enable);
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcar_gen3_phy_usb2_regulator_enable(struct regulator_dev *rdev)
|
||||||
|
{
|
||||||
|
return rcar_gen3_phy_usb2_regulator_endisable(rdev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcar_gen3_phy_usb2_regulator_disable(struct regulator_dev *rdev)
|
||||||
|
{
|
||||||
|
return rcar_gen3_phy_usb2_regulator_endisable(rdev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcar_gen3_phy_usb2_regulator_is_enabled(struct regulator_dev *rdev)
|
||||||
|
{
|
||||||
|
struct rcar_gen3_chan *channel = rdev_get_drvdata(rdev);
|
||||||
|
void __iomem *usb2_base = channel->base;
|
||||||
|
struct device *dev = channel->dev;
|
||||||
|
u32 vbus_ctrl_reg = USB2_VBCTRL;
|
||||||
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_resume_and_get(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_warn(dev, "pm_runtime_get failed: %i\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readl(usb2_base + vbus_ctrl_reg);
|
||||||
|
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
dev_dbg(channel->dev, "%s: %08x\n", __func__, val);
|
||||||
|
|
||||||
|
return (val & USB2_VBCTRL_VBOUT) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regulator_ops rcar_gen3_phy_usb2_regulator_ops = {
|
||||||
|
.enable = rcar_gen3_phy_usb2_regulator_enable,
|
||||||
|
.disable = rcar_gen3_phy_usb2_regulator_disable,
|
||||||
|
.is_enabled = rcar_gen3_phy_usb2_regulator_is_enabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regulator_desc rcar_gen3_phy_usb2_regulator = {
|
||||||
|
.name = "otg-vbus-regulator",
|
||||||
|
.of_match = of_match_ptr("vbus-regulator"),
|
||||||
|
.ops = &rcar_gen3_phy_usb2_regulator_ops,
|
||||||
|
.type = REGULATOR_VOLTAGE,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.fixed_uV = 5000000,
|
||||||
|
.n_voltages = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rcar_gen3_phy_usb2_vbus_disable_action(void *data)
|
||||||
|
{
|
||||||
|
struct regulator *vbus = data;
|
||||||
|
|
||||||
|
regulator_disable(vbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(struct rcar_gen3_chan *channel,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
struct device *dev = channel->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
channel->vbus = devm_regulator_get_exclusive(dev, "vbus");
|
||||||
|
if (IS_ERR(channel->vbus))
|
||||||
|
return PTR_ERR(channel->vbus);
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = regulator_enable(channel->vbus);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action,
|
||||||
|
channel->vbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *channel)
|
||||||
|
{
|
||||||
|
struct device *dev = channel->dev;
|
||||||
|
struct regulator_config rcfg = { .dev = dev, };
|
||||||
|
struct regulator_dev *rdev;
|
||||||
|
bool enable = false;
|
||||||
|
|
||||||
|
rcfg.of_node = of_get_available_child_by_name(dev->of_node,
|
||||||
|
"vbus-regulator");
|
||||||
|
if (rcfg.of_node) {
|
||||||
|
rcfg.driver_data = channel;
|
||||||
|
rdev = devm_regulator_register(dev, &rcar_gen3_phy_usb2_regulator,
|
||||||
|
&rcfg);
|
||||||
|
of_node_put(rcfg.of_node);
|
||||||
|
if (IS_ERR(rdev))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(rdev),
|
||||||
|
"Failed to create vbus-regulator\n");
|
||||||
|
|
||||||
|
channel->otg_internal_reg = true;
|
||||||
|
enable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable);
|
||||||
|
}
|
||||||
|
|
||||||
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
|
@ -890,10 +1019,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||||
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
|
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel)
|
if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) {
|
||||||
channel->vbus = devm_regulator_get_exclusive(dev, "vbus");
|
ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel);
|
||||||
else
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
channel->vbus = devm_regulator_get_optional(dev, "vbus");
|
channel->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||||
|
}
|
||||||
if (IS_ERR(channel->vbus)) {
|
if (IS_ERR(channel->vbus)) {
|
||||||
if (PTR_ERR(channel->vbus) == -EPROBE_DEFER)
|
if (PTR_ERR(channel->vbus) == -EPROBE_DEFER)
|
||||||
return PTR_ERR(channel->vbus);
|
return PTR_ERR(channel->vbus);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user