* [V4 PATCH 20/26] usb: ehci: ehci-mv: add extern chip support
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
It does the similar things as what we do for udc driver.
Signed-off-by: Chao Xie <chao.xie@marvell.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
---
drivers/usb/host/ehci-mv.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index be504fd..171e145 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -256,8 +256,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
goto err_disable_clk;
#endif
} else {
- if (pdata->set_vbus)
- pdata->set_vbus(1);
+ if (mv_usb2_has_extern_call(ehci_mv->mvphy, vbus, set_vbus))
+ mv_usb2_extern_call(ehci_mv->mvphy, vbus, set_vbus, 1);
retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
if (retval) {
@@ -275,8 +275,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
return 0;
err_set_vbus:
- if (pdata->set_vbus)
- pdata->set_vbus(0);
+ if (mv_usb2_has_extern_call(ehci_mv->mvphy, vbus, set_vbus))
+ mv_usb2_extern_call(ehci_mv->mvphy, vbus, set_vbus, 0);
err_disable_clk:
mv_ehci_disable(ehci_mv);
err_clear_drvdata:
@@ -299,8 +299,8 @@ static int mv_ehci_remove(struct platform_device *pdev)
otg_set_host(ehci_mv->otg->otg, NULL);
if (ehci_mv->mode == MV_USB_MODE_HOST) {
- if (ehci_mv->pdata->set_vbus)
- ehci_mv->pdata->set_vbus(0);
+ if (mv_usb2_has_extern_call(ehci_mv->mvphy, vbus, set_vbus))
+ mv_usb2_extern_call(ehci_mv->mvphy, vbus, set_vbus, 1);
mv_ehci_disable(ehci_mv);
}
--
1.7.4.1
^ permalink raw reply related
* [V4 PATCH 21/26] usb: otg: mv_otg: add extern chip support
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
It does the similar things as what we do for udc driver.
Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
drivers/usb/otg/mv_otg.c | 63 ++++++++++++++++++++-------------------------
drivers/usb/otg/mv_otg.h | 3 ++
2 files changed, 31 insertions(+), 35 deletions(-)
diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c
index cc45ef2..379df92 100644
--- a/drivers/usb/otg/mv_otg.c
+++ b/drivers/usb/otg/mv_otg.c
@@ -59,10 +59,10 @@ static char *state_string[] = {
static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
{
struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy);
- if (mvotg->pdata->set_vbus == NULL)
+ if (!mv_usb2_has_extern_call(mvotg->mvphy, vbus, set_vbus))
return -ENODEV;
- return mvotg->pdata->set_vbus(on);
+ return mv_usb2_extern_call(mvotg->mvphy, vbus, set_vbus, on);
}
static int mv_otg_set_host(struct usb_otg *otg,
@@ -184,14 +184,14 @@ static void mv_otg_init_irq(struct mv_otg *mvotg)
mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
| OTGSC_INTSTS_A_VBUS_VALID;
- if (mvotg->pdata->vbus == NULL) {
+ if (!(mvotg->extern_attr & MV_USB_HAS_VBUS_DETECTION)) {
mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
| OTGSC_INTR_B_SESSION_END;
mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
| OTGSC_INTSTS_B_SESSION_END;
}
- if (mvotg->pdata->id == NULL) {
+ if (!(mvotg->extern_attr & MV_USB_HAS_IDPIN_DETECTION)) {
mvotg->irq_en |= OTGSC_INTR_USB_ID;
mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
}
@@ -306,11 +306,14 @@ static void mv_otg_update_inputs(struct mv_otg *mvotg)
{
struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
u32 otgsc;
+ unsigned int vbus, idpin;
otgsc = readl(&mvotg->op_regs->otgsc);
- if (mvotg->pdata->vbus) {
- if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
+ if (mvotg->extern_attr & MV_USB_HAS_VBUS_DETECTION) {
+ if (mv_usb2_extern_call(mvotg->mvphy, vbus, get_vbus, &vbus))
+ return;
+ if (vbus == VBUS_HIGH) {
otg_ctrl->b_sess_vld = 1;
otg_ctrl->b_sess_end = 0;
} else {
@@ -322,8 +325,11 @@ static void mv_otg_update_inputs(struct mv_otg *mvotg)
otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
}
- if (mvotg->pdata->id)
- otg_ctrl->id = !!mvotg->pdata->id->poll();
+ if (mvotg->extern_attr & MV_USB_HAS_IDPIN_DETECTION) {
+ if (mv_usb2_extern_call(mvotg->mvphy, idpin, get_idpin, &idpin))
+ return;
+ otg_ctrl->id = !!idpin;
+ }
else
otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
@@ -505,7 +511,7 @@ static irqreturn_t mv_otg_irq(int irq, void *dev)
* if we have vbus, then the vbus detection for B-device
* will be done by mv_otg_inputs_irq().
*/
- if (mvotg->pdata->vbus)
+ if (mvotg->extern_attr & MV_USB_HAS_VBUS_DETECTION)
if ((otgsc & OTGSC_STS_USB_ID) &&
!(otgsc & OTGSC_INTSTS_USB_ID))
return IRQ_NONE;
@@ -518,9 +524,10 @@ static irqreturn_t mv_otg_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
+static int mv_otg_notifier_call(struct notifier_block *nb,
+ unsigned long val, void *v)
{
- struct mv_otg *mvotg = dev;
+ struct mv_otg *mvotg = container_of(nb, struct mv_otg, notifier);
/* The clock may disabled at this time */
if (!mvotg->active) {
@@ -530,7 +537,7 @@ static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
mv_otg_run_state_machine(mvotg, 0);
- return IRQ_HANDLED;
+ return 0;
}
static ssize_t
@@ -666,6 +673,10 @@ int mv_otg_remove(struct platform_device *pdev)
sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group);
+ if ((mvotg->extern_attr & MV_USB_HAS_VBUS_DETECTION) ||
+ (mvotg->extern_attr & MV_USB_HAS_IDPIN_DETECTION))
+ mv_usb2_unregister_notifier(mvotg->mvphy, &mvotg->notifier);
+
if (mvotg->qwork) {
flush_workqueue(mvotg->qwork);
destroy_workqueue(mvotg->qwork);
@@ -707,6 +718,7 @@ static int mv_otg_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mvotg);
mvotg->pdev = pdev;
+ mvotg->extern_attr = pdata->extern_attr;
mvotg->pdata = pdata;
mvotg->clknum = pdata->clknum;
@@ -773,29 +785,10 @@ static int mv_otg_probe(struct platform_device *pdev)
(struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
+ (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
- if (pdata->id) {
- retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq,
- NULL, mv_otg_inputs_irq,
- IRQF_ONESHOT, "id", mvotg);
- if (retval) {
- dev_info(&pdev->dev,
- "Failed to request irq for ID\n");
- pdata->id = NULL;
- }
- }
-
- if (pdata->vbus) {
- mvotg->clock_gating = 1;
- retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq,
- NULL, mv_otg_inputs_irq,
- IRQF_ONESHOT, "vbus", mvotg);
- if (retval) {
- dev_info(&pdev->dev,
- "Failed to request irq for VBUS, "
- "disable clock gating\n");
- mvotg->clock_gating = 0;
- pdata->vbus = NULL;
- }
+ if ((mvotg->extern_attr & MV_USB_HAS_VBUS_DETECTION) ||
+ (mvotg->extern_attr & MV_USB_HAS_IDPIN_DETECTION)) {
+ mvotg->notifier.notifier_call = mv_otg_notifier_call;
+ mv_usb2_register_notifier(mvotg->mvphy, &mvotg->notifier);
}
if (pdata->disable_otg_clock_gating)
diff --git a/drivers/usb/otg/mv_otg.h b/drivers/usb/otg/mv_otg.h
index 7b9629a..f5bc7dd 100644
--- a/drivers/usb/otg/mv_otg.h
+++ b/drivers/usb/otg/mv_otg.h
@@ -149,6 +149,9 @@ struct mv_otg {
u32 irq_status;
u32 irq_en;
+ unsigned int extern_attr;
+ struct notifier_block notifier;
+
struct delayed_work work;
struct workqueue_struct *qwork;
--
1.7.4.1
^ permalink raw reply related
* [V4 PATCH 22/26] arm: mmp: add extern chip support for brownstone
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
Change the board support for usb as extern chip is supported
in marvell usb PHY driver
Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
arch/arm/mach-mmp/brownstone.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-mmp/brownstone.c b/arch/arm/mach-mmp/brownstone.c
index 90c0340..3dcf38f 100644
--- a/arch/arm/mach-mmp/brownstone.c
+++ b/arch/arm/mach-mmp/brownstone.c
@@ -212,10 +212,8 @@ static struct mv_usb_phy_platform_data brownstone_usb_phy_pdata = {
static struct mv_usb_platform_data brownstone_usb_pdata = {
.clknum = 1,
.clkname = mmp2_usb_clock_name,
- .vbus = NULL,
.mode = MV_USB_MODE_OTG,
.otg_force_a_bus_req = 1,
- .set_vbus = NULL,
};
#endif
#endif
--
1.7.4.1
^ permalink raw reply related
* [V4 PATCH 23/26] arm: mmp: add extern chip support for ttc_dkb
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
Change the board support for usb as extern chip is supported
in marvell usb PHY driver.
Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
arch/arm/mach-mmp/ttc_dkb.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c
index 0962791..eb34608 100644
--- a/arch/arm/mach-mmp/ttc_dkb.c
+++ b/arch/arm/mach-mmp/ttc_dkb.c
@@ -172,10 +172,8 @@ static struct mv_usb_phy_platform_data ttc_usb_phy_pdata = {
static struct mv_usb_platform_data ttc_usb_pdata = {
.clknum = 1,
.clkname = pxa910_usb_clock_name,
- .vbus = NULL,
.mode = MV_USB_MODE_OTG,
.otg_force_a_bus_req = 1,
- .set_vbus = NULL,
};
#endif
#endif
--
1.7.4.1
^ permalink raw reply related
* [V4 PATCH 24/26] usb: gadget: mv_udc: add device tree support
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
In original driver, we have callbacks in platform data, and some
dynamically allocations variables in platform data.
Now, these blocks are removed, the device tree support is easier
now.
Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
drivers/usb/gadget/mv_udc.h | 5 +-
drivers/usb/gadget/mv_udc_core.c | 106 ++++++++++++++++++++++++++++++--------
2 files changed, 86 insertions(+), 25 deletions(-)
diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h
index 50ae7c7..de84722 100644
--- a/drivers/usb/gadget/mv_udc.h
+++ b/drivers/usb/gadget/mv_udc.h
@@ -179,6 +179,7 @@ struct mv_udc {
int irq;
unsigned int extern_attr;
+ unsigned int mode;
struct notifier_block notifier;
struct mv_cap_regs __iomem *cap_regs;
@@ -222,11 +223,9 @@ struct mv_udc {
struct mv_usb2_phy *phy;
struct usb_phy *transceiver;
- struct mv_usb_platform_data *pdata;
-
/* some SOC has mutiple clock sources for USB*/
unsigned int clknum;
- struct clk *clk[0];
+ struct clk **clk;
};
/* endpoint data structure */
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index 4b50334..42eba77 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -34,6 +34,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/of.h>
#include <linux/platform_data/mv_usb.h>
#include <linux/usb/mv_usb2.h>
#include <asm/unaligned.h>
@@ -2153,21 +2154,57 @@ static int mv_udc_remove(struct platform_device *pdev)
return 0;
}
+static int mv_udc_parse_dt(struct platform_device *pdev,
+ struct mv_udc *udc)
+{
+ struct device_node *np = pdev->dev.of_node;
+ unsigned int clks_num;
+ int i, ret;
+ const char *clk_name;
+
+ if (!np)
+ return 1;
+
+ clks_num = of_property_count_strings(np, "clocks");
+ if (clks_num < 0)
+ return clks_num;
+
+ udc->clk = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * clks_num, GFP_KERNEL);
+ if (udc->clk == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < clks_num; i++) {
+ ret = of_property_read_string_index(np, "clocks", i,
+ &clk_name);
+ if (ret)
+ return ret;
+ udc->clk[i] = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(udc->clk[i]))
+ return PTR_ERR(udc->clk[i]);
+ }
+
+ udc->clknum = clks_num;
+
+ ret = of_property_read_u32(np, "extern_attr", &udc->extern_attr);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "mode", &udc->mode);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int mv_udc_probe(struct platform_device *pdev)
{
- struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
struct mv_udc *udc;
int retval = 0;
- int clk_i = 0;
struct resource *r;
size_t size;
- if (pdata == NULL) {
- dev_err(&pdev->dev, "missing platform_data\n");
- return -ENODEV;
- }
-
- size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum;
+ size = sizeof(*udc);
udc = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (udc == NULL) {
dev_err(&pdev->dev, "failed to allocate memory for udc\n");
@@ -2175,14 +2212,43 @@ static int mv_udc_probe(struct platform_device *pdev)
}
udc->done = &release_done;
- udc->extern_attr = pdata->extern_attr;
- udc->pdata = pdev->dev.platform_data;
spin_lock_init(&udc->lock);
udc->dev = pdev;
+ retval = mv_udc_parse_dt(pdev, udc);
+ if (retval > 0) {
+ /* no CONFIG_OF */
+ struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+ int clk_i = 0;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+ udc->extern_attr = pdata->extern_attr;
+ udc->mode = pdata->mode;
+ udc->clknum = pdata->clknum;
+
+ size = sizeof(struct clk *) * udc->clknum;
+ udc->clk = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (udc->clk == NULL)
+ return -ENOMEM;
+ for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
+ udc->clk[clk_i] = devm_clk_get(&pdev->dev,
+ pdata->clkname[clk_i]);
+ if (IS_ERR(udc->clk[clk_i])) {
+ retval = PTR_ERR(udc->clk[clk_i]);
+ return retval;
+ }
+ }
+ } else if (retval < 0) {
+ dev_err(&pdev->dev, "error parse dt\n");
+ return retval;
+ }
+
#ifdef CONFIG_USB_OTG_UTILS
- if (pdata->mode == MV_USB_MODE_OTG) {
+ if (udc->mode == MV_USB_MODE_OTG) {
udc->transceiver = devm_usb_get_phy(&pdev->dev,
USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(udc->transceiver)) {
@@ -2191,17 +2257,6 @@ static int mv_udc_probe(struct platform_device *pdev)
}
}
#endif
-
- udc->clknum = pdata->clknum;
- for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
- udc->clk[clk_i] = devm_clk_get(&pdev->dev,
- pdata->clkname[clk_i]);
- if (IS_ERR(udc->clk[clk_i])) {
- retval = PTR_ERR(udc->clk[clk_i]);
- return retval;
- }
- }
-
r = platform_get_resource(udc->dev, IORESOURCE_MEM, 0);
if (r == NULL) {
dev_err(&pdev->dev, "no I/O memory resource defined\n");
@@ -2466,6 +2521,12 @@ static void mv_udc_shutdown(struct platform_device *pdev)
mv_udc_disable(udc);
}
+static struct of_device_id mv_udc_dt_ids[] = {
+ { .compatible = "mrvl,mv-udc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mv_udc_dt_ids);
+
static struct platform_driver udc_driver = {
.probe = mv_udc_probe,
.remove = mv_udc_remove,
@@ -2476,6 +2537,7 @@ static struct platform_driver udc_driver = {
#ifdef CONFIG_PM
.pm = &mv_udc_pm_ops,
#endif
+ .of_match_table = mv_udc_dt_ids,
},
};
--
1.7.4.1
^ permalink raw reply related
* [V4 PATCH 25/26] usb: otg: mv_otg: add device tree support
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
All blocks are removed. Add the device tree support for otg.
Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
drivers/usb/otg/mv_otg.c | 128 +++++++++++++++++++++++++++++++++++++--------
drivers/usb/otg/mv_otg.h | 6 +-
2 files changed, 108 insertions(+), 26 deletions(-)
diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c
index 379df92..3aa7fdc 100644
--- a/drivers/usb/otg/mv_otg.c
+++ b/drivers/usb/otg/mv_otg.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
+#include <linux/of.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
@@ -333,7 +334,7 @@ static void mv_otg_update_inputs(struct mv_otg *mvotg)
else
otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
- if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
+ if (mvotg->otg_force_a_bus_req && !otg_ctrl->id)
otg_ctrl->a_bus_req = 1;
otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
@@ -690,21 +691,69 @@ int mv_otg_remove(struct platform_device *pdev)
return 0;
}
+static int mv_otg_parse_dt(struct platform_device *pdev,
+ struct mv_otg *mvotg)
+{
+ struct device_node *np = pdev->dev.of_node;
+ unsigned int clks_num;
+ unsigned int val;
+ int i, ret;
+ const char *clk_name;
+
+ if (!np)
+ return 1;
+
+ clks_num = of_property_count_strings(np, "clocks");
+ if (clks_num < 0)
+ return clks_num;
+
+ mvotg->clk = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * clks_num, GFP_KERNEL);
+ if (mvotg->clk == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < clks_num; i++) {
+ ret = of_property_read_string_index(np, "clocks", i,
+ &clk_name);
+ if (ret)
+ return ret;
+ mvotg->clk[i] = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(mvotg->clk[i]))
+ return PTR_ERR(mvotg->clk[i]);
+ }
+
+ mvotg->clknum = clks_num;
+
+ ret = of_property_read_u32(np, "extern_attr", &mvotg->extern_attr);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "mode", &mvotg->mode);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "force_a_bus_req", &val);
+ if (ret)
+ return ret;
+ mvotg->otg_force_a_bus_req = !!val;
+
+ ret = of_property_read_u32(np, "disable_clock_gating", &val);
+ if (ret)
+ return ret;
+ mvotg->clock_gating = !val;
+
+ return 0;
+}
+
static int mv_otg_probe(struct platform_device *pdev)
{
- struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
struct mv_otg *mvotg;
struct usb_otg *otg;
struct resource *r;
- int retval = 0, clk_i, i;
+ int retval = 0, i;
size_t size;
- if (pdata == NULL) {
- dev_err(&pdev->dev, "failed to get platform data\n");
- return -ENODEV;
- }
-
- size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum;
+ size = sizeof(*mvotg);
mvotg = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (!mvotg) {
dev_err(&pdev->dev, "failed to allocate memory!\n");
@@ -718,17 +767,45 @@ static int mv_otg_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mvotg);
mvotg->pdev = pdev;
- mvotg->extern_attr = pdata->extern_attr;
- mvotg->pdata = pdata;
- mvotg->clknum = pdata->clknum;
- for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) {
- mvotg->clk[clk_i] = devm_clk_get(&pdev->dev,
+ retval = mv_otg_parse_dt(pdev, mvotg);
+ if (retval > 0) {
+ struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+ /* no CONFIG_OF */
+ int clk_i = 0;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+ mvotg->extern_attr = pdata->extern_attr;
+ mvotg->mode = pdata->mode;
+ mvotg->clknum = pdata->clknum;
+ mvotg->otg_force_a_bus_req = pdata->otg_force_a_bus_req;
+ if (pdata->disable_otg_clock_gating)
+ mvotg->clock_gating = 0;
+
+ size = sizeof(struct clk *) * mvotg->clknum;
+ mvotg->clk = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (mvotg->clk == NULL) {
+ dev_err(&pdev->dev,
+ "failed to allocate memory for clk\n");
+ return -ENOMEM;
+ }
+
+ for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) {
+ mvotg->clk[clk_i] = devm_clk_get(&pdev->dev,
pdata->clkname[clk_i]);
- if (IS_ERR(mvotg->clk[clk_i])) {
- retval = PTR_ERR(mvotg->clk[clk_i]);
- return retval;
+ if (IS_ERR(mvotg->clk[clk_i])) {
+ dev_err(&pdev->dev, "failed to get clk %s\n",
+ pdata->clkname[clk_i]);
+ retval = PTR_ERR(mvotg->clk[clk_i]);
+ return retval;
+ }
}
+ } else if (retval < 0) {
+ dev_err(&pdev->dev, "error parse dt\n");
+ return retval;
}
mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
@@ -770,6 +847,7 @@ static int mv_otg_probe(struct platform_device *pdev)
}
mvotg->mvphy = mv_usb2_get_phy();
if (mvotg->mvphy == NULL) {
+ dev_err(&pdev->dev, "failed to get usb phy\n");
retval = -ENODEV;
goto err_destroy_workqueue;
}
@@ -791,9 +869,6 @@ static int mv_otg_probe(struct platform_device *pdev)
mv_usb2_register_notifier(mvotg->mvphy, &mvotg->notifier);
}
- if (pdata->disable_otg_clock_gating)
- mvotg->clock_gating = 0;
-
mv_otg_reset(mvotg);
mv_otg_init_irq(mvotg);
@@ -892,13 +967,20 @@ static int mv_otg_resume(struct platform_device *pdev)
}
#endif
+static struct of_device_id mv_otg_dt_ids[] = {
+ { .compatible = "mrvl,mv-otg" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mv_otg_dt_ids);
+
static struct platform_driver mv_otg_driver = {
.probe = mv_otg_probe,
.remove = __exit_p(mv_otg_remove),
.driver = {
- .owner = THIS_MODULE,
- .name = driver_name,
- },
+ .owner = THIS_MODULE,
+ .name = driver_name,
+ .of_match_table = mv_otg_dt_ids,
+ },
#ifdef CONFIG_PM
.suspend = mv_otg_suspend,
.resume = mv_otg_resume,
diff --git a/drivers/usb/otg/mv_otg.h b/drivers/usb/otg/mv_otg.h
index f5bc7dd..3b9356d 100644
--- a/drivers/usb/otg/mv_otg.h
+++ b/drivers/usb/otg/mv_otg.h
@@ -157,12 +157,12 @@ struct mv_otg {
spinlock_t wq_lock;
- struct mv_usb_platform_data *pdata;
-
unsigned int active;
unsigned int clock_gating;
+ unsigned int mode;
+ unsigned int otg_force_a_bus_req;
unsigned int clknum;
- struct clk *clk[0];
+ struct clk **clk;
};
#endif
--
1.7.4.1
^ permalink raw reply related
* [V4 PATCH 26/26] usb: ehci: ehci-mv: add device tree support
From: Chao Xie @ 2013-01-21 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762864-9249-1-git-send-email-chao.xie@marvell.com>
All blocks are removed. Add the device tree support for ehci.
Signed-off-by: Chao Xie <chao.xie@marvell.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
---
drivers/usb/host/ehci-mv.c | 105 +++++++++++++++++++++++++++++++++----------
1 files changed, 80 insertions(+), 25 deletions(-)
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index 171e145..79eca0c 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/of.h>
#include <linux/err.h>
#include <linux/usb/otg.h>
#include <linux/usb/mv_usb2.h>
@@ -32,11 +33,9 @@ struct ehci_hcd_mv {
struct usb_phy *otg;
- struct mv_usb_platform_data *pdata;
-
/* clock source and total clock number */
unsigned int clknum;
- struct clk *clk[0];
+ struct clk **clk;
};
static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
@@ -138,22 +137,55 @@ static const struct hc_driver mv_ehci_hc_driver = {
.bus_resume = ehci_bus_resume,
};
+static int mv_ehci_parse_dt(struct platform_device *pdev,
+ struct ehci_hcd_mv *ehci_mv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ unsigned int clks_num;
+ int i, ret;
+ const char *clk_name;
+
+ if (!np)
+ return 1;
+
+ clks_num = of_property_count_strings(np, "clocks");
+ if (clks_num < 0)
+ return clks_num;
+
+ ehci_mv->clk = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * clks_num, GFP_KERNEL);
+ if (ehci_mv->clk == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < clks_num; i++) {
+ ret = of_property_read_string_index(np, "clocks", i,
+ &clk_name);
+ if (ret)
+ return ret;
+ ehci_mv->clk[i] = clk_get(NULL, clk_name);
+ if (IS_ERR(ehci_mv->clk[i]))
+ return PTR_ERR(ehci_mv->clk[i]);
+ }
+
+ ehci_mv->clknum = clks_num;
+
+ ret = of_property_read_u32(np, "mode", &ehci_mv->mode);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int mv_ehci_probe(struct platform_device *pdev)
{
- struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct ehci_hcd_mv *ehci_mv;
struct resource *r;
- int clk_i, retval = -ENODEV;
+ int retval = -ENODEV;
u32 offset;
size_t size;
- if (!pdata) {
- dev_err(&pdev->dev, "missing platform_data\n");
- return -ENODEV;
- }
-
if (usb_disabled())
return -ENODEV;
@@ -161,7 +193,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
if (!hcd)
return -ENOMEM;
- size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum;
+ size = sizeof(*ehci_mv);
ehci_mv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (ehci_mv == NULL) {
dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n");
@@ -170,19 +202,36 @@ static int mv_ehci_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, ehci_mv);
- ehci_mv->pdata = pdata;
ehci_mv->hcd = hcd;
- ehci_mv->clknum = pdata->clknum;
- for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
- ehci_mv->clk[clk_i] =
- devm_clk_get(&pdev->dev, pdata->clkname[clk_i]);
- if (IS_ERR(ehci_mv->clk[clk_i])) {
- dev_err(&pdev->dev, "error get clck \"%s\"\n",
- pdata->clkname[clk_i]);
- retval = PTR_ERR(ehci_mv->clk[clk_i]);
- goto err_clear_drvdata;
+ retval = mv_ehci_parse_dt(pdev, ehci_mv);
+ if (retval > 0) {
+ struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+ int clk_i = 0;
+
+ /* no CONFIG_OF */
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+ ehci_mv->mode = pdata->mode;
+ ehci_mv->clknum = pdata->clknum;
+
+ size = sizeof(struct clk *) * ehci_mv->clknum;
+ ehci_mv->clk = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (ehci_mv->clk == NULL)
+ return -ENOMEM;
+ for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
+ ehci_mv->clk[clk_i] = devm_clk_get(&pdev->dev,
+ pdata->clkname[clk_i]);
+ if (IS_ERR(ehci_mv->clk[clk_i])) {
+ retval = PTR_ERR(ehci_mv->clk[clk_i]);
+ return retval;
+ }
}
+ } else if (retval < 0) {
+ dev_err(&pdev->dev, "error parse dt\n");
+ return retval;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -230,7 +279,6 @@ static int mv_ehci_probe(struct platform_device *pdev)
ehci = hcd_to_ehci(hcd);
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
- ehci_mv->mode = pdata->mode;
if (ehci_mv->mode == MV_USB_MODE_OTG) {
#ifdef CONFIG_USB_OTG_UTILS
ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
@@ -334,13 +382,20 @@ static void mv_ehci_shutdown(struct platform_device *pdev)
hcd->driver->shutdown(hcd);
}
+static struct of_device_id mv_ehci_dt_ids[] = {
+ { .compatible = "mrvl,mv-ehci" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mv_ehci_dt_ids);
+
static struct platform_driver ehci_mv_driver = {
.probe = mv_ehci_probe,
.remove = mv_ehci_remove,
.shutdown = mv_ehci_shutdown,
.driver = {
- .name = "mv-ehci",
- .bus = &platform_bus_type,
- },
+ .name = "mv-ehci",
+ .bus = &platform_bus_type,
+ .of_match_table = mv_ehci_dt_ids,
+ },
.id_table = ehci_id_table,
};
--
1.7.4.1
^ permalink raw reply related
* [PATCH 02/33] ARM: Convert to devm_ioremap_resource()
From: Thierry Reding @ 2013-01-21 10:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358762966-20791-1-git-send-email-thierry.reding@avionic-design.de>
Convert all uses of devm_request_and_ioremap() to the newly introduced
devm_ioremap_resource() which provides more consistent error handling.
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel at lists.infradead.org
---
arch/arm/mach-omap2/gpmc.c | 8 +++-----
arch/arm/mach-tegra/tegra2_emc.c | 8 +++-----
arch/arm/plat-omap/dmtimer.c | 8 +++-----
arch/arm/plat-samsung/adc.c | 8 +++-----
4 files changed, 12 insertions(+), 20 deletions(-)
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 8033cb7..64bac53 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -1134,11 +1134,9 @@ static int gpmc_probe(struct platform_device *pdev)
phys_base = res->start;
mem_size = resource_size(res);
- gpmc_base = devm_request_and_ioremap(&pdev->dev, res);
- if (!gpmc_base) {
- dev_err(&pdev->dev, "error: request memory / ioremap\n");
- return -EADDRNOTAVAIL;
- }
+ gpmc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpmc_base))
+ return PTR_ERR(gpmc_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL)
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
index e18aa2f..ce7ce42 100644
--- a/arch/arm/mach-tegra/tegra2_emc.c
+++ b/arch/arm/mach-tegra/tegra2_emc.c
@@ -312,11 +312,9 @@ static int tegra_emc_probe(struct platform_device *pdev)
return -ENOMEM;
}
- emc_regbase = devm_request_and_ioremap(&pdev->dev, res);
- if (!emc_regbase) {
- dev_err(&pdev->dev, "failed to remap registers\n");
- return -ENOMEM;
- }
+ emc_regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(emc_regbase))
+ return PTR_ERR(emc_regbase);
pdata = pdev->dev.platform_data;
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 7b433f3..a0daa2f 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -808,11 +808,9 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
return -ENOMEM;
}
- timer->io_base = devm_request_and_ioremap(dev, mem);
- if (!timer->io_base) {
- dev_err(dev, "%s: region already claimed.\n", __func__);
- return -ENOMEM;
- }
+ timer->io_base = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(timer->io_base))
+ return PTR_ERR(timer->io_base);
if (dev->of_node) {
if (of_find_property(dev->of_node, "ti,timer-alwon", NULL))
diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c
index 2d676ab..ca07cb1 100644
--- a/arch/arm/plat-samsung/adc.c
+++ b/arch/arm/plat-samsung/adc.c
@@ -386,11 +386,9 @@ static int s3c_adc_probe(struct platform_device *pdev)
return -ENXIO;
}
- adc->regs = devm_request_and_ioremap(dev, regs);
- if (!adc->regs) {
- dev_err(dev, "failed to map registers\n");
- return -ENXIO;
- }
+ adc->regs = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(adc->regs))
+ return PTR_ERR(adc->regs);
ret = regulator_enable(adc->vdd);
if (ret)
--
1.8.1.1
^ permalink raw reply related
* [PATCH v2 8/9] ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures
From: Barry Song @ 2013-01-21 10:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130121094855.GE15707@e106331-lin.cambridge.arm.com>
Hi Mark,
2013/1/21 Mark Rutland <mark.rutland@arm.com>:
>> >> +
>> >> +static void __init sirfsoc_smp_init_cpus(void)
>> >> +{
>> >> + int i, ncores;
>> >> +
>> >> + ncores = scu_get_core_count(scu_base);
>> >> +
>> >> + for (i = 0; i < ncores; i++)
>> >> + set_cpu_possible(i, true);
>> >> +
>> >> + set_smp_cross_call(gic_raise_softirq);
>> >> +}
>> >
>> > You don't need to use scu_get_core_count to figure out which cpus to set
>> > possible. It duplicates work already done by arm_dt_init_cpu_maps, and doesn't
>> > initialise the logical map (as arm_dt_init_cpu_maps does).
>> >
>> > You're already relying on the arm_dt_init_cpu_maps to set up the logical map
>> > for the holding pen release, so you may as well rely on it to set_cpu_possible
>> > for each of the cpus described in the dt.
>> >
>> > Tegra is already on its way to doing this:
>> >
>> > http://lists.infradead.org/pipermail/linux-arm-kernel/2012-December/138319.html
>> > http://lists.infradead.org/pipermail/linux-arm-kernel/2013-January/140219.html
>> > http://lists.infradead.org/pipermail/linux-arm-kernel/2013-January/141596.html
>> >
>> what if there are 4 nodes in dts but there are actually only one core
>> in soc? for example, using qemu to run vexpress without --smp=4?
>> SiRFmarco will have 2 versions, one is UP, the other one is dual core.
>> if using dt, i might need to take cpus node out of marco.dtsi
>
> If the dt doesn't match the hardware, then the dt is wrong. It's probably worth
> having a separate dtsi for the UP and SMP versions (with all the core stuff in
> another shared dsti).
ok.
i think we might have another two dtsi marco-up.dtsi and marco-dp.dtsi
with only cpus node in it as other devices will be same.
>> >> +static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce)
>> >> +{
>> >> + /* Use existing clock_event for cpu 0 */
>> >> + if (!smp_processor_id())
>> >> + return 0;
>> >
>> > This seems a little scary. Does the timer only exist for 1 core, or is it not
>> > actually a local timer?
>>
>> there are multiple timers, everyone can be avaliable to all cores.
>> here we actually use timer0 as cpu0's tick, timer1 as cpu1's tick.
>> each one uses a seperate timers.
>
> Ok.
>
> From a glance over the code, it looks like the timer0 and timer1 are pretty
> much identical. Is there no way to have the logic unified, figuring out whether
> to use SIRFSOC_TIMER_*_{0,1} automatically?
there always be some ways. one is we take some private data in
clock_event_device and compare the data in related callbacks, but
unfortunately, there isn't the field for the moment.
taking .name to strcmp() is too slow.
then we might take smp_processor_id to select TIMER0/1 automatically
if timer0 is always set by CPU0 and timer1 is always set by CPU1.
>
>>
>> >
>> >> +
>> >> + ce->irq = sirfsoc_timer1_irq.irq;
>> >> + ce->name = "local_timer";
>> >> + ce->features = sirfsoc_clockevent.features;
>> >> + ce->rating = sirfsoc_clockevent.rating;
>> >> + ce->cpumask = cpumask_of(1);
>> >
>> > The local_timer api sets the cpumask, and you've already rejected setups from
>> > cpu0, so this isn't technically necessary.
>>
>> right.
>> >
>> >> + ce->set_mode = sirfsoc_timer1_set_mode;
>> >> + ce->set_next_event = sirfsoc_timer1_set_next_event;
>> >> + ce->shift = sirfsoc_clockevent.shift;
>> >> + ce->mult = sirfsoc_clockevent.mult;
>> >> + ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
>> >> + ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
>> >> +
>> >> + sirfsoc_timer1_irq.dev_id = ce;
>> >> + BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
>> >> + irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
>> >> +
>> >> + clockevents_register_device(ce);
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
>> >> +{
>> >> + sirfsoc_timer_count_disable(1);
>> >> +
>> >> + remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
>> >> +}
>> >
>> > Presumably you need to balance what you've done in sirfsoc_local_timer_setup,
>> > and return early for cpu0.
>
> If you don't unify the code for the two timers, I really think you should have
> an early return here for cpu0.
adding that will make the codes look consitent with setup. but i
suspect whether cpu0 has any chance to stop the local timer of cpu1.
it seems clock_event_device is placed in a percpu_clockevent, every
cpu is getting its own clock_event_device and calling the callbacks.
anyway, i will have more look and give a test.
>
>> >
>> >> +
>> >> +static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = {
>> >> + .setup = sirfsoc_local_timer_setup,
>> >> + .stop = sirfsoc_local_timer_stop,
>> >> +};
>> >> +#endif /* CONFIG_LOCAL_TIMERS */
>> >> +
>> >> +static void __init sirfsoc_clockevent_init(void)
>> >> +{
>> >> + clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
>> >> +
>> >> + sirfsoc_clockevent.max_delta_ns =
>> >> + clockevent_delta2ns(-2, &sirfsoc_clockevent);
>> >
>> > I assume this is a typo. For one thing, clockevent_delta2ns takes an unsigned
>> > long.
>>
>> grep and get:
>> http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git&a=search&h=HEAD&st=grep&s=clockevent_delta2ns
>> almost all platforms don't really take it seriously if we think -2 is
>> very big unsigned long and number like 0xfffffffe is almost not
>> unsigned long without UL.
>
> That's a bit opaque, but as you say, everyone's doing it (or something
> equivalent).
>
>
> Thanks,
> Mark.
-barry
^ permalink raw reply
* [PATCH] ARM: plat-samsung: using vsnprintf instead of vsprintf for the limit buffer length 256
From: Chen Gang @ 2013-01-21 10:25 UTC (permalink / raw)
To: linux-arm-kernel
the buff size is 256, so need use vsnprintf instead of vsprintf.
Signed-off-by: Chen Gang <gang.chen@asianux.com>
---
arch/arm/plat-samsung/pm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c
index 1507028..8d07b45 100644
--- a/arch/arm/plat-samsung/pm.c
+++ b/arch/arm/plat-samsung/pm.c
@@ -51,7 +51,7 @@ void s3c_pm_dbg(const char *fmt, ...)
char buff[256];
va_start(va, fmt);
- vsprintf(buff, fmt, va);
+ vsnprintf(buff, 256, fmt, va);
va_end(va);
printascii(buff);
--
1.7.10.4
^ permalink raw reply related
* [PATCH v5 9/9] ARM: davinci: da850: Added dsp clock definition
From: Sekhar Nori @ 2013-01-21 10:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357863807-380-10-git-send-email-rtivy@ti.com>
On 1/11/2013 5:53 AM, Robert Tivy wrote:
> Added dsp clock definition, keyed to "davinci-rproc.0".
>
> Signed-off-by: Robert Tivy <rtivy@ti.com>
I think this can be done along with 4/9 so I committed it by merging it
with 4/9
Thanks,
Sekhar
^ permalink raw reply
* [RFC v2 16/18] ARM: OMAP2+: AM33XX: Basic suspend resume support
From: Bedia, Vaibhav @ 2013-01-21 10:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87ip6vlwaf.fsf@dell.be.48ers.dk>
Hi Peter,
On Thu, Jan 17, 2013 at 19:57:20, Peter Korsgaard wrote:
> >>>>> "V" == Vaibhav Bedia <vaibhav.bedia@ti.com> writes:
>
> Hi,
>
> V> +static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context)
> V> +{
> V> + struct wkup_m3_context *wkup_m3_context = context;
> V> + struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
> V> + int ret = 0;
> V> +
> V> + /* no firmware found */
> V> + if (!fw) {
> V> + dev_err(wkup_m3_context->dev, "request_firmware failed\n");
> V> + goto err;
> V> + }
> V> +
> V> + memcpy((void *)wkup_m3_context->code, fw->data, fw->size);
>
> A size check would be good. I don't know much about the finer details
> about the m3 and how it is connected, but don't you need to ensure data
> is flushed before resetting the m3?
>
Will add the reg property in the next version. The wkup-m3 memory is coming via
ioremap() so AFAIK it should be ok. I realized that I do need to replace the memcpy()
with memcpy_toio() here.
Thanks,
Vaibhav
^ permalink raw reply
* [PATCH 0/9] ARM: OMAP2+: AM33XX: Misc fixes/updates
From: Bedia, Vaibhav @ 2013-01-21 10:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87zk02aox3.fsf@dell.be.48ers.dk>
Hi Peter,
On Mon, Jan 21, 2013 at 14:33:20, Peter Korsgaard wrote:
> >>>>> "V" == Bedia, Vaibhav <vaibhav.bedia@ti.com> writes:
>
> Hi,
>
> >> Vaibhav Bedia (9):
> >> ARM: OMAP2+: AM33XX: CM: Get rid of unncessary header inclusions
> >> ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files
> >> ARM: OMAP2+: AM33XX: hwmod: Register OCMC RAM hwmod
> >> ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags
> >> ARM: OMAP2+: AM33XX: hwmod: Fixup cpgmac0 hwmod entry
> >> ARM: OMAP2+: AM33XX: hwmod: Update the WKUP-M3 hwmod with reset
> >> status bit
> >> ARM: OMAP2+: AM33XX: Update the hardreset API
> >> ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3
> >> ARM: OMAP2+: AM33XX: control: Add some control module registers and
> >> APIs
> >>
>
> V> Any comments on these before I resend addressing the comments from Sergei?
>
> Wouldn't the ocmram/wkup dt nodes need a reg property to define their
> size as I indirectly mentioned here:
>
> http://thread.gmane.org/gmane.linux.ports.arm.omap/91405/focus=92061
>
My apologies. I missed your comment on that patch and just responded to it.
Will add the reg property for the DT nodes in the next version.
Regards,
Vaibhav
^ permalink raw reply
* [PATCH] ARM: dts: add mshc controller node for Exynos4x12 SoCs
From: Thomas Abraham @ 2013-01-21 10:39 UTC (permalink / raw)
To: linux-arm-kernel
Commit cea0f256 ("ARM: dts: Add board dts file for ODROID-X") includes a node
to describe the board level properties for mshc controller. But the mshc
controller node was not added in the Exynos4x12 dtsi file which resulted
in the following warning when compiling the dtb files.
Warning (reg_format): "reg" property in /mshc at 12550000/slot at 0 has invalid length (4 bytes) (#address-cells == 2, #size-cells == 1)
Warning (avoid_default_addr_size): Relying on default #address-cells value for /mshc at 12550000/slot at 0
Warning (avoid_default_addr_size): Relying on default #size-cells value for /mshc at 12550000/slot at 0
Fix this by adding the mshc controller node for Exynos4x12 SoCs.
Cc: Dongjin Kim <tobetter@gmail.com>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
arch/arm/boot/dts/exynos4412.dtsi | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 78ed377..96f5b66 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -32,4 +32,12 @@
interrupts = <0 57 0>, <0 0 0>, <0 0 0>, <0 0 0>,
<1 12 0>, <1 12 0>, <1 12 0>, <1 12 0>;
};
+
+ mshc at 12550000 {
+ compatible = "samsung,exynos4412-dw-mshc";
+ reg = <0x12550000 0x1000>;
+ interrupts = <0 77 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
};
--
1.7.5.4
^ permalink raw reply related
* [PATCH v6 0/4] Renesas IPMMU driver for sh7372, sh73a0 and r8a7740
From: Hideki EIRAKU @ 2013-01-21 10:54 UTC (permalink / raw)
To: linux-arm-kernel
This is the Renesas IPMMU driver, IOMMU API implementation and IPMMU
device support for sh7372 (AP4EVB and Mackerel), sh73a0 and r8a7740.
The IPMMU module supports the MMU function and the PMB function. The
MMU function provides address translation by pagetable compatible with
ARMv6. The PMB function provides address translation including
tile-linear translation. This is implementation of the MMU function.
Note: The IPMMU module translates DMA memory address from all of the
IP blocks on the ICB. The IOMMU API has iommu_attach_device() which
attaches one device to one domain, but the IPMMU module cannot attach
only one device. The IPMMU module only can attach all devices to one
domain.
Tested on:
- armadillo800eva
- kzma9
- mackerel (I could not make it boot the v3.8-rc4 with Device Tree, so
I tested a kernel without Device Tree support, reverting
3b7b70552afe351a8bd8fff1eb2d60aab2206576 and
0ce53cdc5c7e28f378e480363a0b0c2ed7e7eaf9)
How to test:
1. Boot a kernel built with CONFIG_SHMOBILE_IOMMU=y.
2. Check whether a display on the board shows console normally.
3. Check whether the IMCTR1 register is 1 which means the MMU function enabled.
To read the IMCTR1 register:
# peekpoke 0xfe951000
4. Change the IMCTR1 to 0 to disable the MMU function.
To change the IMCTR1 register to 0:
# peekpoke 0xfe951000 0
5. Check whether the display shows broken data.
6. Change the IMCTR1 to 1 to enable the MMU function.
To change the IMCTR1 register to 1:
# peekpoke 0xfe951000 1
7. Check whether a display on the board shows console normally.
8. Capture and check the framebuffer contents via memory mapped by mmap().
To capture the framebuffer contents and save the image to a file "filename":
# fbcat /dev/fb0 > filename
To look at the contents saved by fbcat on a PC:
$ display filename
- fbcat can be downloaded from https://code.google.com/p/fbcat/.
- display is one of the ImageMagick tools.
- peekpoke is:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <err.h>
int main(int argc,char**argv){
uint32_t*p,addr;
int fd=open("/dev/mem",O_RDWR);
if(fd<0)err(1,"open");
addr=strtoul(argv[1],NULL,0);
p=mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,addr&~0xfff);
if(p==MAP_FAILED)err(1,"mmap");
if(argc<=2)printf("0x%08x\n",p[addr/4&0x3ff]);
else printf("wrote 0x%08x\n",p[addr/4&0x3ff]=strtoul(argv[2],NULL,0));
}
TODO:
- Multiple IPMMU device initialization support.
The ipmmu_iommu_init function and the shmobile_iommu_add_device
function currently do not support multiple IPMMU instances.
- Device Tree support.
Changelog:
v6:
- Rebased on 3.8-rc4.
- Use cached memory for IPMMU page tables.
- Use macros defined in linux/sizes.h.
- Fix a memory leak of page tables when the domain is not attached.
- Use GFP_ATOMIC for allocating page tables.
v5:
- Rebased on 3.8-rc1.
- Add sh73a0 and r8a7740 support.
- CONFIG_SHMOBILE_IOMMU is now selectable only for arm shmobile
architecture.
- Fix "defined but not used" warnings when CONFIG_SHMOBILE_IOMMU is
not set.
- Add help text of CONFIG_SHMOBILE_IOMMU.
- Apply some of Laurent Pinchart's rework patches: Move the driver to
drivers/iommu/.
- Use platform_data to specify attaching devices instead of
ipmmu_add_device() API.
v4:
- Rebased on 3.7-rc1.
- Use address space size instead of page table size in config.
- Use Kconfig default value instead of #ifdef-#define-#endif.
v3:
- Rebased on 3.6-rc5.
- Simplify configs. SHMOBILE_IPMMU is now selected by setting
SHMOBILE_IOMMU.
- Remove weak symbols.
- Use drvdata to store private driver data.
- Make a function for writing to a register of IPMMU.
- Add a lock to accessing the tlb_enabled member.
- Make unmap work correctly with size larger than map size.
- Free L2 page table when 1MiB page is mapped or unmapped.
- Add VEU devices as IP blocks on the ICB.
v2:
- Rebased on v3.6-rc1.
- Make variable names clear.
- Page table size can now be selected by config.
Hideki EIRAKU (4):
iommu/shmobile: Add iommu driver for Renesas IPMMU modules
ARM: mach-shmobile: sh7372: Add IPMMU device
ARM: mach-shmobile: sh73a0: Add IPMMU device
ARM: mach-shmobile: r8a7740: Add IPMMU device
arch/arm/mach-shmobile/setup-r8a7740.c | 33 +++
arch/arm/mach-shmobile/setup-sh7372.c | 39 ++++
arch/arm/mach-shmobile/setup-sh73a0.c | 31 +++
drivers/iommu/Kconfig | 74 ++++++
drivers/iommu/Makefile | 2 +
drivers/iommu/shmobile-iommu.c | 395 +++++++++++++++++++++++++++++++++
drivers/iommu/shmobile-ipmmu.c | 136 ++++++++++++
drivers/iommu/shmobile-ipmmu.h | 34 +++
include/linux/platform_data/sh_ipmmu.h | 18 ++
9 files changed, 762 insertions(+)
create mode 100644 drivers/iommu/shmobile-iommu.c
create mode 100644 drivers/iommu/shmobile-ipmmu.c
create mode 100644 drivers/iommu/shmobile-ipmmu.h
create mode 100644 include/linux/platform_data/sh_ipmmu.h
--
1.8.0
^ permalink raw reply
* [PATCH v6 1/4] iommu/shmobile: Add iommu driver for Renesas IPMMU modules
From: Hideki EIRAKU @ 2013-01-21 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358765669-14567-1-git-send-email-hdk@igel.co.jp>
This is the Renesas IPMMU driver and IOMMU API implementation.
The IPMMU module supports the MMU function and the PMB function. The
MMU function provides address translation by pagetable compatible with
ARMv6. The PMB function provides address translation including
tile-linear translation. This patch implements the MMU function.
The iommu driver does not register a platform driver directly because:
- the register space of the MMU function and the PMB function
have a common register (used for settings flush), so they should ideally
have a way to appropriately share this register.
- the MMU function uses the IOMMU API while the PMB function does not.
- the two functions may be used independently.
Signed-off-by: Hideki EIRAKU <hdk@igel.co.jp>
---
drivers/iommu/Kconfig | 74 ++++++
drivers/iommu/Makefile | 2 +
drivers/iommu/shmobile-iommu.c | 395 +++++++++++++++++++++++++++++++++
drivers/iommu/shmobile-ipmmu.c | 136 ++++++++++++
drivers/iommu/shmobile-ipmmu.h | 34 +++
include/linux/platform_data/sh_ipmmu.h | 18 ++
6 files changed, 659 insertions(+)
create mode 100644 drivers/iommu/shmobile-iommu.c
create mode 100644 drivers/iommu/shmobile-ipmmu.c
create mode 100644 drivers/iommu/shmobile-ipmmu.h
create mode 100644 include/linux/platform_data/sh_ipmmu.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index e39f9db..d364494 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -187,4 +187,78 @@ config EXYNOS_IOMMU_DEBUG
Say N unless you need kernel log message for IOMMU debugging
+config SHMOBILE_IPMMU
+ bool
+
+config SHMOBILE_IPMMU_TLB
+ bool
+
+config SHMOBILE_IOMMU
+ bool "IOMMU for Renesas IPMMU/IPMMUI"
+ default n
+ depends on (ARM && ARCH_SHMOBILE)
+ select IOMMU_API
+ select ARM_DMA_USE_IOMMU
+ select SHMOBILE_IPMMU
+ select SHMOBILE_IPMMU_TLB
+ help
+ Support for Renesas IPMMU/IPMMUI. This option enables
+ remapping of DMA memory accesses from all of the IP blocks
+ on the ICB.
+
+ Warning: Drivers (including userspace drivers of UIO
+ devices) of the IP blocks on the ICB *must* use addresses
+ allocated from the IPMMU (iova) for DMA with this option
+ enabled.
+
+ If unsure, say N.
+
+choice
+ prompt "IPMMU/IPMMUI address space size"
+ default SHMOBILE_IOMMU_ADDRSIZE_2048MB
+ depends on SHMOBILE_IOMMU
+ help
+ This option sets IPMMU/IPMMUI address space size by
+ adjusting the 1st level page table size. The page table size
+ is calculated as follows:
+
+ page table size = number of page table entries * 4 bytes
+ number of page table entries = address space size / 1 MiB
+
+ For example, when the address space size is 2048 MiB, the
+ 1st level page table size is 8192 bytes.
+
+ config SHMOBILE_IOMMU_ADDRSIZE_2048MB
+ bool "2 GiB"
+
+ config SHMOBILE_IOMMU_ADDRSIZE_1024MB
+ bool "1 GiB"
+
+ config SHMOBILE_IOMMU_ADDRSIZE_512MB
+ bool "512 MiB"
+
+ config SHMOBILE_IOMMU_ADDRSIZE_256MB
+ bool "256 MiB"
+
+ config SHMOBILE_IOMMU_ADDRSIZE_128MB
+ bool "128 MiB"
+
+ config SHMOBILE_IOMMU_ADDRSIZE_64MB
+ bool "64 MiB"
+
+ config SHMOBILE_IOMMU_ADDRSIZE_32MB
+ bool "32 MiB"
+
+endchoice
+
+config SHMOBILE_IOMMU_L1SIZE
+ int
+ default 8192 if SHMOBILE_IOMMU_ADDRSIZE_2048MB
+ default 4096 if SHMOBILE_IOMMU_ADDRSIZE_1024MB
+ default 2048 if SHMOBILE_IOMMU_ADDRSIZE_512MB
+ default 1024 if SHMOBILE_IOMMU_ADDRSIZE_256MB
+ default 512 if SHMOBILE_IOMMU_ADDRSIZE_128MB
+ default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB
+ default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index f66b816..ef0e520 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -13,3 +13,5 @@ obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
+obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
+obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c
new file mode 100644
index 0000000..b6e8b57
--- /dev/null
+++ b/drivers/iommu/shmobile-iommu.c
@@ -0,0 +1,395 @@
+/*
+ * IOMMU for IPMMU/IPMMUI
+ * Copyright (C) 2012 Hideki EIRAKU
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <asm/dma-iommu.h>
+#include "shmobile-ipmmu.h"
+
+#define L1_SIZE CONFIG_SHMOBILE_IOMMU_L1SIZE
+#define L1_LEN (L1_SIZE / 4)
+#define L1_ALIGN L1_SIZE
+#define L2_SIZE SZ_1K
+#define L2_LEN (L2_SIZE / 4)
+#define L2_ALIGN L2_SIZE
+
+struct shmobile_iommu_domain_pgtable {
+ uint32_t *pgtable;
+ dma_addr_t handle;
+};
+
+struct shmobile_iommu_archdata {
+ struct list_head attached_list;
+ struct dma_iommu_mapping *iommu_mapping;
+ spinlock_t attach_lock;
+ struct shmobile_iommu_domain *attached;
+ int num_attached_devices;
+ struct shmobile_ipmmu *ipmmu;
+};
+
+struct shmobile_iommu_domain {
+ struct shmobile_iommu_domain_pgtable l1, l2[L1_LEN];
+ spinlock_t map_lock;
+ spinlock_t attached_list_lock;
+ struct list_head attached_list;
+};
+
+static struct shmobile_iommu_archdata *ipmmu_archdata;
+static struct kmem_cache *l1cache, *l2cache;
+
+static int pgtable_alloc(struct shmobile_iommu_domain_pgtable *pgtable,
+ struct kmem_cache *cache, size_t size)
+{
+ pgtable->pgtable = kmem_cache_zalloc(cache, GFP_ATOMIC);
+ if (!pgtable->pgtable)
+ return -ENOMEM;
+ pgtable->handle = dma_map_single(NULL, pgtable->pgtable, size,
+ DMA_TO_DEVICE);
+ return 0;
+}
+
+static void pgtable_free(struct shmobile_iommu_domain_pgtable *pgtable,
+ struct kmem_cache *cache, size_t size)
+{
+ dma_unmap_single(NULL, pgtable->handle, size, DMA_TO_DEVICE);
+ kmem_cache_free(cache, pgtable->pgtable);
+}
+
+static uint32_t pgtable_read(struct shmobile_iommu_domain_pgtable *pgtable,
+ unsigned int index)
+{
+ return pgtable->pgtable[index];
+}
+
+static void pgtable_write(struct shmobile_iommu_domain_pgtable *pgtable,
+ unsigned int index, unsigned int count, uint32_t val)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ pgtable->pgtable[index + i] = val;
+ dma_sync_single_for_device(NULL, pgtable->handle + index * sizeof(val),
+ sizeof(val) * count, DMA_TO_DEVICE);
+}
+
+static int shmobile_iommu_domain_init(struct iommu_domain *domain)
+{
+ struct shmobile_iommu_domain *sh_domain;
+ int i, ret;
+
+ sh_domain = kmalloc(sizeof(*sh_domain), GFP_KERNEL);
+ if (!sh_domain)
+ return -ENOMEM;
+ ret = pgtable_alloc(&sh_domain->l1, l1cache, L1_SIZE);
+ if (ret < 0) {
+ kfree(sh_domain);
+ return ret;
+ }
+ for (i = 0; i < L1_LEN; i++)
+ sh_domain->l2[i].pgtable = NULL;
+ spin_lock_init(&sh_domain->map_lock);
+ spin_lock_init(&sh_domain->attached_list_lock);
+ INIT_LIST_HEAD(&sh_domain->attached_list);
+ domain->priv = sh_domain;
+ return 0;
+}
+
+static void shmobile_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct shmobile_iommu_domain *sh_domain = domain->priv;
+ int i;
+
+ for (i = 0; i < L1_LEN; i++) {
+ if (sh_domain->l2[i].pgtable)
+ pgtable_free(&sh_domain->l2[i], l2cache, L2_SIZE);
+ }
+ pgtable_free(&sh_domain->l1, l1cache, L1_SIZE);
+ kfree(sh_domain);
+ domain->priv = NULL;
+}
+
+static int shmobile_iommu_attach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct shmobile_iommu_archdata *archdata = dev->archdata.iommu;
+ struct shmobile_iommu_domain *sh_domain = domain->priv;
+ int ret = -EBUSY;
+
+ if (!archdata)
+ return -ENODEV;
+ spin_lock(&sh_domain->attached_list_lock);
+ spin_lock(&archdata->attach_lock);
+ if (archdata->attached != sh_domain) {
+ if (archdata->attached)
+ goto err;
+ ipmmu_tlb_set(archdata->ipmmu, sh_domain->l1.handle, L1_SIZE,
+ 0);
+ ipmmu_tlb_flush(archdata->ipmmu);
+ archdata->attached = sh_domain;
+ archdata->num_attached_devices = 0;
+ list_add(&archdata->attached_list, &sh_domain->attached_list);
+ }
+ archdata->num_attached_devices++;
+ ret = 0;
+err:
+ spin_unlock(&archdata->attach_lock);
+ spin_unlock(&sh_domain->attached_list_lock);
+ return ret;
+}
+
+static void shmobile_iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct shmobile_iommu_archdata *archdata = dev->archdata.iommu;
+ struct shmobile_iommu_domain *sh_domain = domain->priv;
+
+ if (!archdata)
+ return;
+ spin_lock(&sh_domain->attached_list_lock);
+ spin_lock(&archdata->attach_lock);
+ archdata->num_attached_devices--;
+ if (!archdata->num_attached_devices) {
+ ipmmu_tlb_set(archdata->ipmmu, 0, 0, 0);
+ ipmmu_tlb_flush(archdata->ipmmu);
+ archdata->attached = NULL;
+ list_del(&archdata->attached_list);
+ }
+ spin_unlock(&archdata->attach_lock);
+ spin_unlock(&sh_domain->attached_list_lock);
+}
+
+static void domain_tlb_flush(struct shmobile_iommu_domain *sh_domain)
+{
+ struct shmobile_iommu_archdata *archdata;
+
+ spin_lock(&sh_domain->attached_list_lock);
+ list_for_each_entry(archdata, &sh_domain->attached_list, attached_list)
+ ipmmu_tlb_flush(archdata->ipmmu);
+ spin_unlock(&sh_domain->attached_list_lock);
+}
+
+static int l2alloc(struct shmobile_iommu_domain *sh_domain,
+ unsigned int l1index)
+{
+ int ret;
+
+ if (!sh_domain->l2[l1index].pgtable) {
+ ret = pgtable_alloc(&sh_domain->l2[l1index], l2cache, L2_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+ pgtable_write(&sh_domain->l1, l1index, 1,
+ sh_domain->l2[l1index].handle | 0x1);
+ return 0;
+}
+
+static void l2realfree(struct shmobile_iommu_domain_pgtable *l2)
+{
+ if (l2->pgtable)
+ pgtable_free(l2, l2cache, L2_SIZE);
+}
+
+static void l2free(struct shmobile_iommu_domain *sh_domain,
+ unsigned int l1index,
+ struct shmobile_iommu_domain_pgtable *l2)
+{
+ pgtable_write(&sh_domain->l1, l1index, 1, 0);
+ if (sh_domain->l2[l1index].pgtable) {
+ *l2 = sh_domain->l2[l1index];
+ sh_domain->l2[l1index].pgtable = NULL;
+ }
+}
+
+static int shmobile_iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL };
+ struct shmobile_iommu_domain *sh_domain = domain->priv;
+ unsigned int l1index, l2index;
+ int ret;
+
+ l1index = iova >> 20;
+ switch (size) {
+ case SZ_4K:
+ l2index = (iova >> 12) & 0xff;
+ spin_lock(&sh_domain->map_lock);
+ ret = l2alloc(sh_domain, l1index);
+ if (!ret)
+ pgtable_write(&sh_domain->l2[l1index], l2index, 1,
+ paddr | 0xff2);
+ spin_unlock(&sh_domain->map_lock);
+ break;
+ case SZ_64K:
+ l2index = (iova >> 12) & 0xf0;
+ spin_lock(&sh_domain->map_lock);
+ ret = l2alloc(sh_domain, l1index);
+ if (!ret)
+ pgtable_write(&sh_domain->l2[l1index], l2index, 0x10,
+ paddr | 0xff1);
+ spin_unlock(&sh_domain->map_lock);
+ break;
+ case SZ_1M:
+ spin_lock(&sh_domain->map_lock);
+ l2free(sh_domain, l1index, &l2);
+ pgtable_write(&sh_domain->l1, l1index, 1, paddr | 0xc02);
+ spin_unlock(&sh_domain->map_lock);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (!ret)
+ domain_tlb_flush(sh_domain);
+ l2realfree(&l2);
+ return ret;
+}
+
+static size_t shmobile_iommu_unmap(struct iommu_domain *domain,
+ unsigned long iova, size_t size)
+{
+ struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL };
+ struct shmobile_iommu_domain *sh_domain = domain->priv;
+ unsigned int l1index, l2index;
+ uint32_t l2entry = 0;
+ size_t ret = 0;
+
+ l1index = iova >> 20;
+ if (!(iova & 0xfffff) && size >= SZ_1M) {
+ spin_lock(&sh_domain->map_lock);
+ l2free(sh_domain, l1index, &l2);
+ spin_unlock(&sh_domain->map_lock);
+ ret = SZ_1M;
+ goto done;
+ }
+ l2index = (iova >> 12) & 0xff;
+ spin_lock(&sh_domain->map_lock);
+ if (sh_domain->l2[l1index].pgtable)
+ l2entry = pgtable_read(&sh_domain->l2[l1index], l2index);
+ switch (l2entry & 3) {
+ case 1:
+ if (l2index & 0xf)
+ break;
+ pgtable_write(&sh_domain->l2[l1index], l2index, 0x10, 0);
+ ret = SZ_64K;
+ break;
+ case 2:
+ pgtable_write(&sh_domain->l2[l1index], l2index, 1, 0);
+ ret = SZ_4K;
+ break;
+ }
+ spin_unlock(&sh_domain->map_lock);
+done:
+ if (ret)
+ domain_tlb_flush(sh_domain);
+ l2realfree(&l2);
+ return ret;
+}
+
+static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long iova)
+{
+ struct shmobile_iommu_domain *sh_domain = domain->priv;
+ uint32_t l1entry = 0, l2entry = 0;
+ unsigned int l1index, l2index;
+
+ l1index = iova >> 20;
+ l2index = (iova >> 12) & 0xff;
+ spin_lock(&sh_domain->map_lock);
+ if (sh_domain->l2[l1index].pgtable)
+ l2entry = pgtable_read(&sh_domain->l2[l1index], l2index);
+ else
+ l1entry = pgtable_read(&sh_domain->l1, l1index);
+ spin_unlock(&sh_domain->map_lock);
+ switch (l2entry & 3) {
+ case 1:
+ return (l2entry & ~0xffff) | (iova & 0xffff);
+ case 2:
+ return (l2entry & ~0xfff) | (iova & 0xfff);
+ default:
+ if ((l1entry & 3) == 2)
+ return (l1entry & ~0xfffff) | (iova & 0xfffff);
+ return 0;
+ }
+}
+
+static int find_dev_name(struct shmobile_ipmmu *ipmmu, const char *dev_name)
+{
+ unsigned int i, n = ipmmu->num_dev_names;
+
+ for (i = 0; i < n; i++) {
+ if (strcmp(ipmmu->dev_names[i], dev_name) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int shmobile_iommu_add_device(struct device *dev)
+{
+ struct shmobile_iommu_archdata *archdata = ipmmu_archdata;
+ struct dma_iommu_mapping *mapping;
+
+ if (!find_dev_name(archdata->ipmmu, dev_name(dev)))
+ return 0;
+ mapping = archdata->iommu_mapping;
+ if (!mapping) {
+ mapping = arm_iommu_create_mapping(&platform_bus_type, 0,
+ L1_LEN << 20, 0);
+ if (IS_ERR(mapping))
+ return PTR_ERR(mapping);
+ archdata->iommu_mapping = mapping;
+ }
+ dev->archdata.iommu = archdata;
+ if (arm_iommu_attach_device(dev, mapping))
+ pr_err("arm_iommu_attach_device failed\n");
+ return 0;
+}
+
+static struct iommu_ops shmobile_iommu_ops = {
+ .domain_init = shmobile_iommu_domain_init,
+ .domain_destroy = shmobile_iommu_domain_destroy,
+ .attach_dev = shmobile_iommu_attach_device,
+ .detach_dev = shmobile_iommu_detach_device,
+ .map = shmobile_iommu_map,
+ .unmap = shmobile_iommu_unmap,
+ .iova_to_phys = shmobile_iommu_iova_to_phys,
+ .add_device = shmobile_iommu_add_device,
+ .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K,
+};
+
+int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu)
+{
+ static struct shmobile_iommu_archdata *archdata;
+
+ l1cache = kmem_cache_create("shmobile-iommu-pgtable1", L1_SIZE,
+ L1_ALIGN, SLAB_HWCACHE_ALIGN, NULL);
+ if (!l1cache)
+ return -ENOMEM;
+ l2cache = kmem_cache_create("shmobile-iommu-pgtable2", L2_SIZE,
+ L2_ALIGN, SLAB_HWCACHE_ALIGN, NULL);
+ if (!l2cache) {
+ kmem_cache_destroy(l1cache);
+ return -ENOMEM;
+ }
+ archdata = kmalloc(sizeof(*archdata), GFP_KERNEL);
+ if (!archdata) {
+ kmem_cache_destroy(l1cache);
+ kmem_cache_destroy(l2cache);
+ return -ENOMEM;
+ }
+ spin_lock_init(&archdata->attach_lock);
+ archdata->attached = NULL;
+ archdata->ipmmu = ipmmu;
+ ipmmu_archdata = archdata;
+ bus_set_iommu(&platform_bus_type, &shmobile_iommu_ops);
+ return 0;
+}
diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c
new file mode 100644
index 0000000..8321f89
--- /dev/null
+++ b/drivers/iommu/shmobile-ipmmu.c
@@ -0,0 +1,136 @@
+/*
+ * IPMMU/IPMMUI
+ * Copyright (C) 2012 Hideki EIRAKU
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/platform_data/sh_ipmmu.h>
+#include "shmobile-ipmmu.h"
+
+#define IMCTR1 0x000
+#define IMCTR2 0x004
+#define IMASID 0x010
+#define IMTTBR 0x014
+#define IMTTBCR 0x018
+
+#define IMCTR1_TLBEN (1 << 0)
+#define IMCTR1_FLUSH (1 << 1)
+
+static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long reg_off,
+ unsigned long data)
+{
+ iowrite32(data, ipmmu->ipmmu_base + reg_off);
+}
+
+void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu)
+{
+ if (!ipmmu)
+ return;
+
+ mutex_lock(&ipmmu->flush_lock);
+ if (ipmmu->tlb_enabled)
+ ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN);
+ else
+ ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH);
+ mutex_unlock(&ipmmu->flush_lock);
+}
+
+void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size,
+ int asid)
+{
+ if (!ipmmu)
+ return;
+
+ mutex_lock(&ipmmu->flush_lock);
+ switch (size) {
+ default:
+ ipmmu->tlb_enabled = 0;
+ break;
+ case 0x2000:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 1);
+ ipmmu->tlb_enabled = 1;
+ break;
+ case 0x1000:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 2);
+ ipmmu->tlb_enabled = 1;
+ break;
+ case 0x800:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 3);
+ ipmmu->tlb_enabled = 1;
+ break;
+ case 0x400:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 4);
+ ipmmu->tlb_enabled = 1;
+ break;
+ case 0x200:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 5);
+ ipmmu->tlb_enabled = 1;
+ break;
+ case 0x100:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 6);
+ ipmmu->tlb_enabled = 1;
+ break;
+ case 0x80:
+ ipmmu_reg_write(ipmmu, IMTTBCR, 7);
+ ipmmu->tlb_enabled = 1;
+ break;
+ }
+ ipmmu_reg_write(ipmmu, IMTTBR, phys);
+ ipmmu_reg_write(ipmmu, IMASID, asid);
+ mutex_unlock(&ipmmu->flush_lock);
+}
+
+static int ipmmu_probe(struct platform_device *pdev)
+{
+ struct shmobile_ipmmu *ipmmu;
+ struct resource *res;
+ struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot get platform resources\n");
+ return -ENOENT;
+ }
+ ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL);
+ if (!ipmmu) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ return -ENOMEM;
+ }
+ mutex_init(&ipmmu->flush_lock);
+ ipmmu->dev = &pdev->dev;
+ ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!ipmmu->ipmmu_base) {
+ dev_err(&pdev->dev, "ioremap_nocache failed\n");
+ return -ENOMEM;
+ }
+ ipmmu->dev_names = pdata->dev_names;
+ ipmmu->num_dev_names = pdata->num_dev_names;
+ platform_set_drvdata(pdev, ipmmu);
+ ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */
+ ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */
+ ipmmu_iommu_init(ipmmu);
+ return 0;
+}
+
+static struct platform_driver ipmmu_driver = {
+ .probe = ipmmu_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ipmmu",
+ },
+};
+
+static int __init ipmmu_init(void)
+{
+ return platform_driver_register(&ipmmu_driver);
+}
+subsys_initcall(ipmmu_init);
diff --git a/drivers/iommu/shmobile-ipmmu.h b/drivers/iommu/shmobile-ipmmu.h
new file mode 100644
index 0000000..4d53684
--- /dev/null
+++ b/drivers/iommu/shmobile-ipmmu.h
@@ -0,0 +1,34 @@
+/* shmobile-ipmmu.h
+ *
+ * Copyright (C) 2012 Hideki EIRAKU
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#ifndef __SHMOBILE_IPMMU_H__
+#define __SHMOBILE_IPMMU_H__
+
+struct shmobile_ipmmu {
+ struct device *dev;
+ void __iomem *ipmmu_base;
+ int tlb_enabled;
+ struct mutex flush_lock;
+ const char * const *dev_names;
+ unsigned int num_dev_names;
+};
+
+#ifdef CONFIG_SHMOBILE_IPMMU_TLB
+void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu);
+void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size,
+ int asid);
+int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu);
+#else
+static inline int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* __SHMOBILE_IPMMU_H__ */
diff --git a/include/linux/platform_data/sh_ipmmu.h b/include/linux/platform_data/sh_ipmmu.h
new file mode 100644
index 0000000..39f7405
--- /dev/null
+++ b/include/linux/platform_data/sh_ipmmu.h
@@ -0,0 +1,18 @@
+/* sh_ipmmu.h
+ *
+ * Copyright (C) 2012 Hideki EIRAKU
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#ifndef __SH_IPMMU_H__
+#define __SH_IPMMU_H__
+
+struct shmobile_ipmmu_platform_data {
+ const char * const *dev_names;
+ unsigned int num_dev_names;
+};
+
+#endif /* __SH_IPMMU_H__ */
--
1.8.0
^ permalink raw reply related
* [PATCH v6 2/4] ARM: mach-shmobile: sh7372: Add IPMMU device
From: Hideki EIRAKU @ 2013-01-21 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358765669-14567-1-git-send-email-hdk@igel.co.jp>
This patch adds an IPMMU device and notifies the IPMMU driver which
devices are connected via the IPMMU module. All devices connected to the main
memory bus via the IPMMU module MUST be registered when SHMOBILE_IPMMU and
SHMOBILE_IOMMU are enabled because physical address cannot be used
while the IPMMU module's MMU function is enabled.
Signed-off-by: Hideki EIRAKU <hdk@igel.co.jp>
---
arch/arm/mach-shmobile/setup-sh7372.c | 39 +++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c
index c917882..04ad98e 100644
--- a/arch/arm/mach-shmobile/setup-sh7372.c
+++ b/arch/arm/mach-shmobile/setup-sh7372.c
@@ -33,6 +33,7 @@
#include <linux/sh_timer.h>
#include <linux/pm_domain.h>
#include <linux/dma-mapping.h>
+#include <linux/platform_data/sh_ipmmu.h>
#include <mach/dma-register.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
@@ -982,6 +983,43 @@ static struct platform_device spu1_device = {
.num_resources = ARRAY_SIZE(spu1_resources),
};
+/* IPMMUI (an IPMMU module for ICB/LMB) */
+static struct resource ipmmu_resources[] = {
+ [0] = {
+ .name = "IPMMUI",
+ .start = 0xfe951000,
+ .end = 0xfe9510ff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const char * const ipmmu_dev_names[] = {
+ "sh_mobile_lcdc_fb.0",
+ "sh_mobile_lcdc_fb.1",
+ "sh_mobile_ceu.0",
+ "uio_pdrv_genirq.0",
+ "uio_pdrv_genirq.1",
+ "uio_pdrv_genirq.2",
+ "uio_pdrv_genirq.3",
+ "uio_pdrv_genirq.4",
+ "uio_pdrv_genirq.5",
+};
+
+static struct shmobile_ipmmu_platform_data ipmmu_platform_data = {
+ .dev_names = ipmmu_dev_names,
+ .num_dev_names = ARRAY_SIZE(ipmmu_dev_names),
+};
+
+static struct platform_device ipmmu_device = {
+ .name = "ipmmu",
+ .id = -1,
+ .dev = {
+ .platform_data = &ipmmu_platform_data,
+ },
+ .resource = ipmmu_resources,
+ .num_resources = ARRAY_SIZE(ipmmu_resources),
+};
+
static struct platform_device *sh7372_early_devices[] __initdata = {
&scif0_device,
&scif1_device,
@@ -993,6 +1031,7 @@ static struct platform_device *sh7372_early_devices[] __initdata = {
&cmt2_device,
&tmu00_device,
&tmu01_device,
+ &ipmmu_device,
};
static struct platform_device *sh7372_late_devices[] __initdata = {
--
1.8.0
^ permalink raw reply related
* [PATCH v6 3/4] ARM: mach-shmobile: sh73a0: Add IPMMU device
From: Hideki EIRAKU @ 2013-01-21 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358765669-14567-1-git-send-email-hdk@igel.co.jp>
This patch adds an IPMMU device and notifies the IPMMU driver which
devices are connected via the IPMMU module. All devices connected to the main
memory bus via the IPMMU module MUST be registered when SHMOBILE_IPMMU and
SHMOBILE_IOMMU are enabled because physical address cannot be used
while the IPMMU module's MMU function is enabled.
Signed-off-by: Hideki EIRAKU <hdk@igel.co.jp>
---
arch/arm/mach-shmobile/setup-sh73a0.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c
index db99a4a..36c2b2e 100644
--- a/arch/arm/mach-shmobile/setup-sh73a0.c
+++ b/arch/arm/mach-shmobile/setup-sh73a0.c
@@ -30,6 +30,7 @@
#include <linux/sh_dma.h>
#include <linux/sh_intc.h>
#include <linux/sh_timer.h>
+#include <linux/platform_data/sh_ipmmu.h>
#include <mach/dma-register.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
@@ -754,6 +755,35 @@ static struct platform_device pmu_device = {
.resource = pmu_resources,
};
+/* an IPMMU module for ICB */
+static struct resource ipmmu_resources[] = {
+ [0] = {
+ .name = "IPMMU",
+ .start = 0xfe951000,
+ .end = 0xfe9510ff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const char * const ipmmu_dev_names[] = {
+ "sh_mobile_lcdc_fb.0",
+};
+
+static struct shmobile_ipmmu_platform_data ipmmu_platform_data = {
+ .dev_names = ipmmu_dev_names,
+ .num_dev_names = ARRAY_SIZE(ipmmu_dev_names),
+};
+
+static struct platform_device ipmmu_device = {
+ .name = "ipmmu",
+ .id = -1,
+ .dev = {
+ .platform_data = &ipmmu_platform_data,
+ },
+ .resource = ipmmu_resources,
+ .num_resources = ARRAY_SIZE(ipmmu_resources),
+};
+
static struct platform_device *sh73a0_early_devices[] __initdata = {
&scif0_device,
&scif1_device,
@@ -767,6 +797,7 @@ static struct platform_device *sh73a0_early_devices[] __initdata = {
&cmt10_device,
&tmu00_device,
&tmu01_device,
+ &ipmmu_device,
};
static struct platform_device *sh73a0_late_devices[] __initdata = {
--
1.8.0
^ permalink raw reply related
* [PATCH v6 4/4] ARM: mach-shmobile: r8a7740: Add IPMMU device
From: Hideki EIRAKU @ 2013-01-21 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358765669-14567-1-git-send-email-hdk@igel.co.jp>
This patch adds an IPMMU device and notifies the IPMMU driver which
devices are connected via the IPMMU module. All devices connected to the main
memory bus via the IPMMU module MUST be registered when SHMOBILE_IPMMU and
SHMOBILE_IOMMU are enabled because physical address cannot be used
while the IPMMU module's MMU function is enabled.
Signed-off-by: Hideki EIRAKU <hdk@igel.co.jp>
---
arch/arm/mach-shmobile/setup-r8a7740.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c
index 0952224..b85bea5 100644
--- a/arch/arm/mach-shmobile/setup-r8a7740.c
+++ b/arch/arm/mach-shmobile/setup-r8a7740.c
@@ -28,6 +28,7 @@
#include <linux/sh_dma.h>
#include <linux/sh_timer.h>
#include <linux/dma-mapping.h>
+#include <linux/platform_data/sh_ipmmu.h>
#include <mach/dma-register.h>
#include <mach/r8a7740.h>
#include <mach/pm-rmobile.h>
@@ -262,6 +263,37 @@ static struct platform_device cmt10_device = {
.num_resources = ARRAY_SIZE(cmt10_resources),
};
+/* IPMMUI (an IPMMU module for ICB/LMB) */
+static struct resource ipmmu_resources[] = {
+ [0] = {
+ .name = "IPMMUI",
+ .start = 0xfe951000,
+ .end = 0xfe9510ff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const char * const ipmmu_dev_names[] = {
+ "sh_mobile_lcdc_fb.0",
+ "sh_mobile_lcdc_fb.1",
+ "sh_mobile_ceu.0",
+};
+
+static struct shmobile_ipmmu_platform_data ipmmu_platform_data = {
+ .dev_names = ipmmu_dev_names,
+ .num_dev_names = ARRAY_SIZE(ipmmu_dev_names),
+};
+
+static struct platform_device ipmmu_device = {
+ .name = "ipmmu",
+ .id = -1,
+ .dev = {
+ .platform_data = &ipmmu_platform_data,
+ },
+ .resource = ipmmu_resources,
+ .num_resources = ARRAY_SIZE(ipmmu_resources),
+};
+
static struct platform_device *r8a7740_early_devices[] __initdata = {
&scif0_device,
&scif1_device,
@@ -273,6 +305,7 @@ static struct platform_device *r8a7740_early_devices[] __initdata = {
&scif7_device,
&scifb_device,
&cmt10_device,
+ &ipmmu_device,
};
/* DMA */
--
1.8.0
^ permalink raw reply related
* [PATCH v9 05/22] mfd: omap-usb-tll: Clean up clock handling
From: Roger Quadros @ 2013-01-21 11:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130118145906.GE23505@n2100.arm.linux.org.uk>
Every channel has a functional clock that is similarly named.
It makes sense to use a for loop to manage these clocks as OMAPs
can come with up to 3 channels.
Dynamically allocate and get channel clocks depending on the
number of clocks avaiable on the platform.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
drivers/mfd/omap-usb-tll.c | 87 +++++++++++++++++++++++++++-----------------
1 files changed, 54 insertions(+), 33 deletions(-)
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 9a19cc7..bf7355e 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -96,10 +96,9 @@
#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL)
struct usbtll_omap {
- struct clk *usbtll_p1_fck;
- struct clk *usbtll_p2_fck;
int nch; /* num. of channels */
struct usbhs_omap_platform_data *pdata;
+ struct clk **ch_clk;
/* secure the register updates */
spinlock_t lock;
};
@@ -225,26 +224,12 @@ static int usbtll_omap_probe(struct platform_device *pdev)
tll->pdata = pdata;
- tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
- if (IS_ERR(tll->usbtll_p1_fck)) {
- ret = PTR_ERR(tll->usbtll_p1_fck);
- dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
- return ret;
- }
-
- tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
- if (IS_ERR(tll->usbtll_p2_fck)) {
- ret = PTR_ERR(tll->usbtll_p2_fck);
- dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
- goto err_p2_fck;
- }
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_request_and_ioremap(dev, res);
if (!base) {
ret = -EADDRNOTAVAIL;
dev_err(dev, "Resource request/ioremap failed:%d\n", ret);
- goto err_res;
+ return ret;
}
platform_set_drvdata(pdev, tll);
@@ -270,6 +255,29 @@ static int usbtll_omap_probe(struct platform_device *pdev)
break;
}
+ spin_unlock_irqrestore(&tll->lock, flags);
+
+ tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]),
+ GFP_KERNEL);
+ if (!tll->ch_clk) {
+ ret = -ENOMEM;
+ dev_err(dev, "Couldn't allocate memory for channel clocks\n");
+ goto err_clk_alloc;
+ }
+
+ for (i = 0; i < tll->nch; i++) {
+ char clkname[] = "usb_tll_hs_usb_chx_clk";
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_tll_hs_usb_ch%d_clk", i);
+ tll->ch_clk[i] = clk_get(dev, clkname);
+
+ if (IS_ERR(tll->ch_clk[i]))
+ dev_dbg(dev, "can't get clock : %s\n", clkname);
+ }
+
+ spin_lock_irqsave(&tll->lock, flags);
+
if (is_ehci_tll_mode(pdata->port_mode[0]) ||
is_ehci_tll_mode(pdata->port_mode[1]) ||
is_ehci_tll_mode(pdata->port_mode[2]) ||
@@ -321,11 +329,9 @@ static int usbtll_omap_probe(struct platform_device *pdev)
return 0;
-err_res:
- clk_put(tll->usbtll_p2_fck);
-
-err_p2_fck:
- clk_put(tll->usbtll_p1_fck);
+err_clk_alloc:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
return ret;
}
@@ -339,9 +345,12 @@ err_p2_fck:
static int usbtll_omap_remove(struct platform_device *pdev)
{
struct usbtll_omap *tll = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < tll->nch; i++)
+ if (!IS_ERR(tll->ch_clk[i]))
+ clk_put(tll->ch_clk[i]);
- clk_put(tll->usbtll_p2_fck);
- clk_put(tll->usbtll_p1_fck);
pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -351,6 +360,7 @@ static int usbtll_runtime_resume(struct device *dev)
struct usbtll_omap *tll = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = tll->pdata;
unsigned long flags;
+ int i;
dev_dbg(dev, "usbtll_runtime_resume\n");
@@ -361,11 +371,20 @@ static int usbtll_runtime_resume(struct device *dev)
spin_lock_irqsave(&tll->lock, flags);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(tll->usbtll_p1_fck);
+ for (i = 0; i < tll->nch; i++) {
+ if (is_ehci_tll_mode(pdata->port_mode[i])) {
+ int r;
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(tll->usbtll_p2_fck);
+ if (IS_ERR(tll->ch_clk[i]))
+ continue;
+
+ r = clk_enable(tll->ch_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Error enabling ch %d clock: %d\n", i, r);
+ }
+ }
+ }
spin_unlock_irqrestore(&tll->lock, flags);
@@ -377,6 +396,7 @@ static int usbtll_runtime_suspend(struct device *dev)
struct usbtll_omap *tll = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = tll->pdata;
unsigned long flags;
+ int i;
dev_dbg(dev, "usbtll_runtime_suspend\n");
@@ -387,11 +407,12 @@ static int usbtll_runtime_suspend(struct device *dev)
spin_lock_irqsave(&tll->lock, flags);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_disable(tll->usbtll_p1_fck);
-
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_disable(tll->usbtll_p2_fck);
+ for (i = 0; i < tll->nch; i++) {
+ if (is_ehci_tll_mode(pdata->port_mode[i])) {
+ if (!IS_ERR(tll->ch_clk[i]))
+ clk_disable(tll->ch_clk[i]);
+ }
+ }
spin_unlock_irqrestore(&tll->lock, flags);
--
1.7.4.1
^ permalink raw reply related
* [PATCH v9 06/22] mfd: omap-usb-tll: introduce and use mode_needs_tll()
From: Roger Quadros @ 2013-01-21 11:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130118150242.GF23505@n2100.arm.linux.org.uk>
This is a handy macro to check if the port requires the
USB TLL module or not. Use it to Enable the TLL module and manage
the clocks.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
drivers/mfd/omap-usb-tll.c | 20 ++++++++++++--------
1 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index bf7355e..af67b96 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -95,6 +95,10 @@
#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL)
+/* only PHY and UNUSED modes don't need TLL */
+#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\
+ (x) != OMAP_EHCI_PORT_MODE_PHY)
+
struct usbtll_omap {
int nch; /* num. of channels */
struct usbhs_omap_platform_data *pdata;
@@ -211,6 +215,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
unsigned long flags;
int ret = 0;
int i, ver;
+ bool needs_tll;
dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
@@ -278,12 +283,11 @@ static int usbtll_omap_probe(struct platform_device *pdev)
spin_lock_irqsave(&tll->lock, flags);
- if (is_ehci_tll_mode(pdata->port_mode[0]) ||
- is_ehci_tll_mode(pdata->port_mode[1]) ||
- is_ehci_tll_mode(pdata->port_mode[2]) ||
- is_ohci_port(pdata->port_mode[0]) ||
- is_ohci_port(pdata->port_mode[1]) ||
- is_ohci_port(pdata->port_mode[2])) {
+ needs_tll = false;
+ for (i = 0; i < tll->nch; i++)
+ needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]);
+
+ if (needs_tll) {
/* Program Common TLL register */
reg = usbtll_read(base, OMAP_TLL_SHARED_CONF);
@@ -372,7 +376,7 @@ static int usbtll_runtime_resume(struct device *dev)
spin_lock_irqsave(&tll->lock, flags);
for (i = 0; i < tll->nch; i++) {
- if (is_ehci_tll_mode(pdata->port_mode[i])) {
+ if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
int r;
if (IS_ERR(tll->ch_clk[i]))
@@ -408,7 +412,7 @@ static int usbtll_runtime_suspend(struct device *dev)
spin_lock_irqsave(&tll->lock, flags);
for (i = 0; i < tll->nch; i++) {
- if (is_ehci_tll_mode(pdata->port_mode[i])) {
+ if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
if (!IS_ERR(tll->ch_clk[i]))
clk_disable(tll->ch_clk[i]);
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH] net: fec: Add support for multiple phys on mdiobus
From: Wolfgang Grandegger @ 2013-01-21 11:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130121100701.GX1906@pengutronix.de>
On 01/21/2013 11:07 AM, Sascha Hauer wrote:
> On Mon, Jan 21, 2013 at 09:56:24AM +0100, Wolfgang Grandegger wrote:
>> On 01/21/2013 09:37 AM, Sascha Hauer wrote:
>>> There may be multiple phys on an mdio bus. This series adds support
>>> for this to the fec driver. I recently had a board which has a switch
>>> connected to the fec's mdio bus, so I had to pick the correct phy.
>>
>> Pick one PHY from a switch port? Well, does a PHY-less (or fixed-link)
>> configuration for a switch not make more sense?
>
> Yes, you're probably right.
>
>> Various ARM Ethernet
>> contoller drivers do not support it. I recently needed a hack for an
>> AT91 board.
>
> I wonder how we want to proceed. Should there be a devicetree property
> 'fixed-link' like done for fs_enet (and not recommended for new code,
> stated in the comment above of_phy_connect_fixed_link)?
Also the gianfar and ucc_geth drivers use this interface (via fixed
link phy). I tried to use it for the AT91 macb driver but stopped
quickly because the usage was not straight forward (too much code)...
even if the idea of using a fake fixed-link phy is not bad.
> Currently I have a property 'phy' in the fec binding which has a phandle
> to a phy provided by the fec's mdio bus, but this could equally well
But than the cable must be connected to the associated switch port.
> point to a fixed dummy phy:
>
> phy = &fixed-phy;
The link speed, full/half duplex and maybe some mroe parameter should
be configurable via device tree.
> Currently there seems to be no common convention for the devicetree how
> to handle such situations, or am I missing something?
That's also may impression. There seem to be a few more related hacks:
$ find . -name '*.c'| xargs grep -i "phy-less"
./ethernet/amd/au1000_eth.c: netdev_info(dev, "using PHY-less setup\n");
./ethernet/amd/au1000_eth.c: } else { /* PHY-less op, assume full-duplex */
./ethernet/ibm/emac/core.c: /* PHY-less configuration.
./ethernet/ibm/emac/core.c: /* PHY-less configuration.
I would prefer to handle the "fixed-link" property of the ethernet dt
node directly in the driver with a generic helper function.
Wolfgang.
^ permalink raw reply
* [PATCH v9 15/22] mfd: omap-usb-host: cleanup clock management code
From: Roger Quadros @ 2013-01-21 11:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358511445-26656-16-git-send-email-rogerq@ti.com>
All ports have similarly named port clocks so we can
bunch them into a port data structure and use for loop
to enable/disable the clocks.
Dynamically allocate and get clocks based on number of ports
available on the platform
Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
drivers/mfd/omap-usb-host.c | 186 ++++++++++++++++++++++++------------------
1 files changed, 106 insertions(+), 80 deletions(-)
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 779588b..9fa0215 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -92,13 +92,12 @@
struct usbhs_hcd_omap {
int nports;
+ struct clk **utmi_clk;
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
- struct clk *utmi_p1_fck;
- struct clk *usbhost_p1_fck;
- struct clk *utmi_p2_fck;
- struct clk *usbhost_p2_fck;
+ struct clk *utmi_p1_gfclk;
+ struct clk *utmi_p2_gfclk;
struct clk *init_60m_fclk;
struct clk *ehci_logic_fck;
@@ -276,22 +275,25 @@ static int usbhs_runtime_resume(struct device *dev)
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = omap->pdata;
unsigned long flags;
+ int i, r;
dev_dbg(dev, "usbhs_runtime_resume\n");
omap_tll_enable();
spin_lock_irqsave(&omap->lock, flags);
- if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+ if (!IS_ERR(omap->ehci_logic_fck))
clk_enable(omap->ehci_logic_fck);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(omap->usbhost_p1_fck);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(omap->usbhost_p2_fck);
+ for (i = 0; i < omap->nports; i++) {
+ if (!is_ehci_tll_mode(pdata->port_mode[i]) ||
+ IS_ERR(omap->utmi_clk[i]))
+ continue;
- clk_enable(omap->utmi_p1_fck);
- clk_enable(omap->utmi_p2_fck);
+ r = clk_enable(omap->utmi_clk[i]);
+ if (r)
+ dev_err(dev, "Can't enable port %d clk : %d\n", i, r);
+ }
spin_unlock_irqrestore(&omap->lock, flags);
@@ -303,20 +305,19 @@ static int usbhs_runtime_suspend(struct device *dev)
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = omap->pdata;
unsigned long flags;
+ int i;
dev_dbg(dev, "usbhs_runtime_suspend\n");
spin_lock_irqsave(&omap->lock, flags);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_disable(omap->usbhost_p1_fck);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_disable(omap->usbhost_p2_fck);
-
- clk_disable(omap->utmi_p2_fck);
- clk_disable(omap->utmi_p1_fck);
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_tll_mode(pdata->port_mode[i]) &&
+ !IS_ERR(omap->utmi_clk[i]))
+ clk_disable(omap->utmi_clk[i]);
+ }
- if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+ if (!IS_ERR(omap->ehci_logic_fck))
clk_disable(omap->ehci_logic_fck);
spin_unlock_irqrestore(&omap->lock, flags);
@@ -458,6 +459,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
int i;
+ bool need_logic_fck;
if (!pdata) {
dev_err(dev, "Missing platform data\n");
@@ -516,76 +518,91 @@ static int usbhs_omap_probe(struct platform_device *pdev)
}
}
- for (i = 0; i < omap->nports; i++)
+ i = sizeof(struct clk *) * omap->nports;
+ omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+ if (!omap->utmi_clk) {
+ dev_err(dev, "Memory allocation failed\n");
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ need_logic_fck = false;
+ for (i = 0; i < omap->nports; i++) {
if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
- is_ehci_hsic_mode(i)) {
- omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
- if (IS_ERR(omap->ehci_logic_fck)) {
- ret = PTR_ERR(omap->ehci_logic_fck);
- dev_warn(dev, "ehci_logic_fck failed:%d\n",
- ret);
- }
- break;
+ is_ehci_hsic_mode(i))
+ need_logic_fck |= true;
+ }
+
+ omap->ehci_logic_fck = ERR_PTR(-EINVAL);
+ if (need_logic_fck) {
+ omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
+ if (IS_ERR(omap->ehci_logic_fck)) {
+ ret = PTR_ERR(omap->ehci_logic_fck);
+ dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret);
}
+ }
- omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
- if (IS_ERR(omap->utmi_p1_fck)) {
- ret = PTR_ERR(omap->utmi_p1_fck);
- dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_end;
+ omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk");
+ if (IS_ERR(omap->utmi_p1_gfclk)) {
+ ret = PTR_ERR(omap->utmi_p1_gfclk);
+ dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
+ goto err_p1_gfclk;
+ }
+
+ omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk");
+ if (IS_ERR(omap->utmi_p2_gfclk)) {
+ ret = PTR_ERR(omap->utmi_p2_gfclk);
+ dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
+ goto err_p2_gfclk;
}
omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
if (IS_ERR(omap->xclk60mhsp1_ck)) {
ret = PTR_ERR(omap->xclk60mhsp1_ck);
dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_utmi_p1_fck;
- }
-
- omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
- if (IS_ERR(omap->utmi_p2_fck)) {
- ret = PTR_ERR(omap->utmi_p2_fck);
- dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_xclk60mhsp1_ck;
+ goto err_xclk60mhsp1;
}
omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_utmi_p2_fck;
- }
-
- omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
- if (IS_ERR(omap->usbhost_p1_fck)) {
- ret = PTR_ERR(omap->usbhost_p1_fck);
- dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
- goto err_xclk60mhsp2_ck;
- }
-
- omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
- if (IS_ERR(omap->usbhost_p2_fck)) {
- ret = PTR_ERR(omap->usbhost_p2_fck);
- dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
- goto err_usbhost_p1_fck;
+ goto err_xclk60mhsp2;
}
omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
if (IS_ERR(omap->init_60m_fclk)) {
ret = PTR_ERR(omap->init_60m_fclk);
dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_usbhost_p2_fck;
+ goto err_init60m;
+ }
+
+ for (i = 0; i < omap->nports; i++) {
+ char clkname[] = "usb_host_hs_utmi_px_clk";
+
+ /* clock names are indexed from 1*/
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_utmi_p%d_clk", i + 1);
+
+ /* If a clock is not found we won't bail out as not all
+ * platforms have all clocks and we can function without
+ * them
+ */
+ omap->utmi_clk[i] = clk_get(dev, clkname);
+ if (IS_ERR(omap->utmi_clk[i]))
+ dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+ clkname, PTR_ERR(omap->utmi_clk[i]));
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
/* for OMAP3 , the clk set paretn fails */
- ret = clk_set_parent(omap->utmi_p1_fck,
+ ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->xclk60mhsp1_ck);
if (ret != 0)
dev_err(dev, "xclk60mhsp1_ck set parent"
"failed error:%d\n", ret);
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
- ret = clk_set_parent(omap->utmi_p1_fck,
+ ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->init_60m_fclk);
if (ret != 0)
dev_err(dev, "init_60m_fclk set parent"
@@ -593,13 +610,13 @@ static int usbhs_omap_probe(struct platform_device *pdev)
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
- ret = clk_set_parent(omap->utmi_p2_fck,
+ ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->xclk60mhsp2_ck);
if (ret != 0)
dev_err(dev, "xclk60mhsp2_ck set parent"
"failed error:%d\n", ret);
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
- ret = clk_set_parent(omap->utmi_p2_fck,
+ ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->init_60m_fclk);
if (ret != 0)
dev_err(dev, "init_60m_fclk set parent"
@@ -617,28 +634,30 @@ static int usbhs_omap_probe(struct platform_device *pdev)
err_alloc:
omap_usbhs_deinit(&pdev->dev);
- clk_put(omap->init_60m_fclk);
-err_usbhost_p2_fck:
- clk_put(omap->usbhost_p2_fck);
+ for (i = 0; i < omap->nports; i++)
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_put(omap->utmi_clk[i]);
-err_usbhost_p1_fck:
- clk_put(omap->usbhost_p1_fck);
+ clk_put(omap->init_60m_fclk);
-err_xclk60mhsp2_ck:
+err_init60m:
clk_put(omap->xclk60mhsp2_ck);
-err_utmi_p2_fck:
- clk_put(omap->utmi_p2_fck);
-
-err_xclk60mhsp1_ck:
+err_xclk60mhsp2:
clk_put(omap->xclk60mhsp1_ck);
-err_utmi_p1_fck:
- clk_put(omap->utmi_p1_fck);
+err_xclk60mhsp1:
+ clk_put(omap->utmi_p2_gfclk);
-err_end:
- clk_put(omap->ehci_logic_fck);
+err_p2_gfclk:
+ clk_put(omap->utmi_p1_gfclk);
+
+err_p1_gfclk:
+ if (!IS_ERR(omap->ehci_logic_fck))
+ clk_put(omap->ehci_logic_fck);
+
+err_mem:
pm_runtime_disable(dev);
return ret;
@@ -653,16 +672,23 @@ err_end:
static int usbhs_omap_remove(struct platform_device *pdev)
{
struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
+ int i;
omap_usbhs_deinit(&pdev->dev);
+
+ for (i = 0; i < omap->nports; i++)
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_put(omap->utmi_clk[i]);
+
clk_put(omap->init_60m_fclk);
- clk_put(omap->usbhost_p2_fck);
- clk_put(omap->usbhost_p1_fck);
+ clk_put(omap->utmi_p1_gfclk);
+ clk_put(omap->utmi_p2_gfclk);
clk_put(omap->xclk60mhsp2_ck);
- clk_put(omap->utmi_p2_fck);
clk_put(omap->xclk60mhsp1_ck);
- clk_put(omap->utmi_p1_fck);
- clk_put(omap->ehci_logic_fck);
+
+ if (!IS_ERR(omap->ehci_logic_fck))
+ clk_put(omap->ehci_logic_fck);
+
pm_runtime_disable(&pdev->dev);
return 0;
--
1.7.4.1
^ permalink raw reply related
* [PATCH v9 16/22] mfd: omap-usb-host: Manage HSIC clocks for HSIC mode
From: Roger Quadros @ 2013-01-21 11:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358511445-26656-18-git-send-email-rogerq@ti.com>
Enable the optional HSIC clocks (60MHz and 480MHz) for the ports
that are configured in HSIC mode.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
drivers/mfd/omap-usb-host.c | 97 ++++++++++++++++++++++++++++++++++++------
1 files changed, 83 insertions(+), 14 deletions(-)
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 9fa0215..bdfc8b7 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -93,6 +93,8 @@
struct usbhs_hcd_omap {
int nports;
struct clk **utmi_clk;
+ struct clk **hsic60m_clk;
+ struct clk **hsic480m_clk;
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
@@ -286,13 +288,40 @@ static int usbhs_runtime_resume(struct device *dev)
clk_enable(omap->ehci_logic_fck);
for (i = 0; i < omap->nports; i++) {
- if (!is_ehci_tll_mode(pdata->port_mode[i]) ||
- IS_ERR(omap->utmi_clk[i]))
- continue;
-
- r = clk_enable(omap->utmi_clk[i]);
- if (r)
- dev_err(dev, "Can't enable port %d clk : %d\n", i, r);
+ switch (pdata->port_mode[i]) {
+ case OMAP_EHCI_PORT_MODE_HSIC:
+ if (!IS_ERR(omap->hsic60m_clk[i])) {
+ r = clk_enable(omap->hsic60m_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d hsic60m clk:%d\n",
+ i, r);
+ }
+ }
+
+ if (!IS_ERR(omap->hsic480m_clk[i])) {
+ r = clk_enable(omap->hsic480m_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d hsic480m clk:%d\n",
+ i, r);
+ }
+ }
+ /* Fall through as HSIC mode needs utmi_clk */
+
+ case OMAP_EHCI_PORT_MODE_TLL:
+ if (!IS_ERR(omap->utmi_clk[i])) {
+ r = clk_enable(omap->utmi_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d clk : %d\n",
+ i, r);
+ }
+ }
+ break;
+ default:
+ break;
+ }
}
spin_unlock_irqrestore(&omap->lock, flags);
@@ -312,9 +341,22 @@ static int usbhs_runtime_suspend(struct device *dev)
spin_lock_irqsave(&omap->lock, flags);
for (i = 0; i < omap->nports; i++) {
- if (is_ehci_tll_mode(pdata->port_mode[i]) &&
- !IS_ERR(omap->utmi_clk[i]))
- clk_disable(omap->utmi_clk[i]);
+ switch (pdata->port_mode[i]) {
+ case OMAP_EHCI_PORT_MODE_HSIC:
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_disable(omap->hsic60m_clk[i]);
+
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_disable(omap->hsic480m_clk[i]);
+ /* Fall through as utmi_clks were used in HSIC mode */
+
+ case OMAP_EHCI_PORT_MODE_TLL:
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_disable(omap->utmi_clk[i]);
+ break;
+ default:
+ break;
+ }
}
if (!IS_ERR(omap->ehci_logic_fck))
@@ -520,7 +562,10 @@ static int usbhs_omap_probe(struct platform_device *pdev)
i = sizeof(struct clk *) * omap->nports;
omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
- if (!omap->utmi_clk) {
+ omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+ omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+
+ if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) {
dev_err(dev, "Memory allocation failed\n");
ret = -ENOMEM;
goto err_mem;
@@ -578,7 +623,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)
}
for (i = 0; i < omap->nports; i++) {
- char clkname[] = "usb_host_hs_utmi_px_clk";
+ char clkname[30];
/* clock names are indexed from 1*/
snprintf(clkname, sizeof(clkname),
@@ -592,6 +637,20 @@ static int usbhs_omap_probe(struct platform_device *pdev)
if (IS_ERR(omap->utmi_clk[i]))
dev_dbg(dev, "Failed to get clock : %s : %ld\n",
clkname, PTR_ERR(omap->utmi_clk[i]));
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_hsic480m_p%d_clk", i + 1);
+ omap->hsic480m_clk[i] = clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic480m_clk[i]))
+ dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+ clkname, PTR_ERR(omap->hsic480m_clk[i]));
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_hsic60m_p%d_clk", i + 1);
+ omap->hsic60m_clk[i] = clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic60m_clk[i]))
+ dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+ clkname, PTR_ERR(omap->hsic60m_clk[i]));
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
@@ -635,9 +694,14 @@ static int usbhs_omap_probe(struct platform_device *pdev)
err_alloc:
omap_usbhs_deinit(&pdev->dev);
- for (i = 0; i < omap->nports; i++)
+ for (i = 0; i < omap->nports; i++) {
if (!IS_ERR(omap->utmi_clk[i]))
clk_put(omap->utmi_clk[i]);
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_put(omap->hsic60m_clk[i]);
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_put(omap->hsic480m_clk[i]);
+ }
clk_put(omap->init_60m_fclk);
@@ -676,9 +740,14 @@ static int usbhs_omap_remove(struct platform_device *pdev)
omap_usbhs_deinit(&pdev->dev);
- for (i = 0; i < omap->nports; i++)
+ for (i = 0; i < omap->nports; i++) {
if (!IS_ERR(omap->utmi_clk[i]))
clk_put(omap->utmi_clk[i]);
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_put(omap->hsic60m_clk[i]);
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_put(omap->hsic480m_clk[i]);
+ }
clk_put(omap->init_60m_fclk);
clk_put(omap->utmi_p1_gfclk);
--
1.7.4.1
^ permalink raw reply related
* [PATCH] net: fec: Add support for multiple phys on mdiobus
From: Florian Fainelli @ 2013-01-21 11:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50FD2172.2020608@grandegger.com>
On 01/21/2013 12:07 PM, Wolfgang Grandegger wrote:
> On 01/21/2013 11:07 AM, Sascha Hauer wrote:
>> On Mon, Jan 21, 2013 at 09:56:24AM +0100, Wolfgang Grandegger wrote:
>>> On 01/21/2013 09:37 AM, Sascha Hauer wrote:
>>>> There may be multiple phys on an mdio bus. This series adds support
>>>> for this to the fec driver. I recently had a board which has a switch
>>>> connected to the fec's mdio bus, so I had to pick the correct phy.
>>>
>>> Pick one PHY from a switch port? Well, does a PHY-less (or fixed-link)
>>> configuration for a switch not make more sense?
>>
>> Yes, you're probably right.
>>
>>> Various ARM Ethernet
>>> contoller drivers do not support it. I recently needed a hack for an
>>> AT91 board.
>>
>> I wonder how we want to proceed. Should there be a devicetree property
>> 'fixed-link' like done for fs_enet (and not recommended for new code,
>> stated in the comment above of_phy_connect_fixed_link)?
>
> Also the gianfar and ucc_geth drivers use this interface (via fixed
> link phy). I tried to use it for the AT91 macb driver but stopped
> quickly because the usage was not straight forward (too much code)...
> even if the idea of using a fake fixed-link phy is not bad.
>
>> Currently I have a property 'phy' in the fec binding which has a phandle
>> to a phy provided by the fec's mdio bus, but this could equally well
>
> But than the cable must be connected to the associated switch port.
>
>> point to a fixed dummy phy:
>>
>> phy = &fixed-phy;
>
> The link speed, full/half duplex and maybe some mroe parameter should
> be configurable via device tree.
>
>> Currently there seems to be no common convention for the devicetree how
>> to handle such situations, or am I missing something?
>
> That's also may impression. There seem to be a few more related hacks:
>
> $ find . -name '*.c'| xargs grep -i "phy-less"
> ./ethernet/amd/au1000_eth.c: netdev_info(dev, "using PHY-less setup\n");
> ./ethernet/amd/au1000_eth.c: } else { /* PHY-less op, assume full-duplex */
> ./ethernet/ibm/emac/core.c: /* PHY-less configuration.
> ./ethernet/ibm/emac/core.c: /* PHY-less configuration.
>
> I would prefer to handle the "fixed-link" property of the ethernet dt
> node directly in the driver with a generic helper function.
Is not what of_phy_connect_fixed_link() offer? I am not sure there can
be much done by an helper than that.
--
Florian
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox