summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Cooper <alcooperx@gmail.com>2017-07-19 15:11:48 -0400
committerFelipe Balbi <felipe.balbi@linux.intel.com>2017-08-15 14:18:59 +0300
commitcc29d4f67757f2bce7fca1a0eab8e40d748b0ead (patch)
tree011243c2601e56f5d5cdbb67bf9b73f58f1509d5
parent3f8b12194224992bf4b64ea00bd74e1b0a45835c (diff)
usb: bdc: Add support for USB phy
If a phy is specified in the device tree node, get it and use it. This was based on a patch by: "Srinath Mannam <srinath.mannam@broadcom.com>" Signed-off-by: Al Cooper <alcooperx@gmail.com> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc.h3
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c74
2 files changed, 75 insertions, 2 deletions
diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h
index 67c5f23a2720..6df0352cdc50 100644
--- a/drivers/usb/gadget/udc/bdc/bdc.h
+++ b/drivers/usb/gadget/udc/bdc/bdc.h
@@ -413,6 +413,9 @@ struct bdc {
/* device lock */
spinlock_t lock;
+ /* generic phy */
+ struct phy **phys;
+ int num_phys;
/* num of endpoints for a particular instantiation of IP */
unsigned int num_eps;
/*
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 2690b6fc4f6f..7a8af4b916cf 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -24,6 +24,7 @@
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/of.h>
+#include <linux/phy/phy.h>
#include <linux/moduleparam.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -445,6 +446,43 @@ static int bdc_hw_init(struct bdc *bdc)
return 0;
}
+static int bdc_phy_init(struct bdc *bdc)
+{
+ int phy_num;
+ int ret;
+
+ for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
+ ret = phy_init(bdc->phys[phy_num]);
+ if (ret)
+ goto err_exit_phy;
+ ret = phy_power_on(bdc->phys[phy_num]);
+ if (ret) {
+ phy_exit(bdc->phys[phy_num]);
+ goto err_exit_phy;
+ }
+ }
+
+ return 0;
+
+err_exit_phy:
+ while (--phy_num >= 0) {
+ phy_power_off(bdc->phys[phy_num]);
+ phy_exit(bdc->phys[phy_num]);
+ }
+
+ return ret;
+}
+
+static void bdc_phy_exit(struct bdc *bdc)
+{
+ int phy_num;
+
+ for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
+ phy_power_off(bdc->phys[phy_num]);
+ phy_exit(bdc->phys[phy_num]);
+ }
+}
+
static int bdc_probe(struct platform_device *pdev)
{
struct bdc *bdc;
@@ -454,6 +492,7 @@ static int bdc_probe(struct platform_device *pdev)
u32 temp;
struct device *dev = &pdev->dev;
struct clk *clk;
+ int phy_num;
dev_dbg(dev, "%s()\n", __func__);
@@ -492,6 +531,35 @@ static int bdc_probe(struct platform_device *pdev)
bdc->dev = dev;
dev_dbg(dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
+ bdc->num_phys = of_count_phandle_with_args(dev->of_node,
+ "phys", "#phy-cells");
+ if (bdc->num_phys > 0) {
+ bdc->phys = devm_kcalloc(dev, bdc->num_phys,
+ sizeof(struct phy *), GFP_KERNEL);
+ if (!bdc->phys)
+ return -ENOMEM;
+ } else {
+ bdc->num_phys = 0;
+ }
+ dev_info(dev, "Using %d phy(s)\n", bdc->num_phys);
+
+ for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
+ bdc->phys[phy_num] = devm_of_phy_get_by_index(
+ dev, dev->of_node, phy_num);
+ if (IS_ERR(bdc->phys[phy_num])) {
+ ret = PTR_ERR(bdc->phys[phy_num]);
+ dev_err(bdc->dev,
+ "BDC phy specified but not found:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = bdc_phy_init(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "BDC phy init failure:%d\n", ret);
+ return ret;
+ }
+
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
if ((temp & BDC_P64) &&
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
@@ -508,7 +576,7 @@ static int bdc_probe(struct platform_device *pdev)
ret = bdc_hw_init(bdc);
if (ret) {
dev_err(dev, "BDC init failure:%d\n", ret);
- return ret;
+ goto phycleanup;
}
ret = bdc_udc_init(bdc);
if (ret) {
@@ -519,7 +587,8 @@ static int bdc_probe(struct platform_device *pdev)
cleanup:
bdc_hw_exit(bdc);
-
+phycleanup:
+ bdc_phy_exit(bdc);
return ret;
}
@@ -531,6 +600,7 @@ static int bdc_remove(struct platform_device *pdev)
dev_dbg(bdc->dev, "%s ()\n", __func__);
bdc_udc_exit(bdc);
bdc_hw_exit(bdc);
+ bdc_phy_exit(bdc);
clk_disable_unprepare(bdc->clk);
return 0;
}