@@ -12,12 +12,14 @@
#include <linux/extcon-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/string.h>
@@ -111,6 +113,12 @@ struct rcar_gen3_phy {
bool powered;
};
+struct rcar_gen3_pwrrdy {
+ struct regmap *regmap;
+ u32 offset;
+ u32 mask;
+};
+
struct rcar_gen3_chan {
void __iomem *base;
struct device *dev; /* platform_device's device */
@@ -118,6 +126,7 @@ struct rcar_gen3_chan {
struct rcar_gen3_phy rphys[NUM_OF_PHYS];
struct regulator *vbus;
struct reset_control *rstc;
+ struct rcar_gen3_pwrrdy *pwrrdy;
struct work_struct work;
struct mutex lock; /* protects rphys[...].powered */
enum usb_dr_mode dr_mode;
@@ -133,6 +142,7 @@ struct rcar_gen3_phy_drv_data {
const struct phy_ops *phy_usb2_ops;
bool no_adp_ctrl;
bool init_bus;
+ bool pwrrdy;
};
/*
@@ -587,6 +597,7 @@ static const struct rcar_gen3_phy_drv_data rz_g3s_phy_usb2_data = {
.phy_usb2_ops = &rcar_gen3_phy_usb2_ops,
.no_adp_ctrl = true,
.init_bus = true,
+ .pwrrdy = true,
};
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
@@ -707,6 +718,55 @@ static int rcar_gen3_phy_usb2_init_bus(struct rcar_gen3_chan *channel)
return ret;
}
+static void rcar_gen3_phy_usb2_set_pwrrdy(struct rcar_gen3_chan *channel, bool power_on)
+{
+ struct rcar_gen3_pwrrdy *pwrrdy = channel->pwrrdy;
+
+ /* N/A on this platform. */
+ if (!pwrrdy)
+ return;
+
+ regmap_update_bits(pwrrdy->regmap, pwrrdy->offset, pwrrdy->mask, !power_on);
+}
+
+static void rcar_gen3_phy_usb2_pwrrdy_off(void *data)
+{
+ rcar_gen3_phy_usb2_set_pwrrdy(data, false);
+}
+
+static int rcar_gen3_phy_usb2_init_pwrrdy(struct rcar_gen3_chan *channel)
+{
+ struct device *dev = channel->dev;
+ struct rcar_gen3_pwrrdy *pwrrdy;
+ struct of_phandle_args args;
+ int ret;
+
+ pwrrdy = devm_kzalloc(dev, sizeof(*pwrrdy), GFP_KERNEL);
+ if (!pwrrdy)
+ return -ENOMEM;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "renesas,sysc-signal",
+ "#renesas,sysc-signal-cells", 0, &args);
+ if (ret)
+ return ret;
+
+ pwrrdy->regmap = syscon_node_to_regmap(args.np);
+ pwrrdy->offset = args.args[0];
+ pwrrdy->mask = args.args[1];
+
+ of_node_put(args.np);
+
+ if (IS_ERR(pwrrdy->regmap))
+ return PTR_ERR(pwrrdy->regmap);
+
+ channel->pwrrdy = pwrrdy;
+
+ /* Power it ON. */
+ rcar_gen3_phy_usb2_set_pwrrdy(channel, true);
+
+ return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_pwrrdy_off, channel);
+}
+
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
{
const struct rcar_gen3_phy_drv_data *phy_data;
@@ -763,6 +823,12 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, channel);
channel->dev = dev;
+ if (phy_data->pwrrdy) {
+ ret = rcar_gen3_phy_usb2_init_pwrrdy(channel);
+ if (ret)
+ goto error;
+ }
+
if (phy_data->init_bus) {
ret = rcar_gen3_phy_usb2_init_bus(channel);
if (ret)