summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMeng Dongyang <daniel.meng@rock-chips.com>2016-09-11 16:30:04 +0800
committerHuang, Tao <huangtao@rock-chips.com>2016-09-23 21:41:32 +0800
commit7f7a1313ef0eab26c22229398ac09ab73027d296 (patch)
tree2453068f06d71536a08e6c817a792e2fcf245aa0 /drivers
parent5c2303a10bc74ed8cff61e9c54a51cbd74a8cabe (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/phy/phy-rockchip-inno-usb2.c167
1 files changed, 140 insertions, 27 deletions
diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
index 3a530eb442ea..6b819162ac22 100644
--- a/drivers/phy/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/phy-rockchip-inno-usb2.c
@@ -131,6 +131,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.
@@ -144,6 +150,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;
@@ -170,6 +182,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_attached: otg device vbus status.
* @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.
* @mutex: for register updating in sm_work.
* @chg_work: charge detect work.
* @otg_sm_work: OTG state machine work.
@@ -191,11 +205,13 @@ 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;
int bvalid_irq;
int ls_irq;
+ int id_irq;
struct mutex mutex;
struct delayed_work chg_work;
struct delayed_work otg_sm_work;
@@ -231,6 +247,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];
@@ -399,6 +416,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;
@@ -418,17 +437,49 @@ static int rockchip_usb2phy_init(struct phy *phy)
if (rport->mode != USB_DR_MODE_HOST) {
/* clear bvalid status and enable bvalid detect irq */
ret = property_enable(rphy,
- &rport->port_cfg->bvalid_det_clr,
+ &rport->port_cfg->
+ bvalid_det_clr,
true);
if (ret)
goto out;
ret = property_enable(rphy,
- &rport->port_cfg->bvalid_det_en,
+ &rport->port_cfg->
+ bvalid_det_en,
true);
if (ret)
goto out;
+ if (rphy->edev_self) {
+ ret = property_enable(rphy,
+ &rport->port_cfg->
+ idfall_det_clr,
+ true);
+ if (ret)
+ goto out;
+
+ ret = property_enable(rphy,
+ &rport->port_cfg->
+ idfall_det_en,
+ true);
+ if (ret)
+ goto out;
+
+ ret = property_enable(rphy,
+ &rport->port_cfg->
+ idrise_det_clr,
+ true);
+ if (ret)
+ goto out;
+
+ ret = property_enable(rphy,
+ &rport->port_cfg->
+ idrise_det_en,
+ true);
+ if (ret)
+ goto out;
+ }
+
schedule_delayed_work(&rport->otg_sm_work,
OTG_SCHEDULE_DELAY);
} else {
@@ -526,25 +577,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 =
+ rport->vbus_attached =
property_enabled(rphy, &rport->port_cfg->utmi_avalid);
else
- vbus_attach =
+ rport->vbus_attached =
property_enabled(rphy, &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:
@@ -553,7 +604,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:
@@ -565,37 +616,35 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
dev_dbg(&rport->phy->dev,
"sdp cable is connecetd\n");
wake_lock(&rport->wakelock);
+ 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 connecetd\n");
+ cable = EXTCON_CHG_USB_DCP;
rockchip_usb2phy_power_off(rport->phy);
- notify_charger = true;
sch_work = true;
- cable = EXTCON_CHG_USB_DCP;
break;
case POWER_SUPPLY_TYPE_USB_CDP:
dev_dbg(&rport->phy->dev,
"cdp cable is connecetd\n");
wake_lock(&rport->wakelock);
+ 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;
case POWER_SUPPLY_TYPE_USB_FLOATING:
dev_dbg(&rport->phy->dev,
"floating cable is connecetd\n");
+ cable = EXTCON_CHG_USB_SLOW;
rockchip_usb2phy_power_off(rport->phy);
- notify_charger = true;
sch_work = true;
- cable = EXTCON_CHG_USB_SLOW;
break;
default:
break;
@@ -605,30 +654,23 @@ 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_cable_state_(rphy->edev,
- cable, 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);
wake_unlock(&rport->wakelock);
+ } else {
+ sch_work = true;
}
- sch_work = true;
break;
case OTG_STATE_A_HOST:
if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) {
@@ -641,6 +683,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_cable_state_(rphy->edev,
+ cable, rport->vbus_attached);
+
+ if (rphy->edev_self &&
+ (extcon_get_state(rphy->edev, EXTCON_USB) !=
+ rport->perip_connected))
+ extcon_set_cable_state_(rphy->edev,
+ EXTCON_USB,
+ rport->perip_connected);
+
if (sch_work)
schedule_delayed_work(&rport->otg_sm_work, delay);
}
@@ -939,6 +992,35 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data)
return IRQ_HANDLED;
}
+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, &rport->port_cfg->idfall_det_st) &&
+ !property_enabled(rphy, &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, &rport->port_cfg->idfall_det_st)) {
+ property_enable(rphy, &rport->port_cfg->idfall_det_clr,
+ true);
+ extcon_set_state(rphy->edev, EXTCON_USB_HOST, true);
+ } else if (property_enabled(rphy, &rport->port_cfg->idrise_det_st)) {
+ property_enable(rphy, &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)
@@ -999,6 +1081,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);
@@ -1028,6 +1111,23 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
return ret;
}
+ 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;
@@ -1081,6 +1181,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);
@@ -1226,6 +1327,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 },
.utmi_avalid = { 0xe2ac, 7, 7, 0, 1 },
.utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 },
},
@@ -1261,6 +1368,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 },
.utmi_avalid = { 0xe2ac, 10, 10, 0, 1 },
.utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 },
},