Skip to content
Snippets Groups Projects
Commit 990ddb28 authored by Kao, Arec's avatar Kao, Arec Committed by Kenneth Johansson
Browse files

Merge pull request #277 in SW_PON/linux from feature/USB_SW-122-add-usb-ocp-Linux to xrx500

* commit '4c078fc9ec13bc961ff5cb35b12f8942b507dc41':
  <USB_SW-122>: replace printk with dev_info
  <USB_SW-122>:add USB ocp to kernel 4.9
parent d2e9fda6
Branches
Tags
No related merge requests found
......@@ -15,9 +15,22 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/workqueue.h>
#include <linux/reset.h>
#define SECOND_TIME_DELAY 30
struct ltq_dwc3_phy;
struct ltq_oc_ops {
int (*enable)(struct ltq_dwc3_phy *);
int (*disable)(struct ltq_dwc3_phy *);
int (*init)(struct ltq_dwc3_phy *);
};
struct ltq_dwc3_phy_cfg {
u8 tx_invert_ovrd;
u8 rx_invert_ovrd;
......@@ -37,6 +50,13 @@ struct ltq_dwc3_phy {
const struct ltq_dwc3_phy_cfg *cfg;
bool is_tx_pol_inv;
bool is_rx_pol_inv;
/* OCP */
bool is_enable_oc;
unsigned int irq;
struct workqueue_struct *workq;
struct delayed_work oc_recover_w;
const struct ltq_oc_ops *oc_ops;
};
static const struct ltq_dwc3_phy_cfg grx500_phy_cfg = {
......@@ -46,6 +66,103 @@ static const struct ltq_dwc3_phy_cfg grx500_phy_cfg = {
.rx_ovrd_offset = 0x4014,
};
static int ltq_grx500_oc_enable(struct ltq_dwc3_phy *dwc3_phy)
{
struct pinctrl *p;
p = devm_pinctrl_get_select(
dwc3_phy->dev, PINCTRL_STATE_IDLE);
if (IS_ERR(p))
return PTR_ERR(p);
msleep(50);
return 0;
}
static int ltq_grx500_oc_disable(struct ltq_dwc3_phy *dwc3_phy)
{
struct pinctrl *p;
p = devm_pinctrl_get_select(
dwc3_phy->dev, PINCTRL_STATE_DEFAULT);
if (IS_ERR(p))
return PTR_ERR(p);
msleep(50);
return 0;
}
void ltq_oc_recovery_worker(struct work_struct *work)
{
struct ltq_dwc3_phy *dwc3_phy = container_of(work,
struct ltq_dwc3_phy, oc_recover_w.work);
dev_info(dwc3_phy->dev, "Turn on the power again to recover\n");
/* Restart OCP to recover */
dwc3_phy->oc_ops->disable(dwc3_phy);
dwc3_phy->oc_ops->enable(dwc3_phy);
irq_set_irq_type(dwc3_phy->irq, IRQ_TYPE_LEVEL_LOW);
}
static irqreturn_t ltq_oc_irq(int irq, void *_dwc3_phy)
{
struct ltq_dwc3_phy *dwc3_phy = _dwc3_phy;
dev_info(dwc3_phy->dev, "USB OC protection is triggered\n");
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
queue_delayed_work(dwc3_phy->workq,
&dwc3_phy->oc_recover_w, SECOND_TIME_DELAY*HZ);
return IRQ_HANDLED;
}
static int ltq_grx500_oc_init(struct ltq_dwc3_phy *dwc3_phy)
{
int ret;
struct device *dev = dwc3_phy->dev;
struct platform_device *pdev = to_platform_device(dev);
dwc3_phy->irq = platform_get_irq(pdev, 0);
if (dwc3_phy->irq < 0) {
dev_err(dev, "missing IRQ resource\n");
return -EINVAL;
}
dwc3_phy->workq = create_singlethread_workqueue("usb ocp wq");
if (!dwc3_phy->workq) {
dev_err(dev, "create_singlethread_workqueue() failed\n");
return -ENOMEM;
}
INIT_DELAYED_WORK(&dwc3_phy->oc_recover_w, ltq_oc_recovery_worker);
if (dwc3_phy->vbus_reg) {
ret = regulator_enable(dwc3_phy->vbus_reg);
if (ret) {
dev_err(dev, "regulator enable failed\n");
return ret;
}
}
ret = devm_request_irq(dev, dwc3_phy->irq, &ltq_oc_irq,
IRQ_TYPE_LEVEL_LOW, "usb ocp", dwc3_phy);
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
dwc3_phy->irq, ret);
return ret;
}
return 0;
}
static const struct ltq_oc_ops grx500_ltq_oc_ops = {
.enable = ltq_grx500_oc_enable,
.disable = ltq_grx500_oc_disable,
.init = ltq_grx500_oc_init,
};
static inline u32 ltq_dwc3_phy_readl(void __iomem *base, u32 offset)
{
return readl(base + offset);
......@@ -108,6 +225,9 @@ static int ltq_dwc3_phy_power_on(struct phy *phy)
}
}
if (dwc3_phy->is_enable_oc)
dwc3_phy->oc_ops->enable(dwc3_phy);
return 0;
}
......@@ -115,6 +235,9 @@ static int ltq_dwc3_phy_power_off(struct phy *phy)
{
struct ltq_dwc3_phy *dwc3_phy = phy_get_drvdata(phy);
if (dwc3_phy->is_enable_oc)
dwc3_phy->oc_ops->disable(dwc3_phy);
if (dwc3_phy->vbus_reg)
regulator_disable(dwc3_phy->vbus_reg);
......@@ -151,6 +274,8 @@ static int ltq_usb_read_dt(struct ltq_dwc3_phy *dwc3_phy,
"invert-rx-polarity");
dwc3_phy->is_tx_pol_inv = of_property_read_bool(dev->of_node,
"invert-tx-polarity");
dwc3_phy->is_enable_oc = of_property_read_bool(dev->of_node,
"enable-over-current");
return 0;
}
......@@ -195,6 +320,8 @@ static int ltq_dwc3_phy_probe(struct platform_device *pdev)
dwc3_phy->base = base;
dwc3_phy->dev = dev;
if (dwc3_phy->is_enable_oc)
dwc3_phy->oc_ops = &grx500_ltq_oc_ops;
platform_set_drvdata(pdev, dwc3_phy);
......@@ -206,6 +333,9 @@ static int ltq_dwc3_phy_probe(struct platform_device *pdev)
phy_set_drvdata(dwc3_phy->phy, dwc3_phy);
if (dwc3_phy->is_enable_oc)
dwc3_phy->oc_ops->init(dwc3_phy);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(provider);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment