* [PATCH v2 14/15] watchdog: orion: Allow to build on any Orion platform
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
After getting rid of all the mach-specific code, it's now possible
to allow builds in any Orion platform.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 5be6e91..1689f72 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -282,7 +282,7 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG
tristate "Orion watchdog"
- depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE
+ depends on PLAT_ORION
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 13/15] ARM: kirkwood: Add RSTOUT 'reg' entry to devicetree
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
In order to support multiplatform builds the watchdog devicetree binding
was modified and now the 'reg' property is specified to need two
entries. This commit adds the second entry as-per the new specification.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
arch/arm/boot/dts/kirkwood.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/kirkwood.dtsi b/arch/arm/boot/dts/kirkwood.dtsi
index 8b73c80..80a56b0 100644
--- a/arch/arm/boot/dts/kirkwood.dtsi
+++ b/arch/arm/boot/dts/kirkwood.dtsi
@@ -165,7 +165,7 @@
wdt: watchdog-timer at 20300 {
compatible = "marvell,orion-wdt";
- reg = <0x20300 0x28>;
+ reg = <0x20300 0x28>, <0x20108 0x4>;
interrupt-parent = <&bridge_intc>;
interrupts = <3>;
clocks = <&gate_clk 7>;
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 12/15] ARM: mvebu: Enable Armada 370/XP watchdog in the devicetree
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
Add the DT nodes to enable watchdog support available in Armada 370
and Armada XP SoCs.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
arch/arm/boot/dts/armada-370-xp.dtsi | 4 ++++
arch/arm/boot/dts/armada-370.dtsi | 5 +++++
arch/arm/boot/dts/armada-xp.dtsi | 6 ++++++
3 files changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi
index 7f10f62..96e0389 100644
--- a/arch/arm/boot/dts/armada-370-xp.dtsi
+++ b/arch/arm/boot/dts/armada-370-xp.dtsi
@@ -151,6 +151,10 @@
interrupts = <37>, <38>, <39>, <40>, <5>, <6>;
};
+ watchdog at 20300 {
+ reg = <0x20300 0x34>, <0x20704 0x4>;
+ };
+
sata at a0000 {
compatible = "marvell,orion-sata";
reg = <0xa0000 0x5000>;
diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi
index 7a4b82e..aebed9e 100644
--- a/arch/arm/boot/dts/armada-370.dtsi
+++ b/arch/arm/boot/dts/armada-370.dtsi
@@ -168,6 +168,11 @@
clocks = <&coreclk 2>;
};
+ watchdog at 20300 {
+ compatible = "marvell,armada-370-wdt";
+ clocks = <&coreclk 2>;
+ };
+
coreclk: mvebu-sar at 18230 {
compatible = "marvell,armada-370-core-clock";
reg = <0x18230 0x08>;
diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi
index 281c644..8c6c06c 100644
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -74,6 +74,12 @@
clock-names = "nbclk", "fixed";
};
+ watchdog at 20300 {
+ compatible = "marvell,armada-xp-wdt";
+ clocks = <&coreclk 2>, <&refclk>;
+ clock-names = "nbclk", "fixed";
+ };
+
coreclk: mvebu-sar at 18230 {
compatible = "marvell,armada-xp-core-clock";
reg = <0x18230 0x08>;
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 11/15] watchdog: orion: Add support for Armada 370 and Armada XP SoC
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
Using the added infrastructure for handling SoC differences,
this commit adds support for the watchdog controller available
in Armada 370 and Armada XP SoCs.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index d6009fe..8631756 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -31,6 +31,7 @@
* Watchdog timer block registers.
*/
#define TIMER_CTRL 0x0000
+#define TIMER_A370_STATUS 0x04
#define WDT_MAX_CYCLE_COUNT 0xffffffff
#define WDT_IN_USE 0
@@ -42,6 +43,12 @@
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
+#define WDT_A370_RATIO_MASK(v) ((v) << 16)
+#define WDT_A370_RATIO_SHIFT 5
+#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
+
+#define WDT_A370_EXPIRED BIT(31)
+
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
@@ -77,6 +84,40 @@ static int orion_wdt_clock_init(struct platform_device *pdev,
return 0;
}
+static int armada370_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
+{
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return -ENODEV;
+ clk_prepare_enable(dev->clk);
+
+ /* Setup watchdog input clock */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT),
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT));
+
+ dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO;
+ return 0;
+}
+
+static int armadaxp_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
+{
+ dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
+ if (IS_ERR(dev->clk))
+ return -ENODEV;
+ clk_prepare_enable(dev->clk);
+
+ /* Enable the fixed watchdog clock input */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_AXP_FIXED_ENABLE_BIT,
+ WDT_AXP_FIXED_ENABLE_BIT);
+
+ dev->clk_rate = clk_get_rate(dev->clk);
+ return 0;
+}
+
static int orion_wdt_ping(struct watchdog_device *wdt_dev)
{
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
@@ -90,6 +131,30 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
return 0;
}
+static int armada370_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ spin_lock(&dev->lock);
+
+ /* Set watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+
+ /* Clear the watchdog expiration bit */
+ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
+
+ /* Enable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
+
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
+
+ spin_unlock(&dev->lock);
+ return 0;
+}
+
static int orion_start(struct watchdog_device *wdt_dev)
{
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
@@ -184,11 +249,35 @@ static const struct orion_watchdog_data orion_data = {
.start = orion_start,
};
+static const struct orion_watchdog_data armada370_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armada370_wdt_clock_init,
+ .start = armada370_start,
+};
+
+static const struct orion_watchdog_data armadaxp_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armadaxp_wdt_clock_init,
+ .start = armada370_start,
+};
+
static const struct of_device_id orion_wdt_of_match_table[] = {
{
.compatible = "marvell,orion-wdt",
.data = &orion_data,
},
+ {
+ .compatible = "marvell,armada-370-wdt",
+ .data = &armada370_data,
+ },
+ {
+ .compatible = "marvell,armada-xp-wdt",
+ .data = &armadaxp_data,
+ },
{},
};
MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 10/15] watchdog: orion: Add per-compatible watchdog start implementation
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
To handle differences between SoCs this commit adds per-compatible
string start() function for the watchdog kick-off. This is preparation
work and makes no functionality changes to the current driver.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index e94bb97..d6009fe 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -53,6 +53,7 @@ struct orion_watchdog_data {
int rstout_enable_bit;
int (*clock_init) (struct platform_device *,
struct orion_watchdog *);
+ int (*start) (struct watchdog_device *);
};
struct orion_watchdog {
@@ -89,7 +90,7 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
return 0;
}
-static int orion_wdt_start(struct watchdog_device *wdt_dev)
+static int orion_start(struct watchdog_device *wdt_dev)
{
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
@@ -111,6 +112,14 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
return 0;
}
+static int orion_wdt_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ /* There are some per-SoC quirks to handle */
+ return dev->data->start(wdt_dev);
+}
+
static int orion_wdt_stop(struct watchdog_device *wdt_dev)
{
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
@@ -172,6 +181,7 @@ static const struct orion_watchdog_data orion_data = {
.wdt_enable_bit = BIT(4),
.wdt_counter_offset = 0x24,
.clock_init = orion_wdt_clock_init,
+ .start = orion_start,
};
static const struct of_device_id orion_wdt_of_match_table[] = {
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 09/15] watchdog: orion: Add per-compatible clock initialization
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
Following the introduction of the compatible-data field,
it's now possible to further abstract the clock initialization.
This will allow to support SoC with a different clock setup.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index a9632ba..e94bb97 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -45,10 +45,14 @@
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
+struct orion_watchdog;
+
struct orion_watchdog_data {
int wdt_counter_offset;
int wdt_enable_bit;
int rstout_enable_bit;
+ int (*clock_init) (struct platform_device *,
+ struct orion_watchdog *);
};
struct orion_watchdog {
@@ -61,6 +65,17 @@ struct orion_watchdog {
struct orion_watchdog_data *data;
};
+static int orion_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
+{
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return -ENODEV;
+ clk_prepare_enable(dev->clk);
+ dev->clk_rate = clk_get_rate(dev->clk);
+ return 0;
+}
+
static int orion_wdt_ping(struct watchdog_device *wdt_dev)
{
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
@@ -156,6 +171,7 @@ static const struct orion_watchdog_data orion_data = {
.rstout_enable_bit = BIT(1),
.wdt_enable_bit = BIT(4),
.wdt_counter_offset = 0x24,
+ .clock_init = orion_wdt_clock_init,
};
static const struct of_device_id orion_wdt_of_match_table[] = {
@@ -190,13 +206,6 @@ static int orion_wdt_probe(struct platform_device *pdev)
dev->wdt.min_timeout = 1;
dev->data = (struct orion_watchdog_data *)match->data;
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- dev_err(&pdev->dev, "Orion Watchdog missing clock\n");
- return -ENODEV;
- }
- clk_prepare_enable(dev->clk);
- dev->clk_rate = clk_get_rate(dev->clk);
spin_lock_init(&dev->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -214,6 +223,12 @@ static int orion_wdt_probe(struct platform_device *pdev)
if (!dev->rstout)
return -ENOMEM;
+ ret = dev->data->clock_init(pdev, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot initialize clock\n");
+ return ret;
+ }
+
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
/*
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 08/15] watchdog: orion: Introduce per-compatible of_device_id data
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
This commit adds an orion_watchdog_data structure to holda compatible-data
information. This allows to remove the driver-wide definition and to
future add support for multiple compatible-strings.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 63 +++++++++++++++++++++++++++++++++-----------
1 file changed, 47 insertions(+), 16 deletions(-)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index a95968c..a9632ba 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -25,23 +25,32 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/of_device.h>
/*
* Watchdog timer block registers.
*/
#define TIMER_CTRL 0x0000
-#define WDT_EN 0x0010
-#define WDT_VAL 0x0024
#define WDT_MAX_CYCLE_COUNT 0xffffffff
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
-#define WDT_RESET_OUT_EN BIT(1)
+#define WDT_A370_RATIO_MASK(v) ((v) << 16)
+#define WDT_A370_RATIO_SHIFT 5
+#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
+
+#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
+struct orion_watchdog_data {
+ int wdt_counter_offset;
+ int wdt_enable_bit;
+ int rstout_enable_bit;
+};
+
struct orion_watchdog {
struct watchdog_device wdt;
void __iomem *reg;
@@ -49,6 +58,7 @@ struct orion_watchdog {
spinlock_t lock;
unsigned long clk_rate;
struct clk *clk;
+ struct orion_watchdog_data *data;
};
static int orion_wdt_ping(struct watchdog_device *wdt_dev)
@@ -57,7 +67,8 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
spin_lock(&dev->lock);
/* Reload watchdog duration */
- writel(dev->clk_rate * wdt_dev->timeout, dev->reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
spin_unlock(&dev->lock);
return 0;
@@ -70,13 +81,16 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
spin_lock(&dev->lock);
/* Set watchdog duration */
- writel(dev->clk_rate * wdt_dev->timeout, dev->reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
/* Enable watchdog timer */
- atomic_io_modify(dev->reg + TIMER_CTRL, WDT_EN, WDT_EN);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
/* Enable reset on watchdog */
- atomic_io_modify(dev->rstout, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
spin_unlock(&dev->lock);
return 0;
@@ -89,10 +103,10 @@ static int orion_wdt_stop(struct watchdog_device *wdt_dev)
spin_lock(&dev->lock);
/* Disable reset on watchdog */
- atomic_io_modify(dev->rstout, WDT_RESET_OUT_EN, 0);
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
/* Disable watchdog timer */
- atomic_io_modify(dev->reg + TIMER_CTRL, WDT_EN, 0);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
spin_unlock(&dev->lock);
return 0;
@@ -104,7 +118,8 @@ static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
unsigned int time_left;
spin_lock(&dev->lock);
- time_left = readl(dev->reg + WDT_VAL) / dev->clk_rate;
+ time_left = readl(dev->reg + dev->data->wdt_counter_offset);
+ time_left /= dev->clk_rate;
spin_unlock(&dev->lock);
return time_left;
@@ -137,9 +152,25 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid)
return IRQ_HANDLED;
}
+static const struct orion_watchdog_data orion_data = {
+ .rstout_enable_bit = BIT(1),
+ .wdt_enable_bit = BIT(4),
+ .wdt_counter_offset = 0x24,
+};
+
+static const struct of_device_id orion_wdt_of_match_table[] = {
+ {
+ .compatible = "marvell,orion-wdt",
+ .data = &orion_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
+
static int orion_wdt_probe(struct platform_device *pdev)
{
struct orion_watchdog *dev;
+ const struct of_device_id *match;
unsigned int wdt_max_duration; /* (seconds) */
struct resource *res;
int ret, irq;
@@ -149,9 +180,15 @@ static int orion_wdt_probe(struct platform_device *pdev)
if (!dev)
return -ENOMEM;
+ match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
+ if (!match)
+ /* Default legacy match */
+ match = &orion_wdt_of_match_table[0];
+
dev->wdt.info = &orion_wdt_info;
dev->wdt.ops = &orion_wdt_ops;
dev->wdt.min_timeout = 1;
+ dev->data = (struct orion_watchdog_data *)match->data;
dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
@@ -226,12 +263,6 @@ static void orion_wdt_shutdown(struct platform_device *pdev)
orion_wdt_stop(wdt_dev);
}
-static const struct of_device_id orion_wdt_of_match_table[] = {
- { .compatible = "marvell,orion-wdt", },
- {},
-};
-MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
-
static struct platform_driver orion_wdt_driver = {
.probe = orion_wdt_probe,
.remove = orion_wdt_remove,
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 07/15] watchdog: orion: Introduce an orion_watchdog device structure
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
In order to prepare to support multiple compatible-strings, this
commit adds a device structure to hold the driver's state.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 114 ++++++++++++++++++++++++++-----------------
1 file changed, 68 insertions(+), 46 deletions(-)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 33e323d..a95968c 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -41,62 +41,71 @@
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
-static unsigned int wdt_max_duration; /* (seconds) */
-static struct clk *clk;
-static unsigned int wdt_tclk;
-static void __iomem *wdt_reg;
-static void __iomem *wdt_rstout;
-static DEFINE_SPINLOCK(wdt_lock);
+
+struct orion_watchdog {
+ struct watchdog_device wdt;
+ void __iomem *reg;
+ void __iomem *rstout;
+ spinlock_t lock;
+ unsigned long clk_rate;
+ struct clk *clk;
+};
static int orion_wdt_ping(struct watchdog_device *wdt_dev)
{
- spin_lock(&wdt_lock);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ spin_lock(&dev->lock);
/* Reload watchdog duration */
- writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout, dev->reg + WDT_VAL);
- spin_unlock(&wdt_lock);
+ spin_unlock(&dev->lock);
return 0;
}
static int orion_wdt_start(struct watchdog_device *wdt_dev)
{
- spin_lock(&wdt_lock);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ spin_lock(&dev->lock);
/* Set watchdog duration */
- writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout, dev->reg + WDT_VAL);
/* Enable watchdog timer */
- atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, WDT_EN);
+ atomic_io_modify(dev->reg + TIMER_CTRL, WDT_EN, WDT_EN);
/* Enable reset on watchdog */
- atomic_io_modify(wdt_rstout, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
+ atomic_io_modify(dev->rstout, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
- spin_unlock(&wdt_lock);
+ spin_unlock(&dev->lock);
return 0;
}
static int orion_wdt_stop(struct watchdog_device *wdt_dev)
{
- spin_lock(&wdt_lock);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ spin_lock(&dev->lock);
/* Disable reset on watchdog */
- atomic_io_modify(wdt_rstout, WDT_RESET_OUT_EN, 0);
+ atomic_io_modify(dev->rstout, WDT_RESET_OUT_EN, 0);
/* Disable watchdog timer */
- atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, 0);
+ atomic_io_modify(dev->reg + TIMER_CTRL, WDT_EN, 0);
- spin_unlock(&wdt_lock);
+ spin_unlock(&dev->lock);
return 0;
}
static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
unsigned int time_left;
- spin_lock(&wdt_lock);
- time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk;
- spin_unlock(&wdt_lock);
+ spin_lock(&dev->lock);
+ time_left = readl(dev->reg + WDT_VAL) / dev->clk_rate;
+ spin_unlock(&dev->lock);
return time_left;
}
@@ -122,12 +131,6 @@ static const struct watchdog_ops orion_wdt_ops = {
.get_timeleft = orion_wdt_get_timeleft,
};
-static struct watchdog_device orion_wdt = {
- .info = &orion_wdt_info,
- .ops = &orion_wdt_ops,
- .min_timeout = 1,
-};
-
static irqreturn_t orion_wdt_irq(int irq, void *devid)
{
panic("Watchdog Timeout");
@@ -136,29 +139,42 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid)
static int orion_wdt_probe(struct platform_device *pdev)
{
+ struct orion_watchdog *dev;
+ unsigned int wdt_max_duration; /* (seconds) */
struct resource *res;
int ret, irq;
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->wdt.info = &orion_wdt_info;
+ dev->wdt.ops = &orion_wdt_ops;
+ dev->wdt.min_timeout = 1;
+
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "Orion Watchdog missing clock\n");
return -ENODEV;
}
- clk_prepare_enable(clk);
- wdt_tclk = clk_get_rate(clk);
+ clk_prepare_enable(dev->clk);
+ dev->clk_rate = clk_get_rate(dev->clk);
+ spin_lock_init(&dev->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!wdt_reg)
+ dev->reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->reg)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
return -ENODEV;
- wdt_rstout = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!wdt_rstout)
+ dev->rstout = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!dev->rstout)
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
@@ -174,34 +190,40 @@ static int orion_wdt_probe(struct platform_device *pdev)
}
}
- wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
+ wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
- orion_wdt.timeout = wdt_max_duration;
- orion_wdt.max_timeout = wdt_max_duration;
- watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
+ dev->wdt.timeout = wdt_max_duration;
+ dev->wdt.max_timeout = wdt_max_duration;
+ watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
- watchdog_set_nowayout(&orion_wdt, nowayout);
- ret = watchdog_register_device(&orion_wdt);
+ platform_set_drvdata(pdev, &dev->wdt);
+ watchdog_set_drvdata(&dev->wdt, dev);
+ watchdog_set_nowayout(&dev->wdt, nowayout);
+ ret = watchdog_register_device(&dev->wdt);
if (ret) {
- clk_disable_unprepare(clk);
+ clk_disable_unprepare(dev->clk);
return ret;
}
pr_info("Initial timeout %d sec%s\n",
- orion_wdt.timeout, nowayout ? ", nowayout" : "");
+ dev->wdt.timeout, nowayout ? ", nowayout" : "");
return 0;
}
static int orion_wdt_remove(struct platform_device *pdev)
{
- watchdog_unregister_device(&orion_wdt);
- clk_disable_unprepare(clk);
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ watchdog_unregister_device(wdt_dev);
+ clk_disable_unprepare(dev->clk);
return 0;
}
static void orion_wdt_shutdown(struct platform_device *pdev)
{
- orion_wdt_stop(&orion_wdt);
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ orion_wdt_stop(wdt_dev);
}
static const struct of_device_id orion_wdt_of_match_table[] = {
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 06/15] watchdog: orion: Remove unneeded BRIDGE_CAUSE clear
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
After adding the IRQ request, the BRIDGE_CAUSE bit should be cleared by the
bridge interrupt controller. There's no longer a need to do it in the watchdog
driver, so we can simply remove it.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 9c7d695..33e323d 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -25,7 +25,6 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <mach/bridge-regs.h>
/*
* Watchdog timer block registers.
@@ -39,7 +38,6 @@
#define WDT_OK_TO_CLOSE 1
#define WDT_RESET_OUT_EN BIT(1)
-#define WDT_INT_REQ BIT(3)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
@@ -68,9 +66,6 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
/* Set watchdog duration */
writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
- /* Clear watchdog timer interrupt */
- writel(~WDT_INT_REQ, BRIDGE_CAUSE);
-
/* Enable watchdog timer */
atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, WDT_EN);
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 05/15] watchdog: orion: Make RSTOUT register a separate resource
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
In order to support other SoC, it's required to distinguish
the 'control' timer register, from the 'rstout' register
that enables system reset on watchdog expiration.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
Documentation/devicetree/bindings/watchdog/marvel.txt | 6 ++++--
arch/arm/mach-dove/include/mach/bridge-regs.h | 1 +
arch/arm/mach-kirkwood/include/mach/bridge-regs.h | 1 +
arch/arm/mach-mv78xx0/include/mach/bridge-regs.h | 1 +
arch/arm/mach-orion5x/include/mach/bridge-regs.h | 1 +
arch/arm/plat-orion/common.c | 10 ++++++----
drivers/watchdog/orion_wdt.c | 12 ++++++++++--
7 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/watchdog/marvel.txt b/Documentation/devicetree/bindings/watchdog/marvel.txt
index 0731fbd..1544fe9 100644
--- a/Documentation/devicetree/bindings/watchdog/marvel.txt
+++ b/Documentation/devicetree/bindings/watchdog/marvel.txt
@@ -3,7 +3,9 @@
Required Properties:
- Compatibility : "marvell,orion-wdt"
-- reg : Address of the timer registers
+- reg : Should contain two entries: first one with the
+ timer control address, second one with the
+ rstout enable address.
Optional properties:
@@ -14,7 +16,7 @@ Example:
wdt at 20300 {
compatible = "marvell,orion-wdt";
- reg = <0x20300 0x28>;
+ reg = <0x20300 0x28>, <0x20108 0x4>;
interrupts = <3>;
timeout-sec = <10>;
status = "okay";
diff --git a/arch/arm/mach-dove/include/mach/bridge-regs.h b/arch/arm/mach-dove/include/mach/bridge-regs.h
index 5362df3..f4a5b34 100644
--- a/arch/arm/mach-dove/include/mach/bridge-regs.h
+++ b/arch/arm/mach-dove/include/mach/bridge-regs.h
@@ -21,6 +21,7 @@
#define CPU_CTRL_PCIE1_LINK 0x00000008
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
+#define RSTOUTn_MASK_PHYS (BRIDGE_PHYS_BASE + 0x0108)
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
diff --git a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
index 8b9d1c9..60f6421 100644
--- a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
+++ b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
@@ -21,6 +21,7 @@
#define CPU_RESET 0x00000002
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
+#define RSTOUTn_MASK_PHYS (BRIDGE_PHYS_BASE + 0x0108)
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
diff --git a/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h b/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h
index 5f03484..e20d6da 100644
--- a/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h
+++ b/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h
@@ -15,6 +15,7 @@
#define L2_WRITETHROUGH 0x00020000
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
+#define RSTOUTn_MASK_PHYS (BRIDGE_PHYS_BASE + 0x0108)
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
diff --git a/arch/arm/mach-orion5x/include/mach/bridge-regs.h b/arch/arm/mach-orion5x/include/mach/bridge-regs.h
index f727d03..5766e3f 100644
--- a/arch/arm/mach-orion5x/include/mach/bridge-regs.h
+++ b/arch/arm/mach-orion5x/include/mach/bridge-regs.h
@@ -18,6 +18,7 @@
#define CPU_CTRL (ORION5X_BRIDGE_VIRT_BASE + 0x104)
#define RSTOUTn_MASK (ORION5X_BRIDGE_VIRT_BASE + 0x108)
+#define RSTOUTn_MASK_PHYS (ORION5X_BRIDGE_PHYS_BASE + 0x108)
#define CPU_SOFT_RESET (ORION5X_BRIDGE_VIRT_BASE + 0x10c)
diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c
index c66d163..3375037 100644
--- a/arch/arm/plat-orion/common.c
+++ b/arch/arm/plat-orion/common.c
@@ -594,14 +594,16 @@ void __init orion_spi_1_init(unsigned long mapbase)
/*****************************************************************************
* Watchdog
****************************************************************************/
-static struct resource orion_wdt_resource =
- DEFINE_RES_MEM(TIMER_PHYS_BASE, 0x28);
+static struct resource orion_wdt_resource[] = {
+ DEFINE_RES_MEM(TIMER_PHYS_BASE, 0x04),
+ DEFINE_RES_MEM(RSTOUTn_MASK_PHYS, 0x04),
+};
static struct platform_device orion_wdt_device = {
.name = "orion_wdt",
.id = -1,
- .num_resources = 1,
- .resource = &orion_wdt_resource,
+ .num_resources = ARRAY_SIZE(orion_wdt_resource),
+ .resource = orion_wdt_resource,
};
void __init orion_wdt_init(void)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 06e766f..9c7d695 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -47,6 +47,7 @@ static unsigned int wdt_max_duration; /* (seconds) */
static struct clk *clk;
static unsigned int wdt_tclk;
static void __iomem *wdt_reg;
+static void __iomem *wdt_rstout;
static DEFINE_SPINLOCK(wdt_lock);
static int orion_wdt_ping(struct watchdog_device *wdt_dev)
@@ -74,7 +75,7 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, WDT_EN);
/* Enable reset on watchdog */
- atomic_io_modify(RSTOUTn_MASK, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
+ atomic_io_modify(wdt_rstout, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
spin_unlock(&wdt_lock);
return 0;
@@ -85,7 +86,7 @@ static int orion_wdt_stop(struct watchdog_device *wdt_dev)
spin_lock(&wdt_lock);
/* Disable reset on watchdog */
- atomic_io_modify(RSTOUTn_MASK, WDT_RESET_OUT_EN, 0);
+ atomic_io_modify(wdt_rstout, WDT_RESET_OUT_EN, 0);
/* Disable watchdog timer */
atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, 0);
@@ -158,6 +159,13 @@ static int orion_wdt_probe(struct platform_device *pdev)
if (!wdt_reg)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -ENODEV;
+ wdt_rstout = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!wdt_rstout)
+ return -ENOMEM;
+
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
/*
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 04/15] watchdog: orion: Handle IRQ
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
DT-enabled where an irqchip driver for the brigde interrupt controller is
available can handle the watchdog IRQ properly. Therefore, we request
the interruption and add a dummy handler that merely calls panic().
This is done in order to have an initial 'ack' of the interruption,
which clears the watchdog state.
Furthermore, since some platforms don't have such IRQ, this commit
makes the interruption specification optional.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
.../devicetree/bindings/watchdog/marvel.txt | 2 ++
drivers/watchdog/orion_wdt.c | 22 +++++++++++++++++++++-
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/watchdog/marvel.txt b/Documentation/devicetree/bindings/watchdog/marvel.txt
index 5dc8d30..0731fbd 100644
--- a/Documentation/devicetree/bindings/watchdog/marvel.txt
+++ b/Documentation/devicetree/bindings/watchdog/marvel.txt
@@ -7,6 +7,7 @@ Required Properties:
Optional properties:
+- interrupts : Contains the IRQ for watchdog expiration
- timeout-sec : Contains the watchdog timeout in seconds
Example:
@@ -14,6 +15,7 @@ Example:
wdt at 20300 {
compatible = "marvell,orion-wdt";
reg = <0x20300 0x28>;
+ interrupts = <3>;
timeout-sec = <10>;
status = "okay";
};
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index cf64510..06e766f 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
@@ -131,10 +132,16 @@ static struct watchdog_device orion_wdt = {
.min_timeout = 1,
};
+static irqreturn_t orion_wdt_irq(int irq, void *devid)
+{
+ panic("Watchdog Timeout");
+ return IRQ_HANDLED;
+}
+
static int orion_wdt_probe(struct platform_device *pdev)
{
struct resource *res;
- int ret;
+ int ret, irq;
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
@@ -151,6 +158,19 @@ static int orion_wdt_probe(struct platform_device *pdev)
if (!wdt_reg)
return -ENOMEM;
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ /*
+ * Not all supported platforms specify an interruption for the
+ * watchdog, so let's make it optional.
+ */
+ ret = request_irq(irq, orion_wdt_irq, 0, pdev->name, NULL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+ }
+
wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
orion_wdt.timeout = wdt_max_duration;
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 03/15] watchdog: orion: Use atomic access for shared registers
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
Since the timer control register is shared with the clocksource driver,
use the recently introduced atomic_io_clear_set() to access such register.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/watchdog/orion_wdt.c | 20 ++++----------------
1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index f7722a4..cf64510 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -61,8 +61,6 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
static int orion_wdt_start(struct watchdog_device *wdt_dev)
{
- u32 reg;
-
spin_lock(&wdt_lock);
/* Set watchdog duration */
@@ -72,14 +70,10 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
writel(~WDT_INT_REQ, BRIDGE_CAUSE);
/* Enable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg |= WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, WDT_EN);
/* Enable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg |= WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ atomic_io_modify(RSTOUTn_MASK, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
spin_unlock(&wdt_lock);
return 0;
@@ -87,19 +81,13 @@ static int orion_wdt_start(struct watchdog_device *wdt_dev)
static int orion_wdt_stop(struct watchdog_device *wdt_dev)
{
- u32 reg;
-
spin_lock(&wdt_lock);
/* Disable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg &= ~WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ atomic_io_modify(RSTOUTn_MASK, WDT_RESET_OUT_EN, 0);
/* Disable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg &= ~WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, 0);
spin_unlock(&wdt_lock);
return 0;
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 02/15] clocksource: orion: Use atomic access for shared registers
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
Replace the driver-specific thread-safe shared register API
by the recently introduced atomic_io_clear_set().
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
drivers/clocksource/time-orion.c | 28 ++++++++++------------------
1 file changed, 10 insertions(+), 18 deletions(-)
diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
index 9c7f018..3f14e56 100644
--- a/drivers/clocksource/time-orion.c
+++ b/drivers/clocksource/time-orion.c
@@ -35,20 +35,6 @@
#define ORION_ONESHOT_MAX 0xfffffffe
static void __iomem *timer_base;
-static DEFINE_SPINLOCK(timer_ctrl_lock);
-
-/*
- * Thread-safe access to TIMER_CTRL register
- * (shared with watchdog timer)
- */
-void orion_timer_ctrl_clrset(u32 clr, u32 set)
-{
- spin_lock(&timer_ctrl_lock);
- writel((readl(timer_base + TIMER_CTRL) & ~clr) | set,
- timer_base + TIMER_CTRL);
- spin_unlock(&timer_ctrl_lock);
-}
-EXPORT_SYMBOL(orion_timer_ctrl_clrset);
/*
* Free-running clocksource handling.
@@ -68,7 +54,8 @@ static int orion_clkevt_next_event(unsigned long delta,
{
/* setup and enable one-shot timer */
writel(delta, timer_base + TIMER1_VAL);
- orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN);
+ atomic_io_modify(timer_base + TIMER_CTRL,
+ TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN);
return 0;
}
@@ -80,10 +67,13 @@ static void orion_clkevt_mode(enum clock_event_mode mode,
/* setup and enable periodic timer at 1/HZ intervals */
writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD);
writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL);
- orion_timer_ctrl_clrset(0, TIMER1_RELOAD_EN | TIMER1_EN);
+ atomic_io_modify(timer_base + TIMER_CTRL,
+ TIMER1_RELOAD_EN | TIMER1_EN,
+ TIMER1_RELOAD_EN | TIMER1_EN);
} else {
/* disable timer */
- orion_timer_ctrl_clrset(TIMER1_RELOAD_EN | TIMER1_EN, 0);
+ atomic_io_modify(timer_base + TIMER_CTRL,
+ TIMER1_RELOAD_EN | TIMER1_EN, 0);
}
}
@@ -131,7 +121,9 @@ static void __init orion_timer_init(struct device_node *np)
/* setup timer0 as free-running clocksource */
writel(~0, timer_base + TIMER0_VAL);
writel(~0, timer_base + TIMER0_RELOAD);
- orion_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | TIMER0_EN);
+ atomic_io_modify(timer_base + TIMER_CTRL,
+ TIMER0_RELOAD_EN | TIMER0_EN,
+ TIMER0_RELOAD_EN | TIMER0_EN);
clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource",
clk_get_rate(clk), 300, 32,
clocksource_mmio_readl_down);
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 01/15] ARM: Introduce atomic MMIO modify
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390295561-3466-1-git-send-email-ezequiel.garcia@free-electrons.com>
Some SoC have MMIO regions that are shared across orthogonal
subsystems. This commit implements a possible solution for the
thread-safe access of such regions through a spinlock-protected API.
Concurrent access is protected with a single spinlock for the
entire MMIO address space. While this protects shared-registers,
it also serializes access to unrelated/unshared registers.
We add relaxed and non-relaxed variants, by using writel_relaxed and writel,
respectively. The rationale for this is that some users may not require
register write completion but only thread-safe access to a register.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
Submitted to ARM patch tracker:
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=7930/1
arch/arm/include/asm/io.h | 6 ++++++
arch/arm/kernel/io.c | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index fbeb39c..8aa4cca 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -38,6 +38,12 @@
#define isa_bus_to_virt phys_to_virt
/*
+ * Atomic MMIO-wide IO modify
+ */
+extern void atomic_io_modify(void __iomem *reg, u32 mask, u32 set);
+extern void atomic_io_modify_relaxed(void __iomem *reg, u32 mask, u32 set);
+
+/*
* Generic IO read/write. These perform native-endian accesses. Note
* that some architectures will want to re-define __raw_{read,write}w.
*/
diff --git a/arch/arm/kernel/io.c b/arch/arm/kernel/io.c
index dcd5b4d..9203cf8 100644
--- a/arch/arm/kernel/io.c
+++ b/arch/arm/kernel/io.c
@@ -1,6 +1,41 @@
#include <linux/export.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
+
+static DEFINE_RAW_SPINLOCK(__io_lock);
+
+/*
+ * Generic atomic MMIO modify.
+ *
+ * Allows thread-safe access to registers shared by unrelated subsystems.
+ * The access is protected by a single MMIO-wide lock.
+ */
+void atomic_io_modify_relaxed(void __iomem *reg, u32 mask, u32 set)
+{
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&__io_lock, flags);
+ value = readl_relaxed(reg) & ~mask;
+ value |= (set & mask);
+ writel_relaxed(value, reg);
+ raw_spin_unlock_irqrestore(&__io_lock, flags);
+}
+EXPORT_SYMBOL(atomic_io_modify_relaxed);
+
+void atomic_io_modify(void __iomem *reg, u32 mask, u32 set)
+{
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&__io_lock, flags);
+ value = readl_relaxed(reg) & ~mask;
+ value |= (set & mask);
+ writel(value, reg);
+ raw_spin_unlock_irqrestore(&__io_lock, flags);
+}
+EXPORT_SYMBOL(atomic_io_modify);
/*
* Copy data from IO memory space to "real" memory space.
--
1.8.1.5
^ permalink raw reply related
* [PATCH v2 00/15] Armada 370/XP watchdog support
From: Ezequiel Garcia @ 2014-01-21 9:12 UTC (permalink / raw)
To: linux-arm-kernel
The second patchset to extend Orion watchdog driver adding support for
Armada 370/XP SoC.
On this series I've tried to address all the issues raised when the previous
patchset was submitted. See here [1] for the previous thread.
I suggest that you go through the above thread to get the full picture.
Anyway, long story short, the main two issues are the ones preventing
multiplatform building:
* Watchdog RSTOUT enable.
While v1 enabled the RSTOUT at each machine initialization, Jason Gunthorpe
later pointed out [2] that such enabling might lead to a spurious watchdog
trigger, in the event of the watchdog expired event not being cleared.
Therefore, the current patchset adds RSTOUT as a second address resource
(or 'reg' entry in devicetree words) to allow different platforms specify
the corresponding address of the register. This change allows to build the
driver on multiplatforms builds as helps remove a mach-specific header.
The drawback of this is that the DT backwards compatibility gets broken;
this was timely discussed but no better solution was achieved or proposed.
* BRIDGE CAUSE clear removal
The watchdog cause clear should be done by the bridge irqchip driver, so
it's fine to remove it from the watchdog driver and instead request the
interruption.
However, there are still a few platforms (orion5x, and legacy
kirkwood/dove) that doesn't have this bridge irqchip support enabled.
On these platforms the bridge cause clear is simply *not* done.
If we are paranoid about this, maybe we can simply add the clear on each
mach-xxx/irq.c, together with the other irq is initialization.
The rest of the work is mostly the same as v1.
Patches 1-3 introduces the Atomic I/O API needed to access shared registers;
this patch has been properly discussed elsewhere and has been submitted to
the ARM patch tracker for its inclusion in v3.14.
Patches 2-3 uses the atomic I/O API on the clocksource and watchdog drivers.
The orion clocksource patch is submitted here because the register is shared
with the watchdog.
Patch 4 adds the interrupt handling as already explained above.
Patch 5-6 peform the already discussed RSTOUT and BRIDGE CAUSE changes and
allows multiplatform build.
Patches 7-10 extends the orion_wdt driver to support other SoCs.
Patch 11 adds the Armada 370/XP support.
Patches 12-13 updates the devicetree files, patch 14 allows to build
the driver on any Orion platform and finally patch 15 updates the
defconfig.
This series is based on v3.13-rc8 and has been tested on:
* Kirkwood Openblocks A6
* Armada 370 Reference Design
[1] http://comments.gmane.org/gmane.linux.ports.arm.kernel/263459
[2] http://permalink.gmane.org/gmane.linux.ports.arm.kernel/263544
Ezequiel Garcia (15):
ARM: Introduce atomic MMIO modify
clocksource: orion: Use atomic access for shared registers
watchdog: orion: Use atomic access for shared registers
watchdog: orion: Handle IRQ
watchdog: orion: Make RSTOUT register a separate resource
watchdog: orion: Remove unneeded BRIDGE_CAUSE clear
watchdog: orion: Introduce an orion_watchdog device structure
watchdog: orion: Introduce per-compatible of_device_id data
watchdog: orion: Add per-compatible clock initialization
watchdog: orion: Add per-compatible watchdog start implementation
watchdog: orion: Add support for Armada 370 and Armada XP SoC
ARM: mvebu: Enable Armada 370/XP watchdog in the devicetree
ARM: kirkwood: Add RSTOUT 'reg' entry to devicetree
watchdog: orion: Allow to build on any Orion platform
ARM: mvebu: Enable watchdog support in defconfig
.../devicetree/bindings/watchdog/marvel.txt | 8 +-
arch/arm/boot/dts/armada-370-xp.dtsi | 4 +
arch/arm/boot/dts/armada-370.dtsi | 5 +
arch/arm/boot/dts/armada-xp.dtsi | 6 +
arch/arm/boot/dts/kirkwood.dtsi | 2 +-
arch/arm/configs/mvebu_defconfig | 2 +
arch/arm/include/asm/io.h | 6 +
arch/arm/kernel/io.c | 35 +++
arch/arm/mach-dove/include/mach/bridge-regs.h | 1 +
arch/arm/mach-kirkwood/include/mach/bridge-regs.h | 1 +
arch/arm/mach-mv78xx0/include/mach/bridge-regs.h | 1 +
arch/arm/mach-orion5x/include/mach/bridge-regs.h | 1 +
arch/arm/plat-orion/common.c | 10 +-
drivers/clocksource/time-orion.c | 28 +-
drivers/watchdog/Kconfig | 2 +-
drivers/watchdog/orion_wdt.c | 316 ++++++++++++++++-----
16 files changed, 333 insertions(+), 95 deletions(-)
--
1.8.1.5
^ permalink raw reply
* [PATCH 1/2] USB: at91: fix the number of endpoint parameter
From: Nicolas Ferre @ 2014-01-21 9:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52DE2C05.9060800@atmel.com>
On 21/01/2014 09:12, Bo Shen :
> Hi J,
>
> On 01/21/2014 01:49 PM, Jean-Christophe PLAGNIOL-VILLARD wrote:
>> On 11:39 Mon 20 Jan , Bo Shen wrote:
>>> Hi J,
>>>
>>> On 01/18/2014 01:20 PM, Jean-Christophe PLAGNIOL-VILLARD wrote:
>>>> On 10:59 Fri 17 Jan , Bo Shen wrote:
>>>>> In sama5d3 SoC, there are 16 endpoints. As the USBA_NR_ENDPOINTS
>>>>> is only 7. So, fix it for sama5d3 SoC using the udc->num_ep.
>>>>>
>>>>> Signed-off-by: Bo Shen <voice.shen@atmel.com>
>>>>> ---
>>>>>
>>>>> drivers/usb/gadget/atmel_usba_udc.c | 2 +-
>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
>>>>> index 2cb52e0..7e67a81 100644
>>>>> --- a/drivers/usb/gadget/atmel_usba_udc.c
>>>>> +++ b/drivers/usb/gadget/atmel_usba_udc.c
>>>>> @@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
>>>>> if (ep_status) {
>>>>> int i;
>>>>>
>>>>> - for (i = 0; i < USBA_NR_ENDPOINTS; i++)
>>>>> + for (i = 0; i < udc->num_ep; i++)
>>>>
>>>> no the limit need to specified in the driver as a checkpoint by the compatible
>>>> or platform driver id
>>>
>>> You mean, we should not trust the data passed from dt node or
>>> platform data? Or do you think we should do double confirm?
>>
>> no base on the driver name or the compatible you will known the MAX EP
>>
>> not based on the dt ep description
>>
>> as we do on pinctrl-at91
>
> I am sorry, I am not fully get it after reading the code of
> pinctrl-at91.c, can you give the example code in pinctrl-at91.c?
>
> Btw, the udc->num_ep is get from the following code.
> for dt
> --->8---
> while ((pp = of_get_next_child(np, pp)))
> udc->num_ep++;
> ---<8---
>
> for non-dt
> --->8---
> udc->num_ep = pdata->num_ep;
> ---8<---
It seems to me pretty valid to use num_ep in this driver and not have to
rely on another compatibility string just for this.
The information is here, it is retrieved pretty cleanly so I vote for a
simple use of it: if we introduce another information we will have to
double check the cross errors that would happen...
Bye,
>>>>> if (ep_status & (1 << i)) {
>>>>> if (ep_is_control(&udc->usba_ep[i]))
>>>>> usba_control_irq(udc, &udc->usba_ep[i]);
>>>>> --
>>>>> 1.8.5.2
>>>>>
>>>
>>> Best Regards,
>>> Bo Shen
>
> Best Regards,
> Bo Shen
>
--
Nicolas Ferre
^ permalink raw reply
* Question: xilinx_zynq_defconfig
From: Michal Simek @ 2014-01-21 9:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140121173442.328B.AA925319@jp.panasonic.com>
Hi Masahiro,
zynq is enabled in multi_v7 defconfig that's why I don't have
a need to add defconfig just for zynq. Even one defconfig per soc
is permitted by maintainers.
In xilinx repo we keep more defconfigs.
Thanks,
Michal
On 01/21/2014 09:34 AM, Masahiro Yamada wrote:
> Hello Michal,
>
> I have a Zynq ZC706 board.
> I have a simple question about Zynq support.
>
> There exist device tree sources for Zynq in the mainline of Kernel,
> whereas xilinx_zynq_defconfig is missing.
> (I know it is maintained in a Xilinx repository.)
>
> Why have the defconfig files not been merged yet?
>
>
> Best Regards
> Masahiro Yamada
>
--
Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 263 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140121/8ff1f469/attachment-0001.sig>
^ permalink raw reply
* [RFC PATCH 1/1] of/irq: create interrupts-extended-2 property
From: David Gibson @ 2014-01-21 8:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOesGMjrLs9eEPurjUpOfgyexnc__FJkRYmAR84_f7C15HH07Q@mail.gmail.com>
On Mon, Jan 20, 2014 at 05:03:23PM -0800, Olof Johansson wrote:
> On Mon, Jan 20, 2014 at 2:47 PM, Grant Likely <grant.likely@linaro.org> wrote:
> > On Wed, 15 Jan 2014 16:12:24 +0000, Mark Rutland <mark.rutland@arm.com> wrote:
> >> >
> >> > Another, more invasive option would be extend the dts syntax and teach
> >> > dtc to handle property appending. Then the soc dts could stay as it is,
> >> > and the board dts could have something like:
> >> >
> >> > /append-property/ interrupts = <&intc1 6 1>;
> >> > /append-property/ interrupt-names = "board-specific-irq";
> >> >
> >> > Both these options solve the issue at the source, are general to other
> >> > properties, and allow more than one level of hierarchy (the proposed
> >> > interrupts-extended-2 only allows one level).
> >>
> >> I've just had a go at implementing the append-property mechanism above
> >> in dtc, and it was far easier than I expected (patch below).
> >>
> >> Does anyone have any issues with the /append-property/ idea?
> >
> > I think that is reasonable.
>
>
> The main problem with this (same for clocks) is if you need to append
> something with a name when the original didn't have any.
>
> Reordering entries might not work for interrupts, since the bindings
> might have requirements on order.
>
> I'm not aware of a good solution for this. Suggestions welcome.
So, in principle my preferred way to handle things like this would be
to add richer expression support, including a token for "previous
value" when overlaying.
But although I've recently had another look at it, that's still a ways
off, so I wouldn't object to another approach as a stopgap, as long as
its not too hideous.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140121/a8e3ce63/attachment.sig>
^ permalink raw reply
* [PATCH v2 0/5] arm64: advertise availability of CRC and crypto instructions
From: Ard Biesheuvel @ 2014-01-21 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140120173838.GD29971@arm.com>
@Russell,
May we have your comments/opinion please on this alternative approach?
Regards,
Ard.
On 20 January 2014 18:38, Catalin Marinas <catalin.marinas@arm.com> wrote:
> On Mon, Dec 23, 2013 at 02:06:27PM +0000, Ard Biesheuvel wrote:
>> This series is a followup to the patch that was recently merged by Catalin that
>> allocates hwcaps bits for CRC and Crypto Extensions instructions so userland can
>> discover whether the current CPU has any of those capabilities.
>>
>> Patch #1 enables ARM support for the ELF_HWCAP2/AT_HWCAP2 ELF auxv entry that
>> was recently added to the kernel and glibc (2.18). It extends the feature bit
>> space to 64 bits (on 32-bit architectures)
>>
>> Patch #2 adds generic support for ELF_HWCAP2/AT_HWCAP2 to the 32-bit ELF compat
>> mode for 64-bit architectures.
>>
>> Patch #3 adds support for ELF_HWCAP2/AT_HWCAP2 to arm64's 32-bit ELF compat mode
>>
>> Patch #4 allocates the HWCAP2 bits in the arch/arm tree. This is necessary
>> because 32-bit ARM binaries can execute both under ARM and under arm64 kernels,
>> so there should be agreement about the meaning of feature bits, even if the ARM
>> kernel has no support yet for ARMv8 32-bit only hardware (such as ARMv8-R).
>
> It looks a bit strange to start filling HWCAP2 before HWCAP is full but
> I guess we want to preserve some future extensions in HWCAP for older
> glibc.
>
> --
> Catalin
^ permalink raw reply
* [PATCH 1/3] mmc: add support for power-on sequencing through DT
From: Ulf Hansson @ 2014-01-21 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140120191302.GA32178@quad.lixom.net>
On 20 January 2014 20:13, Olof Johansson <olof@lixom.net> wrote:
> On Mon, Jan 20, 2014 at 09:44:23AM +0100, Ulf Hansson wrote:
>> On 20 January 2014 04:56, Olof Johansson <olof@lixom.net> wrote:
>> > This patch enables support for power-on sequencing of SDIO peripherals through DT.
>> >
>> > In general, it's quite common that wifi modules and other similar
>> > peripherals have several signals in addition to the SDIO interface that
>> > needs wiggling before the module will power on. It's common to have a
>> > reference clock, one or several power rails and one or several lines
>> > for reset/enable type functions.
>> >
>> > The binding as written today introduces a number of reset gpios,
>> > a regulator and a clock specifier. The code will handle up to 2 gpio
>> > reset lines, but it's trivial to increase to more than that if needed
>> > at some point.
>> >
>> > Implementation-wise, the MMC core has been changed to handle this during
>> > host power up, before the host interface is powered on. I have not yet
>> > implemented the power-down side, I wanted people to have a chance for
>> > reporting back w.r.t. issues (or comments on the bindings) first.
>> >
>> > I have not tested the regulator portion, since the system and module
>> > I'm working on doesn't need one (Samsung Chromebook with Marvell
>> > 8797-based wifi). Testing of those portions (and reporting back) would
>> > be appreciated.
>> >
>> > Signed-off-by: Olof Johansson <olof@lixom.net>
>> > ---
>> > Documentation/devicetree/bindings/mmc/mmc.txt | 11 +++++++
>> > drivers/mmc/core/core.c | 42 +++++++++++++++++++++++++
>> > drivers/mmc/core/host.c | 30 +++++++++++++++++-
>> > include/linux/mmc/host.h | 5 +++
>> > 4 files changed, 87 insertions(+), 1 deletion(-)
>> >
>> > diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
>> > index 458b57f..962e0ee 100644
>> > --- a/Documentation/devicetree/bindings/mmc/mmc.txt
>> > +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
>> > @@ -5,6 +5,8 @@ these definitions.
>> > Interpreted by the OF core:
>> > - reg: Registers location and length.
>> > - interrupts: Interrupts used by the MMC controller.
>> > +- clocks: Clocks needed for the host controller, if any.
>> > +- clock-names: Goes with clocks above.
>> >
>> > Card detection:
>> > If no property below is supplied, host native card detect is used.
>> > @@ -30,6 +32,15 @@ Optional properties:
>> > - cap-sdio-irq: enable SDIO IRQ signalling on this interface
>> > - full-pwr-cycle: full power cycle of the card is supported
>> >
>> > +Card power and reset control:
>> > +The following properties can be specified for cases where the MMC
>> > +peripheral needs additional reset, regulator and clock lines. It is for
>> > +example common for WiFi/BT adapters to have these separate from the main
>> > +MMC bus:
>> > + - card-reset-gpios: Specify GPIOs for card reset (reset active low)
>> > + - card-external-vcc-supply: Regulator to drive (independent) card VCC
>> > + - clock with name "card_ext_clock": External clock provided to the card
>> > +
>> > *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
>> > polarity properties, we have to fix the meaning of the "normal" and "inverted"
>> > line levels. We choose to follow the SDHCI standard, which specifies both those
>> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> > index 098374b..c43e6c8 100644
>> > --- a/drivers/mmc/core/core.c
>> > +++ b/drivers/mmc/core/core.c
>> > @@ -13,11 +13,13 @@
>> > #include <linux/module.h>
>> > #include <linux/init.h>
>> > #include <linux/interrupt.h>
>> > +#include <linux/clk.h>
>> > #include <linux/completion.h>
>> > #include <linux/device.h>
>> > #include <linux/delay.h>
>> > #include <linux/pagemap.h>
>> > #include <linux/err.h>
>> > +#include <linux/gpio.h>
>> > #include <linux/leds.h>
>> > #include <linux/scatterlist.h>
>> > #include <linux/log2.h>
>> > @@ -1519,6 +1521,43 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
>> > mmc_host_clk_release(host);
>> > }
>> >
>> > +static void mmc_card_power_up(struct mmc_host *host)
>> > +{
>> > + int i;
>> > + struct gpio_desc **gds = host->card_reset_gpios;
>> > +
>> > + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
>> > + if (gds[i]) {
>> > + dev_dbg(host->parent, "Asserting reset line %d", i);
>> > + gpiod_set_value(gds[i], 1);
>> > + }
>> > + }
>> > +
>> > + if (host->card_regulator) {
>> > + dev_dbg(host->parent, "Enabling external regulator");
>> > + if (regulator_enable(host->card_regulator))
>> > + dev_err(host->parent, "Failed to enable external regulator");
>> > + }
>> > +
>> > + if (host->card_clk) {
>> > + dev_dbg(host->parent, "Enabling external clock");
>> > + clk_prepare_enable(host->card_clk);
>> > + }
>> > +
>> > + /* 2ms delay to let clocks and power settle */
>> > + mmc_delay(20);
>> > +
>> > + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
>> > + if (gds[i]) {
>> > + dev_dbg(host->parent, "Deasserting reset line %d", i);
>> > + gpiod_set_value(gds[i], 0);
>> > + }
>> > + }
>> > +
>> > + /* 2ms delay to after reset release */
>> > + mmc_delay(20);
>> > +}
>> > +
>> > /*
>> > * Apply power to the MMC stack. This is a two-stage process.
>> > * First, we enable power to the card without the clock running.
>> > @@ -1535,6 +1574,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
>> > if (host->ios.power_mode == MMC_POWER_ON)
>> > return;
>> >
>> > + /* Power up the card/module first, if needed */
>> > + mmc_card_power_up(host);
>> > +
>> > mmc_host_clk_hold(host);
>> >
>> > host->ios.vdd = fls(ocr) - 1;
>> > diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
>> > index 49bc403..e6b850b 100644
>> > --- a/drivers/mmc/core/host.c
>> > +++ b/drivers/mmc/core/host.c
>> > @@ -12,14 +12,18 @@
>> > * MMC host class device management
>> > */
>> >
>> > +#include <linux/kernel.h>
>> > +#include <linux/clk.h>
>> > #include <linux/device.h>
>> > #include <linux/err.h>
>> > +#include <linux/gpio/consumer.h>
>> > #include <linux/idr.h>
>> > #include <linux/of.h>
>> > #include <linux/of_gpio.h>
>> > #include <linux/pagemap.h>
>> > #include <linux/export.h>
>> > #include <linux/leds.h>
>> > +#include <linux/regulator/consumer.h>
>> > #include <linux/slab.h>
>> > #include <linux/suspend.h>
>> >
>> > @@ -312,7 +316,7 @@ int mmc_of_parse(struct mmc_host *host)
>> > u32 bus_width;
>> > bool explicit_inv_wp, gpio_inv_wp = false;
>> > enum of_gpio_flags flags;
>> > - int len, ret, gpio;
>> > + int i, len, ret, gpio;
>> >
>> > if (!host->parent || !host->parent->of_node)
>> > return 0;
>> > @@ -415,6 +419,30 @@ int mmc_of_parse(struct mmc_host *host)
>> > if (explicit_inv_wp ^ gpio_inv_wp)
>> > host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
>> >
>> > + /* Parse card power/reset/clock control */
>>
>> I would like us to prevent to open up for confusion with the "eMMC hw
>> reset" when adding this. Unless we are able to combine them in some
>> way?
>>
>> Could we maybe add some more comments about in what scenarios this DT
>> property would be useful?
>
> Ok, can do. How about something like:
>
> /*
> * Some cards need separate power/reset/clock control from the main
> * MMC/SDIO bus. Parse the description of those controls so we can
> * power on the card before the host controller.
> */
>
>
>> > + if (of_find_property(np, "card-reset-gpios", NULL)) {
>> > + struct gpio_desc *gpd;
>> > + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
>> > + gpd = devm_gpiod_get_index(host->parent, "card-reset", i);
>> > + if (IS_ERR(gpd))
>> > + break;
>> > + gpiod_direction_output(gpd, 0);
>> > + host->card_reset_gpios[i] = gpd;
>> > + }
>> > +
>> > + gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios));
>> > + if (!IS_ERR(gpd)) {
>> > + dev_warn(host->parent, "More reset gpios than we can handle");
>> > + gpiod_put(gpd);
>> > + }
>> > + }
>> > +
>> > + host->card_clk = of_clk_get_by_name(np, "card_ext_clock");
>>
>> of_clk_get_by_name relies on COMMON_CLK, is that really what you want here?
>>
>> > + if (IS_ERR(host->card_clk))
>> > + host->card_clk = NULL;
>> > +
>> > + host->card_regulator = regulator_get(host->parent, "card-external-vcc");
>>
>> Is the above regulator related to host->ocr_avail mask? Could the
>> above regulator be replaced by vmmc?
>
> I have to admit that I don't know MMC as well as I could, but OCR seems to be
> something that's between the driver/controller/device, not related to external
> power control in this case?
This is related to the power of the card, typically external
regulators controlled by the host driver.
Both the card and the host supports a voltage range. This range is
exactly what the OCR mask describes. At initialization of the card,
the host ocr is validated against the card ocr.
>
>> At the moment host drivers uses mmc_regulator_get_supply(), which
>> fetches regulators called "vmmc" and "vqmmc". It is also common to
>> have these defined in DT like "vmmc-supply". This has not been
>> properly documented for most host cases, and we should fix that. I
>> also think it would make sense to include these in the documentation
>> for the common mmc bindings, instead of host specific bindings.
>
> Hm, I had been of the impression that the vmmc stuff is to control
> power/voltage on the signal lines, not for external card power. Still, even in
> that case there's need for the reset line handling and clock control.
vmmc: the power to the card.
vqmmc: the I/O voltage levels (for the signal lines).
Regarding reset, I agree, those seems to be needed.
Regarding clock control. I suppose you are referring to separate
external clocks, not affecting the SDIO/SD/MMC interface speed!?
That could make sense, but still I wonder how those shall be handled
in a fine grained power management setup. In other words, when shall
those be gated/ungated? Is the mmc core able to take the correct
decision about these?
Kind regards
Uffe
>
> I'll take a look and see if there's a way to handle that in a properly
> sequenced way and still use the same regulator.
>
>
> -Olof
^ permalink raw reply
* [PATCH] arch_timer: Move delay timer to drivers clocksource
From: Prashant Gaikwad @ 2014-01-21 8:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52DE326F.40105@linaro.org>
On Tuesday 21 January 2014 02:10 PM, Daniel Lezcano wrote:
> On 01/21/2014 09:20 AM, Prashant Gaikwad wrote:
>> On Monday 20 January 2014 08:12 PM, Daniel Lezcano wrote:
>>> On 01/17/2014 07:36 PM, Stephen Boyd wrote:
>>>> On 01/17/14 05:40, Prashant Gaikwad wrote:
>>>>> Another requirement:
>>>>>
>>>>> We have 3 timers T1, T2, T3 used as wake events for 3 idle states C1,
>>>>> C2, C3 respectively.
>>>>>
>>>>> Rating of T2 is better than T3. If I register T2 and T3 both as
>>>>> broadcast timers then T3 will not be used. But ...
>>>>> - T2 is not preserved in C3 idle state.
>>>>> - T3 resolution is very poor (ms) and can not be used as wake
>>>>> event for C2.
>>>>>
>>>>> Possible solution, register only T3 as broadcast device and use T2 as
>>>>> per-CPU fallback timer.
>>>> We have the same situation on MSM. I've been thinking about proposing we
>>>> allow multiple broadcast timers to exist in the system and then have the
>>>> clockevents_notify() caller indicate which C state is being entered. The
>>>> broadcast timers would need to indicate which C state they don't work in
>>>> though.
>>> IMO, there are different solutions:
>>>
>>> 1. extend the C3STOP to C1STOP, C2STOP, etc ... and pass the idle state
>>> to the time framework where these flags are checked against. I don't
>>> like this approach but it is feasible.
>>>
>>> 2. use the generic power domain. When the power domain is shutdown via
>>> the cpuidle backend driver, it switches the timer.
>> I am aware of a way to attach idle state to GenPD where we enable an
>> idle state when that power domain is turned off but not the other way
>> where domain is shutdown via CPU idle driver. How do we do it?
>>
>> Even though we shutdown power domain via cpuidle driver this still has
>> to happen from CPU idle state, is that correct assumption? and we switch
>> the timer here. So we still need a way to switch timer from CPU idle
>> state. Hence the question remains is how to switch timers from idle state?
> You can effectively attach a power domain to a cpuidle state but that
> wasn't the point.
>
> What I meant is to create a generic power domain which maps the power
> domain of the idle state. When the power domain is shutdown, the
> callback of the genpd will switch to the timer.
>
> I can't give too much details because I am not used to this code but
> maybe it is a good solution for your specific case.
>
Somehow this is not mapping to my use case. We are using generic power
domains with CPU idle states.
^ permalink raw reply
* [patch] ARM: mvebu: checking for IS_ERR() instead of NULL
From: Dan Carpenter @ 2014-01-21 8:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52DE2EB0.9010206@free-electrons.com>
On Tue, Jan 21, 2014 at 09:24:16AM +0100, Gregory CLEMENT wrote:
> On 21/01/2014 07:52, Dan Carpenter wrote:
> > of_iomap() returns NULL on error, it doesn't return error pointers.
> >
>
> Hi Dan,
>
> Thanks for your patch but I already sent this fix yesterday and Jason have
> applied. See: http://thread.gmane.org/gmane.linux.ports.arm.kernel/295642
>
Cool. Congrats to Ezequiel on his new corporate email address. :)
regards,
dan carpenter
^ permalink raw reply
* [PATCH] arch_timer: Move delay timer to drivers clocksource
From: Daniel Lezcano @ 2014-01-21 8:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52DE2DB5.5000101@nvidia.com>
On 01/21/2014 09:20 AM, Prashant Gaikwad wrote:
> On Monday 20 January 2014 08:12 PM, Daniel Lezcano wrote:
>> On 01/17/2014 07:36 PM, Stephen Boyd wrote:
>>> On 01/17/14 05:40, Prashant Gaikwad wrote:
>>>> Another requirement:
>>>>
>>>> We have 3 timers T1, T2, T3 used as wake events for 3 idle states C1,
>>>> C2, C3 respectively.
>>>>
>>>> Rating of T2 is better than T3. If I register T2 and T3 both as
>>>> broadcast timers then T3 will not be used. But ...
>>>> - T2 is not preserved in C3 idle state.
>>>> - T3 resolution is very poor (ms) and can not be used as wake
>>>> event for C2.
>>>>
>>>> Possible solution, register only T3 as broadcast device and use T2 as
>>>> per-CPU fallback timer.
>>> We have the same situation on MSM. I've been thinking about proposing we
>>> allow multiple broadcast timers to exist in the system and then have the
>>> clockevents_notify() caller indicate which C state is being entered. The
>>> broadcast timers would need to indicate which C state they don't work in
>>> though.
>> IMO, there are different solutions:
>>
>> 1. extend the C3STOP to C1STOP, C2STOP, etc ... and pass the idle state
>> to the time framework where these flags are checked against. I don't
>> like this approach but it is feasible.
>>
>> 2. use the generic power domain. When the power domain is shutdown via
>> the cpuidle backend driver, it switches the timer.
>
> I am aware of a way to attach idle state to GenPD where we enable an
> idle state when that power domain is turned off but not the other way
> where domain is shutdown via CPU idle driver. How do we do it?
>
> Even though we shutdown power domain via cpuidle driver this still has
> to happen from CPU idle state, is that correct assumption? and we switch
> the timer here. So we still need a way to switch timer from CPU idle
> state. Hence the question remains is how to switch timers from idle state?
You can effectively attach a power domain to a cpuidle state but that
wasn't the point.
What I meant is to create a generic power domain which maps the power
domain of the idle state. When the power domain is shutdown, the
callback of the genpd will switch to the timer.
I can't give too much details because I am not used to this code but
maybe it is a good solution for your specific case.
--
<http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
^ permalink raw reply
* [PATCH v2 2/7] clk: samsung: add infrastructure to register cpu clocks
From: Thomas Abraham @ 2014-01-21 8:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140120092412.0c2d57e4@amdc2363>
Hi Lukasz,
On Mon, Jan 20, 2014 at 1:54 PM, Lukasz Majewski <l.majewski@samsung.com> wrote:
> Hi Thomas,
>
>> From: Thomas Abraham <thomas.ab@samsung.com>
>>
>> The CPU clock provider supplies the clock to the CPU clock domain. The
>> composition and organization of the CPU clock provider could vary
>> among Exynos SoCs. A CPU clock provider can be composed of clock mux,
>> dividers and gates. This patch defines a new clock type for CPU clock
>> provider and adds infrastructure to register the CPU clock providers
>> for Samsung platforms.
>>
>> In addition to this, the arm cpu clock provider for Exynos4210 and
>> compatible SoCs is instantiated using the new cpu clock type. The
>> clock frequency table and the clock configuration data for this clock
>> is obtained from device tree. This implementation is reusable for
>> Exynos4x12 and Exynos5250 SoCs as well.
>>
>> Cc: Tomasz Figa <t.figa@samsung.com>
>> Cc: Lukasz Majewski <l.majewski@majess.pl>
>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> ---
>> drivers/clk/samsung/Makefile | 2 +-
>> drivers/clk/samsung/clk-cpu.c | 345
>> +++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/samsung/clk.h | 3 + 3 files changed, 349
>> insertions(+), 1 deletions(-) create mode 100644
>> drivers/clk/samsung/clk-cpu.c
>>
>> diff --git a/drivers/clk/samsung/Makefile
>> b/drivers/clk/samsung/Makefile index 8eb4799..e2b453f 100644
>> --- a/drivers/clk/samsung/Makefile
>> +++ b/drivers/clk/samsung/Makefile
>> @@ -2,7 +2,7 @@
>> # Samsung Clock specific Makefile
>> #
>>
>> -obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o
>> +obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o
>> obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o
>> obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
>> obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
>> diff --git a/drivers/clk/samsung/clk-cpu.c
>> b/drivers/clk/samsung/clk-cpu.c new file mode 100644
>> index 0000000..92fba45
>> --- /dev/null
>> +++ b/drivers/clk/samsung/clk-cpu.c
>> @@ -0,0 +1,345 @@
>> +/*
>> + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
>> + * Author: Thomas Abraham <thomas.ab@samsung.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This file contains the utility functions to register the cpu
>> clocks
>> + * for samsung platforms.
>> +*/
>> +
>> +#include <linux/errno.h>
>> +#include "clk.h"
>> +
>> +#define SRC_CPU 0x0
>> +#define STAT_CPU 0x200
>> +#define DIV_CPU0 0x300
>> +#define DIV_CPU1 0x304
>> +#define DIV_STAT_CPU0 0x400
>> +#define DIV_STAT_CPU1 0x404
>> +
>> +/**
>> + * struct samsung_cpuclk_freq_table: table of frequency supported by
>> + * a cpu clock and associated data if any.
>> + * @freq: points to a table of supported frequencies (in KHz)
>> + * @freq_count: number of entries in the frequency table
>> + * @data: cpu clock specific data, if any
>> + *
>> + * This structure holds the frequency options supported by the cpu
>> clock in
>> + * which this structure is contained. The data pointer is an
>> optional data
>> + * that can provide any additional configuration options for the
>> supported
>> + * frequencies. This structure is intended to be reusable for all
>> cpu clocks
>> + * in Samsung SoC based platforms
>> + */
>> +struct samsung_cpuclk_freq_table {
>> + const unsigned long *freq; /* in KHz */
>> + unsigned long freq_count;
>> + const void *data;
>> +};
>> +
>> +/**
>> + * struct exynos4210_freq_data: format of auxillary data associated
>> with
>> + * each frequency supported by the cpu clock for exynos4210.
>> + * @parent_freq: The frequency of the parent clock required to
>> generate the
>> + * supported cpu clock speed.
>> + * @div0: value to be programmed in the div_cpu0 register.
>> + * @div1: value to be programmed in the div_cpu1 register.
>> + *
>> + * This structure holds the auxillary configuration data for each
>> supported
>> + * cpu clock frequency on Exynos4210 and compatible SoCs.
>> + */
>> +struct exynos4210_freq_data {
>> + unsigned long parent_freq;
>> + unsigned int div0;
>> + unsigned int div1;
>> +};
>> +
>> +/**
>> + * struct samsung_cpuclk: information about clock supplied to a CPU
>> core.
>> + * @hw: handle between ccf and cpu clock.
>> + * @ctrl_base: base address of the clock controller.
>> + * @offset: offset from the ctrl_base address where the cpu clock
>> div/mux
>> + * registers can be accessed.
>> + * @parent: clock handle representing the clock output of the parent
>> clock.
>> + * @freq_table: the frequency table supported by this cpu clock.
>> + */
>> +struct samsung_cpuclk {
>> + struct clk_hw hw;
>> + void __iomem *ctrl_base;
>> + unsigned long offset;
>> + struct clk *parent;
>> + const struct samsung_cpuclk_freq_table *freq_table;
>> +};
>> +
>> +#define to_samsung_cpuclk(hw) container_of(hw, struct
>> samsung_cpuclk, hw) +
>> +/**
>> + * struct samsung_cpuclk_match_data: soc specific data for cpu
>> clocks.
>> + * @parser: pointer to a function that can parse SoC specific cpu
>> clock
>> + * frequency and associated configuration data.
>> + * @offset: optional offset from base of clock controller register
>> base,
>> + * to be used when accessing clock controller registers
>> related to the
>> + * cpu clock.
>> + * @offset: offset from the ctrl_base address where the cpu clock
>> div/mux
>> + * registers can be accessed.
>> + */
>> +struct samsung_cpuclk_match_data {
>> + int (*parser)(struct device_node *,
>> + struct samsung_cpuclk_freq_table **);
>> + unsigned int offset;
>> +};
>> +
>> +/* This is a helper function to perform clock rounding for cpu
>> clocks. */ +static long samsung_cpuclk_round_rate(struct clk_hw *hw,
>> + unsigned long drate, unsigned long *prate)
>> +{
>> + struct samsung_cpuclk *cpuclk = to_samsung_cpuclk(hw);
>> + const struct samsung_cpuclk_freq_table *freq_tbl;
>> + int i;
>> +
>> + freq_tbl = cpuclk->freq_table;
>> + drate /= 1000;
>> +
>> + for (i = 0; i < freq_tbl->freq_count; i++) {
>> + if (drate >= freq_tbl->freq[i])
>> + return freq_tbl->freq[i] * 1000;
>> + }
>> + return freq_tbl->freq[i - 1] * 1000;
>> +}
>> +
>> +#define EXYNOS4210_ARM_DIV1(base) ((readl(base + DIV_CPU0) & 0xf) +
>> 1) +#define EXYNOS4210_ARM_DIV2(base) (((readl(base + DIV_CPU0) >>
>> 28) & 0xf) + 1) +
>> +/*
>> + * CPU clock speed for Exynos4210 and compatible SoCs is
>> + * parent clock speed / core1_ratio / core2_ratio
>> + */
>> +static unsigned long exynos4210_armclk_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + struct samsung_cpuclk *armclk = to_samsung_cpuclk(hw);
>> + void __iomem *base = armclk->ctrl_base + armclk->offset;
>> +
>> + return parent_rate / EXYNOS4210_ARM_DIV1(base) /
>> + EXYNOS4210_ARM_DIV2(base);
>> +}
>> +
>> +/* set rate callback for cpuclk type on Exynos4210 and similar SoCs
>> */ +static int exynos4210_armclk_set_rate(struct clk_hw *hw, unsigned
>> long drate,
>> + unsigned long prate)
>> +{
>> + struct samsung_cpuclk *armclk = to_samsung_cpuclk(hw);
>> + const struct samsung_cpuclk_freq_table *freq_tbl;
>> + const struct exynos4210_freq_data *freq_data;
>> + unsigned long mux_reg, idx;
>> + void __iomem *base;
>> +
>> + if (drate == prate)
>> + return 0;
>> +
>> + freq_tbl = armclk->freq_table;
>> + freq_data = freq_tbl->data;
>> + base = armclk->ctrl_base + armclk->offset;
>> +
>> + for (idx = 0; idx < freq_tbl->freq_count; idx++, freq_data++)
>> + if ((freq_tbl->freq[idx] * 1000) == drate)
>> + break;
>> +
>> + if (drate < prate) {
>> + mux_reg = readl(base + SRC_CPU);
>> + writel(mux_reg | (1 << 16), base + SRC_CPU);
>> + while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
>> + ;
>> +
>> + clk_set_rate(armclk->parent, drate);
>> + }
>> +
>> + writel(freq_data->div0, base + DIV_CPU0);
>> + while (readl(base + DIV_STAT_CPU0) != 0)
>> + ;
>> + writel(freq_data->div1, base + DIV_CPU1);
>> + while (readl(base + DIV_STAT_CPU1) != 0)
>> + ;
>> +
>> + if (drate > prate) {
>> + mux_reg = readl(base + SRC_CPU);
>> + writel(mux_reg | (1 << 16), base + SRC_CPU);
>> + while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
>> + ;
>> +
>> + clk_set_rate(armclk->parent, freq_data->parent_freq
>> * 1000);
>> + }
>> +
>> + mux_reg = readl(base + SRC_CPU);
>> + writel(mux_reg & ~(1 << 16), base + SRC_CPU);
>> + while (((readl(base + STAT_CPU) >> 16) & 0x7) != 1)
>> + ;
>> + return 0;
>> +}
>> +
>> +/* clock ops for armclk on Exynos4210 and compatible platforms. */
>> +static const struct clk_ops exynos4210_armclk_clk_ops = {
>> + .recalc_rate = exynos4210_armclk_recalc_rate,
>> + .round_rate = samsung_cpuclk_round_rate,
>> + .set_rate = exynos4210_armclk_set_rate,
>> +};
>> +
>> +/* helper function to register a cpu clock */
>> +static void __init samsung_cpuclk_register(unsigned int lookup_id,
>> + const char *name, const char *parent, const struct
>> clk_ops *ops,
>> + const struct samsung_cpuclk_freq_table *freq_tbl,
>> + void __iomem *reg_base,
>> + const struct samsung_cpuclk_match_data *data)
>> +{
>> + struct samsung_cpuclk *cpuclk;
>> + struct clk_init_data init;
>> + struct clk *clk;
>> +
>> + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
>> + if (!cpuclk) {
>> + pr_err("%s: could not allocate memory for cpuclk
>> %s\n",
>> + __func__, name);
>> + return;
>> + }
>> +
>> + init.name = name;
>> + init.flags = CLK_GET_RATE_NOCACHE;
>> + init.parent_names = &parent;
>> + init.num_parents = 1;
>> + init.ops = ops;
>> +
>> + cpuclk->hw.init = &init;
>> + cpuclk->ctrl_base = reg_base;
>> + cpuclk->offset = data->offset;
>> + cpuclk->freq_table = freq_tbl;
>> + cpuclk->parent = __clk_lookup(parent);
>> +
>> + clk = clk_register(NULL, &cpuclk->hw);
>> + if (IS_ERR(clk)) {
>> + pr_err("%s: could not register cpuclk %s\n",
>> __func__, name);
>> + kfree(cpuclk);
>> + return;
>> + }
>> + samsung_clk_add_lookup(clk, lookup_id);
>> +}
>> +
>> +#define EXYNOS4210_DIV_CPU01(d0, d1, d2, d3, d4, d5, d6,
>> d7) \
>> + ((d0 << 28) | (d1 << 24) | (d2 << 20) | (d3
>> << 16) | \
>> + (d4 << 12) | (d5 << 8) | (d6 << 4) | (d7 << 0))
>> +#define EXYNOS4210_DIV_CPU11(d0, d1,
>> d2) \
>> + ((d0 << 8) | (d1 << 4) | (d2 << 0))
>> +#define EXYNOS4210_CFG_LEN 13
>> +
>> +/*
>> + * parse cpu clock frequency table and auxillary configuration data
>> from dt
>> + * for exynos4210 and compatible SoC's.
>> + */
>> +static int exynos4210_armclk_cfg_parser(struct device_node *np,
>> + struct samsung_cpuclk_freq_table **tbl)
>> +{
>> + struct samsung_cpuclk_freq_table *freq_tbl;
>> + struct exynos4210_freq_data *fdata, *t_fdata;
>> + unsigned long *freqs, cfg[EXYNOS4210_CFG_LEN];
>> + const struct property *prop;
>> + unsigned int tbl_sz, i, j;
>> + const __be32 *val;
>> + int ret;
>> +
>> + prop = of_find_property(np, "arm-frequency-table", NULL);
>> + if (!prop)
>> + return -EINVAL;
>> + if (!prop->value)
>> + return -EINVAL;
>> + if ((prop->length / sizeof(u32)) % EXYNOS4210_CFG_LEN)
>
> Cannot we have the EXYNOS4210_CFG_LEN parsed from DT as well?
As per Rob's suggestion, the clock divider ration table will be
removed. So this portion of the code will be reworked.
>
>> + return -EINVAL;
>> + tbl_sz = (prop->length / sizeof(u32)) / EXYNOS4210_CFG_LEN;
>> +
>> + freq_tbl = kzalloc(sizeof(*freq_tbl), GFP_KERNEL);
>> + if (!freq_tbl)
>> + return -ENOMEM;
>> +
>> + freqs = kzalloc(sizeof(u32) * tbl_sz, GFP_KERNEL);
>> + if (!freqs) {
>> + ret = -ENOMEM;
>> + goto free_freq_tbl;
>> + }
>> +
>> + fdata = kzalloc(sizeof(*fdata) * tbl_sz, GFP_KERNEL);
>> + if (!fdata) {
>> + ret = -ENOMEM;
>> + goto free_freqs;
>> + }
>> + t_fdata = fdata;
>> +
>> + val = prop->value;
>> + for (i = 0; i < tbl_sz; i++, fdata++) {
>> + for (j = 0; j < EXYNOS4210_CFG_LEN; j++)
>> + cfg[j] = be32_to_cpup(val++);
>> + freqs[i] = cfg[0];
>> + fdata->parent_freq = cfg[1];
>
> Why do we need the separate parent_freq entry here?
>
> In the patch 4/7 the freqs (cfg[0]) and parent_freq (cfg[1]) values are
> the same for all supported devices (at "arm-frequency-table").
>
> What is the rationale for having those values duplicated in the DT?
The intention was to support frequencies which may not be direct
output of the parent PLL clock. For instance, if the PLL supports
200MHz, 400MHz and 600MHz and the CPU clock needs to be set to 300MHz,
then 600MHz / 2 is a valid clock output for the cpu clock. So this is
an example where the cpu clock speed is different from its parent
clock speed.
If possible, I will try and remove the need for this table in the next version.
Thanks,
Thomas.
>
>
>> + fdata->div0 = EXYNOS4210_DIV_CPU01(cfg[9], cfg[8],
>> cfg[7],
>> + cfg[6], cfg[5], cfg[4], cfg[3],
>> cfg[2]);
>> + fdata->div1 = EXYNOS4210_DIV_CPU11(cfg[12], cfg[11],
>> cfg[10]);
>> + }
>> +
>> + freq_tbl->freq = freqs;
>> + freq_tbl->freq_count = tbl_sz;
>> + freq_tbl->data = t_fdata;
>> + *tbl = freq_tbl;
>> + return 0;
>> +
>> +free_freqs:
>> + kfree(freqs);
>> +free_freq_tbl:
>> + kfree(freq_tbl);
>> + return ret;
>> +}
>> +
>> +static struct samsung_cpuclk_match_data exynos4210_cpuclk_match_data
>> = {
>> + .parser = exynos4210_armclk_cfg_parser,
>> + .offset = 0x14200,
>> +};
>> +
>> +static struct samsung_cpuclk_match_data exynos5250_cpuclk_match_data
>> = {
>> + .parser = exynos4210_armclk_cfg_parser,
>> + .offset = 0x200,
>> +};
>> +
>> +static const struct of_device_id samsung_clock_ids[] = {
>> + { .compatible = "samsung,exynos4210-clock",
>> + .data = &exynos4210_cpuclk_match_data, },
>> + { .compatible = "samsung,exynos4412-clock",
>> + .data = &exynos4210_cpuclk_match_data, },
>> + { .compatible = "samsung,exynos5250-clock",
>> + .data = &exynos5250_cpuclk_match_data, },
>> +};
>> +
>> +int __init samsung_register_arm_clock(struct device_node *np,
>> + unsigned int lookup_id, const char *parent,
>> void __iomem *base) +{
>> + const struct of_device_id *match;
>> + struct samsung_cpuclk_freq_table *freq_table;
>> + const struct samsung_cpuclk_match_data *data;
>> + int ret;
>> +
>> + match = of_match_node(samsung_clock_ids, np);
>> + if (!match) {
>> + pr_err("%s: could not determine soc type\n",
>> __func__);
>> + return -EINVAL;
>> + }
>> +
>> + data = match->data;
>> + ret = data->parser(np, &freq_table);
>> + if (ret) {
>> + pr_err("%s: error %d in parsing arm clock freq
>> table",
>> + __func__, ret);
>> + return -EINVAL;
>> + }
>> +
>> + samsung_cpuclk_register(lookup_id, "armclk", parent,
>> + &exynos4210_armclk_clk_ops, freq_table, base, data);
>> +
>> + return 0;
>> +}
>> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
>> index 31b4174..a759330 100644
>> --- a/drivers/clk/samsung/clk.h
>> +++ b/drivers/clk/samsung/clk.h
>> @@ -340,4 +340,7 @@ extern void __init
>> samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
>> extern unsigned long _get_rate(const char *clk_name);
>>
>> +extern int __init samsung_register_arm_clock(struct device_node *np,
>> + unsigned int lookup_id, const char *parent, void
>> __iomem *base); +
>> #endif /* __SAMSUNG_CLK_H */
>
>
>
> --
> Best regards,
>
> Lukasz Majewski
>
> Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
^ permalink raw reply
* Question: xilinx_zynq_defconfig
From: Masahiro Yamada @ 2014-01-21 8:34 UTC (permalink / raw)
To: linux-arm-kernel
Hello Michal,
I have a Zynq ZC706 board.
I have a simple question about Zynq support.
There exist device tree sources for Zynq in the mainline of Kernel,
whereas xilinx_zynq_defconfig is missing.
(I know it is maintained in a Xilinx repository.)
Why have the defconfig files not been merged yet?
Best Regards
Masahiro Yamada
^ 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