mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
usb: u2phy: add support for otg function
In the case of platform designed in usb2.0 only mode, which the dwc3 controller connect without fusb302 and type-c phy does not work, the u2phy need to support hot plug and detect otg mode, this patch add support of otg function in this mode. Change-Id: I428a4f6d17d847c6114d124733e62c0a6236b94e Signed-off-by: Meng Dongyang <daniel.meng@rock-chips.com> Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
This commit is contained in:
@@ -130,6 +130,12 @@ struct rockchip_chg_det_reg {
|
||||
* @ls_det_en: linestate detection enable register.
|
||||
* @ls_det_st: linestate detection state register.
|
||||
* @ls_det_clr: linestate detection clear register.
|
||||
* @idfall_det_en: id fall detection enable register.
|
||||
* @idfall_det_st: id fall detection state register.
|
||||
* @idfall_det_clr: id fall detection clear register.
|
||||
* @idrise_det_en: id rise detection enable register.
|
||||
* @idrise_det_st: id rise detection state register.
|
||||
* @idrise_det_clr: id rise detection clear register.
|
||||
* @utmi_avalid: utmi vbus avalid status register.
|
||||
* @utmi_bvalid: utmi vbus bvalid status register.
|
||||
* @utmi_ls: utmi linestate state register.
|
||||
@@ -143,6 +149,12 @@ struct rockchip_usb2phy_port_cfg {
|
||||
struct usb2phy_reg ls_det_en;
|
||||
struct usb2phy_reg ls_det_st;
|
||||
struct usb2phy_reg ls_det_clr;
|
||||
struct usb2phy_reg idfall_det_en;
|
||||
struct usb2phy_reg idfall_det_st;
|
||||
struct usb2phy_reg idfall_det_clr;
|
||||
struct usb2phy_reg idrise_det_en;
|
||||
struct usb2phy_reg idrise_det_st;
|
||||
struct usb2phy_reg idrise_det_clr;
|
||||
struct usb2phy_reg utmi_avalid;
|
||||
struct usb2phy_reg utmi_bvalid;
|
||||
struct usb2phy_reg utmi_ls;
|
||||
@@ -169,6 +181,7 @@ struct rockchip_usb2phy_cfg {
|
||||
/**
|
||||
* struct rockchip_usb2phy_port: usb-phy port data.
|
||||
* @port_id: flag for otg port or host port.
|
||||
* @perip_connected: flag for periphyeral connect status.
|
||||
* @suspended: phy suspended flag.
|
||||
* @utmi_avalid: utmi avalid status usage flag.
|
||||
* true - use avalid to get vbus status
|
||||
@@ -177,6 +190,7 @@ struct rockchip_usb2phy_cfg {
|
||||
* @vbus_always_on: otg vbus is always powered on.
|
||||
* @bvalid_irq: IRQ number assigned for vbus valid rise detection.
|
||||
* @ls_irq: IRQ number assigned for linestate detection.
|
||||
* @id_irq: IRQ number assigned for id fall or rise detection.
|
||||
* @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate
|
||||
* irqs to one irq in otg-port.
|
||||
* @mutex: for register updating in sm_work.
|
||||
@@ -191,12 +205,14 @@ struct rockchip_usb2phy_cfg {
|
||||
struct rockchip_usb2phy_port {
|
||||
struct phy *phy;
|
||||
unsigned int port_id;
|
||||
bool perip_connected;
|
||||
bool suspended;
|
||||
bool utmi_avalid;
|
||||
bool vbus_attached;
|
||||
bool vbus_always_on;
|
||||
int bvalid_irq;
|
||||
int ls_irq;
|
||||
int id_irq;
|
||||
int otg_mux_irq;
|
||||
struct mutex mutex;
|
||||
struct delayed_work chg_work;
|
||||
@@ -234,6 +250,7 @@ struct rockchip_usb2phy {
|
||||
enum power_supply_type chg_type;
|
||||
u8 dcd_retries;
|
||||
u8 primary_retries;
|
||||
bool edev_self;
|
||||
struct extcon_dev *edev;
|
||||
const struct rockchip_usb2phy_cfg *phy_cfg;
|
||||
struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
|
||||
@@ -410,6 +427,8 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
|
||||
dev_err(rphy->dev, "failed to register extcon device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rphy->edev_self = true;
|
||||
}
|
||||
|
||||
rphy->edev = edev;
|
||||
@@ -442,6 +461,36 @@ static int rockchip_usb2phy_init(struct phy *phy)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (rphy->edev_self) {
|
||||
ret = property_enable(rphy->grf,
|
||||
&rport->port_cfg->
|
||||
idfall_det_clr,
|
||||
true);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = property_enable(rphy->grf,
|
||||
&rport->port_cfg->
|
||||
idfall_det_en,
|
||||
true);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = property_enable(rphy->grf,
|
||||
&rport->port_cfg->
|
||||
idrise_det_clr,
|
||||
true);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = property_enable(rphy->grf,
|
||||
&rport->port_cfg->
|
||||
idrise_det_en,
|
||||
true);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
schedule_delayed_work(&rport->otg_sm_work,
|
||||
OTG_SCHEDULE_DELAY * 3);
|
||||
} else {
|
||||
@@ -548,25 +597,25 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
|
||||
static unsigned int cable;
|
||||
unsigned long delay;
|
||||
bool vbus_attach, sch_work, notify_charger;
|
||||
bool sch_work;
|
||||
|
||||
if (rport->utmi_avalid)
|
||||
vbus_attach = property_enabled(rphy->grf,
|
||||
&rport->port_cfg->utmi_avalid);
|
||||
rport->vbus_attached =
|
||||
property_enabled(rphy->grf, &rport->port_cfg->utmi_avalid);
|
||||
else
|
||||
vbus_attach = property_enabled(rphy->grf,
|
||||
&rport->port_cfg->utmi_bvalid);
|
||||
rport->vbus_attached =
|
||||
property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid);
|
||||
|
||||
sch_work = false;
|
||||
notify_charger = false;
|
||||
delay = OTG_SCHEDULE_DELAY;
|
||||
|
||||
dev_dbg(&rport->phy->dev, "%s otg sm work\n",
|
||||
usb_otg_state_string(rport->state));
|
||||
|
||||
switch (rport->state) {
|
||||
case OTG_STATE_UNDEFINED:
|
||||
rport->state = OTG_STATE_B_IDLE;
|
||||
if (!vbus_attach)
|
||||
if (!rport->vbus_attached)
|
||||
rockchip_usb2phy_power_off(rport->phy);
|
||||
/* fall through */
|
||||
case OTG_STATE_B_IDLE:
|
||||
@@ -575,7 +624,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
rport->state = OTG_STATE_A_HOST;
|
||||
rockchip_usb2phy_power_on(rport->phy);
|
||||
return;
|
||||
} else if (vbus_attach) {
|
||||
} else if (rport->vbus_attached) {
|
||||
dev_dbg(&rport->phy->dev, "vbus_attach\n");
|
||||
switch (rphy->chg_state) {
|
||||
case USB_CHG_STATE_UNDEFINED:
|
||||
@@ -585,26 +634,25 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
switch (rphy->chg_type) {
|
||||
case POWER_SUPPLY_TYPE_USB:
|
||||
dev_dbg(&rport->phy->dev, "sdp cable is connected\n");
|
||||
cable = EXTCON_CHG_USB_SDP;
|
||||
rockchip_usb2phy_power_on(rport->phy);
|
||||
rport->state = OTG_STATE_B_PERIPHERAL;
|
||||
notify_charger = true;
|
||||
rport->perip_connected = true;
|
||||
sch_work = true;
|
||||
cable = EXTCON_CHG_USB_SDP;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_DCP:
|
||||
dev_dbg(&rport->phy->dev, "dcp cable is connected\n");
|
||||
rockchip_usb2phy_power_off(rport->phy);
|
||||
notify_charger = true;
|
||||
sch_work = true;
|
||||
cable = EXTCON_CHG_USB_DCP;
|
||||
rockchip_usb2phy_power_off(rport->phy);
|
||||
sch_work = true;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_CDP:
|
||||
dev_dbg(&rport->phy->dev, "cdp cable is connected\n");
|
||||
cable = EXTCON_CHG_USB_CDP;
|
||||
rockchip_usb2phy_power_on(rport->phy);
|
||||
rport->state = OTG_STATE_B_PERIPHERAL;
|
||||
notify_charger = true;
|
||||
rport->perip_connected = true;
|
||||
sch_work = true;
|
||||
cable = EXTCON_CHG_USB_CDP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -614,34 +662,22 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
notify_charger = true;
|
||||
rphy->chg_state = USB_CHG_STATE_UNDEFINED;
|
||||
rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
if (rport->vbus_attached != vbus_attach) {
|
||||
rport->vbus_attached = vbus_attach;
|
||||
|
||||
if (notify_charger && rphy->edev) {
|
||||
extcon_set_state_sync(rphy->edev,
|
||||
cable, vbus_attach);
|
||||
if (cable == EXTCON_CHG_USB_SDP)
|
||||
extcon_set_state_sync(rphy->edev,
|
||||
EXTCON_USB,
|
||||
vbus_attach);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (!vbus_attach) {
|
||||
if (!rport->vbus_attached) {
|
||||
dev_dbg(&rport->phy->dev, "usb disconnect\n");
|
||||
rphy->chg_state = USB_CHG_STATE_UNDEFINED;
|
||||
rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
rport->state = OTG_STATE_B_IDLE;
|
||||
rport->perip_connected = false;
|
||||
delay = 0;
|
||||
rockchip_usb2phy_power_off(rport->phy);
|
||||
} else {
|
||||
sch_work = true;
|
||||
}
|
||||
sch_work = true;
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) {
|
||||
@@ -654,6 +690,17 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
|
||||
if (extcon_get_state(rphy->edev, cable) != rport->vbus_attached)
|
||||
extcon_set_state_sync(rphy->edev,
|
||||
cable, rport->vbus_attached);
|
||||
|
||||
if (rphy->edev_self &&
|
||||
(extcon_get_state(rphy->edev, EXTCON_USB) !=
|
||||
rport->perip_connected))
|
||||
extcon_set_state_sync(rphy->edev,
|
||||
EXTCON_USB,
|
||||
rport->perip_connected);
|
||||
|
||||
if (sch_work)
|
||||
schedule_delayed_work(&rport->otg_sm_work, delay);
|
||||
}
|
||||
@@ -976,6 +1023,35 @@ static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data)
|
||||
{
|
||||
struct rockchip_usb2phy_port *rport = data;
|
||||
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
|
||||
|
||||
if (!property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st) &&
|
||||
!property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st))
|
||||
return IRQ_NONE;
|
||||
|
||||
mutex_lock(&rport->mutex);
|
||||
|
||||
/* clear id fall or rise detect irq pending status */
|
||||
if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) {
|
||||
property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr,
|
||||
true);
|
||||
extcon_set_state(rphy->edev, EXTCON_USB_HOST, true);
|
||||
} else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) {
|
||||
property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr,
|
||||
true);
|
||||
extcon_set_state(rphy->edev, EXTCON_USB_HOST, false);
|
||||
}
|
||||
|
||||
extcon_sync(rphy->edev, EXTCON_USB_HOST);
|
||||
|
||||
mutex_unlock(&rport->mutex);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
|
||||
struct rockchip_usb2phy_port *rport,
|
||||
struct device_node *child_np)
|
||||
@@ -1036,6 +1112,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
*/
|
||||
rport->suspended = true;
|
||||
rport->vbus_attached = false;
|
||||
rport->perip_connected = false;
|
||||
|
||||
mutex_init(&rport->mutex);
|
||||
|
||||
@@ -1110,6 +1187,23 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
}
|
||||
}
|
||||
|
||||
if (rphy->edev_self) {
|
||||
rport->id_irq = of_irq_get_byname(child_np, "otg-id");
|
||||
if (rport->id_irq < 0) {
|
||||
dev_err(rphy->dev, "no otg id irq provided\n");
|
||||
return rport->id_irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(rphy->dev, rport->id_irq, NULL,
|
||||
rockchip_usb2phy_id_irq,
|
||||
IRQF_ONESHOT,
|
||||
"rockchip_usb2phy_id", rport);
|
||||
if (ret) {
|
||||
dev_err(rphy->dev, "failed to request otg-id irq handle\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(rphy->edev)) {
|
||||
rport->event_nb.notifier_call = rockchip_otg_event;
|
||||
|
||||
@@ -1172,6 +1266,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
|
||||
phy_cfgs = match->data;
|
||||
rphy->chg_state = USB_CHG_STATE_UNDEFINED;
|
||||
rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
rphy->edev_self = false;
|
||||
platform_set_drvdata(pdev, rphy);
|
||||
|
||||
ret = rockchip_usb2phy_extcon_register(rphy);
|
||||
@@ -1414,6 +1509,12 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = {
|
||||
.bvalid_det_en = { 0x0680, 3, 3, 0, 1 },
|
||||
.bvalid_det_st = { 0x0690, 3, 3, 0, 1 },
|
||||
.bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 },
|
||||
.idfall_det_en = { 0x0680, 6, 6, 0, 1 },
|
||||
.idfall_det_st = { 0x0690, 6, 6, 0, 1 },
|
||||
.idfall_det_clr = { 0x06a0, 6, 6, 0, 1 },
|
||||
.idrise_det_en = { 0x0680, 5, 5, 0, 1 },
|
||||
.idrise_det_st = { 0x0690, 5, 5, 0, 1 },
|
||||
.idrise_det_clr = { 0x06a0, 5, 5, 0, 1 },
|
||||
.ls_det_en = { 0x0680, 2, 2, 0, 1 },
|
||||
.ls_det_st = { 0x0690, 2, 2, 0, 1 },
|
||||
.ls_det_clr = { 0x06a0, 2, 2, 0, 1 },
|
||||
@@ -1473,6 +1574,12 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
|
||||
.bvalid_det_en = { 0x0110, 2, 2, 0, 1 },
|
||||
.bvalid_det_st = { 0x0114, 2, 2, 0, 1 },
|
||||
.bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
|
||||
.idfall_det_en = { 0x0110, 5, 5, 0, 1 },
|
||||
.idfall_det_st = { 0x0114, 5, 5, 0, 1 },
|
||||
.idfall_det_clr = { 0x0118, 5, 5, 0, 1 },
|
||||
.idrise_det_en = { 0x0110, 4, 4, 0, 1 },
|
||||
.idrise_det_st = { 0x0114, 4, 4, 0, 1 },
|
||||
.idrise_det_clr = { 0x0118, 4, 4, 0, 1 },
|
||||
.ls_det_en = { 0x0110, 0, 0, 0, 1 },
|
||||
.ls_det_st = { 0x0114, 0, 0, 0, 1 },
|
||||
.ls_det_clr = { 0x0118, 0, 0, 0, 1 },
|
||||
@@ -1554,6 +1661,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
|
||||
.bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 },
|
||||
.bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 },
|
||||
.bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
|
||||
.idfall_det_en = { 0xe3c0, 5, 5, 0, 1 },
|
||||
.idfall_det_st = { 0xe3e0, 5, 5, 0, 1 },
|
||||
.idfall_det_clr = { 0xe3d0, 5, 5, 0, 1 },
|
||||
.idrise_det_en = { 0xe3c0, 4, 4, 0, 1 },
|
||||
.idrise_det_st = { 0xe3e0, 4, 4, 0, 1 },
|
||||
.idrise_det_clr = { 0xe3d0, 4, 4, 0, 1 },
|
||||
.ls_det_en = { 0xe3c0, 2, 2, 0, 1 },
|
||||
.ls_det_st = { 0xe3e0, 2, 2, 0, 1 },
|
||||
.ls_det_clr = { 0xe3d0, 2, 2, 0, 1 },
|
||||
@@ -1594,6 +1707,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
|
||||
.bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 },
|
||||
.bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 },
|
||||
.bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
|
||||
.idfall_det_en = { 0xe3c0, 10, 10, 0, 1 },
|
||||
.idfall_det_st = { 0xe3e0, 10, 10, 0, 1 },
|
||||
.idfall_det_clr = { 0xe3d0, 10, 10, 0, 1 },
|
||||
.idrise_det_en = { 0xe3c0, 9, 9, 0, 1 },
|
||||
.idrise_det_st = { 0xe3e0, 9, 9, 0, 1 },
|
||||
.idrise_det_clr = { 0xe3d0, 9, 9, 0, 1 },
|
||||
.ls_det_en = { 0xe3c0, 7, 7, 0, 1 },
|
||||
.ls_det_st = { 0xe3e0, 7, 7, 0, 1 },
|
||||
.ls_det_clr = { 0xe3d0, 7, 7, 0, 1 },
|
||||
|
||||
Reference in New Issue
Block a user