mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
phy: rockchip: inno-usb2: Support GPIO ID detect for USB2PHY
This patch register gpio id notifier to support USB2PHY to get extcon
message from extcon-usb-gpio.c driver. The extcon-gpio-usb.c driver
returns the state based on the ID and Vbus pin values as shown below.
State | ID | VBUS
----------------------------------------
[1] USB | H | H
[2] none | H | L
[3] USB-HOST | L | H
[4] USB-HOST | L | L
There is no need to control usb bvalid when USB_HOST state is true.
When USB_HOST state is true, we need to configure the iddig related
registers to trigger the controller's ID interrupt and set the
controller to HOST mode. When USB_HOST state is false, we need to
restore the register configuration.
Change-Id: Ia17fa67f5a26b2e5d989ede23ee6243cdc52f05f
Signed-off-by: Jianwei Zheng <jianwei.zheng@rock-chips.com>
Signed-off-by: William Wu <william.wu@rock-chips.com>
This commit is contained in:
@@ -248,6 +248,7 @@ struct rockchip_usb2phy_cfg {
|
||||
* @suspended: phy suspended flag.
|
||||
* @typec_vbus_det: Type-C otg vbus detect.
|
||||
* @gpio_vbus_det: gpio otg vbus detect.
|
||||
* @gpio_id_det: gpio otg id detect.
|
||||
* @utmi_avalid: utmi avalid status usage flag.
|
||||
* true - use avalid to get vbus status
|
||||
* false - use bvalid to get vbus status
|
||||
@@ -271,7 +272,8 @@ struct rockchip_usb2phy_cfg {
|
||||
* @sw: orientation switch, communicate with TCPM (Type-C Port Manager).
|
||||
* @port_cfg: port register configuration, assigned by driver data.
|
||||
* @event_nb: hold event notification callback.
|
||||
* @gpio_extcon_nb: hold extcon usb gpio notification callback.
|
||||
* @gpio_vbus_nb: hold extcon usb gpio vbus notification callback.
|
||||
* @gpio_id_nb: hold extcon usb gpio id notification callback.
|
||||
* @state: define OTG enumeration states before device reset.
|
||||
* @mode: the dr_mode of the controller.
|
||||
*/
|
||||
@@ -285,6 +287,7 @@ struct rockchip_usb2phy_port {
|
||||
bool suspended;
|
||||
bool typec_vbus_det;
|
||||
bool gpio_vbus_det;
|
||||
bool gpio_id_det;
|
||||
bool utmi_avalid;
|
||||
bool vbus_attached;
|
||||
bool vbus_always_on;
|
||||
@@ -305,7 +308,8 @@ struct rockchip_usb2phy_port {
|
||||
struct typec_switch_dev *sw;
|
||||
const struct rockchip_usb2phy_port_cfg *port_cfg;
|
||||
struct notifier_block event_nb;
|
||||
struct notifier_block gpio_extcon_nb;
|
||||
struct notifier_block gpio_vbus_nb;
|
||||
struct notifier_block gpio_id_nb;
|
||||
struct wake_lock wakelock;
|
||||
enum usb_otg_state state;
|
||||
enum usb_dr_mode mode;
|
||||
@@ -2060,30 +2064,84 @@ static void rockchip_usb2phy_usb_bvalid_enable(struct rockchip_usb2phy_port *rpo
|
||||
property_enable(rphy->grf, &cfg->bvalid_grf_con, enable);
|
||||
}
|
||||
|
||||
static int rockchip_usb2phy_gpio_extcon_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
static int rockchip_usb2phy_gpio_vbus_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct rockchip_usb2phy_port *rport =
|
||||
container_of(nb, struct rockchip_usb2phy_port, gpio_extcon_nb);
|
||||
container_of(nb, struct rockchip_usb2phy_port, gpio_vbus_nb);
|
||||
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
|
||||
|
||||
/* no need to control bvalid if USB_HOST state is true */
|
||||
if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
rockchip_usb2phy_usb_bvalid_enable(rport,
|
||||
extcon_get_state(rphy->edev, EXTCON_USB));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int rockchip_usb2phy_gpio_id_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct rockchip_usb2phy_port *rport =
|
||||
container_of(nb, struct rockchip_usb2phy_port, gpio_id_nb);
|
||||
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
|
||||
struct regmap *base = get_reg_base(rphy);
|
||||
int state = extcon_get_state(rphy->edev, EXTCON_USB_HOST);
|
||||
bool iddig_output = property_enabled(base, &rport->port_cfg->iddig_output);
|
||||
bool iddig_en = property_enabled(base, &rport->port_cfg->iddig_en);
|
||||
|
||||
if (state < 0)
|
||||
state = 0;
|
||||
|
||||
dev_dbg(rphy->dev, "gpio id extcon state: %d\n", state);
|
||||
|
||||
if (iddig_en && ((state && !iddig_output) || (!state && iddig_output)))
|
||||
goto out;
|
||||
|
||||
if (state) {
|
||||
rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_HOST, 0);
|
||||
property_enable(base, &rport->port_cfg->iddig_output, false);
|
||||
property_enable(base, &rport->port_cfg->iddig_en, true);
|
||||
} else {
|
||||
rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_DEVICE, 0);
|
||||
property_enable(base, &rport->port_cfg->iddig_output, true);
|
||||
property_enable(base, &rport->port_cfg->iddig_en, true);
|
||||
}
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int rockchip_usb2phy_gpio_extcon_register_notifier(struct rockchip_usb2phy *rphy)
|
||||
{
|
||||
struct rockchip_usb2phy_port *rport = &rphy->ports[USB2PHY_PORT_OTG];
|
||||
struct extcon_dev *edev = rphy->edev;
|
||||
struct regmap *base = get_reg_base(rphy);
|
||||
int ret;
|
||||
|
||||
rport->gpio_extcon_nb.notifier_call = rockchip_usb2phy_gpio_extcon_notifier;
|
||||
ret = devm_extcon_register_notifier(rphy->dev, edev, EXTCON_USB,
|
||||
&rport->gpio_extcon_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (rport->gpio_vbus_det) {
|
||||
rport->gpio_vbus_nb.notifier_call = rockchip_usb2phy_gpio_vbus_notifier;
|
||||
ret = devm_extcon_register_notifier(rphy->dev, edev, EXTCON_USB,
|
||||
&rport->gpio_vbus_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rport->gpio_id_det) {
|
||||
rport->gpio_id_nb.notifier_call = rockchip_usb2phy_gpio_id_notifier;
|
||||
ret = devm_extcon_register_notifier(rphy->dev, edev, EXTCON_USB_HOST,
|
||||
&rport->gpio_id_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) {
|
||||
rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_HOST, 0);
|
||||
property_enable(base, &rport->port_cfg->iddig_output, false);
|
||||
property_enable(base, &rport->port_cfg->iddig_en, true);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2221,6 +2279,9 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
rport->gpio_vbus_det =
|
||||
of_property_read_bool(child_np, "rockchip,gpio-vbus-det");
|
||||
|
||||
rport->gpio_id_det =
|
||||
of_property_read_bool(child_np, "rockchip,gpio-id-det");
|
||||
|
||||
rport->sel_pipe_phystatus =
|
||||
of_property_read_bool(child_np, "rockchip,sel-pipe-phystatus");
|
||||
|
||||
@@ -2234,14 +2295,6 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
}
|
||||
}
|
||||
|
||||
if (rport->gpio_vbus_det) {
|
||||
ret = rockchip_usb2phy_gpio_extcon_register_notifier(rphy);
|
||||
if (ret) {
|
||||
dev_err(rphy->dev, "failed to register gpio extcon notifier\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get Vbus regulators */
|
||||
rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus");
|
||||
if (IS_ERR(rport->vbus)) {
|
||||
@@ -2254,6 +2307,14 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
rport->vbus = NULL;
|
||||
}
|
||||
|
||||
if (rport->gpio_vbus_det || rport->gpio_id_det) {
|
||||
ret = rockchip_usb2phy_gpio_extcon_register_notifier(rphy);
|
||||
if (ret) {
|
||||
dev_err(rphy->dev, "failed to register gpio extcon notifier\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
|
||||
iddig = property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig);
|
||||
if (rphy->edev_self && (rport->mode == USB_DR_MODE_HOST ||
|
||||
|
||||
Reference in New Issue
Block a user