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:
Meng Dongyang
2016-09-11 16:30:04 +08:00
committed by Tao Huang
parent ba2b6bf932
commit 9afedca5f6

View File

@@ -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 },